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

mkDerivation, bintools-wrapper: move defaultHardeningFlags determination to bintools-wrapper #259070

Merged
merged 2 commits into from
Dec 10, 2023

Conversation

risicle
Copy link
Contributor

@risicle risicle commented Oct 4, 2023

Description of changes

New hardening flags on the horizon look like they're going to be a bit less "universal" and possibly have more tradeoffs (CET/CFI et al). So it will be useful to make custom selection of hardening flags as convenient as possible to work with. It would also be nice to make it easier for users to experiment with lesser-used hardening schemes in the hopes they'll get more exposure and bug squashing.

mkDerivation has always been an awkward place for defaultHardeningFlags to be determined - these changes make it a lot easier to create a modified stdenv with a different set of defaultHardeningFlags and as a bonus they allow us to inject the correct defaultHardeningFlags into toolchain wrapper scripts, reducing repetition.

While most hardening flags are arguably more of a c-compiler thing, it works better to put them in bintools-wrapper because cc-wrapper can easily refer to bintools but not vice-versa.

mkDerivation can still easily refer to either when it is constructed.

Note this is still different from a compiler's "supported" hardening flags (expressed via cc.hardeningUnsupportedFlags) which are used to filter out particular hardening flags at invocation time.

This also adds stdenvAdapters.withDefaultHardeningFlags as a demonstration of the new abilities.

Things done

  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandbox = true set in nix.conf? (See Nix manual)
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 23.11 Release Notes (or backporting 23.05 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

@LunNova
Copy link
Member

LunNova commented Oct 12, 2023

Is there any advantage to keeping defaults that get used in the build-support scripts when NIX_HARDENING_ENABLE is unset rather than unconditionally setting it? Seems more complicated substituting it into them like this.

@LunNova
Copy link
Member

LunNova commented Oct 12, 2023

@ofborg build stdenv.cc bash

@LunNova
Copy link
Member

LunNova commented Oct 12, 2023

Looks like mismatched defaults are still sneaking through in pkgs/build-support/cc-wrapper/fortran-hook.sh

@risicle
Copy link
Contributor Author

risicle commented Oct 12, 2023

Is there any advantage to keeping defaults that get used in the build-support scripts when NIX_HARDENING_ENABLE is unset rather than unconditionally setting it? Seems more complicated substituting it into them like this.

🤔 Defaults need to exist at some level (even if those defaults are "empty").

And remember, the cc & bintools wrappers don't just get used via mkDerivation - they are also used whenever a user uses a nix-supplied compiler. Deciding that in a vanilla shell environment, a nix-supplied compiler won't add any extra hardening flags would be a significant behaviour change. And significant behaviour changes are something I'm trying to avoid.

Looks like mismatched defaults are still sneaking through in pkgs/build-support/cc-wrapper/fortran-hook.sh

Damn that complicates things.

@LunNova
Copy link
Member

LunNova commented Oct 12, 2023

So we have to substitute in to keep them in sync for usage in shells rather than package builds because otherwise those would have no default at all, got it.

I still think we should unconditionally set NIX_HARDENING_ENABLE in stdenv.mkDerivation on top of this change that substitutes in the correct defaults.
The current situation where it gets set for musl stdenvs or if either of hardeningDisable/hardeningEnable have something in them feels really janky. Is there any benefit we get out of that arrangement?

@risicle
Copy link
Contributor Author

risicle commented Oct 12, 2023

Looks like mismatched defaults are still sneaking through in pkgs/build-support/cc-wrapper/fortran-hook.sh

This is a problem for your PR too, because having an always-set NIX_HARDENING_ENABLE would make the for fortran the same as for the c compiler, which they're (apparently) deliberately not.

@risicle
Copy link
Contributor Author

risicle commented Oct 12, 2023

Is there any benefit we get out of that arrangement?

I think the only one is that it allows the set of default hardening flags to be different for fortran, but this of course falls apart the second someone sets e.g. hardeningEnable and it reverts to a NIX_HARDENING_ENABLE based on the mkDerivation defaults.

@LunNova
Copy link
Member

LunNova commented Oct 12, 2023

#88449 / abfbbb7 seems to be the reason fortran uses different flag defaults like this. It's very fragile right now.

@risicle
Copy link
Contributor Author

risicle commented Oct 13, 2023

It looks to me like what they really wanted to do was filter out fortify and format from the fortran wrapper instead of just removing them from the defaults. And that's fine by me because it would be simpler for us to implement.

From all I can find, fortify and format aren't really applicable to fortran anyway.

@LunNova
Copy link
Member

LunNova commented Oct 13, 2023

It looks like filtering out the flags from NIX_HARDENING_ENABLE means having fortran and c builds in the same shell will make the c build less secure, so it seems incorrect to do that too.

Would it make sense to filter out incompatible flags for a particular language at the point of use instead?

edit: I'm struggling to understand how the hardening flags work with fortran. add-hardening.sh adds the flags to hardeningCFlagsBefore/After. cc-wrapper.sh then uses those in the actual compiler call. Do gfortan calls still go through cc-wrapper.sh? Can we tell at the right point if a particular invocation is for fortran and remove incompatible flags there?

@LunNova
Copy link
Member

LunNova commented Oct 13, 2023

#92412 (comment) - so let's test blas / lapack cross-compile

@risicle
Copy link
Contributor Author

risicle commented Oct 13, 2023

I'm struggling to understand how the hardening flags work with fortran.

Yeah I was too.

Do gfortan calls still go through cc-wrapper.sh

Yes looks like it :)

