Skip to content

Commit c01b7f6

Browse files
committed
Bring the PHP-FPM toolchain up to the verified nginx+FPM chain
Replace the FPM client and labs launcher with the version-aware chain and add the supporting tools: - php2shell_fpm.py gains a fully remote `calibrate` (derives the leak and reclaim geometry of any 8.0+ build over HTTP), the pre-auth session-file `poison` that covers PHP <= 7.x, and a `no-oracle` endgame that refuses the local /proc fallback unless explicitly asked. - php2shell_fpm_versions.py is the single source of truth for the affected-version matrix (is_vulnerable / fixed_in / source_is_fixed classify a tarball by the upstream fix marker, not a version string). - php2shell_fpm_profile.py drafts a profile from an unstripped php-fpm using nm plus php2shell_fpm_offsets.c, compiled per build for the struct layouts. - php2shell_fpm_matrix.py verifies the remote chain on every unpatched build, one lab at a time. - build_php.sh retries truncated tarballs and patches the two glibc misdetections that stop 7.x from building with a current toolchain; build_all.sh builds several versions in parallel. Verified on 8.2.34, 8.3.35, 8.4.0, 8.4.25 and 8.4.26 (amd64). 8.5.x is intentionally out of scope; the fix ships in 8.5.12.
1 parent b84de1f commit c01b7f6

9 files changed

Lines changed: 2319 additions & 208 deletions

