Summary
At revision 02da6aa26a44e5af2a67057876d7c6669a207f56, the -d path in glibc_run.sh rewrites line 1 of the tracked Dockerfile in place before building the Docker image. The edit occurs without a warning about the working-tree mutation, a prompt, a backup, a temporary copy, or a later restore.
On a pristine checkout, a requested glibc version greater than 2.33 changes Dockerfile:1 from from ubuntu:20.04 to from ubuntu:22.04. A version at or below 2.33 writes from ubuntu:20.04; that may leave the pristine file byte-identical, but it still overwrites any custom first line.
Steps to reproduce
On Linux with the script's prerequisites installed and Docker available:
git clone https://github.com/shellphish/how2heap
cd how2heap
git checkout 02da6aa26a44e5af2a67057876d7c6669a207f56
make malloc_playground
git status --short -- Dockerfile
./glibc_run.sh 2.39 ./malloc_playground -d -p
git diff -- Dockerfile
The initial status is clean for Dockerfile. The final diff shows Dockerfile:1 changed from from ubuntu:20.04 to from ubuntu:22.04. The -p option prevents the prepared target from being executed; it does not change the Docker preparation path.
As a red/green closure check, the same command should leave git diff --exit-code -- Dockerfile clean after the Docker build input is made temporary.
Expected behavior
The Docker preparation flow should leave the tracked Dockerfile unchanged. It can select the requested Ubuntu base image using a temporary Dockerfile passed with docker build -f, or otherwise preserve and restore the original bytes on both success and failure.
Actual behavior
glibc_run.sh:143-145 maps -d to DOCKER='X', and glibc_run.sh:198-200 then calls prep_in_docker "$GLIBC_VERSION". Inside that function, glibc_run.sh:87-92 selects Ubuntu 22.04 for versions greater than 2.33 and Ubuntu 20.04 otherwise. After the Docker availability check, glibc_run.sh:103 executes:
sed -i "1s/.*/from ubuntu:$UBUNTU_VERSION/" Dockerfile
The first subsequent message is building the how2heap_docker image! at glibc_run.sh:104, after the edit has already occurred. No later code restores Dockerfile, so a failure in docker build or docker run also leaves the rewritten first line in place.
Affected area
glibc_run.sh:85-108 (prep_in_docker)
glibc_run.sh:143-145 (-d argument handling)
glibc_run.sh:198-200 (call into Docker preparation)
Dockerfile:1 (tracked line rewritten in place)
Runtime or environment
- Source revision:
02da6aa26a44e5af2a67057876d7c6669a207f56
- Verification method: static source inspection; the script was not executed
- Relevant path: Bash with the GNU
sed -i behavior used by the Linux/Docker workflow
- Reachability condition: the target file and earlier glibc preparation steps must succeed, and the Docker check at
glibc_run.sh:95-100 must pass
Evidence
Dockerfile:1 is from ubuntu:20.04 at the verified revision.
- The mutation is exactly at
glibc_run.sh:103.
- The accepted
-d path sets DOCKER at glibc_run.sh:143-145 and reaches prep_in_docker at glibc_run.sh:198-200 after the script's earlier prerequisites succeed.
glibc_run.sh:21-28 describes -d only as building the debugging environment in Docker; it does not disclose that a tracked file is edited.
- The complete script contains no backup, restore, prompt, temporary Dockerfile, or cleanup path for this edit. Its only reference to
Dockerfile is the in-place sed command at line 103.
- The build announcement at
glibc_run.sh:104 is emitted after the edit and does not warn that the working tree was changed.
Impact
- Any uncommitted customization of
Dockerfile:1 is replaced without confirmation.
- On the pinned pristine revision, selecting a glibc version greater than 2.33 leaves a tracked diff that can be staged accidentally with unrelated work.
- The mutation happens before
docker build, so later build or run failure does not roll it back.
- Repeated runs with versions on opposite sides of 2.33 leave line 1 set by the most recent run; no previous first-line content is retained.
- A run for a version at or below 2.33 may produce no Git diff when line 1 already contains
from ubuntu:20.04; the destructive behavior remains observable when line 1 was customized.
Additional context
Root-cause classification: destructive in-place mutation of a tracked build input by a convenience wrapper.
A minimal patch can generate a temporary Dockerfile and pass it explicitly to Docker:
# Replace the in-place edit and docker build in prep_in_docker.
local dockerfile_tmp
dockerfile_tmp=$(mktemp) || return 1
trap 'rm -f "$dockerfile_tmp"' RETURN
sed "1s/.*/from ubuntu:$UBUNTU_VERSION/" Dockerfile >"$dockerfile_tmp" || return 1
echo "building the how2heap_docker image!"
docker build -f "$dockerfile_tmp" -t how2heap_docker .
glibc_run.sh:107 subsequently runs make clean all inside the container. Makefile:66 defines all, while Makefile:68-70 defines clean; neither target reads or edits Dockerfile. Passing a temporary file to docker build therefore isolates the fix from the existing Makefile build flow.
Maintainer checklist:
Verifier notes
The underlying claim is confirmed, but the original draft needed three material corrections. Its clean-clone reproduction did not create ./malloc_playground, so glibc_run.sh:119-122 would exit before argument handling and never reach the edit. The reproduction now builds that target and adds -p to avoid executing it. The impact is also narrowed to replacement of the first line, and the Git-dirty claim is limited to cases where the selected Ubuntu line differs from the current content. Unverified runtime and severity statements were replaced with the static verification boundary requested for this review.
Summary
At revision
02da6aa26a44e5af2a67057876d7c6669a207f56, the-dpath inglibc_run.shrewrites line 1 of the trackedDockerfilein place before building the Docker image. The edit occurs without a warning about the working-tree mutation, a prompt, a backup, a temporary copy, or a later restore.On a pristine checkout, a requested glibc version greater than 2.33 changes
Dockerfile:1fromfrom ubuntu:20.04tofrom ubuntu:22.04. A version at or below 2.33 writesfrom ubuntu:20.04; that may leave the pristine file byte-identical, but it still overwrites any custom first line.Steps to reproduce
On Linux with the script's prerequisites installed and Docker available:
git clone https://github.com/shellphish/how2heap cd how2heap git checkout 02da6aa26a44e5af2a67057876d7c6669a207f56 make malloc_playground git status --short -- Dockerfile ./glibc_run.sh 2.39 ./malloc_playground -d -p git diff -- DockerfileThe initial status is clean for
Dockerfile. The final diff showsDockerfile:1changed fromfrom ubuntu:20.04tofrom ubuntu:22.04. The-poption prevents the prepared target from being executed; it does not change the Docker preparation path.As a red/green closure check, the same command should leave
git diff --exit-code -- Dockerfileclean after the Docker build input is made temporary.Expected behavior
The Docker preparation flow should leave the tracked
Dockerfileunchanged. It can select the requested Ubuntu base image using a temporary Dockerfile passed withdocker build -f, or otherwise preserve and restore the original bytes on both success and failure.Actual behavior
glibc_run.sh:143-145maps-dtoDOCKER='X', andglibc_run.sh:198-200then callsprep_in_docker "$GLIBC_VERSION". Inside that function,glibc_run.sh:87-92selects Ubuntu 22.04 for versions greater than 2.33 and Ubuntu 20.04 otherwise. After the Docker availability check,glibc_run.sh:103executes:sed -i "1s/.*/from ubuntu:$UBUNTU_VERSION/" DockerfileThe first subsequent message is
building the how2heap_docker image!atglibc_run.sh:104, after the edit has already occurred. No later code restoresDockerfile, so a failure indocker buildordocker runalso leaves the rewritten first line in place.Affected area
glibc_run.sh:85-108(prep_in_docker)glibc_run.sh:143-145(-dargument handling)glibc_run.sh:198-200(call into Docker preparation)Dockerfile:1(tracked line rewritten in place)Runtime or environment
02da6aa26a44e5af2a67057876d7c6669a207f56sed -ibehavior used by the Linux/Docker workflowglibc_run.sh:95-100must passEvidence
Dockerfile:1isfrom ubuntu:20.04at the verified revision.glibc_run.sh:103.-dpath setsDOCKERatglibc_run.sh:143-145and reachesprep_in_dockeratglibc_run.sh:198-200after the script's earlier prerequisites succeed.glibc_run.sh:21-28describes-donly as building the debugging environment in Docker; it does not disclose that a tracked file is edited.Dockerfileis the in-placesedcommand at line 103.glibc_run.sh:104is emitted after the edit and does not warn that the working tree was changed.Impact
Dockerfile:1is replaced without confirmation.docker build, so later build or run failure does not roll it back.from ubuntu:20.04; the destructive behavior remains observable when line 1 was customized.Additional context
Root-cause classification: destructive in-place mutation of a tracked build input by a convenience wrapper.
A minimal patch can generate a temporary Dockerfile and pass it explicitly to Docker:
glibc_run.sh:107subsequently runsmake clean allinside the container.Makefile:66definesall, whileMakefile:68-70definesclean; neither target reads or editsDockerfile. Passing a temporary file todocker buildtherefore isolates the fix from the existing Makefile build flow.Maintainer checklist:
2.39-d -preproduction and confirmgit diff --exit-code -- Dockerfileremains clean.Dockerfileand confirm both successful and failed Docker builds preserve it byte-for-byte.make clean allflow.Verifier notes
The underlying claim is confirmed, but the original draft needed three material corrections. Its clean-clone reproduction did not create
./malloc_playground, soglibc_run.sh:119-122would exit before argument handling and never reach the edit. The reproduction now builds that target and adds-pto avoid executing it. The impact is also narrowed to replacement of the first line, and the Git-dirty claim is limited to cases where the selected Ubuntu line differs from the current content. Unverified runtime and severity statements were replaced with the static verification boundary requested for this review.