Can we tell at the right point if a particular invocation is for fortran and remove incompatible flags there?

Maybe. I think at this point it's a matter of seeing which is the least-ugly option.

Then there is of course the hardening_unsupported_flags/cc.hardeningUnsupportedFlags mechanism already present for doing flag filtering, which could surely be repurposed for this.

@risicle risicle force-pushed the ris-cc-default-hardening-flags branch from 6606a10 to 76f7703 Compare October 14, 2023 19:49
Comment on lines 215 to +277
inherit expand-response-params;

inherit nixSupport;

inherit defaultHardeningFlags;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
inherit expand-response-params;
inherit nixSupport;
inherit defaultHardeningFlags;
inherit expand-response-params nixSupport defaultHardeningFlags;

nit

!(stdenv.hostPlatform.isAarch && stdenv.hostPlatform.isStatic)
then supportedHardeningFlags
else lib.remove "pie" supportedHardeningFlags;
knownHardeningFlags = [
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
knownHardeningFlags = [
// when updating consider update the default list in bintoos-wrapper
knownHardeningFlags = [

or similar

!(stdenv.hostPlatform.isAarch && stdenv.hostPlatform.isStatic)
then supportedHardeningFlags
else lib.remove "pie" supportedHardeningFlags;
knownHardeningFlags = [
Copy link
Member

Choose a reason for hiding this comment

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

While we're at it, maybe we could also extract hardcoded constants in add-hardening.sh?

declare -a allHardeningFlags=(fortify stackprotector pie pic strictoverflow format)

declare -a allHardeningFlags=(pie relro bindnow)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Flippin heck I forgot about those.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Honestly think this is too tricky to do without introducing a bizarre two-way dependency between mkDerivation and the wrappers. And attempting to bring the knownHardeningFlags into the wrappers alongside defaultHardeningFlags causes all sorts of problems around bootstrapping and use of unwrapped compilers/binutils because all of a sudden mkDerivation has an empty knownHardeningFlags which causes it to throw errors if any derivation happens to use hardeningDisable or hardeningEnable.

@risicle risicle force-pushed the ris-cc-default-hardening-flags branch from 76f7703 to 52b481a Compare October 21, 2023 16:51
Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

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

While most hardening flags are arguably more of a c-compiler thing, it works better to put them in bintools-wrapper

I'm okay with this if there are any (even just one) hardening flags that are passed to the linker rather than the c-compiler. Is there such a flag?

If the linker really handles zero hardening flags, and is unlikely to handle any in the future, then I think the bintools-wrapper is a pretty unintuitive place to stash this default.

because cc-wrapper can easily refer to bintools but not vice-versa.

Why not make it a top-level non-derivation attribute in the packageset like pkgs.default-gcc-version? Then it can be overridden with a simple overlay rather than having to build a whole new stdenv.

See also my comment below; even if we keep the defaultHardeningFlags in the bintools-wrapper we should make sure the defaulting logic happens in Nix rather than bash so you can change the defaults without rebuilding gcc.

@risicle
Copy link
Contributor Author

risicle commented Oct 22, 2023

I'm okay with this if there are any (even just one) hardening flags that are passed to the linker rather than the c-compiler. Is there such a flag?

There are three already and likely more to come.

Why not make it a top-level non-derivation attribute in the packageset like pkgs.default-gcc-version? Then it can be overridden with a simple overlay rather than having to build a whole new stdenv.

It doesn't feel like there's a huge amount of prior art for polluting pkgs with build-related settings. default-gcc-version feels like it's there because it's used in all-packages.nix itself.

@risicle risicle force-pushed the ris-cc-default-hardening-flags branch 2 times, most recently from 8354218 to 49bb1b7 Compare October 23, 2023 19:01
@risicle
Copy link
Contributor Author

risicle commented Oct 23, 2023

Thanks for the reviews - my instinct is that it's too close to the release to merge kind of change but I don't know what others think.

@ghost
Copy link

ghost commented Oct 23, 2023

Thanks for the reviews - my instinct is that it's too close to the release to merge kind of change but I don't know what others think.

I say go for it, and tell @vcunat that if it causes problems just revert it (i.e. if it does cause problems it isn't worth spending significant amount of time to fix them).

@vcunat
Copy link
Member

vcunat commented Oct 24, 2023

I wouldn't merge it for 23.11 (anymore). Certainly not with the only argument that we can revert. There are reasons why we have merging restrictions #258640 (comment)

@ghost
Copy link

ghost commented Oct 24, 2023

There are reasons why we have merging restrictions

This is not a breaking change.

But waiting until after 23.11 is fine too.

@@ -32,7 +32,7 @@ if [[ -n "${hardeningEnableMap[fortify3]-}" ]]; then
fi

if (( "${NIX_DEBUG:-0}" >= 1 )); then
declare -a allHardeningFlags=(fortify stackprotector pie pic strictoverflow format)
declare -a allHardeningFlags=(fortify fortify3 stackprotector pie pic strictoverflow format)
Copy link
Contributor

Choose a reason for hiding this comment

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

is relro not here on purpose?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is a very good question. It predates me, but it's also missing bindnow so presumably this debug statement was meant to only print the cc-wrapper-related flags and not the bintools ones?

Copy link

@ghost ghost Nov 30, 2023

Choose a reason for hiding this comment

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

BTW it would be awesome if this list of flags could be changed without causing rebuilds for NIX_DEBUG=0 (which is the default).

Edit: nevermind, this is cc-wrapper, I thought it was bintools-wrapper. I'm working on a PR which will accomplish this.

@ghost
Copy link

ghost commented Nov 30, 2023

Now that staging is branched off we should merge this.

Are there any objections?

@risicle
Copy link
Contributor Author

risicle commented Nov 30, 2023

I'd prefer to wait for the dust to settle a bit more after the release (and for this have more of my attention in case it causes any fallout)

…ion to bintools-wrapper

this makes it a lot easier to create a modified stdenv with a
different set of defaultHardeningFlags and as a bonus allows us
to inject the correct defaultHardeningFlags into toolchain wrapper
scripts, reducing repetition.

while most hardening flags are arguably more of a compiler thing,
it works better to put them in bintools-wrapper because cc-wrapper
can easily refer to bintools but not vice-versa.

mkDerivation can still easily refer to either when it is constructed.

this also switches fortran-hook.sh to use the same defaults for
NIX_HARDENING_ENABLE as for C. previously NIX_HARDENING_ENABLE
defaults were apparently used to avoid passing problematic flags
to a fortran compiler, but this falls apart as soon as mkDerivation
sets its own NIX_HARDENING_ENABLE - cc.hardeningUnsupportedFlags
is a more appropriate mechanism for this as it actively filters
out flags from being used by the wrapper, so switch to using that
instead.

this is still an imperfect mechanism because it doesn't handle a
compiler which has both langFortran *and* langC very well - applying
the superset of the two's hardeningUnsupportedFlags to either
compiler's invocation. however this is nothing new - cc-wrapper
already poorly handles a langFortran+langC compiler, applying two
setup hooks that have contradictory options.
@risicle risicle force-pushed the ris-cc-default-hardening-flags branch from 49bb1b7 to dc2247a Compare December 9, 2023 16:45
@risicle
Copy link
Contributor Author

risicle commented Dec 9, 2023

Let's do this.

Am currently re-testing on macos and linux.

@risicle
Copy link
Contributor Author

risicle commented Dec 10, 2023

Cherry-picking #253186 (which could also do with review) to correct the tests, tests.hardeningFlags do the expected thing for me on nixos x86_64, pkgsCross.aarch64-multiplatform, pkgsLLVM and pkgsi686Linux variants (required packages for those tests appear broken on pkgsMusl & pkgsStatic for now). tests.hardeningFlags tests that are expected to work on darwin (*ExecTest) do on macos 12 x86_64.

@risicle risicle merged commit aac4bc3 into NixOS:staging Dec 10, 2023
18 checks passed
@risicle
Copy link
Contributor Author

risicle commented Dec 12, 2023

A release-notes entry for this: #273840

@sternenseemann
Copy link
Member

Fix for eval regression: #280537

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants