Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stack overflow killing build with SGX crates #209

Closed
sphw opened this issue Aug 16, 2021 · 7 comments · Fixed by #303
Closed

Stack overflow killing build with SGX crates #209

sphw opened this issue Aug 16, 2021 · 7 comments · Fixed by #303

Comments

@sphw
Copy link

sphw commented Aug 16, 2021

I have a similar issue to the one described in #42, but I suspect the root cause is somewhat different. I am attempting to build a Rust project that makes extensive use of Intel SGX and the Rust SGX SDK. To use the Rust SGX SDK you have to use patched versions of common crates. These patched versions replace std with the special SGX std version. When using some of these crates with crate2nix I get stack-overflows.

I can reproduce the issue in a few different ways, but I have put up the easiest minimal reproduction here: https://github.com/sphw/crate2nix-sgx-test/blob/main/Cargo.nix

When I run nix-instantiate Cargo.nix -A rootCrate.build -vvvvvv --show-trace I get the following output:

➜  sgx-nix-test git:(main) ✗ nix-instantiate Cargo.nix -A rootCrate.build -vvvvvv --show-trace                                                                
evaluating file '/nix/store/9dliwin402wwlgn1bgpgxb0p6lwffnj2-nix-2.3.14/share/nix/corepkgs/derivation.nix'
resolved search path element '/home/sphw/.nix-defexpr/channels' to '/home/sphw/.nix-defexpr/channels'
resolved search path element '/nix/var/nix/profiles/per-user/root/channels/nixpkgs' to '/nix/var/nix/profiles/per-user/root/channels/nixpkgs'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/default.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/minver.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/top-level/impure.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/top-level/default.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/stdenv/booter.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/default.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/fixed-points.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/lists.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/stdenv/default.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/systems/default.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/attrsets.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/systems/inspect.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/systems/architectures.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/systems/parse.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/strings.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/types.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/trivial.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/systems/platforms.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/modules.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/top-level/config.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/options.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/stdenv/linux/default.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/top-level/stage.nix'
resolved search path element '/nix/var/nix/profiles/per-user/root/channels' to '/nix/var/nix/profiles/per-user/root/channels'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/stdenv/adapters.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/build-support/trivial-builders.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/top-level/splice.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/top-level/all-packages.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/top-level/aliases.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/stdenv/generic/default.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/customisation.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/tools/text/gawk/default.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/servers/x11/xorg/default.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/servers/x11/xorg/overrides.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/stdenv/generic/make-derivation.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/build-support/rust/default-crate-overrides.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/build-support/rust/build-rust-crate/default.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/stdenv/generic/check-meta.nix'
evaluating file '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/lib/systems/doubles.nix'
processing attribute 'args'
locking this thread to CPU 15
acquiring global GC lock '/nix/var/nix/gc.lock'
acquiring read lock on '/nix/var/nix/temproots/169351'
acquiring write lock on '/nix/var/nix/temproots/169351'
downgrading to read lock on '/nix/var/nix/temproots/169351'
copied source '/nix/store/yzs6r04j9q3kwrkj4553gxc0r6sb9bvr-nixpkgs-21.11pre301506.b2c82b443ef/nixpkgs/pkgs/stdenv/generic/default-builder.sh' -> '/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh'
processing attribute 'build'
processing attribute 'buildDependencies'
error: stack overflow (possible infinite recursion)

I'm not sure the root-cause of this issue. I have run the build with ulimit -s unlimited, which causes the process to use all the remaining system memory.

@sphw sphw changed the title Stack overflow killing build Stack overflow killing build with SGX crates Aug 16, 2021
@pandaman64
Copy link
Contributor

Another reproduction: using aws-config causes stack overflow: https://github.com/pandaman64/crate2nix-aws-infinite-recursion/blob/main/Cargo.toml

@andir
Copy link
Collaborator

andir commented Nov 28, 2021

Whats the nix Version you are using? It might be worth checking against the latest master branch of Nix as that had some changes that enabled the compiler to do tail-call-optimization in a couple of places.

