-
-
Notifications
You must be signed in to change notification settings - Fork 18.1k
hardening: fix #18995 #28029
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
hardening: fix #18995 #28029
Changes from all commits
9fe17b2
9783a67
0937df4
9920923
cc7ce57
fc46895
634c748
806edaa
273ce83
386e77d
4c76d87
2364c22
21818ae
ac4d74b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,53 +1,58 @@ | ||
| hardeningFlags=(relro bindnow) | ||
| # Intentionally word-split in case 'hardeningEnable' is defined in | ||
| # Nix. Also, our bootstrap tools version of bash is old enough that | ||
| # undefined arrays trip `set -u`. | ||
| if [[ -v hardeningEnable[@] ]]; then | ||
| hardeningFlags+=(${hardeningEnable[@]}) | ||
| fi | ||
| hardeningLDFlags=() | ||
| declare -a hardeningLDFlags=() | ||
|
|
||
| declare -A hardeningEnableMap=() | ||
|
|
||
| declare -A hardeningDisableMap | ||
| # Intentionally word-split in case 'NIX_HARDENING_ENABLE' is defined in Nix. The | ||
| # array expansion also prevents undefined variables from causing trouble with | ||
| # `set -u`. | ||
| for flag in ${NIX_@infixSalt@_HARDENING_ENABLE-}; do | ||
| hardeningEnableMap["$flag"]=1 | ||
| done | ||
|
|
||
| # Intentionally word-split in case 'hardeningDisable' is defined in Nix. | ||
| for flag in ${hardeningDisable[@]:-IGNORED_KEY} @hardening_unsupported_flags@ | ||
| do | ||
| hardeningDisableMap[$flag]=1 | ||
| # Remove unsupported flags. | ||
| for flag in @hardening_unsupported_flags@; do | ||
| unset -v hardeningEnableMap["$flag"] | ||
| done | ||
|
|
||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then | ||
| declare -a allHardeningFlags=(pie relro bindnow) | ||
| declare -A hardeningDisableMap=() | ||
|
|
||
| # Determine which flags were effectively disabled so we can report below. | ||
| for flag in "${allHardeningFlags[@]}"; do | ||
| if [[ -z "${hardeningEnableMap[$flag]-}" ]]; then | ||
| hardeningDisableMap[$flag]=1 | ||
| fi | ||
| done | ||
|
|
||
| printf 'HARDENING: disabled flags:' >&2 | ||
| (( "${#hardeningDisableMap[@]}" )) && printf ' %q' "${!hardeningDisableMap[@]}" >&2 | ||
| echo >&2 | ||
| fi | ||
|
|
||
| if [[ -z "${hardeningDisableMap[all]:-}" ]]; then | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then | ||
| if (( "${#hardeningEnableMap[@]}" )); then | ||
| echo 'HARDENING: Is active (not completely disabled with "all" flag)' >&2; | ||
| fi | ||
| for flag in "${hardeningFlags[@]}" | ||
| do | ||
| if [[ -z "${hardeningDisableMap[$flag]:-}" ]]; then | ||
| case $flag in | ||
| pie) | ||
| if [[ ! ("$*" =~ " -shared " || "$*" =~ " -static ") ]]; then | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling LDFlags -pie >&2; fi | ||
| hardeningLDFlags+=('-pie') | ||
| fi | ||
| ;; | ||
| relro) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling relro >&2; fi | ||
| hardeningLDFlags+=('-z' 'relro') | ||
| ;; | ||
| bindnow) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling bindnow >&2; fi | ||
| hardeningLDFlags+=('-z' 'now') | ||
| ;; | ||
| *) | ||
| # Ignore unsupported. Checked in Nix that at least *some* | ||
| # tool supports each flag. | ||
| ;; | ||
| esac | ||
| fi | ||
| done | ||
| fi | ||
|
|
||
| for flag in "${!hardeningEnableMap[@]}"; do | ||
| case $flag in | ||
| pie) | ||
| if [[ ! ("$*" =~ " -shared " || "$*" =~ " -static ") ]]; then | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling LDFlags -pie >&2; fi | ||
| hardeningLDFlags+=('-pie') | ||
| fi | ||
| ;; | ||
| relro) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling relro >&2; fi | ||
| hardeningLDFlags+=('-z' 'relro') | ||
| ;; | ||
| bindnow) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling bindnow >&2; fi | ||
| hardeningLDFlags+=('-z' 'now') | ||
| ;; | ||
| *) | ||
| # Ignore unsupported. Checked in Nix that at least *some* | ||
| # tool supports each flag. | ||
| ;; | ||
| esac | ||
| done |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,8 +57,8 @@ fi | |
|
|
||
| source @out@/nix-support/add-hardening.sh | ||
|
|
||
| extraAfter=("${hardeningLDFlags[@]}") | ||
| extraBefore=() | ||
| extraAfter=() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really need to go to sleep so feel free to try it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can the hardening flags be injected at the start instead of the end? I'd hate to see us reintroduce the problems that prompted this PR in the first place. |
||
| extraBefore=(${hardeningLDFlags[@]+"${hardeningLDFlags[@]}"}) | ||
|
|
||
| if [ -z "${NIX_@infixSalt@_LDFLAGS_SET:-}" ]; then | ||
| extraAfter+=($NIX_@infixSalt@_LDFLAGS) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,67 +1,72 @@ | ||
| hardeningFlags=(fortify stackprotector pic strictoverflow format relro bindnow) | ||
| # Intentionally word-split in case 'hardeningEnable' is defined in | ||
| # Nix. Also, our bootstrap tools version of bash is old enough that | ||
| # undefined arrays trip `set -u`. | ||
| if [[ -v hardeningEnable[@] ]]; then | ||
| hardeningFlags+=(${hardeningEnable[@]}) | ||
| fi | ||
| hardeningCFlags=() | ||
| declare -a hardeningCFlags=() | ||
|
|
||
| declare -A hardeningEnableMap=() | ||
|
|
||
| declare -A hardeningDisableMap | ||
| # Intentionally word-split in case 'NIX_HARDENING_ENABLE' is defined in Nix. The | ||
| # array expansion also prevents undefined variables from causing trouble with | ||
| # `set -u`. | ||
| for flag in ${NIX_@infixSalt@_HARDENING_ENABLE-}; do | ||
| hardeningEnableMap["$flag"]=1 | ||
| done | ||
|
|
||
| # Intentionally word-split in case 'hardeningDisable' is defined in Nix. | ||
| for flag in ${hardeningDisable[@]:-IGNORED_KEY} @hardening_unsupported_flags@ | ||
| do | ||
| hardeningDisableMap[$flag]=1 | ||
| # Remove unsupported flags. | ||
| for flag in @hardening_unsupported_flags@; do | ||
| unset -v hardeningEnableMap["$flag"] | ||
| done | ||
|
|
||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then | ||
| declare -a allHardeningFlags=(fortify stackprotector pie pic strictoverflow format) | ||
| declare -A hardeningDisableMap=() | ||
|
|
||
| # Determine which flags were effectively disabled so we can report below. | ||
| for flag in "${allHardeningFlags[@]}"; do | ||
| if [[ -z "${hardeningEnableMap[$flag]-}" ]]; then | ||
| hardeningDisableMap["$flag"]=1 | ||
| fi | ||
| done | ||
|
|
||
| printf 'HARDENING: disabled flags:' >&2 | ||
| (( "${#hardeningDisableMap[@]}" )) && printf ' %q' "${!hardeningDisableMap[@]}" >&2 | ||
| echo >&2 | ||
| fi | ||
|
|
||
| if [[ -z "${hardeningDisableMap[all]:-}" ]]; then | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then | ||
| if (( "${#hardeningEnableMap[@]}" )); then | ||
| echo 'HARDENING: Is active (not completely disabled with "all" flag)' >&2; | ||
| fi | ||
| for flag in "${hardeningFlags[@]}" | ||
| do | ||
| if [[ -z "${hardeningDisableMap[$flag]:-}" ]]; then | ||
| case $flag in | ||
| fortify) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling fortify >&2; fi | ||
| hardeningCFlags+=('-O2' '-D_FORTIFY_SOURCE=2') | ||
| ;; | ||
| stackprotector) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling stackprotector >&2; fi | ||
| hardeningCFlags+=('-fstack-protector-strong' '--param' 'ssp-buffer-size=4') | ||
| ;; | ||
| pie) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling CFlags -fPIE >&2; fi | ||
| hardeningCFlags+=('-fPIE') | ||
| if [[ ! ("$*" =~ " -shared " || "$*" =~ " -static ") ]]; then | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling LDFlags -pie >&2; fi | ||
| hardeningCFlags+=('-pie') | ||
| fi | ||
| ;; | ||
| pic) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling pic >&2; fi | ||
| hardeningCFlags+=('-fPIC') | ||
| ;; | ||
| strictoverflow) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling strictoverflow >&2; fi | ||
| hardeningCFlags+=('-fno-strict-overflow') | ||
| ;; | ||
| format) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling format >&2; fi | ||
| hardeningCFlags+=('-Wformat' '-Wformat-security' '-Werror=format-security') | ||
| ;; | ||
| *) | ||
| # Ignore unsupported. Checked in Nix that at least *some* | ||
| # tool supports each flag. | ||
| ;; | ||
| esac | ||
| fi | ||
| done | ||
| fi | ||
|
|
||
| for flag in "${!hardeningEnableMap[@]}"; do | ||
| case $flag in | ||
| fortify) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling fortify >&2; fi | ||
| hardeningCFlags+=('-O2' '-D_FORTIFY_SOURCE=2') | ||
| ;; | ||
| stackprotector) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling stackprotector >&2; fi | ||
| hardeningCFlags+=('-fstack-protector-strong' '--param' 'ssp-buffer-size=4') | ||
| ;; | ||
| pie) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling CFlags -fPIE >&2; fi | ||
| hardeningCFlags+=('-fPIE') | ||
| if [[ ! ("$*" =~ " -shared " || "$*" =~ " -static ") ]]; then | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling LDFlags -pie >&2; fi | ||
| hardeningCFlags+=('-pie') | ||
| fi | ||
| ;; | ||
| pic) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling pic >&2; fi | ||
| hardeningCFlags+=('-fPIC') | ||
| ;; | ||
| strictoverflow) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling strictoverflow >&2; fi | ||
| hardeningCFlags+=('-fno-strict-overflow') | ||
| ;; | ||
| format) | ||
| if (( "${NIX_DEBUG:-0}" >= 1 )); then echo HARDENING: enabling format >&2; fi | ||
| hardeningCFlags+=('-Wformat' '-Wformat-security' '-Werror=format-security') | ||
| ;; | ||
| *) | ||
| # Ignore unsupported. Checked in Nix that at least *some* | ||
| # tool supports each flag. | ||
| ;; | ||
| esac | ||
| done |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -147,6 +147,10 @@ export ${role_pre}CXX=@named_cxx@ | |
| export CC${role_post}=@named_cc@ | ||
| export CXX${role_post}=@named_cxx@ | ||
|
|
||
| # If unset, assume the default hardening flags. | ||
| : ${NIX_HARDENING_ENABLE="fortify stackprotector pic strictoverflow format relro bindnow"} | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When I split up into two variables, this will contain just the flags that make sense for the cc wrapper. Similarly for the bintools' setup-hook.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (For posterity: per IRC convo, I not sure if we're going with the split variables over the unified variable approach. In light of this, the above comment might be misleading.) |
||
| export NIX_HARDENING_ENABLE | ||
|
|
||
| # No local scope in sourced file | ||
| unset -v role_pre role_post | ||
| set +u | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -74,6 +74,11 @@ rec { | |
| # TODO(@Ericson2314): Make this more modular, and not O(n^2). | ||
| let | ||
| supportedHardeningFlags = [ "fortify" "stackprotector" "pie" "pic" "strictoverflow" "format" "relro" "bindnow" ]; | ||
| defaultHardeningFlags = lib.remove "pie" supportedHardeningFlags; | ||
| enabledHardeningOptions = | ||
| if builtins.elem "all" hardeningDisable | ||
| then [] | ||
| else lib.subtractLists hardeningDisable (defaultHardeningFlags ++ hardeningEnable); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hopefully a little cleaner than before. |
||
| # hardeningDisable additionally supports "all". | ||
| erroneousHardeningFlags = lib.subtractLists supportedHardeningFlags (hardeningEnable ++ lib.remove "all" hardeningDisable); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cstrahan does this overlap with the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Ericson2314 Overlap in what way? I think they're distinct uses (one to check for erroneous flags, the other to calculate the effective flags). |
||
| in if builtins.length erroneousHardeningFlags != 0 | ||
|
|
@@ -179,6 +184,8 @@ rec { | |
| ++ optional (elem "host" configurePlatforms) "--host=${stdenv.hostPlatform.config}" | ||
| ++ optional (elem "target" configurePlatforms) "--target=${stdenv.targetPlatform.config}"; | ||
|
|
||
| } // lib.optionalAttrs (hardeningDisable != [] || hardeningEnable != []) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIRC doing this sort of stuff on the Nix level is somewhat discouraged since it slows down evaluation of Nixpkgs.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, more bash causes other problems too. I don't think its so clear cut. |
||
| NIX_HARDENING_ENABLE = enabledHardeningOptions; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Ericson2314 Regarding your comment regarding
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Now that there's a bintools wrapper too, we would have to do an analogous thing there with this plan. So OTOH if we want just one variable with both, then maybe we shouldn't add the |
||
| } // lib.optionalAttrs (stdenv.buildPlatform.isDarwin) { | ||
| # TODO: remove lib.unique once nix has a list canonicalization primitive | ||
| __sandboxProfile = | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you copy the
+thing or did you find it was needed? No bash can tell the difference between empty and undefined arrays. Old bash needs your thing withset -ufor empty and undefined alike, but new bash doesn't for empty and undefined alike.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, got errors due to
role_infixesbeing undefined. Happened while buildinglinuxHeaders, though might have been poked due to that in combination with some other element I'm missing. But yeah, this line failed otherwise.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK sounds good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Err is this a thing or did you mean ":+" instead of "+"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dtzWill Yeah. Per the docs:
https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html#Shell-Parameter-Expansion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL! Thanks! 👍