mirror of
https://github.com/google/brotli.git
synced 2026-07-08 17:56:58 +00:00
CloseFiles() previously called fclose(context->fout) before invoking CopyStat() on the output pathname. CopyStat() then used path-based chmod() and chown(), which gave an attacker with write access to the output directory a window to replace the path with a symlink between the close and the metadata syscalls, redirecting the chmod/chown to an arbitrary target. Close the race by doing the metadata copy while the output file is still open and by switching to fd-based syscalls on fileno(fout): - CopyStat() now takes FILE* fout and calls fchmod()/fchown() on the underlying fd. - CopyTimeStat() uses futimens(fd, ...) in the HAVE_UTIMENSAT branch; the legacy utime() fallback is preserved for platforms without it. - CloseFiles() invokes CopyStat() before fclose() and skips it when current_output_path is NULL (stdout), matching the issue's guidance. - Windows no-op shims updated from chmod/chown to fchmod/fchown. Adds deterministic regression coverage under tests/regression/t01/: an LD_PRELOAD fclose() swap reproduces the pre-fix behavior, and a negative-assertion wrapper confirms the attack no longer succeeds after the patch. Additional scripts cover mode/timestamp propagation, the -n (no-copy-stat) flag, stdin input, stdout output, mode mask, and roundtrip correctness. Refs google/brotli#1461
31 lines
1.2 KiB
Bash
Executable File
31 lines
1.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Negative regression test for T-01 (google/brotli#1461).
|
|
#
|
|
# Wraps repro_copystat_swap.sh and INVERTS the success condition.
|
|
#
|
|
# Before the fix: the inner repro script exits 0 and flips target.txt mode
|
|
# from 0600 -> 0644 via a post-close symlink swap.
|
|
# After the fix: CopyStat() runs BEFORE fclose(), uses fchmod() on the still-
|
|
# open fd, and can no longer be redirected at a swapped pathname. The target
|
|
# file mode must remain 0600, and the inner script must therefore exit non-
|
|
# zero. A non-zero exit from the inner script is what we want.
|
|
#
|
|
# Usage: test_copystat_swap_fixed.sh /path/to/brotli /path/to/libfclose_swap.so
|
|
set -euo pipefail
|
|
|
|
if [[ $# -ne 2 ]]; then
|
|
echo "usage: $0 /path/to/brotli /path/to/libfclose_swap.so" >&2
|
|
exit 2
|
|
fi
|
|
|
|
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
|
|
|
if "$script_dir/repro_copystat_swap.sh" "$@" >/dev/null 2>&1; then
|
|
echo "REGRESSION: T-01 post-close CopyStat TOCTOU is reproducible again" >&2
|
|
echo " The attacker still got target.txt mode flipped to 0644." >&2
|
|
echo " The fix in CloseFiles() / CopyStat() has regressed." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "T-01 FIXED: CopyStat no longer follows post-close symlink swap"
|