From c47f2452530b9cdb58de578f1c44c37e8aa616f0 Mon Sep 17 00:00:00 2001 From: emilylange Date: Sat, 16 Sep 2023 14:20:56 +0200 Subject: [PATCH] patch-shebangs: fix crash with shebang without trailing newline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a bug where `patchShebangs` crashes when trying to patch files that contain only a shebang (e.g. `#!/bin/bash`) (and nothing else) and do not end with a newline. Such file can be produced using `printf "#!/bin/bash" > example` or `echo -n "#!/bin/bash" > example`. I don't understand why one would want to create such files, as they do literally nothing, but the chromium tarball we are using started shipping some 🫠 Full reproducer: ```nix with import { }; stdenv.mkDerivation { dontUnpack = true; name = "patch-shebangs-no-trailing-newline-reproducer"; postPatch = '' printf "#!/bin/bash" > reproducer chmod +x reproducer patchShebangs reproducer ''; } ``` ``` ❯ nix-build reproducer.nix this derivation will be built: /nix/store/vmbshdkdk4a0bayw3wi21wvxyhzpcsy2-patch-shebangs-no-trailing-newline-reproducer.drv building '/nix/store/vmbshdkdk4a0bayw3wi21wvxyhzpcsy2-patch-shebangs-no-trailing-newline-reproducer.drv'... patching sources patching script interpreter paths in reproducer /nix/store/vr6wwdxkmyy44sg0gwxi10b8fc5zhwz0-stdenv-linux/setup: line 144: pop_var_context: head of shell_variables not a function context error: builder for '/nix/store/vmbshdkdk4a0bayw3wi21wvxyhzpcsy2-patch-shebangs-no-trailing-newline-reproducer.drv' failed with exit code 1; last 3 log lines: > patching sources > patching script interpreter paths in reproducer > /nix/store/vr6wwdxkmyy44sg0gwxi10b8fc5zhwz0-stdenv-linux/setup: line 144: pop_var_context: head of shell_variables not a function context For full logs, run 'nix log /nix/store/vmbshdkdk4a0bayw3wi21wvxyhzpcsy2-patch-shebangs-no-trailing-newline-reproducer.drv'. ``` --- .../build-support/setup-hooks/patch-shebangs.sh | 5 ++++- pkgs/test/stdenv/patch-shebangs.nix | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/pkgs/build-support/setup-hooks/patch-shebangs.sh b/pkgs/build-support/setup-hooks/patch-shebangs.sh index a22f989362c4..80a29d727c85 100644 --- a/pkgs/build-support/setup-hooks/patch-shebangs.sh +++ b/pkgs/build-support/setup-hooks/patch-shebangs.sh @@ -72,7 +72,10 @@ patchShebangs() { while IFS= read -r -d $'\0' f; do isScript "$f" || continue - read -r oldInterpreterLine < "$f" + # read exits unclean if the shebang does not end with a newline, but still assigns the variable. + # So if read returns errno != 0, we check if the assigned variable is non-empty and continue. + read -r oldInterpreterLine < "$f" || [ "$oldInterpreterLine" ] + read -r oldPath arg0 args <<< "${oldInterpreterLine:2}" if [[ -z "${pathName:-}" ]]; then diff --git a/pkgs/test/stdenv/patch-shebangs.nix b/pkgs/test/stdenv/patch-shebangs.nix index 888d4a53a273..db9ca2fcaafe 100644 --- a/pkgs/test/stdenv/patch-shebangs.nix +++ b/pkgs/test/stdenv/patch-shebangs.nix @@ -72,11 +72,26 @@ let }; }; + without-trailing-newline = stdenv.mkDerivation { + name = "without-trailing-newline"; + strictDeps = false; + dontUnpack = true; + installPhase = '' + mkdir -p $out/bin + printf "#!/bin/bash" > $out/bin/test + chmod +x $out/bin/test + dontPatchShebangs= + ''; + passthru = { + assertion = "grep '^#!${stdenv.shell}' $out/bin/test > /dev/null"; + }; + }; + }; in stdenv.mkDerivation { name = "test-patch-shebangs"; - passthru = { inherit (tests) bad-shebang ignores-nix-store updates-nix-store split-string; }; + passthru = { inherit (tests) bad-shebang ignores-nix-store updates-nix-store split-string without-trailing-newline; }; buildCommand = '' validate() { local name=$1