diff --git a/.github/actions/bench/action.yml b/.github/actions/bench/action.yml index bdc18266c..9810e2732 100644 --- a/.github/actions/bench/action.yml +++ b/.github/actions/bench/action.yml @@ -26,7 +26,7 @@ inputs: description: opt flag to set for tests script default: "true" bench_extra_args: - description: Further arguments to be appended to command line for `bench` script + description: Newline-delimited additional arguments for the `bench` script default: "" store_results: description: Whether to push results to GH pages @@ -56,6 +56,8 @@ runs: using: composite steps: - uses: ./.github/actions/setup-shell + env: + BENCH_CROSS_PREFIX: ${{ inputs.cross_prefix }} with: nix-shell: ${{ inputs.nix-shell }} nix-cache: ${{ inputs.nix-cache }} @@ -63,13 +65,17 @@ runs: gh_token: ${{ inputs.gh_token }} custom_shell: ${{ inputs.custom_shell }} script: | + if [[ ! "$BENCH_CROSS_PREFIX" =~ ^[A-Za-z0-9_./+-]*$ ]]; then + printf 'cross_prefix contains unsupported characters\n' >&2 + exit 1 + fi ARCH=$(uname -m) cat >> $GITHUB_STEP_SUMMARY <<-EOF ## Setup Architecture: $ARCH - $(uname -a) - $(nix --version) - - $(${{ matrix.target.cross_prefix }}gcc --version | grep -m1 "") + - $("${BENCH_CROSS_PREFIX}gcc" --version | grep -m1 "") - $(bash --version | grep -m1 "") ## CPU Info @@ -77,18 +83,60 @@ runs: EOF - name: Run benchmark shell: ${{ env.SHELL }} + env: + BENCH_ARCHFLAGS: ${{ inputs.archflags }} + BENCH_CFLAGS: ${{ inputs.cflags }} + BENCH_CROSS_PREFIX: ${{ inputs.cross_prefix }} + BENCH_EXTRA_ARGS: ${{ inputs.bench_extra_args }} + BENCH_LDFLAGS: ${{ inputs.ldflags }} + BENCH_OPT: ${{ inputs.opt }} + BENCH_PERF: ${{ inputs.perf }} run: | - ./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 }} + validate_value() { + local name=$1 + local value=$2 + local pattern=$3 + if [[ ! "$value" =~ $pattern ]]; then + printf '%s contains unsupported characters\n' "$name" >&2 + exit 1 + fi + } - ./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 }} + validate_value perf "$BENCH_PERF" '^(NO|PMU|PERF|MAC)$' + validate_value cross_prefix "$BENCH_CROSS_PREFIX" '^[A-Za-z0-9_./+-]*$' + validate_value cflags "$BENCH_CFLAGS" '^[A-Za-z0-9_.,:+=/@% -]*$' + validate_value archflags "$BENCH_ARCHFLAGS" '^[A-Za-z0-9_.,:+=/@% -]*$' + validate_value ldflags "$BENCH_LDFLAGS" '^[A-Za-z0-9_.,:+=/@% -]*$' + + if [[ "$BENCH_OPT" == "false" ]]; then + bench_opt=no_opt + elif [[ "$BENCH_OPT" == "true" ]]; then + bench_opt=opt + else + printf 'opt must be true or false\n' >&2 + exit 1 + fi + + extra_args=() + if [[ -n "$BENCH_EXTRA_ARGS" ]]; then + while IFS= read -r arg; do + [[ -z "$arg" ]] && continue + extra_args+=("$arg") + done <<< "$BENCH_EXTRA_ARGS" + fi + + common_args=( + ./scripts/tests bench + -c "$BENCH_PERF" + "--cross-prefix=$BENCH_CROSS_PREFIX" + "--cflags=$BENCH_CFLAGS $BENCH_ARCHFLAGS" + "--ldflags=$BENCH_LDFLAGS" + "--opt=$bench_opt" + -v + ) + + "${common_args[@]}" --output=output.json "${extra_args[@]}" + "${common_args[@]}" --components "${extra_args[@]}" - name: Check namespace shell: ${{ env.SHELL }} run: | diff --git a/.github/workflows/bench_ec2_any.yml b/.github/workflows/bench_ec2_any.yml index 22ef13604..67fb95318 100644 --- a/.github/workflows/bench_ec2_any.yml +++ b/.github/workflows/bench_ec2_any.yml @@ -43,7 +43,7 @@ on: - opt - no_opt bench_extra_args: - description: Additional command line to be appended to `tests bench` script + description: Newline-delimited additional arguments for `tests bench` default: '' compiler: description: Compiler to use. When unset, default nix shell is used. diff --git a/.github/workflows/bench_ec2_reusable.yml b/.github/workflows/bench_ec2_reusable.yml index af30ed823..be5bed1bd 100644 --- a/.github/workflows/bench_ec2_reusable.yml +++ b/.github/workflows/bench_ec2_reusable.yml @@ -52,7 +52,7 @@ on: default: false bench_extra_args: type: string - description: Additional command line to be appended to `bench` script + description: Newline-delimited additional arguments for the `bench` script default: '' compiler: type: string @@ -97,18 +97,35 @@ jobs: ec2-instance-id: ${{ steps.start-ec2-runner.outputs.ec2-instance-id }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Determine AMI ID id: det_ami_id + env: + EC2_AMI: ${{ inputs.ec2_ami }} + EC2_AMI_ID: ${{ inputs.ec2_ami_id }} run: | - if [[ "${{ inputs.ec2_ami }}" == "ubuntu-latest (x86_64)" ]]; then - AMI_ID=${{ env.AMI_UBUNTU_LATEST_X86_64 }} - elif [[ "${{ inputs.ec2_ami }}" == "ubuntu-latest (aarch64)" ]]; then - AMI_ID=${{ env.AMI_UBUNTU_LATEST_AARCH64 }} - elif [[ "${{ inputs.ec2_ami }}" == "ubuntu-latest (custom AMI)" ]]; then - AMI_ID=${{ inputs.ec2_ami_id }} - fi - echo "Using AMI ID: $AMI_ID" - echo "AMI_ID=$AMI_ID" >> "$GITHUB_OUTPUT" + case "$EC2_AMI" in + "ubuntu-latest (x86_64)") + AMI_ID="$AMI_UBUNTU_LATEST_X86_64" + ;; + "ubuntu-latest (aarch64)") + AMI_ID="$AMI_UBUNTU_LATEST_AARCH64" + ;; + "ubuntu-latest (custom AMI)") + if [[ ! "$EC2_AMI_ID" =~ ^ami-[0-9a-f]{8,17}$ ]]; then + printf 'Invalid custom AMI ID: %s\n' "$EC2_AMI_ID" >&2 + exit 1 + fi + AMI_ID="$EC2_AMI_ID" + ;; + *) + printf 'Unsupported AMI label: %s\n' "$EC2_AMI" >&2 + exit 1 + ;; + esac + printf 'Using AMI ID: %s\n' "$AMI_ID" + printf 'AMI_ID=%s\n' "$AMI_ID" >> "$GITHUB_OUTPUT" - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@e7f100cf4c008499ea8adda475de1042d6975c7b # v6.2.0 with: @@ -139,6 +156,8 @@ jobs: if: ${{ inputs.compiler == '' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: ./.github/actions/bench if: ${{ inputs.opt == 'all' || inputs.opt == 'opt' }} with: @@ -176,12 +195,20 @@ jobs: if: ${{ inputs.compiler != '' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - uses: ./.github/actions/setup-apt with: packages: ${{ inputs.additional_packages }} - name: Set compiler + env: + BENCH_COMPILER: ${{ inputs.compiler }} run: | - echo "CC=${{ inputs.compiler }}" >> "$GITHUB_ENV" + if [[ ! "$BENCH_COMPILER" =~ ^[A-Za-z0-9_./+-]+$ ]]; then + printf 'compiler contains unsupported characters\n' >&2 + exit 1 + fi + printf 'CC=%s\n' "$BENCH_COMPILER" >> "$GITHUB_ENV" - uses: ./.github/actions/bench if: ${{ inputs.opt == 'all' || inputs.opt == 'opt' }} with: diff --git a/scripts/format b/scripts/format index 3cf55ca5d..70336049d 100755 --- a/scripts/format +++ b/scripts/format @@ -43,7 +43,7 @@ fi # Find all scripts in the repo SHELL_SCRIPTS=$(git grep -l '' :/ | xargs printf "'%s' " | xargs -L 1 shfmt -f) -echo $SHELL_SCRIPTS | xargs -L 1 shfmt -s -w -l -i 2 -ci -fn +printf "%s\n" "$SHELL_SCRIPTS" | xargs -L 1 shfmt -s -w -l -i 2 -ci -fn info "Formatting python scripts with ruff" if ! command -v ruff >/dev/null 2>&1; then @@ -60,32 +60,47 @@ fi nproc=$(getconf _NPROCESSORS_ONLN || echo 1) -git ls-files -- ":/*.c" ":/*.h" | xargs -P "$nproc" -I {} sh -c ' +# The worker script is single-quoted so tracked filenames cannot become shell +# source; each filename is passed as $1. +# shellcheck disable=SC2016 +git ls-files -z -- ":/*.c" ":/*.h" | xargs -0 -P "$nproc" -I {} sh -c ' + file=$1 # Ignore symlinks - if [[ ! -L {} ]]; then - clang-format -i {} - fi' + if [ ! -L "$file" ]; then + clang-format -i -- "$file" + fi +' sh {} info "Expanding tabs" expand-tabs() { - git ls-files -- ":/" ":/!:Makefile" ":/!:**/Makefile" ":/!:**/Makefile.*" ":/!:Makefile.*" ":/!:*.mk" ":/!:*.patch" ":/!:nix/valgrind/*.txt" | xargs -P "$nproc" -I {} sh -c ' - if [ ! -L {} ] && grep -Pq '"'"'\t'"'"' "{}"; then + # The worker script is single-quoted so tracked filenames cannot become shell + # source; each filename is passed as $1. + # shellcheck disable=SC2016 + git ls-files -z -- ":/" ":/!:Makefile" ":/!:**/Makefile" ":/!:**/Makefile.*" ":/!:Makefile.*" ":/!:*.mk" ":/!:*.patch" ":/!:nix/valgrind/*.txt" | xargs -0 -P "$nproc" -I {} sh -c ' + file=$1 + if [ ! -L "$file" ] && grep -Pq "\t" -- "$file"; then tmp=$(mktemp) - expand -t 4 "{}" > "$tmp" && mv "$tmp" "{}" - echo "{}" - fi' + expand -t 4 "$file" > "$tmp" && mv "$tmp" "$file" + echo "$file" + fi + ' sh {} } expand-tabs info "Checking for eol" check-eol() { - git ls-files -- ":/" ":/!:*.png" | xargs -P "$nproc" -I {} sh -c ' + # The worker script is single-quoted so tracked filenames cannot become shell + # source; each filename is passed as $1. + # shellcheck disable=SC2016 + git ls-files -z -- ":/" ":/!:*.png" | xargs -0 -P "$nproc" -I {} sh -c ' + file=$1 # Ignore symlinks - if [[ ! -L {} && $(tail -c1 "{}" | wc -l) == 0 ]]; then - echo "" >>"{}" - echo "{}" - fi' + if [ ! -L "$file" ] && [ "$(tail -c1 "$file" | wc -l)" -eq 0 ]; then + printf "\n" >>"$file" + echo "$file" + fi + ' sh {} } check-eol diff --git a/scripts/tests b/scripts/tests index 14360b2cf..ca220f054 100755 --- a/scripts/tests +++ b/scripts/tests @@ -33,6 +33,22 @@ def dict2str(dict): return s +SAFE_MAKE_FLAG_RE = re.compile(r"^[A-Za-z0-9_.,:+=/@% -]*$") +SAFE_CROSS_PREFIX_RE = re.compile(r"^[A-Za-z0-9_./+-]*$") + + +def validate_cli_inputs(parser, args): + checks = [ + ("cflags", SAFE_MAKE_FLAG_RE), + ("ldflags", SAFE_MAKE_FLAG_RE), + ("cross_prefix", SAFE_CROSS_PREFIX_RE), + ] + for name, pattern in checks: + value = getattr(args, name, None) + if value is not None and not pattern.fullmatch(value): + parser.error(f"--{name.replace('_', '-')} contains unsupported characters") + + def github_log(msg): if os.environ.get("GITHUB_ENV") is None: return @@ -1603,6 +1619,7 @@ def cli(): ) args = main_parser.parse_args() + validate_cli_inputs(main_parser, args) if not hasattr(args, "mac_taskpolicy"): args.mac_taskpolicy = None