Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 57 additions & 11 deletions install-external-deps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,26 +45,72 @@ ANYSCAN_USE_AF_XDP="${ANYSCAN_USE_AF_XDP:-0}"
ANYSCAN_USE_PFRING_ZC="${ANYSCAN_USE_PFRING_ZC:-0}"

# Opt-in kernel backport upgrade. Default 0 leaves the running kernel
# untouched (existing AMIs unchanged). Setting 1 installs the Debian
# bookworm-backports kernel image so the host can run kernel 6.16+
# with the in-flight `ena_xdp_zc` ENA driver patches that AF_XDP
# zerocopy on ENA needs. The PR 65 §10 / anygpt-42 live bench showed
# ENA on kernel 6.12.74 caps the c6in.metal 8-NIC cap=4 throughput
# at ~22M pps in drv+copy mode, vs the 30-50M projection driver-mode
# zerocopy was supposed to deliver. See PR 65 issuecomment-4336192354
# for the constraint trace.
# untouched (existing AMIs unchanged). Setting 1 installs a Debian
# backports kernel image so the host can run kernel 6.16+ with the
# in-flight `ena_xdp_zc` ENA driver patches that AF_XDP zerocopy on
# ENA needs. The PR 65 §10 / anygpt-42 live bench showed ENA on
# kernel 6.12.74 caps the c6in.metal 8-NIC cap=4 throughput at ~22M
# pps in drv+copy mode, vs the 30-50M projection driver-mode zerocopy
# was supposed to deliver. See PR 65 issuecomment-4336192354 for the
# constraint trace.
#
# The suite and package default to the host's Debian codename:
# trixie-backports / linux-image-amd64 on Debian 13, bookworm-backports
# / linux-image-cloud-amd64 on Debian 12. PR 65 issuecomment-4338158487
# (anygpt-48) caught the previous static bookworm-backports default
# silently no-op'ing on the Trixie AMI: bookworm-backports
# linux-image-cloud-amd64 resolves to 6.12.74-2~bpo12+1 — exactly the
# kernel the metal already runs — so the opt-in completed "0 upgraded,
# 0 newly installed" and the operator got a green light without ever
# upgrading. Trixie's linux-image-cloud-amd64 is also still 6.12 as of
# 2026-04, so we explicitly switch the package to the non-cloud
# linux-image-amd64 on non-bookworm suites.
#
# Operator-set ANYSCAN_KERNEL_BACKPORT_SUITE / _PACKAGE / _SOURCES_LIST
# still win — the codename detection is just a smarter default.
#
# Never auto-reboots. The new kernel is staged on disk and the operator
# has to schedule the reboot themselves. After install the script
# probes `/sys/module/ena/version` + dmesg for `ena_xdp_zc` support
# and warns if absent so the operator knows whether the
# CURRENTLY-RUNNING kernel will deliver zerocopy.

# Detect the Debian codename so backport defaults match the running
# release. /etc/os-release VERSION_CODENAME is the canonical source on
# Debian/Ubuntu hosts; missing or unreadable file → fall back to
# "bookworm" so the legacy default doesn't change for hosts where the
# os-release file isn't accessible. Override the file path with
# ANYSCAN_OS_RELEASE_FILE for testing.
detect_debian_codename() {
local release_file="${ANYSCAN_OS_RELEASE_FILE:-/etc/os-release}"
local codename=""
if [ -r "$release_file" ]; then
# shellcheck source=/dev/null
codename="$(. "$release_file" 2>/dev/null && printf '%s\n' "${VERSION_CODENAME:-}")"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Parse os-release instead of sourcing it

detect_debian_codename() executes . "$release_file" where release_file is controllable via ANYSCAN_OS_RELEASE_FILE; this means arbitrary shell in that file will run as part of script startup (often as root), even when kernel backport install is disabled. Because this commit introduced the env override specifically for tests, a malformed or attacker-controlled file can now trigger command execution or early exits in production invocations that inherit environment variables. Read VERSION_CODENAME as data (e.g., grep/awk) rather than sourcing the file.

Useful? React with 👍 / 👎.

fi
if [ -z "$codename" ]; then
codename="bookworm"
fi
printf '%s\n' "$codename"
}