‎lab/fpm/build_all.sh‎

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env bash
2+
# build_all.sh — build several PHP-FPM versions into the lab runtime.
3+
#
4+
# Usage: lab/fpm/build_all.sh <version> [<version> ...]
5+
# lab/fpm/build_all.sh --range 8.4.0-8.4.26
6+
#
7+
# Environment:
8+
# PAR parallel build jobs (default: 4)
9+
# JOBS make -j per build (default: 4)
10+
#
11+
# Each build reuses lab/fpm/build_php.sh, so the sources are downloaded once
12+
# and the user prefix lands in lab/fpm/runtime/php-<version>/.
13+
set -uo pipefail
14+
15+
HERE="$(cd "$(dirname "$0")" && pwd)"
16+
PAR="${PAR:-4}"
17+
JOBS="${JOBS:-4}"
18+
LOGDIR="${LOGDIR:-$HERE/runtime/logs/builds}"
19+
20+
mkdir -p "$LOGDIR"
21+
22+
expand_range() {
23+
# 8.4.0-8.4.26 -> 8.4.0 ... 8.4.26
24+
local spec="$1" first last prefix v
25+
first="${spec%%-*}"
26+
last="${spec##*-}"
27+
prefix="${first%.*}."
28+
for ((v = ${first##*.}; v <= ${last##*.}; v++)); do
29+
printf '%s%d\n' "$prefix" "$v"
30+
done
31+
}
32+
33+
VERSIONS=()
34+
for arg in "$@"; do
35+
case "$arg" in
36+
--range) continue ;;
37+
*-*)
38+
while IFS= read -r line; do VERSIONS+=("$line"); done \
39+
< <(expand_range "$arg")
40+
;;
41+
*) VERSIONS+=("$arg") ;;
42+
esac
43+
done
44+
45+
if [ ${#VERSIONS[@]} -eq 0 ]; then
46+
echo "usage: build_all.sh <version> [<version> ...] | --range 8.4.0-8.4.26"
47+
exit 2
48+
fi
49+
50+
running=0
51+
failures=0
52+
for version in "${VERSIONS[@]}"; do
53+
prefix="$HERE/runtime/php-$version"
54+
if [ -x "$prefix/sbin/php-fpm" ]; then
55+
echo "[=] $version: already built"
56+
continue
57+
fi
58+
while [ "$(jobs -rp | wc -l)" -ge "$PAR" ]; do wait -n; done
59+
(
60+
log="$LOGDIR/build-$version.log"
61+
if "$HERE/build_php.sh" "$version" >"$log" 2>&1; then
62+
echo "[+] $version: built"
63+
else
64+
echo "[!] $version: build failed (see $log)"
65+
exit 1
66+
fi
67+
) &
68+
running=$((running + 1))
69+
done
70+
wait
71+
72+
for version in "${VERSIONS[@]}"; do
73+
prefix="$HERE/runtime/php-$version"
74+
if [ ! -x "$prefix/sbin/php-fpm" ]; then
75+
echo "[!] $version: missing $prefix/sbin/php-fpm"
76+
failures=$((failures + 1))
77+
fi
78+
done
79+
80+
if [ "$failures" -gt 0 ]; then
81+
echo "[!] $failures build(s) failed"
82+
exit 1
83+
fi
84+
echo "[+] all requested versions built"

‎lab/fpm/build_php.sh‎

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,61 @@ JOBS="${JOBS:-$(nproc)}"
1818

1919
mkdir -p "$SRC"
2020

21+
download_tarball() {
22+
# A truncated download is a common failure mode: the archive is checked
23+
# with gzip -t and re-fetched until it is intact.
24+
local url tarball attempt
25+
tarball="$1"
26+
for attempt in 1 2 3; do
27+
rm -f "$tarball"
28+
for url in "https://www.php.net/distributions/php-$VER.tar.gz" \
29+
"https://museum.php.net/php8/php-$VER.tar.gz"; do
30+
echo "[*] downloading php-$VER (attempt $attempt) from $url"
31+
if curl -fsSL -o "$tarball" "$url" && gzip -t "$tarball" 2>/dev/null; then
32+
return 0
33+
fi
34+
echo "[!] download or archive check failed for php-$VER"
35+
done
36+
done
37+
echo "[!] could not fetch a valid php-$VER tarball"
38+
return 1
39+
}
40+
2141
if [ ! -d "$SRC/php-$VER" ]; then
2242
if [ -z "$TARBALL" ]; then
2343
TARBALL="$SRC/php-$VER.tar.gz"
24-
if [ ! -f "$TARBALL" ]; then
25-
echo "[*] downloading php-$VER"
26-
curl -fsSL -o "$TARBALL" "https://www.php.net/distributions/php-$VER.tar.gz" ||
27-
curl -fsSL -o "$TARBALL" "https://museum.php.net/php8/php-$VER.tar.gz"
44+
if [ ! -f "$TARBALL" ] || ! gzip -t "$TARBALL" 2>/dev/null; then
45+
download_tarball "$TARBALL"
2846
fi
2947
fi
3048
echo "[*] extracting $TARBALL"
3149
tar xzf "$TARBALL" -C "$SRC"
3250
fi
3351

52+
# Legacy branches (7.x) mis-detect modern glibc in two places, both of which
53+
# stop the build with a current toolchain:
54+
#
55+
# * readdir_r() is the POSIX three-argument form since glibc 2.30, while
56+
# configure still selects the legacy two-argument one. PHP 7.2 also
57+
# calls php_readdir_r() from ext/session/mod_files.c before including
58+
# php_reentrancy.h, so the macro that maps it onto readdir_r() cannot be
59+
# relied on: patch the call itself.
60+
# * the fopencookie seek handler takes a pointer to an off64_t, a plain
61+
# configure decision, so it is fixed in the generated php_config.h.
62+
fix_legacy_config() {
63+
local cfg="$SRC/php-$VER/main/php_config.h"
64+
local reent="$SRC/php-$VER/main/reentrancy.c"
65+
[ -f "$cfg" ] || return 0
66+
if grep -q '^/\* #undef COOKIE_SEEKER_USES_OFF64_T \*/' "$cfg"; then
67+
sed -i 's|^/\* #undef COOKIE_SEEKER_USES_OFF64_T \*/|#define COOKIE_SEEKER_USES_OFF64_T 1|' \
68+
"$cfg"
69+
fi
70+
if [ -f "$reent" ] && grep -q '^ readdir_r(dirp, entry);$' "$reent"; then
71+
sed -i 's|^ readdir_r(dirp, entry);$| struct dirent *r_dir_entry = NULL;\n readdir_r(dirp, entry, \&r_dir_entry);|' \
72+
"$reent"
73+
fi
74+
}
75+
3476
cd "$SRC/php-$VER"
3577
if [ ! -f Makefile ] || [ ! -f config.status ]; then
3678
echo "[*] configuring php-$VER (fpm, session, minimal)"
@@ -43,8 +85,9 @@ if [ ! -f Makefile ] || [ ! -f config.status ]; then
4385
--without-pear \
4486
--disable-cgi \
4587
--disable-phpdbg \
46-
CFLAGS="-O2 -g"
88+
CFLAGS="${CFLAGS:--O2 -g}"
4789
fi
90+
fix_legacy_config
4891
echo "[*] building php-$VER (-j$JOBS)"
4992
make -j"$JOBS" >/dev/null
5093
make install >/dev/null

0 commit comments

Comments
 (0)