The manual EC2 benchmark workflow exposes free-form inputs that are intended to
be benchmark configuration, compiler options, and extra test arguments. The
workflow forwards those values into shell run blocks without first placing
them behind an argument boundary. A repository user with permission to dispatch
the manual benchmark workflow, or a maintainer who runs it with values copied
from an untrusted request, can therefore cause shell metacharacters in those
fields to execute on privileged CI runners.
The dispatchable workflow exposes cflags, archflags, ldflags,
bench_extra_args, and compiler as free-form inputs and forwards them into
the reusable EC2 benchmark workflow.
cflags:
description: Custom CFLAGS for compilation
default:
archflags:
description: Custom ARCH flags for compilation
default: ''
ldflags:
description: Custom LDFLAGS for linking
default: ''
# ...
bench_extra_args:
description: Additional command line to be appended to `tests bench` script
default: ''
compiler:
description: Compiler to use. When unset, default nix shell is used.
default: ''
# ...
cflags: ${{ inputs.cflags }}
archflags: ${{ inputs.archflags }}
ldflags: ${{ inputs.ldflags }}
# ...
bench_extra_args: ${{ inputs.bench_extra_args }}
compiler: ${{ inputs.compiler }}
Figure X.1: The manual EC2 benchmark workflow accepts and forwards free-form benchmark inputs (mldsa-native/.github/workflows/bench_ec2_any.yml#L29-L69)
The reusable workflow writes the compiler input directly into a shell command
that appends to $GITHUB_ENV. GitHub Actions substitutes the expression before
Bash parses the script, so characters such as quotes, semicolons, and command
substitutions are interpreted as shell syntax.
- name: Set compiler
run: |
echo "CC=${{ inputs.compiler }}" >> "$GITHUB_ENV"
Figure X.2: The reusable benchmark workflow interpolates compiler into a shell command (mldsa-native/.github/workflows/bench_ec2_reusable.yml#L182-L184)
The benchmark action also interpolates forwarded benchmark options directly
into two ./scripts/tests bench command lines. These jobs run on EC2-backed
self-hosted runners and use repository tokens for benchmark publication and
pull-request updates, so command execution in this path can affect CI state.
./scripts/tests bench -c ${{ inputs.perf }} --cross-prefix="${{ inputs.cross_prefix }}" \
--cflags="${{ inputs.cflags }} ${{ inputs.archflags }}" \
--ldflags="${{ inputs.ldflags }}" \
--opt=$([[ ${{ inputs.opt }} == "false" ]] && echo "no_opt" || echo "opt") \
-v --output=output.json ${{ inputs.bench_extra_args }}
./scripts/tests bench --components -c ${{ inputs.perf }} --cross-prefix="${{ inputs.cross_prefix }}" \
--cflags="${{ inputs.cflags }} ${{ inputs.archflags }}" \
--ldflags="${{ inputs.ldflags }}" \
--opt=$([[ ${{ inputs.opt }} == "false" ]] && echo "no_opt" || echo "opt") \
-v ${{ inputs.bench_extra_args }}
Figure X.3: The benchmark action interpolates free-form benchmark inputs into shell commands (mldsa-native/.github/actions/bench/action.yml#L80-L91)
The same data-to-code boundary appears in local developer tooling. The
formatter reads tracked filenames from Git and substitutes them into sh -c
script text. A contributor can place shell metacharacters in a tracked filename
on an untrusted branch, and a maintainer who runs the formatter before review
can execute that filename as shell syntax.
git ls-files -- ":/*.c" ":/*.h" | xargs -P "$nproc" -I {} sh -c '
# Ignore symlinks
if [[ ! -L {} ]]; then
clang-format -i {}
fi'
# ...
git ls-files -- ":/" ":/!:Makefile" ":/!:**/Makefile" ":/!:**/Makefile.*" ":/!:Makefile.*" ":/!:*.mk" ":/!:*.patch" ":/!:nix/valgrind/*.txt" | xargs -P "$nproc" -I {} sh -c '
if [ ! -L {} ] && grep -Pq '"'"'\t'"'"' "{}"; then
tmp=$(mktemp)
expand -t 4 "{}" > "$tmp" && mv "$tmp" "{}"
echo "{}"
fi'
Figure X.4: The formatter substitutes tracked filenames into sh -c script text (mldsa-native/scripts/format#L63-L77)
The manual EC2 benchmark workflow exposes free-form inputs that are intended to
be benchmark configuration, compiler options, and extra test arguments. The
workflow forwards those values into shell
runblocks without first placingthem behind an argument boundary. A repository user with permission to dispatch
the manual benchmark workflow, or a maintainer who runs it with values copied
from an untrusted request, can therefore cause shell metacharacters in those
fields to execute on privileged CI runners.
The dispatchable workflow exposes
cflags,archflags,ldflags,bench_extra_args, andcompileras free-form inputs and forwards them intothe reusable EC2 benchmark workflow.
Figure X.1: The manual EC2 benchmark workflow accepts and forwards free-form benchmark inputs (mldsa-native/.github/workflows/bench_ec2_any.yml#L29-L69)
The reusable workflow writes the
compilerinput directly into a shell commandthat appends to
$GITHUB_ENV. GitHub Actions substitutes the expression beforeBash parses the script, so characters such as quotes, semicolons, and command
substitutions are interpreted as shell syntax.
Figure X.2: The reusable benchmark workflow interpolates
compilerinto a shell command (mldsa-native/.github/workflows/bench_ec2_reusable.yml#L182-L184)The benchmark action also interpolates forwarded benchmark options directly
into two
./scripts/tests benchcommand lines. These jobs run on EC2-backedself-hosted runners and use repository tokens for benchmark publication and
pull-request updates, so command execution in this path can affect CI state.
Figure X.3: The benchmark action interpolates free-form benchmark inputs into shell commands (mldsa-native/.github/actions/bench/action.yml#L80-L91)
The same data-to-code boundary appears in local developer tooling. The
formatter reads tracked filenames from Git and substitutes them into
sh -cscript text. A contributor can place shell metacharacters in a tracked filename
on an untrusted branch, and a maintainer who runs the formatter before review
can execute that filename as shell syntax.
Figure X.4: The formatter substitutes tracked filenames into
sh -cscript text (mldsa-native/scripts/format#L63-L77)