ANYSCAN_INSTALL_KERNEL_BACKPORT="${ANYSCAN_INSTALL_KERNEL_BACKPORT:-0}"
ANYSCAN_KERNEL_BACKPORT_MIN_VERSION="${ANYSCAN_KERNEL_BACKPORT_MIN_VERSION:-6.16}"
ANYSCAN_KERNEL_BACKPORT_PACKAGE="${ANYSCAN_KERNEL_BACKPORT_PACKAGE:-linux-image-cloud-amd64}"
ANYSCAN_KERNEL_BACKPORT_SUITE="${ANYSCAN_KERNEL_BACKPORT_SUITE:-bookworm-backports}"
ANYSCAN_KERNEL_BACKPORT_SOURCES_LIST="${ANYSCAN_KERNEL_BACKPORT_SOURCES_LIST:-/etc/apt/sources.list.d/anyscan-bookworm-backports.list}"
_anyscan_codename="$(detect_debian_codename)"
ANYSCAN_KERNEL_BACKPORT_SUITE="${ANYSCAN_KERNEL_BACKPORT_SUITE:-${_anyscan_codename}-backports}"
# Package selection: trixie-backports linux-image-cloud-amd64 is still
# 6.12 as of 2026-04 — only the non-cloud linux-image-amd64 jumps to
# 6.19. Bookworm-backports keeps the legacy linux-image-cloud-amd64
# default for back-compat on operators still on bookworm hosts.
if [ "$ANYSCAN_KERNEL_BACKPORT_SUITE" = "bookworm-backports" ]; then
_anyscan_default_pkg="linux-image-cloud-amd64"
else
_anyscan_default_pkg="linux-image-amd64"
fi
ANYSCAN_KERNEL_BACKPORT_PACKAGE="${ANYSCAN_KERNEL_BACKPORT_PACKAGE:-$_anyscan_default_pkg}"
ANYSCAN_KERNEL_BACKPORT_SOURCES_LIST="${ANYSCAN_KERNEL_BACKPORT_SOURCES_LIST:-/etc/apt/sources.list.d/anyscan-${ANYSCAN_KERNEL_BACKPORT_SUITE}.list}"
ANYSCAN_KERNEL_BACKPORT_MIRROR="${ANYSCAN_KERNEL_BACKPORT_MIRROR:-http://deb.debian.org/debian}"
unset _anyscan_codename _anyscan_default_pkg

# True when the existing scanner binary was linked against libxdp at build
# time. The AF_XDP build path (USE_AF_XDP=1 in the engine Makefile) adds
Expand Down
94 changes: 93 additions & 1 deletion tools/test-install-external-deps-kernel-backport.sh
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,13 @@ run_install_script() {
local install_kernel_backport="$2"
local uname_release="$3"
local apt_present="$4"
# Optional 5th arg: os-release VERSION_CODENAME to write into a
# synthetic /etc/os-release the install script will read via
# ANYSCAN_OS_RELEASE_FILE. When empty, we pre-set
# ANYSCAN_KERNEL_BACKPORT_SUITE/_PACKAGE explicitly to bookworm
# defaults so existing case-1..4 assertions stay stable regardless
# of the test host's actual codename.
local os_release_codename="${5:-}"

local repo_dir="$case_dir/engine"
local runtime_env="$case_dir/runtime.env"
Expand All @@ -220,6 +227,7 @@ run_install_script() {
local git_log="$case_dir/git.log"
local stub_dir="$case_dir/stubs"
local sources_list="$case_dir/anyscan-bookworm-backports.list"
local os_release_file=""

mkdir -p "$repo_dir" "$artifact_dir"
: >"$apt_log"
Expand All @@ -232,6 +240,12 @@ run_install_script() {
prepare_stubs "$stub_dir" "$apt_log" "$make_log" "$git_log" \
"$uname_release" "$apt_present"

if [ -n "$os_release_codename" ]; then
os_release_file="$case_dir/os-release"
printf 'ID=debian\nVERSION_CODENAME=%s\n' "$os_release_codename" \
>"$os_release_file"
fi

(
# PATH = stub_dir only. prepare_stubs symlinks the essentials
# install-external-deps.sh needs (bash, mktemp, install, sed,
Expand All @@ -242,7 +256,23 @@ run_install_script() {
# apt-get` returns false for the install script.
export PATH="$stub_dir"
export ANYSCAN_INSTALL_KERNEL_BACKPORT="$install_kernel_backport"
export ANYSCAN_KERNEL_BACKPORT_SOURCES_LIST="$sources_list"
if [ -n "$os_release_file" ]; then
# Auto-detect path: let the script derive suite/package from
# the synthetic os-release. Pin the sources-list path to a
# per-case file so the test doesn't try to write under
# /etc/apt — but still derive its name from the codename so
# the assertion side stays meaningful.
export ANYSCAN_OS_RELEASE_FILE="$os_release_file"
export ANYSCAN_KERNEL_BACKPORT_SOURCES_LIST="$case_dir/anyscan-${os_release_codename}-backports.list"
unset ANYSCAN_KERNEL_BACKPORT_SUITE
unset ANYSCAN_KERNEL_BACKPORT_PACKAGE
else
# Legacy path: pin suite/package explicitly so the
# existing case-1..4 assertions hold on any test host.
export ANYSCAN_KERNEL_BACKPORT_SUITE="bookworm-backports"
export ANYSCAN_KERNEL_BACKPORT_PACKAGE="linux-image-cloud-amd64"
export ANYSCAN_KERNEL_BACKPORT_SOURCES_LIST="$sources_list"
fi
export ANYSCAN_VULNSCANNER_REPO_DIR="$repo_dir"
export ANYSCAN_INSTALL_AFXDP_DEPS=false
export ANYSCAN_USE_AF_XDP=0
Expand Down Expand Up @@ -351,6 +381,68 @@ else
note_fail "knob=1 + no apt-get build" "install-external-deps.sh exited non-zero (see $case_dir/stderr.log)"
fi

# ---------------------------------------------------------------------------
# Case 5: knob=1 + Trixie host + kernel 6.12 → suite=trixie-backports,
# package=linux-image-amd64 (NOT linux-image-cloud-amd64).
# Confirms PR 65 issuecomment-4338158487 fix: bookworm-backports default
# silently no-op'd on Trixie because bookworm cloud-amd64 is still 6.12.
# ---------------------------------------------------------------------------
case_dir="$WORK_ROOT/case-trixie-host"
mkdir -p "$case_dir"
if run_install_script "$case_dir" "1" "6.12.74-cloud-amd64" "yes" "trixie"; then
note_pass "knob=1 + trixie host build runs successfully"
assert_contains_substring \
"knob=1 + trixie: apt-get install -t trixie-backports linux-image-amd64 fires" \
"install -y --no-install-recommends -t trixie-backports linux-image-amd64" \
"$case_dir/apt-get.log"
assert_not_contains_substring \
"knob=1 + trixie: bookworm-backports NOT used (would silently no-op)" \
"bookworm-backports" \
"$case_dir/apt-get.log"
assert_not_contains_substring \
"knob=1 + trixie: linux-image-cloud-amd64 NOT used (still 6.12 in trixie-backports)" \
"linux-image-cloud-amd64" \
"$case_dir/apt-get.log"
assert_file_present \
"knob=1 + trixie: apt source list at trixie-backports path" \
"$case_dir/anyscan-trixie-backports.list"
assert_contains_substring \
"knob=1 + trixie: apt source list points at trixie-backports" \
"trixie-backports" \
"$case_dir/anyscan-trixie-backports.list"
assert_file_missing \
"knob=1 + trixie: bookworm-backports source list NOT created" \
"$case_dir/anyscan-bookworm-backports.list"
else
note_fail "knob=1 + trixie host build" "install-external-deps.sh exited non-zero (see $case_dir/stderr.log)"
fi

# ---------------------------------------------------------------------------
# Case 6: knob=1 + Bookworm host + kernel 6.12 → legacy default preserved
# (suite=bookworm-backports, package=linux-image-cloud-amd64).
# ---------------------------------------------------------------------------
case_dir="$WORK_ROOT/case-bookworm-host"
mkdir -p "$case_dir"
if run_install_script "$case_dir" "1" "6.12.74-cloud-amd64" "yes" "bookworm"; then
note_pass "knob=1 + bookworm host build runs successfully"
assert_contains_substring \
"knob=1 + bookworm: apt-get install -t bookworm-backports linux-image-cloud-amd64 fires" \
"install -y --no-install-recommends -t bookworm-backports linux-image-cloud-amd64" \
"$case_dir/apt-get.log"
assert_file_present \
"knob=1 + bookworm: apt source list at bookworm-backports path" \
"$case_dir/anyscan-bookworm-backports.list"
assert_contains_substring \
"knob=1 + bookworm: apt source list points at bookworm-backports" \
"bookworm-backports" \
"$case_dir/anyscan-bookworm-backports.list"
assert_file_missing \
"knob=1 + bookworm: trixie-backports source list NOT created" \
"$case_dir/anyscan-trixie-backports.list"
else
note_fail "knob=1 + bookworm host build" "install-external-deps.sh exited non-zero (see $case_dir/stderr.log)"
fi

printf '\n'
printf 'PASS: %d\n' "$PASS"
printf 'FAIL: %d\n' "$FAIL"
Expand Down