The klp-build script is currently very strict with input patches, requiring them to apply cleanly via `git apply --recount`. This prevents the use of patches with minor contextual fuzz relative to the target kernel sources.
Add an optional -z/--fuzz option to allow klp-build to "rebase" input patches within its klp-tmp/ scratch space. When enabled, the script utilizes GNU patch's fuzzy matching to apply changes to a temporary directory and then creates a normalized version of the patch using `git diff --no-index`. This rebased patch contains the exact line counts and context required for the subsequent klp-build fixup and build steps, allowing users to reuse a patch across similar kernel streams. Signed-off-by: Joe Lawrence <[email protected]> --- scripts/livepatch/klp-build | 105 +++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) Using the same 1-line-offset input combined.patch from the previous patch in this set and adding --fuzz, we can successfully now build it: $ ./scripts/livepatch/klp-build -T --fuzz combined.patch Rebasing 1 patch(es) -> combined.patch patching file fs/proc/cmdline.c Hunk #1 succeeded at 7 (offset 1 line). patching file fs/proc/version.c patching file fs/proc/cmdline.c Hunk #1 succeeded at 7 (offset 1 line). patching file fs/proc/version.c Validating patch(es) Building original kernel Copying original object files Fixing patch(es) Building patched kernel Copying patched object files Diffing objects vmlinux.o: changed function: cmdline_proc_show vmlinux.o: changed function: version_proc_show Building patch module: livepatch-combined.ko SUCCESS diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index 2313bc909f58..535ca18e32c5 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -26,6 +26,7 @@ REPLACE=1 SHORT_CIRCUIT=0 JOBS="$(getconf _NPROCESSORS_ONLN)" VERBOSE="-s" +FUZZ_FACTOR="" shopt -o xtrace | grep -q 'on' && XTRACE=1 # Avoid removing the previous $TMP_DIR until args have been fully processed. @@ -49,6 +50,7 @@ KMOD_DIR="$TMP_DIR/kmod" STASH_DIR="$TMP_DIR/stash" TIMESTAMP="$TMP_DIR/timestamp" PATCH_TMP_DIR="$TMP_DIR/tmp" +REBASE_DIR="$TMP_DIR/rebase" KLP_DIFF_LOG="$DIFF_DIR/diff.log" @@ -131,6 +133,7 @@ Advanced Options: 3|diff Diff objects 4|kmod Build patch module -T, --keep-tmp Preserve tmp dir on exit + -z, --fuzz[=NUM] Rebase patches using fuzzy matching [default: 2] EOF } @@ -145,8 +148,8 @@ process_args() { local long local args - short="hfj:o:vdS:T" - long="help,show-first-changed,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp" + short="hfj:o:vdS:Tz::" + long="help,show-first-changed,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp,fuzz::" args=$(getopt --options "$short" --longoptions "$long" -- "$@") || { echo; usage; exit @@ -204,6 +207,14 @@ process_args() { keep_tmp=1 shift ;; + -z | --fuzz) + if [[ -n "$2" ]]; then + FUZZ_FACTOR="$2" + else + FUZZ_FACTOR=2 + fi + shift 2 + ;; --) shift break @@ -304,6 +315,94 @@ get_patch_files() { | sort -u } +# Rebase a patch using GNU patch with fuzz +# Outputs path to rebased patch on success, non-zero on failure +rebase_patch() { + local idx="$1" + local input_patch="$2" + local patch_name="$(basename "$input_patch" .patch)" + local work_dir="$REBASE_DIR/$idx-$patch_name" + local output_patch="$work_dir/rebased.patch" + local files=() + local file + + rm -rf "$work_dir" + mkdir -p "$work_dir/orig" "$work_dir/patched" + + get_patch_files "$input_patch" | mapfile -t files + + # Copy original files (before patch) + for file in "${files[@]}"; do + [[ "$file" == "dev/null" ]] && continue + if [[ -f "$SRC/$file" ]]; then + mkdir -p "$work_dir/orig/$(dirname "$file")" + cp -f "$SRC/$file" "$work_dir/orig/$file" + fi + done + + # Apply with fuzz + ( + cd "$SRC" + sed -n '/^-- /q;p' "$input_patch" | \ + patch -p1 \ + -F"$FUZZ_FACTOR" \ + --no-backup-if-mismatch \ + -r /dev/null \ + --forward >&2 + ) || return 1 + + # Copy patched files (after patch) + for file in "${files[@]}"; do + [[ "$file" == "dev/null" ]] && continue + if [[ -f "$SRC/$file" ]]; then + mkdir -p "$work_dir/patched/$(dirname "$file")" + cp -f "$SRC/$file" "$work_dir/patched/$file" + fi + done + + # Revert with fuzz + ( + cd "$SRC" + sed -n '/^-- /q;p' "$input_patch" | \ + patch -p1 -R \ + -F"$FUZZ_FACTOR" \ + --no-backup-if-mismatch \ + -r /dev/null >&2 + ) || { + warn "fuzzy revert failed; source tree may be corrupted" + return 1 + } + + # Generate clean patch from captured state + ( cd "$work_dir" && git diff --no-index --no-prefix orig patched ) > "$output_patch" || true + + echo "$output_patch" +} + +# If the user specified --fuzz, iterate through PATCHES and rebase them +# Updates PATCHES array in-place with rebased patch paths +maybe_rebase_patches() { + local i + local idx + local patch + local rebased + + [[ -z "$FUZZ_FACTOR" ]] && return 0 + + status "Rebasing ${#PATCHES[@]} patch(es)" + + mkdir -p "$REBASE_DIR" + + idx=0001 + for i in "${!PATCHES[@]}"; do + patch="${PATCHES[$i]}" + echo "-> $(basename "$patch")" + rebased=$(rebase_patch "$idx" "$patch") || die "rebase failed: $patch" + PATCHES[i]="$rebased" + idx=$(printf "%04d" $(( 10#$idx + 1 ))) + done +} + # Make sure git re-stats the changed files git_refresh() { local patch="$1" @@ -807,6 +906,8 @@ build_patch_module() { process_args "$@" do_init +maybe_rebase_patches + if (( SHORT_CIRCUIT <= 1 )); then status "Validating patch(es)" validate_patches -- 2.52.0
