Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: dependencies
run: sudo apt-get install -y libelf-dev linux-headers-$(uname -r) shellcheck elfutils
run: sudo apt-get install -y libelf-dev linux-headers-$(uname -r) shellcheck elfutils gawk
- name: make
run: make
- name: submodule update
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ check:
shellcheck test/difftree.sh test/integration/kpatch-test \
test/integration/lib.sh test/integration/rebase-patches \
test/integration/test-vagrant \
test/integration/vm-integration-run
test/integration/vm-integration-run test/module-version-test.sh
bash test/module-version-test.sh

help:
@echo "kpatch Makefile"
Expand Down
21 changes: 20 additions & 1 deletion kpatch-build/kpatch-build
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ declare -a PATCH_LIST
APPLIED_PATCHES=0
OOT_MODULE=
KLP_REPLACE=1
MODVERSION=""

GCC="${CROSS_COMPILE:-}gcc"
CLANG="${CROSS_COMPILE:-}clang"
Expand Down Expand Up @@ -785,6 +786,7 @@ usage() {
echo " -j, --jobs Specify the number of make jobs" >&2
echo " -t, --target Specify custom kernel build targets" >&2
echo " -n, --name Specify the name of the kpatch module" >&2
echo " --module-version Set the patch module's version metadata" >&2
echo " -o, --output Specify output folder" >&2
echo " -d, --debug Enable 'xtrace' and keep scratch files" >&2
echo " in <CACHEDIR>/tmp" >&2
Expand All @@ -803,7 +805,7 @@ if ! command -v gawk &> /dev/null; then
die "gawk not installed"
fi

options="$(getopt -o ha:r:s:c:v:j:t:n:o:dR -l "help,archversion:,sourcerpm:,sourcedir:,config:,vmlinux:,jobs:,target:,name:,output:,oot-module:,oot-module-src:,debug,skip-gcc-check,skip-compiler-check,skip-cleanup,non-replace,version" -- "$@")" || die "getopt failed"
options="$(getopt -o ha:r:s:c:v:j:t:n:o:dR -l "help,archversion:,sourcerpm:,sourcedir:,config:,vmlinux:,jobs:,target:,name:,module-version:,output:,oot-module:,oot-module-src:,debug,skip-gcc-check,skip-compiler-check,skip-cleanup,non-replace,version" -- "$@")" || die "getopt failed"

eval set -- "$options"

Expand Down Expand Up @@ -850,6 +852,13 @@ while [[ $# -gt 0 ]]; do
MODNAME="$(module_name_string "$2")"
shift
;;
--module-version)
# Restrict the value to a safe C string and a single modinfo line.
[[ "$2" =~ ^[a-zA-Z0-9][a-zA-Z0-9._+-]{0,63}$ ]] ||
die "Invalid module version: expected 1-64 ASCII characters, starting with a letter or digit, followed by letters, digits, '.', '_', '+' or '-'"
MODVERSION="$2"
shift
;;
-o|--output)
[[ ! -d "$2" ]] && die "output dir '$2' not found"
BASE="$(readlink -f "$2")"
Expand Down Expand Up @@ -1293,6 +1302,10 @@ remove_patches
# interfere with cleanup later, so ensure the $TEMPDIR is read/write.
cp -LR "$DATADIR/patch" "$TEMPDIR" || die
chmod -R u+rw "$TEMPDIR" || die
if [[ -n "$MODVERSION" ]]; then
printf '\nMODULE_VERSION("%s");\n' "$MODVERSION" >> "$TEMPDIR/patch/patch-hook.c" ||
die "Failed to add module version"
fi

declare -a ARCH_MAKEVARS
if [[ "$ARCH" = "ppc64le" ]]; then
Expand Down Expand Up @@ -1585,6 +1598,12 @@ if [[ "$USE_KLP" -eq 1 ]]; then
[[ "$rc" -ne 0 ]] && die "create-klp-module: exited with return code: $rc"
fi

if [[ -n "$MODVERSION" ]]; then
actual_version=$(modinfo -F version "$TEMPDIR/patch/$MODNAME.ko") ||
die "Failed to read module version"
[[ "$actual_version" = "$MODVERSION" ]] || die "Module version mismatch"
fi

if [[ -n "$CONFIG_MODVERSIONS" ]]; then
# Check that final module does not reference symbols with different version
# than the target kernel
Expand Down
10 changes: 10 additions & 0 deletions man/kpatch-build.1
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ effect.
-n|--name
Specify the name of the kpatch module

--module-version <version>
Set MODULE_VERSION metadata in the generated patch module.
The value must contain 1-64 ASCII characters: an initial letter or digit,
followed by letters, digits, '.', '_', '+' or '-'.
Read it with 'modinfo -F version <module.ko>' or, after loading, from
/sys/module/<module_name>/version. This is a caller-supplied identifier,
not an indication of livepatch activation or symbol version compatibility.
If omitted, no version metadata is added. Unlike --version, this option
does not print the version of the kpatch-build tool.

-o|--output
Specify output folder

Expand Down
44 changes: 44 additions & 0 deletions test/module-version-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/bash
# Fast CLI regression tests: no kernel build, Docker, or root access required.
set -euo pipefail
export LC_ALL=C

BUILD="$(dirname "$(readlink -f "$0")")/../kpatch-build/kpatch-build"
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT

fail() {
echo "FAIL: $*" >&2
exit 1
}

command -v gawk >/dev/null || fail "gawk is required by kpatch-build"

# --help exits after option parsing, before source preparation or compilation.
max=$(printf '%064d' 0)
for version in "0" "1.2.3-rc1+test_2" "0123456789abcdef0123456789abcdef01234567" "$max"; do
"$BUILD" --module-version "$version" --help >"$TMP/out" 2>"$TMP/err" ||
fail "valid version rejected: $version"
done

for version in "" "${max}0" "-1" "a b" 'a"b' 'a\b' $'a\nb' $'a\rb' \
"a/b" "a;exit" "\$(false)" $'\xc3\xa9'; do
if "$BUILD" --module-version "$version" --help >"$TMP/out" 2>"$TMP/err"; then
fail "invalid version accepted: $version"
fi
grep -q "Invalid module version" "$TMP/err" ||
fail "unexpected failure for invalid version: $version"
done

if "$BUILD" --module-version >"$TMP/out" 2>"$TMP/err"; then
fail "missing argument accepted"
fi
grep -q "requires an argument" "$TMP/err" || fail "missing-argument diagnostic not found"

# The new option must not conflict with the existing tool-version option.
"$BUILD" --version >"$TMP/out" 2>"$TMP/err" || fail "--version failed"
grep -q "Version :" "$TMP/out" || fail "tool version missing"
"$BUILD" --help >"$TMP/out" 2>"$TMP/err" || fail "--help failed"
grep -q -- "--module-version" "$TMP/err" || fail "option missing from help"

echo "module-version CLI tests: PASS"