@pandaman64
Copy link
Contributor

I'm using nix (Nix) 2.4pre20211006_53e4794. Let me try newer version later.

@pandaman64
Copy link
Contributor

I updated nix command to nix (Nix) 2.5pre20211007_844dd90, but it still fails.
I have not updated the system nix yet (user-space command only), so this might explain the failure.

@pandaman64
Copy link
Contributor

@andir I was unable to successfully run Nix against my repository for the following versions:

  • nix (Nix) 2.5pre20211007_844dd90 from nixos-unstable's nixUnstable
  • nix (Nix) 2.4 from nixos-unstable's nix

So this issue is not fixed, I guess.

@Fuuzetsu
Copy link
Contributor

I have ran into this in the wild today. I made a reproducer.

https://github.com/Fuuzetsu/crate2nix-stack-overflow-repro/tree/fb5913fd7d56ad870fa763e2b9b47f3beeb25e32

In the reproducer you I bundle a patched nix which allows us to further figure out what's happening. We can see that it gets stuck running in circles in the expandFeatures function in Cargo.nix. I attach a (compressed) trace.

trace.gz

You can debug what's going on like so:

diff --git a/nix/Cargo.nix b/nix/Cargo.nix
index 0edef57..ff00f34 100644
--- a/nix/Cargo.nix
+++ b/nix/Cargo.nix
@@ -5044,6 +5044,7 @@ rec {
   mergePackageFeatures =
     { crateConfigs ? crates
     , packageId
+    , depth ? 0
     , rootPackageId ? packageId
     , features ? rootFeatures
     , dependencyPath ? [ crates.${packageId}.crateName ]
@@ -5063,8 +5064,12 @@ rec {
       assert (builtins.isBool runTests);
       let
         crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}");
-        expandedFeatures = expandFeatures (crateConfig.features or { }) features;
-        enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures;
+        dbgStr = d: s: if d == 0 then s else " ${dbgStr (d - 1) s}";
+        dbg = s: lib.debug.traceSeq (dbgStr depth s);
+        expandedFeatures_ = expandFeatures depth (crateConfig.features or { }) (dbg "Expanding for ${packageId} (${builtins.toString features})" features);
+        expandedFeatures = dbg "Expanded for ${packageId} (${builtins.toString (builtins.length expandedFeatures_)})" expandedFeatures_;
+        enabledFeatures_ = enableFeatures (crateConfig.dependencies or [ ]) (dbg "Enabling for ${packageId} (${builtins.toString (builtins.length expandedFeatures)})" expandedFeatures);
+        enabledFeatures = dbg "Enabled for ${packageId} (${builtins.toString (builtins.length enabledFeatures_)})" enabledFeatures_;
         depWithResolvedFeatures = dependency:
           let
             packageId = dependency.packageId;
@@ -5093,6 +5098,7 @@ rec {
                 then cache
                 else
                   mergePackageFeatures {
+                    depth = depth + 1;
                     features = combinedFeatures;
                     featuresByPackageId = cache;
                     inherit crateConfigs packageId target runTests rootPackageId;
@@ -5157,16 +5163,21 @@ rec {
     featureMap is an attribute set which maps feature names to lists of further
     feature names to enable in case this feature is selected.
   */
-  expandFeatures = featureMap: inputFeatures:
+  expandFeatures = depth: featureMap: inputFeatures:
     assert (builtins.isAttrs featureMap);
     assert (builtins.isList inputFeatures);
     let
+      dbgStr = d: s: if d == 0 then s else " ${dbgStr (d - 1) s}";
+      dbg = s: lib.debug.traceSeq (dbgStr depth s);
       expandFeature = feature:
         assert (builtins.isString feature);
-        [ feature ] ++ (expandFeatures featureMap (featureMap."${feature}" or [ ]));
+        let 
+          enables = featureMap."${feature}" or [ ];
+        in 
+        (dbg "${feature} --> ${builtins.toString enables}") [ feature ] ++ (expandFeatures (depth + 1) featureMap enables);
       outFeatures = lib.concatMap expandFeature inputFeatures;
     in
-    sortedUnique outFeatures;
+    /*dbg "Expanding ${builtins.toString inputFeatures} with ${builtins.toString (builtins.attrNames featureMap)}"*/ (sortedUnique outFeatures);
 
   /* This function adds optional dependencies as features if they are enabled
     indirectly by dependency features. This function mimics Cargo's behavior

If you do this (being careful about nix laziness), you can spot a cycle like this:

  • temporal feature expands to the features to right of -->

    temporal --> dtype-datetime dtype-date dtype-time dtype-duration polars-plan/temporal
    
  • We process the features in order, dtype-datetime expands to the stuff after
    -->. Notably see that it expands to another temporal!

    dtype-datetime --> polars-plan/dtype-datetime polars-time/dtype-datetime temporal
    
  • polars-plan/dtype-datetime expands to nothing

    polars-plan/dtype-datetime --> 
    
  • polars-time/dtype-datetime expands to nothing

    polars-time/dtype-datetime --> 
    
  • We make finally make it to temporal at the back of the dtype-datetime expansion and the cycle starts again!

    temporal --> dtype-datetime dtype-date dtype-time dtype-duration polars-plan/temporal
    

    I'm considering what the right patch is. I tried patching it by making sure it doesn't go into a cycle but I failed and it didn't work properly. I'll try again but I thought I'd at least put out the information out there.

@Fuuzetsu
Copy link
Contributor

The below patch should work I think. I'm able to apply this to our rather large tree and it fetches from the binary cache which implies that usual behaviour is completely unchanged. I'll try to cook up a patch.

diff --git a/nix/Cargo.nix b/nix/Cargo.nix
index c89bae956..44f8c5a19 100644
--- a/nix/Cargo.nix
+++ b/nix/Cargo.nix
@@ -36514,12 +36514,19 @@ rec {
     assert (builtins.isAttrs featureMap);
     assert (builtins.isList inputFeatures);
     let
-      expandFeature = feature:
-        assert (builtins.isString feature);
-        [ feature ] ++ (expandFeatures featureMap (featureMap."${feature}" or [ ]));
-      outFeatures = lib.concatMap expandFeature inputFeatures;
-    in
-    sortedUnique outFeatures;
+      expandFeaturesNoCycle = oldSeen: inputFeatures:
+        if inputFeatures != []
+        then
+          let
+            feature = builtins.head inputFeatures;
+            tail = builtins.tail inputFeatures;
+            seen = oldSeen // { ${feature} = 1; };
+            enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or []);
+          in [ feature ] ++ (expandFeaturesNoCycle seen (tail ++ enables))
+        # We're done.
+        else [];
+      outFeatures = expandFeaturesNoCycle { } inputFeatures;
+    in sortedUnique outFeatures;
 
   /* This function adds optional dependencies as features if they are enabled
     indirectly by dependency features. This function mimics Cargo's behavior

Fuuzetsu added a commit to Fuuzetsu/crate2nix that referenced this issue Oct 13, 2023
Fixes nix-community#209. Hopefully.

I'm not sure how to properly update all the tests and stuff. For now I
manually re-generated the same set of files that was re-generated in
some other, recent commit.
kolloch pushed a commit that referenced this issue Oct 14, 2023
Fixes #209. Hopefully.

I'm not sure how to properly update all the tests and stuff. For now I
manually re-generated the same set of files that was re-generated in
some other, recent commit.
kolloch pushed a commit that referenced this issue Oct 25, 2023
Fixes #209. Hopefully.

I'm not sure how to properly update all the tests and stuff. For now I
manually re-generated the same set of files that was re-generated in
some other, recent commit.
lelongg pushed a commit to lelongg/crate2nix that referenced this issue Apr 3, 2024
Fixes nix-community#209. Hopefully.

I'm not sure how to properly update all the tests and stuff. For now I
manually re-generated the same set of files that was re-generated in
some other, recent commit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants