* [PATCH v2 01/12] klp-build: Fix dependent patches in a series
2026-10-28 21:45 [PATCH v2 00/12] klp-build: Fix dependent patches, add selftests Song Liu
@ 2026-10-28 21:45 ` Song Liu
2026-10-28 21:45 ` [PATCH v2 02/12] klp-build: Process the patches before building the original kernel Song Liu
` (10 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Song Liu @ 2026-10-28 21:45 UTC (permalink / raw)
To: live-patching, linux-kselftest
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, shuah,
linux-kernel, Song Liu
fix_patches() refreshes each patch on its own against the original
source: it applies the patch, copies the patched files, and reverts it.
If a later patch in the series depends on an earlier one, it doesn't
apply to the original source, and klp-build fails even though
validate_patches(), which applies the patches in order, accepted the
series:
$ ./scripts/livepatch/klp-build 0001-dummy.patch 0002-Revert-dummy.patch
Validating patch(es)
Building original kernel
Copying original object files
Fixing patch(es)
Reversed (or previously applied) patch detected! Assume -R? [n]
Apply anyway? [n]
1 out of 1 hunk ignored
error: klp-build: .../klp-tmp/tmp.patch did not apply
Refreshing each patch on top of the previous ones wouldn't work either.
fix-patch-lines adds #line directives to each fixed patch, so the next
patch's context wouldn't match. The #line numbers in later patches
would also refer to the intermediate source rather than the original
source, which defeats the purpose of preserving __LINE__.
Instead, apply all the patches in order and diff the result against the
original source to get a single combined patch, then run fix-patch-lines
on that. If the patches have no net effect, fail with "no changes
detected" instead of building the patched kernel for nothing.
The combined patch gets its own variable, $COMBINED_PATCH, instead of
replacing the patches in $PATCHES, and fix_patches() becomes
process_patches(), as it no longer fixes the patches it is given. The
intermediate patch without #line directives is left in klp-tmp for
debugging. get_patch_files() is now the only user of
get_patch_input_files() and get_patch_output_files(), so fold them into
it.
Fixes: 24ebfcd65a87 ("livepatch/klp-build: Introduce klp-build script for generating livepatch modules")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <song@kernel.org>
---
scripts/livepatch/klp-build | 121 +++++++++++++++++-------------------
1 file changed, 57 insertions(+), 64 deletions(-)
diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
index b52a8489d9f6..a0bfc40093f9 100755
--- a/scripts/livepatch/klp-build
+++ b/scripts/livepatch/klp-build
@@ -47,6 +47,7 @@ KMOD_DIR="$TMP_DIR/5-kmod"
STASH_DIR="$TMP_DIR/stash"
TIMESTAMP="$TMP_DIR/timestamp"
PATCH_TMP_DIR="$TMP_DIR/tmp"
+COMBINED_PATCH="$TMP_DIR/combined.patch"
KLP_DIFF_LOG="$DIFF_DIR/diff.log"
@@ -328,33 +329,16 @@ set_kernelversion() {
sed -i "2i echo $kernelrelease; exit 0" scripts/setlocalversion
}
-get_patch_input_files() {
- local patch="$1"
-
- grep0 -E '^--- ' "$patch" \
- | grep0 -v -e '/dev/null' -e '1969-12-31' -e '1970-01-01' \
- | gawk '{print $2}' \
- | sed 's|^[^/]*/||' \
- | sort -u
-}
-
-get_patch_output_files() {
+get_patch_files() {
local patch="$1"
- grep0 -E '^\+\+\+ ' "$patch" \
+ grep0 -E '^(---|\+\+\+) ' "$patch" \
| grep0 -v -e '/dev/null' -e '1969-12-31' -e '1970-01-01' \
| gawk '{print $2}' \
| sed 's|^[^/]*/||' \
| sort -u
}
-get_patch_files() {
- local patch="$1"
-
- { get_patch_input_files "$patch"; get_patch_output_files "$patch"; } \
- | sort -u
-}
-
check_unsupported_patches() {
local patch
@@ -467,60 +451,69 @@ do_init() {
set_kernelversion
}
-# Refresh the patch hunk headers, specifically the line numbers and counts.
-refresh_patch() {
- local patch="$1"
+# Combine the patches into a single patch against the original source. The
+# patches may depend on each other, so they can't be diffed individually.
+combine_patches() {
+ local outfile="$1"
local tmpdir="$PATCH_TMP_DIR"
- local input_files=()
- local output_files=()
+ local files=()
+ local patch
+ local file
rm -rf "$tmpdir"
- mkdir -p "$tmpdir/a"
- mkdir -p "$tmpdir/b"
+ mkdir -p "$tmpdir"
- # Get all source files affected by the patch
- get_patch_input_files "$patch" | mapfile -t input_files
- get_patch_output_files "$patch" | mapfile -t output_files
+ # Get all source files affected by the patches
+ for patch in "${PATCHES[@]}"; do
+ get_patch_files "$patch"
+ done | sort -u | mapfile -t files
- # Copy orig source files to 'a'
- echo "${input_files[@]}" | xargs cp --parents --target-directory="$tmpdir/a"
+ # Copy orig source files to $tmpdir
+ for file in "${files[@]}"; do
+ if [[ -e "$file" ]]; then
+ cp --parents "$file" "$tmpdir"
+ fi
+ done
- # Copy patched source files to 'b'
- apply_patch "$patch" "--silent"
- echo "${output_files[@]}" | xargs cp --parents --target-directory="$tmpdir/b"
- revert_patch "$patch"
+ # Diff the orig source files against the patched ones in place to make a
+ # clean patch. Use /dev/null for created and deleted files.
+ : > "$outfile"
+ apply_patches "--silent"
+ for file in "${files[@]}"; do
+ local old="$tmpdir/$file"
+ local new="$file"
+ local old_label="a/$file"
+ local new_label="b/$file"
+ local status=0
+
+ if [[ ! -e "$old" ]]; then
+ old=/dev/null
+ old_label=/dev/null
+ fi
+ if [[ ! -e "$new" ]]; then
+ new=/dev/null
+ new_label=/dev/null
+ fi
- # Diff 'a' and 'b' to make a clean patch
- ( cd "$tmpdir" && diff -Nupr a b > "$patch" ) || true
+ diff -up --label "$old_label" --label "$new_label" "$old" "$new" \
+ >> "$outfile" || status=$?
+ if (( status > 1 )); then
+ die "diff failed for $file"
+ fi
+ done
+ revert_patches
}
-# Copy the patches to a temporary directory, fix their lines so as not to
+# Combine the patches into $COMBINED_PATCH, and fix its lines so as not to
# affect the __LINE__ macro for otherwise unchanged functions further down the
-# file, and update $PATCHES to point to the fixed patches.
-fix_patches() {
- local idx
- local i
-
- rm -f "$TMP_DIR"/*.patch
-
- idx=0001
- for i in "${!PATCHES[@]}"; do
- local old_patch="${PATCHES[$i]}"
- local tmp_patch="$TMP_DIR/tmp.patch"
- local patch="${PATCHES[$i]}"
- local new_patch
+# file.
+process_patches() {
+ local tmp_patch="$TMP_DIR/tmp.patch"
- new_patch="$TMP_DIR/$idx-fixed-$(basename "$patch")"
+ combine_patches "$tmp_patch"
+ [[ ! -s "$tmp_patch" ]] && die "no changes detected"
- cp -f "$old_patch" "$tmp_patch"
- refresh_patch "$tmp_patch"
- "$FIX_PATCH_LINES" "$tmp_patch" | recountdiff > "$new_patch"
-
- PATCHES[i]="$new_patch"
-
- rm -f "$tmp_patch"
- idx=$(printf "%04d" $(( 10#$idx + 1 )))
- done
+ "$FIX_PATCH_LINES" "$tmp_patch" | recountdiff > "$COMBINED_PATCH"
}
clean_kernel() {
@@ -909,9 +902,9 @@ if (( SHORT_CIRCUIT <= 1 )); then
fi
if (( SHORT_CIRCUIT <= 2 )); then
- status "Fixing patch(es)"
- fix_patches
- apply_patches "--silent"
+ status "Processing patch(es)"
+ process_patches
+ apply_patch "$COMBINED_PATCH" "--silent"
status "Building patched kernel"
build_kernel "patched"
revert_patches
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 02/12] klp-build: Process the patches before building the original kernel
2026-10-28 21:45 [PATCH v2 00/12] klp-build: Fix dependent patches, add selftests Song Liu
2026-10-28 21:45 ` [PATCH v2 01/12] klp-build: Fix dependent patches in a series Song Liu
@ 2026-10-28 21:45 ` Song Liu
2026-10-28 21:45 ` [PATCH v2 03/12] klp-build: Move patch processing to klp-prepare-patch Song Liu
` (9 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Song Liu @ 2026-10-28 21:45 UTC (permalink / raw)
To: live-patching, linux-kselftest
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, shuah,
linux-kernel, Song Liu
klp-build validates the patches before building the original kernel,
but only combines them and adds #line directives after the build, which
applies and reverts them a second time. Nothing in the second step
needs the original build, but the errors it finds, like patches with no
net effect, only show up after a full kernel build.
Do both in one step, before the build. process_patches() checks for
patches to unsupported files, then applies the patches in order, which
also catches patches that don't apply and warns about those which apply
with fuzz, and combines them.
Suggested-by: Josh Poimboeuf <jpoimboe@kernel.org>
Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <song@kernel.org>
---
scripts/livepatch/klp-build | 24 ++++++++----------------
1 file changed, 8 insertions(+), 16 deletions(-)
diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
index a0bfc40093f9..28c3bf6b6418 100755
--- a/scripts/livepatch/klp-build
+++ b/scripts/livepatch/klp-build
@@ -395,11 +395,10 @@ revert_patch() {
}
apply_patches() {
- local extra_args=("$@")
local patch
for patch in "${PATCHES[@]}"; do
- apply_patch "$patch" "${extra_args[@]}"
+ apply_patch "$patch"
done
}
@@ -413,12 +412,6 @@ revert_patches() {
APPLIED_PATCHES=()
}
-validate_patches() {
- check_unsupported_patches
- apply_patches
- revert_patches
-}
-
do_init() {
# We're not yet smart enough to handle anything other than in-tree
# builds in pwd.
@@ -478,7 +471,7 @@ combine_patches() {
# Diff the orig source files against the patched ones in place to make a
# clean patch. Use /dev/null for created and deleted files.
: > "$outfile"
- apply_patches "--silent"
+ apply_patches
for file in "${files[@]}"; do
local old="$tmpdir/$file"
local new="$file"
@@ -504,12 +497,13 @@ combine_patches() {
revert_patches
}
-# Combine the patches into $COMBINED_PATCH, and fix its lines so as not to
-# affect the __LINE__ macro for otherwise unchanged functions further down the
-# file.
+# Validate the patches, combine them into $COMBINED_PATCH, and fix its lines so
+# as not to affect the __LINE__ macro for otherwise unchanged functions further
+# down the file.
process_patches() {
local tmp_patch="$TMP_DIR/tmp.patch"
+ check_unsupported_patches
combine_patches "$tmp_patch"
[[ ! -s "$tmp_patch" ]] && die "no changes detected"
@@ -889,8 +883,8 @@ process_args "$@"
do_init
if (( SHORT_CIRCUIT <= 2 )); then
- status "Validating patch(es)"
- validate_patches
+ status "Processing patch(es)"
+ process_patches
fi
if (( SHORT_CIRCUIT <= 1 )); then
@@ -902,8 +896,6 @@ if (( SHORT_CIRCUIT <= 1 )); then
fi
if (( SHORT_CIRCUIT <= 2 )); then
- status "Processing patch(es)"
- process_patches
apply_patch "$COMBINED_PATCH" "--silent"
status "Building patched kernel"
build_kernel "patched"
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 03/12] klp-build: Move patch processing to klp-prepare-patch
2026-10-28 21:45 [PATCH v2 00/12] klp-build: Fix dependent patches, add selftests Song Liu
2026-10-28 21:45 ` [PATCH v2 01/12] klp-build: Fix dependent patches in a series Song Liu
2026-10-28 21:45 ` [PATCH v2 02/12] klp-build: Process the patches before building the original kernel Song Liu
@ 2026-10-28 21:45 ` Song Liu
2026-10-28 21:45 ` [PATCH v2 04/12] selftests/livepatch: Add tests for klp-prepare-patch Song Liu
` (8 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Song Liu @ 2026-10-28 21:45 UTC (permalink / raw)
To: live-patching, linux-kselftest
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, shuah,
linux-kernel, Song Liu
klp-build's patch processing doesn't need a kernel build, but the only
way to run it is through klp-build, which builds the kernel twice. That
makes it hard to test.
Move it into a new script, klp-prepare-patch, so that tests can run it
directly. It validates the patches, combines them into a single patch
against the source tree in the current directory, adds #line directives
and prints the result. klp-build runs it and builds the patched kernel
with its output.
klp-prepare-patch doesn't need a configured kernel tree, and it uses its
own temporary directory, so it leaves klp-tmp alone. The intermediate
patch without #line directives, which klp-build left in klp-tmp, now
goes away with that directory.
klp-build still applies and reverts the combined patch, so a little of
the patch handling is duplicated between the two scripts.
Suggested-by: Josh Poimboeuf <jpoimboe@kernel.org>
Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <song@kernel.org>
---
scripts/livepatch/Makefile | 3 +-
scripts/livepatch/klp-build | 106 +----------
scripts/livepatch/klp-prepare-patch | 277 ++++++++++++++++++++++++++++
3 files changed, 287 insertions(+), 99 deletions(-)
create mode 100755 scripts/livepatch/klp-prepare-patch
diff --git a/scripts/livepatch/Makefile b/scripts/livepatch/Makefile
index 17b590213740..d77ef3de7d9a 100644
--- a/scripts/livepatch/Makefile
+++ b/scripts/livepatch/Makefile
@@ -4,7 +4,8 @@
SHELLCHECK := $(shell which shellcheck 2> /dev/null)
SRCS := \
- klp-build
+ klp-build \
+ klp-prepare-patch
.DEFAULT_GOAL := help
.PHONY: help
diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
index 28c3bf6b6418..4796b36b5cc8 100755
--- a/scripts/livepatch/klp-build
+++ b/scripts/livepatch/klp-build
@@ -31,7 +31,7 @@ KEEP_TMP=1
SCRIPT="$(basename "$0")"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-FIX_PATCH_LINES="$SCRIPT_DIR/fix-patch-lines"
+PREPARE_PATCH="$SCRIPT_DIR/klp-prepare-patch"
OBJTOOL="$PWD/tools/objtool/objtool"
CONFIG="$PWD/.config"
@@ -46,7 +46,6 @@ KMOD_DIR="$TMP_DIR/5-kmod"
STASH_DIR="$TMP_DIR/stash"
TIMESTAMP="$TMP_DIR/timestamp"
-PATCH_TMP_DIR="$TMP_DIR/tmp"
COMBINED_PATCH="$TMP_DIR/combined.patch"
KLP_DIFF_LOG="$DIFF_DIR/diff.log"
@@ -329,34 +328,6 @@ set_kernelversion() {
sed -i "2i echo $kernelrelease; exit 0" scripts/setlocalversion
}
-get_patch_files() {
- local patch="$1"
-
- grep0 -E '^(---|\+\+\+) ' "$patch" \
- | grep0 -v -e '/dev/null' -e '1969-12-31' -e '1970-01-01' \
- | gawk '{print $2}' \
- | sed 's|^[^/]*/||' \
- | sort -u
-}
-
-check_unsupported_patches() {
- local patch
-
- for patch in "${PATCHES[@]}"; do
- local files=()
-
- get_patch_files "$patch" | mapfile -t files
-
- for file in "${files[@]}"; do
- case "$file" in
- lib/*|*/vdso/*|*/realmode/rm/*|*.S)
- die "${patch}: unsupported patch to $file"
- ;;
- esac
- done
- done
-}
-
apply_patch() {
local patch="$1"
shift
@@ -394,14 +365,6 @@ revert_patch() {
APPLIED_PATCHES=("${tmp[@]}")
}
-apply_patches() {
- local patch
-
- for patch in "${PATCHES[@]}"; do
- apply_patch "$patch"
- done
-}
-
revert_patches() {
local patches=("${APPLIED_PATCHES[@]}")
@@ -436,78 +399,25 @@ do_init() {
APPLIED_PATCHES=()
- [[ -x "$FIX_PATCH_LINES" ]] || die "can't find fix-patch-lines"
- command -v recountdiff &>/dev/null || die "recountdiff not found (install patchutils)"
+ [[ -x "$PREPARE_PATCH" ]] || die "can't find klp-prepare-patch"
validate_config
set_module_name
set_kernelversion
}
-# Combine the patches into a single patch against the original source. The
-# patches may depend on each other, so they can't be diffed individually.
-combine_patches() {
- local outfile="$1"
- local tmpdir="$PATCH_TMP_DIR"
- local files=()
- local patch
- local file
-
- rm -rf "$tmpdir"
- mkdir -p "$tmpdir"
-
- # Get all source files affected by the patches
- for patch in "${PATCHES[@]}"; do
- get_patch_files "$patch"
- done | sort -u | mapfile -t files
-
- # Copy orig source files to $tmpdir
- for file in "${files[@]}"; do
- if [[ -e "$file" ]]; then
- cp --parents "$file" "$tmpdir"
- fi
- done
-
- # Diff the orig source files against the patched ones in place to make a
- # clean patch. Use /dev/null for created and deleted files.
- : > "$outfile"
- apply_patches
- for file in "${files[@]}"; do
- local old="$tmpdir/$file"
- local new="$file"
- local old_label="a/$file"
- local new_label="b/$file"
- local status=0
-
- if [[ ! -e "$old" ]]; then
- old=/dev/null
- old_label=/dev/null
- fi
- if [[ ! -e "$new" ]]; then
- new=/dev/null
- new_label=/dev/null
- fi
-
- diff -up --label "$old_label" --label "$new_label" "$old" "$new" \
- >> "$outfile" || status=$?
- if (( status > 1 )); then
- die "diff failed for $file"
- fi
- done
- revert_patches
-}
-
# Validate the patches, combine them into $COMBINED_PATCH, and fix its lines so
# as not to affect the __LINE__ macro for otherwise unchanged functions further
# down the file.
process_patches() {
- local tmp_patch="$TMP_DIR/tmp.patch"
+ local cmd=()
- check_unsupported_patches
- combine_patches "$tmp_patch"
- [[ ! -s "$tmp_patch" ]] && die "no changes detected"
+ cmd=("$PREPARE_PATCH")
+ [[ -v VERBOSE ]] && cmd+=("--verbose")
+ cmd+=("--")
+ cmd+=("${PATCHES[@]}")
- "$FIX_PATCH_LINES" "$tmp_patch" | recountdiff > "$COMBINED_PATCH"
+ "${cmd[@]}" > "$COMBINED_PATCH" || die "klp-prepare-patch failed"
}
clean_kernel() {
diff --git a/scripts/livepatch/klp-prepare-patch b/scripts/livepatch/klp-prepare-patch
new file mode 100755
index 000000000000..f6f8524f7088
--- /dev/null
+++ b/scripts/livepatch/klp-prepare-patch
@@ -0,0 +1,277 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Validate the patches given to klp-build and combine them into a single patch
+
+if (( BASH_VERSINFO[0] < 4 || \
+ (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 4) )); then
+ echo "error: this script requires bash 4.4+" >&2
+ exit 1
+fi
+
+set -o errtrace
+set -o pipefail
+set -o nounset
+
+# Allow doing 'cmd | mapfile -t array' instead of 'mapfile -t array < <(cmd)'.
+# This helps keep execution in pipes so pipefail+ERR trap can catch errors.
+shopt -s lastpipe
+
+unset TMP_DIR VERBOSE
+
+SCRIPT="$(basename "$0")"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+FIX_PATCH_LINES="$SCRIPT_DIR/fix-patch-lines"
+
+APPLIED_PATCHES=()
+
+grep0() {
+ # shellcheck disable=SC2317
+ command grep "$@" || true
+}
+
+# Because pipefail is enabled, the grep0 helper should be used instead of
+# grep, otherwise a failed match can propagate to an error.
+grep() {
+ echo "error: $SCRIPT: use grep0 or 'command grep' instead of bare grep" >&2
+ exit 1
+}
+
+warn() {
+ echo "warning: $SCRIPT: $*" >&2
+}
+
+die() {
+ echo "error: $SCRIPT: $*" >&2
+ exit 1
+}
+
+cleanup() {
+ set +o nounset
+ revert_patches
+ [[ -v TMP_DIR ]] && rm -rf "$TMP_DIR"
+ return 0
+}
+
+trap_err() {
+ die "line ${BASH_LINENO[0]}: '$BASH_COMMAND'"
+}
+
+trap cleanup EXIT INT TERM HUP
+trap trap_err ERR
+
+usage() {
+ cat <<EOF >&2
+Usage: $SCRIPT [OPTIONS] PATCH_FILE(s)
+Validate patches and combine them into a single patch for klp-build.
+
+The patches are applied in order to the source tree in the current directory,
+and reverted afterwards. The combined patch is printed to stdout, with #line
+directives added so that the patch doesn't change the line numbers of the code
+after the changed lines.
+
+Options:
+ -v, --verbose Show the details of patches which apply with fuzz
+
+EOF
+}
+
+process_args() {
+ local short
+ local long
+ local args
+ local patch
+
+ short="hv"
+ long="help,verbose"
+
+ args=$(getopt --options "$short" --longoptions "$long" -- "$@") || {
+ echo; usage; exit 1
+ }
+ eval set -- "$args"
+
+ while true; do
+ case "$1" in
+ -h | --help)
+ usage
+ exit 0
+ ;;
+ -v | --verbose)
+ VERBOSE=1
+ shift
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ usage
+ exit 1
+ ;;
+ esac
+ done
+
+ if [[ $# -eq 0 ]]; then
+ usage
+ exit 1
+ fi
+
+ PATCHES=("$@")
+
+ for patch in "${PATCHES[@]}"; do
+ [[ -f "$patch" ]] || die "$patch doesn't exist"
+ done
+}
+
+do_init() {
+ [[ -x "$FIX_PATCH_LINES" ]] || die "can't find fix-patch-lines"
+ command -v recountdiff &>/dev/null || die "recountdiff not found (install patchutils)"
+
+ TMP_DIR="$(mktemp -d)"
+}
+
+get_patch_files() {
+ local patch="$1"
+
+ grep0 -E '^(---|\+\+\+) ' "$patch" \
+ | grep0 -v -e '/dev/null' -e '1969-12-31' -e '1970-01-01' \
+ | gawk '{print $2}' \
+ | sed 's|^[^/]*/||' \
+ | sort -u
+}
+
+check_unsupported_patches() {
+ local patch
+
+ for patch in "${PATCHES[@]}"; do
+ local files=()
+
+ get_patch_files "$patch" | mapfile -t files
+
+ for file in "${files[@]}"; do
+ case "$file" in
+ lib/*|*/vdso/*|*/realmode/rm/*|*.S)
+ die "${patch}: unsupported patch to $file"
+ ;;
+ esac
+ done
+ done
+}
+
+apply_patch() {
+ local patch="$1"
+ local drift_regex="with fuzz|offset [0-9]+ line"
+ local output
+ local status
+
+ status=0
+ output=$(patch -p1 --dry-run --no-backup-if-mismatch -r /dev/null < "$patch" 2>&1) || status=$?
+ if [[ "$status" -ne 0 ]]; then
+ echo "$output" >&2
+ die "$patch did not apply"
+ elif [[ "$output" =~ $drift_regex ]]; then
+ [[ -v VERBOSE ]] && echo "$output" >&2
+ warn "${patch} applied with fuzz"
+ fi
+
+ APPLIED_PATCHES+=("$patch")
+ patch -p1 --no-backup-if-mismatch -r /dev/null --silent < "$patch"
+}
+
+revert_patch() {
+ local patch="$1"
+ local tmp=()
+
+ patch -p1 -R --force --no-backup-if-mismatch -r /dev/null &> /dev/null < "$patch" || true
+
+ for p in "${APPLIED_PATCHES[@]}"; do
+ [[ "$p" == "$patch" ]] && continue
+ tmp+=("$p")
+ done
+
+ APPLIED_PATCHES=("${tmp[@]}")
+}
+
+apply_patches() {
+ local patch
+
+ for patch in "${PATCHES[@]}"; do
+ apply_patch "$patch"
+ done
+}
+
+revert_patches() {
+ local patches=("${APPLIED_PATCHES[@]}")
+
+ for (( i=${#patches[@]}-1 ; i>=0 ; i-- )) ; do
+ revert_patch "${patches[$i]}"
+ done
+
+ APPLIED_PATCHES=()
+}
+
+# Combine the patches into a single patch against the original source. The
+# patches may depend on each other, so they can't be diffed individually.
+combine_patches() {
+ local outfile="$1"
+ local orig_dir="$TMP_DIR/orig"
+ local files=()
+ local patch
+ local file
+
+ mkdir -p "$orig_dir"
+
+ # Get all source files affected by the patches
+ for patch in "${PATCHES[@]}"; do
+ get_patch_files "$patch"
+ done | sort -u | mapfile -t files
+
+ # Copy orig source files to $orig_dir
+ for file in "${files[@]}"; do
+ if [[ -e "$file" ]]; then
+ cp --parents "$file" "$orig_dir"
+ fi
+ done
+
+ # Diff the orig source files against the patched ones in place to make a
+ # clean patch. Use /dev/null for created and deleted files.
+ : > "$outfile"
+ apply_patches
+ for file in "${files[@]}"; do
+ local old="$orig_dir/$file"
+ local new="$file"
+ local old_label="a/$file"
+ local new_label="b/$file"
+ local status=0
+
+ if [[ ! -e "$old" ]]; then
+ old=/dev/null
+ old_label=/dev/null
+ fi
+ if [[ ! -e "$new" ]]; then
+ new=/dev/null
+ new_label=/dev/null
+ fi
+
+ diff -up --label "$old_label" --label "$new_label" "$old" "$new" \
+ >> "$outfile" || status=$?
+ if (( status > 1 )); then
+ die "diff failed for $file"
+ fi
+ done
+ revert_patches
+}
+
+
+################################################################################
+
+process_args "$@"
+do_init
+
+check_unsupported_patches
+combine_patches "$TMP_DIR/combined.patch"
+[[ ! -s "$TMP_DIR/combined.patch" ]] && die "no changes detected"
+
+# Fix the patch lines so as not to affect the __LINE__ macro for otherwise
+# unchanged functions further down the file.
+"$FIX_PATCH_LINES" "$TMP_DIR/combined.patch" | recountdiff
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 04/12] selftests/livepatch: Add tests for klp-prepare-patch
2026-10-28 21:45 [PATCH v2 00/12] klp-build: Fix dependent patches, add selftests Song Liu
` (2 preceding siblings ...)
2026-10-28 21:45 ` [PATCH v2 03/12] klp-build: Move patch processing to klp-prepare-patch Song Liu
@ 2026-10-28 21:45 ` Song Liu
2026-10-28 21:45 ` [PATCH v2 05/12] selftests/livepatch: Test klp-prepare-patch with dependent patches Song Liu
` (7 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Song Liu @ 2026-10-28 21:45 UTC (permalink / raw)
To: live-patching, linux-kselftest
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, shuah,
linux-kernel, Song Liu
Before building the patched kernel, klp-build runs klp-prepare-patch to
combine the patches it is given into a single patch and add #line
directives to it. Mistakes there break the build, or make klp-build
include functions which didn't change.
Add a kselftest target, livepatch/test_klp_build, which runs
klp-prepare-patch on patches to a small source tree and checks the patch
it prints. Nothing is compiled, so the tests don't need a configured
kernel, root, or the livepatch test modules.
Each test creates its source files and patches from heredocs, so it is
self-contained. functions.sh provides:
- setup_tree() creates a scratch source tree and a patch directory
- add_file() and add_patch() fill them
- run_klp_prepare_patch() runs klp-prepare-patch from the top of the
tree and checks that it left the tree as it found it
- expect_patch() checks that klp-prepare-patch succeeded without
warnings and printed the expected patch. Only what the patch changes
is compared: trailing whitespace is ignored, so blank context lines
can be written as empty lines, and so are "diff" lines and timestamps,
which depend on how the patch was generated.
Installed tests don't have the source tree, so klp-prepare-patch and
fix-patch-lines are installed next to them. The tests skip if gawk,
recountdiff or GNU patch is missing.
kselftest prints several lines for every test. For running the tests
by hand, add run-tests.sh and a "tests" make target, which print one
line per test case, the details of any failure, and the totals.
Start with a test of a single patch, which gets a #line directive after
the lines it adds.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <song@kernel.org>
---
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/livepatch/README | 16 +++
.../livepatch/test_klp_build/Makefile | 19 +++
.../livepatch/test_klp_build/functions.sh | 135 ++++++++++++++++++
.../livepatch/test_klp_build/run-tests.sh | 68 +++++++++
.../livepatch/test_klp_build/test-basic.sh | 57 ++++++++
6 files changed, 296 insertions(+)
create mode 100644 tools/testing/selftests/livepatch/test_klp_build/Makefile
create mode 100644 tools/testing/selftests/livepatch/test_klp_build/functions.sh
create mode 100755 tools/testing/selftests/livepatch/test_klp_build/run-tests.sh
create mode 100755 tools/testing/selftests/livepatch/test_klp_build/test-basic.sh
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 2d960626750e..30d46cb71cbb 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -64,6 +64,7 @@ TARGETS += kvm
TARGETS += landlock
TARGETS += lib
TARGETS += livepatch
+TARGETS += livepatch/test_klp_build
TARGETS += liveupdate
TARGETS += lkdtm
TARGETS += lsm
diff --git a/tools/testing/selftests/livepatch/README b/tools/testing/selftests/livepatch/README
index d2035dd64a2b..b8727d72469e 100644
--- a/tools/testing/selftests/livepatch/README
+++ b/tools/testing/selftests/livepatch/README
@@ -44,6 +44,22 @@ or
% make kselftest TARGETS=livepatch
+klp-build tests
+---------------
+
+The test_klp_build directory holds tests for klp-prepare-patch, which
+scripts/livepatch/klp-build uses to prepare the patches it is given. They
+don't build a kernel or load any modules, so they don't need root, and they
+are a kselftest target of their own:
+
+ % make -C tools/testing/selftests TARGETS=livepatch/test_klp_build run_tests
+
+For one line of output per test case, which is easier to read when running
+them by hand:
+
+ % make -C tools/testing/selftests/livepatch/test_klp_build tests
+
+
Adding tests
------------
diff --git a/tools/testing/selftests/livepatch/test_klp_build/Makefile b/tools/testing/selftests/livepatch/test_klp_build/Makefile
new file mode 100644
index 000000000000..71ae793b0595
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_klp_build/Makefile
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+
+TEST_PROGS := \
+ test-basic.sh
+
+TEST_PROGS_EXTENDED := functions.sh
+
+# Installed tests run without the source tree, so install the scripts too
+TEST_FILES := \
+ ../../../../../scripts/livepatch/klp-prepare-patch \
+ ../../../../../scripts/livepatch/fix-patch-lines
+
+include ../../lib.mk
+
+# Run the tests by hand, with one line of output per test case
+tests:
+ @./run-tests.sh $(TEST_PROGS)
+
+.PHONY: tests
diff --git a/tools/testing/selftests/livepatch/test_klp_build/functions.sh b/tools/testing/selftests/livepatch/test_klp_build/functions.sh
new file mode 100644
index 000000000000..6b2f30d72f99
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_klp_build/functions.sh
@@ -0,0 +1,135 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Shell functions for the klp-build tests.
+#
+# Each test creates a small source tree, adds some patches, runs
+# klp-prepare-patch on them and checks the patch it prints. Nothing gets
+# compiled, so the source files don't need to build.
+
+export LC_ALL=C
+
+# Kselftest framework requirement - SKIP code is 4
+ksft_skip=4
+
+# skip(msg) - testing can't proceed
+# msg - explanation
+function skip() {
+ echo "SKIP: $1" >&2
+ exit $ksft_skip
+}
+
+# die(msg) - game over, man
+# msg - dying words
+function die() {
+ echo "ERROR: $1" >&2
+ exit 1
+}
+
+TEST_DIR="$(cd "$(dirname "$0")" && pwd)"
+
+# Installed tests have copies of the scripts next to them
+if [[ -x "$TEST_DIR/klp-prepare-patch" ]]; then
+ SCRIPT_DIR="$TEST_DIR"
+else
+ SCRIPT_DIR="$TEST_DIR/../../../../../scripts/livepatch"
+fi
+[[ -x "$SCRIPT_DIR/klp-prepare-patch" ]] || skip "klp-prepare-patch not found"
+
+for cmd in gawk recountdiff; do
+ command -v "$cmd" > /dev/null || skip "$cmd not found"
+done
+patch --version 2> /dev/null | grep -q "GNU patch" || skip "GNU patch not found"
+
+# setup_tree() - create an empty source tree for klp-prepare-patch to run in
+#
+# Patches are kept outside the tree.
+function setup_tree() {
+ WORK_DIR="$(mktemp -d)" || die "mktemp failed"
+ trap 'rm -rf "$WORK_DIR"' EXIT
+
+ TREE="$WORK_DIR/tree"
+ PATCH_DIR="$WORK_DIR/patches"
+
+ mkdir -p "$TREE" "$PATCH_DIR"
+}
+
+# add_file(path) - add a source file read from stdin to the tree
+# path - path of the file, relative to the top of the tree
+function add_file() {
+ mkdir -p "$TREE/$(dirname "$1")"
+ cat > "$TREE/$1"
+}
+
+# add_patch(name) - add a patch read from stdin
+# name - file name of the patch
+function add_patch() {
+ cat > "$PATCH_DIR/$1"
+}
+
+# start_test(name) - start a test
+# name - what the test checks
+function start_test() {
+ echo -n "TEST: $1 ... "
+}
+
+# not_ok(msg, [file]) - report a failed test and exit
+# msg - what went wrong
+# file - details to show
+function not_ok() {
+ echo -e "not ok\n"
+ echo "$1"
+ [[ $# -gt 1 ]] && cat "$2"
+ echo
+ die "klp-build kselftest(s) failed"
+}
+
+# run_klp_prepare_patch(patch...) - run klp-prepare-patch from the top of the
+# tree, and check that it leaves the tree as it found it
+# patch - name of a patch added with add_patch()
+function run_klp_prepare_patch() {
+ local patches=()
+ local patch
+
+ for patch in "$@"; do
+ patches+=("$PATCH_DIR/$patch")
+ done
+
+ rm -rf "$WORK_DIR/orig"
+ cp -a "$TREE" "$WORK_DIR/orig"
+
+ STATUS=0
+ (cd "$TREE" && "$SCRIPT_DIR/klp-prepare-patch" "${patches[@]}") \
+ > "$WORK_DIR/stdout" 2> "$WORK_DIR/stderr" || STATUS=$?
+
+ diff -ru "$WORK_DIR/orig" "$TREE" > "$WORK_DIR/tree.diff" ||
+ not_ok "klp-prepare-patch left the source tree modified:" "$WORK_DIR/tree.diff"
+}
+
+# expect_patch() - check that klp-prepare-patch succeeded without warnings, and
+# printed the patch read from stdin
+#
+# Only what the patch changes is compared. Trailing whitespace is ignored, so
+# blank context lines can be written as empty lines, and so are "diff" lines
+# and timestamps. A file diffed against an epoch timestamp counts as
+# /dev/null.
+function expect_patch() {
+ local expected="$WORK_DIR/expected"
+ local result="$WORK_DIR/result"
+ local normalize=(sed -E
+ -e '/^diff /d'
+ -e 's/^(---|\+\+\+) [^\t]*\t(1969-12-31|1970-01-01) .*/\1 \/dev\/null/'
+ -e 's/^((---|\+\+\+) [^\t]*)\t.*/\1/'
+ -e 's/[[:space:]]*$//')
+
+ [[ $STATUS -eq 0 ]] || not_ok "klp-prepare-patch failed:" "$WORK_DIR/stderr"
+ [[ -s "$WORK_DIR/stderr" ]] && not_ok "klp-prepare-patch warned:" "$WORK_DIR/stderr"
+
+ "${normalize[@]}" > "$expected"
+ "${normalize[@]}" "$WORK_DIR/stdout" > "$result"
+
+ diff -u --label expected --label result "$expected" "$result" > "$WORK_DIR/result.diff" ||
+ not_ok "unexpected patch:" "$WORK_DIR/result.diff"
+
+ echo "ok"
+}
diff --git a/tools/testing/selftests/livepatch/test_klp_build/run-tests.sh b/tools/testing/selftests/livepatch/test_klp_build/run-tests.sh
new file mode 100755
index 000000000000..d67770006d7f
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_klp_build/run-tests.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Run the klp-build tests with less output than "make run_tests": one line
+# per test case, the details of any failure, and the totals.
+#
+# Usage: run-tests.sh [test-*.sh...]
+
+cd "$(dirname "$0")" || exit 1
+
+tests=("$@")
+[[ ${#tests[@]} -eq 0 ]] && tests=(test-*.sh)
+
+pass=0
+fail=0
+skip=0
+
+echo "TAP version 13"
+
+for test in "${tests[@]}"; do
+ name="${test%.sh}"
+ failed=0
+ cases=0
+ status=0
+
+ out="$(./"$test" 2>&1)" || status=$?
+
+ if [[ $status -eq 4 ]]; then
+ echo "ok - $name # SKIP $(sed -n 's/^SKIP: //p' <<< "$out")"
+ skip=$((skip + 1))
+ continue
+ fi
+
+ while IFS= read -r line; do
+ case "$line" in
+ "TEST: "*" ... ok")
+ line="${line#TEST: }"
+ echo "ok - $name: ${line% ... ok}"
+ pass=$((pass + 1))
+ cases=$((cases + 1))
+ ;;
+ "TEST: "*" ... not ok")
+ line="${line#TEST: }"
+ echo "not ok - $name: ${line% ... not ok}"
+ failed=1
+ cases=$((cases + 1))
+ ;;
+ "ERROR: klp-build kselftest(s) failed" | "")
+ ;;
+ *)
+ [[ $failed -eq 1 ]] && echo "# $line"
+ ;;
+ esac
+ done <<< "$out"
+
+ if [[ $failed -eq 0 && ( $status -ne 0 || $cases -eq 0 ) ]]; then
+ echo "not ok - $name: exited with status $status after $cases test case(s)"
+ sed 's/^/# /' <<< "$out"
+ failed=1
+ fi
+
+ fail=$((fail + failed))
+done
+
+echo "1..$((pass + fail + skip))"
+echo "# pass:$pass fail:$fail skip:$skip"
+
+[[ $fail -eq 0 ]]
diff --git a/tools/testing/selftests/livepatch/test_klp_build/test-basic.sh b/tools/testing/selftests/livepatch/test_klp_build/test-basic.sh
new file mode 100755
index 000000000000..127d678a07ce
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_klp_build/test-basic.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+. "$(dirname "$0")/functions.sh"
+
+setup_tree
+
+add_file drivers/foo/foo.c <<'EOT'
+int foo(int x)
+{
+ if (x < 0)
+ return -1;
+ return x;
+}
+
+int bar(void)
+{
+ return __LINE__;
+}
+EOT
+
+# - a patch which adds lines to foo() gets a #line directive after them, so
+# bar() still sees its original __LINE__
+
+start_test "single patch"
+
+add_patch 0001-foo.patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -1,6 +1,8 @@
+ int foo(int x)
+ {
++ if (!x)
++ return 0;
+ if (x < 0)
+ return -1;
+ return x;
+ }
+EOT
+
+run_klp_prepare_patch 0001-foo.patch
+
+expect_patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -1,5 +1,8 @@
+ int foo(int x)
+ {
++ if (!x)
++ return 0;
++#line 3
+ if (x < 0)
+ return -1;
+ return x;
+EOT
+
+exit 0
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 05/12] selftests/livepatch: Test klp-prepare-patch with dependent patches
2026-10-28 21:45 [PATCH v2 00/12] klp-build: Fix dependent patches, add selftests Song Liu
` (3 preceding siblings ...)
2026-10-28 21:45 ` [PATCH v2 04/12] selftests/livepatch: Add tests for klp-prepare-patch Song Liu
@ 2026-10-28 21:45 ` Song Liu
2026-10-28 21:45 ` [PATCH v2 06/12] selftests/livepatch: Test klp-prepare-patch with independent patches Song Liu
` (6 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Song Liu @ 2026-10-28 21:45 UTC (permalink / raw)
To: live-patching, linux-kselftest
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, shuah,
linux-kernel, Song Liu
Add a test case where the second patch changes lines added by the
first, so it only applies on top of it. klp-prepare-patch has to
combine the two into a single patch against the original source, rather
than fixing each one against the original source on its own.
The case uses the same source file and first patch as the single patch
test, so add it to test-basic.sh.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <song@kernel.org>
---
.../livepatch/test_klp_build/test-basic.sh | 36 +++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/tools/testing/selftests/livepatch/test_klp_build/test-basic.sh b/tools/testing/selftests/livepatch/test_klp_build/test-basic.sh
index 127d678a07ce..70a653ee72aa 100755
--- a/tools/testing/selftests/livepatch/test_klp_build/test-basic.sh
+++ b/tools/testing/selftests/livepatch/test_klp_build/test-basic.sh
@@ -54,4 +54,40 @@ expect_patch <<'EOT'
return x;
EOT
+# - a patch which changes lines added by an earlier patch doesn't apply to the
+# original source on its own, but the combined patch does
+
+start_test "patch which depends on an earlier one"
+
+add_patch 0002-foo-again.patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -1,8 +1,8 @@
+ int foo(int x)
+ {
+ if (!x)
+- return 0;
++ return 1;
+ if (x < 0)
+ return -1;
+ return x;
+ }
+EOT
+
+run_klp_prepare_patch 0001-foo.patch 0002-foo-again.patch
+
+expect_patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -1,5 +1,8 @@
+ int foo(int x)
+ {
++ if (!x)
++ return 1;
++#line 3
+ if (x < 0)
+ return -1;
+ return x;
+EOT
+
exit 0
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 06/12] selftests/livepatch: Test klp-prepare-patch with independent patches
2026-10-28 21:45 [PATCH v2 00/12] klp-build: Fix dependent patches, add selftests Song Liu
` (4 preceding siblings ...)
2026-10-28 21:45 ` [PATCH v2 05/12] selftests/livepatch: Test klp-prepare-patch with dependent patches Song Liu
@ 2026-10-28 21:45 ` Song Liu
2026-10-28 21:45 ` [PATCH v2 07/12] selftests/livepatch: Test klp-prepare-patch with patches which cancel out Song Liu
` (5 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Song Liu @ 2026-10-28 21:45 UTC (permalink / raw)
To: live-patching, linux-kselftest
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, shuah,
linux-kernel, Song Liu
Add a test where two patches change different functions of the same
file, and the second also changes a header. The patches are combined
into one patch per file, and the #line directives use line numbers from
the original file, even for the function the first patch moved down.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <song@kernel.org>
---
.../livepatch/test_klp_build/Makefile | 3 +-
.../test-independent-patches.sh | 124 ++++++++++++++++++
2 files changed, 126 insertions(+), 1 deletion(-)
create mode 100755 tools/testing/selftests/livepatch/test_klp_build/test-independent-patches.sh
diff --git a/tools/testing/selftests/livepatch/test_klp_build/Makefile b/tools/testing/selftests/livepatch/test_klp_build/Makefile
index 71ae793b0595..50205a047dcd 100644
--- a/tools/testing/selftests/livepatch/test_klp_build/Makefile
+++ b/tools/testing/selftests/livepatch/test_klp_build/Makefile
@@ -1,7 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
TEST_PROGS := \
- test-basic.sh
+ test-basic.sh \
+ test-independent-patches.sh
TEST_PROGS_EXTENDED := functions.sh
diff --git a/tools/testing/selftests/livepatch/test_klp_build/test-independent-patches.sh b/tools/testing/selftests/livepatch/test_klp_build/test-independent-patches.sh
new file mode 100755
index 000000000000..eebb075da7ce
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_klp_build/test-independent-patches.sh
@@ -0,0 +1,124 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+. "$(dirname "$0")/functions.sh"
+
+setup_tree
+
+add_file drivers/foo/foo.c <<'EOT'
+int foo(int x)
+{
+ if (x < 0)
+ return -1;
+ return x;
+}
+
+int bar(void)
+{
+ return __LINE__;
+}
+
+int baz(int x)
+{
+ if (x > 100)
+ return 100;
+ return x;
+}
+EOT
+
+add_file include/linux/foo.h <<'EOT'
+#ifndef _LINUX_FOO_H
+#define _LINUX_FOO_H
+
+int foo(int x);
+int bar(void);
+
+#endif
+EOT
+
+# - patches to different parts of a file are combined into one patch, with
+# #line numbers from the original file even where an earlier patch moved
+# the lines down
+
+start_test "patches to different functions of a file"
+
+add_patch 0001-foo.patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -1,6 +1,8 @@
+ int foo(int x)
+ {
++ if (!x)
++ return 0;
+ if (x < 0)
+ return -1;
+ return x;
+ }
+EOT
+
+add_patch 0002-baz.patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -14,7 +14,7 @@ int bar(void)
+
+ int baz(int x)
+ {
+- if (x > 100)
+- return 100;
++ if (x > FOO_MAX)
++ return FOO_MAX;
+ return x;
+ }
+--- a/include/linux/foo.h
++++ b/include/linux/foo.h
+@@ -1,6 +1,8 @@
+ #ifndef _LINUX_FOO_H
+ #define _LINUX_FOO_H
+
++#define FOO_MAX 100
++
+ int foo(int x);
+ int bar(void);
+
+EOT
+
+run_klp_prepare_patch 0001-foo.patch 0002-baz.patch
+
+expect_patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -1,5 +1,8 @@
+ int foo(int x)
+ {
++ if (!x)
++ return 0;
++#line 3
+ if (x < 0)
+ return -1;
+ return x;
+@@ -12,7 +15,8 @@ int bar(void)
+
+ int baz(int x)
+ {
+- if (x > 100)
+- return 100;
++ if (x > FOO_MAX)
++ return FOO_MAX;
++#line 17
+ return x;
+ }
+--- a/include/linux/foo.h
++++ b/include/linux/foo.h
+@@ -1,6 +1,9 @@
+ #ifndef _LINUX_FOO_H
+ #define _LINUX_FOO_H
+
++#define FOO_MAX 100
++
++#line 4
+ int foo(int x);
+ int bar(void);
+
+EOT
+
+exit 0
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 07/12] selftests/livepatch: Test klp-prepare-patch with patches which cancel out
2026-10-28 21:45 [PATCH v2 00/12] klp-build: Fix dependent patches, add selftests Song Liu
` (5 preceding siblings ...)
2026-10-28 21:45 ` [PATCH v2 06/12] selftests/livepatch: Test klp-prepare-patch with independent patches Song Liu
@ 2026-10-28 21:45 ` Song Liu
2026-10-28 21:45 ` [PATCH v2 08/12] selftests/livepatch: Test klp-prepare-patch with created and deleted files Song Liu
` (4 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Song Liu @ 2026-10-28 21:45 UTC (permalink / raw)
To: live-patching, linux-kselftest
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, shuah,
linux-kernel, Song Liu
Add a test where a patch is followed by its revert. On their own the
two leave nothing to build, so klp-prepare-patch fails with "no changes
detected", and klp-build stops before building anything. With another
patch after them, only that patch's change is left in the combined
patch.
Add expect_error() to functions.sh to check for a failure with a given
error message.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <song@kernel.org>
---
.../livepatch/test_klp_build/Makefile | 3 +-
.../livepatch/test_klp_build/functions.sh | 14 +++
.../test_klp_build/test-cancelling-patches.sh | 102 ++++++++++++++++++
3 files changed, 118 insertions(+), 1 deletion(-)
create mode 100755 tools/testing/selftests/livepatch/test_klp_build/test-cancelling-patches.sh
diff --git a/tools/testing/selftests/livepatch/test_klp_build/Makefile b/tools/testing/selftests/livepatch/test_klp_build/Makefile
index 50205a047dcd..158a08d8a1b8 100644
--- a/tools/testing/selftests/livepatch/test_klp_build/Makefile
+++ b/tools/testing/selftests/livepatch/test_klp_build/Makefile
@@ -2,7 +2,8 @@
TEST_PROGS := \
test-basic.sh \
- test-independent-patches.sh
+ test-independent-patches.sh \
+ test-cancelling-patches.sh
TEST_PROGS_EXTENDED := functions.sh
diff --git a/tools/testing/selftests/livepatch/test_klp_build/functions.sh b/tools/testing/selftests/livepatch/test_klp_build/functions.sh
index 6b2f30d72f99..e28988d07bc9 100644
--- a/tools/testing/selftests/livepatch/test_klp_build/functions.sh
+++ b/tools/testing/selftests/livepatch/test_klp_build/functions.sh
@@ -133,3 +133,17 @@ function expect_patch() {
echo "ok"
}
+
+# expect_error(msg) - check that klp-prepare-patch failed with an error message
+# msg - the message, without the "error: klp-prepare-patch: " prefix
+function expect_error() {
+ [[ $STATUS -ne 0 ]] ||
+ not_ok "klp-prepare-patch succeeded unexpectedly:" "$WORK_DIR/stdout"
+ [[ -s "$WORK_DIR/stdout" ]] &&
+ not_ok "klp-prepare-patch printed a patch:" "$WORK_DIR/stdout"
+
+ grep -qxF "error: klp-prepare-patch: $1" "$WORK_DIR/stderr" ||
+ not_ok "klp-prepare-patch didn't fail with \"$1\":" "$WORK_DIR/stderr"
+
+ echo "ok"
+}
diff --git a/tools/testing/selftests/livepatch/test_klp_build/test-cancelling-patches.sh b/tools/testing/selftests/livepatch/test_klp_build/test-cancelling-patches.sh
new file mode 100755
index 000000000000..da6a12064be9
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_klp_build/test-cancelling-patches.sh
@@ -0,0 +1,102 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+. "$(dirname "$0")/functions.sh"
+
+setup_tree
+
+add_file drivers/foo/foo.c <<'EOT'
+int foo(int x)
+{
+ if (x < 0)
+ return -1;
+ return x;
+}
+
+int bar(void)
+{
+ return __LINE__;
+}
+
+int baz(int x)
+{
+ if (x > 100)
+ return 100;
+ return x;
+}
+EOT
+
+add_patch 0001-foo.patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -1,6 +1,8 @@
+ int foo(int x)
+ {
++ if (!x)
++ return 0;
+ if (x < 0)
+ return -1;
+ return x;
+ }
+EOT
+
+add_patch 0002-revert-foo.patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -1,8 +1,6 @@
+ int foo(int x)
+ {
+- if (!x)
+- return 0;
+ if (x < 0)
+ return -1;
+ return x;
+ }
+EOT
+
+add_patch 0003-baz.patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -12,7 +12,7 @@ int bar(void)
+
+ int baz(int x)
+ {
+- if (x > 100)
+- return 100;
++ if (x > 1000)
++ return 1000;
+ return x;
+ }
+EOT
+
+# - a patch and its revert leave nothing to build
+
+start_test "patch and its revert"
+
+run_klp_prepare_patch 0001-foo.patch 0002-revert-foo.patch
+
+expect_error "no changes detected"
+
+# - with another patch after them, only that patch's change is left
+
+start_test "patch and its revert, then another patch"
+
+run_klp_prepare_patch 0001-foo.patch 0002-revert-foo.patch 0003-baz.patch
+
+expect_patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -12,7 +12,8 @@ int bar(void)
+
+ int baz(int x)
+ {
+- if (x > 100)
+- return 100;
++ if (x > 1000)
++ return 1000;
++#line 17
+ return x;
+ }
+EOT
+
+exit 0
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 08/12] selftests/livepatch: Test klp-prepare-patch with created and deleted files
2026-10-28 21:45 [PATCH v2 00/12] klp-build: Fix dependent patches, add selftests Song Liu
` (6 preceding siblings ...)
2026-10-28 21:45 ` [PATCH v2 07/12] selftests/livepatch: Test klp-prepare-patch with patches which cancel out Song Liu
@ 2026-10-28 21:45 ` Song Liu
2026-10-28 21:45 ` [PATCH v2 09/12] selftests/livepatch: Test where klp-prepare-patch adds #line directives Song Liu
` (3 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Song Liu @ 2026-10-28 21:45 UTC (permalink / raw)
To: live-patching, linux-kselftest
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, shuah,
linux-kernel, Song Liu
Add a test of patches which create and delete files. Both are diffed
against /dev/null, and the created file gets no #line directive, as it
has no original line numbers to preserve. A file created by one patch
and changed by the next is a new file in the combined patch.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <song@kernel.org>
---
.../livepatch/test_klp_build/Makefile | 3 +-
.../test-new-and-deleted-files.sh | 94 +++++++++++++++++++
2 files changed, 96 insertions(+), 1 deletion(-)
create mode 100755 tools/testing/selftests/livepatch/test_klp_build/test-new-and-deleted-files.sh
diff --git a/tools/testing/selftests/livepatch/test_klp_build/Makefile b/tools/testing/selftests/livepatch/test_klp_build/Makefile
index 158a08d8a1b8..2818264fb31a 100644
--- a/tools/testing/selftests/livepatch/test_klp_build/Makefile
+++ b/tools/testing/selftests/livepatch/test_klp_build/Makefile
@@ -3,7 +3,8 @@
TEST_PROGS := \
test-basic.sh \
test-independent-patches.sh \
- test-cancelling-patches.sh
+ test-cancelling-patches.sh \
+ test-new-and-deleted-files.sh
TEST_PROGS_EXTENDED := functions.sh
diff --git a/tools/testing/selftests/livepatch/test_klp_build/test-new-and-deleted-files.sh b/tools/testing/selftests/livepatch/test_klp_build/test-new-and-deleted-files.sh
new file mode 100755
index 000000000000..560221854b3e
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_klp_build/test-new-and-deleted-files.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+. "$(dirname "$0")/functions.sh"
+
+setup_tree
+
+add_file drivers/foo/old.c <<'EOT'
+int old(void)
+{
+ return 0;
+}
+EOT
+
+# - created and deleted files are diffed against /dev/null, and a created
+# file gets no #line directive since it has no original line numbers
+
+start_test "created and deleted files"
+
+add_patch 0001-new-and-old.patch <<'EOT'
+--- /dev/null
++++ b/drivers/foo/new.c
+@@ -0,0 +1,4 @@
++int new(void)
++{
++ return 1;
++}
+--- a/drivers/foo/old.c
++++ /dev/null
+@@ -1,4 +0,0 @@
+-int old(void)
+-{
+- return 0;
+-}
+EOT
+
+run_klp_prepare_patch 0001-new-and-old.patch
+
+expect_patch <<'EOT'
+--- /dev/null
++++ b/drivers/foo/new.c
+@@ -0,0 +1,4 @@
++int new(void)
++{
++ return 1;
++}
+--- a/drivers/foo/old.c
++++ /dev/null
+@@ -1,4 +0,0 @@
+-int old(void)
+-{
+- return 0;
+-}
+EOT
+
+# - a file created by one patch and changed by the next is a new file in the
+# combined patch
+
+start_test "created file changed by a later patch"
+
+add_patch 0001-new.patch <<'EOT'
+--- /dev/null
++++ b/drivers/foo/new.c
+@@ -0,0 +1,4 @@
++int new(void)
++{
++ return 1;
++}
+EOT
+
+add_patch 0002-new-again.patch <<'EOT'
+--- a/drivers/foo/new.c
++++ b/drivers/foo/new.c
+@@ -1,4 +1,4 @@
+ int new(void)
+ {
+- return 1;
++ return 2;
+ }
+EOT
+
+run_klp_prepare_patch 0001-new.patch 0002-new-again.patch
+
+expect_patch <<'EOT'
+--- /dev/null
++++ b/drivers/foo/new.c
+@@ -0,0 +1,4 @@
++int new(void)
++{
++ return 2;
++}
+EOT
+
+exit 0
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 09/12] selftests/livepatch: Test where klp-prepare-patch adds #line directives
2026-10-28 21:45 [PATCH v2 00/12] klp-build: Fix dependent patches, add selftests Song Liu
` (7 preceding siblings ...)
2026-10-28 21:45 ` [PATCH v2 08/12] selftests/livepatch: Test klp-prepare-patch with created and deleted files Song Liu
@ 2026-10-28 21:45 ` Song Liu
2026-10-28 21:45 ` [PATCH v2 10/12] selftests/livepatch: Test klp-prepare-patch with unsupported files Song Liu
` (2 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Song Liu @ 2026-10-28 21:45 UTC (permalink / raw)
To: live-patching, linux-kselftest
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, shuah,
linux-kernel, Song Liu
Add a test of where #line directives go:
- each group of changed lines in a hunk gets its own directive
- after removed lines, the directive skips over them
- files other than C sources and headers get none
- lines added at the end of a file get none, as nothing after them
needs renumbering
Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <song@kernel.org>
---
.../livepatch/test_klp_build/Makefile | 3 +-
.../test_klp_build/test-line-directives.sh | 175 ++++++++++++++++++
2 files changed, 177 insertions(+), 1 deletion(-)
create mode 100755 tools/testing/selftests/livepatch/test_klp_build/test-line-directives.sh
diff --git a/tools/testing/selftests/livepatch/test_klp_build/Makefile b/tools/testing/selftests/livepatch/test_klp_build/Makefile
index 2818264fb31a..902a62bfca86 100644
--- a/tools/testing/selftests/livepatch/test_klp_build/Makefile
+++ b/tools/testing/selftests/livepatch/test_klp_build/Makefile
@@ -4,7 +4,8 @@ TEST_PROGS := \
test-basic.sh \
test-independent-patches.sh \
test-cancelling-patches.sh \
- test-new-and-deleted-files.sh
+ test-new-and-deleted-files.sh \
+ test-line-directives.sh
TEST_PROGS_EXTENDED := functions.sh
diff --git a/tools/testing/selftests/livepatch/test_klp_build/test-line-directives.sh b/tools/testing/selftests/livepatch/test_klp_build/test-line-directives.sh
new file mode 100755
index 000000000000..9f599e64c330
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_klp_build/test-line-directives.sh
@@ -0,0 +1,175 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+. "$(dirname "$0")/functions.sh"
+
+setup_tree
+
+# - each group of changed lines in a hunk gets its own #line directive
+
+start_test "two groups of changes in one hunk"
+
+add_file drivers/foo/groups.c <<'EOT'
+int foo(int x)
+{
+ int y = 0;
+
+ if (x < 0)
+ y = -1;
+ if (x > 100)
+ y = 100;
+ return y;
+}
+EOT
+
+add_patch 0001-groups.patch <<'EOT'
+--- a/drivers/foo/groups.c
++++ b/drivers/foo/groups.c
+@@ -1,9 +1,11 @@
+ int foo(int x)
+ {
+- int y = 0;
++ int y = x;
+
+ if (x < 0)
+ y = -1;
++ else if (!x)
++ y = 1;
+ if (x > 100)
+ y = 100;
+ return y;
+EOT
+
+run_klp_prepare_patch 0001-groups.patch
+
+expect_patch <<'EOT'
+--- a/drivers/foo/groups.c
++++ b/drivers/foo/groups.c
+@@ -1,9 +1,13 @@
+ int foo(int x)
+ {
+- int y = 0;
++ int y = x;
++#line 4
+
+ if (x < 0)
+ y = -1;
++ else if (!x)
++ y = 1;
++#line 7
+ if (x > 100)
+ y = 100;
+ return y;
+EOT
+
+# - after removed lines, the #line directive skips over them
+
+start_test "removed lines"
+
+add_file drivers/foo/remove.c <<'EOT'
+int foo(int x)
+{
+ if (x < 0)
+ return -1;
+ return x;
+}
+EOT
+
+add_patch 0001-remove.patch <<'EOT'
+--- a/drivers/foo/remove.c
++++ b/drivers/foo/remove.c
+@@ -1,6 +1,4 @@
+ int foo(int x)
+ {
+- if (x < 0)
+- return -1;
+ return x;
+ }
+EOT
+
+run_klp_prepare_patch 0001-remove.patch
+
+expect_patch <<'EOT'
+--- a/drivers/foo/remove.c
++++ b/drivers/foo/remove.c
+@@ -1,6 +1,5 @@
+ int foo(int x)
+ {
+- if (x < 0)
+- return -1;
++#line 5
+ return x;
+ }
+EOT
+
+# - only C sources and headers get #line directives
+
+start_test "change to a Makefile"
+
+add_file drivers/foo/Makefile <<'EOT'
+obj-y += foo.o
+obj-y += groups.o
+EOT
+
+add_patch 0001-makefile.patch <<'EOT'
+--- a/drivers/foo/Makefile
++++ b/drivers/foo/Makefile
+@@ -1,2 +1,3 @@
+ obj-y += foo.o
++obj-y += new.o
+ obj-y += groups.o
+EOT
+
+run_klp_prepare_patch 0001-makefile.patch
+
+expect_patch <<'EOT'
+--- a/drivers/foo/Makefile
++++ b/drivers/foo/Makefile
+@@ -1,2 +1,3 @@
+ obj-y += foo.o
++obj-y += new.o
+ obj-y += groups.o
+EOT
+
+# - lines added at the end of a file have nothing after them to renumber
+
+start_test "lines added at the end of a file"
+
+add_file drivers/foo/tail.c <<'EOT'
+int foo(void)
+{
+ return 0;
+}
+EOT
+
+add_patch 0001-tail.patch <<'EOT'
+--- a/drivers/foo/tail.c
++++ b/drivers/foo/tail.c
+@@ -2,3 +2,8 @@ int foo(void)
+ {
+ return 0;
+ }
++
++int bar(void)
++{
++ return 1;
++}
+EOT
+
+run_klp_prepare_patch 0001-tail.patch
+
+expect_patch <<'EOT'
+--- a/drivers/foo/tail.c
++++ b/drivers/foo/tail.c
+@@ -2,3 +2,8 @@ int foo(void)
+ {
+ return 0;
+ }
++
++int bar(void)
++{
++ return 1;
++}
+EOT
+
+exit 0
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 10/12] selftests/livepatch: Test klp-prepare-patch with unsupported files
2026-10-28 21:45 [PATCH v2 00/12] klp-build: Fix dependent patches, add selftests Song Liu
` (8 preceding siblings ...)
2026-10-28 21:45 ` [PATCH v2 09/12] selftests/livepatch: Test where klp-prepare-patch adds #line directives Song Liu
@ 2026-10-28 21:45 ` Song Liu
2026-10-28 21:45 ` [PATCH v2 11/12] selftests/livepatch: Test klp-prepare-patch with patches which don't apply Song Liu
2026-10-28 21:45 ` [PATCH v2 12/12] selftests/livepatch: Test klp-prepare-patch with offset and fuzz Song Liu
11 siblings, 0 replies; 13+ messages in thread
From: Song Liu @ 2026-10-28 21:45 UTC (permalink / raw)
To: live-patching, linux-kselftest
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, shuah,
linux-kernel, Song Liu
Add a test that klp-prepare-patch rejects patches to lib/, the vDSO,
real mode code and assembly files, which klp-build can't build a
livepatch for.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <song@kernel.org>
---
.../livepatch/test_klp_build/Makefile | 3 +-
.../test_klp_build/test-unsupported-files.sh | 30 +++++++++++++++++++
2 files changed, 32 insertions(+), 1 deletion(-)
create mode 100755 tools/testing/selftests/livepatch/test_klp_build/test-unsupported-files.sh
diff --git a/tools/testing/selftests/livepatch/test_klp_build/Makefile b/tools/testing/selftests/livepatch/test_klp_build/Makefile
index 902a62bfca86..02feb1ad52d0 100644
--- a/tools/testing/selftests/livepatch/test_klp_build/Makefile
+++ b/tools/testing/selftests/livepatch/test_klp_build/Makefile
@@ -5,7 +5,8 @@ TEST_PROGS := \
test-independent-patches.sh \
test-cancelling-patches.sh \
test-new-and-deleted-files.sh \
- test-line-directives.sh
+ test-line-directives.sh \
+ test-unsupported-files.sh
TEST_PROGS_EXTENDED := functions.sh
diff --git a/tools/testing/selftests/livepatch/test_klp_build/test-unsupported-files.sh b/tools/testing/selftests/livepatch/test_klp_build/test-unsupported-files.sh
new file mode 100755
index 000000000000..eb360923ecb1
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_klp_build/test-unsupported-files.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+. "$(dirname "$0")/functions.sh"
+
+setup_tree
+
+# - patches to lib/, the vDSO, real mode code and assembly are rejected
+
+for file in lib/string.c \
+ arch/x86/entry/vdso/vma.c \
+ arch/x86/realmode/rm/video-mode.c \
+ arch/x86/kernel/head_64.S; do
+
+ start_test "patch to $file"
+
+ add_patch unsupported.patch <<EOT
+--- a/$file
++++ b/$file
+@@ -1 +1 @@
+-int x;
++int y;
+EOT
+
+ run_klp_prepare_patch unsupported.patch
+
+ expect_error "$PATCH_DIR/unsupported.patch: unsupported patch to $file"
+done
+
+exit 0
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 11/12] selftests/livepatch: Test klp-prepare-patch with patches which don't apply
2026-10-28 21:45 [PATCH v2 00/12] klp-build: Fix dependent patches, add selftests Song Liu
` (9 preceding siblings ...)
2026-10-28 21:45 ` [PATCH v2 10/12] selftests/livepatch: Test klp-prepare-patch with unsupported files Song Liu
@ 2026-10-28 21:45 ` Song Liu
2026-10-28 21:45 ` [PATCH v2 12/12] selftests/livepatch: Test klp-prepare-patch with offset and fuzz Song Liu
11 siblings, 0 replies; 13+ messages in thread
From: Song Liu @ 2026-10-28 21:45 UTC (permalink / raw)
To: live-patching, linux-kselftest
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, shuah,
linux-kernel, Song Liu
Add a test that klp-prepare-patch rejects a patch which doesn't match
the source, and that when the second patch of a series doesn't apply,
the first one is reverted, leaving the source tree as it was.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <song@kernel.org>
---
.../livepatch/test_klp_build/Makefile | 3 +-
.../test_klp_build/test-patch-not-applying.sh | 73 +++++++++++++++++++
2 files changed, 75 insertions(+), 1 deletion(-)
create mode 100755 tools/testing/selftests/livepatch/test_klp_build/test-patch-not-applying.sh
diff --git a/tools/testing/selftests/livepatch/test_klp_build/Makefile b/tools/testing/selftests/livepatch/test_klp_build/Makefile
index 02feb1ad52d0..3650de4d6975 100644
--- a/tools/testing/selftests/livepatch/test_klp_build/Makefile
+++ b/tools/testing/selftests/livepatch/test_klp_build/Makefile
@@ -6,7 +6,8 @@ TEST_PROGS := \
test-cancelling-patches.sh \
test-new-and-deleted-files.sh \
test-line-directives.sh \
- test-unsupported-files.sh
+ test-unsupported-files.sh \
+ test-patch-not-applying.sh
TEST_PROGS_EXTENDED := functions.sh
diff --git a/tools/testing/selftests/livepatch/test_klp_build/test-patch-not-applying.sh b/tools/testing/selftests/livepatch/test_klp_build/test-patch-not-applying.sh
new file mode 100755
index 000000000000..32e871dc7ffc
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_klp_build/test-patch-not-applying.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+. "$(dirname "$0")/functions.sh"
+
+setup_tree
+
+add_file drivers/foo/foo.c <<'EOT'
+int foo(int x)
+{
+ if (x < 0)
+ return -1;
+ return x;
+}
+EOT
+
+# - a patch which doesn't match the source is rejected
+
+start_test "patch which doesn't apply"
+
+add_patch 0001-bad.patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -1,6 +1,6 @@
+ int foo(int x)
+ {
+- if (x > 0)
++ if (x >= 0)
+ return -1;
+ return x;
+ }
+EOT
+
+run_klp_prepare_patch 0001-bad.patch
+
+expect_error "$PATCH_DIR/0001-bad.patch did not apply"
+
+# - when a later patch doesn't apply, the earlier ones are reverted
+
+start_test "series whose second patch doesn't apply"
+
+add_patch 0001-foo.patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -1,6 +1,8 @@
+ int foo(int x)
+ {
++ if (!x)
++ return 0;
+ if (x < 0)
+ return -1;
+ return x;
+ }
+EOT
+
+add_patch 0002-bad.patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -3,6 +3,6 @@
+ if (!x)
+ return 0;
+- if (x > 0)
++ if (x >= 0)
+ return -1;
+ return x;
+ }
+EOT
+
+run_klp_prepare_patch 0001-foo.patch 0002-bad.patch
+
+expect_error "$PATCH_DIR/0002-bad.patch did not apply"
+
+exit 0
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v2 12/12] selftests/livepatch: Test klp-prepare-patch with offset and fuzz
2026-10-28 21:45 [PATCH v2 00/12] klp-build: Fix dependent patches, add selftests Song Liu
` (10 preceding siblings ...)
2026-10-28 21:45 ` [PATCH v2 11/12] selftests/livepatch: Test klp-prepare-patch with patches which don't apply Song Liu
@ 2026-10-28 21:45 ` Song Liu
11 siblings, 0 replies; 13+ messages in thread
From: Song Liu @ 2026-10-28 21:45 UTC (permalink / raw)
To: live-patching, linux-kselftest
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, shuah,
linux-kernel, Song Liu
klp-prepare-patch accepts patches which apply with an offset or fuzz,
and warns about them. Add a test that both are accepted with a
warning, and that the combined patch has the line numbers and context of
the real source, so the #line directives refer to the right lines.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Song Liu <song@kernel.org>
---
.../livepatch/test_klp_build/Makefile | 3 +-
.../test_klp_build/test-offset-and-fuzz.sh | 120 ++++++++++++++++++
2 files changed, 122 insertions(+), 1 deletion(-)
create mode 100755 tools/testing/selftests/livepatch/test_klp_build/test-offset-and-fuzz.sh
diff --git a/tools/testing/selftests/livepatch/test_klp_build/Makefile b/tools/testing/selftests/livepatch/test_klp_build/Makefile
index 3650de4d6975..0daeea7a7db1 100644
--- a/tools/testing/selftests/livepatch/test_klp_build/Makefile
+++ b/tools/testing/selftests/livepatch/test_klp_build/Makefile
@@ -7,7 +7,8 @@ TEST_PROGS := \
test-new-and-deleted-files.sh \
test-line-directives.sh \
test-unsupported-files.sh \
- test-patch-not-applying.sh
+ test-patch-not-applying.sh \
+ test-offset-and-fuzz.sh
TEST_PROGS_EXTENDED := functions.sh
diff --git a/tools/testing/selftests/livepatch/test_klp_build/test-offset-and-fuzz.sh b/tools/testing/selftests/livepatch/test_klp_build/test-offset-and-fuzz.sh
new file mode 100755
index 000000000000..8e36d5aa026f
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_klp_build/test-offset-and-fuzz.sh
@@ -0,0 +1,120 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+. "$(dirname "$0")/functions.sh"
+
+# expect_warning(msg) - check that klp-prepare-patch warned, and drop the
+# warning so expect_patch() doesn't complain about it
+# msg - the warning, without the "warning: klp-prepare-patch: " prefix
+function expect_warning() {
+ local line="warning: klp-prepare-patch: $1"
+
+ grep -qxF "$line" "$WORK_DIR/stderr" ||
+ not_ok "klp-prepare-patch didn't warn \"$1\":" "$WORK_DIR/stderr"
+
+ grep -vxF "$line" "$WORK_DIR/stderr" > "$WORK_DIR/stderr.rest"
+ mv "$WORK_DIR/stderr.rest" "$WORK_DIR/stderr"
+}
+
+setup_tree
+
+add_file drivers/foo/foo.c <<'EOT'
+int foo(int x)
+{
+ if (x < 0)
+ return -1;
+ return x;
+}
+
+int bar(void)
+{
+ return __LINE__;
+}
+
+int baz(int x)
+{
+ if (x > 100)
+ return 100;
+ return x;
+}
+EOT
+
+# - a patch made against a source with fewer lines applies at an offset, and
+# the combined patch has the real line numbers
+
+start_test "patch with an offset"
+
+add_patch 0001-offset.patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -9,7 +9,7 @@
+
+ int baz(int x)
+ {
+- if (x > 100)
+- return 100;
++ if (x > 1000)
++ return 1000;
+ return x;
+ }
+EOT
+
+run_klp_prepare_patch 0001-offset.patch
+
+expect_warning "$PATCH_DIR/0001-offset.patch applied with fuzz"
+expect_patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -12,7 +12,8 @@ int bar(void)
+
+ int baz(int x)
+ {
+- if (x > 100)
+- return 100;
++ if (x > 1000)
++ return 1000;
++#line 17
+ return x;
+ }
+EOT
+
+# - a patch whose context differs from the source applies with fuzz, and the
+# combined patch has the real context
+
+start_test "patch with fuzz"
+
+add_patch 0001-fuzz.patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -12,7 +12,7 @@
+ /* clamp x */
+ int baz(int x)
+ {
+- if (x > 100)
+- return 100;
++ if (x > 1000)
++ return 1000;
+ return x;
+ }
+EOT
+
+run_klp_prepare_patch 0001-fuzz.patch
+
+expect_warning "$PATCH_DIR/0001-fuzz.patch applied with fuzz"
+expect_patch <<'EOT'
+--- a/drivers/foo/foo.c
++++ b/drivers/foo/foo.c
+@@ -12,7 +12,8 @@ int bar(void)
+
+ int baz(int x)
+ {
+- if (x > 100)
+- return 100;
++ if (x > 1000)
++ return 1000;
++#line 17
+ return x;
+ }
+EOT
+
+exit 0
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] 13+ messages in thread