From 61c74e1aee1b7fb35ca3c550f57286cf85999e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sat, 22 May 2021 10:01:09 +0200 Subject: [PATCH 1/3] glibc: allow to build position-independent static executable This enables ALSR on static executables, which makes them harder to exploit by providing a crt suitable for static PIEs. Does this break existing binaries? Likely not. Static-pie is only used if explicitly enabled. --- pkgs/development/libraries/glibc/common.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/development/libraries/glibc/common.nix b/pkgs/development/libraries/glibc/common.nix index 0f580baba33f..d1c224093bbf 100644 --- a/pkgs/development/libraries/glibc/common.nix +++ b/pkgs/development/libraries/glibc/common.nix @@ -153,6 +153,8 @@ stdenv.mkDerivation ({ "--enable-add-ons" "--sysconfdir=/etc" "--enable-stackguard-randomization" + "--enable-static-pie" + "--enable-bind-now" (lib.withFeatureAs withLinuxHeaders "headers" "${linuxHeaders}/include") (lib.enableFeature profilingLibraries "profile") ] ++ lib.optionals withLinuxHeaders [ From 166948d4794be5c56c6279bf580ac416b1bcf022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sat, 22 May 2021 13:55:47 +0200 Subject: [PATCH 2/3] cc-wrapper: don't set rpath on static-pie executables --- .../bintools-wrapper/ld-wrapper.sh | 33 +++++++++-------- pkgs/build-support/cc-wrapper/cc-wrapper.sh | 15 ++++---- pkgs/build-support/wrapper-common/utils.bash | 35 +++++++++++++++++++ pkgs/test/cc-wrapper/default.nix | 3 +- 4 files changed, 62 insertions(+), 24 deletions(-) diff --git a/pkgs/build-support/bintools-wrapper/ld-wrapper.sh b/pkgs/build-support/bintools-wrapper/ld-wrapper.sh index e54dd6f47146..58ddc2aee909 100644 --- a/pkgs/build-support/bintools-wrapper/ld-wrapper.sh +++ b/pkgs/build-support/bintools-wrapper/ld-wrapper.sh @@ -20,15 +20,23 @@ if [ -z "${NIX_BINTOOLS_WRAPPER_FLAGS_SET_@suffixSalt@:-}" ]; then source @out@/nix-support/add-flags.sh fi -setDynamicLinker=1 # Optionally filter out paths not refering to the store. expandResponseParams "$@" + +# NIX_LINK_TYPE is set if ld has been called through our cc wrapper +if [[ -n "${NIX_LINK_TYPE_@suffixSalt@:-}" ]]; then + linkType=$NIX_LINK_TYPE_@suffixSalt@ +else + linkType=$(checkLinkType "$@") +fi + if [[ "${NIX_ENFORCE_PURITY:-}" = 1 && -n "${NIX_STORE:-}" - && ( -z "$NIX_IGNORE_LD_THROUGH_GCC_@suffixSalt@" || -z "${NIX_LDFLAGS_SET_@suffixSalt@:-}" ) ]]; then + && ( -z "$NIX_IGNORE_LD_THROUGH_GCC_@suffixSalt@" || -z "${NIX_LINK_TYPE_@suffixSalt@:-}" ) ]]; then rest=() nParams=${#params[@]} declare -i n=0 + while (( "$n" < "$nParams" )); do p=${params[n]} p2=${params[n+1]:-} # handle `p` being last one @@ -48,11 +56,6 @@ if [[ "${NIX_ENFORCE_PURITY:-}" = 1 && -n "${NIX_STORE:-}" # Our ld is not built with sysroot support (Can we fix that?) : else - if [[ "$p" = -static || "$p" = -static-pie ]]; then - # Using a dynamic linker for static binaries can lead to crashes. - # This was observed for rust binaries. - setDynamicLinker=0 - fi rest+=("$p") fi n+=1 @@ -61,22 +64,24 @@ if [[ "${NIX_ENFORCE_PURITY:-}" = 1 && -n "${NIX_STORE:-}" params=(${rest+"${rest[@]}"}) fi + source @out@/nix-support/add-hardening.sh extraAfter=() extraBefore=(${hardeningLDFlags[@]+"${hardeningLDFlags[@]}"}) -if [ -z "${NIX_LDFLAGS_SET_@suffixSalt@:-}" ]; then - extraAfter+=($NIX_LDFLAGS_@suffixSalt@) - extraBefore+=($NIX_LDFLAGS_BEFORE_@suffixSalt@) +if [ -z "${NIX_LINK_TYPE_@suffixSalt@:-}" ]; then + extraAfter+=($(filterRpathFlags "$linkType" $NIX_LDFLAGS_@suffixSalt@)) + extraBefore+=($(filterRpathFlags "$linkType" $NIX_LDFLAGS_BEFORE_@suffixSalt@)) + # By adding dynamic linker to extraBefore we allow the users set their # own dynamic linker as NIX_LD_FLAGS will override earlier set flags - if [[ "$setDynamicLinker" = 1 && -n "$NIX_DYNAMIC_LINKER_@suffixSalt@" ]]; then + if [[ "$linkType" == dynamic && -n "$NIX_DYNAMIC_LINKER_@suffixSalt@" ]]; then extraBefore+=("-dynamic-linker" "$NIX_DYNAMIC_LINKER_@suffixSalt@") fi fi -extraAfter+=($NIX_LDFLAGS_AFTER_@suffixSalt@) +extraAfter+=($(filterRpathFlags "$linkType" $NIX_LDFLAGS_AFTER_@suffixSalt@)) # These flags *must not* be pulled up to -Wl, flags, so they can't go in # add-flags.sh. They must always be set, so must not be disabled by @@ -173,7 +178,7 @@ do prev="$p" done -if [[ "$link32" = "1" && "$setDynamicLinker" = 1 && -e "@out@/nix-support/dynamic-linker-m32" ]]; then +if [[ "$link32" == "1" && "$linkType" == dynamic && -e "@out@/nix-support/dynamic-linker-m32" ]]; then # We have an alternate 32-bit linker and we're producing a 32-bit ELF, let's # use it. extraAfter+=( @@ -183,7 +188,7 @@ if [[ "$link32" = "1" && "$setDynamicLinker" = 1 && -e "@out@/nix-support/dynami fi # Add all used dynamic libraries to the rpath. -if [ "$NIX_DONT_SET_RPATH_@suffixSalt@" != 1 ]; then +if [[ "$NIX_DONT_SET_RPATH_@suffixSalt@" != 1 && "$linkType" != static-pie ]]; then # For each directory in the library search path (-L...), # see if it contains a dynamic library used by a -l... flag. If # so, add the directory to the rpath. diff --git a/pkgs/build-support/cc-wrapper/cc-wrapper.sh b/pkgs/build-support/cc-wrapper/cc-wrapper.sh index 2e62aef46048..aa25de336418 100644 --- a/pkgs/build-support/cc-wrapper/cc-wrapper.sh +++ b/pkgs/build-support/cc-wrapper/cc-wrapper.sh @@ -29,9 +29,10 @@ cc1=0 cxxInclude=1 cxxLibrary=1 cInclude=1 -setDynamicLinker=1 expandResponseParams "$@" +linkType=$(checkLinkType "$@") + declare -i n=0 nParams=${#params[@]} while (( "$n" < "$nParams" )); do @@ -60,8 +61,6 @@ while (( "$n" < "$nParams" )); do cxxInclude=0 elif [ "$p" = -nostdinc++ ]; then cxxInclude=0 - elif [[ "$p" = -static || "$p" = -static-pie ]]; then - setDynamicLinker=0 elif [[ "$p" != -?* ]]; then # A dash alone signifies standard input; it is not a flag nonFlagArgs=1 @@ -151,24 +150,24 @@ if [ "$dontLink" != 1 ]; then # Add the flags that should only be passed to the compiler when # linking. - extraAfter+=($NIX_CFLAGS_LINK_@suffixSalt@) + extraAfter+=($(filterRpathFlags "$linkType" $NIX_CFLAGS_LINK_@suffixSalt@)) # Add the flags that should be passed to the linker (and prevent # `ld-wrapper' from adding NIX_LDFLAGS_@suffixSalt@ again). - for i in $NIX_LDFLAGS_BEFORE_@suffixSalt@; do + for i in $(filterRpathFlags "$linkType" $NIX_LDFLAGS_BEFORE_@suffixSalt@); do extraBefore+=("-Wl,$i") done - if [[ "$setDynamicLinker" = 1 && -n "$NIX_DYNAMIC_LINKER_@suffixSalt@" ]]; then + if [[ "$linkType" == dynamic && -n "$NIX_DYNAMIC_LINKER_@suffixSalt@" ]]; then extraBefore+=("-Wl,-dynamic-linker=$NIX_DYNAMIC_LINKER_@suffixSalt@") fi - for i in $NIX_LDFLAGS_@suffixSalt@; do + for i in $(filterRpathFlags "$linkType" $NIX_LDFLAGS_@suffixSalt@); do if [ "${i:0:3}" = -L/ ]; then extraAfter+=("$i") else extraAfter+=("-Wl,$i") fi done - export NIX_LDFLAGS_SET_@suffixSalt@=1 + export NIX_LINK_TYPE_@suffixSalt@=$linkType fi # As a very special hack, if the arguments are just `-v', then don't diff --git a/pkgs/build-support/wrapper-common/utils.bash b/pkgs/build-support/wrapper-common/utils.bash index 66b7c3f3e83c..a0e322520200 100644 --- a/pkgs/build-support/wrapper-common/utils.bash +++ b/pkgs/build-support/wrapper-common/utils.bash @@ -123,3 +123,38 @@ expandResponseParams() { fi done } + +checkLinkType() { + local arg mode + type="dynamic" + for arg in "$@"; do + if [[ "$arg" = -static ]]; then + type="static" + elif [[ "$arg" = -static-pie ]]; then + type="static-pie" + fi + done + echo "$type" +} + +# When building static-pie executables we cannot have rpath +# set. At least glibc requires rpath to be empty +filterRpathFlags() { + local linkType=$1 ret="" i + shift + + if [[ "$linkType" == "static-pie" ]]; then + while [[ "$#" -gt 0 ]]; do + i="$1"; shift 1 + if [[ "$i" == -rpath ]]; then + # also skip its argument + shift + else + ret+="$i " + fi + done + else + ret=$@ + fi + echo $ret +} diff --git a/pkgs/test/cc-wrapper/default.nix b/pkgs/test/cc-wrapper/default.nix index d82ba296e2fe..b483372dea04 100644 --- a/pkgs/test/cc-wrapper/default.nix +++ b/pkgs/test/cc-wrapper/default.nix @@ -35,8 +35,7 @@ in stdenv.mkDerivation { printf "checking whether compiler builds valid static C binaries... " >&2 $CC ${staticLibc} -static -o cc-static ${./cc-main.c} ./cc-static - # our glibc does not have pie enabled yet. - ${lib.optionalString (stdenv.hostPlatform.isMusl && stdenv.cc.isGNU) '' + ${lib.optionalString (stdenv.cc.isGNU && lib.versionAtLeast (lib.getVersion stdenv.cc.name) "8.0.0") '' printf "checking whether compiler builds valid static pie C binaries... " >&2 $CC ${staticLibc} -static-pie -o cc-static-pie ${./cc-main.c} ./cc-static-pie From 602b5f8747e63ff737d48e6f18c8fd35168a707c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Mon, 24 May 2021 15:44:01 +0100 Subject: [PATCH 3/3] Update pkgs/build-support/bintools-wrapper/ld-wrapper.sh Co-authored-by: John Ericson --- pkgs/build-support/bintools-wrapper/ld-wrapper.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkgs/build-support/bintools-wrapper/ld-wrapper.sh b/pkgs/build-support/bintools-wrapper/ld-wrapper.sh index 58ddc2aee909..2f96480f80c1 100644 --- a/pkgs/build-support/bintools-wrapper/ld-wrapper.sh +++ b/pkgs/build-support/bintools-wrapper/ld-wrapper.sh @@ -24,7 +24,9 @@ fi # Optionally filter out paths not refering to the store. expandResponseParams "$@" -# NIX_LINK_TYPE is set if ld has been called through our cc wrapper +# NIX_LINK_TYPE is set if ld has been called through our cc wrapper. We take +# advantage of this to avoid both recalculating it, and also repeating other +# processing cc wrapper has already done. if [[ -n "${NIX_LINK_TYPE_@suffixSalt@:-}" ]]; then linkType=$NIX_LINK_TYPE_@suffixSalt@ else