Skip to content

avoid null pointer dereference when attrSet->pos is null#4895

Closed
manveru wants to merge 1 commit intoNixOS:masterfrom
manveru:work-around-missing-attrSet-pos
Closed

avoid null pointer dereference when attrSet->pos is null#4895
manveru wants to merge 1 commit intoNixOS:masterfrom
manveru:work-around-missing-attrSet-pos

Conversation

@manveru
Copy link
Contributor

@manveru manveru commented Jun 8, 2021

This may be a fix for #4893

I added a small test that just checks for the segfault, given that builtins.tryEval cannot catch this kind of error.

To recap, here's the behavior before:

❯ nix eval --expr 'builtins.getAttr "bar" (builtins.listToAttrs [ { name = "foo"; value = "bar"; } ])'
[1]    1731608 segmentation fault (core dumped)  nix eval --expr 

And after the fix:

❯ ./result/bin/nix eval --expr 'builtins.getAttr "bar" (builtins.listToAttrs [ { name = "foo"; value = "bar"; } ])'
error: attribute 'bar' missing for call to 'getAttr'

       at «string»:1:1:

            1| builtins.getAttr "bar" (builtins.listToAttrs [ { name = "foo"; value = "bar"; } ])
             | ^

@manveru manveru force-pushed the work-around-missing-attrSet-pos branch 2 times, most recently from e833200 to f93d30d Compare June 8, 2021 09:03
Copy link
Member

@thufschmitt thufschmitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix!

I have a couple of comments to clean this a slight bit, but overall 👍🏻


Pos aPos = *attrSet->pos;
if (aPos == noPos) {
if (attrSet->pos == nullptr) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be factored out a slight bit I guess. Something along the lines of:

if (attrSet->pos && *attrSet->pos != noPos) {
  // Throw with the right location and the stack trace
} else {
  // Throw with the parent location
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this look OK to you now?

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than adding a brand new toplevel test (which does cost a bit as initializing a test is slightly expensive), I think you can

Also: Can you add a reference to the issue in the test file, so that we know why it’s here?

Copy link
Member

@thufschmitt thufschmitt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good now!

@manveru
Copy link
Contributor Author

manveru commented Jun 8, 2021

Thanks for all the guidance :)

@domenkozar domenkozar requested a review from edolstra June 10, 2021 14:11
@roberth
Copy link
Member

roberth commented Jun 22, 2021

I've run nixUnstable with this patch for some days now and it caused a significant improvement in stability.

cc @edolstra

This was referenced Jun 24, 2021
@domenkozar
Copy link
Member

domenkozar commented Jun 24, 2021

I plan to merge this in a few days unless someone raises a concern.

@edolstra
Copy link
Member

@domenkozar I want to have a look at it first.

@roberth
Copy link
Member

roberth commented Jun 29, 2021

The majority of other sites where pos is dereferenced do not check for null either.
So while this PR does make pos handling a bit more robust, it doesn't seem to be a complete fix.
Part of the problem is that the codebase seems to have two representations of "no position": null or &noPos. The latter is more robust; more than std::optional<Pos> even, because it doesn't ever need to throw or crash. It would seem best to remove all null initializations of pos with an assert(pos) in the appropriate Attr constructor. However, I didn't spot any such initializations in a quick sample. Perhaps the problem is that some Bindings aren't initialized correctly?

Meanwhile, it doesn't hurt to merge this.

@thufschmitt
Copy link
Member

Part of the problem is that the codebase seems to have two representations of "no position": null or &noPos. The latter is more robust; more than std::optional<Pos> even, because it doesn't ever need to throw or crash.

Couldn’t we make pos a ref (assuming that ref has no runtime overhead)? That way we can statically enforce that it can’t be null and we sidestep this issue

It would seem best to remove all null initializations of pos with an assert(pos) in the appropriate Attr constructor. However, I didn't spot any such initializations in a quick sample.

IIRC in the current case, we explicitly pass a pos argument to the Attr constructor, but this argument happens to be null

Copy link
Member

@Ma27 Ma27 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes a fairly weird problem I had when forgetting to pass version (along with pname) to buildRustPackage in an overlay evaluated by a flake.

@roberth
Copy link
Member

roberth commented Jul 9, 2021

@edolstra This change is an improvement. Could you merge it?
It may not be the final fix, but the improved stability in the meanwhile will be greatly appreciated.

@manveru manveru force-pushed the work-around-missing-attrSet-pos branch from 1e41462 to 11dbec4 Compare August 26, 2021 12:36
@manveru
Copy link
Contributor Author

manveru commented Aug 26, 2021

Did another rebase and squashed the commits to make this hopefully a bit more attractive to merge :)

@Ma27
Copy link
Member

Ma27 commented Aug 26, 2021

@edolstra would be awesome to see this merged :)

roberth added a commit to hercules-ci/nix that referenced this pull request Aug 29, 2021
This fixes a class of crashes and introduces ptr<T> to make the
code robust against this failure mode going forward.

Thanks regnat for the idea of a ref<T> without overhead!

Closes NixOS#4895
Closes NixOS#4893
Closes NixOS#5127
Closes NixOS#5113
@roberth
Copy link
Member

roberth commented Aug 29, 2021

There's #5192 now, which builds on the ideas from this PR and is more thorough.

@manveru
Copy link
Contributor Author

manveru commented Aug 29, 2021

Awesome, that looks way better!
I'll close this PR then.

@manveru manveru closed this Aug 29, 2021
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 this pull request may close these issues.

6 participants