diff --git a/.travis.yml b/.travis.yml index 1c1fda6..b7dbd7a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,14 @@ before_install: - sudo apt-get -qq update - sudo apt-get install -y libelf-dev linux-headers-$(uname -r) shellcheck elfutils +jobs: + include: + - name: "Default" + - name: "-O2" + env: CFLAGS="-O2" + - name: "-O3" + env: CFLAGS="-O3" + script: - make - make unit diff --git a/Makefile b/Makefile index 6fb06c7..44cf771 100644 --- a/Makefile +++ b/Makefile @@ -7,10 +7,14 @@ UNINSTALL_DIRS = $(SUBDIRS:%=uninstall-%) CLEAN_DIRS = $(SUBDIRS:%=clean-%) UNITTEST_DIR = test/unit +INTEGRATION_DIR = test/integration CLEAN_DIRS += clean-$(UNITTEST_DIR) .PHONY: all install uninstall clean check unit .PHONY: $(SUBDIRS) $(BUILD_DIRS) $(INSTALL_DIRS) $(CLEAN_DIRS) +.PHONY: integration integration-slow integration-quick +.PHONY: vagrant-integration-slow vagrant-integration-quick vagrant-integration +.PHONY: vagrant-install all: $(BUILD_DIRS) @@ -32,5 +36,34 @@ $(CLEAN_DIRS): unit: $(UNITTEST_DIR)/Makefile build-kpatch-build $(MAKE) -C $(UNITTEST_DIR) +integration: integration-quick + +integration-slow: $(INTEGRATION_DIR)/Makefile build-kpatch-build build-kpatch build-kmod + $(MAKE) -C $(INTEGRATION_DIR) slow + +integration-quick: $(INTEGRATION_DIR)/Makefile build-kpatch-build build-kpatch build-kmod + $(MAKE) -C $(INTEGRATION_DIR) quick + +vagrant-install: $(INTEGRATION_DIR)/lib.sh +ifneq ($(shell id -u), 0) + @echo "WARNING: This target is intended for use on freshly-installed machines/vms only." && \ + echo "Do not proceed unless you read $(INTEGRATION_DIR)/lib.sh and realise what this target does." && \ + echo "Press ctrl-c to abort, return to proceed." && \ + read +endif + source $(INTEGRATION_DIR)/lib.sh && kpatch_check_install_vagrant + +vagrant-integration: vagrant-integration-quick + +vagrant-integration-slow: + $(MAKE) -C $(INTEGRATION_DIR) vagrant-slow + +vagrant-integration-quick: + $(MAKE) -C $(INTEGRATION_DIR) vagrant-quick + check: shellcheck kpatch/kpatch kpatch-build/kpatch-build kpatch-build/kpatch-gcc + 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 diff --git a/Makefile.inc b/Makefile.inc index 7351047..15049f3 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -17,10 +17,5 @@ MANDIR = $(DESTDIR)$(PREFIX)/share/man/man1 SYSTEMDDIR = $(DESTDIR)$(PREFIX)/lib/systemd/system UPSTARTDIR = $(DESTDIR)/etc/init -# The core module is only supported on x86_64 -ifeq ($(ARCH),x86_64) -BUILDMOD ?= yes -endif - .PHONY: all install clean .DEFAULT: all diff --git a/README.md b/README.md index 4b38f9a..f66b5a1 100644 --- a/README.md +++ b/README.md @@ -20,100 +20,58 @@ And a few more: - https://www.youtube.com/watch?v=rN0sFjrJQfU - https://www.youtube.com/watch?v=Mftc80KyjA4 +Table of contents +================= + +- [Supported Architectures](#supported-architectures) +- [Installation](#installation) + - [Prerequisites](#prerequisites) + - [Fedora, RHEL, CentOS](#fedora-rhel-centos) + - [Oracle Linux 7](#oracle-linux-7) + - [Ubuntu](#ubuntu) + - [Debian 9 (Stretch)](#debian-9-stretch) + - [Debian 8 (Jessie)](#debian-8-jessie) + - [Debian 7 (Lenny)](#debian-7-lenny) + - [Gentoo](#gentoo) + - [Build](#build) + - [Install](#install) +- [Quick start](#quick-start) +- [Patch Author Guide](#patch-author-guide) +- [How it works](#how-it-works) + - [kpatch-build](#kpatch-build) + - [Patching](#patching) +- [Limitations](#limitations) +- [Frequently Asked Questions](#frequently-asked-questions) +- [Get involved](#get-involved) +- [License](#license) + + +Supported Architectures +----------------------- + +- [x] x86-64 +- [x] ppc64le +- [ ] arm64 +- [ ] s390 + Installation ------------ ### Prerequisites -#### Fedora - -*NOTE: You'll need about 15GB of free disk space for the kpatch-build cache in -`~/.kpatch` and for ccache.* - -Install the dependencies for compiling kpatch: +Before starting, see [Supported Architectures](#supported-architectures) and check if your device's architecture is supported. -```bash -UNAME=$(uname -r) -sudo dnf install gcc kernel-devel-${UNAME%.*} elfutils elfutils-devel -``` - -Install the dependencies for the "kpatch-build" command: - -```bash -sudo dnf install pesign yum-utils openssl wget numactl-devel -sudo dnf builddep kernel-${UNAME%.*} -sudo dnf debuginfo-install kernel-${UNAME%.*} - -# optional, but highly recommended -sudo dnf install ccache -ccache --max-size=5G - -# optional, for kpatch-test -sudo dnf install patchutils -``` - -#### RHEL 7 +#### Fedora, RHEL, CentOS *NOTE: You'll need about 15GB of free disk space for the kpatch-build cache in `~/.kpatch` and for ccache.* -Install the dependencies for compiling kpatch: +Install the dependencies for compiling kpatch and running kpatch-build: ```bash -UNAME=$(uname -r) -sudo yum install gcc kernel-devel-${UNAME%.*} elfutils elfutils-devel -``` - -Install the dependencies for the "kpatch-build" command: - -```bash -sudo yum-config-manager --enable rhel-7-server-optional-rpms -sudo yum install pesign yum-utils zlib-devel \ - binutils-devel newt-devel python-devel perl-ExtUtils-Embed \ - audit-libs-devel numactl-devel pciutils-devel bison ncurses-devel - -sudo yum-builddep kernel-${UNAME%.*} -sudo debuginfo-install kernel-${UNAME%.*} - -# optional, but highly recommended -sudo yum install https://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/c/ccache-3.3.4-1.el7.x86_64.rpm -ccache --max-size=5G - -# optional, for kpatch-test -sudo yum install patchutils -``` - -#### CentOS 7 - -*NOTE: You'll need about 15GB of free disk space for the kpatch-build cache in -`~/.kpatch` and for ccache.* - -Install the dependencies for compiling kpatch: - -```bash -UNAME=$(uname -r) -sudo yum install gcc kernel-devel-${UNAME%.*} elfutils elfutils-devel -``` - -Install the dependencies for the "kpatch-build" command: - -```bash -sudo yum install pesign yum-utils zlib-devel \ - binutils-devel newt-devel python-devel perl-ExtUtils-Embed \ - audit-libs audit-libs-devel numactl-devel pciutils-devel bison - -# enable CentOS 7 debug repo -sudo yum-config-manager --enable debug - -sudo yum-builddep kernel-${UNAME%.*} -sudo debuginfo-install kernel-${UNAME%.*} - -# optional, but highly recommended - enable EPEL 7 -sudo yum install ccache -ccache --max-size=5G - -# optional, for kpatch-test -sudo yum install patchutils +source test/integration/lib.sh +# Will request root privileges +kpatch_dependencies ``` #### Oracle Linux 7 @@ -133,7 +91,7 @@ Install the dependencies for the "kpatch-build" command: ```bash sudo yum install pesign yum-utils zlib-devel \ binutils-devel newt-devel python-devel perl-ExtUtils-Embed \ - audit-libs numactl-devel pciutils-devel bison + audit-libs numactl-devel pciutils-devel bison patchutils # enable ol7_optional_latest repo sudo yum-config-manager --enable ol7_optional_latest @@ -147,54 +105,24 @@ rpm -ivh https://oss.oracle.com/ol7/debuginfo/kernel-debuginfo-common-x86_64-$(u # optional, but highly recommended - enable EPEL 7 sudo yum install ccache ccache --max-size=5G - -# optional, for kpatch-test -sudo yum install patchutils ``` -#### Ubuntu 14.04 +#### Ubuntu *NOTE: You'll need about 15GB of free disk space for the kpatch-build cache in `~/.kpatch` and for ccache.* -Install the dependencies for compiling kpatch: - -```bash -apt-get install make gcc libelf-dev -``` - -Install the dependencies for the "kpatch-build" command: +Install the dependencies for compiling kpatch and running kpatch-build ```bash -apt-get install dpkg-dev devscripts -apt-get build-dep linux - -# optional, but highly recommended -apt-get install ccache -ccache --max-size=5G +source test/integration/lib.sh +# required on ppc64le +# e.g., on Ubuntu 18.04 for gcc-7.3 +apt-get install gcc-7-plugin-dev +# Will request root privileges +kpatch_dependencies ``` -Install kernel debug symbols: - -```bash -# Add ddebs repository -codename=$(lsb_release -sc) -sudo tee /etc/apt/sources.list.d/ddebs.list << EOF -deb http://ddebs.ubuntu.com/ ${codename} main restricted universe multiverse -deb http://ddebs.ubuntu.com/ ${codename}-security main restricted universe multiverse -deb http://ddebs.ubuntu.com/ ${codename}-updates main restricted universe multiverse -deb http://ddebs.ubuntu.com/ ${codename}-proposed main restricted universe multiverse -EOF - -# add APT key -wget -Nq http://ddebs.ubuntu.com/dbgsym-release-key.asc -O- | sudo apt-key add - -apt-get update && apt-get install linux-image-$(uname -r)-dbgsym -``` -If there are no packages published yet to the codename-security pocket, the -apt update may report a "404 Not Found" error, as well as a complaint about -disabling the repository by default. This message may be ignored (see issue -#710). - #### Debian 9 (Stretch) Since Stretch the stock kernel can be used without changes, however the @@ -232,6 +160,10 @@ Install the dependencies for the "kpatch-build" command: apt-get install dpkg-dev apt-get build-dep linux + # required on ppc64le + # e.g., on stretch for gcc-6.3 + apt-get install gcc-6-plugin-dev + # optional, but highly recommended apt-get install ccache ccache --max-size=5G @@ -459,11 +391,10 @@ Limitations supported. kpatch-build will return an error if the patch attempts to do so. -- Patches which modify statically allocated data are not supported. - kpatch-build will detect that and return an error. (In the future - we will add a facility to support it. It will probably require the - user to write code which runs at patch module loading time which manually - updates the data.) +- Patches which modify statically allocated data are not directly supported. + kpatch-build will detect that and return an error. This limitation can be + overcome by using callbacks or shadow variables, as described in the + [Patch Author Guide](doc/patch-author-guide.md). - Patches which change the way a function interacts with dynamically allocated data might be safe, or might not. It isn't possible for @@ -634,9 +565,27 @@ sys_nanosleep(), etc?** **Q. Can you patch out-of-tree modules?** -- Yes, though it's currently a bit of a manual process. See this - [message](https://www.redhat.com/archives/kpatch/2015-June/msg00004.html) on - the kpatch mailing list for more information. +Yes! There's a few requirements, and the feature is still in its infancy. + +1. You need to use the `--oot-module` flag to specify the version of the +module that's currently running on the machine. +2. `--sourcedir` has to be passed with a directory containing the same +version of code as the running module, all set up and ready to build with a +`make` command. For example, some modules need `autogen.sh` and +`./configure` to have been run with the appropriate flags to match the +currently-running module. +3. If the `Module.symvers` file for the out-of-tree module doesn't appear +in the root of the provided source directory, a symlink needs to be created +in that directory that points to its actual location. +4. Usually you'll need to pass the `--target` flag as well, to specify the +proper `make` target names. +5. This has only been tested for a single out-of-tree module per patch, and +not for out-of-tree modules with dependencies on other out-of-tree modules +built separately. + +***Sample invocation*** + +`kpatch-build --sourcedir ~/test/ --target default --oot-module /lib/modules/$(uname -r)/extra/test.ko test.patch` Get involved diff --git a/contrib/kpatch.service b/contrib/kpatch.service index 5286f6c..6240256 100644 --- a/contrib/kpatch.service +++ b/contrib/kpatch.service @@ -1,12 +1,13 @@ [Unit] Description="Apply kpatch kernel patches" ConditionKernelCommandLine=!kpatch.enable=0 +Before=network-pre.target +Wants=network-pre.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=PREFIX/sbin/kpatch load --all -ExecStop=PREFIX/sbin/kpatch unload --all [Install] WantedBy=multi-user.target diff --git a/contrib/kpatch.spec b/contrib/kpatch.spec index d8caf4c..676e404 100644 --- a/contrib/kpatch.spec +++ b/contrib/kpatch.spec @@ -1,6 +1,12 @@ +# needed for the kernel specific module +%define KVER %(uname -r) + +# Don't build kpatch kernel module by default +%bcond_with kpatch_mod + Name: kpatch Summary: Dynamic kernel patching -Version: 0.6.1 +Version: 0.9.2 License: GPLv2 Group: System Environment/Kernel URL: http://github.com/dynup/kpatch @@ -9,11 +15,12 @@ Source0: %{name}-%{version}.tar.gz Requires: kmod bash BuildRequires: gcc kernel-devel elfutils elfutils-devel +%if %{with kpatch_mod} +BuildRequires: kernel-devel-uname-r = %{KVER} +BuildRequires: kernel-uname-r = %{KVER} +%endif BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) -# needed for the kernel specific module -%define KVER %(uname -r) - %description kpatch is a Linux dynamic kernel patching tool which allows you to patch a running kernel without rebooting or restarting any processes. It enables @@ -47,6 +54,7 @@ having to wait for long-running tasks to complete, users to log off, or for scheduled reboot windows. It gives more control over up-time without sacrificing security or stability. +%if %{with kpatch_mod} %package %{KVER} Requires: %{name} Summary: Dynamic kernel patching @@ -58,17 +66,18 @@ having to wait for long-running tasks to complete, users to log off, or for scheduled reboot windows. It gives more control over up-time without sacrificing security or stability. +%endif %prep %setup -q %build -make %{_smp_mflags} +make %{_smp_mflags} %{?with_kpatch_mod: BUILDMOD=yes KPATCH_BUILD=/lib/modules/%{KVER}/build} %install rm -rf %{buildroot} -make install PREFIX=/%{_usr} DESTDIR=%{buildroot} +make install PREFIX=/%{_usr} DESTDIR=%{buildroot} %{?with_kpatch_mod: BUILDMOD=yes KPATCH_BUILD=/lib/modules/%{KVER}/build} %clean rm -rf %{buildroot} @@ -81,9 +90,11 @@ rm -rf %{buildroot} %{_usr}/lib/systemd/system/* %{_sysconfdir}/init/kpatch.conf +%if %{with kpatch_mod} %files %{KVER} %defattr(-,root,root,-) %{_usr}/lib/kpatch/%{KVER} +%endif %files build %defattr(-,root,root,-) @@ -93,6 +104,94 @@ rm -rf %{buildroot} %{_mandir}/man1/kpatch-build.1* %changelog +* Tue Sep 8 2020 Joe Lawrence - 0.9.2 +- Integration test support for rhel-{7.8,7.9,8.1,8.2}, centos-8 +- Better support for gcc child functions +- Batch jump label errors to report all instances +- Dynrela code cleanup +- Remove .klp.arch and add support for jump labels in v5.8+ kernels +- Mark ignored sections earlier to support functions missing ftrace hook +- Minor README.md improvements +- Add ppc64le mcount support to patched functions +- Show additional stalled process information in kpatch script +- Increased shellcheck coverage and fixes +- ppc64le plugin fixes for gcc v10 +- Ignore __UNIQUE_ID_ symbol from tristate config objects +- Don't clear dmesg during integration tests +- Detect and report MODVERSIONS symbol version CRC changes + +* Wed Mar 11 2020 Yannick Cote - 0.9.1 +- Handle ppc64le toc with only constants +- Don't strip callback section symbols +- Integration tests update +- Fix -Wconversion warnings +- Process debug sections last + +* Wed Mar 11 2020 Yannick Cote - 0.9.0 +- Many fixes in integration tests and adding rhel-8.0 +- Updates to documentation +- Many updates and additions to the patch author guide +- Fix to relocations used for ZERO_PAGE(0) +- Simplify static local variables correlation +- Make symvers reading code more flexible +- Free sections in elf teardown +- Fix kpatch-test module unloading +- Disable the build of kpatch.ko module by default +- Simplify mangled function correlation +- Use whole word filename matching in find_parent_obj() +- Simplify relocation processing + +* Wed Aug 21 2019 Artem Savkov - 0.8.0 +- kpatch.ko atomic replace fixes +- Fixes for potential problems found by covscan +- Remove manual signaling logic from kpatch utility +- Don't strip callback symbols +- Allow dynamic debug static keys + +* Wed Jul 24 2019 Josh Poimboeuf - 0.7.1 +- Fix several powerpc-specific bugs, including two which can result in kernel + panics +- Use rpmbuild --nodeps for installing srpm on Fedora/RHEL +- Fix inconsistent unit test failures for FAIL tests + +* Thu Jul 18 2019 Artem Savkov - 0.7.0 +- Multiple memory leak fixes in kpatch-build +- livepatch-patch-hook compatability fixes for kernels 5.1+ +- Making kpatch-build compatible with custom gcc names +- Added rhel-rebased integration tests +- kpatch.service will no longer unload modules on stop +- kpatch load will no longer fail if a module is already loaded and enabled +- kpatch-build will now check for *_fixup section changes on ppc64le and will + fail on such changes +- Add support for R_X86_64_PLT32 +- don't allow jump labels +- ppc64le-specific kpatch-build fixes + +* Fri Apr 12 2019 Joe Lawrence - 0.6.3 +- Lots of integration test work +- Better support for building out-of-tree modules +- Updated manpage options, drop deprecated distro specific mentions +- README.md updates for shadow variables, out-of-tree modules +- Fix core module compilation with CONFIG_HAVE_ARCH_PREL32_RELOCATIONS +- kpatch-build detects and abort on unsupported options + GCC_PLUGIN_LATENT_ENTROPY, GCC_PLUGIN_RANDSTRUCT +- Fix patch linking with 4.20+ +- Other minor shellcheck and kpatch-build fixups + +* Tue Oct 2 2018 Joe Lawrence - 0.6.2 +- ppc64le: relax .text section addralign value check +- gcc8: unit-tests +- gcc8: support parent/child symbol relations +- gcc8: handle functions changing subsection +- gcc8: consider ".text.hot" sections bundleable +- kpatch-build: bugfix for less aggressive clean build-cache +- ubuntu: remove "-signed" substring from the kernel source package name +- ubuntu: explicitly note elfutils dependency +- upstream 4.18: unit-tests +- upstream 4.18: KCFLAGS -mcount-record support support +- RHEL-8: don't care who provides yumdownloader +- RHEL-8: account for quirky SRPM / release name conventions + * Tue May 29 2018 Joe Lawrence - 0.6.1 - Increase the transition timeout, helpful for large CPU count systems - Miscellaneous unit testing, ppc64, etc. fixes diff --git a/doc/patch-author-guide.md b/doc/patch-author-guide.md index fb0121a..33c1b27 100644 --- a/doc/patch-author-guide.md +++ b/doc/patch-author-guide.md @@ -25,10 +25,11 @@ and how they relate to the live patching environment.** kpatch vs livepatch vs kGraft ----------------------------- -This document assumes that the kpatch core module is being used. Other live -patching systems (e.g., livepatch and kGraft) have different consistency -models. Each comes with its own guarantees, and there are some subtle -differences. The guidance in this document applies **only** to kpatch. +This document assumes that the kpatch-build tool is being used to create +livepatch kernel modules. Other live patching systems may have different +consistency models, their own guarantees, and other subtle differences. +The guidance in this document applies **only** to kpatch-build generated +livepatches. Patch upgrades -------------- @@ -102,16 +103,15 @@ new case before looking in the data structure: ``` Not only is this an easy solution, it's also safer than touching data since -kpatch creates a barrier between the calling of old functions and new -functions. +`svm_exit_handlers[]` may be in use by tasks that haven't been patched +yet. ### Use a kpatch callback macro -Kpatch supports livepatch style callbacks, as described by the kernel's -[Documentation/livepatch/callbacks.txt](https://github.com/torvalds/linux/blob/master/Documentation/livepatch/callbacks.txt). - -`kpatch-macros.h` defines the following macros that can be used to -register such callbacks: +Kpatch supports the kernel's livepatch [(Un)patching +callbacks](https://github.com/torvalds/linux/blob/master/Documentation/livepatch/callbacks.rst). +The kernel API requires callback registration through `struct klp_callbacks`, +but to do so through kpatch-build, `kpatch-macros.h` defines the following: * `KPATCH_PRE_PATCH_CALLBACK` - executed before patching * `KPATCH_POST_PATCH_CALLBACK` - executed after patching @@ -124,9 +124,10 @@ A pre-patch callback routine has the following signature: ``` static int callback(patch_object *obj) { } +KPATCH_PRE_PATCH_CALLBACK(callback); ``` -and any non-zero return status indicates failure to the kpatch core. For more +and any non-zero return status indicates failure to the kernel. For more information on pre-patch callback failure, see the **Pre-patch return status** section below. @@ -135,6 +136,9 @@ following signature: ``` static void callback(patch_object *obj) { } +KPATCH_POST_PATCH_CALLBACK(callback); /* or */ +KPATCH_PRE_UNPATCH_CALLBACK(callback); /* or */ +KPATCH_POST_UNPATCH_CALLBACK(callback); ``` Generally pre-patch callbacks are paired with post-unpatch callbacks, meaning @@ -143,30 +147,28 @@ callback. Likewise for post-patch and pre-unpatch callbacks. #### Pre-patch return status -If kpatch is currently patching already-loaded objects (vmlinux always by +If kpatch is currently patching already loaded objects (vmlinux always by definition as well as any currently loaded kernel modules), a non-zero pre-patch -callback status results in the kpatch core reverting the current -patch-in-progress. The kpatch-module is rejected, completely reverted, and -unloaded. +callback status stops the current patch in progress. The kpatch-module +is rejected, completely reverted, and unloaded. -If kpatch is patching a newly loaded kernel module, then a failing pre-patch -callback will only result in a WARN message. This is non-intuitive and a -deviation from livepatch callback behavior, but the result of a limitation of -kpatch and linux module notifiers. +If an already loaded kpatch is patching an incoming kernel module, then +a failing pre-patch callback will result in the kernel module loader +rejecting the new module. -In both cases, if a pre-patch callback fails, none of its other callbacks will -be executed. +In both cases, if a pre-patch callback fails, none of its other +associated callbacks will be executed. #### Callback context * For patches to vmlinux or already loaded kernel modules, callback functions -will be run by `stop_machine` as part of applying or removing a patch. -(Therefore the callbacks must not block or sleep.) +will be run around the livepatch transitions in the `klp_enable_patch()` +callchain. This is executed automatically on kpatch module init. * For patches to kernel modules which haven't been loaded yet, a -module-notifier will execute callbacks when the associated module is loaded -into the `MODULE_STATE_COMING` state. The pre and post-patch callbacks -are called before any module_init code. +module-notifier will execute callbacks when the module is loaded into +the `MODULE_STATE_COMING` state. The pre and post-patch callbacks are +called before any module_init code. Example: a kpatch fix for CVE-2016-5389 could utilize the `KPATCH_PRE_PATCH_CALLBACK` and `KPATCH_POST_UNPATCH_CALLBACK` macros to modify @@ -193,53 +195,11 @@ static void kpatch_post_unpatch_tcp_send_challenge_ack(patch_object *obj) +KPATCH_POST_UNPATCH_CALLBACK(kpatch_post_unpatch_tcp_send_challenge_ack); ``` -Don't forget to protect access to the data as needed. Please note that -spinlocks and mutexes / sleeping locks can't be used from stop_machine -context. Also note the pre-patch callback return code will be ignored by the -kernel's module notifier, so it does not affect the target module or livepatch -module status. This means: - -* Pre-patch callbacks to loaded objects (vmlinux, loaded kernel modules) are - run from stop_machine(), so they may only inspect lock state (i.e. - spin_is_locked(), mutex_is_locked()) and optionally return -EBUSY to prevent - patching. - -* Post-patch, pre-unpatch, and post-unpatch callbacks to loaded objects are - also run from stop_machine(), so the same locking context applies. No - return status is supported. - -* Deferred pre-patch callbacks to newly loading objects do not run from - stop_machine(), so they may spin or schedule, i.e. spin_lock(), - mutex_lock()). Return status is ignored. - -* Post-patch, pre-unpatch, and post-unpatch callbacks to unloading objects are - also *not* run from stop_machine(), so they may spin or sleep. No return - status is supported. - -Unfortunately there is no simple, all-case-inclusive kpatch callback -implementation that handles data structures and mutual exclusion. - -A few workarounds: +Don't forget to protect access to data as needed. Spinlocks and mutexes / +sleeping locks **may be used** (this is a change of behavior from when kpatch +relied on the kpatch.ko support module and `stop_machine()` context.) -1. If a given lock/mutex is held and released by the same set of functions -(that is, functions that take a lock/mutex always release it before -returning), a trivial change to those functions can re-purpose kpatch's -activeness safety check to avoid patching when the lock/mutex may be held. -This assumes that all lock/mutex holders can be patched. - -2. If it can be assured that all patch targets will be loaded before the -kpatch patch module, pre-patch callbacks may return -EBUSY if the lock/mutex -is held to block the patching. - -3. Finally, if a kpatch is disabled or removed and while all patch targets are -still loaded, then all unpatch callbacks will run from stop_machine() -- the -unpatching cannot be stopped at this point and the callbacks cannot spin or -sleep. - - With that in mind, it is probably easiest to omit unpatching callbacks -at this point. - -Also be careful when upgrading. If patch A has a pre/post-patch callback which +Be careful when upgrading. If patch A has a pre/post-patch callback which writes to X, and then you load patch B which is a superset of A, in some cases you may want to prevent patch B from writing to X, if A is already loaded. @@ -247,72 +207,126 @@ you may want to prevent patch B from writing to X, if A is already loaded. ### Use a shadow variable If you need to add a field to an existing data structure, or even many existing -data structures, you can use the `kpatch_shadow_*()` functions: - -* `kpatch_shadow_alloc` - allocates a new shadow variable associated with a - given object -* `kpatch_shadow_get` - find and return a pointer to a shadow variable -* `kpatch_shadow_free` - find and free a shadow variable +data structures, you can use the kernel's +[Shadow Variable](https://www.kernel.org/doc/html/latest/livepatch/shadow-vars.html) API. -Example: The `shadow-newpid.patch` integration test demonstrates the usage of -these functions. +Example: The `shadow-newpid.patch` integration test employs shadow variables +to add a rolling counter to the new `struct task_struct` instances. A +simplified version is presented here. A shadow PID variable is allocated in `do_fork()`: it is associated with the -current `struct task_struct *p` value, given a string lookup key of "newpid", -sized accordingly, and allocated as per `GFP_KERNEL` flag rules. +current `struct task_struct *p` value, given an ID of `KPATCH_SHADOW_NEWPID`, +sized accordingly, and allocated as per `GFP_KERNEL` flag rules. Note that +the shadow variable association is global -- hence it is best to +provide unique ID enumerations per kpatch as needed. -`kpatch_shadow_alloc` returns a pointer to the shadow variable, so we can +`klp_shadow_alloc()` returns a pointer to the shadow variable, so we can dereference and make assignments as usual. In this patch chunk, the shadow `newpid` is allocated then assigned to a rolling `ctr` counter value: ``` +diff --git a/kernel/fork.c b/kernel/fork.c +index 9bff3b28c357..18374fd35bd9 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -1751,6 +1751,8 @@ struct task_struct *fork_idle(int cpu) + return task; + } + ++#include ++#define KPATCH_SHADOW_NEWPID 0 + /* + * Ok, this is the main fork-routine. + * +@@ -1794,6 +1796,14 @@ long do_fork(unsigned long clone_flags, + if (!IS_ERR(p)) { + struct completion vfork; + struct pid *pid; + int *newpid; + static int ctr = 0; + -+ newpid = kpatch_shadow_alloc(p, "newpid", sizeof(*newpid), -+ GFP_KERNEL); ++ newpid = klp_shadow_get_or_alloc(p, KPATCH_SHADOW_NEWPID, ++ sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); + if (newpid) + *newpid = ctr++; + + trace_sched_process_fork(current, p); ``` -A shadow variable may also be accessed via `kpatch_shadow_get`. Here the -patch modifies `task_context_switch_counts()` to fetch the shadow variable -associated with the current `struct task_struct *p` object and a "newpid" tag. -As in the previous patch chunk, the shadow variable pointer may be accessed -as an ordinary pointer type: +A shadow variable may be accessed via `klp_shadow_get()`. Here the patch +modifies `task_context_switch_counts()` to fetch the shadow variable +associated with the current `struct task_struct *p` object and a +`KPATCH_SHADOW_NEWPID ID`. As in the previous patch chunk, the shadow +variable pointer may be accessed as an ordinary pointer type: ``` -+ int *newpid; -+ - seq_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw); - seq_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw); +diff --git a/fs/proc/array.c b/fs/proc/array.c +index 39684c79e8e2..fe0259d057a3 100644 +--- a/fs/proc/array.c ++++ b/fs/proc/array.c +@@ -394,13 +394,19 @@ static inline void task_seccomp(struct seq_file *m, struct task_struct *p) seq_putc(m, '\n'); -+ -+ newpid = kpatch_shadow_get(p, "newpid"); + } + ++#include ++#define KPATCH_SHADOW_NEWPID 0 + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; + seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); ++ newpid = klp_shadow_get(p, KPATCH_SHADOW_NEWPID); + if (newpid) + seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) ``` -A shadow variable is freed by calling `kpatch_shadow_free` and providing -the object / string key combination. Once freed, the shadow variable is not -safe to access: +A shadow variable is freed by calling `klp_shadow_free()` and providing +the object / enum ID combination. Once freed, the shadow variable is no +longer safe to access: ``` - exit_task_work(tsk); - exit_thread(tsk); +diff --git a/kernel/exit.c b/kernel/exit.c +index 148a7842928d..44b6fe61e912 100644 +--- a/kernel/exit.c ++++ b/kernel/exit.c +@@ -791,6 +791,8 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include ++#define KPATCH_SHADOW_NEWPID 0 + void do_exit(long code) + { + struct task_struct *tsk = current; +@@ -888,6 +890,8 @@ void do_exit(long code) + check_stack_usage(); + exit_thread(); -+ kpatch_shadow_free(tsk, "newpid"); ++ klp_shadow_free(tsk, KPATCH_SHADOW_NEWPID, NULL); + /* * Flush inherited counters to the parent - before the parent * gets woken up by child-exit notifications. ``` Notes: -* `kpatch_shadow_alloc` initializes only shadow variable metadata. It - allocates variable storage via `kmalloc` with the `gfp_t` flags it is - given, but otherwise leaves the area untouched. Initialization of a shadow - variable is the responsibility of the caller. -* As soon as `kpatch_shadow_alloc` creates a shadow variable, its presence - will be reported by `kpatch_shadow_get`. Care should be taken to avoid any - potential race conditions between a kernel thread that allocates a shadow - variable and concurrent threads that may attempt to use it. +* `klp_shadow_alloc()` and `klp_shadow_get_or_alloc()` initialize only shadow + variable metadata. They allocate variable storage via `kmalloc` with the + `gfp_t` flags given, but otherwise leave the area untouched. Initialization + of a shadow variable is the responsibility of the caller. +* As soon as `klp_shadow_alloc()` or `klp_shadow_get_or_alloc()` create a shadow + variable, its presence will be reported by `klp_shadow_get()`. Care should be + taken to avoid any potential race conditions between a kernel thread that + allocates a shadow variable and concurrent threads that may attempt to use + it. +* Patches may need to call `klp_shadow_free_all()` from a post-unpatch handler + to safely cleanup any shadow variables of a particular ID. From post-unpatch + context, unloading kpatch module code (aside from .exit) should be + completely inactive. As long as these shadow variables were only accessed by + the unloaded kpatch, they are be safe to release. Data semantic changes --------------------- @@ -323,47 +337,62 @@ kioctx.reqs_active`. Associating a shadow variable to new instances of this structure can be used by patched code to handle both new (post-patch) and existing (pre-patch) instances. -(This example is trimmed to highlight this use-case. Boilerplate code is also -required to allocate/free a shadow variable called "reqs_active_v2" whenever a -new `struct kioctx` is created/released. No values are ever assigned to the -shadow variable.) +(Note: this example is trimmed to highlight this use-case. Boilerplate code is +also required to allocate/free a shadow variable with enum ID +`KPATCH_SHADOW_REQS_ACTIVE_V2` whenever a new `struct kioctx` is +created/released. No values are ever assigned to the shadow variable.) + +``` +diff --git a/fs/aio.c b/fs/aio.c +index ebd06fd0de89..6a33b73c9107 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -280,6 +280,8 @@ static void free_ioctx_rcu(struct rcu_head *head) + * and ctx->users has dropped to 0, so we know no more kiocbs can be submitted - + * now it's safe to cancel any that need to be. + */ ++#include ++#define KPATCH_SHADOW_REQS_ACTIVE_V2 1 + static void free_ioctx(struct kioctx *ctx) + { + struct aio_ring *ring; +``` -Shadow variable existence can be verified before applying the new data +Shadow variable existence can be verified before applying the *new* data semantic of the associated object: ``` -@@ -678,6 +688,9 @@ void aio_complete(struct kiocb *iocb, lo +@@ -678,6 +681,8 @@ void aio_complete(struct kiocb *iocb, long res, long res2) put_rq: - /* everything turned out well, dispose of the aiocb. */ - aio_put_req(iocb); -+ reqs_active_v2 = kpatch_shadow_get(ctx, "reqs_active_v2"); -+ if (reqs_active_v2) -+ atomic_dec(&ctx->reqs_active); + /* everything turned out well, dispose of the aiocb. */ + aio_put_req(iocb); ++ if (klp_shadow_get(ctx, KPATCH_SHADOW_REQS_ACTIVE_V2)) ++ atomic_dec(&ctx->reqs_active); - /* - * We have to order our ring_info tail store above and test + /* + * We have to order our ring_info tail store above and test ``` Likewise, shadow variable non-existence can be tested to continue applying the -old data semantic: +*old* data semantic: ``` -@@ -705,6 +718,7 @@ static long aio_read_events_ring(struct - unsigned head, pos; - long ret = 0; - int copy_ret; -+ int *reqs_active_v2; - - mutex_lock(&ctx->ring_lock); +@@ -310,7 +312,8 @@ static void free_ioctx(struct kioctx *ctx) -@@ -756,7 +770,9 @@ static long aio_read_events_ring(struct + avail = (head <= ctx->tail ? ctx->tail : ctx->nr_events) - head; - pr_debug("%li h%u t%u\n", ret, head, ctx->tail); +- atomic_sub(avail, &ctx->reqs_active); ++ if (!klp_shadow_get(ctx, KPATCH_SHADOW_REQS_ACTIVE_V2)) ++ atomic_sub(avail, &ctx->reqs_active); + head += avail; + head %= ctx->nr_events; + } +@@ -757,6 +762,8 @@ static long aio_read_events_ring(struct kioctx *ctx, + pr_debug("%li h%u t%u\n", ret, head, ctx->tail); -- atomic_sub(ret, &ctx->reqs_active); -+ reqs_active_v2 = kpatch_shadow_get(ctx, "reqs_active_v2"); -+ if (!reqs_active_v2) -+ atomic_sub(ret, &ctx->reqs_active); + atomic_sub(ret, &ctx->reqs_active); ++ if (!klp_shadow_get(ctx, KPATCH_SHADOW_REQS_ACTIVE_V2)) ++ atomic_sub(ret, &ctx->reqs_active); out: - mutex_unlock(&ctx->ring_lock); + mutex_unlock(&ctx->ring_lock); ``` The previous example can be extended to use shadow variable storage to handle @@ -371,22 +400,37 @@ locking semantic changes. Consider the [upstream fix](https://git.kernel.org/pu for CVE-2014-2706, which added a `ps_lock` to `struct sta_info` to protect critical sections throughout `net/mac80211/sta_info.c`. -When allocating a new `struct sta_info`, allocate a corresponding "ps_lock" -shadow variable large enough to hold a `spinlock_t` instance, then initialize -the spinlock: +When allocating a new `struct sta_info`, allocate a corresponding shadow +variable large enough to hold a `spinlock_t` instance, then initialize the +spinlock: ``` -@@ -333,12 +336,16 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, - struct sta_info *sta; +diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c +index decd30c1e290..758533dda4d8 100644 +--- a/net/mac80211/sta_info.c ++++ b/net/mac80211/sta_info.c +@@ -287,6 +287,8 @@ static int sta_prepare_rate_control(struct ieee80211_local *local, + return 0; + } + ++#include ++#define KPATCH_SHADOW_PS_LOCK 2 + struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, + const u8 *addr, gfp_t gfp) + { +@@ -295,6 +297,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, struct timespec uptime; + struct ieee80211_tx_latency_bin_ranges *tx_latency; int i; + spinlock_t *ps_lock; sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp); if (!sta) - return NULL; +@@ -330,6 +333,10 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, + rcu_read_unlock(); spin_lock_init(&sta->lock); -+ ps_lock = kpatch_shadow_alloc(sta, "ps_lock", sizeof(*ps_lock), gfp); ++ ps_lock = klp_shadow_alloc(sta, KPATCH_SHADOW_PS_LOCK, ++ sizeof(*ps_lock), gfp, NULL, NULL); + if (ps_lock) + spin_lock_init(ps_lock); INIT_WORK(&sta->drv_unblock_wk, sta_unblock); @@ -394,17 +438,37 @@ the spinlock: mutex_init(&sta->ampdu_mlme.mtx); ``` -Patched code can reference the "ps_lock" shadow variable associated with a -given `struct sta_info` to determine and apply the correct locking semantic -for that instance: +Patched code can reference the shadow variable associated with a given `struct +sta_info` to determine and apply the correct locking semantic for that +instance: ``` -@@ -471,6 +475,23 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) +diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c +index 97a02d3f7d87..0edb0ed8dc60 100644 +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -459,12 +459,15 @@ static int ieee80211_use_mfp(__le16 fc, struct sta_info *sta, + return 1; + } + ++#include ++#define KPATCH_SHADOW_PS_LOCK 2 + static ieee80211_tx_result + ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) + { + struct sta_info *sta = tx->sta; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); + struct ieee80211_local *local = tx->local; ++ spinlock_t *ps_lock; + + if (unlikely(!sta)) + return TX_CONTINUE; +@@ -478,6 +481,23 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx) sta->sta.addr, sta->sta.aid, ac); if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) purge_old_ps_buffers(tx->local); + + /* sync with ieee80211_sta_ps_deliver_wakeup */ -+ ps_lock = kpatch_shadow_get(sta, "ps_lock"); ++ ps_lock = klp_shadow_get(sta, KPATCH_SHADOW_PS_LOCK); + if (ps_lock) { + spin_lock(ps_lock); + /* @@ -484,7 +548,7 @@ Some examples: any new functions to the bottom of source files, using newline whitespace to maintain original line counts, etc. A more exact fix can be employed by modifying the source code that invokes `__LINE__` and hard-coding the - original line number in place. + original line number in place. This occurred in issue #1124 for example. Removing references to static local variables --------------------------------------------- @@ -567,8 +631,8 @@ In some patching cases it might be necessary to completely remove the original function to avoid the compiler complaining about a defined, but unused function. This will depend on symbol scope and kernel build options. -Other issues ------------- +"Once" macros +------------- When adding a call to `printk_once()`, `pr_warn_once()`, or any other "once" variation of `printk()`, you'll get the following eror: @@ -597,3 +661,205 @@ For example, a `pr_warn_once()` can be replaced with: pr_warn("..."); } ``` + +inline implies notrace +---------------------- + +The linux kernel defines its own version of "inline" in +include/linux/compiler_types.h which includes "notrace" as well: + +``` +#if !defined(CONFIG_OPTIMIZE_INLINING) +#define inline inline __attribute__((__always_inline__)) __gnu_inline \ + __inline_maybe_unused notrace +#else +#define inline inline __gnu_inline \ + __inline_maybe_unused notrace +#endif +``` + +With the implicit "notrace", use of "inline" in patch sources may lead +to kpatch-build errors like the following: + +1. `__tcp_mtu_to_mss()` is marked as inline: + +``` +net/ipv4/tcp_output.c: + +/* Calculate MSS not accounting any TCP options. */ +static inline int __tcp_mtu_to_mss(struct sock *sk, int pmtu) +{ +``` + +2. the compiler decides not to inline it and keeps it in its own + function-section. Then kpatch-build notices that it doesn't have an + fentry/mcount call: + +``` +% kpatch-build ... + +tcp_output.o: function __tcp_mtu_to_mss has no fentry/mcount call, unable to patch +``` + +3. a peek at the generated code: + +``` +Disassembly of section .text.__tcp_mtu_to_mss: + +0000000000000000 <__tcp_mtu_to_mss>: + 0: 48 8b 87 60 05 00 00 mov 0x560(%rdi),%rax + 7: 0f b7 50 30 movzwl 0x30(%rax),%edx + b: 0f b7 40 32 movzwl 0x32(%rax),%eax + f: 29 d6 sub %edx,%esi + 11: 83 ee 14 sub $0x14,%esi + ... +``` + +This could be a little confusing since one might have expected to see +changes to all of `__tcp_mtu_to_mss()` callers (ie, it was inlined as +requested). In this case, a simple workaround is to specify +`__tcp_mtu_to_mss()` as `__always_inline` to force the compiler to do so. + +Jump labels +----------- + +When modifying a function that contains a jump label, kpatch-build may +return an error like: `ERROR: oom_kill.o: kpatch_regenerate_special_section: 2109: Found a jump label at out_of_memory()+0x10a, using key cpusets_enabled_key. Jump labels aren't currently supported. Use static_key_enabled() instead.` + +This is due to a limitation in the kernel to process static key +livepatch relocations (resolved by late-module patching). Older +versions of kpatch-build may have reported successfully building +kpatch module, but issue +[#931](https://github.com/dynup/kpatch/issues/931) revealed potentially +dangerous behavior if the static key value had been modified from its +compiled default. + +The current workaround is to remove the jump label by explictly checking +the static key: + +``` +DEFINE_STATIC_KEY_TRUE(true_key); +DEFINE_STATIC_KEY_FALSE(false_key); + +/* unsupported */ +if (static_key_true(&true_key)) +if (static_key_false(&false_key)) +if (static_branch_likely(&key)) + +/* supported */ +if (static_key_enabled(&true_key)) +if (static_key_enabled(&false_key)) +if (likely(static_key_enabled(&key))) +``` + +Sibling calls +------------- + +GCC may generate sibling calls that are incompatible with kpatch, resulting in +an error like: `ERROR("Found an unsupported sibling call at foo()+0x123. Add __attribute__((optimize("-fno-optimize-sibling-calls"))) to foo() definition."` + +For example, if function A() calls function B() at the end of A() and both +return similar data-types, GCC may deem them "sibling calls" and apply a tail +call optimization in which A() restores the stack to is callee state before +setting up B()'s arguments and jumping to B(). + +This may be an issue for kpatches on PowerPC which modify only A() or B() and +the function call crosses a kernel module boundary: the sibling call +optimization has changed expected calling conventions and (un)patched code may +not be similarly modified. + +Commit [8b952bd77130](https://github.com/dynup/kpatch/commit/8b952bd77130) +("create-diff-object/ppc64le: Don't allow sibling calls") contains an +excellent example and description of this problem with annotated disassembly. + +Adding `__attribute__((optimize("-fno-optimize-sibling-calls")))` instructs +GCC to turn off the optimization for the given function. + +Exported symbol versioning +-------------------------- + +### Background + +`CONFIG_MODVERSIONS` enables an ABI check between exported kernel symbols and +modules referencing those symbols, enforced on module load. When building the +kernel, preprocessor output from `gcc -E` for each source file is passed to +scripts/genksyms. The genksyms script recursively expands each exported symbol +to its basic types. A hash is generated for each symbol as it traverses back up +the symbol tree. The end result is a CRC for each exported function in +the Module.symvers file and embedded in the vmlinux kernel object itself. + +A similar checksumming is performed when building modules: referenced exported +symbol CRCs are stored in the module’s `__versions` section (you can also find +these in plain-text intermediate \*.mod.c files.) + +When the kernel loads a module, the symbol CRCs found in its `__versions` are +compared to those of the kernel, if the two do not match, the kernel will refuse +to load it: +``` +: disagrees about version of symbol +: Unknown symbol (err -22) +``` + +### Kpatch detection + +After building the original and patched sources, kpatch-build compares the +newly calculated Module.symvers against the original. Discrepancies are +reported: + +``` +ERROR: Version disagreement for symbol +``` + +These reports should be addressed to ensure that the resulting kpatch module +can be loaded. + +#### False positives + +It is rare, but possible for a kpatch to introduce inadvertent symbol CRC +changes that are not true ABI changes. The following conditions must occur: + +1. The kpatch must modify the definition of an exported symbol. For example, + introducing a new header file may further define an opaque data type: + Before the kpatch, compilation unit U from the original kernel build only + knew about a `struct S` declaration (not its complete type). At the same + time, U contains function F, which has an interface that references S. If + the kpatch adds a header file to U that now fully defines `struct S { int + a, b, c; }`, its symbol type graph changes, CRCs generated for F are updated, + but its ABI remains consistent. + +2. The kpatch must introduce either a change or reference to F such that it is + included in the resulting kpatch module. This will force a `__version` + entry based on the new CRC. + + Note: if a kpatch doesn't change or reference F such that it is **not** + included in the resulting kpatch module, the new CRC value won't be added + to the module's `__version` table. However, if a future accumulative patch + does add a new change or reference to F, the new CRC will become a problem. + +#### Avoidance + +Kpatches should introduce new `#include` directives sparingly. Whenever +possible, extract the required definitions from header filers into kpatched +compilation units directly. + +If additional header files or symbol definitions cannot be avoided, consider +surrounding the offending include/definitions in an `#ifndef __GENKSYMS__` +macro. The genksyms script will skip over those blocks when performing its CRC +calculations. + +### But what about a real ABI change? + +If a kpatch introduces a true ABI change, each of calling functions would +consequently need to be updated in the kpatch module. For unexported functions, +this may be handled safely if the kpatch does indeed update all callers. +However, since motivation behind `CONFIG_MODVERSIONS` is to provide basic ABI +verification between the kernel and modules for **exported** functions, kpatch +cannot safely change this ABI without worrying about breaking other out-of-tree +drivers. Those drivers have been built against the reference kernel's original +set of CRCs and expect the original ABI. + +To track down specifically what caused a symbol CRC change, tools like +[kabi-dw](https://github.com/skozina/kabi-dw) can be employed to produce a +detailed symbol definition report. For a kpatch-build, kabi-dw can be modified +to operate on .o object files (not just .ko and vmlinux files) and the +`$CACHEDIR/tmp/{orig, patched}` directories compared. diff --git a/kmod/core/core.c b/kmod/core/core.c index 4a73a47..cc97ad0 100644 --- a/kmod/core/core.c +++ b/kmod/core/core.c @@ -83,6 +83,11 @@ struct kpatch_kallsyms_args { unsigned long pos; }; +struct kpatch_apply_patch_args { + struct kpatch_module *kpmod; + bool replace; +}; + /* this is a double loop, use goto instead of break */ #define do_for_each_linked_func(kpmod, func) { \ struct kpatch_object *_object; \ @@ -207,7 +212,8 @@ static inline int kpatch_compare_addresses(unsigned long stack_addr, } static int kpatch_backtrace_address_verify(struct kpatch_module *kpmod, - unsigned long address) + unsigned long address, + bool replace) { struct kpatch_func *func; int i; @@ -242,8 +248,11 @@ static int kpatch_backtrace_address_verify(struct kpatch_module *kpmod, } while_for_each_linked_func(); /* in the replace case, need to check the func hash as well */ - hash_for_each_rcu(kpatch_func_hash, i, func, node) { - if (func->op == KPATCH_OP_UNPATCH && !func->force) { + if (replace) { + hash_for_each_rcu(kpatch_func_hash, i, func, node) { + if (func->op != KPATCH_OP_UNPATCH || func->force) + continue; + ret = kpatch_compare_addresses(address, func->new_addr, func->new_size, @@ -262,7 +271,8 @@ static int kpatch_backtrace_address_verify(struct kpatch_module *kpmod, * * This function is called from stop_machine() context. */ -static int kpatch_verify_activeness_safety(struct kpatch_module *kpmod) +static int kpatch_verify_activeness_safety(struct kpatch_module *kpmod, + bool replace) { struct task_struct *g, *t; int i; @@ -284,7 +294,8 @@ static int kpatch_verify_activeness_safety(struct kpatch_module *kpmod) if (trace.entries[i] == ULONG_MAX) break; ret = kpatch_backtrace_address_verify(kpmod, - trace.entries[i]); + trace.entries[i], + replace); if (ret) goto out; } @@ -350,27 +361,22 @@ static inline void post_unpatch_callback(struct kpatch_object *object) /* Called from stop_machine */ static int kpatch_apply_patch(void *data) { - struct kpatch_module *kpmod = data; + struct kpatch_apply_patch_args *args = data; + struct kpatch_module *kpmod; struct kpatch_func *func; + struct hlist_node *tmp; struct kpatch_object *object; int ret; + int i; - ret = kpatch_verify_activeness_safety(kpmod); + kpmod = args->kpmod; + + ret = kpatch_verify_activeness_safety(kpmod, args->replace); if (ret) { kpatch_state_finish(KPATCH_STATE_FAILURE); return ret; } - /* run any user-defined pre-patch callbacks */ - list_for_each_entry(object, &kpmod->objects, list) { - ret = pre_patch_callback(object); - if (ret) { - pr_err("pre-patch callback failed!\n"); - kpatch_state_finish(KPATCH_STATE_FAILURE); - goto err; - } - } - /* tentatively add the new funcs to the global func hash */ do_for_each_linked_func(kpmod, func) { hash_add_rcu(kpatch_func_hash, &func->node, func->old_addr); @@ -392,8 +398,20 @@ static int kpatch_apply_patch(void *data) hash_del_rcu(&func->node); } while_for_each_linked_func(); - ret = -EBUSY; - goto err; + return -EBUSY; + } + + /* + * The new patch has been applied successfully. Remove the functions + * provided by the replaced patches (if any) from hash, to make sure + * they will not be executed anymore. + */ + if (args->replace) { + hash_for_each_safe(kpatch_func_hash, i, tmp, func, node) { + if (func->op != KPATCH_OP_UNPATCH) + continue; + hash_del_rcu(&func->node); + } } /* run any user-defined post-patch callbacks */ @@ -401,12 +419,6 @@ static int kpatch_apply_patch(void *data) post_patch_callback(object); return 0; -err: - /* undo pre-patch callbacks by calling post-unpatch counterparts */ - list_for_each_entry(object, &kpmod->objects, list) - post_unpatch_callback(object); - - return ret; } /* Called from stop_machine */ @@ -417,7 +429,7 @@ static int kpatch_remove_patch(void *data) struct kpatch_object *object; int ret; - ret = kpatch_verify_activeness_safety(kpmod); + ret = kpatch_verify_activeness_safety(kpmod, false); if (ret) { kpatch_state_finish(KPATCH_STATE_FAILURE); return ret; @@ -439,10 +451,6 @@ static int kpatch_remove_patch(void *data) hash_del_rcu(&func->node); } while_for_each_linked_func(); - /* run any user-defined post-unpatch callbacks */ - list_for_each_entry(object, &kpmod->objects, list) - post_unpatch_callback(object); - return 0; err: @@ -651,7 +659,11 @@ static int kpatch_find_external_symbol(const char *objname, const char *name, sym = find_symbol(name, NULL, NULL, true, true); preempt_enable(); if (sym) { +#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS + *addr = (unsigned long)offset_to_ptr(&sym->value_offset); +#else *addr = sym->value; +#endif return 0; } @@ -696,6 +708,7 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod, case R_X86_64_NONE: continue; case R_X86_64_PC32: + case R_X86_64_PLT32: loc = dynrela->dest; val = (u32)(dynrela->src + dynrela->addend - dynrela->dest); @@ -708,7 +721,7 @@ static int kpatch_write_relocations(struct kpatch_module *kpmod, break; case R_X86_64_64: loc = dynrela->dest; - val = dynrela->src; + val = dynrela->src + dynrela->addend; size = 8; break; default: @@ -970,12 +983,41 @@ out: return 0; } +/* + * Remove the obsolete functions from the ftrace filter. + * Return 1 if one or more of such functions have 'force' flag set, + * 0 otherwise. + */ +static int kpatch_ftrace_remove_unpatched_funcs(void) +{ + struct kpatch_module *kpmod; + struct kpatch_func *func; + int force = 0; + + list_for_each_entry(kpmod, &kpmod_list, list) { + do_for_each_linked_func(kpmod, func) { + if (func->op != KPATCH_OP_UNPATCH) + continue; + if (func->force) + force = 1; + WARN_ON(kpatch_ftrace_remove_func(func->old_addr)); + } while_for_each_linked_func(); + } + + return force; +} + int kpatch_register(struct kpatch_module *kpmod, bool replace) { - int ret, i, force = 0; + int ret, i; struct kpatch_object *object, *object_err = NULL; struct kpatch_func *func; + struct kpatch_apply_patch_args args = { + .kpmod = kpmod, + .replace = replace, + }; + if (!kpmod->mod || list_empty(&kpmod->objects)) return -EINVAL; @@ -1023,28 +1065,41 @@ int kpatch_register(struct kpatch_module *kpmod, bool replace) kpatch_state_updating(); - /* - * Idle the CPUs, verify activeness safety, and atomically make the new - * functions visible to the ftrace handler. - */ - ret = stop_machine(kpatch_apply_patch, kpmod, NULL); + /* run any user-defined pre-patch callbacks */ + list_for_each_entry(object, &kpmod->objects, list) { + ret = pre_patch_callback(object); + if(ret){ + pr_err("pre-patch callback failed!\n"); + kpatch_state_finish(KPATCH_STATE_FAILURE); + break; + } + } + + /* if pre_patch_callback succeed. */ + if (!ret) { + /* + * Idle the CPUs, verify activeness safety, and atomically make the new + * functions visible to the ftrace handler. + */ + ret = stop_machine(kpatch_apply_patch, &args, NULL); + } + + /* if pre_patch_callback or stop_machine failed */ + if (ret) { + list_for_each_entry(object, &kpmod->objects, list) + post_unpatch_callback(object); + } /* - * For the replace case, remove any obsolete funcs from the hash and - * the ftrace filter, and disable the owning patch module so that it - * can be removed. + * For the replace case, remove any obsolete funcs from the ftrace + * filter, and disable the owning patch module so that it can be + * removed. */ if (!ret && replace) { struct kpatch_module *kpmod2, *safe; + int force; - hash_for_each_rcu(kpatch_func_hash, i, func, node) { - if (func->op != KPATCH_OP_UNPATCH) - continue; - if (func->force) - force = 1; - hash_del_rcu(&func->node); - WARN_ON(kpatch_ftrace_remove_func(func->old_addr)); - } + force = kpatch_ftrace_remove_unpatched_funcs(); list_for_each_entry_safe(kpmod2, safe, &kpmod_list, list) { if (kpmod == kpmod2) @@ -1167,6 +1222,11 @@ int kpatch_unregister(struct kpatch_module *kpmod) ret = stop_machine(kpatch_remove_patch, kpmod, NULL); + if (!ret) { + /* run any user-defined post-unpatch callbacks */ + list_for_each_entry(object, &kpmod->objects, list) + post_unpatch_callback(object); + } /* NMI handlers can return to normal now */ kpatch_state_idle(); diff --git a/kmod/patch/Makefile b/kmod/patch/Makefile index 96d1fca..e017b17 100644 --- a/kmod/patch/Makefile +++ b/kmod/patch/Makefile @@ -11,13 +11,15 @@ KBUILD_CFLAGS_MODULE += -mcmodel=large endif obj-m += $(KPATCH_NAME).o +ldflags-y += -T $(src)/kpatch.lds +extra-y := kpatch.lds -$(KPATCH_NAME)-objs += patch-hook.o kpatch.lds output.o +$(KPATCH_NAME)-objs += patch-hook.o output.o all: $(KPATCH_NAME).ko $(KPATCH_NAME).ko: - $(KPATCH_MAKE) $(KPATCH_NAME).ko + $(KPATCH_MAKE) patch-hook.o: patch-hook.c kpatch-patch-hook.c livepatch-patch-hook.c $(KPATCH_MAKE) patch-hook.o diff --git a/kmod/patch/kpatch-patch.h b/kmod/patch/kpatch-patch.h index 917ea32..da4f6a0 100644 --- a/kmod/patch/kpatch-patch.h +++ b/kmod/patch/kpatch-patch.h @@ -40,7 +40,7 @@ struct kpatch_patch_dynrela { char *name; char *objname; int external; - int addend; + long addend; }; struct kpatch_pre_patch_callback { diff --git a/kmod/patch/livepatch-patch-hook.c b/kmod/patch/livepatch-patch-hook.c index 7a587a3..e12fd50 100644 --- a/kmod/patch/livepatch-patch-hook.c +++ b/kmod/patch/livepatch-patch-hook.c @@ -64,6 +64,16 @@ # define HAVE_CALLBACKS #endif +#ifdef RHEL_RELEASE_CODE +# if (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 8) && \ + RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(8, 0)) || \ + RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8, 2) +# define HAVE_SIMPLE_ENABLE +# endif +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) +# define HAVE_SIMPLE_ENABLE +#endif + /* * There are quite a few similar structures at play in this file: * - livepatch.h structs prefixed with klp_* @@ -437,15 +447,19 @@ static int __init patch_init(void) */ patch_free_scaffold(); +#ifndef HAVE_SIMPLE_ENABLE ret = klp_register_patch(lpatch); if (ret) { patch_free_livepatch(lpatch); return ret; } +#endif ret = klp_enable_patch(lpatch); if (ret) { +#ifndef HAVE_SIMPLE_ENABLE WARN_ON(klp_unregister_patch(lpatch)); +#endif patch_free_livepatch(lpatch); return ret; } @@ -459,7 +473,10 @@ out: static void __exit patch_exit(void) { +#ifndef HAVE_SIMPLE_ENABLE WARN_ON(klp_unregister_patch(lpatch)); +#endif + patch_free_livepatch(lpatch); } module_init(patch_init); diff --git a/kpatch-build/Makefile b/kpatch-build/Makefile index 232b336..b5b2ffd 100644 --- a/kpatch-build/Makefile +++ b/kpatch-build/Makefile @@ -1,6 +1,7 @@ include ../Makefile.inc -CFLAGS += -MMD -MP -I../kmod/patch -Iinsn -Wall -Wsign-compare -g -Werror +CFLAGS += -MMD -MP -I../kmod/patch -Iinsn -Wall -Wsign-compare \ + -Wconversion -Wno-sign-conversion -g -Werror LDLIBS = -lelf TARGETS = create-diff-object create-klp-module create-kpatch-module @@ -12,13 +13,17 @@ SOURCES = create-diff-object.c kpatch-elf.c \ ifeq ($(ARCH),x86_64) SOURCES += insn/insn.c insn/inat.c INSN = insn/insn.o insn/inat.o +insn/%.o: CFLAGS := $(filter-out -Wconversion, $(CFLAGS)) else ifeq ($(ARCH),ppc64le) SOURCES += gcc-plugins/ppc64le-plugin.c PLUGIN = gcc-plugins/ppc64le-plugin.so TARGETS += $(PLUGIN) GCC_PLUGINS_DIR := $(shell gcc -print-file-name=plugin) -PLUGIN_CFLAGS = -shared $(CFLAGS) -I$(GCC_PLUGINS_DIR)/include \ +PLUGIN_CFLAGS := $(filter-out -Wconversion, $(CFLAGS)) +PLUGIN_CFLAGS += -shared -I$(GCC_PLUGINS_DIR)/include \ -Igcc-plugins -fPIC -fno-rtti -O2 -Wall +else +$(error Unsupported architecture ${ARCH}, check https://github.com/dynup/kpatch/#supported-architectures) endif diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index d242d01..cee8adf 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -68,8 +68,16 @@ char *childobj; +enum subsection { + SUBSECTION_NORMAL, + SUBSECTION_HOT, + SUBSECTION_UNLIKELY +}; + enum loglevel loglevel = NORMAL; +bool KLP_ARCH; + /******************* * Data structures * ****************/ @@ -96,12 +104,27 @@ static int is_bundleable(struct symbol *sym) !strncmp(sym->sec->name + 15, sym->name, strlen(sym->sec->name) - 15)))) return 1; + if (sym->type == STT_FUNC && + !strncmp(sym->sec->name, ".text.hot.",10) && + !strcmp(sym->sec->name + 10, sym->name)) + return 1; + if (sym->type == STT_OBJECT && !strncmp(sym->sec->name, ".data.",6) && !strcmp(sym->sec->name + 6, sym->name)) return 1; if (sym->type == STT_OBJECT && + !strncmp(sym->sec->name, ".data.rel.", 10) && + !strcmp(sym->sec->name + 10, sym->name)) + return 1; + + if (sym->type == STT_OBJECT && + !strncmp(sym->sec->name, ".data.rel.ro.", 13) && + !strcmp(sym->sec->name + 13, sym->name)) + return 1; + + if (sym->type == STT_OBJECT && !strncmp(sym->sec->name, ".rodata.",8) && !strcmp(sym->sec->name + 8, sym->name)) return 1; @@ -150,14 +173,45 @@ static int is_gcc6_localentry_bundled_sym(struct symbol *sym) } #endif +/* + * On ppc64le, when a function references data, it does so indirectly, via the + * .toc section. So there are *two* levels of relas: + * + * 1) the original function rela, referring to the .toc section; and + * + * 2) the .toc section rela, referring to the data needed by the function. + * + * For example: + * + * Relocation section '.rela.text.netlink_release' at offset 0xcadf0 contains 44 entries: + * ... + * 0000000000000398 0000007300000032 R_PPC64_TOC16_HA 0000000000000000 .toc + 138 + * 00000000000003a0 0000007300000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + 138 + * + * Relocation section '.rela.toc' at offset 0xcc6b0 contains 46 entries: + * ... + * 0000000000000138 0000002a00000026 R_PPC64_ADDR64 0000000000000000 .text.deferred_put_nlk_sk + 8 + * + * The below function takes the "first level" rela as input, and, if it refers + * to .toc, returns the "second level" rela, which is the one that refers to + * the actual data symbol. + * + * In some rare cases, a .toc entry has constant data, and thus has no + * corresponding rela. In that case, NULL is returned. + */ static struct rela *toc_rela(const struct rela *rela) { if (rela->type != R_PPC64_TOC16_HA && rela->type != R_PPC64_TOC16_LO_DS) return (struct rela *)rela; + /* Only constants in toc */ + if (!rela->sym->sec->rela) + return NULL; + /* Will return NULL for .toc constant entries */ - return find_rela_by_offset(rela->sym->sec->rela, rela->addend); + return find_rela_by_offset(rela->sym->sec->rela, + (unsigned int)rela->addend); } /* @@ -183,6 +237,70 @@ static void kpatch_bundle_symbols(struct kpatch_elf *kelf) } } +static struct symbol *kpatch_lookup_parent(struct kpatch_elf *kelf, + const char *symname, + const char *child_suffix) +{ + struct symbol *parent; + char *pname; + + pname = strndup(symname, child_suffix - symname); + if (!pname) + ERROR("strndup"); + + parent = find_symbol_by_name(&kelf->symbols, pname); + free(pname); + + return parent; +} + +/* + * During optimization gcc may move unlikely execution branches into *.cold + * subfunctions. Some functions can also be split into multiple *.part + * functions. + * kpatch_detect_child_functions detects such subfunctions and + * crossreferences them with their parent functions through parent/child + * pointers. + */ +static void kpatch_detect_child_functions(struct kpatch_elf *kelf) +{ + struct symbol *sym; + + list_for_each_entry(sym, &kelf->symbols, list) { + char *childstr; + + if (sym->type != STT_FUNC) + continue; + + childstr = strstr(sym->name, ".cold."); + if (childstr) { + sym->parent = kpatch_lookup_parent(kelf, sym->name, + childstr); + if (!sym->parent) + ERROR("failed to find parent function for %s", + sym->name); + } else { + childstr = strstr(sym->name, ".part."); + if (!childstr) + continue; + sym->parent = kpatch_lookup_parent(kelf, sym->name, + childstr); + } + + if (sym->parent) + list_add_tail(&sym->subfunction_node, &sym->parent->children); + } +} + +static bool is_dynamic_debug_symbol(struct symbol *sym) +{ + if (sym->type == STT_OBJECT && !strcmp(sym->sec->name, "__verbose")) + return true; + if (sym->type == STT_SECTION && !strcmp(sym->name, "__verbose")) + return true; + return false; +} + /* * This function detects whether the given symbol is a "special" static local * variable (for lack of a better term). @@ -195,8 +313,8 @@ static int is_special_static(struct symbol *sym) static char *prefixes[] = { "__key.", "__warned.", - "descriptor.", "__func__.", + "__FUNCTION__.", "_rs.", "CSWTCH.", NULL, @@ -206,12 +324,12 @@ static int is_special_static(struct symbol *sym) if (!sym) return 0; - if (sym->type == STT_SECTION) { - /* __verbose section contains the descriptor variables */ - if (!strcmp(sym->name, "__verbose")) - return 1; + /* pr_debug() uses static local variables in the __verbose section */ + if (is_dynamic_debug_symbol(sym)) + return 1; - /* otherwise make sure section is bundled */ + if (sym->type == STT_SECTION) { + /* make sure section is bundled */ if (!sym->sec->sym) return 0; @@ -235,6 +353,13 @@ static int is_special_static(struct symbol *sym) */ static int kpatch_mangled_strcmp(char *s1, char *s2) { + /* + * ELF string sections aren't mangled, though they look that way. Just + * compare them normally. + */ + if (strstr(s1, ".str1.")) + return strcmp(s1, s2); + while (*s1 == *s2) { if (!*s1) return 0; @@ -253,14 +378,27 @@ static int kpatch_mangled_strcmp(char *s1, char *s2) return 1; } -static int rela_equal(struct rela *rela1, struct rela *rela2) +static bool rela_equal(struct rela *rela1, struct rela *rela2) { struct rela *rela_toc1, *rela_toc2; unsigned long toc_data1 = 0, toc_data2 = 0; /* = 0 to prevent gcc warning */ if (rela1->type != rela2->type || rela1->offset != rela2->offset) - return 0; + return false; + + /* + * On x86, .altinstr_aux is used to store temporary code which allows + * static_cpu_has() to work before apply_alternatives() has run. This + * code is completely inert for modules, because apply_alternatives() + * runs during module init, before the module is fully formed. Any + * changed references to it (i.e. changed addend) can be ignored. As + * long as they're both references to .altinstr_aux, they can be + * considered equal, even if the addends differ. + */ + if (!strcmp(rela1->sym->name, ".altinstr_aux") && + !strcmp(rela2->sym->name, ".altinstr_aux")) + return true; /* * With -mcmodel=large on ppc64le, GCC might generate entries in the .toc @@ -305,30 +443,29 @@ static int rela_equal(struct rela *rela1, struct rela *rela2) */ memcpy(&toc_data1, rela1->sym->sec->data->d_buf + rela1->addend, sizeof(toc_data1)); if (!toc_data1) - ERROR(".toc entry not found %s + %x", rela1->sym->name, rela1->addend); + ERROR(".toc entry not found %s + %lx", rela1->sym->name, rela1->addend); } rela_toc2 = toc_rela(rela2); if (!rela_toc2) { memcpy(&toc_data2, rela2->sym->sec->data->d_buf + rela2->addend, sizeof(toc_data2)); if (!toc_data2) - ERROR(".toc entry not found %s + %x", rela2->sym->name, rela2->addend); + ERROR(".toc entry not found %s + %lx", rela2->sym->name, rela2->addend); } if (!rela_toc1 && !rela_toc2) return toc_data1 == toc_data2; + if (!rela_toc1 || !rela_toc2) + return false; + if (rela_toc1->string) return rela_toc2->string && !strcmp(rela_toc1->string, rela_toc2->string); if (rela_toc1->addend != rela_toc2->addend) - return 0; + return false; - if (is_special_static(rela_toc1->sym)) - return !kpatch_mangled_strcmp(rela_toc1->sym->name, - rela_toc2->sym->name); - - return !strcmp(rela_toc1->sym->name, rela_toc2->sym->name); + return !kpatch_mangled_strcmp(rela_toc1->sym->name, rela_toc2->sym->name); } static void kpatch_compare_correlated_rela_section(struct section *sec) @@ -375,9 +512,10 @@ static void kpatch_compare_correlated_section(struct section *sec) /* Compare section headers (must match or fatal) */ if (sec1->sh.sh_type != sec2->sh.sh_type || sec1->sh.sh_flags != sec2->sh.sh_flags || - sec1->sh.sh_addralign != sec2->sh.sh_addralign || - sec1->sh.sh_entsize != sec2->sh.sh_entsize) - DIFF_FATAL("%s section header details differ", sec1->name); + sec1->sh.sh_entsize != sec2->sh.sh_entsize || + (sec1->sh.sh_addralign != sec2->sh.sh_addralign && + !is_text_section(sec1))) + DIFF_FATAL("%s section header details differ from %s", sec1->name, sec2->name); /* Short circuit for mcount sections, we rebuild regardless */ if (!strcmp(sec->name, ".rela__mcount_loc") || @@ -585,6 +723,26 @@ static int kpatch_line_macro_change_only(struct section *sec) } #endif +/* + * Child functions with "*.cold" names don't have _fentry_ calls, but "*.part", + * often do. In the later case, it is not necessary to include the parent + * in the output object when the child function has changed. + */ +static bool kpatch_changed_child_needs_parent_profiling(struct symbol *sym) +{ + struct symbol *child; + + list_for_each_entry(child, &sym->children, subfunction_node) { + if (child->has_func_profiling) + continue; + if (child->sec->status == CHANGED || + kpatch_changed_child_needs_parent_profiling(child)) + return true; + } + + return false; +} + static void kpatch_compare_sections(struct list_head *seclist) { struct section *sec; @@ -612,12 +770,41 @@ static void kpatch_compare_sections(struct list_head *seclist) if (sec->base->sym && sec->base->sym->status != CHANGED) sec->base->sym->status = sec->status; } else { - if (sec->sym && sec->sym->status != CHANGED) - sec->sym->status = sec->status; + struct symbol *sym = sec->sym; + + if (sym && sym->status != CHANGED) + sym->status = sec->status; + + if (sym && sym->status == SAME && + kpatch_changed_child_needs_parent_profiling(sym)) + sym->status = CHANGED; } } } +static enum subsection kpatch_subsection_type(struct section *sec) +{ + if (!strncmp(sec->name, ".text.unlikely.", 15)) + return SUBSECTION_UNLIKELY; + + if (!strncmp(sec->name, ".text.hot.", 10)) + return SUBSECTION_HOT; + + return SUBSECTION_NORMAL; +} + +static int kpatch_subsection_changed(struct section *sec1, struct section *sec2) +{ + return kpatch_subsection_type(sec1) != kpatch_subsection_type(sec2); +} + +static struct symbol *kpatch_get_correlated_parent(struct symbol *sym) +{ + while (sym->parent && !sym->parent->twin) + sym = sym->parent; + return sym->parent; +} + static void kpatch_compare_correlated_symbol(struct symbol *sym) { struct symbol *sym1 = sym, *sym2 = sym->twin; @@ -630,10 +817,12 @@ static void kpatch_compare_correlated_symbol(struct symbol *sym) /* * If two symbols are correlated but their sections are not, then the * symbol has changed sections. This is only allowed if the symbol is - * moving out of an ignored section. + * moving out of an ignored section, or moving between normal/hot/unlikely + * subsections. */ if (sym1->sec && sym2->sec && sym1->sec->twin != sym2->sec) { - if (sym2->sec->twin && sym2->sec->twin->ignore) + if ((sym2->sec->twin && sym2->sec->twin->ignore) || + kpatch_subsection_changed(sym1->sec, sym2->sec)) sym->status = CHANGED; else DIFF_FATAL("symbol changed sections: %s", sym1->name); @@ -667,13 +856,72 @@ static void kpatch_compare_symbols(struct list_head *symlist) } } +#define CORRELATE_ELEMENT(_e1_, _e2_, kindstr) \ +do { \ + typeof(_e1_) e1 = (_e1_); \ + typeof(_e2_) e2 = (_e2_); \ + e1->twin = e2; \ + e2->twin = e1; \ + /* set initial status, might change */ \ + e1->status = e2->status = SAME; \ + if (strcmp(e1->name, e2->name)) { \ + /* Rename mangled element */ \ + log_debug("renaming %s %s to %s\n", \ + kindstr, e2->name, e1->name); \ + e2->name = strdup(e1->name); \ + } \ +} while (0) + +#define UNCORRELATE_ELEMENT(_elem_) \ +do { \ + typeof(_elem_) elem = (_elem_); \ + elem->twin->twin = NULL; \ + elem->twin = NULL; \ +} while (0) + +static void __kpatch_correlate_section(struct section *sec1, struct section *sec2) +{ + CORRELATE_ELEMENT(sec1, sec2, "section"); +} + +static void kpatch_correlate_symbol(struct symbol *sym1, struct symbol *sym2) +{ + CORRELATE_ELEMENT(sym1, sym2, "symbol"); +} + +static void kpatch_correlate_static_local(struct symbol *sym1, struct symbol *sym2) +{ + CORRELATE_ELEMENT(sym1, sym2, "static local"); +} + +static void kpatch_correlate_section(struct section *sec1, struct section *sec2) +{ + __kpatch_correlate_section(sec1, sec2); + + if (is_rela_section(sec1)) { + __kpatch_correlate_section(sec1->base, sec2->base); + sec1 = sec1->base; + sec2 = sec2->base; + } else if (sec1->rela) { + __kpatch_correlate_section(sec1->rela, sec2->rela); + } + + if (sec1->secsym) + kpatch_correlate_symbol(sec1->secsym, sec2->secsym); + if (sec1->sym) + kpatch_correlate_symbol(sec1->sym, sec2->sym); +} + static void kpatch_correlate_sections(struct list_head *seclist1, struct list_head *seclist2) { struct section *sec1, *sec2; list_for_each_entry(sec1, seclist1, list) { + if (sec1->twin) + continue; list_for_each_entry(sec2, seclist2, list) { - if (strcmp(sec1->name, sec2->name)) + if (kpatch_mangled_strcmp(sec1->name, sec2->name) || + sec2->twin) continue; if (is_special_static(is_rela_section(sec1) ? @@ -692,10 +940,8 @@ static void kpatch_correlate_sections(struct list_head *seclist1, struct list_he sec1->data->d_size)) continue; } - sec1->twin = sec2; - sec2->twin = sec1; - /* set initial status, might change */ - sec1->status = sec2->status = SAME; + + kpatch_correlate_section(sec1, sec2); break; } } @@ -706,9 +952,11 @@ static void kpatch_correlate_symbols(struct list_head *symlist1, struct list_hea struct symbol *sym1, *sym2; list_for_each_entry(sym1, symlist1, list) { + if (sym1->twin) + continue; list_for_each_entry(sym2, symlist2, list) { - if (strcmp(sym1->name, sym2->name) || - sym1->type != sym2->type) + if (kpatch_mangled_strcmp(sym1->name, sym2->name) || + sym1->type != sym2->type || sym2->twin) continue; if (is_special_static(sym1)) @@ -738,10 +986,7 @@ static void kpatch_correlate_symbols(struct list_head *symlist1, struct list_hea sym1->sec->twin != sym2->sec) continue; - sym1->twin = sym2; - sym2->twin = sym1; - /* set initial status, might change */ - sym1->status = sym2->status = SAME; + kpatch_correlate_symbol(sym1, sym2); break; } } @@ -804,82 +1049,66 @@ static void kpatch_mark_grouped_sections(struct kpatch_elf *kelf) } } -/* - * When gcc makes compiler optimizations which affect a function's calling - * interface, it mangles the function's name. For example, sysctl_print_dir is - * renamed to sysctl_print_dir.isra.2. The problem is that the trailing number - * is chosen arbitrarily, and the patched version of the function may end up - * with a different trailing number. Rename any mangled patched functions to - * match their base counterparts. - */ -static void kpatch_rename_mangled_functions(struct kpatch_elf *base, - struct kpatch_elf *patched) +static char *kpatch_section_function_name(struct section *sec) { - struct symbol *sym, *basesym; - char name[256], *origname; - struct section *sec, *basesec; - int found; + if (is_rela_section(sec)) + sec = sec->base; + return sec->sym ? sec->sym->name : sec->name; +} - list_for_each_entry(sym, &patched->symbols, list) { - if (sym->type != STT_FUNC) - continue; +static struct symbol *kpatch_find_uncorrelated_rela(struct section *rela_sec, + struct symbol *sym) +{ + struct rela *rela, *rela_toc; - if (!strstr(sym->name, ".isra.") && - !strstr(sym->name, ".constprop.") && - !strstr(sym->name, ".cold.") && - !strstr(sym->name, ".part.")) - continue; + /* find the patched object's corresponding variable */ + list_for_each_entry(rela, &rela_sec->relas, list) { + struct symbol *patched_sym; - found = 0; - list_for_each_entry(basesym, &base->symbols, list) { - if (!kpatch_mangled_strcmp(basesym->name, sym->name)) { - found = 1; - break; - } - } + rela_toc = toc_rela(rela); + if (!rela_toc) + continue; /* skip toc constants */ - if (!found) - continue; + patched_sym = rela_toc->sym; - if (!strcmp(sym->name, basesym->name)) + if (patched_sym->twin) continue; - log_debug("renaming %s to %s\n", sym->name, basesym->name); - origname = sym->name; - sym->name = strdup(basesym->name); - - if (sym != sym->sec->sym) + if (sym->type != patched_sym->type || + (sym->type == STT_OBJECT && + sym->sym.st_size != patched_sym->sym.st_size)) continue; - sym->sec->name = strdup(basesym->sec->name); - if (sym->sec->rela) - sym->sec->rela->name = strdup(basesym->sec->rela->name); - - /* - * When function foo.isra.1 has a switch statement, it might - * have a corresponding bundled .rodata.foo.isra.1 section (in - * addition to .text.foo.isra.1 which we renamed above). - */ - sprintf(name, ".rodata.%s", origname); - sec = find_section_by_name(&patched->sections, name); - if (!sec) + if (kpatch_mangled_strcmp(patched_sym->name, sym->name)) continue; - sprintf(name, ".rodata.%s", basesym->name); - basesec = find_section_by_name(&base->sections, name); - if (!basesec) - continue; - sec->name = strdup(basesec->name); - sec->secsym->name = sec->name; - if (sec->rela) - sec->rela->name = strdup(basesec->rela->name); + + return patched_sym; } + + return NULL; } -static char *kpatch_section_function_name(struct section *sec) +static struct symbol *kpatch_find_static_twin_in_children(struct symbol *parent, + struct symbol *sym) { - if (is_rela_section(sec)) - sec = sec->base; - return sec->sym ? sec->sym->name : sec->name; + struct symbol *child; + + list_for_each_entry(child, &parent->children, subfunction_node) { + struct symbol *res; + + /* Only look in children whose rela section differ from the parent's */ + if (child->sec->rela == parent->sec->rela || !child->sec->rela) + continue; + + res = kpatch_find_uncorrelated_rela(child->sec->rela, sym); + /* Need to go deeper */ + if (!res) + res = kpatch_find_static_twin_in_children(child, sym); + if (res != NULL) + return res; + } + + return NULL; } /* @@ -890,26 +1119,32 @@ static char *kpatch_section_function_name(struct section *sec) static struct symbol *kpatch_find_static_twin(struct section *sec, struct symbol *sym) { - struct rela *rela, *rela_toc; + struct symbol *res; - if (!sec->twin) - return NULL; + if (!sec->twin && sec->base->sym) { + struct symbol *parent = NULL; - /* find the patched object's corresponding variable */ - list_for_each_entry(rela, &sec->twin->relas, list) { + /* + * The static twin might have been in a .part. symbol in the + * original object that got removed in the patched object. + */ + parent = kpatch_get_correlated_parent(sec->base->sym); + if (parent) + sec = parent->sec->rela; - rela_toc = toc_rela(rela); - if (!rela_toc) - continue; /* skip toc constants */ + } - if (rela_toc->sym->twin) - continue; + if (!sec->twin) + return NULL; - if (kpatch_mangled_strcmp(rela_toc->sym->name, sym->name)) - continue; + res = kpatch_find_uncorrelated_rela(sec->twin, sym); + if (res != NULL) + return res; - return rela_toc->sym; - } + /* Look if reference might have moved to child functions' sections */ + if (sec->twin->base->sym) + return kpatch_find_static_twin_in_children(sec->twin->base->sym, + sym); return NULL; } @@ -928,6 +1163,35 @@ static int kpatch_is_normal_static_local(struct symbol *sym) return 1; } +static struct rela *kpatch_find_static_twin_ref(struct section *rela_sec, struct symbol *sym) +{ + struct rela *rela; + + list_for_each_entry(rela, &rela_sec->relas, list) { + if (rela->sym == sym->twin) + return rela; + } + + /* Reference to static variable might have moved to child function section */ + if (rela_sec->base->sym) { + struct symbol *parent = rela_sec->base->sym; + struct symbol *child; + + list_for_each_entry(child, &parent->children, subfunction_node) { + /* Only look in children whose rela section differ from the parent's */ + if (child->sec->rela == parent->sec->rela || + !child->sec->rela) + continue; + + rela = kpatch_find_static_twin_ref(child->sec->rela, sym); + if (rela) + return rela; + } + } + + return NULL; +} + /* * gcc renames static local variables by appending a period and a number. For * example, __foo could be renamed to __foo.31452. Unfortunately this number @@ -957,8 +1221,8 @@ static void kpatch_correlate_static_local_variables(struct kpatch_elf *base, { struct symbol *sym, *patched_sym; struct section *sec; - struct rela *rela, *rela2; - int bundled, patched_bundled, found; + struct rela *rela; + int bundled, patched_bundled; /* * First undo the correlations for all static locals. Two static @@ -970,23 +1234,17 @@ static void kpatch_correlate_static_local_variables(struct kpatch_elf *base, if (!kpatch_is_normal_static_local(sym)) continue; - if (sym->twin) { - sym->twin->twin = NULL; - sym->twin = NULL; - } + if (sym->twin) + UNCORRELATE_ELEMENT(sym); bundled = sym == sym->sec->sym; if (bundled && sym->sec->twin) { - sym->sec->twin->twin = NULL; - sym->sec->twin = NULL; + UNCORRELATE_ELEMENT(sym->sec); - sym->sec->secsym->twin->twin = NULL; - sym->sec->secsym->twin = NULL; + UNCORRELATE_ELEMENT(sym->sec->secsym); - if (sym->sec->rela) { - sym->sec->rela->twin->twin = NULL; - sym->sec->rela->twin = NULL; - } + if (sym->sec->rela) + UNCORRELATE_ELEMENT(sym->sec->rela); } } @@ -1037,31 +1295,13 @@ static void kpatch_correlate_static_local_variables(struct kpatch_elf *base, if (bundled != patched_bundled) ERROR("bundle mismatch for symbol %s", sym->name); if (!bundled && sym->sec->twin != patched_sym->sec) - ERROR("sections %s and %s aren't correlated", - sym->sec->name, patched_sym->sec->name); - - log_debug("renaming and correlating static local %s to %s\n", - patched_sym->name, sym->name); - - patched_sym->name = strdup(sym->name); - sym->twin = patched_sym; - patched_sym->twin = sym; + ERROR("sections %s and %s aren't correlated for symbol %s", + sym->sec->name, patched_sym->sec->name, sym->name); - /* set initial status, might change */ - sym->status = patched_sym->status = SAME; + kpatch_correlate_static_local(sym, patched_sym); - if (bundled) { - sym->sec->twin = patched_sym->sec; - patched_sym->sec->twin = sym->sec; - - sym->sec->secsym->twin = patched_sym->sec->secsym; - patched_sym->sec->secsym->twin = sym->sec->secsym; - - if (sym->sec->rela && patched_sym->sec->rela) { - sym->sec->rela->twin = patched_sym->sec->rela; - patched_sym->sec->rela->twin = sym->sec->rela; - } - } + if (bundled) + kpatch_correlate_section(sym->sec, patched_sym->sec); } } @@ -1082,28 +1322,29 @@ static void kpatch_correlate_static_local_variables(struct kpatch_elf *base, continue; list_for_each_entry(rela, &sec->relas, list) { + struct section *target_sec = sec; sym = rela->sym; if (!kpatch_is_normal_static_local(sym)) continue; - if (!sym->twin || !sec->twin) - DIFF_FATAL("reference to static local variable %s in %s was removed", - sym->name, - kpatch_section_function_name(sec)); + if (!sec->twin && sec->base->sym) { + struct symbol *parent = NULL; - found = 0; - list_for_each_entry(rela2, &sec->twin->relas, list) { - if (rela2->sym == sym->twin) { - found = 1; - break; - } + parent = kpatch_get_correlated_parent(sec->base->sym); + if (parent) + target_sec = parent->sec->rela; } - if (!found) + if (!sym->twin || !target_sec->twin) + DIFF_FATAL("reference to static local variable %s in %s was removed", + sym->name, + kpatch_section_function_name(target_sec)); + + if (!kpatch_find_static_twin_ref(target_sec->twin, sym)) DIFF_FATAL("static local %s has been correlated with %s, but patched %s is missing a reference to it", sym->name, sym->twin->name, - kpatch_section_function_name(sec->twin)); + kpatch_section_function_name(target_sec->twin)); } } @@ -1149,12 +1390,17 @@ static void kpatch_compare_correlated_elements(struct kpatch_elf *kelf) } #ifdef __x86_64__ -static void rela_insn(struct section *sec, struct rela *rela, struct insn *insn) +static void rela_insn(const struct section *sec, const struct rela *rela, + struct insn *insn) { unsigned long insn_addr, start, end, rela_addr; start = (unsigned long)sec->base->data->d_buf; end = start + sec->base->sh.sh_size; + + if (end <= start) + ERROR("bad section size"); + rela_addr = start + rela->offset; for (insn_addr = start; insn_addr < end; insn_addr += insn->length) { insn_init(insn, (void *)insn_addr, 1); @@ -1169,6 +1415,28 @@ static void rela_insn(struct section *sec, struct rela *rela, struct insn *insn) } #endif +static bool is_callback_section(struct section *sec) { + + static char *callback_sections[] = { + ".kpatch.callbacks.pre_patch", + ".kpatch.callbacks.post_patch", + ".kpatch.callbacks.pre_unpatch", + ".kpatch.callbacks.post_unpatch", + ".rela.kpatch.callbacks.pre_patch", + ".rela.kpatch.callbacks.post_patch", + ".rela.kpatch.callbacks.pre_unpatch", + ".rela.kpatch.callbacks.post_unpatch", + NULL, + }; + char **callback_sec; + + for (callback_sec = callback_sections; *callback_sec; callback_sec++) + if (!strcmp(sec->name, *callback_sec)) + return true; + + return false; +} + /* * Mangle the relas a little. The compiler will sometimes use section symbols * to reference local objects and functions rather than the object or function @@ -1181,7 +1449,7 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf) struct section *sec; struct rela *rela; struct symbol *sym; - int add_off; + unsigned int add_off; log_debug("\n"); @@ -1192,28 +1460,40 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf) list_for_each_entry(rela, &sec->relas, list) { - if (rela->sym->type != STT_SECTION) + if (rela->sym->type != STT_SECTION || !rela->sym->sec) continue; /* * Replace references to bundled sections with their * symbols. */ - if (rela->sym->sec && rela->sym->sec->sym) { + if (rela->sym->sec->sym) { rela->sym = rela->sym->sec->sym; + /* + * On ppc64le with GCC6+, even with + * -ffunction-sections, the function symbol + * starts 8 bytes past the beginning of the + * section, because the .TOC pointer is at the + * beginning, right before the code. So even + * though the symbol is bundled, we can't + * assume it's at offset 0 in the section. + */ + rela->addend -= rela->sym->sym.st_value; + continue; } #ifdef __powerpc64__ add_off = 0; #else - if (rela->type == R_X86_64_PC32) { + if (rela->type == R_X86_64_PC32 || + rela->type == R_X86_64_PLT32) { struct insn insn; rela_insn(sec, rela, &insn); - add_off = (long)insn.next_byte - + add_off = (unsigned int)((long)insn.next_byte - (long)sec->base->data->d_buf - - rela->offset; + rela->offset); } else if (rela->type == R_X86_64_64 || rela->type == R_X86_64_32S) add_off = 0; @@ -1226,7 +1506,7 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf) * with their symbols. */ list_for_each_entry(sym, &kelf->symbols, list) { - int start, end; + long start, end; if (sym->type == STT_SECTION || sym->sec != rela->sym->sec) @@ -1237,8 +1517,8 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf) if (!is_text_section(sym->sec) && rela->type == R_X86_64_32S && - rela->addend == (int)sym->sec->sh.sh_size && - end == (int)sym->sec->sh.sh_size) { + rela->addend == (long)sym->sec->sh.sh_size && + end == (long)sym->sec->sh.sh_size) { /* * A special case where gcc needs a @@ -1276,7 +1556,7 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf) rela->addend + add_off >= end) continue; - log_debug("%s: replacing %s+%d reference with %s+%d\n", + log_debug("%s: replacing %s+%ld reference with %s+%ld\n", sec->name, rela->sym->name, rela->addend, sym->name, rela->addend - start); @@ -1296,7 +1576,8 @@ static void kpatch_check_func_profiling_calls(struct kpatch_elf *kelf) int errs = 0; list_for_each_entry(sym, &kelf->symbols, list) { - if (sym->type != STT_FUNC || sym->status != CHANGED) + if (sym->type != STT_FUNC || sym->status != CHANGED || + (sym->parent && sym->parent->status == CHANGED)) continue; if (!sym->twin->has_func_profiling) { log_normal("function %s has no fentry/mcount call, unable to patch\n", @@ -1450,57 +1731,29 @@ static int kpatch_include_callback_elements(struct kpatch_elf *kelf) struct rela *rela; int found = 0; - static char *callback_sections[] = { - ".kpatch.callbacks.pre_patch", - ".kpatch.callbacks.post_patch", - ".kpatch.callbacks.pre_unpatch", - ".kpatch.callbacks.post_unpatch", - ".rela.kpatch.callbacks.pre_patch", - ".rela.kpatch.callbacks.post_patch", - ".rela.kpatch.callbacks.pre_unpatch", - ".rela.kpatch.callbacks.post_unpatch", - NULL, - }; - char **callback_section; - /* include load/unload sections */ list_for_each_entry(sec, &kelf->sections, list) { + if (!is_callback_section(sec)) + continue; - for (callback_section = callback_sections; *callback_section; callback_section++) { - - if (strcmp(*callback_section, sec->name)) - continue; - - sec->include = 1; - found = 1; - if (is_rela_section(sec)) { - /* include callback dependencies */ - rela = list_entry(sec->relas.next, - struct rela, list); - sym = rela->sym; - log_normal("found callback: %s\n",sym->name); - kpatch_include_symbol(sym); - /* strip the callback symbol */ - sym->include = 0; - sym->sec->sym = NULL; - /* use section symbol instead */ - rela->sym = sym->sec->secsym; - } else { - sec->secsym->include = 1; - } + sec->include = 1; + found = 1; + if (is_rela_section(sec)) { + /* include callback dependencies */ + rela = list_entry(sec->relas.next, struct rela, list); + sym = rela->sym; + log_normal("found callback: %s\n",sym->name); + kpatch_include_symbol(sym); + } else { + sec->secsym->include = 1; } } - /* Strip temporary global structures used by the callback macros. */ + /* Strip temporary structure symbols used by the callback macros. */ list_for_each_entry(sym, &kelf->symbols, list) { - if (!sym->sec) - continue; - for (callback_section = callback_sections; *callback_section; callback_section++) { - if (!strcmp(*callback_section, sym->sec->name)) { - sym->include = 0; - break; - } - } + if (sym->type == STT_OBJECT && sym->sec && + is_callback_section(sym->sec)) + sym->include = 0; } return found; @@ -1576,7 +1829,7 @@ static void kpatch_print_changes(struct kpatch_elf *kelf) struct symbol *sym; list_for_each_entry(sym, &kelf->symbols, list) { - if (!sym->include || !sym->sec || sym->type != STT_FUNC) + if (!sym->include || !sym->sec || sym->type != STT_FUNC || sym->parent) continue; if (sym->status == NEW) log_normal("new function: %s\n", sym->name); @@ -1692,29 +1945,44 @@ static int ex_table_group_size(struct kpatch_elf *kelf, int offset) return size; } -#ifdef __x86_64__ -static int parainstructions_group_size(struct kpatch_elf *kelf, int offset) +static int jump_table_group_size(struct kpatch_elf *kelf, int offset) { static int size = 0; char *str; if (!size) { - str = getenv("PARA_STRUCT_SIZE"); + str = getenv("JUMP_STRUCT_SIZE"); if (!str) - ERROR("PARA_STRUCT_SIZE not set"); + ERROR("JUMP_STRUCT_SIZE not set"); size = atoi(str); } return size; } -static int altinstructions_group_size(struct kpatch_elf *kelf, int offset) +#ifdef __x86_64__ +static int parainstructions_group_size(struct kpatch_elf *kelf, int offset) { static int size = 0; char *str; if (!size) { - str = getenv("ALT_STRUCT_SIZE"); + str = getenv("PARA_STRUCT_SIZE"); + if (!str) + ERROR("PARA_STRUCT_SIZE not set"); + size = atoi(str); + } + + return size; +} + +static int altinstructions_group_size(struct kpatch_elf *kelf, int offset) +{ + static int size = 0; + char *str; + + if (!size) { + str = getenv("ALT_STRUCT_SIZE"); if (!str) ERROR("ALT_STRUCT_SIZE not set"); size = atoi(str); @@ -1746,7 +2014,12 @@ static int fixup_entry_group_size(struct kpatch_elf *kelf, int offset) static int fixup_lwsync_group_size(struct kpatch_elf *kelf, int offset) { - return 4; + return 8; +} + +static int fixup_barrier_nospec_group_size(struct kpatch_elf *kelf, int offset) +{ + return 8; } #endif @@ -1792,10 +2065,12 @@ static int fixup_group_size(struct kpatch_elf *kelf, int offset) /* last group */ struct section *fixupsec; fixupsec = find_section_by_name(&kelf->sections, ".fixup"); - return fixupsec->sh.sh_size - offset; + if (!fixupsec) + ERROR("missing .fixup section"); + return (int)(fixupsec->sh.sh_size - offset); } - return rela->addend - offset; + return (int)(rela->addend - offset); } static struct special_section special_sections[] = { @@ -1803,16 +2078,6 @@ static struct special_section special_sections[] = { .name = "__bug_table", .group_size = bug_table_group_size, }, -#ifdef __x86_64__ - { - .name = ".smp_locks", - .group_size = smp_locks_group_size, - }, - { - .name = ".parainstructions", - .group_size = parainstructions_group_size, - }, -#endif { .name = ".fixup", .group_size = fixup_group_size, @@ -1821,8 +2086,20 @@ static struct special_section special_sections[] = { .name = "__ex_table", /* must come after .fixup */ .group_size = ex_table_group_size, }, + { + .name = "__jump_table", + .group_size = jump_table_group_size, + }, #ifdef __x86_64__ { + .name = ".smp_locks", + .group_size = smp_locks_group_size, + }, + { + .name = ".parainstructions", + .group_size = parainstructions_group_size, + }, + { .name = ".altinstructions", .group_size = altinstructions_group_size, }, @@ -1844,29 +2121,133 @@ static struct special_section special_sections[] = { .name = "__lwsync_fixup", .group_size = fixup_lwsync_group_size, }, + { + .name = "__barrier_nospec_fixup", + .group_size = fixup_barrier_nospec_group_size, + }, #endif {}, }; -static int should_keep_rela_group(struct section *sec, unsigned int start, - unsigned int size) +static bool should_keep_jump_label(struct lookup_table *lookup, + struct section *sec, + unsigned int group_offset, + unsigned int group_size, + int *jump_labels_found) +{ + struct rela *code = NULL, *key = NULL, *rela; + bool tracepoint = false, dynamic_debug = false; + struct lookup_result symbol; + int i = 0; + + /* + * Here we hard-code knowledge about the contents of the jump_entry + * struct. It has three fields: code, target, and key. Each field has + * a relocation associated with it. + */ + list_for_each_entry(rela, &sec->relas, list) { + if (rela->offset >= group_offset && + rela->offset < group_offset + group_size) { + if (i == 0) + code = rela; + else if (i == 2) + key = rela; + i++; + } + } + + if (i != 3 || !key || !code) + ERROR("BUG: __jump_table has an unexpected format"); + + if (!strncmp(key->sym->name, "__tracepoint_", 13)) + tracepoint = true; + + if (is_dynamic_debug_symbol(key->sym)) + dynamic_debug = true; + + if (KLP_ARCH) { + /* + * On older kernels (with .klp.arch support), jump labels + * aren't supported at all. Error out when they occur in a + * replacement function, with the exception of tracepoints and + * dynamic debug printks. An inert tracepoint or printk is + * harmless enough, but a broken jump label can cause + * unexpected behavior. + */ + if (tracepoint || dynamic_debug) + return false; + + /* + * This will be upgraded to an error after all jump labels have + * been reported. + */ + log_normal("Found a jump label at %s()+0x%lx, using key %s. Jump labels aren't supported with this kernel. Use static_key_enabled() instead.\n", + code->sym->name, code->addend, key->sym->name); + (*jump_labels_found)++; + return false; + } + + /* + * On newer (5.8+) kernels, jump labels are supported in the case where + * the corresponding static key lives in vmlinux. That's because such + * kernels apply vmlinux-specific .klp.rela sections at the same time + * (in the klp module load) as normal relas, before jump label init. + * On the other hand, jump labels based on static keys which are + * defined in modules aren't supported, because late module patching + * can result in the klp relas getting applied *after* the klp module's + * jump label init. + */ + + if (lookup_symbol(lookup, key->sym->name, &symbol) && + strcmp(symbol.objname, "vmlinux")) { + + /* The static key lives in a module -- not supported */ + + /* Inert tracepoints and dynamic debug printks are harmless */ + if (tracepoint || dynamic_debug) + return false; + + /* + * This will be upgraded to an error after all jump labels have + * been reported. + */ + log_normal("Found a jump label at %s()+0x%lx, using key %s, which is defined in a module. Use static_key_enabled() instead.\n", + code->sym->name, code->addend, key->sym->name); + (*jump_labels_found)++; + return false; + } + + /* The static key lives in vmlinux or the patch module itself */ + return true; +} + +static bool should_keep_rela_group(struct lookup_table *lookup, + struct section *sec, unsigned int offset, + unsigned int size, int *jump_labels_found) { struct rela *rela; - int found = 0; + bool found = false; /* check if any relas in the group reference any changed functions */ list_for_each_entry(rela, &sec->relas, list) { - if (rela->offset >= start && - rela->offset < start + size && + if (rela->offset >= offset && + rela->offset < offset + size && rela->sym->type == STT_FUNC && rela->sym->sec->include) { - found = 1; + found = true; log_debug("new/changed symbol %s found in special section %s\n", rela->sym->name, sec->name); } } - return found; + if (!found) + return false; + + if (!strcmp(sec->name, ".rela__jump_table")) + return should_keep_jump_label(lookup, sec, offset, size, + jump_labels_found); + + return true; } /* @@ -1895,12 +2276,14 @@ static void kpatch_update_ex_table_addend(struct kpatch_elf *kelf, } static void kpatch_regenerate_special_section(struct kpatch_elf *kelf, + struct lookup_table *lookup, struct special_section *special, struct section *sec) { struct rela *rela, *safe; char *src, *dest; - unsigned int group_size, src_offset, dest_offset, include; + unsigned int group_size, src_offset, dest_offset; + int jump_labels_found = 0; LIST_HEAD(newrelas); @@ -1918,7 +2301,6 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf, } } - group_size = 0; src_offset = 0; dest_offset = 0; for ( ; src_offset < sec->base->sh.sh_size; src_offset += group_size) { @@ -1934,11 +2316,10 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf, * section. */ if (src_offset + group_size > sec->base->sh.sh_size) - group_size = sec->base->sh.sh_size - src_offset; - - include = should_keep_rela_group(sec, src_offset, group_size); + group_size = (unsigned int)(sec->base->sh.sh_size - src_offset); - if (!include) + if (!should_keep_rela_group(lookup, sec, src_offset, group_size, + &jump_labels_found)) continue; /* @@ -1958,7 +2339,6 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf, rela->sym->include = 1; - if (!strcmp(special->name, ".fixup")) kpatch_update_ex_table_addend(kelf, special, src_offset, @@ -1972,6 +2352,10 @@ static void kpatch_regenerate_special_section(struct kpatch_elf *kelf, dest_offset += group_size; } + if (jump_labels_found) + ERROR("Found %d jump label(s) in the patched code. Jump labels aren't currently supported. Use static_key_enabled() instead.", + jump_labels_found); + if (!dest_offset) { /* no changed or global functions referenced */ sec->status = sec->base->status = SAME; @@ -2020,6 +2404,9 @@ static void kpatch_regenerate_orc_sections(struct kpatch_elf *kelf) return; orc_entry_size = atoi(str); + if (!orc_entry_size) + ERROR("bad ORC_STRUCT_SIZE"); + LIST_HEAD(newrelas); orc_sec = find_section_by_name(&kelf->sections, ".orc_unwind"); @@ -2099,8 +2486,8 @@ static void kpatch_check_relocations(struct kpatch_elf *kelf) list_for_each_entry(rela, &sec->relas, list) { if (rela->sym->sec) { sdata = rela->sym->sec->data; - if (rela->addend > (int)sdata->d_size) { - ERROR("out-of-range relocation %s+%x in %s", rela->sym->sec->name, + if (rela->addend > (long)sdata->d_size) { + ERROR("out-of-range relocation %s+%lx in %s", rela->sym->sec->name, rela->addend, sec->name); } } @@ -2167,6 +2554,7 @@ static void kpatch_mark_ignored_sections(struct kpatch_elf *kelf) * from the section data comparison, but this is a simpler way. */ strsec->include = 1; + strsec->secsym->include = 1; name = strsec->data->d_buf + rela->addend; ignoresec = find_section_by_name(&kelf->sections, name); if (!ignoresec) @@ -2209,6 +2597,16 @@ static void kpatch_mark_ignored_sections_same(struct kpatch_elf *kelf) sym->status = SAME; } +static void kpatch_mark_ignored_children_same(struct symbol *sym) +{ + struct symbol *child; + + list_for_each_entry(child, &sym->children, subfunction_node) { + child->status = SAME; + kpatch_mark_ignored_children_same(child); + } +} + static void kpatch_mark_ignored_functions_same(struct kpatch_elf *kelf) { struct section *sec; @@ -2229,6 +2627,9 @@ static void kpatch_mark_ignored_functions_same(struct kpatch_elf *kelf) log_normal("NOTICE: no change detected in function %s, unnecessary KPATCH_IGNORE_FUNCTION()?\n", rela->sym->name); rela->sym->status = SAME; rela->sym->sec->status = SAME; + + kpatch_mark_ignored_children_same(rela->sym); + if (rela->sym->sec->secsym) rela->sym->sec->secsym->status = SAME; if (rela->sym->sec->rela) @@ -2245,15 +2646,16 @@ static void kpatch_mark_ignored_functions_same(struct kpatch_elf *kelf) static void kpatch_create_kpatch_arch_section(struct kpatch_elf *kelf, char *objname) { struct special_section *special; - struct kpatch_arch *entries; struct symbol *strsym; struct section *sec, *karch_sec; struct rela *rela; int nr, index = 0; + if (!KLP_ARCH) + return; + nr = sizeof(special_sections) / sizeof(special_sections[0]); - karch_sec = create_section_pair(kelf, ".kpatch.arch", sizeof(*entries), nr); - entries = karch_sec->data->d_buf; + karch_sec = create_section_pair(kelf, ".kpatch.arch", sizeof(struct kpatch_arch), nr); /* lookup strings symbol */ strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings"); @@ -2274,16 +2676,16 @@ static void kpatch_create_kpatch_arch_section(struct kpatch_elf *kelf, char *obj rela->sym = sec->secsym; rela->type = ABSOLUTE_RELA_TYPE; rela->addend = 0; - rela->offset = index * sizeof(*entries) + \ - offsetof(struct kpatch_arch, sec); + rela->offset = (unsigned int)(index * sizeof(struct kpatch_arch) + \ + offsetof(struct kpatch_arch, sec)); /* entries[index].objname */ ALLOC_LINK(rela, &karch_sec->rela->relas); rela->sym = strsym; rela->type = ABSOLUTE_RELA_TYPE; rela->addend = offset_of_string(&kelf->strings, objname); - rela->offset = index * sizeof(*entries) + \ - offsetof(struct kpatch_arch, objname); + rela->offset = (unsigned int)(index * sizeof(struct kpatch_arch) + \ + offsetof(struct kpatch_arch, objname)); index++; } @@ -2292,7 +2694,8 @@ static void kpatch_create_kpatch_arch_section(struct kpatch_elf *kelf, char *obj karch_sec->sh.sh_size = karch_sec->data->d_size; } -static void kpatch_process_special_sections(struct kpatch_elf *kelf) +static void kpatch_process_special_sections(struct kpatch_elf *kelf, + struct lookup_table *lookup) { struct special_section *special; struct section *sec; @@ -2302,16 +2705,12 @@ static void kpatch_process_special_sections(struct kpatch_elf *kelf) for (special = special_sections; special->name; special++) { sec = find_section_by_name(&kelf->sections, special->name); - if (!sec) + if (!sec || !sec->rela) continue; - sec = sec->rela; - if (!sec) - continue; - - kpatch_regenerate_special_section(kelf, special, sec); + kpatch_regenerate_special_section(kelf, lookup, special, sec->rela); - if (!strcmp(special->name, ".altinstructions") && sec->base->include) + if (!strcmp(special->name, ".altinstructions") && sec->include) altinstr = 1; } @@ -2342,29 +2741,32 @@ static void kpatch_process_special_sections(struct kpatch_elf *kelf) sec->rela->include = 1; /* include all symbols referenced by relas */ list_for_each_entry(rela, &sec->rela->relas, list) - rela->sym->include = 1; + kpatch_include_symbol(rela->sym); } } - /* - * The following special sections aren't supported, so make sure we - * don't ever try to include them. Otherwise the kernel will see the - * jump table during module loading and get confused. Generally it - * should be safe to exclude them, it just means that you can't modify - * jump labels and enable tracepoints in a patched function. - */ - list_for_each_entry(sec, &kelf->sections, list) { - if (strcmp(sec->name, "__jump_table") && - strcmp(sec->name, "__tracepoints") && - strcmp(sec->name, "__tracepoints_ptrs") && - strcmp(sec->name, "__tracepoints_strings")) - continue; + if (KLP_ARCH) { + /* + * The following special sections aren't supported with older + * kernels, so make sure we don't ever try to include them. + * Otherwise the kernel will see the jump table during module + * loading and get confused. Generally it should be safe to + * exclude them, it just means that you can't modify jump + * labels and enable tracepoints in a patched function. + */ + list_for_each_entry(sec, &kelf->sections, list) { + if (strcmp(sec->name, "__jump_table") && + strcmp(sec->name, "__tracepoints") && + strcmp(sec->name, "__tracepoints_ptrs") && + strcmp(sec->name, "__tracepoints_strings")) + continue; - sec->status = SAME; - sec->include = 0; - if (sec->rela) { - sec->rela->status = SAME; - sec->rela->include = 0; + sec->status = SAME; + sec->include = 0; + if (sec->rela) { + sec->rela->status = SAME; + sec->rela->include = 0; + } } } @@ -2400,7 +2802,7 @@ static struct sym_compare_type *kpatch_elf_locals(struct kpatch_elf *kelf) continue; sym_array[i].type = sym->type; - sym_array[i++].name = sym->name; + sym_array[i++].name = strdup(sym->name); } sym_array[i].type = 0; sym_array[i].name = NULL; @@ -2416,14 +2818,17 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf, struct section *sec, *relasec; struct symbol *sym, *strsym; struct rela *rela; - struct lookup_result result; + struct lookup_result symbol; struct kpatch_patch_func *funcs; /* count patched functions */ nr = 0; - list_for_each_entry(sym, &kelf->symbols, list) - if (sym->type == STT_FUNC && sym->status == CHANGED) - nr++; + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type != STT_FUNC || sym->status != CHANGED || + sym->parent) + continue; + nr++; + } /* create text/rela section pair */ sec = create_section_pair(kelf, ".kpatch.funcs", sizeof(*funcs), nr); @@ -2441,71 +2846,69 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf, /* populate sections */ index = 0; list_for_each_entry(sym, &kelf->symbols, list) { - if (sym->type == STT_FUNC && sym->status == CHANGED) { - if (sym->bind == STB_LOCAL) { - if (lookup_local_symbol(table, sym->name, - &result)) - ERROR("lookup_local_symbol %s", - sym->name); - } else { - if(lookup_global_symbol(table, sym->name, - &result)) - ERROR("lookup_global_symbol %s", - sym->name); - } - log_debug("lookup for %s @ 0x%016lx len %lu\n", - sym->name, result.value, result.size); + if (sym->type != STT_FUNC || sym->status != CHANGED || + sym->parent) + continue; - /* - * Convert global symbols to local so other objects in - * the patch module (like the patch callback object's init - * code) won't link to this function and call it before - * its relocations have been applied. - */ - sym->bind = STB_LOCAL; - sym->sym.st_info = GELF_ST_INFO(sym->bind, sym->type); + if (!lookup_symbol(table, sym->name, &symbol)) + ERROR("can't find symbol '%s' in symbol table", sym->name); - /* add entry in text section */ - funcs[index].old_addr = result.value; - funcs[index].old_size = result.size; - funcs[index].new_size = sym->sym.st_size; - funcs[index].sympos = result.pos; + if (sym->bind == STB_LOCAL && symbol.global) + ERROR("can't find local symbol '%s' in symbol table", sym->name); - /* - * Add a relocation that will populate - * the funcs[index].new_addr field at - * module load time. - */ - ALLOC_LINK(rela, &relasec->relas); - rela->sym = sym; - rela->type = ABSOLUTE_RELA_TYPE; - rela->addend = 0; - rela->offset = index * sizeof(*funcs); + log_debug("lookup for %s: obj=%s sympos=%lu size=%lu", + sym->name, symbol.objname, symbol.sympos, + symbol.size); - /* - * Add a relocation that will populate - * the funcs[index].name field. - */ - ALLOC_LINK(rela, &relasec->relas); - rela->sym = strsym; - rela->type = ABSOLUTE_RELA_TYPE; - rela->addend = offset_of_string(&kelf->strings, sym->name); - rela->offset = index * sizeof(*funcs) + - offsetof(struct kpatch_patch_func, name); + /* + * Convert global symbols to local so other objects in the + * patch module (like the patch callback object's init code) + * won't link to this function and call it before its + * relocations have been applied. + */ + sym->bind = STB_LOCAL; + sym->sym.st_info = (unsigned char) + GELF_ST_INFO(sym->bind, sym->type); - /* - * Add a relocation that will populate - * the funcs[index].objname field. - */ - ALLOC_LINK(rela, &relasec->relas); - rela->sym = strsym; - rela->type = ABSOLUTE_RELA_TYPE; - rela->addend = objname_offset; - rela->offset = index * sizeof(*funcs) + - offsetof(struct kpatch_patch_func,objname); + /* add entry in text section */ + funcs[index].old_addr = symbol.addr; + funcs[index].old_size = symbol.size; + funcs[index].new_size = sym->sym.st_size; + funcs[index].sympos = symbol.sympos; - index++; - } + /* + * Add a relocation that will populate the + * funcs[index].new_addr field at module load time. + */ + ALLOC_LINK(rela, &relasec->relas); + rela->sym = sym; + rela->type = ABSOLUTE_RELA_TYPE; + rela->addend = 0; + rela->offset = (unsigned int)(index * sizeof(*funcs)); + + /* + * Add a relocation that will populate the funcs[index].name + * field. + */ + ALLOC_LINK(rela, &relasec->relas); + rela->sym = strsym; + rela->type = ABSOLUTE_RELA_TYPE; + rela->addend = offset_of_string(&kelf->strings, sym->name); + rela->offset = (unsigned int)(index * sizeof(*funcs) + + offsetof(struct kpatch_patch_func, name)); + + /* + * Add a relocation that will populate the funcs[index].objname + * field. + */ + ALLOC_LINK(rela, &relasec->relas); + rela->sym = strsym; + rela->type = ABSOLUTE_RELA_TYPE; + rela->addend = objname_offset; + rela->offset = (unsigned int)(index * sizeof(*funcs) + + offsetof(struct kpatch_patch_func,objname)); + + index++; } /* sanity check, index should equal nr */ @@ -2521,75 +2924,178 @@ static int kpatch_is_core_module_symbol(char *name) !strcmp(name, "kpatch_shadow_get")); } -/* - * If the patched code refers to a symbol, for example, calls a function - * or stores a pointer to a function somewhere, the address of that symbol - * must be resolved somehow before the patch is applied. The symbol may be - * present in the original code too, so the patch may refer either to that - * version of the symbol (dynrela is used for that) or to its patched - * version directly (with a normal relocation). - * - * Dynrelas may be needed for the symbols not present in this object file - * (rela->sym->sec is NULL), because it is unknown if the patched versions - * of these symbols exist and where they are. - * - * The patched code can usually refer to a symbol from this object file - * directly. If it is a function, this may also improve performance because - * it will not be needed to call the original function first, find the - * patched one and then use Ftrace to pass control to it. - * - * There is an exception though, at least on x86. It is safer to use - * a dynrela if the patched code stores a pointer to a function somewhere - * (relocation of type R_X86_64_32S). The function could be used as - * a callback and some kinds of callbacks are called asynchronously. If - * the patch module sets such callback and is unloaded shortly after, - * the kernel could try to call the function via an invalid pointer and - * would crash. With dynrela, the kernel would call the original function - * in that case. - */ static int function_ptr_rela(const struct rela *rela) { const struct rela *rela_toc = toc_rela(rela); return (rela_toc && rela_toc->sym->type == STT_FUNC && - /* skip switch table on PowerPC */ + !rela_toc->sym->parent && rela_toc->addend == (int)rela_toc->sym->sym.st_value && (rela->type == R_X86_64_32S || rela->type == R_PPC64_TOC16_HA || rela->type == R_PPC64_TOC16_LO_DS)); } -static int may_need_dynrela(const struct rela *rela) +static bool need_dynrela(struct lookup_table *table, const struct rela *rela) { + struct lookup_result symbol; + /* - * References to .TOC. are treated specially by the module loader and + * These references are treated specially by the module loader and * should never be converted to dynrelas. */ if (rela->type == R_PPC64_REL16_HA || rela->type == R_PPC64_REL16_LO || - rela->type == R_PPC64_REL64) - return 0; + rela->type == R_PPC64_REL64 || rela->type == R_PPC64_ENTRY) + return false; - if (!rela->sym->sec) - return 1; + /* + * On powerpc, the function prologue generated by GCC 6 has the + * sequence: + * + * .globl my_func + * .type my_func, @function + * .quad .TOC.-my_func + * my_func: + * .reloc ., R_PPC64_ENTRY ; optional + * ld r2,-8(r12) + * add r2,r2,r12 + * .localentry my_func, .-my_func + * + * The R_PPC64_ENTRY is optional and its symbol might have an empty + * name. Leave it as a normal rela. + */ + if (rela->type == R_PPC64_ENTRY) + return false; /* - * Nested functions used as callbacks are a special case. - * They are not supposed to be visible outside of the - * function that defines them. Their names may differ in - * the original and the patched kernels which makes it - * difficult to use dynrelas. Fortunately, nested functions - * are rare and are unlikely to be used as asynchronous - * callbacks, so the patched code can refer to them directly. - * It seems, one can only distinguish such functions by their - * names containing a dot. Other kinds of functions with - * such names (e.g. optimized copies of functions) are - * unlikely to be used as callbacks. + * Allow references to core module symbols to remain as normal + * relas. They should be exported. */ - return (function_ptr_rela(rela) && - toc_rela(rela)->sym->status != NEW && - !strchr(toc_rela(rela)->sym->name, '.')); + if (kpatch_is_core_module_symbol(rela->sym->name)) + return false; + + if (rela->sym->sec) { + /* + * Internal symbols usually don't need dynrelas, because they + * live in the patch module and can be relocated normally. + * + * There's one exception: function pointers. + * + * If the rela references a function pointer, we convert it to + * a dynrela, so that the function pointer will refer to the + * original function rather than the patched function. This + * can prevent crashes in cases where the function pointer is + * called asynchronously after the patch module has been + * unloaded. + */ + if (!function_ptr_rela(rela)) + return false; + + /* + * Function pointers which refer to _nested_ functions are a + * special case. They are not supposed to be visible outside + * of the function that defines them. Their names may differ + * in the original and the patched kernels which makes it + * difficult to use dynrelas. Fortunately, nested functions + * are rare and are unlikely to be used as asynchronous + * callbacks, so the patched code can refer to them directly. + * It seems, one can only distinguish such functions by their + * names containing a dot. Other kinds of functions with such + * names (e.g. optimized copies of functions) are unlikely to + * be used as callbacks. + * + * Function pointers to *new* functions don't have this issue, + * just use a normal rela for them. + */ + return toc_rela(rela)->sym->status != NEW && + !strchr(toc_rela(rela)->sym->name, '.'); + } + + if (!lookup_symbol(table, rela->sym->name, &symbol)) { + /* + * Assume the symbol lives in another .o in the patch module. + * A normal rela should work. + */ + return false; + } + + if (rela->sym->bind == STB_LOCAL) { + + if (symbol.global) + ERROR("can't find local symbol '%s' in symbol table", + rela->sym->name); + + /* + * The symbol is (formerly) local. Use a dynrela to access the + * original version of the symbol in the patched object. + */ + return true; + } + + if (symbol.exported) { + + if (is_gcc6_localentry_bundled_sym(rela->sym)) { + /* + * On powerpc, the symbol is global and exported, but + * it was also in the changed object file. In this + * case the rela refers to the 'localentry' point, so a + * normal rela wouldn't work. Force a dynrela so it + * can be handled correctly by the livepatch relocation + * code. + */ + return true; + } + + if (!strcmp(symbol.objname, "vmlinux")) { + /* + * The symbol is exported by vmlinux. Use a normal + * rela. + */ + return false; + } + + /* + * The symbol is exported by the to-be-patched module, or by + * another module which the patched module depends on. Use a + * dynrela because of late module loading: the patch module may + * be loaded before the to-be-patched (or other) module. + */ + return true; + } + + if (symbol.global) { + /* + * The symbol is global in the to-be-patched object, but not + * exported. Use a dynrela to work around the fact that it's + * an unexported sybmbol. + */ + return true; + } + + /* + * The symbol is global and not exported, but it's not in the parent + * object. The only explanation is that it's defined in another object + * in the patch module. A normal rela should resolve it. + */ + return false; } +/* + * kpatch_create_intermediate_sections() + * + * The primary purpose of this function is to convert some relas (also known as + * relocations) to dynrelas (also known as dynamic relocations or livepatch + * relocations or klp relas). + * + * If the patched code refers to a symbol, for example, if it calls a function + * or stores a pointer to a function somewhere or accesses some global data, + * the address of that symbol must be resolved somehow before the patch is + * applied. + * + * If the symbol lives outside the patch module, and if it's not exported by + * vmlinux (e.g., with EXPORT_SYMBOL) then the rela needs to be converted to a + * dynrela so the livepatch code can resolve it at runtime. + */ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf, struct lookup_table *table, char *objname, @@ -2601,11 +3107,10 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf, struct symbol *strsym, *ksym_sec_sym; struct kpatch_symbol *ksyms; struct kpatch_relocation *krelas; - struct lookup_result result; - char *sym_objname; - int ret, vmlinux, external; - - vmlinux = !strcmp(objname, "vmlinux"); + struct lookup_result symbol; + bool special; + bool vmlinux = !strcmp(objname, "vmlinux"); + struct special_section *s; /* count rela entries that need to be dynamic */ nr = 0; @@ -2615,25 +3120,22 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf, if (!strcmp(sec->name, ".rela.kpatch.funcs")) continue; list_for_each_entry(rela, &sec->relas, list) { - nr++; /* upper bound on number of kpatch relas and symbols */ + + /* upper bound on number of kpatch relas and symbols */ + nr++; + /* - * Relocation section '.rela.toc' at offset 0xcc6b0 contains 46 entries: - * ... - * 0000000000000138 0000002a00000026 R_PPC64_ADDR64 0000000000000000 .text.deferred_put_nlk_sk + 8 - * - * Relocation section '.rela.text.netlink_release' at offset 0xcadf0 contains 44 entries: - * ... - * 0000000000000398 0000007300000032 R_PPC64_TOC16_HA 0000000000000000 .toc + 138 - * 00000000000003a0 0000007300000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + 138 + * We set 'need_dynrela' here in the first pass because + * the .toc section's 'need_dynrela' values are + * dependent on all the other sections. Otherwise, if + * we did this analysis in the second pass, we'd have + * to convert .toc dynrelas at the very end. * - * On PowerPC, may_need_dynrela() should be using rela's reference in .rela.toc for - * the rela like in the example, where the sym name is .toc + offset. In such case, - * the checks are performed on both rela and its reference in .rela.toc. Where the - * rela is checked for rela->type and its corresponding rela in .rela.toc for function - * pointer/switch label. If rela->need_dynrela needs to be set, it's referenced rela - * in (.rela.toc)->need_dynrela is set, as they represent the function sym. + * Specifically, this is needed for the powerpc + * internal symbol function pointer check which is done + * via .toc indirection in need_dynrela(). */ - if (may_need_dynrela(rela)) + if (need_dynrela(table, rela)) toc_rela(rela)->need_dynrela = 1; } } @@ -2665,144 +3167,51 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf, if (!is_rela_section(sec)) continue; if (!strcmp(sec->name, ".rela.kpatch.funcs") || - !strcmp(sec->name, ".rela.kpatch.dynrelas")) + !strcmp(sec->name, ".rela.kpatch.relocations") || + !strcmp(sec->name, ".rela.kpatch.symbols")) continue; + + special = false; + for (s = special_sections; s->name; s++) + if (!strcmp(sec->base->name, s->name)) + special = true; + list_for_each_entry_safe(rela, safe, &sec->relas, list) { if (!rela->need_dynrela) continue; /* - * Allow references to core module symbols to remain as - * normal relas, since the core module may not be - * compiled into the kernel, and they should be - * exported anyway. - */ - if (kpatch_is_core_module_symbol(rela->sym->name)) - continue; - - external = 0; - /* - * sym_objname is the name of the object to which - * rela->sym belongs. We'll need this to build - * ".klp.sym." symbol names later on. - * - * By default sym_objname is the name of the - * component being patched (vmlinux or module). - * If it's an external symbol, sym_objname - * will get reassigned appropriately. - */ - sym_objname = objname; - - /* - * On ppc64le, the function prologue generated by GCC 6 - * has the sequence: + * Starting with Linux 5.8, .klp.arch sections are no + * longer supported: now that vmlinux relocations are + * written early, before paravirt and alternative + * module init, .klp.arch is technically not needed. * - * .globl my_func - * .type my_func, @function - * .quad .TOC.-my_func - * my_func: - * .reloc ., R_PPC64_ENTRY ; optional - * ld r2,-8(r12) - * add r2,r2,r12 - * .localentry my_func, .-my_func - * - * The R_PPC64_ENTRY is optional and its symbol might - * have an empty name. Leave it as a normal rela. + * For sanity we just need to make sure that there are + * no .klp.rela.{module}.{section} sections for special + * sections. Otherwise there might be ordering issues, + * if the .klp.relas are applied after the module + * special section init code (e.g., apply_paravirt) + * runs due to late module patching. */ - if (rela->type == R_PPC64_ENTRY) - continue; - - if (rela->sym->bind == STB_LOCAL) { - /* An unchanged local symbol */ - ret = lookup_local_symbol(table, - rela->sym->name, &result); - if (ret) - ERROR("lookup_local_symbol %s needed for %s", - rela->sym->name, sec->base->name); - - } - else if (vmlinux) { - /* - * We have a patch to vmlinux which references - * a global symbol. Use a normal rela for - * exported symbols and a dynrela otherwise. - */ -#ifdef __powerpc64__ - /* - * An exported symbol might be local to an - * object file and any access to the function - * might be through localentry (toc+offset) - * instead of global offset. - * - * fs/proc/proc_sysctl::sysctl_head_grab: - * 166: 0000000000000000 256 FUNC GLOBAL DEFAULT [: 8] 42 unregister_sysctl_table - * 167: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND .TOC. - * - * These type of symbols have a type of - * STT_FUNC. Treat them like local symbols. - * They will be handled by the livepatch - * relocation code. - */ - if (lookup_is_exported_symbol(table, rela->sym->name)) { - if (rela->sym->type != STT_FUNC) - continue; - } -#else - if (lookup_is_exported_symbol(table, rela->sym->name)) - continue; -#endif - /* - * If lookup_global_symbol() fails, assume the - * symbol is defined in another object in the - * patch module. - */ - if (lookup_global_symbol(table, rela->sym->name, - &result)) - continue; - } else { - /* - * We have a patch to a module which references - * a global symbol. Try to find the symbol in - * the module being patched. - */ - if (lookup_global_symbol(table, rela->sym->name, - &result)) { - /* - * Not there, see if the symbol is - * exported, and set sym_objname to the - * object the exported symbol belongs - * to. If it's not exported, assume sym - * is provided by another .o in the - * patch module. - */ - sym_objname = lookup_exported_symbol_objname(table, rela->sym->name); - if (!sym_objname) - sym_objname = pmod_name; + if (!KLP_ARCH && !vmlinux && special) + ERROR("unsupported dynrela reference to symbol '%s' in module-specific special section '%s'", + rela->sym->name, sec->base->name); - /* - * For a symbol exported by vmlinux, use - * the original rela. - * - * For a symbol exported by a module, - * convert to a dynrela because the - * module might not be loaded yet. - */ - if (!strcmp(sym_objname, "vmlinux")) - continue; + if (!lookup_symbol(table, rela->sym->name, &symbol)) + ERROR("can't find symbol '%s' in symbol table", + rela->sym->name); - external = 1; - } - } - log_debug("lookup for %s @ 0x%016lx len %lu\n", - rela->sym->name, result.value, result.size); + log_debug("lookup for %s: obj=%s sympos=%lu", + rela->sym->name, symbol.objname, + symbol.sympos); /* Fill in ksyms[index] */ if (vmlinux) - ksyms[index].src = result.value; + ksyms[index].src = symbol.addr; else /* for modules, src is discovered at runtime */ ksyms[index].src = 0; - ksyms[index].pos = result.pos; + ksyms[index].sympos = symbol.sympos; ksyms[index].type = rela->sym->type; ksyms[index].bind = rela->sym->bind; @@ -2811,16 +3220,16 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf, rela2->sym = strsym; rela2->type = ABSOLUTE_RELA_TYPE; rela2->addend = offset_of_string(&kelf->strings, rela->sym->name); - rela2->offset = index * sizeof(*ksyms) + \ - offsetof(struct kpatch_symbol, name); + rela2->offset = (unsigned int)(index * sizeof(*ksyms) + \ + offsetof(struct kpatch_symbol, name)); /* add rela to fill in ksyms[index].objname field */ ALLOC_LINK(rela2, &ksym_sec->rela->relas); rela2->sym = strsym; rela2->type = ABSOLUTE_RELA_TYPE; - rela2->addend = offset_of_string(&kelf->strings, sym_objname); - rela2->offset = index * sizeof(*ksyms) + \ - offsetof(struct kpatch_symbol, objname); + rela2->addend = offset_of_string(&kelf->strings, symbol.objname); + rela2->offset = (unsigned int)(index * sizeof(*ksyms) + \ + offsetof(struct kpatch_symbol, objname)); /* Fill in krelas[index] */ if (is_gcc6_localentry_bundled_sym(rela->sym) && @@ -2828,7 +3237,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf, rela->addend -= rela->sym->sym.st_value; krelas[index].addend = rela->addend; krelas[index].type = rela->type; - krelas[index].external = external; + krelas[index].external = !vmlinux && symbol.exported; /* add rela to fill in krelas[index].dest field */ ALLOC_LINK(rela2, &krela_sec->rela->relas); @@ -2840,24 +3249,24 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf, rela2->type = ABSOLUTE_RELA_TYPE; rela2->addend = rela->offset; - rela2->offset = index * sizeof(*krelas) + \ - offsetof(struct kpatch_relocation, dest); + rela2->offset = (unsigned int)(index * sizeof(*krelas) + \ + offsetof(struct kpatch_relocation, dest)); /* add rela to fill in krelas[index].objname field */ ALLOC_LINK(rela2, &krela_sec->rela->relas); rela2->sym = strsym; rela2->type = ABSOLUTE_RELA_TYPE; rela2->addend = offset_of_string(&kelf->strings, objname); - rela2->offset = index * sizeof(*krelas) + \ - offsetof(struct kpatch_relocation, objname); + rela2->offset = (unsigned int)(index * sizeof(*krelas) + \ + offsetof(struct kpatch_relocation, objname)); /* add rela to fill in krelas[index].ksym field */ ALLOC_LINK(rela2, &krela_sec->rela->relas); rela2->sym = ksym_sec_sym; rela2->type = ABSOLUTE_RELA_TYPE; - rela2->addend = index * sizeof(*ksyms); - rela2->offset = index * sizeof(*krelas) + \ - offsetof(struct kpatch_relocation, ksym); + rela2->addend = (unsigned int)(index * sizeof(*ksyms)); + rela2->offset = (unsigned int)(index * sizeof(*krelas) + \ + offsetof(struct kpatch_relocation, ksym)); /* * Mark the referred to symbol for removal but @@ -2926,9 +3335,6 @@ static void kpatch_create_callbacks_objname_rela(struct kpatch_elf *kelf, char * } } -#ifdef __powerpc64__ -void kpatch_create_mcount_sections(struct kpatch_elf *kelf) { } -#else /* * This function basically reimplements the functionality of the Linux * recordmcount script, so that patched functions can be recognized by ftrace. @@ -2941,9 +3347,9 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) int nr, index; struct section *sec, *relasec; struct symbol *sym; - struct rela *rela; - void **funcs, *newdata; - unsigned char *insn; + struct rela *rela, *mcount_rela; + void **funcs; + unsigned long insn_offset; nr = 0; list_for_each_entry(sym, &kelf->symbols, list) @@ -2952,9 +3358,8 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) nr++; /* create text/rela section pair */ - sec = create_section_pair(kelf, "__mcount_loc", sizeof(*funcs), nr); + sec = create_section_pair(kelf, "__mcount_loc", sizeof(void*), nr); relasec = sec->rela; - funcs = sec->data->d_buf; /* populate sections */ index = 0; @@ -2968,33 +3373,75 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) continue; } - /* add rela in .rela__mcount_loc to fill in function pointer */ - ALLOC_LINK(rela, &relasec->relas); - rela->sym = sym; - rela->type = R_X86_64_64; - rela->addend = 0; - rela->offset = index * sizeof(*funcs); +#ifdef __x86_64__ + + rela = list_first_entry(&sym->sec->rela->relas, struct rela, list); /* - * Modify the first instruction of the function to "callq - * __fentry__" so that ftrace will be happy. + * For "call fentry", the relocation points to 1 byte past the + * beginning of the instruction. */ - newdata = malloc(sym->sec->data->d_size); - memcpy(newdata, sym->sec->data->d_buf, sym->sec->data->d_size); - sym->sec->data->d_buf = newdata; - insn = newdata; - if (insn[0] != 0xf) - ERROR("%s: unexpected instruction at the start of the function", - sym->name); - insn[0] = 0xe8; - insn[1] = 0; - insn[2] = 0; - insn[3] = 0; - insn[4] = 0; - - rela = list_first_entry(&sym->sec->rela->relas, struct rela, - list); - rela->type = R_X86_64_PC32; + insn_offset = rela->offset - 1; + + if (rela->type == R_X86_64_NONE) { + void *newdata; + unsigned char *insn; + + /* + * R_X86_64_NONE is only generated by older versions of + * kernel/gcc which use the mcount script. There's a + * NOP instead of a call to fentry. + */ + + /* Make a writable copy of the text section data */ + newdata = malloc(sym->sec->data->d_size); + memcpy(newdata, sym->sec->data->d_buf, sym->sec->data->d_size); + sym->sec->data->d_buf = newdata; + insn = newdata; + + /* + * Replace the NOP with a call to fentry. The fentry + * rela symbol is already there, just need to change + * the relocation type accordingly. + */ + insn = sym->sec->data->d_buf; + if (insn[0] != 0xf) + ERROR("%s: unexpected instruction at the start of the function", sym->name); + insn[0] = 0xe8; + insn[1] = 0; + insn[2] = 0; + insn[3] = 0; + insn[4] = 0; + + rela->type = R_X86_64_PC32; + } + +#else /* __powerpc64__ */ +{ + bool found = false; + + list_for_each_entry(rela, &sym->sec->rela->relas, list) + if (!strcmp(rela->sym->name, "_mcount")) { + found = true; + break; + } + + if (!found) + ERROR("%s: unexpected missing call to _mcount()", __func__); + + insn_offset = rela->offset; +} +#endif + /* + * 'rela' points to the mcount/fentry call. + * + * Create a .rela__mcount_loc entry which also points to it. + */ + ALLOC_LINK(mcount_rela, &relasec->relas); + mcount_rela->sym = sym; + mcount_rela->type = ABSOLUTE_RELA_TYPE; + mcount_rela->addend = insn_offset - sym->sym.st_value; + mcount_rela->offset = (unsigned int) (index * sizeof(*funcs)); index++; } @@ -3003,7 +3450,6 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf) if (index != nr) ERROR("size mismatch in funcs sections"); } -#endif /* * This function strips out symbols that were referenced by changed rela @@ -3060,7 +3506,7 @@ static void kpatch_build_strings_section_data(struct kpatch_elf *kelf) { struct string *string; struct section *sec; - int size; + size_t size; char *strtab; sec = find_section_by_name(&kelf->sections, ".kpatch.strings"); @@ -3086,15 +3532,70 @@ static void kpatch_build_strings_section_data(struct kpatch_elf *kelf) } } +/* + * Don't allow sibling calls from patched functions on ppc64le. Before doing a + * sibling call, the patched function restores the stack to its caller's stack. + * The kernel-generated stub then writes the patch module's r2 (toc) value to + * the caller's stack, corrupting it, eventually causing a panic after it + * returns to the caller and the caller tries to use the livepatch module's toc + * value. + * + * In theory we could instead a) generate a custom stub, or b) modify the + * kernel livepatch_handler code to save/restore the stack r2 value, but this + * is easier for now. + */ +static void kpatch_no_sibling_calls_ppc64le(struct kpatch_elf *kelf) +{ +#ifdef __powerpc64__ + struct symbol *sym; + unsigned int insn; + unsigned int offset; + + list_for_each_entry(sym, &kelf->symbols, list) { + if (sym->type != STT_FUNC || sym->status != CHANGED) + continue; + + for (offset = 0; offset < sym->sec->data->d_size; offset += 4) { + + insn = *(unsigned int *)(sym->sec->data->d_buf + offset); + + /* + * The instruction 0x48000000 can be assumed to be a + * sibling call: + * + * Bits 0-5 (opcode) == 0x9: unconditional branch + * Bit 30 (absolute) == 0: relative address + * Bit 31 (link) == 0: doesn't set LR (not a call) + * + * Bits 6-29 (branch address) == zero, which means + * it's either a branch to self (infinite loop), or + * there's a REL24 relocation for the address which + * will be written by the linker or the kernel. + */ + if (insn != 0x48000000) + continue; + + /* Make sure it's not a branch-to-self: */ + if (!find_rela_by_offset(sym->sec->rela, offset)) + continue; + + ERROR("Found an unsupported sibling call at %s()+0x%lx. Add __attribute__((optimize(\"-fno-optimize-sibling-calls\"))) to %s() definition.", + sym->name, sym->sym.st_value + offset, sym->name); + } + } +#endif +} + struct arguments { char *args[7]; - int debug; + bool debug, klp_arch; }; static char args_doc[] = "original.o patched.o parent-name parent-symtab Module.symvers patch-module-name output.o"; static struct argp_option options[] = { - {"debug", 'd', NULL, 0, "Show debug output" }, + {"debug", 'd', NULL, 0, "Show debug output" }, + {"klp-arch", 'a', NULL, 0, "Kernel supports .klp.arch section" }, { NULL } }; @@ -3109,6 +3610,9 @@ static error_t parse_opt (int key, char *arg, struct argp_state *state) case 'd': arguments->debug = 1; break; + case 'a': + arguments->klp_arch = 1; + break; case ARGP_KEY_ARG: if (state->arg_num >= 7) /* Too many arguments. */ @@ -3138,12 +3642,14 @@ int main(int argc, char *argv[]) struct symbol *sym; char *hint = NULL, *orig_obj, *patched_obj, *parent_name; char *parent_symtab, *mod_symvers, *patch_name, *output_obj; - struct sym_compare_type *base_locals; + struct sym_compare_type *base_locals, *sym_comp; - arguments.debug = 0; + memset(&arguments, 0, sizeof(arguments)); argp_parse (&argp, argc, argv, 0, NULL, &arguments); if (arguments.debug) loglevel = DEBUG; + if (arguments.klp_arch) + KLP_ARCH = true; elf_version(EV_CURRENT); @@ -3160,16 +3666,19 @@ int main(int argc, char *argv[]) kelf_base = kpatch_elf_open(orig_obj); kelf_patched = kpatch_elf_open(patched_obj); - kpatch_bundle_symbols(kelf_base); - kpatch_bundle_symbols(kelf_patched); - kpatch_compare_elf_headers(kelf_base->elf, kelf_patched->elf); kpatch_check_program_headers(kelf_base->elf); kpatch_check_program_headers(kelf_patched->elf); + kpatch_bundle_symbols(kelf_base); + kpatch_bundle_symbols(kelf_patched); + + kpatch_detect_child_functions(kelf_base); + kpatch_detect_child_functions(kelf_patched); + list_for_each_entry(sym, &kelf_base->symbols, list) { if (sym->type == STT_FILE) { - hint = sym->name; + hint = strdup(sym->name); break; } } @@ -3178,15 +3687,14 @@ int main(int argc, char *argv[]) return EXIT_STATUS_NO_CHANGE; } - /* create symbol lookup table */ base_locals = kpatch_elf_locals(kelf_base); - lookup = lookup_open(parent_symtab, mod_symvers, hint, base_locals); - free(base_locals); + + lookup = lookup_open(parent_symtab, parent_name, mod_symvers, hint, + base_locals); kpatch_mark_grouped_sections(kelf_patched); kpatch_replace_sections_syms(kelf_base); kpatch_replace_sections_syms(kelf_patched); - kpatch_rename_mangled_functions(kelf_base, kelf_patched); kpatch_correlate_elfs(kelf_base, kelf_patched); kpatch_correlate_static_local_variables(kelf_base, kelf_patched); @@ -3198,24 +3706,24 @@ int main(int argc, char *argv[]) */ kpatch_mark_ignored_sections(kelf_patched); kpatch_compare_correlated_elements(kelf_patched); + kpatch_mark_ignored_functions_same(kelf_patched); + kpatch_mark_ignored_sections_same(kelf_patched); kpatch_check_func_profiling_calls(kelf_patched); kpatch_elf_teardown(kelf_base); kpatch_elf_free(kelf_base); - kpatch_mark_ignored_functions_same(kelf_patched); - kpatch_mark_ignored_sections_same(kelf_patched); - kpatch_include_standard_elements(kelf_patched); num_changed = kpatch_include_changed_functions(kelf_patched); - kpatch_include_debug_sections(kelf_patched); callbacks_exist = kpatch_include_callback_elements(kelf_patched); kpatch_include_force_elements(kelf_patched); new_globals_exist = kpatch_include_new_globals(kelf_patched); + kpatch_include_debug_sections(kelf_patched); + + kpatch_process_special_sections(kelf_patched, lookup); kpatch_print_changes(kelf_patched); kpatch_dump_kelf(kelf_patched); - kpatch_process_special_sections(kelf_patched); kpatch_verify_patchability(kelf_patched); if (!num_changed && !new_globals_exist) { @@ -3223,6 +3731,7 @@ int main(int argc, char *argv[]) log_debug("no changed functions were found, but callbacks exist\n"); else { log_debug("no changed functions were found\n"); + free(hint); return EXIT_STATUS_NO_CHANGE; } } @@ -3238,6 +3747,13 @@ int main(int argc, char *argv[]) */ kpatch_elf_teardown(kelf_patched); + for (sym_comp = base_locals; sym_comp && sym_comp->name; sym_comp++) + free(sym_comp->name); + free(base_locals); + free(hint); + + kpatch_no_sibling_calls_ppc64le(kelf_out); + /* create strings, patches, and dynrelas sections */ kpatch_create_strings_elements(kelf_out); kpatch_create_patches_sections(kelf_out, lookup, parent_name); @@ -3264,6 +3780,9 @@ int main(int argc, char *argv[]) * buffers from the relas lists. */ symtab = find_section_by_name(&kelf_out->sections, ".symtab"); + if (!symtab) + ERROR("missing .symtab section"); + list_for_each_entry(sec, &kelf_out->sections, list) { if (!is_rela_section(sec)) continue; @@ -3279,6 +3798,7 @@ int main(int argc, char *argv[]) kpatch_dump_kelf(kelf_out); kpatch_write_output_elf(kelf_out, kelf_patched->elf, output_obj); + lookup_close(lookup); kpatch_elf_free(kelf_patched); kpatch_elf_teardown(kelf_out); kpatch_elf_free(kelf_out); diff --git a/kpatch-build/create-klp-module.c b/kpatch-build/create-klp-module.c index 253704b..e0b62e6 100644 --- a/kpatch-build/create-klp-module.c +++ b/kpatch-build/create-klp-module.c @@ -45,33 +45,29 @@ static struct symbol *find_or_add_ksym_to_symbols(struct kpatch_elf *kelf, struct rela *rela; char *objname, *name; char pos[32], buf[256]; - int index; + unsigned int index; ksyms = ksymsec->data->d_buf; - index = offset / sizeof(*ksyms); + index = (unsigned int)(offset / sizeof(*ksyms)); ksym = &ksyms[index]; /* Get name of ksym */ rela = find_rela_by_offset(ksymsec->rela, - offset + offsetof(struct kpatch_symbol, name)); + (unsigned int)(offset + offsetof(struct kpatch_symbol, name))); if (!rela) ERROR("name of ksym not found?"); - name = strdup(strings + rela->addend); - if (!name) - ERROR("strdup"); + name = strings + rela->addend; /* Get objname of ksym */ rela = find_rela_by_offset(ksymsec->rela, - offset + offsetof(struct kpatch_symbol, objname)); + (unsigned int)(offset + offsetof(struct kpatch_symbol, objname))); if (!rela) ERROR("objname of ksym not found?"); - objname = strdup(strings + rela->addend); - if (!objname) - ERROR("strdup"); + objname = strings + rela->addend; - snprintf(pos, 32, "%lu", ksym->pos); + snprintf(pos, 32, "%lu", ksym->sympos); /* .klp.sym.objname.name,pos */ snprintf(buf, 256, KLP_SYM_PREFIX "%s.%s,%s", objname, name, pos); @@ -92,7 +88,7 @@ static struct symbol *find_or_add_ksym_to_symbols(struct kpatch_elf *kelf, * and sym->index is set in kpatch_reindex_elements() */ sym->sym.st_shndx = SHN_LIVEPATCH; - sym->sym.st_info = GELF_ST_INFO(sym->bind, sym->type); + sym->sym.st_info = (unsigned char)GELF_ST_INFO(sym->bind, sym->type); /* * Figure out where to put the new symbol: * a) locals need to be grouped together, before globals @@ -179,13 +175,13 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section struct symbol *sym, *dest; struct rela *rela; char *objname; - int nr, index, offset, dest_off; + unsigned int nr, index, offset, dest_off; krelas = krelasec->data->d_buf; - nr = krelasec->data->d_size / sizeof(*krelas); + nr = (unsigned int)(krelasec->data->d_size / sizeof(*krelas)); for (index = 0; index < nr; index++) { - offset = index * sizeof(*krelas); + offset = (unsigned int)(index * sizeof(*krelas)); /* Get the rela dest sym + offset */ rela = find_rela_by_offset(krelasec->rela, @@ -194,26 +190,25 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section ERROR("find_rela_by_offset"); dest = rela->sym; - dest_off = rela->addend; + dest_off = (unsigned int)rela->addend; /* Get the name of the object the dest belongs to */ rela = find_rela_by_offset(krelasec->rela, - offset + offsetof(struct kpatch_relocation, objname)); + (unsigned int)(offset + offsetof(struct kpatch_relocation, objname))); if (!rela) ERROR("find_rela_by_offset"); - objname = strdup(strings + rela->addend); - if (!objname) - ERROR("strdup"); + objname = strings + rela->addend; /* Get the .kpatch.symbol entry for the rela src */ rela = find_rela_by_offset(krelasec->rela, - offset + offsetof(struct kpatch_relocation, ksym)); + (unsigned int)(offset + offsetof(struct kpatch_relocation, ksym))); if (!rela) ERROR("find_rela_by_offset"); /* Create (or find) a klp symbol from the rela src entry */ - sym = find_or_add_ksym_to_symbols(kelf, ksymsec, strings, rela->addend); + sym = find_or_add_ksym_to_symbols(kelf, ksymsec, strings, + (unsigned int)rela->addend); if (!sym) ERROR("error finding or adding ksym to symtab"); @@ -224,7 +219,7 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section /* Add the klp rela to the .klp.rela. section */ ALLOC_LINK(rela, &klp_relasec->relas); - rela->offset = dest->sym.st_value + dest_off; + rela->offset = (unsigned int)(dest->sym.st_value + dest_off); rela->type = krelas[index].type; rela->sym = sym; rela->addend = krelas[index].addend; @@ -250,21 +245,20 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section static void create_klp_arch_sections(struct kpatch_elf *kelf, char *strings) { struct section *karch, *sec, *base = NULL; - struct kpatch_arch *entries; struct rela *rela, *rela2; char *secname, *objname = NULL; char buf[256]; - int nr, index, offset, old_size, new_size; + unsigned int nr, index, offset; + size_t new_size, old_size; karch = find_section_by_name(&kelf->sections, ".kpatch.arch"); if (!karch) return; - entries = karch->data->d_buf; - nr = karch->data->d_size / sizeof(*entries); + nr = (unsigned int)(karch->data->d_size / sizeof(struct kpatch_arch)); for (index = 0; index < nr; index++) { - offset = index * sizeof(*entries); + offset = (unsigned int)(index * sizeof(struct kpatch_arch)); /* Get the base section (.parainstructions or .altinstructions) */ rela = find_rela_by_offset(karch->rela, @@ -278,13 +272,11 @@ static void create_klp_arch_sections(struct kpatch_elf *kelf, char *strings) /* Get the name of the object the base section belongs to */ rela = find_rela_by_offset(karch->rela, - offset + offsetof(struct kpatch_arch, objname)); + (unsigned int)(offset + offsetof(struct kpatch_arch, objname))); if (!rela) ERROR("find_rela_by_offset"); - objname = strdup(strings + rela->addend); - if (!objname) - ERROR("strdup"); + objname = strings + rela->addend; /* Example: .klp.arch.vmlinux..parainstructions */ snprintf(buf, 256, "%s%s.%s", KLP_ARCH_PREFIX, objname, base->name); @@ -344,7 +336,7 @@ static void create_klp_arch_sections(struct kpatch_elf *kelf, char *strings) rela2->sym = rela->sym; rela2->type = rela->type; rela2->addend = rela->addend; - rela2->offset = old_size + rela->offset; + rela2->offset = (unsigned int)(old_size + rela->offset); } } } @@ -439,7 +431,7 @@ int main(int argc, char *argv[]) struct section *ksymsec, *krelasec, *strsec; struct arguments arguments; char *strings; - int ksyms_nr, krelas_nr; + unsigned int ksyms_nr, krelas_nr; memset(&arguments, 0, sizeof(arguments)); argp_parse (&argp, argc, argv, 0, 0, &arguments); @@ -466,12 +458,12 @@ int main(int argc, char *argv[]) ksymsec = find_section_by_name(&kelf->sections, ".kpatch.symbols"); if (!ksymsec) ERROR("missing .kpatch.symbols section"); - ksyms_nr = ksymsec->data->d_size / sizeof(struct kpatch_symbol); + ksyms_nr = (unsigned int)(ksymsec->data->d_size / sizeof(struct kpatch_symbol)); krelasec = find_section_by_name(&kelf->sections, ".kpatch.relocations"); if (!krelasec) ERROR("missing .kpatch.relocations section"); - krelas_nr = krelasec->data->d_size / sizeof(struct kpatch_relocation); + krelas_nr = (unsigned int)(krelasec->data->d_size / sizeof(struct kpatch_relocation)); if (krelas_nr != ksyms_nr) ERROR("number of krelas and ksyms do not match"); @@ -496,6 +488,9 @@ int main(int argc, char *argv[]) /* Rebuild rela sections, new klp rela sections will be rebuilt too. */ symtab = find_section_by_name(&kelf->sections, ".symtab"); + if (!symtab) + ERROR("missing .symtab section"); + list_for_each_entry(sec, &kelf->sections, list) { if (!is_rela_section(sec)) continue; diff --git a/kpatch-build/create-kpatch-module.c b/kpatch-build/create-kpatch-module.c index 67b16b0..ac1bc88 100644 --- a/kpatch-build/create-kpatch-module.c +++ b/kpatch-build/create-kpatch-module.c @@ -46,17 +46,20 @@ static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section struct section *dynsec; struct symbol *sym; struct rela *rela; - int index, nr, offset, dest_offset, objname_offset, name_offset; + unsigned int index, nr, offset, dest_offset, objname_offset, name_offset; + unsigned int type; + long addend; + char *target_name; ksyms = ksymsec->data->d_buf; krelas = krelasec->data->d_buf; - nr = krelasec->data->d_size / sizeof(*krelas); + nr = (unsigned int)(krelasec->data->d_size / sizeof(*krelas)); dynsec = create_section_pair(kelf, ".kpatch.dynrelas", sizeof(*dynrelas), nr); dynrelas = dynsec->data->d_buf; for (index = 0; index < nr; index++) { - offset = index * sizeof(*krelas); + offset = index * (unsigned int)sizeof(*krelas); /* * To fill in each dynrela entry, find dest location, @@ -69,58 +72,66 @@ static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section if (!rela) ERROR("find_rela_by_offset"); sym = rela->sym; - dest_offset = rela->addend; + dest_offset = (unsigned int)rela->addend; /* Get objname offset */ rela = find_rela_by_offset(krelasec->rela, - offset + offsetof(struct kpatch_relocation, objname)); + (unsigned int)(offset + offsetof(struct kpatch_relocation, objname))); if (!rela) ERROR("find_rela_by_offset"); - objname_offset = rela->addend; + objname_offset = (unsigned int)rela->addend; /* Get ksym (.kpatch.symbols entry) and symbol name offset */ rela = find_rela_by_offset(krelasec->rela, - offset + offsetof(struct kpatch_relocation, ksym)); + (unsigned int)(offset + offsetof(struct kpatch_relocation, ksym))); if (!rela) ERROR("find_rela_by_offset"); ksym = ksyms + (rela->addend / sizeof(*ksyms)); - offset = index * sizeof(*ksyms); + offset = (unsigned int )(index * sizeof(*ksyms)); rela = find_rela_by_offset(ksymsec->rela, - offset + offsetof(struct kpatch_symbol, name)); + (unsigned int)(offset + offsetof(struct kpatch_symbol, name))); if (!rela) ERROR("find_rela_by_offset"); - name_offset = rela->addend; + name_offset = (unsigned int)rela->addend; /* Fill in dynrela entry */ + type = krelas[index].type; + addend = krelas[index].addend; + if (type == R_X86_64_64 && (addend > INT_MAX || addend <= INT_MIN)) { + target_name = (char *)strsec->data->d_buf + name_offset; + ERROR("got R_X86_64_64 dynrela for '%s' with addend too large or too small for an int: %lx", + target_name, addend); + } + dynrelas[index].src = ksym->src; - dynrelas[index].addend = krelas[index].addend; - dynrelas[index].type = krelas[index].type; + dynrelas[index].addend = addend; + dynrelas[index].type = type; dynrelas[index].external = krelas[index].external; - dynrelas[index].sympos = ksym->pos; + dynrelas[index].sympos = ksym->sympos; /* dest */ ALLOC_LINK(rela, &dynsec->rela->relas); rela->sym = sym; rela->type = R_X86_64_64; rela->addend = dest_offset; - rela->offset = index * sizeof(*dynrelas); + rela->offset = (unsigned int)(index * sizeof(*dynrelas)); /* name */ ALLOC_LINK(rela, &dynsec->rela->relas); rela->sym = strsec->secsym; rela->type = R_X86_64_64; rela->addend = name_offset; - rela->offset = index * sizeof(*dynrelas) + \ - offsetof(struct kpatch_patch_dynrela, name); + rela->offset = (unsigned int)(index * sizeof(*dynrelas) + \ + offsetof(struct kpatch_patch_dynrela, name)); /* objname */ ALLOC_LINK(rela, &dynsec->rela->relas); rela->sym = strsec->secsym; rela->type = R_X86_64_64; rela->addend = objname_offset; - rela->offset = index * sizeof(*dynrelas) + \ - offsetof(struct kpatch_patch_dynrela, objname); + rela->offset = (unsigned int)(index * sizeof(*dynrelas) + \ + offsetof(struct kpatch_patch_dynrela, objname)); } } @@ -188,7 +199,7 @@ int main(int argc, char *argv[]) struct section *symtab, *sec; struct section *ksymsec, *krelasec, *strsec; struct arguments arguments; - int ksyms_nr, krelas_nr; + unsigned int ksyms_nr, krelas_nr; arguments.debug = 0; argp_parse (&argp, argc, argv, 0, 0, &arguments); @@ -214,12 +225,12 @@ int main(int argc, char *argv[]) ksymsec = find_section_by_name(&kelf->sections, ".kpatch.symbols"); if (!ksymsec) ERROR("missing .kpatch.symbols section"); - ksyms_nr = ksymsec->data->d_size / sizeof(struct kpatch_symbol); + ksyms_nr = (unsigned int)(ksymsec->data->d_size / sizeof(struct kpatch_symbol)); krelasec = find_section_by_name(&kelf->sections, ".kpatch.relocations"); if (!krelasec) ERROR("missing .kpatch.relocations section"); - krelas_nr = krelasec->data->d_size / sizeof(struct kpatch_relocation); + krelas_nr = (unsigned int)(krelasec->data->d_size / sizeof(struct kpatch_relocation)); if (krelas_nr != ksyms_nr) ERROR("number of krelas and ksyms do not match"); @@ -231,6 +242,9 @@ int main(int argc, char *argv[]) kpatch_reindex_elements(kelf); symtab = find_section_by_name(&kelf->sections, ".symtab"); + if (!symtab) + ERROR("missing .symtab section"); + list_for_each_entry(sec, &kelf->sections, list) { if (!is_rela_section(sec)) continue; diff --git a/kpatch-build/gcc-plugins/gcc-common.h b/kpatch-build/gcc-plugins/gcc-common.h index 501df31..443faf6 100644 --- a/kpatch-build/gcc-plugins/gcc-common.h +++ b/kpatch-build/gcc-plugins/gcc-common.h @@ -34,7 +34,9 @@ #include "ggc.h" #include "timevar.h" +#if BUILDING_GCC_VERSION < 10000 #include "params.h" +#endif #if BUILDING_GCC_VERSION <= 4009 #include "pointer-set.h" @@ -836,6 +838,7 @@ static inline gimple gimple_build_assign_with_ops(enum tree_code subcode, tree l return gimple_build_assign(lhs, subcode, op1, op2 PASS_MEM_STAT); } +#if BUILDING_GCC_VERSION < 10000 template <> template <> inline bool is_a_helper::test(const_gimple gs) @@ -849,6 +852,7 @@ inline bool is_a_helper::test(const_gimple gs) { return gs->code == GIMPLE_RETURN; } +#endif static inline gasm *as_a_gasm(gimple stmt) { diff --git a/kpatch-build/gcc-plugins/ppc64le-plugin.c b/kpatch-build/gcc-plugins/ppc64le-plugin.c index 181438c..e3ec20f 100644 --- a/kpatch-build/gcc-plugins/ppc64le-plugin.c +++ b/kpatch-build/gcc-plugins/ppc64le-plugin.c @@ -3,6 +3,18 @@ #define PLUGIN_NAME "ppc64le-plugin" +#if BUILDING_GCC_VERSION < 10000 +#define CALL_LOCAL "*call_local_aixdi" +#define CALL_NONLOCAL "*call_nonlocal_aixdi" +#define CALL_VALUE_LOCAL "*call_value_local_aixdi" +#define CALL_VALUE_NONLOCAL "*call_value_nonlocal_aixdi" +#else +#define CALL_LOCAL "*call_localdi" +#define CALL_NONLOCAL "*call_nonlocal_aixdi" +#define CALL_VALUE_LOCAL "*call_value_localdi" +#define CALL_VALUE_NONLOCAL "*call_value_nonlocal_aixdi" +#endif + int plugin_is_GPL_compatible; struct plugin_info plugin_info = { @@ -29,13 +41,13 @@ static unsigned int ppc64le_plugin_execute(void) if (!name) continue; - if (!strcmp(name , "*call_local_aixdi")) + if (!strcmp(name , CALL_LOCAL)) local_code = code; - else if (!strcmp(name , "*call_nonlocal_aixdi")) + else if (!strcmp(name , CALL_NONLOCAL)) nonlocal_code = code; - else if (!strcmp(name, "*call_value_local_aixdi")) + else if (!strcmp(name, CALL_VALUE_LOCAL)) value_local_code = code; - else if (!strcmp(name, "*call_value_nonlocal_aixdi")) + else if (!strcmp(name, CALL_VALUE_NONLOCAL)) value_nonlocal_code = code; if (nonlocal_code != -1 && local_code != -1 && @@ -46,8 +58,7 @@ static unsigned int ppc64le_plugin_execute(void) found: if (nonlocal_code == -1 || local_code == -1 || value_nonlocal_code == -1 || value_local_code == -1) { - fprintf(stderr, PLUGIN_NAME ": can't find call instruction codes"); - return 1; + error("%s: cannot find call instruction codes", PLUGIN_NAME); } /* Convert local calls to non-local */ diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build index a76913f..0caf747 100755 --- a/kpatch-build/kpatch-build +++ b/kpatch-build/kpatch-build @@ -29,8 +29,8 @@ # - Either uses a specified kernel source directory or downloads the kernel # source package for the currently running kernel # - Unpacks and prepares the source package for building if necessary -# - Builds the base kernel (vmlinux) -# - Builds the patched kernel and monitors changed objects +# - Builds the base kernel or module +# - Builds the patched kernel/module and monitors changed objects # - Builds the patched objects with gcc flags -f[function|data]-sections # - Runs kpatch tools to create and link the patch kernel module @@ -46,12 +46,15 @@ RPMTOPDIR="$CACHEDIR/buildroot" VERSIONFILE="$CACHEDIR/version" TEMPDIR="$CACHEDIR/tmp" LOGFILE="$CACHEDIR/build.log" +RELEASE_FILE=/etc/os-release DEBUG=0 SKIPCLEANUP=0 SKIPGCCCHECK=0 ARCH_KCFLAGS="" +DEBUG_KCFLAGS="" declare -a PATCH_LIST APPLIED_PATCHES=0 +OOT_MODULE= warn() { echo "ERROR: $1" >&2 @@ -85,6 +88,33 @@ logger() { fi } +verify_patch_files() { + local path + local dir + local ret=0 + + for patch in "${PATCH_LIST[@]}"; do + for path in $(lsdiff "$patch" 2>/dev/null); do + + dir=$(dirname "$path") + ext="${path##*.}" + + if [[ "$dir" =~ ^lib$ ]] || [[ "$dir" =~ ^lib/ ]] ; then + warn "$patch: unsupported patch to lib/: $path" + ret=1 + fi + + if [[ "$ext" == "S" ]] ; then + warn "$patch: unsupported patch to assembly: $path" + ret=1 + fi + + done + done + + [[ $ret == 1 ]] && die "Unsupported changes detected" +} + apply_patches() { local patch @@ -125,7 +155,7 @@ cleanup() { } clean_cache() { - rm -rf "${CACHEDIR:?}/*" + rm -rf "${CACHEDIR:?}"/* mkdir -p "$TEMPDIR" || die "Couldn't create $TEMPDIR" } @@ -140,17 +170,16 @@ check_pipe_status() { cp core* /tmp die "core file at /tmp/$(ls core*)" fi - die "no core file found, run 'ulimit -c unlimited' and try to recreate" + die "There was a SIGSEGV, but no core dump was found in the current directory. Depending on your distro you might find it in /var/lib/systemd/coredump or /var/crash." fi } -# $1 >= $2 -version_gte() { - [ "$1" = "$(echo -e "$1\\n$2" | sort -rV | head -n1)" ] +kernel_version_gte() { + [ "${ARCHVERSION//-*/}" = "$(echo -e "${ARCHVERSION//-*}\\n$1" | sort -rV | head -n1)" ] } -is_rhel() { - [[ $1 =~ \.el[78]\. ]] +kernel_is_rhel() { + [[ "$ARCHVERSION" =~ \.el[78]\. ]] } find_dirs() { @@ -187,7 +216,7 @@ find_core_symvers() { } gcc_version_from_file() { - readelf -p .comment "$1" | grep -o 'GCC:.*' + readelf -p .comment "$1" | grep -o 'GCC:.*' | head -n 1 } gcc_version_check() { @@ -199,8 +228,11 @@ gcc_version_check() { echo 'void main(void) {}' > "$c" out="$(gcc -c -pg -ffunction-sections -o "$o" "$c" 2>&1)" gccver="$(gcc_version_from_file "$o")" - kgccver="$(gcc_version_from_file "$VMLINUX")" - rm -f "$c" "$o" + if [[ -n "$OOT_MODULE" ]]; then + kgccver="$(gcc_version_from_file "$OOT_MODULE")" + else + kgccver="$(gcc_version_from_file "$VMLINUX")" + fi if [[ -n "$out" ]]; then warn "gcc >= 4.8 required for -pg -ffunction-settings" @@ -208,6 +240,12 @@ gcc_version_check() { return 1 fi + out="$(gcc -c -gz=none -o "$o" "$c" 2>&1)" + if [[ -z "$out" ]]; then + DEBUG_KCFLAGS="-gz=none" + fi + rm -f "$c" "$o" + # ensure gcc version matches that used to build the kernel if [[ "$gccver" != "$kgccver" ]]; then warn "gcc/kernel version mismatch" @@ -222,33 +260,40 @@ gcc_version_check() { } find_special_section_data_ppc64le() { + + [[ "$CONFIG_JUMP_LABEL" -eq 0 ]] && AWK_OPTIONS="-vskip_j=1" + SPECIAL_VARS="$(readelf -wi "$VMLINUX" | gawk --non-decimal-data ' - BEGIN { f = b = e = 0 } + BEGIN { f = b = e = j = 0 } # Set state if name matches f == 0 && /DW_AT_name.* fixup_entry[[:space:]]*$/ {f = 1; next} b == 0 && /DW_AT_name.* bug_entry[[:space:]]*$/ {b = 1; next} e == 0 && /DW_AT_name.* exception_table_entry[[:space:]]*$/ {e = 1; next} + j == 0 && /DW_AT_name.* jump_entry[[:space:]]*$/ {j = 1; next} # Reset state unless this abbrev describes the struct size f == 1 && !/DW_AT_byte_size/ { f = 0; next } b == 1 && !/DW_AT_byte_size/ { b = 0; next } e == 1 && !/DW_AT_byte_size/ { e = 0; next } + j == 1 && !/DW_AT_byte_size/ { j = 0; next } # Now that we know the size, stop parsing for it - f == 1 {printf("export FIXUP_STRUCT_SIZE=%d\n", $4); a = 2} + f == 1 {printf("export FIXUP_STRUCT_SIZE=%d\n", $4); f = 2} b == 1 {printf("export BUG_STRUCT_SIZE=%d\n", $4); b = 2} e == 1 {printf("export EX_STRUCT_SIZE=%d\n", $4); e = 2} + j == 1 {printf("export JUMP_STRUCT_SIZE=%d\n", $4); j = 2} # Bail out once we have everything - f == 2 && b == 2 && e == 2 {exit}')" + f == 2 && b == 2 && e == 2 && (j == 2 || skip_j) {exit}')" [[ -n "$SPECIAL_VARS" ]] && eval "$SPECIAL_VARS" [[ -z "$FIXUP_STRUCT_SIZE" ]] && die "can't find special struct fixup_entry size" [[ -z "$BUG_STRUCT_SIZE" ]] && die "can't find special struct bug_entry size" [[ -z "$EX_STRUCT_SIZE" ]] && die "can't find special struct exception_table_entry size" + [[ -z "$JUMP_STRUCT_SIZE" && "$CONFIG_JUMP_LABEL" -ne 0 ]] && die "can't find special struct jump_entry size" return } @@ -261,12 +306,13 @@ find_special_section_data() { [[ "$CONFIG_PARAVIRT" -eq 0 ]] && AWK_OPTIONS="-vskip_p=1" [[ "$CONFIG_UNWINDER_ORC" -eq 0 ]] && AWK_OPTIONS="$AWK_OPTIONS -vskip_o=1" + [[ "$CONFIG_JUMP_LABEL" -eq 0 ]] && AWK_OPTIONS="$AWK_OPTIONS -vskip_j=1" # If $AWK_OPTIONS are blank gawk would treat "" as a blank script # shellcheck disable=SC2086 SPECIAL_VARS="$(readelf -wi "$VMLINUX" | gawk --non-decimal-data $AWK_OPTIONS ' - BEGIN { a = b = p = e = o = 0 } + BEGIN { a = b = p = e = o = j = 0 } # Set state if name matches a == 0 && /DW_AT_name.* alt_instr[[:space:]]*$/ {a = 1; next} @@ -274,6 +320,7 @@ find_special_section_data() { p == 0 && /DW_AT_name.* paravirt_patch_site[[:space:]]*$/ {p = 1; next} e == 0 && /DW_AT_name.* exception_table_entry[[:space:]]*$/ {e = 1; next} o == 0 && /DW_AT_name.* orc_entry[[:space:]]*$/ {o = 1; next} + j == 0 && /DW_AT_name.* jump_entry[[:space:]]*$/ {j = 1; next} # Reset state unless this abbrev describes the struct size a == 1 && !/DW_AT_byte_size/ { a = 0; next } @@ -281,6 +328,7 @@ find_special_section_data() { p == 1 && !/DW_AT_byte_size/ { p = 0; next } e == 1 && !/DW_AT_byte_size/ { e = 0; next } o == 1 && !/DW_AT_byte_size/ { o = 0; next } + j == 1 && !/DW_AT_byte_size/ { j = 0; next } # Now that we know the size, stop parsing for it a == 1 {printf("export ALT_STRUCT_SIZE=%d\n", $4); a = 2} @@ -288,9 +336,10 @@ find_special_section_data() { p == 1 {printf("export PARA_STRUCT_SIZE=%d\n", $4); p = 2} e == 1 {printf("export EX_STRUCT_SIZE=%d\n", $4); e = 2} o == 1 {printf("export ORC_STRUCT_SIZE=%d\n", $4); o = 2} + j == 1 {printf("export JUMP_STRUCT_SIZE=%d\n", $4); j = 2} # Bail out once we have everything - a == 2 && b == 2 && (p == 2 || skip_p) && e == 2 && (o == 2 || skip_o) {exit}')" + a == 2 && b == 2 && (p == 2 || skip_p) && e == 2 && (o == 2 || skip_o) && (j == 2 || skip_j) {exit}')" [[ -n "$SPECIAL_VARS" ]] && eval "$SPECIAL_VARS" @@ -299,10 +348,19 @@ find_special_section_data() { [[ -z "$EX_STRUCT_SIZE" ]] && die "can't find special struct paravirt_patch_site size" [[ -z "$PARA_STRUCT_SIZE" && "$CONFIG_PARAVIRT" -ne 0 ]] && die "can't find special struct paravirt_patch_site size" [[ -z "$ORC_STRUCT_SIZE" && "$CONFIG_UNWINDER_ORC" -ne 0 ]] && die "can't find special struct orc_entry size" + [[ -z "$JUMP_STRUCT_SIZE" && "$CONFIG_JUMP_LABEL" -ne 0 ]] && die "can't find special struct jump_entry size" return } +filter_parent_obj() +{ + local dir="${1}" + local file="${2}" + + grep -v "\.mod\.cmd$" | grep -Fv "${dir}/.${file}.cmd" +} + find_parent_obj() { dir="$(dirname "$1")" absdir="$(readlink -f "$dir")" @@ -314,17 +372,17 @@ find_parent_obj() { if [[ "$DEEP_FIND" -eq 1 ]]; then num=0 if [[ -n "$last_deep_find" ]]; then - parent="$(grep -l "$grepname" "$last_deep_find"/.*.cmd | grep -Fv "$pdir/.${file}.cmd" | head -n1)" - num="$(grep -l "$grepname" "$last_deep_find"/.*.cmd | grep -Fvc "$pdir/.${file}.cmd")" + parent="$(grep -lw "$grepname" "$last_deep_find"/.*.cmd | filter_parent_obj "${pdir}" "${file}" | head -n1)" + num="$(grep -lw "$grepname" "$last_deep_find"/.*.cmd | filter_parent_obj "${pdir}" "${file}" | wc -l)" fi if [[ "$num" -eq 0 ]]; then - parent="$(find ./* -name ".*.cmd" -print0 | xargs -0 grep -l "$grepname" | grep -Fv "$pdir/.${file}.cmd" | cut -c3- | head -n1)" - num="$(find ./* -name ".*.cmd" -print0 | xargs -0 grep -l "$grepname" | grep -Fvc "$pdir/.${file}.cmd")" + parent="$(find . -name ".*.cmd" -print0 | xargs -0 grep -lw "$grepname" | filter_parent_obj "${pdir}" "${file}" | cut -c3- | head -n1)" + num="$(find . -name ".*.cmd" -print0 | xargs -0 grep -lw "$grepname" | filter_parent_obj "${pdir}" "${file}" | wc -l)" [[ "$num" -eq 1 ]] && last_deep_find="$(dirname "$parent")" fi else - parent="$(grep -l "$grepname" "$dir"/.*.cmd | grep -Fv "$dir/.${file}.cmd" | head -n1)" - num="$(grep -l "$grepname" "$dir"/.*.cmd | grep -Fvc "$dir/.${file}.cmd")" + parent="$(grep -lw "$grepname" "$dir"/.*.cmd | filter_parent_obj "${dir}" "${file}" | head -n1)" + num="$(grep -lw "$grepname" "$dir"/.*.cmd | filter_parent_obj "${dir}" "${file}" | wc -l)" fi [[ "$num" -eq 0 ]] && PARENT="" && return @@ -334,7 +392,7 @@ find_parent_obj() { PARENT="$(basename "$parent")" PARENT="${PARENT#.}" PARENT="${PARENT%.cmd}" - PARENT="$dir/$PARENT" + [[ $dir != "." ]] && PARENT="$dir/$PARENT" [[ ! -e "$PARENT" ]] && die "ERROR: can't find parent $PARENT for $1" } @@ -392,12 +450,14 @@ usage() { echo " -d, --debug Enable 'xtrace' and keep scratch files" >&2 echo " in /tmp" >&2 echo " (can be specified multiple times)" >&2 + echo " -e, --oot-module Enable patching out-of-tree module," >&2 + echo " specify current version of module" >&2 echo " --skip-cleanup Skip post-build cleanup" >&2 echo " --skip-gcc-check Skip gcc version matching check" >&2 echo " (not recommended)" >&2 } -options="$(getopt -o ha:r:s:c:v:j:t:n:o:d -l "help,archversion:,sourcerpm:,sourcedir:,config:,vmlinux:,jobs:,target:,name:,output:,debug,skip-gcc-check,skip-cleanup" -- "$@")" || die "getopt failed" +options="$(getopt -o ha:r:s:c:v:j:t:n:o:de: -l "help,archversion:,sourcerpm:,sourcedir:,config:,vmlinux:,jobs:,target:,name:,output:,oot-module:,debug,skip-gcc-check,skip-cleanup" -- "$@")" || die "getopt failed" eval set -- "$options" @@ -455,6 +515,11 @@ while [[ $# -gt 0 ]]; do echo "DEBUG mode enabled" fi ;; + -e|--oot-module) + [[ ! -f "$2" ]] && die "out-of-tree module '$2' not found" + OOT_MODULE="$(readlink -f "$2")" + shift + ;; --skip-cleanup) echo "Skipping cleanup" SKIPCLEANUP=1 @@ -483,8 +548,7 @@ if [[ $DEBUG -eq 1 ]] || [[ $DEBUG -ge 3 ]]; then fi if [[ -n "$ARCHVERSION" ]] && [[ -n "$VMLINUX" ]]; then - warn "--archversion is incompatible with --vmlinux" - exit 1 + die "--archversion is incompatible with --vmlinux" fi if [[ -n "$SRCRPM" ]]; then @@ -498,6 +562,11 @@ if [[ -n "$SRCRPM" ]]; then ARCHVERSION="${ARCHVERSION#alt-}" fi +if [[ -n "$OOT_MODULE" ]] && [[ -z "$USERSRCDIR" ]]; then + warn "--oot-module requires --sourcedir" + exit 1 +fi + # ensure cachedir and tempdir are setup properly and cleaned mkdir -p "$TEMPDIR" || die "Couldn't create $TEMPDIR" rm -rf "${TEMPDIR:?}"/* @@ -510,11 +579,15 @@ if [[ -n "$USERSRCDIR" ]]; then fi SRCDIR="$USERSRCDIR" - [[ -z "$VMLINUX" ]] && VMLINUX="$SRCDIR"/vmlinux - [[ ! -e "$VMLINUX" ]] && die "can't find vmlinux" + if [[ -z "$OOT_MODULE" ]]; then + [[ -z "$VMLINUX" ]] && VMLINUX="$SRCDIR"/vmlinux + [[ ! -e "$VMLINUX" ]] && die "can't find vmlinux" - # Extract the target kernel version from vmlinux in this case. - ARCHVERSION="$(strings "$VMLINUX" | grep -m 1 -e "^Linux version" | awk '{ print($3); }')" + # Extract the target kernel version from vmlinux in this case. + ARCHVERSION="$(strings "$VMLINUX" | grep -m 1 -e "^Linux version" | awk '{ print($3); }')" + else + ARCHVERSION="$(modinfo -F vermagic "$OOT_MODULE" | awk '{print $1}')" + fi fi [[ -z "$ARCHVERSION" ]] && ARCHVERSION="$(uname -r)" @@ -531,8 +604,8 @@ fi [[ -z "$TARGETS" ]] && TARGETS="vmlinux modules" # Don't check external file. -# shellcheck disable=SC1091 -source /etc/os-release +# shellcheck disable=SC1090 +[[ -f "$RELEASE_FILE" ]] && source "$RELEASE_FILE" DISTRO="$ID" if [[ "$DISTRO" = fedora ]] || [[ "$DISTRO" = rhel ]] || [[ "$DISTRO" = ol ]] || [[ "$DISTRO" = centos ]]; then [[ -z "$VMLINUX" ]] && VMLINUX="/usr/lib/debug/lib/modules/$ARCHVERSION/vmlinux" @@ -563,7 +636,15 @@ if [[ -n "$USERSRCDIR" ]]; then echo "Using source directory at $USERSRCDIR" # save original vmlinux before it gets overwritten by sourcedir build - [[ "$VMLINUX" -ef "$SRCDIR"/vmlinux ]] && cp -f "$VMLINUX" "$TEMPDIR/vmlinux" && VMLINUX="$TEMPDIR/vmlinux" + if [[ -z "$OOT_MODULE" ]] && [[ "$VMLINUX" -ef "$SRCDIR"/vmlinux ]]; then + cp -f "$VMLINUX" "$TEMPDIR/vmlinux" + VMLINUX="$TEMPDIR/vmlinux" + fi + + # For external modules, use the running kernel's config + if [[ -n "$OOT_MODULE" ]] && [[ -z "$CONFIGFILE" ]]; then + CONFIGFILE="/boot/config-${ARCHVERSION}" + fi elif [[ -e "$SRCDIR"/.config ]] && [[ -e "$VERSIONFILE" ]] && [[ "$(cat "$VERSIONFILE")" = "$ARCHVERSION" ]]; then echo "Using cache at $SRCDIR" @@ -580,7 +661,7 @@ else if [[ "$DISTRO" = fedora ]]; then wget -P "$TEMPDIR" "http://kojipkgs.fedoraproject.org/packages/kernel/$KVER/$KREL/src/kernel-$KVER-$KREL.src.rpm" 2>&1 | logger || die else - rpm -q --quiet yum-utils || die "yum-utils not installed" + command -v yumdownloader &>/dev/null || die "yumdownloader (yum-utils or dnf-utils) not installed" yumdownloader --source --destdir "$TEMPDIR" "kernel$ALT-$KVER-$KREL" 2>&1 | logger || die fi SRCRPM="$TEMPDIR/kernel$ALT-$KVER-$KREL.src.rpm" @@ -589,10 +670,10 @@ else echo "Unpacking kernel source" rpm -D "_topdir $RPMTOPDIR" -ivh "$SRCRPM" 2>&1 | logger || die - rpmbuild -D "_topdir $RPMTOPDIR" -bp "--target=$(uname -m)" "$RPMTOPDIR"/SPECS/kernel$ALT.spec 2>&1 | logger || + rpmbuild -D "_topdir $RPMTOPDIR" -bp --nodeps "--target=$(uname -m)" "$RPMTOPDIR"/SPECS/kernel$ALT.spec 2>&1 | logger || die "rpmbuild -bp failed. you may need to run 'yum-builddep kernel' first." - mv "$RPMTOPDIR"/BUILD/kernel-*/linux-"${ARCHVERSION%.*}"*"${ARCHVERSION##*.}" "$SRCDIR" 2>&1 | logger || die + mv "$RPMTOPDIR"/BUILD/kernel-*/linux-* "$SRCDIR" 2>&1 | logger || die rm -rf "$RPMTOPDIR" rm -rf "$SRCDIR/.git" @@ -623,7 +704,7 @@ else sublevel="SUBLEVEL =" fi - pkgname="$(dpkg-query -W -f='${Source}' "linux-image-$ARCHVERSION")" + pkgname="$(dpkg-query -W -f='${Source}' "linux-image-$ARCHVERSION" | sed s/-signed//)" pkgver="$(dpkg-query -W -f='${Version}' "linux-image-$ARCHVERSION")" dscname="${pkgname}_${pkgver}.dsc" @@ -652,46 +733,54 @@ fi [[ ! -e "$CONFIGFILE" ]] && die "can't find config file" [[ ! "$CONFIGFILE" -ef "$SRCDIR"/.config ]] && cp -f "$CONFIGFILE" "$SRCDIR/.config" +# kernel option checking +grep -q "CONFIG_DEBUG_INFO=y" "$CONFIGFILE" || die "kernel doesn't have 'CONFIG_DEBUG_INFO' enabled" + # Build variables - Set some defaults, then adjust features # according to .config and kernel version KBUILD_EXTRA_SYMBOLS="" KPATCH_LDFLAGS="" -KPATCH_MODULE=true +USE_KLP=0 +USE_KLP_ARCH=0 +CONFIG_PARAVIRT=0 +CONFIG_UNWINDER_ORC=0 +CONFIG_JUMP_LABEL=0 +CONFIG_MODVERSIONS=0 -# kernel option checking -grep -q "CONFIG_DEBUG_INFO=y" "$CONFIGFILE" || die "kernel doesn't have 'CONFIG_DEBUG_INFO' enabled" -if grep -q "CONFIG_LIVEPATCH=y" "$CONFIGFILE"; then - # The kernel supports livepatch. - if version_gte "${ARCHVERSION//-*/}" 4.7.0 || is_rhel "$ARCHVERSION"; then - # Use new .klp.rela. sections - KPATCH_MODULE=false - if version_gte "${ARCHVERSION//-*/}" 4.9.0 || is_rhel "$ARCHVERSION"; then - KPATCH_LDFLAGS="--unique=.parainstructions --unique=.altinstructions" - fi +if grep -q "CONFIG_LIVEPATCH=y" "$CONFIGFILE" && (kernel_is_rhel || kernel_version_gte 4.9.0); then + + USE_KLP=1 + + if kernel_is_rhel || ! kernel_version_gte 5.8.0; then + USE_KLP_ARCH=1 + KPATCH_LDFLAGS="--unique=.parainstructions --unique=.altinstructions" + CDO_FLAGS="--klp-arch" fi else # No support for livepatch in the kernel. Kpatch core module is needed. + + # There may be ordering bugs, with jump labels and other special + # sections. Use with caution! + echo "WARNING: Use of kpatch core module (kpatch.ko) is deprecated! There may be bugs!" >&2 + find_core_symvers || die "unable to find Module.symvers for kpatch core module" KBUILD_EXTRA_SYMBOLS="$SYMVERSFILE" fi # optional kernel configs: -if grep -q "CONFIG_PARAVIRT=y" "$CONFIGFILE"; then - CONFIG_PARAVIRT=1 -else - CONFIG_PARAVIRT=0 -fi -if grep -q "CONFIG_UNWINDER_ORC=y" "$CONFIGFILE"; then - CONFIG_UNWINDER_ORC=1 -else - CONFIG_UNWINDER_ORC=0 -fi +grep -q "CONFIG_PARAVIRT=y" "$CONFIGFILE" && CONFIG_PARAVIRT=1 +grep -q "CONFIG_UNWINDER_ORC=y" "$CONFIGFILE" && CONFIG_UNWINDER_ORC=1 +grep -q "CONFIG_JUMP_LABEL=y" "$CONFIGFILE" && CONFIG_JUMP_LABEL=1 +grep -q "CONFIG_MODVERSIONS=y" "$CONFIGFILE" && CONFIG_MODVERSIONS=1 -# unsupported kernel option checking: CONFIG_DEBUG_INFO_SPLIT +# unsupported kernel option checking grep -q "CONFIG_DEBUG_INFO_SPLIT=y" "$CONFIGFILE" && die "kernel option 'CONFIG_DEBUG_INFO_SPLIT' not supported" +grep -q "CONFIG_GCC_PLUGIN_LATENT_ENTROPY=y" "$CONFIGFILE" && die "kernel option 'CONFIG_GCC_PLUGIN_LATENT_ENTROPY' not supported" +grep -q "CONFIG_GCC_PLUGIN_RANDSTRUCT=y" "$CONFIGFILE" && die "kernel option 'CONFIG_GCC_PLUGIN_RANDSTRUCT' not supported" echo "Testing patch file(s)" cd "$SRCDIR" || die +verify_patch_files apply_patches remove_patches @@ -701,7 +790,8 @@ if [[ "$ARCH" = "ppc64le" ]]; then ARCH_KCFLAGS="-mcmodel=large -fplugin=$PLUGINDIR/ppc64le-plugin.so" fi -export KCFLAGS="-I$DATADIR/patch -ffunction-sections -fdata-sections $ARCH_KCFLAGS" +export KCFLAGS="-I$DATADIR/patch -ffunction-sections -fdata-sections \ + $ARCH_KCFLAGS $DEBUG_KCFLAGS" echo "Reading special section data" find_special_section_data @@ -710,18 +800,23 @@ if [[ $DEBUG -ge 4 ]]; then export KPATCH_GCC_DEBUG=1 fi -echo "Building original kernel" -./scripts/setlocalversion --save-scmversion || die +echo "Building original source" +[[ -n "$OOT_MODULE" ]] || ./scripts/setlocalversion --save-scmversion || die unset KPATCH_GCC_TEMPDIR # $TARGETS used as list, no quotes. # shellcheck disable=SC2086 CROSS_COMPILE="$TOOLSDIR/kpatch-gcc " make "-j$CPUS" $TARGETS 2>&1 | logger || die -echo "Building patched kernel" +# Save original module symvers +cp "$SRCDIR/Module.symvers" "$TEMPDIR/Module.symvers" + +echo "Building patched source" apply_patches mkdir -p "$TEMPDIR/orig" "$TEMPDIR/patched" KPATCH_GCC_TEMPDIR="$TEMPDIR" export KPATCH_GCC_TEMPDIR +KPATCH_GCC_SRCDIR="$SRCDIR" +export KPATCH_GCC_SRCDIR # $TARGETS used as list, no quotes. # shellcheck disable=SC2086 CROSS_COMPILE="$TOOLSDIR/kpatch-gcc " \ @@ -739,7 +834,30 @@ if [[ ! -e "$TEMPDIR/changed_objs" ]]; then die "no changed objects found" fi -grep -q vmlinux "$SRCDIR/Module.symvers" || die "truncated $SRCDIR/Module.symvers file" +[[ -n "$OOT_MODULE" ]] || grep -q vmlinux "$SRCDIR/Module.symvers" || die "truncated $SRCDIR/Module.symvers file" + +if [[ "$CONFIG_MODVERSIONS" -eq 1 ]]; then + while read -ra sym_line; do + if [[ ${#sym_line[@]} -lt 4 ]]; then + die "Malformed ${TEMPDIR}/Module.symvers file" + fi + + sym=${sym_line[1]} + + read -ra patched_sym_line <<< "$(grep "\s$sym\s" "$SRCDIR/Module.symvers")" + if [[ ${#patched_sym_line[@]} -lt 4 ]]; then + die "Malformed symbol entry for ${sym} in ${SRCDIR}/Module.symvers file" + fi + + # Assume that both original and patched symvers have the same format. + # In both cases, the symbol should have the same CRC, belong to the same + # Module/Namespace and have the same export type. + if [[ ${#sym_line[@]} -ne ${#patched_sym_line[@]} || \ + "${sym_line[*]}" != "${patched_sym_line[*]}" ]]; then + warn "Version disagreement for symbol ${sym}" + fi + done < "${TEMPDIR}/Module.symvers" +fi # Read as words, no quotes. # shellcheck disable=SC2013 @@ -764,10 +882,10 @@ if [[ -z "$MODNAME" ]] ; then MODNAME="patch" fi - if "$KPATCH_MODULE"; then - MODNAME="kpatch-$MODNAME" - else + if [[ "$USE_KLP" -eq 1 ]]; then MODNAME="livepatch-$MODNAME" + else + MODNAME="kpatch-$MODNAME" fi MODNAME="$(module_name_string "$MODNAME")" @@ -778,6 +896,14 @@ mkdir output declare -a objnames CHANGED=0 ERROR=0 + +# Prepare OOT module symvers file +if [[ -n "$OOT_MODULE" ]]; then + BUILDDIR="/lib/modules/$ARCHVERSION/build/" + cp "$SRCDIR/Module.symvers" "$TEMPDIR/Module.symvers" + awk '{ print $1 "\t" $2 "\t" $3 "\t" $4}' "${BUILDDIR}/Module.symvers" >> "$TEMPDIR/Module.symvers" +fi + for i in $FILES; do # In RHEL 7 based kernels, copy_user_64.o misuses the .fixup section, # which confuses create-diff-object. It's fine to skip it, it's an @@ -792,23 +918,33 @@ for i in $FILES; do find_kobj "$i" cd "$TEMPDIR" || die if [[ -e "orig/$i" ]]; then - if [[ "$KOBJFILE" = vmlinux ]]; then + if [[ "$(basename "$KOBJFILE")" = vmlinux ]]; then KOBJFILE_NAME=vmlinux KOBJFILE_PATH="$VMLINUX" SYMTAB="${TEMPDIR}/${KOBJFILE_NAME}.symtab" + SYMVERS_FILE="$SRCDIR/Module.symvers" + elif [[ "$(basename "$KOBJFILE")" = "$(basename "$OOT_MODULE")" ]]; then + KOBJFILE_NAME="$(basename --suffix=.ko "$OOT_MODULE")" + KOBJFILE_PATH="$OOT_MODULE" + SYMTAB="${TEMPDIR}/module/${KOBJFILE_NAME}.symtab" + SYMVERS_FILE="$TEMPDIR/Module.symvers" else KOBJFILE_NAME=$(basename "${KOBJFILE%.ko}") - KOBJFILE_NAME="${KOBJFILE_NAME/-/_}" + KOBJFILE_NAME="${KOBJFILE_NAME//-/_}" KOBJFILE_PATH="${TEMPDIR}/module/$KOBJFILE" SYMTAB="${KOBJFILE_PATH}.symtab" + SYMVERS_FILE="$SRCDIR/Module.symvers" fi - eu-readelf -s "$KOBJFILE_PATH" > "$SYMTAB" + readelf -s --wide "$KOBJFILE_PATH" > "$SYMTAB" + if [[ "$ARCH" = "ppc64le" ]]; then + sed -ri 's/\s+\[: 8\]//' "$SYMTAB" + fi # create-diff-object orig.o patched.o parent-name parent-symtab # Module.symvers patch-mod-name output.o - "$TOOLSDIR"/create-diff-object "orig/$i" "patched/$i" "$KOBJFILE_NAME" \ - "$SYMTAB" "$SRCDIR/Module.symvers" "${MODNAME//-/_}" \ + "$TOOLSDIR"/create-diff-object $CDO_FLAGS "orig/$i" "patched/$i" "$KOBJFILE_NAME" \ + "$SYMTAB" "$SYMVERS_FILE" "${MODNAME//-/_}" \ "output/$i" 2>&1 | logger 1 check_pipe_status create-diff-object # create-diff-object returns 3 if no functional change is found @@ -840,7 +976,7 @@ done echo export KCFLAGS="-I$DATADIR/patch $ARCH_KCFLAGS" -if "$KPATCH_MODULE"; then +if [[ "$USE_KLP" -eq 0 ]]; then export KCPPFLAGS="-D__KPATCH_MODULE__" fi @@ -858,26 +994,32 @@ cd "$TEMPDIR/output" || die # shellcheck disable=SC2086,SC2046 ld -r $KPATCH_LDFLAGS -o ../patch/tmp_output.o $(find . -name "*.o") 2>&1 | logger || die -if "$KPATCH_MODULE"; then +if [[ "$USE_KLP" -eq 1 ]]; then + cp "$TEMPDIR"/patch/tmp_output.o "$TEMPDIR"/patch/output.o || die + # Avoid MODPOST warning (pre-v5.8) and error (v5.8+) with an empty .cmd file + touch "$TEMPDIR"/patch/.output.o.cmd || die +else # Add .kpatch.checksum for kpatch script md5sum ../patch/tmp_output.o | awk '{printf "%s\0", $1}' > checksum.tmp || die objcopy --add-section .kpatch.checksum=checksum.tmp --set-section-flags .kpatch.checksum=alloc,load,contents,readonly ../patch/tmp_output.o || die rm -f checksum.tmp "$TOOLSDIR"/create-kpatch-module "$TEMPDIR"/patch/tmp_output.o "$TEMPDIR"/patch/output.o 2>&1 | logger 1 check_pipe_status create-kpatch-module -else - cp "$TEMPDIR"/patch/tmp_output.o "$TEMPDIR"/patch/output.o || die fi cd "$TEMPDIR/patch" || die - -KPATCH_BUILD="$SRCDIR" KPATCH_NAME="$MODNAME" \ +if [[ -z "$OOT_MODULE" ]]; then + KPATCH_BUILD="$SRCDIR" +else + KPATCH_BUILD="/lib/modules/$ARCHVERSION/build" +fi +KPATCH_BUILD="$KPATCH_BUILD" KPATCH_NAME="$MODNAME" \ KBUILD_EXTRA_SYMBOLS="$KBUILD_EXTRA_SYMBOLS" \ KPATCH_LDFLAGS="$KPATCH_LDFLAGS" \ make 2>&1 | logger || die -if ! "$KPATCH_MODULE"; then - if [[ -z "$KPATCH_LDFLAGS" ]]; then +if [[ "$USE_KLP" -eq 1 ]]; then + if [[ "$USE_KLP_ARCH" -eq 0 ]]; then extra_flags="--no-klp-arch-sections" fi cp "$TEMPDIR/patch/$MODNAME.ko" "$TEMPDIR/patch/tmp.ko" || die @@ -885,11 +1027,35 @@ if ! "$KPATCH_MODULE"; then check_pipe_status create-klp-module fi +if [[ "$CONFIG_MODVERSIONS" -eq 1 ]]; then + # Check that final module does not reference symbols with different version + # than the target kernel + KP_MOD_VALID=true + # shellcheck disable=SC2086 + while read -ra mod_symbol; do + if [[ ${#mod_symbol[@]} -lt 2 ]]; then + continue + fi + + # Check if the symbol exists in the old Module.symvers, and if it does + # check that the CRCs are unchanged. + if ! awk -v sym="${mod_symbol[1]}" -v crc="${mod_symbol[0]}" \ + '$2==sym && $1!=crc { exit 1 }' "$TEMPDIR/Module.symvers"; then + warn "Patch module references ${mod_symbol[1]} with invalid version" + KP_MOD_VALID=false + fi + done <<< "$(modprobe --dump-modversions $TEMPDIR/patch/$MODNAME.ko)" + if ! $KP_MOD_VALID; then + die "Patch module referencing altered exported kernel symbols cannot be loaded" + fi +fi + readelf --wide --symbols "$TEMPDIR/patch/$MODNAME.ko" 2>/dev/null | \ + sed -r 's/\s+\[: 8\]//' | \ awk '($4=="FUNC" || $4=="OBJECT") && ($5=="GLOBAL" || $5=="WEAK") && $7!="UND" {print $NF}' \ >"${TEMPDIR}"/new_symbols -if "$KPATCH_MODULE"; then +if [[ "$USE_KLP" -eq 0 ]]; then cat >>"${TEMPDIR}"/new_symbols <<-EOF kpatch_shadow_free kpatch_shadow_alloc @@ -904,10 +1070,10 @@ fi # column containing lines unique to first file. UNDEFINED=$(comm -23 <(sort -u "${TEMPDIR}"/undefined_references) \ <(sort -u "${TEMPDIR}"/new_symbols) | tr '\n' ' ') -[[ ! -z "$UNDEFINED" ]] && die "Undefined symbols: $UNDEFINED" +[[ -n "$UNDEFINED" ]] && die "Undefined symbols: $UNDEFINED" cp -f "$TEMPDIR/patch/$MODNAME.ko" "$BASE" || die -[[ "$DEBUG" -eq 0 ]] && rm -f "$LOGFILE" +[[ "$DEBUG" -eq 0 && "$SKIPCLEANUP" -eq 0 ]] && rm -f "$LOGFILE" echo "SUCCESS" diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index fcb7161..377dc7f 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -142,7 +142,7 @@ int offset_of_string(struct list_head *list, char *name) list_for_each_entry(string, list, list) { if (!strcmp(string->name, name)) return index; - index += strlen(string->name) + 1; + index += (int)strlen(string->name) + 1; } /* allocate a new string */ @@ -153,9 +153,10 @@ int offset_of_string(struct list_head *list, char *name) void kpatch_create_rela_list(struct kpatch_elf *kelf, struct section *sec) { - int rela_nr, index = 0, skip = 0; + int index = 0, skip = 0; struct rela *rela; unsigned int symndx; + unsigned long rela_nr; /* find matching base (text/data) section */ sec->base = find_section_by_index(&kelf->sections, sec->sh.sh_info); @@ -167,7 +168,7 @@ void kpatch_create_rela_list(struct kpatch_elf *kelf, struct section *sec) rela_nr = sec->sh.sh_size / sec->sh.sh_entsize; - log_debug("\n=== rela list for %s (%d entries) ===\n", + log_debug("\n=== rela list for %s (%ld entries) ===\n", sec->base->name, rela_nr); if (is_debug_section(sec)) { @@ -185,8 +186,8 @@ void kpatch_create_rela_list(struct kpatch_elf *kelf, struct section *sec) rela->type = GELF_R_TYPE(rela->rela.r_info); rela->addend = rela->rela.r_addend; - rela->offset = rela->rela.r_offset; - symndx = GELF_R_SYM(rela->rela.r_info); + rela->offset = (unsigned int)rela->rela.r_offset; + symndx = (unsigned int)GELF_R_SYM(rela->rela.r_info); rela->sym = find_symbol_by_index(&kelf->symbols, symndx); if (!rela->sym) ERROR("could not find rela entry symbol\n"); @@ -194,15 +195,15 @@ void kpatch_create_rela_list(struct kpatch_elf *kelf, struct section *sec) (rela->sym->sec->sh.sh_flags & SHF_STRINGS)) { rela->string = rela->sym->sec->data->d_buf + rela->addend; if (!rela->string) - ERROR("could not lookup rela string for %s+%d", + ERROR("could not lookup rela string for %s+%ld", rela->sym->name, rela->addend); } if (skip) continue; - log_debug("offset %d, type %d, %s %s %d", rela->offset, + log_debug("offset %d, type %d, %s %s %ld", rela->offset, rela->type, rela->sym->name, - (rela->addend < 0)?"-":"+", abs(rela->addend)); + (rela->addend < 0)?"-":"+", labs(rela->addend)); if (rela->string) log_debug(" (string = %s)", rela->string); log_debug("\n"); @@ -247,7 +248,7 @@ void kpatch_create_section_list(struct kpatch_elf *kelf) if (!sec->data) ERROR("elf_getdata"); - sec->index = elf_ndxscn(scn); + sec->index = (unsigned int)elf_ndxscn(scn); log_debug("ndx %02d, data %p, size %zu, name %s\n", sec->index, sec->data->d_buf, sec->data->d_size, @@ -269,13 +270,15 @@ void kpatch_create_symbol_list(struct kpatch_elf *kelf) if (!symtab) ERROR("missing symbol table"); - symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize; + symbols_nr = (unsigned int)(symtab->sh.sh_size / symtab->sh.sh_entsize); log_debug("\n=== symbol list (%d entries) ===\n", symbols_nr); while (symbols_nr--) { ALLOC_LINK(sym, &kelf->symbols); + INIT_LIST_HEAD(&sym->children); + sym->index = index; if (!gelf_getsym(symtab->data, index, &sym->sym)) ERROR("gelf_getsym"); @@ -332,7 +335,9 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf) #else rela = list_first_entry(&sym->sec->rela->relas, struct rela, list); - if (rela->type != R_X86_64_NONE || + if ((rela->type != R_X86_64_NONE && + rela->type != R_X86_64_PC32 && + rela->type != R_X86_64_PLT32) || strcmp(rela->sym->name, "__fentry__")) continue; @@ -401,11 +406,11 @@ void kpatch_dump_kelf(struct kpatch_elf *kelf) goto next; printf("rela section expansion\n"); list_for_each_entry(rela, &sec->relas, list) { - printf("sym %d, offset %d, type %d, %s %s %d\n", + printf("sym %d, offset %d, type %d, %s %s %ld\n", rela->sym->index, rela->offset, rela->type, rela->sym->name, (rela->addend < 0)?"-":"+", - abs(rela->addend)); + labs(rela->addend)); } } else { if (sec->sym) @@ -487,7 +492,7 @@ void kpatch_create_shstrtab(struct kpatch_elf *kelf) offset = 1; list_for_each_entry(sec, &kelf->sections, list) { len = strlen(sec->name) + 1; - sec->sh.sh_name = offset; + sec->sh.sh_name = (unsigned int)offset; memcpy(buf + offset, sec->name, len); offset += len; } @@ -540,7 +545,7 @@ void kpatch_create_strtab(struct kpatch_elf *kelf) continue; } len = strlen(sym->name) + 1; - sym->sym.st_name = offset; + sym->sym.st_name = (unsigned int)offset; memcpy(buf + offset, sym->name, len); offset += len; } @@ -565,10 +570,12 @@ void kpatch_create_strtab(struct kpatch_elf *kelf) void kpatch_create_symtab(struct kpatch_elf *kelf) { struct section *symtab; + struct section *strtab; struct symbol *sym; char *buf; size_t size; - int nr = 0, offset = 0, nr_local = 0; + int nr = 0, nr_local = 0; + unsigned long offset = 0; symtab = find_section_by_name(&kelf->sections, ".symtab"); if (!symtab) @@ -598,7 +605,11 @@ void kpatch_create_symtab(struct kpatch_elf *kelf) symtab->data->d_size = size; /* update symtab section header */ - symtab->sh.sh_link = find_section_by_name(&kelf->sections, ".strtab")->index; + strtab = find_section_by_name(&kelf->sections, ".strtab"); + if (!strtab) + ERROR("missing .strtab section"); + + symtab->sh.sh_link = strtab->index; symtab->sh.sh_info = nr_local; } @@ -647,6 +658,7 @@ struct section *create_section_pair(struct kpatch_elf *kelf, char *name, relasec->data = malloc(sizeof(*relasec->data)); if (!relasec->data) ERROR("malloc"); + relasec->data->d_type = ELF_T_RELA; /* set section header */ relasec->sh.sh_type = SHT_RELA; @@ -707,7 +719,7 @@ void kpatch_reindex_elements(struct kpatch_elf *kelf) list_for_each_entry(sym, &kelf->symbols, list) { sym->index = index++; if (sym->sec) - sym->sym.st_shndx = sym->sec->index; + sym->sym.st_shndx = (unsigned short)sym->sec->index; else if (sym->sym.st_shndx != SHN_ABS && sym->sym.st_shndx != SHN_LIVEPATCH) sym->sym.st_shndx = SHN_UNDEF; @@ -717,8 +729,9 @@ void kpatch_reindex_elements(struct kpatch_elf *kelf) void kpatch_rebuild_rela_section_data(struct section *sec) { struct rela *rela; - int nr = 0, index = 0, size; + int nr = 0, index = 0; GElf_Rela *relas; + size_t size; list_for_each_entry(rela, &sec->relas, list) nr++; @@ -750,6 +763,7 @@ void kpatch_write_output_elf(struct kpatch_elf *kelf, Elf *elf, char *outfile) { int fd; struct section *sec; + struct section *shstrtab; Elf *elfout; GElf_Ehdr eh, ehout; Elf_Scn *scn; @@ -765,7 +779,7 @@ void kpatch_write_output_elf(struct kpatch_elf *kelf, Elf *elf, char *outfile) if (!elfout) ERROR("elf_begin"); - if (!gelf_newehdr(elfout, gelf_getclass(kelf->elf))) + if (!gelf_newehdr(elfout, gelf_getclass(elf))) ERROR("gelf_newehdr"); if (!gelf_getehdr(elfout, &ehout)) @@ -779,7 +793,12 @@ void kpatch_write_output_elf(struct kpatch_elf *kelf, Elf *elf, char *outfile) ehout.e_machine = eh.e_machine; ehout.e_type = eh.e_type; ehout.e_version = EV_CURRENT; - ehout.e_shstrndx = find_section_by_name(&kelf->sections, ".shstrtab")->index; + + shstrtab = find_section_by_name(&kelf->sections, ".shstrtab"); + if (!shstrtab) + ERROR("missing .shstrtab section"); + + ehout.e_shstrndx = (unsigned short)shstrtab->index; /* add changed sections */ list_for_each_entry(sec, &kelf->sections, list) { @@ -814,6 +833,9 @@ void kpatch_write_output_elf(struct kpatch_elf *kelf, Elf *elf, char *outfile) printf("%s\n",elf_errmsg(-1)); ERROR("elf_update"); } + + elf_end(elfout); + close(fd); } /* @@ -830,17 +852,21 @@ void kpatch_elf_teardown(struct kpatch_elf *kelf) struct rela *rela, *saferela; list_for_each_entry_safe(sec, safesec, &kelf->sections, list) { + if (sec->twin) + sec->twin->twin = NULL; if (is_rela_section(sec)) { list_for_each_entry_safe(rela, saferela, &sec->relas, list) { memset(rela, 0, sizeof(*rela)); free(rela); } - memset(sec, 0, sizeof(*sec)); - free(sec); } + memset(sec, 0, sizeof(*sec)); + free(sec); } list_for_each_entry_safe(sym, safesym, &kelf->symbols, list) { + if (sym->twin) + sym->twin->twin = NULL; memset(sym, 0, sizeof(*sym)); free(sym); } diff --git a/kpatch-build/kpatch-elf.h b/kpatch-build/kpatch-elf.h index bec6398..d2bb454 100644 --- a/kpatch-build/kpatch-elf.h +++ b/kpatch-build/kpatch-elf.h @@ -70,6 +70,9 @@ struct section { struct symbol { struct list_head list; struct symbol *twin; + struct symbol *parent; + struct list_head children; + struct list_head subfunction_node; struct section *sec; GElf_Sym sym; char *name; @@ -88,8 +91,8 @@ struct rela { GElf_Rela rela; struct symbol *sym; unsigned int type; - int addend; unsigned int offset; + long addend; char *string; bool need_dynrela; }; diff --git a/kpatch-build/kpatch-gcc b/kpatch-build/kpatch-gcc index 2d56da1..9663290 100755 --- a/kpatch-build/kpatch-gcc +++ b/kpatch-build/kpatch-gcc @@ -13,7 +13,7 @@ fi declare -a args=("$@") -if [[ "$TOOLCHAINCMD" = "gcc" ]] ; then +if [[ "$TOOLCHAINCMD" =~ "gcc" ]] ; then while [ "$#" -gt 0 ]; do if [ "$1" = "-o" ]; then obj="$2" @@ -23,7 +23,8 @@ if [[ "$TOOLCHAINCMD" = "gcc" ]] ; then [[ "$obj" = */.tmp_mc_*.o ]] && break; [[ "$obj" = */.tmp_*.o ]] && obj="${obj/.tmp_/}" - case "$obj" in + relobj=${obj//$KPATCH_GCC_SRCDIR\//} + case "$relobj" in *.mod.o|\ *built-in.o|\ *built-in.a|\ @@ -47,9 +48,9 @@ if [[ "$TOOLCHAINCMD" = "gcc" ]] ; then break ;; *.o) - mkdir -p "$KPATCH_GCC_TEMPDIR/orig/$(dirname "$obj")" - [[ -e "$obj" ]] && cp -f "$obj" "$KPATCH_GCC_TEMPDIR/orig/$obj" - echo "$obj" >> "$KPATCH_GCC_TEMPDIR/changed_objs" + mkdir -p "$KPATCH_GCC_TEMPDIR/orig/$(dirname "$relobj")" + [[ -e "$obj" ]] && cp -f "$obj" "$KPATCH_GCC_TEMPDIR/orig/$relobj" + echo "$relobj" >> "$KPATCH_GCC_TEMPDIR/changed_objs" break ;; *) @@ -63,10 +64,11 @@ elif [[ "$TOOLCHAINCMD" = "ld" ]] ; then while [ "$#" -gt 0 ]; do if [ "$1" = "-o" ]; then obj="$2" + relobj=${obj//$KPATCH_GCC_SRCDIR\//} case "$obj" in *.ko) - mkdir -p "$KPATCH_GCC_TEMPDIR/module/$(dirname "$obj")" - cp -f "$obj" "$KPATCH_GCC_TEMPDIR/module/$obj" + mkdir -p "$KPATCH_GCC_TEMPDIR/module/$(dirname "$relobj")" + cp -f "$obj" "$KPATCH_GCC_TEMPDIR/module/$relobj" break ;; .tmp_vmlinux*|vmlinux) diff --git a/kpatch-build/kpatch-intermediate.h b/kpatch-build/kpatch-intermediate.h index 3dea775..2036cb3 100644 --- a/kpatch-build/kpatch-intermediate.h +++ b/kpatch-build/kpatch-intermediate.h @@ -26,7 +26,7 @@ struct kpatch_symbol { unsigned long src; - unsigned long pos; + unsigned long sympos; unsigned char bind, type; char *name; char *objname; /* object to which this sym belongs */ @@ -35,8 +35,8 @@ struct kpatch_symbol { struct kpatch_relocation { unsigned long dest; unsigned int type; - int addend; int external; + long addend; char *objname; /* object to which this rela applies to */ struct kpatch_symbol *ksym; }; diff --git a/kpatch-build/lookup.c b/kpatch-build/lookup.c index d08c10b..9086c9c 100644 --- a/kpatch-build/lookup.c +++ b/kpatch-build/lookup.c @@ -35,12 +35,13 @@ #include #include #include +#include #include "lookup.h" #include "log.h" struct object_symbol { - unsigned long value; + unsigned long addr; unsigned long size; char *name; int type, bind; @@ -56,6 +57,7 @@ struct lookup_table { struct object_symbol *obj_syms; struct export_symbol *exp_syms; struct object_symbol *local_syms; + char *objname; }; #define for_each_obj_symbol(ndx, iter, table) \ @@ -80,7 +82,9 @@ static int maybe_discarded_sym(const char *name) */ if (!strncmp(name, "__exitcall_", 11) || !strncmp(name, "__brk_reservation_fn_", 21) || - !strncmp(name, "__func_stack_frame_non_standard_", 32)) + !strncmp(name, "__func_stack_frame_non_standard_", 32) || + !strncmp(name, "__addressable_", 14) || + !strncmp(name, "__UNIQUE_ID_", 12)) return 1; return 0; @@ -165,70 +169,98 @@ static void find_local_syms(struct lookup_table *table, char *hint, if (!locals_match(table, i, child_locals)) continue; if (table->local_syms) - ERROR("find_local_syms for %s: found_dup", hint); + ERROR("found duplicate matches for %s local symbols in %s symbol table", + hint, table->objname); table->local_syms = sym; } if (!table->local_syms) - ERROR("find_local_syms for %s: couldn't find in vmlinux symbol table", hint); + ERROR("couldn't find matching %s local symbols in %s symbol table", + hint, table->objname); } /* Strip the path and replace '-' with '_' */ static char *make_modname(char *modname) { - char *cur; + char *cur, *name; if (!modname) return NULL; - cur = modname; + name = strdup(basename(modname)); + if (!name) + ERROR("strdup"); + + cur = name; /* use cur as tmp */ while (*cur != '\0') { if (*cur == '-') *cur = '_'; cur++; } - return basename(modname); + return name; } static void symtab_read(struct lookup_table *table, char *path) { FILE *file; - long unsigned int value, size; - unsigned int i = 0; - char line[256], name[256], type[16], bind[16], ndx[16]; + long unsigned int addr; + int alloc_nr = 0, i = 0; + int matched; + bool skip = false; + char line[256], name[256], size[16], type[16], bind[16], ndx[16]; if ((file = fopen(path, "r")) == NULL) ERROR("fopen"); - while (fgets(line, 256, file)) { - if (sscanf(line, "%*s %lx %lu %s %s %*s %s %s\n", - &value, &size, type, bind, ndx, name) != 6 || - !strcmp(ndx, "UNDEF") || - !strcmp(bind, "SECTION")) - continue; - - table->obj_nr++; - } + /* + * First, get an upper limit on the number of entries for allocation + * purposes: + */ + while (fgets(line, 256, file)) + alloc_nr++; - table->obj_syms = malloc(table->obj_nr * sizeof(*table->obj_syms)); + table->obj_syms = malloc(alloc_nr * sizeof(*table->obj_syms)); if (!table->obj_syms) ERROR("malloc table.obj_syms"); - memset(table->obj_syms, 0, table->obj_nr * sizeof(*table->obj_syms)); + memset(table->obj_syms, 0, alloc_nr * sizeof(*table->obj_syms)); rewind(file); + /* Now read the actual entries: */ while (fgets(line, 256, file)) { - if (sscanf(line, "%*s %lx %lu %s %s %*s %s %s\n", - &value, &size, type, bind, ndx, name) != 6 || - !strcmp(ndx, "UNDEF") || - !strcmp(bind, "SECTION")) + + /* + * On powerpc, "readelf -s" shows both .dynsym and .symtab + * tables. .dynsym is just a subset of .symtab, so skip it to + * avoid duplicates. + */ + if (strstr(line, ".dynsym")) { + skip = true; + continue; + } else if (strstr(line, ".symtab")) { + skip = false; + continue; + } + if (skip) continue; - table->obj_syms[i].value = value; - table->obj_syms[i].size = size; - table->obj_syms[i].name = strdup(name); + matched = sscanf(line, "%*s %lx %s %s %s %*s %s %s\n", + &addr, size, type, bind, ndx, name); + + if (matched == 5) { + name[0] = '\0'; + matched++; + } + + if (matched != 6 || + !strcmp(ndx, "UND") || + !strcmp(type, "SECTION")) + continue; + + table->obj_syms[i].addr = addr; + table->obj_syms[i].size = strtoul(size, NULL, 0); if (!strcmp(bind, "LOCAL")) { table->obj_syms[i].bind = STB_LOCAL; @@ -255,26 +287,54 @@ static void symtab_read(struct lookup_table *table, char *path) table->obj_syms[i].name = strdup(name); if (!table->obj_syms[i].name) ERROR("strdup"); + i++; } + table->obj_nr = i; + fclose(file); } +/* + * The Module.symvers file format is one of the following, depending on kernel + * version: + * + * + * + * + * + * All we care about is Symbol and Module. Since the format is unpredictable, + * we have to dynamically determine which column is Module by looking for + * "vmlinux". + */ static void symvers_read(struct lookup_table *table, char *path) { FILE *file; - unsigned int crc, i = 0; - char name[256], mod[256], export[256]; - char *objname, *symname; + int i, column, mod_column = 0; + char line[4096]; + char *tmp, *objname, *symname; if ((file = fopen(path, "r")) == NULL) ERROR("fopen"); - while (fscanf(file, "%x %s %s %s\n", - &crc, name, mod, export) != EOF) + while (fgets(line, 4096, file)) { table->exp_nr++; + if (mod_column) + continue; + + /* Find the module column */ + for (column = 1, tmp = line; (tmp = strchr(tmp, '\t')); column++) { + tmp++; + if (*tmp && !strncmp(tmp, "vmlinux", 7)) + mod_column = column; + } + } + + if (table->exp_nr && !mod_column) + ERROR("Module.symvers: invalid format"); + table->exp_syms = malloc(table->exp_nr * sizeof(*table->exp_syms)); if (!table->exp_syms) ERROR("malloc table.exp_syms"); @@ -282,29 +342,36 @@ static void symvers_read(struct lookup_table *table, char *path) table->exp_nr * sizeof(*table->exp_syms)); rewind(file); + for (i = 0; fgets(line, 4096, file); i++) { + char *name = NULL, *mod = NULL; + + for (column = 1, tmp = line; (tmp = strchr(tmp, '\t')); column++) { + *tmp++ = '\0'; + if (*tmp && column == 1) + name = tmp; + else if (*tmp && column == mod_column) + mod = tmp; + } + + if (!name || !mod) + continue; - while (fscanf(file, "%x %s %s %s\n", - &crc, name, mod, export) != EOF) { symname = strdup(name); if (!symname) perror("strdup"); - objname = strdup(mod); - if (!objname) - perror("strdup"); - /* Modifies objname in-place */ - objname = make_modname(objname); + objname = make_modname(mod); table->exp_syms[i].name = symname; table->exp_syms[i].objname = objname; - i++; } fclose(file); } -struct lookup_table *lookup_open(char *symtab_path, char *symvers_path, - char *hint, struct sym_compare_type *locals) +struct lookup_table *lookup_open(char *symtab_path, char *objname, + char *symvers_path, char *hint, + struct sym_compare_type *locals) { struct lookup_table *table; @@ -313,6 +380,7 @@ struct lookup_table *lookup_open(char *symtab_path, char *symvers_path, ERROR("malloc table"); memset(table, 0, sizeof(*table)); + table->objname = objname; symtab_read(table, symtab_path); symvers_read(table, symvers_path); find_local_syms(table, hint, locals); @@ -322,25 +390,36 @@ struct lookup_table *lookup_open(char *symtab_path, char *symvers_path, void lookup_close(struct lookup_table *table) { + int i; + struct object_symbol *obj_sym; + struct export_symbol *exp_sym; + + for_each_obj_symbol(i, obj_sym, table) + free(obj_sym->name); free(table->obj_syms); + + for_each_exp_symbol(i, exp_sym, table) { + free(exp_sym->name); + free(exp_sym->objname); + } free(table->exp_syms); free(table); } -int lookup_local_symbol(struct lookup_table *table, char *name, - struct lookup_result *result) +static bool lookup_local_symbol(struct lookup_table *table, char *name, + struct lookup_result *result) { struct object_symbol *sym; - unsigned long pos = 0; - int i, match = 0, in_file = 0; + unsigned long sympos = 0; + int i, in_file = 0; if (!table->local_syms) - return 1; + return false; memset(result, 0, sizeof(*result)); for_each_obj_symbol(i, sym, table) { if (sym->bind == STB_LOCAL && !strcmp(sym->name, name)) - pos++; + sympos++; if (table->local_syms == sym) { in_file = 1; @@ -354,115 +433,91 @@ int lookup_local_symbol(struct lookup_table *table, char *name, break; if (sym->bind == STB_LOCAL && !strcmp(sym->name, name)) { - match = 1; - break; - } - } - - if (!match) - return 1; - - result->pos = pos; - result->value = sym->value; - result->size = sym->size; - return 0; -} -int lookup_global_symbol(struct lookup_table *table, char *name, - struct lookup_result *result) -{ - struct object_symbol *sym; - int i; + if (result->objname) + ERROR("duplicate local symbol found for %s", name); - memset(result, 0, sizeof(*result)); - for_each_obj_symbol(i, sym, table) { - if ((sym->bind == STB_GLOBAL || sym->bind == STB_WEAK) && - !strcmp(sym->name, name)) { - result->value = sym->value; - result->size = sym->size; - result->pos = 0; /* always 0 for global symbols */ - return 0; + result->objname = table->objname; + result->addr = sym->addr; + result->size = sym->size; + result->sympos = sympos; + result->global = false; + result->exported = false; } } - return 1; + return !!result->objname; } -int lookup_is_exported_symbol(struct lookup_table *table, char *name) +static bool lookup_exported_symbol(struct lookup_table *table, char *name, + struct lookup_result *result) { - struct export_symbol *sym, *match = NULL; + struct export_symbol *sym; int i; + if (result) + memset(result, 0, sizeof(*result)); + for_each_exp_symbol(i, sym, table) { if (!strcmp(sym->name, name)) { - if (match) - ERROR("duplicate exported symbol found for %s", name); - match = sym; - } - } - return !!match; -} + if (!result) + return true; -/* - * lookup_exported_symbol_objname - find the object/module an exported - * symbol belongs to. - */ -char *lookup_exported_symbol_objname(struct lookup_table *table, char *name) -{ - struct export_symbol *sym, *match = NULL; - int i; - - for_each_exp_symbol(i, sym, table) { - if (!strcmp(sym->name, name)) { - if (match) + if (result->objname) ERROR("duplicate exported symbol found for %s", name); - match = sym; + + result->objname = sym->objname; + result->addr = 0; /* determined at runtime */ + result->size = 0; /* not used for exported symbols */ + result->sympos = 0; /* always 0 for exported symbols */ + result->global = true; + result->exported = true; } } - if (match) - return match->objname; - - return NULL; - } + return result && result->objname; +} -#if 0 /* for local testing */ -static void find_this(struct lookup_table *table, char *sym, char *hint) +bool is_exported(struct lookup_table *table, char *name) { - struct lookup_result result; - - if (hint) - lookup_local_symbol(table, sym, hint, &result); - else - lookup_global_symbol(table, sym, &result); - - printf("%s %s w/ %s hint at 0x%016lx len %lu pos %lu\n", - hint ? "local" : "global", sym, hint ? hint : "no", - result.value, result.size, result.pos); + return lookup_exported_symbol(table, name, NULL); } -int main(int argc, char **argv) +static bool lookup_global_symbol(struct lookup_table *table, char *name, + struct lookup_result *result) { - struct lookup_table *vmlinux; + struct object_symbol *sym; + int i; - if (argc != 2) - return 1; + memset(result, 0, sizeof(*result)); + for_each_obj_symbol(i, sym, table) { + if ((sym->bind == STB_GLOBAL || sym->bind == STB_WEAK) && + !strcmp(sym->name, name)) { - vmlinux = lookup_open(argv[1]); + if (result->objname) + ERROR("duplicate global symbol found for %s", name); - printf("printk is%s exported\n", - lookup_is_exported_symbol(vmlinux, "__fentry__") ? "" : " not"); - printf("meminfo_proc_show is%s exported\n", - lookup_is_exported_symbol(vmlinux, "meminfo_proc_show") ? "" : " not"); + result->objname = table->objname; + result->addr = sym->addr; + result->size = sym->size; + result->sympos = 0; /* always 0 for global symbols */ + result->global = true; + result->exported = is_exported(table, name); + } + } - find_this(vmlinux, "printk", NULL); - find_this(vmlinux, "pages_to_scan_show", "ksm.c"); - find_this(vmlinux, "pages_to_scan_show", "huge_memory.c"); - find_this(vmlinux, "pages_to_scan_show", NULL); /* should fail */ + return !!result->objname; +} - lookup_close(vmlinux); +bool lookup_symbol(struct lookup_table *table, char *name, + struct lookup_result *result) +{ + if (lookup_local_symbol(table, name, result)) + return true; - return 0; + if (lookup_global_symbol(table, name, result)) + return true; + + return lookup_exported_symbol(table, name, result); } -#endif diff --git a/kpatch-build/lookup.h b/kpatch-build/lookup.h index 420d0f0..8d29900 100644 --- a/kpatch-build/lookup.h +++ b/kpatch-build/lookup.h @@ -1,12 +1,16 @@ #ifndef _LOOKUP_H_ #define _LOOKUP_H_ +#include + struct lookup_table; struct lookup_result { - unsigned long value; + char *objname; + unsigned long addr; unsigned long size; - unsigned long pos; + unsigned long sympos; + bool global, exported; }; struct sym_compare_type { @@ -14,14 +18,11 @@ struct sym_compare_type { int type; }; -struct lookup_table *lookup_open(char *symtab_path, char *symvers_path, - char *hint, struct sym_compare_type *locals); +struct lookup_table *lookup_open(char *symtab_path, char *objname, + char *symvers_path, char *hint, + struct sym_compare_type *locals); void lookup_close(struct lookup_table *table); -int lookup_local_symbol(struct lookup_table *table, char *name, - struct lookup_result *result); -int lookup_global_symbol(struct lookup_table *table, char *name, - struct lookup_result *result); -int lookup_is_exported_symbol(struct lookup_table *table, char *name); -char *lookup_exported_symbol_objname(struct lookup_table *table, char *name); +bool lookup_symbol(struct lookup_table *table, char *name, + struct lookup_result *result); #endif /* _LOOKUP_H_ */ diff --git a/kpatch/kpatch b/kpatch/kpatch index 2e02245..bca8f41 100755 --- a/kpatch/kpatch +++ b/kpatch/kpatch @@ -25,7 +25,7 @@ INSTALLDIR=/var/lib/kpatch SCRIPTDIR="$(readlink -f "$(dirname "$(type -p "$0")")")" -VERSION="0.6.1" +VERSION="0.9.2" POST_ENABLE_WAIT=15 # seconds POST_SIGNAL_WAIT=60 # seconds @@ -126,7 +126,7 @@ find_core_module() { } core_loaded () { - grep -q -e "T klp_register_patch" -e "T kpatch_register" /proc/kallsyms + grep -q -e "T klp_enable_patch" -e "T kpatch_register" /proc/kallsyms } get_module_name () { @@ -157,7 +157,7 @@ verify_module_checksum () { checksum="$(readelf -p .kpatch.checksum "$1" 2>&1 | grep '\[.*\]' | awk '{print $3}')" # Fail checksum match only if both exist and diverge - if [[ ! -z "$checksum" ]] && [[ -e "$SYSFS/${modname}/checksum" ]] ; then + if [[ -n "$checksum" ]] && [[ -e "$SYSFS/${modname}/checksum" ]] ; then sysfs_checksum="$(cat "$SYSFS/${modname}/checksum")" [[ "$checksum" == "$sysfs_checksum" ]] || return 1 fi @@ -217,7 +217,7 @@ show_stalled_processes() { echo "Stalled processes:" for proc_task in /proc/[0-9]*/task/[0-9]*; do tid=${proc_task#*/task/} - is_stalled "$module" "$tid" && echo "$tid $(cat "$proc_task"/comm 2>/dev/null)" + is_stalled "$module" "$tid" && echo -e "$tid $(cat "$proc_task"/comm 2>/dev/null)\nstack:\n$(cat "$proc_task"/stack 2>/dev/null)" done } @@ -230,21 +230,8 @@ signal_stalled_processes() { [[ -z "$module" ]] && return if [[ -e "/sys/kernel/livepatch/$module/signal" ]] ; then + echo "signaling stalled process(es):" echo 1 > "/sys/kernel/livepatch/$module/signal" - else - for proc_task in /proc/[0-9]*/task/[0-9]*; do - tid=${proc_task#*/task/} - if is_stalled "$module" "$tid" ; then - if [[ "$tid" -eq "$$" ]] ; then - echo "skipping pid $tid $(cat "$proc_task"/comm 2>/dev/null)" - else - echo "signaling pid $tid $(cat "$proc_task"/comm 2>/dev/null)" - kill -SIGSTOP "$tid" - sleep .1 - kill -SIGCONT "$tid" - fi - fi - done fi } @@ -263,7 +250,7 @@ wait_for_patch_transition() { sleep 1s done - echo "patch transition has stalled, signaling stalled process(es):" + echo "patch transition has stalled!" signal_stalled_processes echo "waiting (up to $POST_SIGNAL_WAIT seconds) for patch transition to complete..." @@ -304,6 +291,7 @@ load_module () { echo "module already loaded, re-enabling" echo 1 > "${moddir}/enabled" || die "failed to re-enable module $modname" if ! wait_for_patch_transition "$modname" ; then + show_stalled_processes echo "module $modname did not complete its transition, disabling..." echo 0 > "${moddir}/enabled" || die "failed to disable module $modname" wait_for_patch_transition "$modname" @@ -314,32 +302,33 @@ load_module () { die "error: cannot re-enable patch module $modname, cannot verify checksum match" fi else - die "error: module named $modname already loaded and enabled" + echo "module named $modname already loaded and enabled" fi - fi + else + echo "loading patch module: $module" + local i=0 + while true; do + out="$(LC_ALL=C insmod "$module" 2>&1)" + [[ -z "$out" ]] && break + echo "$out" 1>&2 + [[ ! "$out" =~ "Device or resource busy" ]] && + die "failed to load module $module" - echo "loading patch module: $module" - local i=0 - while true; do - out="$(LC_ALL=C insmod "$module" 2>&1)" - [[ -z "$out" ]] && break - echo "$out" 1>&2 - [[ ! "$out" =~ "Device or resource busy" ]] && - die "failed to load module $module" - - # "Device or resource busy" means the activeness safety check - # failed. Retry in a few seconds. - i=$((i+1)) - if [[ $i -eq $MAX_LOAD_ATTEMPTS ]]; then - die "failed to load module $module" - break - else - warn "retrying..." - sleep $RETRY_INTERVAL - fi - done + # "Device or resource busy" means the activeness safety check + # failed. Retry in a few seconds. + i=$((i+1)) + if [[ $i -eq $MAX_LOAD_ATTEMPTS ]]; then + die "failed to load module $module" + break + else + warn "retrying..." + sleep $RETRY_INTERVAL + fi + done + fi if ! wait_for_patch_transition "$modname" ; then + show_stalled_processes echo "module $modname did not complete its transition, unloading..." unload_module "$modname" die "error: failed to load module $modname (transition stalled)" @@ -352,7 +341,10 @@ disable_patch () { local modname="$1" local enabled="$SYSFS/$modname/enabled" - [[ -e "$enabled" ]] || die "patch module $1 is not loaded" + if ! [[ -e "$enabled" ]]; then + warn "patch module $modname is not loaded" + return 1 + fi if [[ "$(cat "$enabled")" -eq 1 ]]; then echo "disabling patch module: $modname" @@ -362,35 +354,44 @@ disable_patch () { [[ -z "$out" ]] && break echo "$out" 1>&2 if [[ ! "$out" =~ "Device or resource busy" ]]; then - die "failed to disable module $modname" + return 1 fi # "Device or resource busy" means the activeness safety check # failed. Retry in a few seconds. i=$((i+1)) if [[ $i -eq $MAX_LOAD_ATTEMPTS ]]; then - die "failed to disable module $modname" + return 1 else warn "retrying..." sleep $RETRY_INTERVAL fi done fi +} + +disable_patch_strict () { + local modname="$1" + + disable_patch "$modname" || die "failed to disable module $modname" if ! wait_for_patch_transition "$modname" ; then die "transition stalled for $modname" fi } +remove_module () { + echo "unloading patch module: $1" + # ignore any error here because rmmod can fail if the module used + # KPATCH_FORCE_UNSAFE. + rmmod "$1" 2> /dev/null || return 0 +} + unload_module () { PATCH="${1//-/_}" PATCH="${PATCH%.ko}" - disable_patch "$PATCH" - - echo "unloading patch module: $PATCH" - # ignore any error here because rmmod can fail if the module used - # KPATCH_FORCE_UNSAFE. - rmmod "$PATCH" 2> /dev/null || return 0 + disable_patch_strict "$PATCH" + remove_module "$PATCH" } get_module_version() { @@ -428,10 +429,39 @@ case "$1" in [[ "$#" -ne 2 ]] && usage case "$2" in "--all") + # Versions of linux < 5.1 livepatching require patches to be + # disabled in the inverse order in which they were enabled. + while true; do + nr_disabled=0 + for module in "$SYSFS"/*; do + modname="$(basename "$module")" + + [[ -e "$module" ]] || continue + disable_patch "$modname" || continue + if ! wait_for_patch_transition "$modname" ; then + warn "transition stalled for $modname" + continue + fi + remove_module "$modname" + nr_disabled=$((nr_disabled + 1)) + done + if [ $nr_disabled -eq 0 ]; then + break + fi + done + + nr_remaining=0 for module in "$SYSFS"/*; do + modname="$(basename "$module")" + [[ -e "$module" ]] || continue - unload_module "$(basename "$module")" || die "failed to unload module $module" + nr_remaining=$((nr_remaining + 1)) + warn "failed to unload module $modname" done + + if [ $nr_remaining -gt 0 ]; then + exit 1 + fi ;; *) unload_module "$(basename "$2")" || die "failed to unload module $2" @@ -469,7 +499,7 @@ case "$1" in echo "installing $PATCH ($KVER)" mkdir -p "$INSTALLDIR/$KVER" || die "failed to create install directory" cp -f "$PATCH" "$INSTALLDIR/$KVER" || die "failed to install module $PATCH" - systemctl enable kpatch.service + command -v systemctl > /dev/null 2>&1 && systemctl enable kpatch.service ;; "uninstall") diff --git a/man/kpatch-build.1 b/man/kpatch-build.1 index 81d90a3..35bc3c3 100644 --- a/man/kpatch-build.1 +++ b/man/kpatch-build.1 @@ -11,9 +11,6 @@ currently running and creates a kernel module that will replace modified functions in the kernel such that the patched code takes effect. -This script currently only works on Fedora and will need to be adapted -to work on other distros. - .SH OPTIONS -h|--help @@ -50,6 +47,10 @@ to work on other distros. Keep scratch files in /tmp (can be specified multiple times) +-e|--oot-module + Enable patching out-of-tree module, + specify current version of module + --skip-cleanup Skip post-build cleanup diff --git a/man/kpatch.1 b/man/kpatch.1 index 511f646..a30bdb7 100644 --- a/man/kpatch.1 +++ b/man/kpatch.1 @@ -35,6 +35,9 @@ info list list installed patch modules +signal + signal/poke any process stalling the current patch transition + version display the kpatch version diff --git a/test/difftree.sh b/test/difftree.sh index 251147e..9a0ebf4 100755 --- a/test/difftree.sh +++ b/test/difftree.sh @@ -14,17 +14,18 @@ #set -x OBJDIR="$HOME/.kpatch/obj" -SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" +# shellcheck disable=SC2046 +SCRIPTDIR=$(readlink -f $(dirname $(type -p "$0"))) TEMPDIR=$(mktemp -d) RESULTSDIR="$TEMPDIR/results" -VMVLINUX="/usr/lib/debug/lib/modules/$(uname -r)/vmlinux" # path for F20 if [[ ! -d $OBJDIR ]]; then echo "please run kpatch-build to populate the object tree in $OBJDIR" fi cd "$OBJDIR" || exit 1 -for i in $(find * -name '*.o') +# shellcheck disable=SC2044 +for i in $(find ./* -name '*.o') do # copied from kpatch-build/kpatch-gcc; keep in sync case $i in @@ -46,13 +47,14 @@ do ;; esac # skip objects that are the linked product of more than one object file - [[ $(eu-readelf -s $i | grep FILE | wc -l) -ne 1 ]] && continue - $SCRIPTDIR/../kpatch-build/create-diff-object $i $i /usr/lib/debug/lib/modules/$(uname -r)/vmlinux "$TEMPDIR/output.o" > "$TEMPDIR/log.txt" 2>&1 + [[ $(readelf -s "$i" | awk '$4=="FILE" {n++} END {print n}') -ne 1 ]] && continue + "$SCRIPTDIR"/../kpatch-build/create-diff-object "$i" "$i" "/usr/lib/debug/lib/modules/$(uname -r)/vmlinux" "$TEMPDIR/output.o" > "$TEMPDIR/log.txt" 2>&1 RETCODE=$? # expect RETCODE to be 3 indicating no change [[ $RETCODE -eq 3 ]] && continue # otherwise record error - mkdir -p $RESULTSDIR/$(dirname $i) || exit 1 + # shellcheck disable=SC2046 + mkdir -p "$RESULTSDIR"/$(dirname "$i") || exit 1 cp "$i" "$RESULTSDIR/$i" || exit 1 case $RETCODE in 139) @@ -82,9 +84,10 @@ rm -f "$TEMPDIR/log.txt" > /dev/null 2>&1 cd "$RESULTSDIR" || exit 1 echo "" echo "Results:" -for i in $(find * -iname '*.log') +# shellcheck disable=SC2044 +for i in $(find ./* -iname '*.log') do - echo $(cat $i | head -1 | cut -f2-3 -d':') + head -1 "$i" | cut -f2-3 -d':' done | sort | uniq -c | sort -n -r | tee "$TEMPDIR/results.log" echo "results are in $TEMPDIR" diff --git a/test/integration/Makefile b/test/integration/Makefile index a1abff6..fb6e601 100644 --- a/test/integration/Makefile +++ b/test/integration/Makefile @@ -18,6 +18,14 @@ quick: clean cached: ./kpatch-test -d $(PATCH_DIR) --cached $(PATCHES) +vagrant: vagrant-quick + +vagrant-quick: + ./test-vagrant + +vagrant-slow: + ./test-vagrant --slow + clean: rm -f *.ko *.log COMBINED.patch diff --git a/test/integration/centos-7~39d30e8... kpatch-0.9.2 base b/test/integration/centos-7~39d30e8... kpatch-0.9.2 base new file mode 120000 index 0000000..66d70af --- /dev/null +++ b/test/integration/centos-7~39d30e8... kpatch-0.9.2 base @@ -0,0 +1 @@ +rhel-7.8/ \ No newline at end of file diff --git a/test/integration/centos-8 b/test/integration/centos-8 new file mode 120000 index 0000000..64546c1 --- /dev/null +++ b/test/integration/centos-8 @@ -0,0 +1 @@ +rhel-8.2 \ No newline at end of file diff --git a/test/integration/common/multiple.template b/test/integration/common/multiple.template new file mode 100755 index 0000000..678279c --- /dev/null +++ b/test/integration/common/multiple.template @@ -0,0 +1,80 @@ +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" +ROOTDIR="$(readlink -f $SCRIPTDIR/../../..)" +KPATCH="sudo $ROOTDIR/kpatch/kpatch" + +MODULE_PREFIX="test-" +MODULE_POSTFIX=".ko" +TEST_POSTFIX="-LOADED.test" +PATCH_POSTFIX=".patch" +DISABLED_POSTFIX="${PATCH_POSTFIX}.disabled" + +set -o errexit + +declare -a loaded_modules + +cleanup_modules() { + for ((idx=${#loaded_modules[@]}-1 ; idx>=0 ; idx--)); do + mod=${loaded_modules[idx]} + $KPATCH unload $mod + done +} + +die_clean() { + cleanup_modules + exit 1 +} + +die() { + echo "ERROR: $@" >&2 + die_clean +} + +ko_to_test() { + tmp=${1%${MODULE_POSTFIX}}${TEST_POSTFIX} + echo ${tmp#${MODULE_PREFIX}} +} + +# make sure any modules added here are disjoint +declare -a modules + +for file in "${SCRIPTDIR}"/*"${TEST_POSTFIX}"; do + name=$(basename ${file}) + skip=0 + for bname in "${blacklist[@]}"; do + if [ "${bname}" == "${name}" ]; then + skip=1 + break + fi + done + if ! [ -e ${file/${TEST_POSTFIX}/${PATCH_POSTFIX}} ] && \ + [ -e ${file/${TEST_POSTFIX}/${DISABLED_POSTFIX}} ]; then + skip=1 + fi + if [ ${skip} -eq 0 ]; then + modules+=(${MODULE_PREFIX}${name%${TEST_POSTFIX}}${MODULE_POSTFIX}) + fi +done + +for mod in "${modules[@]}"; do + testprog=$(ko_to_test $mod) + $SCRIPTDIR/$testprog && die "$SCRIPTDIR/$testprog succeeded before loading any modules" +done + +for mod in "${modules[@]}"; do + $KPATCH load $mod || die_clean + loaded_modules+=($mod) +done + +for mod in "${modules[@]}"; do + testprog=$(ko_to_test $mod) + $SCRIPTDIR/$testprog || die "$SCRIPTDIR/$testprog failed after loading modules" +done + +cleanup_modules + +for mod in "${modules[@]}"; do + testprog=$(ko_to_test $mod) + $SCRIPTDIR/$testprog && die "$SCRIPTDIR/$testprog succeeded after unloading modules" +done + +exit 0 diff --git a/test/integration/fedora-27/meminfo-cmdline-rebuild-SLOW-LOADED.test.disabled b/test/integration/fedora-27/meminfo-cmdline-rebuild-SLOW-LOADED.test.disabled new file mode 100755 index 0000000..e2b647d --- /dev/null +++ b/test/integration/fedora-27/meminfo-cmdline-rebuild-SLOW-LOADED.test.disabled @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo && grep kpatch=1 /proc/cmdline diff --git a/test/integration/fedora-27/meminfo-cmdline-rebuild-SLOW.patch.disabled b/test/integration/fedora-27/meminfo-cmdline-rebuild-SLOW.patch.disabled new file mode 100644 index 0000000..a0a183b --- /dev/null +++ b/test/integration/fedora-27/meminfo-cmdline-rebuild-SLOW.patch.disabled @@ -0,0 +1,40 @@ +Disabled due to https://github.com/dynup/kpatch/issues/767 +--- +Index: src/fs/proc/cmdline.c +=================================================================== +--- src.orig/fs/proc/cmdline.c ++++ src/fs/proc/cmdline.c +@@ -7,7 +7,7 @@ + static int cmdline_proc_show(struct seq_file *m, void *v) + { + seq_puts(m, saved_command_line); +- seq_putc(m, '\n'); ++ seq_puts(m, " kpatch=1\n"); + return 0; + } + +Index: src/fs/proc/meminfo.c +=================================================================== +--- src.orig/fs/proc/meminfo.c ++++ src/fs/proc/meminfo.c +@@ -120,7 +120,7 @@ static int meminfo_proc_show(struct seq_ + seq_printf(m, "VmallocTotal: %8lu kB\n", + (unsigned long)VMALLOC_TOTAL >> 10); + show_val_kb(m, "VmallocUsed: ", 0ul); +- show_val_kb(m, "VmallocChunk: ", 0ul); ++ show_val_kb(m, "VMALLOCCHUNK: ", 0ul); + + #ifdef CONFIG_MEMORY_FAILURE + seq_printf(m, "HardwareCorrupted: %5lu kB\n", +Index: src/include/linux/kernel.h +=================================================================== +--- src.orig/include/linux/kernel.h ++++ src/include/linux/kernel.h +@@ -3,6 +3,7 @@ + #define _LINUX_KERNEL_H + + ++ + #include + #include + #include diff --git a/test/integration/fedora-27/module-shadow.patch.disabled b/test/integration/fedora-27/module-shadow.patch.disabled new file mode 100644 index 0000000..37c7997 --- /dev/null +++ b/test/integration/fedora-27/module-shadow.patch.disabled @@ -0,0 +1,24 @@ +diff -Nupr src.orig/arch/x86/kvm/vmx.c src/arch/x86/kvm/vmx.c +--- src.orig/arch/x86/kvm/vmx.c 2017-11-17 15:58:19.369211972 -0500 ++++ src/arch/x86/kvm/vmx.c 2017-11-17 15:59:29.615211972 -0500 +@@ -11259,10 +11259,20 @@ static void vmx_leave_nested(struct kvm_ + * It should only be called before L2 actually succeeded to run, and when + * vmcs01 is current (it doesn't leave_guest_mode() or switch vmcss). + */ ++#include "kpatch.h" + static void nested_vmx_entry_failure(struct kvm_vcpu *vcpu, + struct vmcs12 *vmcs12, + u32 reason, unsigned long qualification) + { ++ int *kpatch; ++ ++ kpatch = kpatch_shadow_alloc(vcpu, "kpatch", sizeof(*kpatch), ++ GFP_KERNEL); ++ if (kpatch) { ++ kpatch_shadow_get(vcpu, "kpatch"); ++ kpatch_shadow_free(vcpu, "kpatch"); ++ } ++ + load_vmcs12_host_state(vcpu, vmcs12); + vmcs12->vm_exit_reason = reason | VMX_EXIT_REASONS_FAILED_VMENTRY; + vmcs12->exit_qualification = qualification; diff --git a/test/integration/fedora-27/multiple.test b/test/integration/fedora-27/multiple.test index 2d5ed9f..a7ea608 100755 --- a/test/integration/fedora-27/multiple.test +++ b/test/integration/fedora-27/multiple.test @@ -1,45 +1,7 @@ #!/bin/bash SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" -ROOTDIR="$(readlink -f $SCRIPTDIR/../../..)" -KPATCH="sudo $ROOTDIR/kpatch/kpatch" -set -o errexit +declare -a blacklist=(meminfo-cmdline-rebuild-SLOW-LOADED.test) -die() { - echo "ERROR: $@" >&2 - exit 1 -} - -ko_to_test() { - tmp=${1%.ko}-LOADED.test - echo ${tmp#kpatch-} -} - -# make sure any modules added here are disjoint -declare -a modules=(kpatch-cmdline-string.ko kpatch-meminfo-string.ko) - -for mod in "${modules[@]}"; do - testprog=$(ko_to_test $mod) - $SCRIPTDIR/$testprog && die "$SCRIPTDIR/$testprog succeeded before loading any modules" -done - -for mod in "${modules[@]}"; do - $KPATCH load $mod -done - -for mod in "${modules[@]}"; do - testprog=$(ko_to_test $mod) - $SCRIPTDIR/$testprog || die "$SCRIPTDIR/$testprog failed after loading modules" -done - -for mod in "${modules[@]}"; do - $KPATCH unload $mod -done - -for mod in "${modules[@]}"; do - testprog=$(ko_to_test $mod) - $SCRIPTDIR/$testprog && die "$SCRIPTDIR/$testprog succeeded after unloading modules" -done - -exit 0 +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/fedora-27/shadow-newpid.patch.disabled b/test/integration/fedora-27/shadow-newpid.patch.disabled new file mode 100644 index 0000000..1a2c1b6 --- /dev/null +++ b/test/integration/fedora-27/shadow-newpid.patch.disabled @@ -0,0 +1,78 @@ +Index: src/fs/proc/array.c +=================================================================== +--- src.orig/fs/proc/array.c ++++ src/fs/proc/array.c +@@ -363,12 +363,19 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include "kpatch.h" + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw); + seq_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw); + seq_putc(m, '\n'); ++ ++ newpid = kpatch_shadow_get(p, "newpid"); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +Index: src/kernel/exit.c +=================================================================== +--- src.orig/kernel/exit.c ++++ src/kernel/exit.c +@@ -760,6 +760,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include "kpatch.h" + void __noreturn do_exit(long code) + { + struct task_struct *tsk = current; +@@ -865,6 +866,8 @@ void __noreturn do_exit(long code) + exit_task_work(tsk); + exit_thread(tsk); + ++ kpatch_shadow_free(tsk, "newpid"); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +Index: src/kernel/fork.c +=================================================================== +--- src.orig/kernel/fork.c ++++ src/kernel/fork.c +@@ -2062,6 +2062,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include "kpatch.h" + long _do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -2074,6 +2075,8 @@ long _do_fork(unsigned long clone_flags, + struct task_struct *p; + int trace = 0; + long nr; ++ int *newpid; ++ static int ctr = 0; + + /* + * Determine whether and which event to report to ptracer. When +@@ -2100,6 +2103,11 @@ long _do_fork(unsigned long clone_flags, + if (IS_ERR(p)) + return PTR_ERR(p); + ++ newpid = kpatch_shadow_alloc(p, "newpid", sizeof(*newpid), ++ GFP_KERNEL); ++ if (newpid) ++ *newpid = ctr++; ++ + /* + * Do this prior waking up the new thread - the thread pointer + * might get invalid after that point, if the thread exits quickly. diff --git a/test/integration/kpatch-test b/test/integration/kpatch-test index 17cb7fb..4359b46 100755 --- a/test/integration/kpatch-test +++ b/test/integration/kpatch-test @@ -28,8 +28,6 @@ # # - foo.patch: patch that should build successfully # -# - foo-SLOW.patch: patch that should be skipped in the quick test -# # - bar-FAIL.patch: patch that should fail to build # # - foo-LOADED.test: executable which tests whether the foo.patch module is @@ -42,16 +40,17 @@ shopt -s nullglob -SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" -ROOTDIR="$(readlink -f $SCRIPTDIR/../..)" -# TODO: option to use system-installed binaries instead +# shellcheck disable=SC2046 +SCRIPTDIR=$(readlink -f $(dirname $(type -p "$0"))) +ROOTDIR=$(readlink -f "$SCRIPTDIR/../..") KPATCH="sudo $ROOTDIR/kpatch/kpatch" -RMMOD="sudo rmmod" unset CCACHE_HASHDIR KPATCHBUILD="$ROOTDIR"/kpatch-build/kpatch-build ERROR=0 LOG=test.log -rm -f $LOG +DYNDEBUG_CONTROL=/sys/kernel/debug/dynamic_debug/control +DYNDEBUG_ENABLED=1 +rm -f ./*.log PATCHDIR="${PATCHDIR:-$PWD}" declare -a PATCH_LIST @@ -63,10 +62,12 @@ usage() { echo " -h, --help Show this help message" >&2 echo " -c, --cached Don't rebuild patch modules" >&2 echo " -d, --directory Patch directory" >&2 - echo " -q, --quick Just combine all patches into one module for testing" >&2 + echo " -q, --quick Test combined patch and -FAIL patches only" >&2 + echo " --system-kpatch-tools Use kpatch tools installed in the system" >&2 + echo " --kpatch-build-opts Additional options to pass to kpatch-build" >&2 } -options=$(getopt -o hcd:q -l "help,cached,directory,quick" -- "$@") || exit 1 +options=$(getopt -o hcd:q -l "help,cached,directory,quick,system-kpatch-tools,kpatch-build-opts:" -- "$@") || exit 1 eval set -- "$options" @@ -86,6 +87,14 @@ while [[ $# -gt 0 ]]; do -q|--quick) QUICK=1 ;; + --system-kpatch-tools) + KPATCH="sudo kpatch" + KPATCHBUILD="kpatch-build" + ;; + --kpatch-build-opts) + KPATCHBUILD_OPTS=$2 + shift + ;; *) [[ "$1" = "--" ]] && shift && continue PATCH_LIST+=("$1") @@ -95,8 +104,8 @@ while [[ $# -gt 0 ]]; do done if [[ ${#PATCH_LIST[@]} = 0 ]]; then - PATCH_LIST=($PATCHDIR/*.patch) - TEST_LIST=($PATCHDIR/*.test) + PATCH_LIST=("$PATCHDIR"/*.patch) + TEST_LIST=("$PATCHDIR"/*.test) if [[ ${#PATCH_LIST[@]} = 0 ]]; then echo "No patches found!" exit 1 @@ -106,12 +115,11 @@ else prefix=${file%%.patch} [[ -e "$prefix-FAIL.test" ]] && TEST_LIST+=("$prefix-FAIL.test") [[ -e "$prefix-LOADED.test" ]] && TEST_LIST+=("$prefix-LOADED.test") - [[ -e "$prefix-SLOW.test" ]] && TEST_LIST+=("$prefix-SLOW.test") done fi error() { - echo "ERROR: $@" |tee -a $LOG >&2 + echo "ERROR: $*" |tee -a $LOG >&2 ERROR=$((ERROR + 1)) } @@ -120,19 +128,12 @@ log() { } unload_all() { - for i in `/sbin/lsmod |egrep '^kpatch' |awk '{print $1}'`; do - if [[ $i != kpatch ]]; then - $KPATCH unload $i >> $LOG 2>&1 || error "\"kpatch unload $i\" failed" - fi - done - if /sbin/lsmod |egrep -q '^kpatch'; then - $RMMOD kpatch >> $LOG 2>&1 || error "\"rmmod kpatch\" failed" - fi + $KPATCH unload --all } build_module() { file=$1 - prefix=$(basename ${file%%.patch}) + prefix=$(basename "${file%%.patch}") modname="test-$prefix" module="${modname}.ko" @@ -151,8 +152,14 @@ build_module() { log "build: $prefix" - if ! $KPATCHBUILD -n $modname $file >> $LOG 2>&1; then - [[ $shouldfail -eq 0 ]] && error "$prefix: build failed" + # shellcheck disable=SC2086 + # KPATCHBUILD_OPTS may contain several space-separated options, + # it should remain without quotes. + if ! $KPATCHBUILD $KPATCHBUILD_OPTS -n "$modname" "$file" >> $LOG 2>&1; then + if [[ $shouldfail -eq 0 ]]; then + error "$prefix: build failed" + cp "$HOME/.kpatch/build.log" "$prefix.log" + fi else [[ $shouldfail -eq 1 ]] && error "$prefix: build succeeded when it should have failed" fi @@ -160,10 +167,10 @@ build_module() { run_load_test() { file=$1 - prefix=$(basename ${file%%.patch}) + prefix=$(basename "${file%%.patch}") modname="test-$prefix" module="${modname}.ko" - testprog="$(dirname $1)/$prefix-LOADED.test" + testprog=$(dirname "$1")/"$prefix-LOADED.test" [[ $prefix =~ -FAIL ]] && return @@ -184,7 +191,7 @@ run_load_test() { return fi - if ! $KPATCH load $module >> $LOG 2>&1; then + if ! $KPATCH load "$module" >> $LOG 2>&1; then error "$prefix: kpatch load failed" return fi @@ -193,7 +200,7 @@ run_load_test() { error "$prefix: $testprog failed after kpatch load" fi - if ! $KPATCH unload $module >> $LOG 2>&1; then + if ! $KPATCH unload "$module" >> $LOG 2>&1; then error "$prefix: kpatch unload failed" return fi @@ -206,7 +213,7 @@ run_load_test() { run_custom_test() { testprog=$1 - prefix=$(basename ${file%%.test}) + prefix=$(basename "${testprog%%.test}") [[ $testprog = *-LOADED.test ]] && return @@ -227,8 +234,7 @@ build_combined_module() { declare -a COMBINED_LIST for file in "${PATCH_LIST[@]}"; do [[ $file =~ -FAIL ]] && log "combine: skipping $file" && continue - [[ $file =~ -SLOW ]] && log "combine: skipping $file" && continue - COMBINED_LIST+=($file) + COMBINED_LIST+=("$file") done if [[ ${#COMBINED_LIST[@]} -le 1 ]]; then log "skipping build: combined (only ${#PATCH_LIST[@]} patch(es))" @@ -237,8 +243,10 @@ build_combined_module() { log "build: combined module" - if ! $KPATCHBUILD -n test-COMBINED "${COMBINED_LIST[@]}" >> $LOG 2>&1; then + # shellcheck disable=SC2086 + if ! $KPATCHBUILD $KPATCHBUILD_OPTS -n test-COMBINED "${COMBINED_LIST[@]}" >> $LOG 2>&1; then error "combined build failed" + cp "$HOME/.kpatch/build.log" combined.log fi } @@ -267,6 +275,7 @@ run_combined_test() { for testprog in "${TEST_LIST[@]}"; do [[ $testprog != *-LOADED.test ]] && continue + [ -e "${testprog/-LOADED.test/.patch.disabled}" ] && continue if ! $testprog >> $LOG 2>&1; then error "combined: $testprog failed after kpatch load" fi @@ -287,22 +296,45 @@ run_combined_test() { } -echo "clearing printk buffer" -sudo dmesg -C +# save existing dmesg so we can detect new content +save_dmesg() { + SAVED_DMESG="$(dmesg | tail -n1)" +} -if [[ $QUICK != 1 ]]; then - for file in "${PATCH_LIST[@]}"; do - build_module $file - done +# new dmesg entries since our saved entry +new_dmesg() { + if ! dmesg | awk -v last="$SAVED_DMESG" 'p; $0 == last{p=1} END {exit !p}'; then + error "dmesg overflow, try increasing kernel log buffer size" + fi +} + +# shellcheck disable=SC1091 +source /etc/os-release +if [[ "${ID}" == "rhel" && "${VERSION_ID%%.*}" == "7" && "${VERSION_ID##*.}" -le "6" ]]; then + DYNDEBUG_ENABLED=0 + echo "Dynamic debug is not supported on '${PRETTY_NAME}', disabling." fi +for file in "${PATCH_LIST[@]}"; do + if [[ $QUICK != 1 || "$file" =~ -FAIL ]]; then + build_module "$file" + fi +done + build_combined_module unload_all +save_dmesg + +if [ "${DYNDEBUG_ENABLED}" == "1" ]; then + prev_dyndebug=$(grep klp_try_switch_task "${DYNDEBUG_CONTROL}" | awk '{print $3;}') + echo "func klp_try_switch_task +p" >"${DYNDEBUG_CONTROL}" 2>/dev/null +fi + if [[ $QUICK != 1 ]]; then for file in "${PATCH_LIST[@]}"; do - run_load_test $file + run_load_test "$file" done fi @@ -310,15 +342,24 @@ run_combined_test if [[ $QUICK != 1 ]]; then for testprog in "${TEST_LIST[@]}"; do - unload_all - run_custom_test $testprog + if [[ ! $testprog =~ -FAIL ]]; then + unload_all + run_custom_test "$testprog" + fi done fi unload_all -dmesg |grep -q "Call Trace" && error "kernel error detected in printk buffer" +if [ "${DYNDEBUG_ENABLED}" == "1" ]; then + echo "func klp_try_switch_task ${prev_dyndebug}" >"${DYNDEBUG_CONTROL}" 2>/dev/null +fi + +if new_dmesg | grep -q "Call Trace"; then + new_dmesg > dmesg.log + error "kernel error detected in printk buffer" +fi if [[ $ERROR -gt 0 ]]; then log "$ERROR errors encountered" diff --git a/test/integration/lib.sh b/test/integration/lib.sh new file mode 100644 index 0000000..9f675a3 --- /dev/null +++ b/test/integration/lib.sh @@ -0,0 +1,326 @@ +#!/bin/bash + +kpatch_set_ccache_max_size() +{ + local ccache_max_size=${1:-10G} + + ccache --max-size="${ccache_max_size}" +} + +kpatch_fedora_dependencies() +{ + local kernel_version + kernel_version=$(uname -r) + + sudo dnf install -y gcc "kernel-devel-${kernel_version%.*}" elfutils elfutils-devel + sudo dnf install -y pesign yum-utils openssl wget numactl-devel + sudo dnf builddep -y "kernel-${kernel_version%.*}" + sudo dnf debuginfo-install -y "kernel-${kernel_version%.*}" + + sudo dnf install -y ccache +} + +kpatch_ubuntu_dependencies() +{ + sudo sed -i 's/# deb-src/deb-src/' /etc/apt/sources.list + sudo apt-get update + + sudo apt-get install -y make gcc libelf-dev elfutils + sudo apt-get install -y dpkg-dev devscripts + sudo apt-get build-dep -y linux + + sudo apt-get install -y ccache + + # Add ddebs repository + if ! grep -q 'ddebs.ubuntu.com' /etc/apt/sources.list.d/ddebs.list; then + local codename + codename=$(lsb_release -sc) + sudo tee /etc/apt/sources.list.d/ddebs.list <<-EOF + deb http://ddebs.ubuntu.com/ ${codename} main restricted universe multiverse + deb http://ddebs.ubuntu.com/ ${codename}-security main restricted universe multiverse + deb http://ddebs.ubuntu.com/ ${codename}-updates main restricted universe multiverse + deb http://ddebs.ubuntu.com/ ${codename}-proposed main restricted universe multiverse + EOF + + # add APT key + wget -Nq http://ddebs.ubuntu.com/dbgsym-release-key.asc -O- | sudo apt-key add - + sudo apt-get update + fi + sudo apt-get install -y "linux-image-$(uname -r)-dbgsym" +} + +kpatch_rhel_dependencies() +{ + local kernel_version + local arch + local rhel_major + local py_version + kernel_version=$(uname -r) + arch=$(uname -m) + rhel_major=${VERSION_ID%%.*} + + if [ "${rhel_major}" -ge 8 ]; then + py_version="3" + else + py_version="2" + fi + + sudo yum install -y git gcc gcc-c++ "kernel-devel-${kernel_version%.*}" elfutils elfutils-devel + sudo yum install -y yum-utils zlib-devel binutils-devel newt-devel \ + python${py_version}-devel perl-ExtUtils-Embed audit-libs-devel numactl-devel \ + pciutils-devel bison ncurses-devel rpm-build java-devel + sudo yum-builddep -y "kernel-${kernel_version%.*}" + sudo debuginfo-install -y "kernel-${kernel_version%.*}" + + case "${arch}" in + "x86_64") + sudo yum install -y pesign + ;; + "ppc64le") + sudo yum install -y gcc-plugin-devel + if [ "${rhel_major}" -ge 8 ]; then + # yum-builddep doesn't provide everything we need :( + sudo yum install -y flex openssl-devel + fi + ;; + *) + ;; + esac + + sudo yum install -y "https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm" + sudo yum install -y ccache + sudo yum remove -y epel-release +} + +kpatch_centos_dependencies() +{ + local kernel_version + local arch + kernel_version=$(uname -r) + arch=$(uname -m) + + sudo yum install -y gcc gcc-c++ "kernel-devel-${kernel_version%.*}" elfutils elfutils-devel + sudo yum install -y yum-utils zlib-devel binutils-devel newt-devel \ + python-devel perl-ExtUtils-Embed audit-libs-devel numactl-devel \ + pciutils-devel bison ncurses-devel rpm-build java-devel pesign + sudo yum-config-manager --enable debug + sudo yum-builddep -y "kernel-${kernel_version%.*}" + sudo debuginfo-install -y "kernel-${kernel_version%.*}" + + sudo yum install -y "https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm" + sudo yum install -y ccache + sudo yum remove -y epel-release +} + +kpatch_dependencies() +{ + # shellcheck disable=SC1091 + source /etc/os-release + + eval "kpatch_${ID}_dependencies" || { echo "Unsupported distro: ${ID}"; exit 1; } +} + +kpatch_separate_partition_cache() +{ + local partition=${1} + local mountpoint=${2} + local reformat=${3} + local owner=${USER} + + if [[ "${reformat}" == "y" ]]; then + sudo mkfs.xfs -f "${partition}" + fi + + sudo mkdir -p "${mountpoint}" + sudo mount "${partition}" "${mountpoint}" + sudo chown "${owner}":"${owner}" "${mountpoint}" + + rm -rf "${mountpoint}/.ccache" + rm -rf "${mountpoint}/.kpatch" + mkdir "${mountpoint}/.ccache" + mkdir "${mountpoint}/.kpatch" + + rm -rf "${HOME}/.ccache" + rm -rf "${HOME}/.kpatch" + + ln -sv "${mountpoint}/.ccache" "${HOME}/.ccache" + ln -sv "${mountpoint}/.kpatch" "${HOME}/.kpatch" +} + +kpatch_separate_disk_cache() +{ + local device=${1} + local mountpoint=${2} + local partition="${device}1" + + echo -e "o\\nn\\np\\n1\\n\\n\\nw\\n" | sudo fdisk "${device}" + kpatch_separate_partition_cache "${partition}" "${mountpoint}" y +} + +kpatch_install_vagrant_centos() +{ + local image_path=${1} + + sudo yum group install -y "Development Tools" + sudo yum -y install qemu-kvm libvirt virt-install bridge-utils libvirt-devel libxslt-devel libxml2-devel libvirt-devel libguestfs-tools-c libvirt-client + + echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-ipforward.conf + sudo sysctl -p /etc/sysctl.d/99-ipforward.conf + + sudo systemctl enable libvirtd + sudo systemctl start libvirtd || exit 1 + + if [[ -n "${image_path}" ]]; then + mkdir -p "${image_path}/libvirt/images" + virsh pool-define-as --target "${image_path}/libvirt/images" default dir || exit 1 + virsh pool-start default || exit 1 + fi + + sudo yum install -y https://releases.hashicorp.com/vagrant/2.1.2/vagrant_2.1.2_x86_64.rpm || exit 1 + + vagrant plugin install vagrant-libvirt +} + +kpatch_install_vagrant_rhel() +{ + local image_path=${1} + + kpatch_install_vagrant_centos "${image_path}" + + sudo systemctl enable nfs + sudo systemctl start nfs || exit 1 + +} + +kpatch_install_vagrant_fedora() +{ + local image_path=${1} + + echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-ipforward.conf + sudo sysctl -p /etc/sysctl.d/99-ipforward.conf + + sudo dnf install -y libvirt virt-install libvirt-client nfs-utils vagrant vagrant-libvirt + + echo "[nfsd]" | sudo tee -a /etc/nfs.conf + echo "udp=y" | sudo tee -a /etc/nfs.conf + echo "vers3=y" | sudo tee -a /etc/nfs.conf + sudo systemctl restart nfs + + sudo systemctl enable libvirtd + sudo systemctl start libvirtd || exit 1 + + if [[ -n "${image_path}" ]]; then + mkdir -p "${image_path}/libvirt/images" + virsh pool-define-as --target "${image_path}/libvirt/images" default dir || exit 1 + virsh pool-start default || exit 1 + fi +} + +kpatch_install_vagrant() +{ + local image_path=${1} + + # shellcheck disable=SC1091 + source /etc/os-release + + eval "kpatch_install_vagrant_${ID} ${image_path}" || { echo "Unsupported distro: ${ID}"; exit 1; } +} + +kpatch_check_install_vagrant() +{ + local image_path=${1} + # shellcheck disable=SC2230 + [ "$(which vagrant)" == "" ] && kpatch_install_vagrant "${image_path}" + return 0 +} + +kpatch_write_vagrantfile_template() +{ + local target_distro=${1} + + local box_prefix="kpatch" + + cat >Vagrantfile < '40G' + libvirt.cpus = $(getconf _NPROCESSORS_ONLN) + libvirt.memory = $(awk '/MemTotal/ {printf("%d\n", ($2*0.8)/1024)}' /proc/meminfo) + libvirt.graphics_type = "none" + libvirt.disk_bus = 'virtio' + libvirt.disk_device = 'vda' + end + config.vm.box = "${box_prefix}/${target_distro}" + config.vm.synced_folder ".", "/vagrant", type: "nfs" +EOF +} + +kpatch_write_vagrantfile_centos_provision() +{ + cat >>Vagrantfile <>Vagrantfile +} + +kpatch_integration_tests_vagrant_distro() +{ + local target_distro=${1} + local test_script=${2} + local slowtest=${3} + + local testdir + local workdir + local logdir + + testdir="$(pwd)" + workdir="${target_distro}.vagrant" + rm -rf "${workdir}" + mkdir -p "${workdir}" + cd "${workdir}" || exit 1 + + kpatch_write_vagrantfile "${target_distro}" + + vagrant up || { vagrant destroy -f; exit 1; } + + local test_cmd="KPATCH_GIT=${KPATCH_GIT} KPATCH_REV=${KPATCH_REV} bash /vagrant/runtest.sh" + if [ "${slowtest}" == "1" ]; then + test_cmd="${test_cmd} --slow" + fi + + cp "${test_script}" ./runtest.sh + vagrant ssh -c "${test_cmd}" + local rc=$? + + if [ $rc -eq 0 ]; then + echo "${target_distro} PASS" + else + echo "${target_distro} FAIL" + fi + + logdir="${testdir}/${target_distro}_log" + rm -rf "${logdir}" + mkdir -p "${logdir}" + cp logs/* "${logdir}" + + vagrant destroy -f + + cd "${testdir}" || exit 1 + if [ $rc -eq 0 ]; then + rm -rf "${workdir}" + fi + + return "${rc}" +} diff --git a/test/integration/rebase-patches b/test/integration/rebase-patches index 79d220c..64faed6 100755 --- a/test/integration/rebase-patches +++ b/test/integration/rebase-patches @@ -1,50 +1,56 @@ #!/bin/bash # -# rebase a set of patches, assumes the kernel has already been downloaded into -# the kpatch $CACHEDIR.Output patches go into ./${ID}-${VERSION_ID}/ +# rebase a set of integration test patches # # Example: # -# ./rebase-patches old_dir/*.patch - -CACHEDIR="${CACHEDIR:-$HOME/.kpatch}" -SRCDIR="$CACHEDIR/src" +# 1 - Extract kernel sources: +# +# % (rpm -ivh kernel-3.10.0-1127.el7.src.rpm; \ +# cd ~/rpmbuild/SPECS; \ +# rpmbuild --nodeps -bp kernel.spec) +# +# +# 2 - Rebase from previous release tests: +# +# % cd test/integration +# % SRCDIR="$HOME/rpmbuild/BUILD/kernel-3.10.0-1127.el7/linux-3.10.0-1127.el7.x86_64" \ +# ID=rhel VERSION_ID=7.8 ./rebase-patches rhel-7.7/*{.patch,.disabled} +# % cp rhel-7.7/*.test rhel-7.8/ -source /etc/os-release OUTDIR=$(pwd)/${ID}-${VERSION_ID} -mkdir -p $OUTDIR +mkdir -p "$OUTDIR" echo "* Making backup copy of kernel sources" -rm -rf ${SRCDIR}.orig -cp -r $SRCDIR ${SRCDIR}.orig - +rm -rf "${SRCDIR}.orig" +cp -r "$SRCDIR" "${SRCDIR}.orig" -for P in $@; do +for P in "$@"; do echo - echo "* Patch: $(basename $P)" + echo "* Patch: $(basename "$P")" echo "** dry run..." - patch -d $CACHEDIR --dry-run --quiet -p0 < $P - [[ $? -ne 0 ]] && echo "*** Skipping! ***" && continue + if ! patch -d "$SRCDIR" --dry-run --quiet -p1 < "$P"; then + echo "*** Skipping! ***" && continue + fi echo "** patching..." - patch -d $CACHEDIR -p0 --no-backup-if-mismatch < $P + patch -d "$SRCDIR" -p1 --no-backup-if-mismatch < "$P" - echo "** generating new $(basename $P)..." - NEWP=$OUTDIR/$(basename $P) - awk '/^diff|^patch/{exit} {print $LF}' $P > $NEWP - cd $CACHEDIR - diff -Nupr src.orig src >> $NEWP - cd - + echo "** generating new $(basename "$P")..." + NEWP="$OUTDIR"/$(basename "$P") + awk '/^Index|^diff|^patch/{exit} {print $LF}' "$P" > "$NEWP" + diff -Nupr "$SRCDIR.orig" "${SRCDIR}" >> "$NEWP" + sed -i "s#$SRCDIR#src#g" "$NEWP" echo "** reversing patch to restore tree..." - patch -d $CACHEDIR -p0 -R < $NEWP + patch -d "$SRCDIR" -p1 -R < "$NEWP" done echo "*** Removing backup copy of kernel sources" -rm -rf ${SRCDIR}.orig +rm -rf "${SRCDIR}.orig" echo echo "*** Done" diff --git a/test/integration/rhel-7.4/bug-table-section.patch b/test/integration/rhel-7.4/bug-table-section.patch new file mode 100644 index 0000000..71f8c1b --- /dev/null +++ b/test/integration/rhel-7.4/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/proc_sysctl.c 2017-09-22 15:27:21.769056469 -0400 +@@ -266,6 +266,8 @@ void sysctl_head_put(struct ctl_table_he + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-7.4/cmdline-string-LOADED.test b/test/integration/rhel-7.4/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-7.4/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-7.4/cmdline-string.patch b/test/integration/rhel-7.4/cmdline-string.patch new file mode 100644 index 0000000..749861f --- /dev/null +++ b/test/integration/rhel-7.4/cmdline-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/cmdline.c 2017-09-22 15:27:22.955061380 -0400 +@@ -5,7 +5,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_printf(m, "%s\n", saved_command_line); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-7.4/data-new-LOADED.test b/test/integration/rhel-7.4/data-new-LOADED.test new file mode 100755 index 0000000..598b6bb --- /dev/null +++ b/test/integration/rhel-7.4/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-7.4/data-new.patch b/test/integration/rhel-7.4/data-new.patch new file mode 100644 index 0000000..879828c --- /dev/null +++ b/test/integration/rhel-7.4/data-new.patch @@ -0,0 +1,28 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:24.102066130 -0400 +@@ -20,6 +20,8 @@ void __attribute__((weak)) arch_report_m + { + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -106,6 +108,7 @@ static int meminfo_proc_show(struct seq_ + #ifdef CONFIG_TRANSPARENT_HUGEPAGE + "AnonHugePages: %8lu kB\n" + #endif ++ "kpatch: %d" + , + K(i.totalram), + K(i.freeram), +@@ -167,6 +170,7 @@ static int meminfo_proc_show(struct seq_ + ,K(global_page_state(NR_ANON_TRANSPARENT_HUGEPAGES) * + HPAGE_PMD_NR) + #endif ++ ,foo + ); + + hugetlb_report_meminfo(m); diff --git a/test/integration/rhel-7.4/data-read-mostly.patch.disabled b/test/integration/rhel-7.4/data-read-mostly.patch.disabled new file mode 100644 index 0000000..611662f --- /dev/null +++ b/test/integration/rhel-7.4/data-read-mostly.patch.disabled @@ -0,0 +1,11 @@ +diff -Nupr src.orig/net/core/dev.c src/net/core/dev.c +--- src.orig/net/core/dev.c 2017-09-22 15:27:21.759056428 -0400 ++++ src/net/core/dev.c 2017-09-22 15:27:25.244070859 -0400 +@@ -4012,6 +4012,7 @@ ncls: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-7.4/fixup-section.patch b/test/integration/rhel-7.4/fixup-section.patch new file mode 100644 index 0000000..0e2022e --- /dev/null +++ b/test/integration/rhel-7.4/fixup-section.patch @@ -0,0 +1,12 @@ +diff --git a/fs/readdir.c b/fs/readdir.c +index fee38e04fae4..bce1e5ce74e5 100644 +--- a/fs/readdir.c ++++ b/fs/readdir.c +@@ -166,6 +166,7 @@ static int filldir(void * __buf, const char * name, int namlen, loff_t offset, + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-7.4/gcc-constprop.patch b/test/integration/rhel-7.4/gcc-constprop.patch new file mode 100644 index 0000000..63ba45d --- /dev/null +++ b/test/integration/rhel-7.4/gcc-constprop.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2017-09-22 15:27:21.602055778 -0400 ++++ src/kernel/time/timekeeping.c 2017-09-22 15:27:27.522080292 -0400 +@@ -877,6 +877,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-7.4/gcc-isra.patch b/test/integration/rhel-7.4/gcc-isra.patch new file mode 100644 index 0000000..a869797 --- /dev/null +++ b/test/integration/rhel-7.4/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/proc_sysctl.c 2017-09-22 15:27:28.670085046 -0400 +@@ -24,6 +24,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-7.4/gcc-mangled-3.patch b/test/integration/rhel-7.4/gcc-mangled-3.patch new file mode 100644 index 0000000..5828680 --- /dev/null +++ b/test/integration/rhel-7.4/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2017-09-22 15:27:21.618055844 -0400 ++++ src/mm/slub.c 2017-09-22 15:27:29.830089850 -0400 +@@ -5528,6 +5528,9 @@ void get_slabinfo(struct kmem_cache *s, + unsigned long nr_free = 0; + int node; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_online_node(node) { + struct kmem_cache_node *n = get_node(s, node); + diff --git a/test/integration/rhel-7.4/gcc-static-local-var-2.patch b/test/integration/rhel-7.4/gcc-static-local-var-2.patch new file mode 100644 index 0000000..4f653d7 --- /dev/null +++ b/test/integration/rhel-7.4/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/mmap.c src/mm/mmap.c +--- src.orig/mm/mmap.c 2017-09-22 15:27:21.618055844 -0400 ++++ src/mm/mmap.c 2017-09-22 15:27:31.024094794 -0400 +@@ -1687,6 +1688,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-7.4/gcc-static-local-var-3.patch b/test/integration/rhel-7.4/gcc-static-local-var-3.patch new file mode 100644 index 0000000..d87677b --- /dev/null +++ b/test/integration/rhel-7.4/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2017-09-22 15:27:21.601055773 -0400 ++++ src/kernel/sys.c 2017-09-22 15:27:32.170099540 -0400 +@@ -554,8 +554,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-7.4/gcc-static-local-var-4.patch b/test/integration/rhel-7.4/gcc-static-local-var-4.patch new file mode 100644 index 0000000..e22ead7 --- /dev/null +++ b/test/integration/rhel-7.4/gcc-static-local-var-4.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2017-09-22 15:27:21.702056192 -0400 ++++ src/fs/aio.c 2017-09-22 15:27:33.299104215 -0400 +@@ -219,9 +219,16 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(aio_ring_file->f_inode, 0); + diff --git a/test/integration/rhel-7.4/gcc-static-local-var-4.test b/test/integration/rhel-7.4/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-7.4/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-7.4/gcc-static-local-var-5.patch b/test/integration/rhel-7.4/gcc-static-local-var-5.patch new file mode 100644 index 0000000..540affa --- /dev/null +++ b/test/integration/rhel-7.4/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2017-09-22 15:27:21.602055778 -0400 ++++ src/kernel/audit.c 2017-09-22 15:27:34.429108894 -0400 +@@ -205,6 +205,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -215,6 +221,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -234,6 +241,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -282,6 +294,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-7.4/gcc-static-local-var-6.patch b/test/integration/rhel-7.4/gcc-static-local-var-6.patch new file mode 100644 index 0000000..bd5493c --- /dev/null +++ b/test/integration/rhel-7.4/gcc-static-local-var-6.patch @@ -0,0 +1,23 @@ +diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c +index a9d587a..23336ed 100644 +--- a/net/ipv6/netfilter.c ++++ b/net/ipv6/netfilter.c +@@ -106,6 +106,8 @@ static int nf_ip6_reroute(struct sk_buff *skb, + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -119,6 +121,9 @@ static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-7.4/gcc-static-local-var.patch b/test/integration/rhel-7.4/gcc-static-local-var.patch new file mode 100644 index 0000000..2dab9db --- /dev/null +++ b/test/integration/rhel-7.4/gcc-static-local-var.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/arch/x86/kernel/ldt.c src/arch/x86/kernel/ldt.c +--- src.orig/arch/x86/kernel/ldt.c 2017-09-22 15:27:20.847052651 -0400 ++++ src/arch/x86/kernel/ldt.c 2017-09-22 15:27:35.573113632 -0400 +@@ -98,6 +98,12 @@ static inline int copy_ldt(mm_context_t + return 0; + } + ++void hi_there(void) ++{ ++ if (!jiffies) ++ printk("hi there\n"); ++} ++ + /* + * we do not have to muck with descriptors here, that is + * done in switch_mm() as needed. +@@ -107,6 +113,8 @@ int init_new_context(struct task_struct + struct mm_struct *old_mm; + int retval = 0; + ++ hi_there(); ++ + mutex_init(&mm->context.lock); + mm->context.size = 0; + old_mm = current->mm; diff --git a/test/integration/rhel-7.4/macro-callbacks.patch b/test/integration/rhel-7.4/macro-callbacks.patch new file mode 100644 index 0000000..0d6831b --- /dev/null +++ b/test/integration/rhel-7.4/macro-callbacks.patch @@ -0,0 +1,160 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +--- src.old/fs/aio.c 2018-02-26 11:07:51.522610407 -0500 ++++ src/fs/aio.c 2018-03-05 11:17:21.560015449 -0500 +@@ -42,6 +42,50 @@ + #include + #include + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 +--- src.old/drivers/input/joydev.c 2018-02-26 11:07:49.470610407 -0500 ++++ src/drivers/input/joydev.c 2018-03-05 11:18:13.998015449 -0500 +@@ -954,3 +954,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +--- src.old/drivers/input/misc/pcspkr.c 2018-02-26 11:07:49.477610407 -0500 ++++ src/drivers/input/misc/pcspkr.c 2018-03-05 11:18:23.411015449 -0500 +@@ -136,3 +136,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); diff --git a/test/integration/rhel-7.4/macro-printk.patch b/test/integration/rhel-7.4/macro-printk.patch new file mode 100644 index 0000000..9f591f4 --- /dev/null +++ b/test/integration/rhel-7.4/macro-printk.patch @@ -0,0 +1,147 @@ +diff -Nupr src.orig/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c +--- src.orig/net/ipv4/fib_frontend.c 2017-09-22 16:52:10.646110299 -0400 ++++ src/net/ipv4/fib_frontend.c 2017-09-22 16:55:14.395870305 -0400 +@@ -633,6 +633,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) + { + struct net *net = sock_net(skb->sk); +@@ -651,6 +652,7 @@ static int inet_rtm_newroute(struct sk_b + } + + err = fib_table_insert(net, tb, &cfg); ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c +--- src.orig/net/ipv4/fib_semantics.c 2017-09-22 16:52:10.645110295 -0400 ++++ src/net/ipv4/fib_semantics.c 2017-09-22 16:54:05.175584004 -0400 +@@ -925,6 +925,7 @@ fib_convert_metrics(struct fib_info *fi, + return 0; + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg) + { + int err; +@@ -949,6 +950,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -969,6 +971,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (fi == NULL) +@@ -980,6 +983,7 @@ struct fib_info *fib_create_info(struct + } else + fi->fib_metrics = (u32 *) dst_default_metrics; + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); + + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; +@@ -996,8 +1000,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1048,6 +1054,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) +@@ -1065,6 +1072,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) + goto err_inval; +@@ -1087,6 +1095,7 @@ struct fib_info *fib_create_info(struct + goto failure; + } endfor_nexthops(fi) + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc) { + if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || +@@ -1099,6 +1108,7 @@ struct fib_info *fib_create_info(struct + fib_info_update_nh_saddr(net, nexthop_nh); + fib_add_weight(fi, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1110,6 +1120,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + atomic_inc(&fi->fib_clntref); +@@ -1133,6 +1144,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1143,6 +1155,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff -Nupr src.orig/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c +--- src.orig/net/ipv4/fib_trie.c 2017-09-22 16:52:10.645110295 -0400 ++++ src/net/ipv4/fib_trie.c 2017-09-22 16:55:39.940975963 -0400 +@@ -1191,6 +1191,7 @@ static int fib_insert_alias(struct trie + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg) + { +@@ -1216,11 +1217,14 @@ int fib_table_insert(struct net *net, st + if ((plen < KEYLENGTH) && (key << plen)) + return -EINVAL; + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority) : NULL; diff --git a/test/integration/rhel-7.4/meminfo-init-FAIL.patch b/test/integration/rhel-7.4/meminfo-init-FAIL.patch new file mode 100644 index 0000000..5df5225 --- /dev/null +++ b/test/integration/rhel-7.4/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:40.130132502 -0400 +@@ -191,6 +191,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.4/meminfo-init2-FAIL.patch b/test/integration/rhel-7.4/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..c030f61 --- /dev/null +++ b/test/integration/rhel-7.4/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:38.972127707 -0400 +@@ -30,6 +30,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long pages[NR_LRU_LISTS]; + int lru; + ++ printk("a\n"); + /* + * display in kilobytes. + */ +@@ -191,6 +192,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.4/meminfo-string-LOADED.test b/test/integration/rhel-7.4/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-7.4/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-7.4/meminfo-string.patch b/test/integration/rhel-7.4/meminfo-string.patch new file mode 100644 index 0000000..afdc5d0 --- /dev/null +++ b/test/integration/rhel-7.4/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:41.274137239 -0400 +@@ -99,7 +99,7 @@ static int meminfo_proc_show(struct seq_ + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" +- "VmallocChunk: %8lu kB\n" ++ "VMALLOCCHUNK: %8lu kB\n" + #ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" + #endif diff --git a/test/integration/rhel-7.4/module-call-external.patch b/test/integration/rhel-7.4/module-call-external.patch new file mode 100644 index 0000000..754d725 --- /dev/null +++ b/test/integration/rhel-7.4/module-call-external.patch @@ -0,0 +1,33 @@ +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2017-09-22 15:27:21.705056204 -0400 ++++ src/fs/nfsd/export.c 2017-09-22 15:27:42.411141948 -0400 +@@ -1184,6 +1184,8 @@ static void exp_flags(struct seq_file *m + } + } + ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; +@@ -1193,6 +1195,7 @@ static int e_show(struct seq_file *m, vo + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2017-09-22 15:27:21.754056407 -0400 ++++ src/net/netlink/af_netlink.c 2017-09-22 15:27:42.412141952 -0400 +@@ -3260,4 +3260,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-7.4/module-kvm-fixup.patch b/test/integration/rhel-7.4/module-kvm-fixup.patch new file mode 100644 index 0000000..174ad65 --- /dev/null +++ b/test/integration/rhel-7.4/module-kvm-fixup.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/arch/x86/kvm/vmx.c src/arch/x86/kvm/vmx.c +--- src.orig/arch/x86/kvm/vmx.c 2017-09-22 15:27:20.853052676 -0400 ++++ src/arch/x86/kvm/vmx.c 2017-09-22 15:27:43.583146801 -0400 +@@ -10597,6 +10597,8 @@ static int vmx_check_intercept(struct kv + struct x86_instruction_info *info, + enum x86_intercept_stage stage) + { ++ if (!jiffies) ++ printk("kpatch vmx_check_intercept\n"); + return X86EMUL_CONTINUE; + } + diff --git a/test/integration/rhel-7.4/module-shadow.patch b/test/integration/rhel-7.4/module-shadow.patch new file mode 100644 index 0000000..c7da353 --- /dev/null +++ b/test/integration/rhel-7.4/module-shadow.patch @@ -0,0 +1,24 @@ +diff -Nupr src.orig/arch/x86/kvm/vmx.c src/arch/x86/kvm/vmx.c +--- src.orig/arch/x86/kvm/vmx.c 2017-09-22 15:27:20.853052676 -0400 ++++ src/arch/x86/kvm/vmx.c 2017-09-22 15:27:44.742151601 -0400 +@@ -10581,10 +10581,20 @@ static void vmx_leave_nested(struct kvm_ + * It should only be called before L2 actually succeeded to run, and when + * vmcs01 is current (it doesn't leave_guest_mode() or switch vmcss). + */ ++#include "kpatch.h" + static void nested_vmx_entry_failure(struct kvm_vcpu *vcpu, + struct vmcs12 *vmcs12, + u32 reason, unsigned long qualification) + { ++ int *kpatch; ++ ++ kpatch = kpatch_shadow_alloc(vcpu, "kpatch", sizeof(*kpatch), ++ GFP_KERNEL); ++ if (kpatch) { ++ kpatch_shadow_get(vcpu, "kpatch"); ++ kpatch_shadow_free(vcpu, "kpatch"); ++ } ++ + load_vmcs12_host_state(vcpu, vmcs12); + vmcs12->vm_exit_reason = reason | VMX_EXIT_REASONS_FAILED_VMENTRY; + vmcs12->exit_qualification = qualification; diff --git a/test/integration/rhel-7.4/multiple.test b/test/integration/rhel-7.4/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-7.4/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-7.4/new-function.patch b/test/integration/rhel-7.4/new-function.patch new file mode 100644 index 0000000..cf47c83 --- /dev/null +++ b/test/integration/rhel-7.4/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2017-09-22 15:27:21.084053633 -0400 ++++ src/drivers/tty/n_tty.c 2017-09-22 15:27:45.888156346 -0400 +@@ -2016,7 +2016,7 @@ do_it_again: + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2098,6 +2098,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-7.4/new-globals.patch b/test/integration/rhel-7.4/new-globals.patch new file mode 100644 index 0000000..3d9d349 --- /dev/null +++ b/test/integration/rhel-7.4/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/cmdline.c 2017-09-22 15:27:47.028161067 -0400 +@@ -27,3 +27,10 @@ static int __init proc_cmdline_init(void + return 0; + } + module_init(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:47.029161071 -0400 +@@ -16,6 +16,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -53,6 +55,7 @@ static int meminfo_proc_show(struct seq_ + /* + * Tagged format, for easy grepping and expansion. + */ ++ kpatch_print_message(); + seq_printf(m, + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" diff --git a/test/integration/rhel-7.4/parainstructions-section.patch b/test/integration/rhel-7.4/parainstructions-section.patch new file mode 100644 index 0000000..809dce4 --- /dev/null +++ b/test/integration/rhel-7.4/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/generic.c 2017-09-22 15:27:48.190165879 -0400 +@@ -194,6 +194,7 @@ int proc_alloc_inum(unsigned int *inum) + unsigned int i; + int error; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + retry: + if (!ida_pre_get(&proc_inum_ida, GFP_KERNEL)) + return -ENOMEM; diff --git a/test/integration/rhel-7.4/replace-section-references.patch b/test/integration/rhel-7.4/replace-section-references.patch new file mode 100644 index 0000000..e41bba9 --- /dev/null +++ b/test/integration/rhel-7.4/replace-section-references.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2017-09-22 15:27:20.852052672 -0400 ++++ src/arch/x86/kvm/x86.c 2017-09-22 15:27:49.362170732 -0400 +@@ -248,6 +248,8 @@ static void shared_msr_update(unsigned s + + void kvm_define_shared_msr(unsigned slot, u32 msr) + { ++ if (!jiffies) ++ printk("kpatch kvm define shared msr\n"); + BUG_ON(slot >= KVM_NR_SHARED_MSRS); + shared_msrs_global.msrs[slot] = msr; + if (slot >= shared_msrs_global.nr) diff --git a/test/integration/rhel-7.4/shadow-newpid-LOADED.test b/test/integration/rhel-7.4/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-7.4/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-7.4/shadow-newpid.patch b/test/integration/rhel-7.4/shadow-newpid.patch new file mode 100644 index 0000000..cde1810 --- /dev/null +++ b/test/integration/rhel-7.4/shadow-newpid.patch @@ -0,0 +1,69 @@ +diff -Nupr src.orig/fs/proc/array.c src/fs/proc/array.c +--- src.orig/fs/proc/array.c 2017-09-22 16:52:10.597110096 -0400 ++++ src/fs/proc/array.c 2017-09-22 16:59:40.799972178 -0400 +@@ -359,13 +359,20 @@ static inline void task_seccomp(struct s + #endif + } + ++#include "kpatch.h" + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); ++ ++ newpid = kpatch_shadow_get(p, "newpid"); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff -Nupr src.orig/kernel/exit.c src/kernel/exit.c +--- src.orig/kernel/exit.c 2017-09-22 16:52:10.506109720 -0400 ++++ src/kernel/exit.c 2017-09-22 16:59:40.799972178 -0400 +@@ -715,6 +715,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include "kpatch.h" + void do_exit(long code) + { + struct task_struct *tsk = current; +@@ -812,6 +813,8 @@ void do_exit(long code) + check_stack_usage(); + exit_thread(); + ++ kpatch_shadow_free(tsk, "newpid"); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2017-09-22 16:52:10.504109711 -0400 ++++ src/kernel/fork.c 2017-09-22 17:00:44.938237460 -0400 +@@ -1700,6 +1700,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include "kpatch.h" + long do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -1737,6 +1738,13 @@ long do_fork(unsigned long clone_flags, + if (!IS_ERR(p)) { + struct completion vfork; + struct pid *pid; ++ int *newpid; ++ static int ctr = 0; ++ ++ newpid = kpatch_shadow_alloc(p, "newpid", sizeof(*newpid), ++ GFP_KERNEL); ++ if (newpid) ++ *newpid = ctr++; + + trace_sched_process_fork(current, p); + diff --git a/test/integration/rhel-7.4/smp-locks-section.patch b/test/integration/rhel-7.4/smp-locks-section.patch new file mode 100644 index 0000000..6f39d53 --- /dev/null +++ b/test/integration/rhel-7.4/smp-locks-section.patch @@ -0,0 +1,14 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2017-09-22 15:27:21.077053604 -0400 ++++ src/drivers/tty/tty_buffer.c 2017-09-22 15:27:50.542175618 -0400 +@@ -217,6 +217,10 @@ int tty_buffer_request_room(struct tty_p + /* OPTIMISATION: We could keep a per tty "zero" sized buffer to + remove this conditional if its worth it. This would be invisible + to the callers */ ++ ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b != NULL) + left = b->size - b->used; diff --git a/test/integration/rhel-7.4/special-static-2.patch b/test/integration/rhel-7.4/special-static-2.patch new file mode 100644 index 0000000..146d5b5 --- /dev/null +++ b/test/integration/rhel-7.4/special-static-2.patch @@ -0,0 +1,24 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2017-09-22 15:27:20.852052672 -0400 ++++ src/arch/x86/kvm/x86.c 2017-09-22 15:27:51.744180596 -0400 +@@ -2093,12 +2093,20 @@ static void record_steal_time(struct kvm + &vcpu->arch.st.steal, sizeof(struct kvm_steal_time)); + } + ++void kpatch_kvm_x86_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch kvm x86 foo\n"); ++} ++ + int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) + { + bool pr = false; + u32 msr = msr_info->index; + u64 data = msr_info->data; + ++ kpatch_kvm_x86_foo(); ++ + switch (msr) { + case MSR_AMD64_NB_CFG: + case MSR_IA32_UCODE_REV: diff --git a/test/integration/rhel-7.4/special-static.patch b/test/integration/rhel-7.4/special-static.patch new file mode 100644 index 0000000..84647ec --- /dev/null +++ b/test/integration/rhel-7.4/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2017-09-22 15:27:21.600055769 -0400 ++++ src/kernel/fork.c 2017-09-22 15:27:53.052186012 -0400 +@@ -1129,10 +1129,18 @@ static void posix_cpu_timers_init_group( + INIT_LIST_HEAD(&sig->cpu_timers[2]); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-7.4/tracepoints-section.patch b/test/integration/rhel-7.4/tracepoints-section.patch new file mode 100644 index 0000000..b770f9e --- /dev/null +++ b/test/integration/rhel-7.4/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/timer.c src/kernel/timer.c +--- src.orig/kernel/timer.c 2017-09-22 15:27:21.600055769 -0400 ++++ src/kernel/timer.c 2017-09-22 15:27:54.288191131 -0400 +@@ -1390,6 +1390,9 @@ static void run_timer_softirq(struct sof + { + struct tvec_base *base = __this_cpu_read(tvec_bases); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + if (time_after_eq(jiffies, base->timer_jiffies)) + __run_timers(base); + } diff --git a/test/integration/rhel-7.4/warn-detect-FAIL.patch b/test/integration/rhel-7.4/warn-detect-FAIL.patch new file mode 100644 index 0000000..8efa782 --- /dev/null +++ b/test/integration/rhel-7.4/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2017-09-22 15:27:20.852052672 -0400 ++++ src/arch/x86/kvm/x86.c 2017-09-22 15:27:55.489196104 -0400 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-7.5/bug-table-section.patch b/test/integration/rhel-7.5/bug-table-section.patch new file mode 100644 index 0000000..71f8c1b --- /dev/null +++ b/test/integration/rhel-7.5/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/proc_sysctl.c 2017-09-22 15:27:21.769056469 -0400 +@@ -266,6 +266,8 @@ void sysctl_head_put(struct ctl_table_he + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-7.5/cmdline-string-LOADED.test b/test/integration/rhel-7.5/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-7.5/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-7.5/cmdline-string.patch b/test/integration/rhel-7.5/cmdline-string.patch new file mode 100644 index 0000000..749861f --- /dev/null +++ b/test/integration/rhel-7.5/cmdline-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/cmdline.c 2017-09-22 15:27:22.955061380 -0400 +@@ -5,7 +5,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_printf(m, "%s\n", saved_command_line); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-7.5/data-new-LOADED.test b/test/integration/rhel-7.5/data-new-LOADED.test new file mode 100755 index 0000000..598b6bb --- /dev/null +++ b/test/integration/rhel-7.5/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-7.5/data-new.patch b/test/integration/rhel-7.5/data-new.patch new file mode 100644 index 0000000..879828c --- /dev/null +++ b/test/integration/rhel-7.5/data-new.patch @@ -0,0 +1,28 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:24.102066130 -0400 +@@ -20,6 +20,8 @@ void __attribute__((weak)) arch_report_m + { + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -106,6 +108,7 @@ static int meminfo_proc_show(struct seq_ + #ifdef CONFIG_TRANSPARENT_HUGEPAGE + "AnonHugePages: %8lu kB\n" + #endif ++ "kpatch: %d" + , + K(i.totalram), + K(i.freeram), +@@ -167,6 +170,7 @@ static int meminfo_proc_show(struct seq_ + ,K(global_page_state(NR_ANON_TRANSPARENT_HUGEPAGES) * + HPAGE_PMD_NR) + #endif ++ ,foo + ); + + hugetlb_report_meminfo(m); diff --git a/test/integration/rhel-7.5/data-read-mostly.patch.disabled b/test/integration/rhel-7.5/data-read-mostly.patch.disabled new file mode 100644 index 0000000..611662f --- /dev/null +++ b/test/integration/rhel-7.5/data-read-mostly.patch.disabled @@ -0,0 +1,11 @@ +diff -Nupr src.orig/net/core/dev.c src/net/core/dev.c +--- src.orig/net/core/dev.c 2017-09-22 15:27:21.759056428 -0400 ++++ src/net/core/dev.c 2017-09-22 15:27:25.244070859 -0400 +@@ -4012,6 +4012,7 @@ ncls: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-7.5/fixup-section.patch b/test/integration/rhel-7.5/fixup-section.patch new file mode 100644 index 0000000..18d13b5 --- /dev/null +++ b/test/integration/rhel-7.5/fixup-section.patch @@ -0,0 +1,12 @@ +diff --git a/fs/readdir.c b/fs/readdir.c +index febd02dfbe2d..064db7bd70d0 100644 +--- a/fs/readdir.c ++++ b/fs/readdir.c +@@ -176,6 +176,7 @@ static int filldir(void * __buf, const char * name, int namlen, loff_t offset, + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-7.5/gcc-constprop.patch b/test/integration/rhel-7.5/gcc-constprop.patch new file mode 100644 index 0000000..63ba45d --- /dev/null +++ b/test/integration/rhel-7.5/gcc-constprop.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2017-09-22 15:27:21.602055778 -0400 ++++ src/kernel/time/timekeeping.c 2017-09-22 15:27:27.522080292 -0400 +@@ -877,6 +877,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-7.5/gcc-isra.patch b/test/integration/rhel-7.5/gcc-isra.patch new file mode 100644 index 0000000..a869797 --- /dev/null +++ b/test/integration/rhel-7.5/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/proc_sysctl.c 2017-09-22 15:27:28.670085046 -0400 +@@ -24,6 +24,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-7.5/gcc-mangled-3.patch b/test/integration/rhel-7.5/gcc-mangled-3.patch new file mode 100644 index 0000000..5828680 --- /dev/null +++ b/test/integration/rhel-7.5/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2017-09-22 15:27:21.618055844 -0400 ++++ src/mm/slub.c 2017-09-22 15:27:29.830089850 -0400 +@@ -5528,6 +5528,9 @@ void get_slabinfo(struct kmem_cache *s, + unsigned long nr_free = 0; + int node; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_online_node(node) { + struct kmem_cache_node *n = get_node(s, node); + diff --git a/test/integration/rhel-7.5/gcc-static-local-var-2.patch b/test/integration/rhel-7.5/gcc-static-local-var-2.patch new file mode 100644 index 0000000..4f653d7 --- /dev/null +++ b/test/integration/rhel-7.5/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/mmap.c src/mm/mmap.c +--- src.orig/mm/mmap.c 2017-09-22 15:27:21.618055844 -0400 ++++ src/mm/mmap.c 2017-09-22 15:27:31.024094794 -0400 +@@ -1687,6 +1688,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-7.5/gcc-static-local-var-3.patch b/test/integration/rhel-7.5/gcc-static-local-var-3.patch new file mode 100644 index 0000000..d87677b --- /dev/null +++ b/test/integration/rhel-7.5/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2017-09-22 15:27:21.601055773 -0400 ++++ src/kernel/sys.c 2017-09-22 15:27:32.170099540 -0400 +@@ -554,8 +554,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-7.5/gcc-static-local-var-4.patch b/test/integration/rhel-7.5/gcc-static-local-var-4.patch new file mode 100644 index 0000000..e22ead7 --- /dev/null +++ b/test/integration/rhel-7.5/gcc-static-local-var-4.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2017-09-22 15:27:21.702056192 -0400 ++++ src/fs/aio.c 2017-09-22 15:27:33.299104215 -0400 +@@ -219,9 +219,16 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(aio_ring_file->f_inode, 0); + diff --git a/test/integration/rhel-7.5/gcc-static-local-var-4.test b/test/integration/rhel-7.5/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-7.5/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-7.5/gcc-static-local-var-5.patch b/test/integration/rhel-7.5/gcc-static-local-var-5.patch new file mode 100644 index 0000000..540affa --- /dev/null +++ b/test/integration/rhel-7.5/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2017-09-22 15:27:21.602055778 -0400 ++++ src/kernel/audit.c 2017-09-22 15:27:34.429108894 -0400 +@@ -205,6 +205,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -215,6 +221,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -234,6 +241,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -282,6 +294,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-7.5/gcc-static-local-var-6.patch b/test/integration/rhel-7.5/gcc-static-local-var-6.patch new file mode 100644 index 0000000..bd5493c --- /dev/null +++ b/test/integration/rhel-7.5/gcc-static-local-var-6.patch @@ -0,0 +1,23 @@ +diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c +index a9d587a..23336ed 100644 +--- a/net/ipv6/netfilter.c ++++ b/net/ipv6/netfilter.c +@@ -106,6 +106,8 @@ static int nf_ip6_reroute(struct sk_buff *skb, + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -119,6 +121,9 @@ static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-7.5/gcc-static-local-var.patch b/test/integration/rhel-7.5/gcc-static-local-var.patch new file mode 100644 index 0000000..2dab9db --- /dev/null +++ b/test/integration/rhel-7.5/gcc-static-local-var.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/arch/x86/kernel/ldt.c src/arch/x86/kernel/ldt.c +--- src.orig/arch/x86/kernel/ldt.c 2017-09-22 15:27:20.847052651 -0400 ++++ src/arch/x86/kernel/ldt.c 2017-09-22 15:27:35.573113632 -0400 +@@ -98,6 +98,12 @@ static inline int copy_ldt(mm_context_t + return 0; + } + ++void hi_there(void) ++{ ++ if (!jiffies) ++ printk("hi there\n"); ++} ++ + /* + * we do not have to muck with descriptors here, that is + * done in switch_mm() as needed. +@@ -107,6 +113,8 @@ int init_new_context(struct task_struct + struct mm_struct *old_mm; + int retval = 0; + ++ hi_there(); ++ + mutex_init(&mm->context.lock); + mm->context.size = 0; + old_mm = current->mm; diff --git a/test/integration/rhel-7.5/macro-callbacks.patch b/test/integration/rhel-7.5/macro-callbacks.patch new file mode 100644 index 0000000..0d6831b --- /dev/null +++ b/test/integration/rhel-7.5/macro-callbacks.patch @@ -0,0 +1,160 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +--- src.old/fs/aio.c 2018-02-26 11:07:51.522610407 -0500 ++++ src/fs/aio.c 2018-03-05 11:17:21.560015449 -0500 +@@ -42,6 +42,50 @@ + #include + #include + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 +--- src.old/drivers/input/joydev.c 2018-02-26 11:07:49.470610407 -0500 ++++ src/drivers/input/joydev.c 2018-03-05 11:18:13.998015449 -0500 +@@ -954,3 +954,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +--- src.old/drivers/input/misc/pcspkr.c 2018-02-26 11:07:49.477610407 -0500 ++++ src/drivers/input/misc/pcspkr.c 2018-03-05 11:18:23.411015449 -0500 +@@ -136,3 +136,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); diff --git a/test/integration/rhel-7.5/macro-printk.patch b/test/integration/rhel-7.5/macro-printk.patch new file mode 100644 index 0000000..9f591f4 --- /dev/null +++ b/test/integration/rhel-7.5/macro-printk.patch @@ -0,0 +1,147 @@ +diff -Nupr src.orig/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c +--- src.orig/net/ipv4/fib_frontend.c 2017-09-22 16:52:10.646110299 -0400 ++++ src/net/ipv4/fib_frontend.c 2017-09-22 16:55:14.395870305 -0400 +@@ -633,6 +633,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) + { + struct net *net = sock_net(skb->sk); +@@ -651,6 +652,7 @@ static int inet_rtm_newroute(struct sk_b + } + + err = fib_table_insert(net, tb, &cfg); ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c +--- src.orig/net/ipv4/fib_semantics.c 2017-09-22 16:52:10.645110295 -0400 ++++ src/net/ipv4/fib_semantics.c 2017-09-22 16:54:05.175584004 -0400 +@@ -925,6 +925,7 @@ fib_convert_metrics(struct fib_info *fi, + return 0; + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg) + { + int err; +@@ -949,6 +950,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -969,6 +971,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (fi == NULL) +@@ -980,6 +983,7 @@ struct fib_info *fib_create_info(struct + } else + fi->fib_metrics = (u32 *) dst_default_metrics; + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); + + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; +@@ -996,8 +1000,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1048,6 +1054,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) +@@ -1065,6 +1072,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) + goto err_inval; +@@ -1087,6 +1095,7 @@ struct fib_info *fib_create_info(struct + goto failure; + } endfor_nexthops(fi) + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc) { + if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || +@@ -1099,6 +1108,7 @@ struct fib_info *fib_create_info(struct + fib_info_update_nh_saddr(net, nexthop_nh); + fib_add_weight(fi, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1110,6 +1120,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + atomic_inc(&fi->fib_clntref); +@@ -1133,6 +1144,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1143,6 +1155,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff -Nupr src.orig/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c +--- src.orig/net/ipv4/fib_trie.c 2017-09-22 16:52:10.645110295 -0400 ++++ src/net/ipv4/fib_trie.c 2017-09-22 16:55:39.940975963 -0400 +@@ -1191,6 +1191,7 @@ static int fib_insert_alias(struct trie + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg) + { +@@ -1216,11 +1217,14 @@ int fib_table_insert(struct net *net, st + if ((plen < KEYLENGTH) && (key << plen)) + return -EINVAL; + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority) : NULL; diff --git a/test/integration/rhel-7.5/meminfo-init-FAIL.patch b/test/integration/rhel-7.5/meminfo-init-FAIL.patch new file mode 100644 index 0000000..5df5225 --- /dev/null +++ b/test/integration/rhel-7.5/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:40.130132502 -0400 +@@ -191,6 +191,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.5/meminfo-init2-FAIL.patch b/test/integration/rhel-7.5/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..c030f61 --- /dev/null +++ b/test/integration/rhel-7.5/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:38.972127707 -0400 +@@ -30,6 +30,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long pages[NR_LRU_LISTS]; + int lru; + ++ printk("a\n"); + /* + * display in kilobytes. + */ +@@ -191,6 +192,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.5/meminfo-string-LOADED.test b/test/integration/rhel-7.5/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-7.5/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-7.5/meminfo-string.patch b/test/integration/rhel-7.5/meminfo-string.patch new file mode 100644 index 0000000..afdc5d0 --- /dev/null +++ b/test/integration/rhel-7.5/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:41.274137239 -0400 +@@ -99,7 +99,7 @@ static int meminfo_proc_show(struct seq_ + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" +- "VmallocChunk: %8lu kB\n" ++ "VMALLOCCHUNK: %8lu kB\n" + #ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" + #endif diff --git a/test/integration/rhel-7.5/module-call-external.patch b/test/integration/rhel-7.5/module-call-external.patch new file mode 100644 index 0000000..754d725 --- /dev/null +++ b/test/integration/rhel-7.5/module-call-external.patch @@ -0,0 +1,33 @@ +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2017-09-22 15:27:21.705056204 -0400 ++++ src/fs/nfsd/export.c 2017-09-22 15:27:42.411141948 -0400 +@@ -1184,6 +1184,8 @@ static void exp_flags(struct seq_file *m + } + } + ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; +@@ -1193,6 +1195,7 @@ static int e_show(struct seq_file *m, vo + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2017-09-22 15:27:21.754056407 -0400 ++++ src/net/netlink/af_netlink.c 2017-09-22 15:27:42.412141952 -0400 +@@ -3260,4 +3260,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-7.5/module-kvm-fixup.patch b/test/integration/rhel-7.5/module-kvm-fixup.patch new file mode 100644 index 0000000..174ad65 --- /dev/null +++ b/test/integration/rhel-7.5/module-kvm-fixup.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/arch/x86/kvm/vmx.c src/arch/x86/kvm/vmx.c +--- src.orig/arch/x86/kvm/vmx.c 2017-09-22 15:27:20.853052676 -0400 ++++ src/arch/x86/kvm/vmx.c 2017-09-22 15:27:43.583146801 -0400 +@@ -10597,6 +10597,8 @@ static int vmx_check_intercept(struct kv + struct x86_instruction_info *info, + enum x86_intercept_stage stage) + { ++ if (!jiffies) ++ printk("kpatch vmx_check_intercept\n"); + return X86EMUL_CONTINUE; + } + diff --git a/test/integration/rhel-7.5/module-shadow.patch.disabled b/test/integration/rhel-7.5/module-shadow.patch.disabled new file mode 100644 index 0000000..e4821c9 --- /dev/null +++ b/test/integration/rhel-7.5/module-shadow.patch.disabled @@ -0,0 +1,25 @@ +Index: src/arch/x86/kvm/vmx.c +=================================================================== +--- src.orig/arch/x86/kvm/vmx.c ++++ src/arch/x86/kvm/vmx.c +@@ -11168,10 +11168,20 @@ static void vmx_leave_nested(struct kvm_ + * It should only be called before L2 actually succeeded to run, and when + * vmcs01 is current (it doesn't leave_guest_mode() or switch vmcss). + */ ++#include + static void nested_vmx_entry_failure(struct kvm_vcpu *vcpu, + struct vmcs12 *vmcs12, + u32 reason, unsigned long qualification) + { ++ int *kpatch; ++ ++ kpatch = klp_shadow_alloc(vcpu, 0, NULL, sizeof(*kpatch), ++ GFP_KERNEL); ++ if (kpatch) { ++ klp_shadow_get(vcpu, 0); ++ klp_shadow_free(vcpu, 0); ++ } ++ + load_vmcs12_host_state(vcpu, vmcs12); + vmcs12->vm_exit_reason = reason | VMX_EXIT_REASONS_FAILED_VMENTRY; + vmcs12->exit_qualification = qualification; diff --git a/test/integration/rhel-7.5/multiple.test b/test/integration/rhel-7.5/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-7.5/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-7.5/new-function.patch b/test/integration/rhel-7.5/new-function.patch new file mode 100644 index 0000000..bef6803 --- /dev/null +++ b/test/integration/rhel-7.5/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2017-09-22 15:27:21.084053633 -0400 ++++ src/drivers/tty/n_tty.c 2017-09-22 15:27:45.888156346 -0400 +@@ -2016,7 +2016,7 @@ do_it_again: + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2098,6 +2098,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-7.5/new-globals.patch b/test/integration/rhel-7.5/new-globals.patch new file mode 100644 index 0000000..3d9d349 --- /dev/null +++ b/test/integration/rhel-7.5/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/cmdline.c 2017-09-22 15:27:47.028161067 -0400 +@@ -27,3 +27,10 @@ static int __init proc_cmdline_init(void + return 0; + } + module_init(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:47.029161071 -0400 +@@ -16,6 +16,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -53,6 +55,7 @@ static int meminfo_proc_show(struct seq_ + /* + * Tagged format, for easy grepping and expansion. + */ ++ kpatch_print_message(); + seq_printf(m, + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" diff --git a/test/integration/rhel-7.5/parainstructions-section.patch b/test/integration/rhel-7.5/parainstructions-section.patch new file mode 100644 index 0000000..809dce4 --- /dev/null +++ b/test/integration/rhel-7.5/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/generic.c 2017-09-22 15:27:48.190165879 -0400 +@@ -194,6 +194,7 @@ int proc_alloc_inum(unsigned int *inum) + unsigned int i; + int error; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + retry: + if (!ida_pre_get(&proc_inum_ida, GFP_KERNEL)) + return -ENOMEM; diff --git a/test/integration/rhel-7.5/replace-section-references.patch b/test/integration/rhel-7.5/replace-section-references.patch new file mode 100644 index 0000000..e41bba9 --- /dev/null +++ b/test/integration/rhel-7.5/replace-section-references.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2017-09-22 15:27:20.852052672 -0400 ++++ src/arch/x86/kvm/x86.c 2017-09-22 15:27:49.362170732 -0400 +@@ -248,6 +248,8 @@ static void shared_msr_update(unsigned s + + void kvm_define_shared_msr(unsigned slot, u32 msr) + { ++ if (!jiffies) ++ printk("kpatch kvm define shared msr\n"); + BUG_ON(slot >= KVM_NR_SHARED_MSRS); + shared_msrs_global.msrs[slot] = msr; + if (slot >= shared_msrs_global.nr) diff --git a/test/integration/rhel-7.5/shadow-newpid-LOADED.test b/test/integration/rhel-7.5/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-7.5/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-7.5/shadow-newpid.patch b/test/integration/rhel-7.5/shadow-newpid.patch new file mode 100644 index 0000000..ff2019c --- /dev/null +++ b/test/integration/rhel-7.5/shadow-newpid.patch @@ -0,0 +1,72 @@ +Index: src/fs/proc/array.c +=================================================================== +--- src.orig/fs/proc/array.c ++++ src/fs/proc/array.c +@@ -394,13 +394,20 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +Index: src/kernel/exit.c +=================================================================== +--- src.orig/kernel/exit.c ++++ src/kernel/exit.c +@@ -715,6 +715,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void do_exit(long code) + { + struct task_struct *tsk = current; +@@ -812,6 +813,8 @@ void do_exit(long code) + check_stack_usage(); + exit_thread(); + ++ klp_shadow_free(tsk, 0); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +Index: src/kernel/fork.c +=================================================================== +--- src.orig/kernel/fork.c ++++ src/kernel/fork.c +@@ -1751,6 +1751,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -1788,6 +1789,13 @@ long do_fork(unsigned long clone_flags, + if (!IS_ERR(p)) { + struct completion vfork; + struct pid *pid; ++ int *newpid; ++ static int ctr = 0; ++ ++ newpid = klp_shadow_get_or_alloc(p, 0, NULL, sizeof(*newpid), ++ GFP_KERNEL); ++ if (newpid) ++ *newpid = ctr++; + + trace_sched_process_fork(current, p); + diff --git a/test/integration/rhel-7.5/smp-locks-section.patch b/test/integration/rhel-7.5/smp-locks-section.patch new file mode 100644 index 0000000..6f39d53 --- /dev/null +++ b/test/integration/rhel-7.5/smp-locks-section.patch @@ -0,0 +1,14 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2017-09-22 15:27:21.077053604 -0400 ++++ src/drivers/tty/tty_buffer.c 2017-09-22 15:27:50.542175618 -0400 +@@ -217,6 +217,10 @@ int tty_buffer_request_room(struct tty_p + /* OPTIMISATION: We could keep a per tty "zero" sized buffer to + remove this conditional if its worth it. This would be invisible + to the callers */ ++ ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b != NULL) + left = b->size - b->used; diff --git a/test/integration/rhel-7.5/special-static-2.patch b/test/integration/rhel-7.5/special-static-2.patch new file mode 100644 index 0000000..146d5b5 --- /dev/null +++ b/test/integration/rhel-7.5/special-static-2.patch @@ -0,0 +1,24 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2017-09-22 15:27:20.852052672 -0400 ++++ src/arch/x86/kvm/x86.c 2017-09-22 15:27:51.744180596 -0400 +@@ -2093,12 +2093,20 @@ static void record_steal_time(struct kvm + &vcpu->arch.st.steal, sizeof(struct kvm_steal_time)); + } + ++void kpatch_kvm_x86_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch kvm x86 foo\n"); ++} ++ + int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) + { + bool pr = false; + u32 msr = msr_info->index; + u64 data = msr_info->data; + ++ kpatch_kvm_x86_foo(); ++ + switch (msr) { + case MSR_AMD64_NB_CFG: + case MSR_IA32_UCODE_REV: diff --git a/test/integration/rhel-7.5/special-static.patch b/test/integration/rhel-7.5/special-static.patch new file mode 100644 index 0000000..84647ec --- /dev/null +++ b/test/integration/rhel-7.5/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2017-09-22 15:27:21.600055769 -0400 ++++ src/kernel/fork.c 2017-09-22 15:27:53.052186012 -0400 +@@ -1129,10 +1129,18 @@ static void posix_cpu_timers_init_group( + INIT_LIST_HEAD(&sig->cpu_timers[2]); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-7.5/tracepoints-section.patch b/test/integration/rhel-7.5/tracepoints-section.patch new file mode 100644 index 0000000..b770f9e --- /dev/null +++ b/test/integration/rhel-7.5/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/timer.c src/kernel/timer.c +--- src.orig/kernel/timer.c 2017-09-22 15:27:21.600055769 -0400 ++++ src/kernel/timer.c 2017-09-22 15:27:54.288191131 -0400 +@@ -1390,6 +1390,9 @@ static void run_timer_softirq(struct sof + { + struct tvec_base *base = __this_cpu_read(tvec_bases); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + if (time_after_eq(jiffies, base->timer_jiffies)) + __run_timers(base); + } diff --git a/test/integration/rhel-7.5/warn-detect-FAIL.patch b/test/integration/rhel-7.5/warn-detect-FAIL.patch new file mode 100644 index 0000000..8efa782 --- /dev/null +++ b/test/integration/rhel-7.5/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2017-09-22 15:27:20.852052672 -0400 ++++ src/arch/x86/kvm/x86.c 2017-09-22 15:27:55.489196104 -0400 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-7.6/bug-table-section.patch b/test/integration/rhel-7.6/bug-table-section.patch new file mode 100644 index 0000000..f75e398 --- /dev/null +++ b/test/integration/rhel-7.6/bug-table-section.patch @@ -0,0 +1,13 @@ +Index: kernel-rhel7/fs/proc/proc_sysctl.c +=================================================================== +--- kernel-rhel7.orig/fs/proc/proc_sysctl.c ++++ kernel-rhel7/fs/proc/proc_sysctl.c +@@ -301,6 +301,8 @@ void sysctl_head_put(struct ctl_table_he + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-7.6/cmdline-string-LOADED.test b/test/integration/rhel-7.6/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-7.6/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-7.6/cmdline-string.patch b/test/integration/rhel-7.6/cmdline-string.patch new file mode 100644 index 0000000..749861f --- /dev/null +++ b/test/integration/rhel-7.6/cmdline-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/cmdline.c 2017-09-22 15:27:22.955061380 -0400 +@@ -5,7 +5,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_printf(m, "%s\n", saved_command_line); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-7.6/data-new-LOADED.test b/test/integration/rhel-7.6/data-new-LOADED.test new file mode 100755 index 0000000..598b6bb --- /dev/null +++ b/test/integration/rhel-7.6/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-7.6/data-new.patch b/test/integration/rhel-7.6/data-new.patch new file mode 100644 index 0000000..e2f9333 --- /dev/null +++ b/test/integration/rhel-7.6/data-new.patch @@ -0,0 +1,29 @@ +Index: kernel-rhel7/fs/proc/meminfo.c +=================================================================== +--- kernel-rhel7.orig/fs/proc/meminfo.c ++++ kernel-rhel7/fs/proc/meminfo.c +@@ -20,6 +20,8 @@ void __attribute__((weak)) arch_report_m + { + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -110,6 +112,7 @@ static int meminfo_proc_show(struct seq_ + "CmaTotal: %8lu kB\n" + "CmaFree: %8lu kB\n" + #endif ++ "kpatch: %d" + , + K(i.totalram), + K(i.freeram), +@@ -175,6 +178,7 @@ static int meminfo_proc_show(struct seq_ + , K(totalcma_pages) + , K(global_page_state(NR_FREE_CMA_PAGES)) + #endif ++ ,foo + ); + + hugetlb_report_meminfo(m); diff --git a/test/integration/rhel-7.6/data-read-mostly.patch.disabled b/test/integration/rhel-7.6/data-read-mostly.patch.disabled new file mode 100644 index 0000000..42b02cb --- /dev/null +++ b/test/integration/rhel-7.6/data-read-mostly.patch.disabled @@ -0,0 +1,12 @@ +Index: kernel-rhel7/net/core/dev.c +=================================================================== +--- kernel-rhel7.orig/net/core/dev.c ++++ kernel-rhel7/net/core/dev.c +@@ -4199,6 +4199,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-7.6/fixup-section.patch b/test/integration/rhel-7.6/fixup-section.patch new file mode 100644 index 0000000..18d13b5 --- /dev/null +++ b/test/integration/rhel-7.6/fixup-section.patch @@ -0,0 +1,12 @@ +diff --git a/fs/readdir.c b/fs/readdir.c +index febd02dfbe2d..064db7bd70d0 100644 +--- a/fs/readdir.c ++++ b/fs/readdir.c +@@ -176,6 +176,7 @@ static int filldir(void * __buf, const char * name, int namlen, loff_t offset, + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-7.6/gcc-constprop.patch.disabled b/test/integration/rhel-7.6/gcc-constprop.patch.disabled new file mode 100644 index 0000000..63ba45d --- /dev/null +++ b/test/integration/rhel-7.6/gcc-constprop.patch.disabled @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2017-09-22 15:27:21.602055778 -0400 ++++ src/kernel/time/timekeeping.c 2017-09-22 15:27:27.522080292 -0400 +@@ -877,6 +877,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-7.6/gcc-isra.patch b/test/integration/rhel-7.6/gcc-isra.patch new file mode 100644 index 0000000..1eab35c --- /dev/null +++ b/test/integration/rhel-7.6/gcc-isra.patch @@ -0,0 +1,12 @@ +Index: kernel-rhel7/fs/proc/proc_sysctl.c +=================================================================== +--- kernel-rhel7.orig/fs/proc/proc_sysctl.c ++++ kernel-rhel7/fs/proc/proc_sysctl.c +@@ -46,6 +46,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-7.6/gcc-mangled-3.patch b/test/integration/rhel-7.6/gcc-mangled-3.patch new file mode 100644 index 0000000..92ba80e --- /dev/null +++ b/test/integration/rhel-7.6/gcc-mangled-3.patch @@ -0,0 +1,14 @@ +Index: kernel-rhel7/mm/slub.c +=================================================================== +--- kernel-rhel7.orig/mm/slub.c ++++ kernel-rhel7/mm/slub.c +@@ -5611,6 +5611,9 @@ void get_slabinfo(struct kmem_cache *s, + unsigned long nr_free = 0; + int node; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_online_node(node) { + struct kmem_cache_node *n = get_node(s, node); + diff --git a/test/integration/rhel-7.6/gcc-static-local-var-2.patch b/test/integration/rhel-7.6/gcc-static-local-var-2.patch new file mode 100644 index 0000000..7153968 --- /dev/null +++ b/test/integration/rhel-7.6/gcc-static-local-var-2.patch @@ -0,0 +1,14 @@ +Index: kernel-rhel7/mm/mmap.c +=================================================================== +--- kernel-rhel7.orig/mm/mmap.c ++++ kernel-rhel7/mm/mmap.c +@@ -1715,6 +1715,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-7.6/gcc-static-local-var-3.patch b/test/integration/rhel-7.6/gcc-static-local-var-3.patch new file mode 100644 index 0000000..d751fc6 --- /dev/null +++ b/test/integration/rhel-7.6/gcc-static-local-var-3.patch @@ -0,0 +1,20 @@ +Index: kernel-rhel7/kernel/sys.c +=================================================================== +--- kernel-rhel7.orig/kernel/sys.c ++++ kernel-rhel7/kernel/sys.c +@@ -559,8 +559,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-7.6/gcc-static-local-var-4.patch b/test/integration/rhel-7.6/gcc-static-local-var-4.patch new file mode 100644 index 0000000..e25d343 --- /dev/null +++ b/test/integration/rhel-7.6/gcc-static-local-var-4.patch @@ -0,0 +1,21 @@ +Index: kernel-rhel7/fs/aio.c +=================================================================== +--- kernel-rhel7.orig/fs/aio.c ++++ kernel-rhel7/fs/aio.c +@@ -223,9 +223,16 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(aio_ring_file->f_inode, 0); + diff --git a/test/integration/rhel-7.6/gcc-static-local-var-4.test b/test/integration/rhel-7.6/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-7.6/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-7.6/gcc-static-local-var-5.patch b/test/integration/rhel-7.6/gcc-static-local-var-5.patch new file mode 100644 index 0000000..540affa --- /dev/null +++ b/test/integration/rhel-7.6/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2017-09-22 15:27:21.602055778 -0400 ++++ src/kernel/audit.c 2017-09-22 15:27:34.429108894 -0400 +@@ -205,6 +205,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -215,6 +221,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -234,6 +241,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -282,6 +294,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-7.6/gcc-static-local-var-6.patch b/test/integration/rhel-7.6/gcc-static-local-var-6.patch new file mode 100644 index 0000000..bd5493c --- /dev/null +++ b/test/integration/rhel-7.6/gcc-static-local-var-6.patch @@ -0,0 +1,23 @@ +diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c +index a9d587a..23336ed 100644 +--- a/net/ipv6/netfilter.c ++++ b/net/ipv6/netfilter.c +@@ -106,6 +106,8 @@ static int nf_ip6_reroute(struct sk_buff *skb, + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -119,6 +121,9 @@ static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-7.6/macro-callbacks.patch b/test/integration/rhel-7.6/macro-callbacks.patch new file mode 100644 index 0000000..0d6831b --- /dev/null +++ b/test/integration/rhel-7.6/macro-callbacks.patch @@ -0,0 +1,160 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +--- src.old/fs/aio.c 2018-02-26 11:07:51.522610407 -0500 ++++ src/fs/aio.c 2018-03-05 11:17:21.560015449 -0500 +@@ -42,6 +42,50 @@ + #include + #include + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 +--- src.old/drivers/input/joydev.c 2018-02-26 11:07:49.470610407 -0500 ++++ src/drivers/input/joydev.c 2018-03-05 11:18:13.998015449 -0500 +@@ -954,3 +954,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +--- src.old/drivers/input/misc/pcspkr.c 2018-02-26 11:07:49.477610407 -0500 ++++ src/drivers/input/misc/pcspkr.c 2018-03-05 11:18:23.411015449 -0500 +@@ -136,3 +136,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); diff --git a/test/integration/rhel-7.6/macro-printk.patch b/test/integration/rhel-7.6/macro-printk.patch new file mode 100644 index 0000000..e837173 --- /dev/null +++ b/test/integration/rhel-7.6/macro-printk.patch @@ -0,0 +1,151 @@ +Index: kernel-rhel7/net/ipv4/fib_frontend.c +=================================================================== +--- kernel-rhel7.orig/net/ipv4/fib_frontend.c ++++ kernel-rhel7/net/ipv4/fib_frontend.c +@@ -685,6 +685,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) + { + struct net *net = sock_net(skb->sk); +@@ -703,6 +704,7 @@ static int inet_rtm_newroute(struct sk_b + } + + err = fib_table_insert(net, tb, &cfg); ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +Index: kernel-rhel7/net/ipv4/fib_semantics.c +=================================================================== +--- kernel-rhel7.orig/net/ipv4/fib_semantics.c ++++ kernel-rhel7/net/ipv4/fib_semantics.c +@@ -969,6 +969,7 @@ fib_convert_metrics(struct fib_info *fi, + return 0; + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg) + { + int err; +@@ -993,6 +994,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1013,6 +1015,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (fi == NULL) +@@ -1028,6 +1031,8 @@ struct fib_info *fib_create_info(struct + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1043,8 +1048,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1095,6 +1102,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) +@@ -1112,6 +1120,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) + goto err_inval; +@@ -1134,6 +1143,7 @@ struct fib_info *fib_create_info(struct + goto failure; + } endfor_nexthops(fi) + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc) { + if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || +@@ -1146,6 +1156,7 @@ struct fib_info *fib_create_info(struct + fib_info_update_nh_saddr(net, nexthop_nh); + fib_add_weight(fi, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1157,6 +1168,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + atomic_inc(&fi->fib_clntref); +@@ -1180,6 +1192,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1190,6 +1203,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +Index: kernel-rhel7/net/ipv4/fib_trie.c +=================================================================== +--- kernel-rhel7.orig/net/ipv4/fib_trie.c ++++ kernel-rhel7/net/ipv4/fib_trie.c +@@ -1105,6 +1105,7 @@ static int fib_insert_alias(struct trie + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg) + { +@@ -1130,11 +1131,14 @@ int fib_table_insert(struct net *net, st + if ((plen < KEYLENGTH) && (key << plen)) + return -EINVAL; + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-7.6/meminfo-init-FAIL.patch b/test/integration/rhel-7.6/meminfo-init-FAIL.patch new file mode 100644 index 0000000..3b8672d --- /dev/null +++ b/test/integration/rhel-7.6/meminfo-init-FAIL.patch @@ -0,0 +1,12 @@ +Index: kernel-rhel7/fs/proc/meminfo.c +=================================================================== +--- kernel-rhel7.orig/fs/proc/meminfo.c ++++ kernel-rhel7/fs/proc/meminfo.c +@@ -199,6 +199,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.6/meminfo-init2-FAIL.patch b/test/integration/rhel-7.6/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..cd70ef9 --- /dev/null +++ b/test/integration/rhel-7.6/meminfo-init2-FAIL.patch @@ -0,0 +1,20 @@ +Index: kernel-rhel7/fs/proc/meminfo.c +=================================================================== +--- kernel-rhel7.orig/fs/proc/meminfo.c ++++ kernel-rhel7/fs/proc/meminfo.c +@@ -30,6 +30,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long pages[NR_LRU_LISTS]; + int lru; + ++ printk("a\n"); + /* + * display in kilobytes. + */ +@@ -199,6 +200,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.6/meminfo-string-LOADED.test b/test/integration/rhel-7.6/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-7.6/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-7.6/meminfo-string.patch b/test/integration/rhel-7.6/meminfo-string.patch new file mode 100644 index 0000000..afdc5d0 --- /dev/null +++ b/test/integration/rhel-7.6/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:41.274137239 -0400 +@@ -99,7 +99,7 @@ static int meminfo_proc_show(struct seq_ + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" +- "VmallocChunk: %8lu kB\n" ++ "VMALLOCCHUNK: %8lu kB\n" + #ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" + #endif diff --git a/test/integration/rhel-7.6/module-call-external.patch b/test/integration/rhel-7.6/module-call-external.patch new file mode 100644 index 0000000..f149edf --- /dev/null +++ b/test/integration/rhel-7.6/module-call-external.patch @@ -0,0 +1,40 @@ +diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c +index 27459a453bb8..2247255877be 100644 +--- a/fs/nfsd/export.c ++++ b/fs/nfsd/export.c +@@ -1184,7 +1184,13 @@ static void exp_flags(struct seq_file *m, int flag, int fsid, + } + } + ++extern char *kpatch_string(void); ++ ++#ifdef CONFIG_PPC64 ++static int __attribute__((optimize("-fno-optimize-sibling-calls"))) e_show(struct seq_file *m, void *p) ++#else + static int e_show(struct seq_file *m, void *p) ++#endif + { + struct cache_head *cp = p; + struct svc_export *exp = container_of(cp, struct svc_export, h); +@@ -1193,6 +1199,7 @@ static int e_show(struct seq_file *m, void *p) + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index 592f64643491..21b87cb948c9 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -2568,4 +2568,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-7.6/multiple.test b/test/integration/rhel-7.6/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-7.6/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-7.6/new-function.patch b/test/integration/rhel-7.6/new-function.patch new file mode 100644 index 0000000..a9a0724 --- /dev/null +++ b/test/integration/rhel-7.6/new-function.patch @@ -0,0 +1,26 @@ +Index: kernel-rhel7/drivers/tty/n_tty.c +=================================================================== +--- kernel-rhel7.orig/drivers/tty/n_tty.c ++++ kernel-rhel7/drivers/tty/n_tty.c +@@ -2128,7 +2128,7 @@ do_it_again: + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2210,6 +2210,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-7.6/new-globals.patch b/test/integration/rhel-7.6/new-globals.patch new file mode 100644 index 0000000..3d9d349 --- /dev/null +++ b/test/integration/rhel-7.6/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/cmdline.c 2017-09-22 15:27:47.028161067 -0400 +@@ -27,3 +27,10 @@ static int __init proc_cmdline_init(void + return 0; + } + module_init(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2017-09-22 15:27:21.699056179 -0400 ++++ src/fs/proc/meminfo.c 2017-09-22 15:27:47.029161071 -0400 +@@ -16,6 +16,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -53,6 +55,7 @@ static int meminfo_proc_show(struct seq_ + /* + * Tagged format, for easy grepping and expansion. + */ ++ kpatch_print_message(); + seq_printf(m, + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" diff --git a/test/integration/rhel-7.6/parainstructions-section.patch b/test/integration/rhel-7.6/parainstructions-section.patch new file mode 100644 index 0000000..809dce4 --- /dev/null +++ b/test/integration/rhel-7.6/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2017-09-22 15:27:21.698056175 -0400 ++++ src/fs/proc/generic.c 2017-09-22 15:27:48.190165879 -0400 +@@ -194,6 +194,7 @@ int proc_alloc_inum(unsigned int *inum) + unsigned int i; + int error; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + retry: + if (!ida_pre_get(&proc_inum_ida, GFP_KERNEL)) + return -ENOMEM; diff --git a/test/integration/rhel-7.6/shadow-newpid-LOADED.test b/test/integration/rhel-7.6/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-7.6/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-7.6/shadow-newpid.patch b/test/integration/rhel-7.6/shadow-newpid.patch new file mode 100644 index 0000000..17bf527 --- /dev/null +++ b/test/integration/rhel-7.6/shadow-newpid.patch @@ -0,0 +1,72 @@ +diff --git a/fs/proc/array.c b/fs/proc/array.c +index 39684c79e8e2..138b60d1314d 100644 +--- a/fs/proc/array.c ++++ b/fs/proc/array.c +@@ -394,13 +394,20 @@ static inline void task_seccomp(struct seq_file *m, struct task_struct *p) + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff --git a/kernel/exit.c b/kernel/exit.c +index 148a7842928d..e1dbab71b37d 100644 +--- a/kernel/exit.c ++++ b/kernel/exit.c +@@ -791,6 +791,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void do_exit(long code) + { + struct task_struct *tsk = current; +@@ -888,6 +889,8 @@ void do_exit(long code) + check_stack_usage(); + exit_thread(); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff --git a/kernel/fork.c b/kernel/fork.c +index 9bff3b28c357..529a2c943d7c 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -1757,6 +1757,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -1794,6 +1795,13 @@ long do_fork(unsigned long clone_flags, + if (!IS_ERR(p)) { + struct completion vfork; + struct pid *pid; ++ int *newpid; ++ static int ctr = 0; ++ ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; + + trace_sched_process_fork(current, p); + diff --git a/test/integration/rhel-7.6/smp-locks-section.patch b/test/integration/rhel-7.6/smp-locks-section.patch new file mode 100644 index 0000000..6f39d53 --- /dev/null +++ b/test/integration/rhel-7.6/smp-locks-section.patch @@ -0,0 +1,14 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2017-09-22 15:27:21.077053604 -0400 ++++ src/drivers/tty/tty_buffer.c 2017-09-22 15:27:50.542175618 -0400 +@@ -217,6 +217,10 @@ int tty_buffer_request_room(struct tty_p + /* OPTIMISATION: We could keep a per tty "zero" sized buffer to + remove this conditional if its worth it. This would be invisible + to the callers */ ++ ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b != NULL) + left = b->size - b->used; diff --git a/test/integration/rhel-7.6/special-static.patch b/test/integration/rhel-7.6/special-static.patch new file mode 100644 index 0000000..7fa529f --- /dev/null +++ b/test/integration/rhel-7.6/special-static.patch @@ -0,0 +1,23 @@ +Index: kernel-rhel7/kernel/fork.c +=================================================================== +--- kernel-rhel7.orig/kernel/fork.c ++++ kernel-rhel7/kernel/fork.c +@@ -1146,10 +1146,18 @@ static void posix_cpu_timers_init_group( + INIT_LIST_HEAD(&sig->cpu_timers[2]); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-7.6/symvers-disagreement-FAIL.patch b/test/integration/rhel-7.6/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..d8ef754 --- /dev/null +++ b/test/integration/rhel-7.6/symvers-disagreement-FAIL.patch @@ -0,0 +1,51 @@ +From bcb86fa4e9c31379a9e2716eae29cd53ccca064f Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes usb_get_dev() referencing a get_device() whose CRC has + changed, causing the symbol and the new CRC to be included in the + __version section of the final module. + +This makes the final module unloadable for the target kernel. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/base/core.c b/drivers/base/core.c +index b9a71137208..4af27e069c2 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -31,6 +31,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index f9db3660999..bece0bf4f5a 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -691,6 +691,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; +-- +2.21.1 + diff --git a/test/integration/rhel-7.6/tracepoints-section.patch b/test/integration/rhel-7.6/tracepoints-section.patch new file mode 100644 index 0000000..512e512 --- /dev/null +++ b/test/integration/rhel-7.6/tracepoints-section.patch @@ -0,0 +1,14 @@ +Index: kernel-rhel7/kernel/timer.c +=================================================================== +--- kernel-rhel7.orig/kernel/timer.c ++++ kernel-rhel7/kernel/timer.c +@@ -1454,6 +1454,9 @@ static void run_timer_softirq(struct sof + { + struct tvec_base *base = __this_cpu_read(tvec_bases); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + if (time_after_eq(jiffies, base->timer_jiffies)) + __run_timers(base); + } diff --git a/test/integration/rhel-7.6/warn-detect-FAIL.patch b/test/integration/rhel-7.6/warn-detect-FAIL.patch new file mode 100644 index 0000000..8efa782 --- /dev/null +++ b/test/integration/rhel-7.6/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2017-09-22 15:27:20.852052672 -0400 ++++ src/arch/x86/kvm/x86.c 2017-09-22 15:27:55.489196104 -0400 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-7.7/bug-table-section.patch b/test/integration/rhel-7.7/bug-table-section.patch new file mode 100644 index 0000000..34342c0 --- /dev/null +++ b/test/integration/rhel-7.7/bug-table-section.patch @@ -0,0 +1,13 @@ +Index: kernel/fs/proc/proc_sysctl.c +=================================================================== +--- kernel.orig/fs/proc/proc_sysctl.c ++++ kernel/fs/proc/proc_sysctl.c +@@ -301,6 +301,8 @@ void sysctl_head_put(struct ctl_table_he + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-7.7/cmdline-string-LOADED.test b/test/integration/rhel-7.7/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-7.7/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-7.7/cmdline-string.patch b/test/integration/rhel-7.7/cmdline-string.patch new file mode 100644 index 0000000..f1f12b3 --- /dev/null +++ b/test/integration/rhel-7.7/cmdline-string.patch @@ -0,0 +1,13 @@ +Index: kernel/fs/proc/cmdline.c +=================================================================== +--- kernel.orig/fs/proc/cmdline.c ++++ kernel/fs/proc/cmdline.c +@@ -5,7 +5,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_printf(m, "%s\n", saved_command_line); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-7.7/data-new-LOADED.test b/test/integration/rhel-7.7/data-new-LOADED.test new file mode 100755 index 0000000..598b6bb --- /dev/null +++ b/test/integration/rhel-7.7/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-7.7/data-new.patch b/test/integration/rhel-7.7/data-new.patch new file mode 100644 index 0000000..4f9abcf --- /dev/null +++ b/test/integration/rhel-7.7/data-new.patch @@ -0,0 +1,29 @@ +Index: kernel/fs/proc/meminfo.c +=================================================================== +--- kernel.orig/fs/proc/meminfo.c ++++ kernel/fs/proc/meminfo.c +@@ -20,6 +20,8 @@ void __attribute__((weak)) arch_report_m + { + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -110,6 +112,7 @@ static int meminfo_proc_show(struct seq_ + "CmaTotal: %8lu kB\n" + "CmaFree: %8lu kB\n" + #endif ++ "kpatch: %d" + , + K(i.totalram), + K(i.freeram), +@@ -175,6 +178,7 @@ static int meminfo_proc_show(struct seq_ + , K(totalcma_pages) + , K(global_page_state(NR_FREE_CMA_PAGES)) + #endif ++ ,foo + ); + + hugetlb_report_meminfo(m); diff --git a/test/integration/rhel-7.7/data-read-mostly.patch.disabled b/test/integration/rhel-7.7/data-read-mostly.patch.disabled new file mode 100644 index 0000000..5e55826 --- /dev/null +++ b/test/integration/rhel-7.7/data-read-mostly.patch.disabled @@ -0,0 +1,12 @@ +Index: kernel/net/core/dev.c +=================================================================== +--- kernel.orig/net/core/dev.c ++++ kernel/net/core/dev.c +@@ -4327,6 +4327,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-7.7/fixup-section.patch b/test/integration/rhel-7.7/fixup-section.patch new file mode 100644 index 0000000..27c01cf --- /dev/null +++ b/test/integration/rhel-7.7/fixup-section.patch @@ -0,0 +1,12 @@ +Index: kernel/fs/readdir.c +=================================================================== +--- kernel.orig/fs/readdir.c ++++ kernel/fs/readdir.c +@@ -176,6 +176,7 @@ static int filldir(void * __buf, const c + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-7.7/gcc-constprop.patch.disabled b/test/integration/rhel-7.7/gcc-constprop.patch.disabled new file mode 100644 index 0000000..63ba45d --- /dev/null +++ b/test/integration/rhel-7.7/gcc-constprop.patch.disabled @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2017-09-22 15:27:21.602055778 -0400 ++++ src/kernel/time/timekeeping.c 2017-09-22 15:27:27.522080292 -0400 +@@ -877,6 +877,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-7.7/gcc-isra.patch b/test/integration/rhel-7.7/gcc-isra.patch new file mode 100644 index 0000000..ce5434a --- /dev/null +++ b/test/integration/rhel-7.7/gcc-isra.patch @@ -0,0 +1,12 @@ +Index: kernel/fs/proc/proc_sysctl.c +=================================================================== +--- kernel.orig/fs/proc/proc_sysctl.c ++++ kernel/fs/proc/proc_sysctl.c +@@ -46,6 +46,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-7.7/gcc-mangled-3.patch b/test/integration/rhel-7.7/gcc-mangled-3.patch new file mode 100644 index 0000000..7dbfc62 --- /dev/null +++ b/test/integration/rhel-7.7/gcc-mangled-3.patch @@ -0,0 +1,14 @@ +Index: kernel/mm/slub.c +=================================================================== +--- kernel.orig/mm/slub.c ++++ kernel/mm/slub.c +@@ -5675,6 +5675,9 @@ void get_slabinfo(struct kmem_cache *s, + unsigned long nr_free = 0; + int node; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_online_node(node) { + struct kmem_cache_node *n = get_node(s, node); + diff --git a/test/integration/rhel-7.7/gcc-static-local-var-2.patch b/test/integration/rhel-7.7/gcc-static-local-var-2.patch new file mode 100644 index 0000000..d1b70f1 --- /dev/null +++ b/test/integration/rhel-7.7/gcc-static-local-var-2.patch @@ -0,0 +1,14 @@ +Index: kernel/mm/mmap.c +=================================================================== +--- kernel.orig/mm/mmap.c ++++ kernel/mm/mmap.c +@@ -1716,6 +1716,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-7.7/gcc-static-local-var-3.patch b/test/integration/rhel-7.7/gcc-static-local-var-3.patch new file mode 100644 index 0000000..584c869 --- /dev/null +++ b/test/integration/rhel-7.7/gcc-static-local-var-3.patch @@ -0,0 +1,20 @@ +Index: kernel/kernel/sys.c +=================================================================== +--- kernel.orig/kernel/sys.c ++++ kernel/kernel/sys.c +@@ -559,8 +559,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-7.7/gcc-static-local-var-4.patch b/test/integration/rhel-7.7/gcc-static-local-var-4.patch new file mode 100644 index 0000000..85e4df5 --- /dev/null +++ b/test/integration/rhel-7.7/gcc-static-local-var-4.patch @@ -0,0 +1,21 @@ +Index: kernel/fs/aio.c +=================================================================== +--- kernel.orig/fs/aio.c ++++ kernel/fs/aio.c +@@ -223,9 +223,16 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(aio_ring_file->f_inode, 0); + diff --git a/test/integration/rhel-7.7/gcc-static-local-var-4.test b/test/integration/rhel-7.7/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-7.7/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-7.7/gcc-static-local-var-5.patch b/test/integration/rhel-7.7/gcc-static-local-var-5.patch new file mode 100644 index 0000000..3028676 --- /dev/null +++ b/test/integration/rhel-7.7/gcc-static-local-var-5.patch @@ -0,0 +1,46 @@ +Index: kernel/kernel/audit.c +=================================================================== +--- kernel.orig/kernel/audit.c ++++ kernel/kernel/audit.c +@@ -205,6 +205,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -215,6 +221,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -234,6 +241,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -282,6 +294,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-7.7/gcc-static-local-var-6.patch b/test/integration/rhel-7.7/gcc-static-local-var-6.patch new file mode 100644 index 0000000..53c3946 --- /dev/null +++ b/test/integration/rhel-7.7/gcc-static-local-var-6.patch @@ -0,0 +1,23 @@ +Index: kernel/net/ipv6/netfilter.c +=================================================================== +--- kernel.orig/net/ipv6/netfilter.c ++++ kernel/net/ipv6/netfilter.c +@@ -112,6 +112,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -125,6 +127,9 @@ static int nf_ip6_route(struct net *net, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-7.7/macro-callbacks.patch b/test/integration/rhel-7.7/macro-callbacks.patch new file mode 100644 index 0000000..d6757e0 --- /dev/null +++ b/test/integration/rhel-7.7/macro-callbacks.patch @@ -0,0 +1,166 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +Index: kernel/fs/aio.c +=================================================================== +--- kernel.orig/fs/aio.c ++++ kernel/fs/aio.c +@@ -42,6 +42,50 @@ + #include + #include + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 +Index: kernel/drivers/input/joydev.c +=================================================================== +--- kernel.orig/drivers/input/joydev.c ++++ kernel/drivers/input/joydev.c +@@ -954,3 +954,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +Index: kernel/drivers/input/misc/pcspkr.c +=================================================================== +--- kernel.orig/drivers/input/misc/pcspkr.c ++++ kernel/drivers/input/misc/pcspkr.c +@@ -136,3 +136,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); diff --git a/test/integration/rhel-7.7/macro-printk.patch b/test/integration/rhel-7.7/macro-printk.patch new file mode 100644 index 0000000..a4ff62c --- /dev/null +++ b/test/integration/rhel-7.7/macro-printk.patch @@ -0,0 +1,151 @@ +Index: kernel/net/ipv4/fib_frontend.c +=================================================================== +--- kernel.orig/net/ipv4/fib_frontend.c ++++ kernel/net/ipv4/fib_frontend.c +@@ -686,6 +686,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) + { + struct net *net = sock_net(skb->sk); +@@ -704,6 +705,7 @@ static int inet_rtm_newroute(struct sk_b + } + + err = fib_table_insert(net, tb, &cfg); ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +Index: kernel/net/ipv4/fib_semantics.c +=================================================================== +--- kernel.orig/net/ipv4/fib_semantics.c ++++ kernel/net/ipv4/fib_semantics.c +@@ -985,6 +985,7 @@ fib_convert_metrics(struct fib_info *fi, + return 0; + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg) + { + int err; +@@ -1009,6 +1010,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1029,6 +1031,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (fi == NULL) +@@ -1044,6 +1047,8 @@ struct fib_info *fib_create_info(struct + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1059,8 +1064,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1111,6 +1118,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) +@@ -1128,6 +1136,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) + goto err_inval; +@@ -1150,6 +1159,7 @@ struct fib_info *fib_create_info(struct + goto failure; + } endfor_nexthops(fi) + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc) { + if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || +@@ -1162,6 +1172,7 @@ struct fib_info *fib_create_info(struct + fib_info_update_nh_saddr(net, nexthop_nh); + fib_add_weight(fi, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1173,6 +1184,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + atomic_inc(&fi->fib_clntref); +@@ -1196,6 +1208,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1206,6 +1219,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +Index: kernel/net/ipv4/fib_trie.c +=================================================================== +--- kernel.orig/net/ipv4/fib_trie.c ++++ kernel/net/ipv4/fib_trie.c +@@ -1105,6 +1105,7 @@ static int fib_insert_alias(struct trie + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg) + { +@@ -1130,11 +1131,14 @@ int fib_table_insert(struct net *net, st + if ((plen < KEYLENGTH) && (key << plen)) + return -EINVAL; + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-7.7/meminfo-init-FAIL.patch b/test/integration/rhel-7.7/meminfo-init-FAIL.patch new file mode 100644 index 0000000..f3e6c73 --- /dev/null +++ b/test/integration/rhel-7.7/meminfo-init-FAIL.patch @@ -0,0 +1,12 @@ +Index: kernel/fs/proc/meminfo.c +=================================================================== +--- kernel.orig/fs/proc/meminfo.c ++++ kernel/fs/proc/meminfo.c +@@ -199,6 +199,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.7/meminfo-init2-FAIL.patch b/test/integration/rhel-7.7/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..e225294 --- /dev/null +++ b/test/integration/rhel-7.7/meminfo-init2-FAIL.patch @@ -0,0 +1,20 @@ +Index: kernel/fs/proc/meminfo.c +=================================================================== +--- kernel.orig/fs/proc/meminfo.c ++++ kernel/fs/proc/meminfo.c +@@ -30,6 +30,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long pages[NR_LRU_LISTS]; + int lru; + ++ printk("a\n"); + /* + * display in kilobytes. + */ +@@ -199,6 +200,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.7/meminfo-string-LOADED.test b/test/integration/rhel-7.7/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-7.7/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-7.7/meminfo-string.patch b/test/integration/rhel-7.7/meminfo-string.patch new file mode 100644 index 0000000..23ff7a7 --- /dev/null +++ b/test/integration/rhel-7.7/meminfo-string.patch @@ -0,0 +1,13 @@ +Index: kernel/fs/proc/meminfo.c +=================================================================== +--- kernel.orig/fs/proc/meminfo.c ++++ kernel/fs/proc/meminfo.c +@@ -99,7 +99,7 @@ static int meminfo_proc_show(struct seq_ + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" +- "VmallocChunk: %8lu kB\n" ++ "VMALLOCCHUNK: %8lu kB\n" + #ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" + #endif diff --git a/test/integration/rhel-7.7/module-call-external.patch b/test/integration/rhel-7.7/module-call-external.patch new file mode 100644 index 0000000..f149edf --- /dev/null +++ b/test/integration/rhel-7.7/module-call-external.patch @@ -0,0 +1,40 @@ +diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c +index 27459a453bb8..2247255877be 100644 +--- a/fs/nfsd/export.c ++++ b/fs/nfsd/export.c +@@ -1184,7 +1184,13 @@ static void exp_flags(struct seq_file *m, int flag, int fsid, + } + } + ++extern char *kpatch_string(void); ++ ++#ifdef CONFIG_PPC64 ++static int __attribute__((optimize("-fno-optimize-sibling-calls"))) e_show(struct seq_file *m, void *p) ++#else + static int e_show(struct seq_file *m, void *p) ++#endif + { + struct cache_head *cp = p; + struct svc_export *exp = container_of(cp, struct svc_export, h); +@@ -1193,6 +1199,7 @@ static int e_show(struct seq_file *m, void *p) + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index 592f64643491..21b87cb948c9 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -2568,4 +2568,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-7.7/multiple.test b/test/integration/rhel-7.7/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-7.7/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-7.7/new-function.patch b/test/integration/rhel-7.7/new-function.patch new file mode 100644 index 0000000..604dbe5 --- /dev/null +++ b/test/integration/rhel-7.7/new-function.patch @@ -0,0 +1,26 @@ +Index: kernel/drivers/tty/n_tty.c +=================================================================== +--- kernel.orig/drivers/tty/n_tty.c ++++ kernel/drivers/tty/n_tty.c +@@ -2175,7 +2175,7 @@ do_it_again: + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2264,6 +2264,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-7.7/new-globals.patch b/test/integration/rhel-7.7/new-globals.patch new file mode 100644 index 0000000..e18d09d --- /dev/null +++ b/test/integration/rhel-7.7/new-globals.patch @@ -0,0 +1,36 @@ +Index: kernel/fs/proc/cmdline.c +=================================================================== +--- kernel.orig/fs/proc/cmdline.c ++++ kernel/fs/proc/cmdline.c +@@ -27,3 +27,10 @@ static int __init proc_cmdline_init(void + return 0; + } + module_init(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +Index: kernel/fs/proc/meminfo.c +=================================================================== +--- kernel.orig/fs/proc/meminfo.c ++++ kernel/fs/proc/meminfo.c +@@ -16,6 +16,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -53,6 +55,7 @@ static int meminfo_proc_show(struct seq_ + /* + * Tagged format, for easy grepping and expansion. + */ ++ kpatch_print_message(); + seq_printf(m, + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" diff --git a/test/integration/rhel-7.7/parainstructions-section.patch b/test/integration/rhel-7.7/parainstructions-section.patch new file mode 100644 index 0000000..151d263 --- /dev/null +++ b/test/integration/rhel-7.7/parainstructions-section.patch @@ -0,0 +1,12 @@ +Index: kernel/fs/proc/generic.c +=================================================================== +--- kernel.orig/fs/proc/generic.c ++++ kernel/fs/proc/generic.c +@@ -194,6 +194,7 @@ int proc_alloc_inum(unsigned int *inum) + unsigned int i; + int error; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + retry: + if (!ida_pre_get(&proc_inum_ida, GFP_KERNEL)) + return -ENOMEM; diff --git a/test/integration/rhel-7.7/shadow-newpid-LOADED.test b/test/integration/rhel-7.7/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-7.7/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-7.7/shadow-newpid.patch b/test/integration/rhel-7.7/shadow-newpid.patch new file mode 100644 index 0000000..cae690d --- /dev/null +++ b/test/integration/rhel-7.7/shadow-newpid.patch @@ -0,0 +1,72 @@ +Index: kernel/fs/proc/array.c +=================================================================== +--- kernel.orig/fs/proc/array.c ++++ kernel/fs/proc/array.c +@@ -395,13 +395,20 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +Index: kernel/kernel/exit.c +=================================================================== +--- kernel.orig/kernel/exit.c ++++ kernel/kernel/exit.c +@@ -791,6 +791,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void do_exit(long code) + { + struct task_struct *tsk = current; +@@ -888,6 +889,8 @@ void do_exit(long code) + check_stack_usage(); + exit_thread(); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +Index: kernel/kernel/fork.c +=================================================================== +--- kernel.orig/kernel/fork.c ++++ kernel/kernel/fork.c +@@ -1760,6 +1760,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -1797,6 +1798,13 @@ long do_fork(unsigned long clone_flags, + if (!IS_ERR(p)) { + struct completion vfork; + struct pid *pid; ++ int *newpid; ++ static int ctr = 0; ++ ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; + + trace_sched_process_fork(current, p); + diff --git a/test/integration/rhel-7.7/smp-locks-section.patch b/test/integration/rhel-7.7/smp-locks-section.patch new file mode 100644 index 0000000..7aa1c31 --- /dev/null +++ b/test/integration/rhel-7.7/smp-locks-section.patch @@ -0,0 +1,15 @@ +Index: kernel/drivers/tty/tty_buffer.c +=================================================================== +--- kernel.orig/drivers/tty/tty_buffer.c ++++ kernel/drivers/tty/tty_buffer.c +@@ -217,6 +217,10 @@ int tty_buffer_request_room(struct tty_p + /* OPTIMISATION: We could keep a per tty "zero" sized buffer to + remove this conditional if its worth it. This would be invisible + to the callers */ ++ ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b != NULL) + left = b->size - b->used; diff --git a/test/integration/rhel-7.7/special-static.patch b/test/integration/rhel-7.7/special-static.patch new file mode 100644 index 0000000..8a757ba --- /dev/null +++ b/test/integration/rhel-7.7/special-static.patch @@ -0,0 +1,23 @@ +Index: kernel/kernel/fork.c +=================================================================== +--- kernel.orig/kernel/fork.c ++++ kernel/kernel/fork.c +@@ -1146,10 +1146,18 @@ static void posix_cpu_timers_init_group( + INIT_LIST_HEAD(&sig->cpu_timers[2]); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-7.7/symvers-disagreement-FAIL.patch b/test/integration/rhel-7.7/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..e940645 --- /dev/null +++ b/test/integration/rhel-7.7/symvers-disagreement-FAIL.patch @@ -0,0 +1,51 @@ +From d5513eae5155c6e7e884554d5e3e2c65a7b39cbe Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes usb_get_dev() referencing a get_device() whose CRC has + changed, causing the symbol and the new CRC to be included in the + __version section of the final module. + +This makes the final module unloadable for the target kernel. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/base/core.c b/drivers/base/core.c +index b9a71137208..4af27e069c2 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -31,6 +31,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index d6337db2164..0ff9722dfa2 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -693,6 +693,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; +-- +2.21.3 + diff --git a/test/integration/rhel-7.7/tracepoints-section.patch b/test/integration/rhel-7.7/tracepoints-section.patch new file mode 100644 index 0000000..b992e14 --- /dev/null +++ b/test/integration/rhel-7.7/tracepoints-section.patch @@ -0,0 +1,14 @@ +Index: kernel/kernel/timer.c +=================================================================== +--- kernel.orig/kernel/timer.c ++++ kernel/kernel/timer.c +@@ -1454,6 +1454,9 @@ static void run_timer_softirq(struct sof + { + struct tvec_base *base = __this_cpu_read(tvec_bases); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + if (time_after_eq(jiffies, base->timer_jiffies)) + __run_timers(base); + } diff --git a/test/integration/rhel-7.7/warn-detect-FAIL.patch b/test/integration/rhel-7.7/warn-detect-FAIL.patch new file mode 100644 index 0000000..801fa4c --- /dev/null +++ b/test/integration/rhel-7.7/warn-detect-FAIL.patch @@ -0,0 +1,9 @@ +Index: kernel/arch/x86/kvm/x86.c +=================================================================== +--- kernel.orig/arch/x86/kvm/x86.c ++++ kernel/arch/x86/kvm/x86.c +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-7.8/bug-table-section.patch b/test/integration/rhel-7.8/bug-table-section.patch new file mode 100644 index 0000000..864ad9b --- /dev/null +++ b/test/integration/rhel-7.8/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2020-03-10 10:35:54.568563834 -0400 ++++ src/fs/proc/proc_sysctl.c 2020-03-10 10:35:57.040558842 -0400 +@@ -331,6 +331,8 @@ static void start_unregistering(struct c + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-7.8/cmdline-string-LOADED.test b/test/integration/rhel-7.8/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-7.8/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-7.8/cmdline-string.patch b/test/integration/rhel-7.8/cmdline-string.patch new file mode 100644 index 0000000..0c691b1 --- /dev/null +++ b/test/integration/rhel-7.8/cmdline-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2020-03-10 10:35:54.567563836 -0400 ++++ src/fs/proc/cmdline.c 2020-03-10 10:36:03.207546389 -0400 +@@ -5,7 +5,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_printf(m, "%s\n", saved_command_line); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-7.8/data-new-LOADED.test b/test/integration/rhel-7.8/data-new-LOADED.test new file mode 100755 index 0000000..598b6bb --- /dev/null +++ b/test/integration/rhel-7.8/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-7.8/data-new.patch b/test/integration/rhel-7.8/data-new.patch new file mode 100644 index 0000000..e213f82 --- /dev/null +++ b/test/integration/rhel-7.8/data-new.patch @@ -0,0 +1,28 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-03-10 10:35:54.568563834 -0400 ++++ src/fs/proc/meminfo.c 2020-03-10 10:36:07.968536775 -0400 +@@ -21,6 +21,8 @@ void __attribute__((weak)) arch_report_m + { + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -112,6 +114,7 @@ static int meminfo_proc_show(struct seq_ + "CmaTotal: %8lu kB\n" + "CmaFree: %8lu kB\n" + #endif ++ "kpatch: %d" + , + K(i.totalram), + K(i.freeram), +@@ -178,6 +181,7 @@ static int meminfo_proc_show(struct seq_ + , K(totalcma_pages) + , K(global_page_state(NR_FREE_CMA_PAGES)) + #endif ++ ,foo + ); + + hugetlb_report_meminfo(m); diff --git a/test/integration/rhel-7.8/data-read-mostly.patch.disabled b/test/integration/rhel-7.8/data-read-mostly.patch.disabled new file mode 100644 index 0000000..12f3b0c --- /dev/null +++ b/test/integration/rhel-7.8/data-read-mostly.patch.disabled @@ -0,0 +1,11 @@ +diff -Nupr src.orig/net/core/dev.c src/net/core/dev.c +--- src.orig/net/core/dev.c 2020-03-10 10:35:55.176562607 -0400 ++++ src/net/core/dev.c 2020-03-10 10:37:54.458302249 -0400 +@@ -4327,6 +4327,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-7.8/fixup-section.patch b/test/integration/rhel-7.8/fixup-section.patch new file mode 100644 index 0000000..65b3ed5 --- /dev/null +++ b/test/integration/rhel-7.8/fixup-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/readdir.c src/fs/readdir.c +--- src.orig/fs/readdir.c 2020-03-10 10:35:54.574563822 -0400 ++++ src/fs/readdir.c 2020-03-10 10:36:12.621527378 -0400 +@@ -176,6 +176,7 @@ static int filldir(void * __buf, const c + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-7.8/gcc-constprop.patch.disabled b/test/integration/rhel-7.8/gcc-constprop.patch.disabled new file mode 100644 index 0000000..609f52d --- /dev/null +++ b/test/integration/rhel-7.8/gcc-constprop.patch.disabled @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2020-03-10 10:35:55.091562778 -0400 ++++ src/kernel/time/timekeeping.c 2020-03-10 10:37:59.105290886 -0400 +@@ -852,6 +852,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-7.8/gcc-isra.patch b/test/integration/rhel-7.8/gcc-isra.patch new file mode 100644 index 0000000..dc58bdb --- /dev/null +++ b/test/integration/rhel-7.8/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2020-03-10 10:35:54.568563834 -0400 ++++ src/fs/proc/proc_sysctl.c 2020-03-10 10:36:17.393517742 -0400 +@@ -46,6 +46,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-7.8/gcc-mangled-3.patch b/test/integration/rhel-7.8/gcc-mangled-3.patch new file mode 100644 index 0000000..29477f1 --- /dev/null +++ b/test/integration/rhel-7.8/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2020-03-10 10:35:55.138562683 -0400 ++++ src/mm/slub.c 2020-03-10 10:36:22.189508057 -0400 +@@ -5716,6 +5716,9 @@ void get_slabinfo(struct kmem_cache *s, + unsigned long nr_free = 0; + int node; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_online_node(node) { + struct kmem_cache_node *n = get_node(s, node); + diff --git a/test/integration/rhel-7.8/gcc-static-local-var-2.patch b/test/integration/rhel-7.8/gcc-static-local-var-2.patch new file mode 100644 index 0000000..34087aa --- /dev/null +++ b/test/integration/rhel-7.8/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/mmap.c src/mm/mmap.c +--- src.orig/mm/mmap.c 2020-03-10 10:35:55.133562693 -0400 ++++ src/mm/mmap.c 2020-03-10 10:36:26.787498772 -0400 +@@ -1721,6 +1721,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-7.8/gcc-static-local-var-3.patch b/test/integration/rhel-7.8/gcc-static-local-var-3.patch new file mode 100644 index 0000000..3f9723b --- /dev/null +++ b/test/integration/rhel-7.8/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2020-03-10 10:35:55.088562784 -0400 ++++ src/kernel/sys.c 2020-03-10 10:36:31.420489416 -0400 +@@ -559,8 +559,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-7.8/gcc-static-local-var-4.patch b/test/integration/rhel-7.8/gcc-static-local-var-4.patch new file mode 100644 index 0000000..0533a9f --- /dev/null +++ b/test/integration/rhel-7.8/gcc-static-local-var-4.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2020-03-10 10:35:54.403564168 -0400 ++++ src/fs/aio.c 2020-03-10 10:36:36.025480117 -0400 +@@ -223,9 +223,16 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(aio_ring_file->f_inode, 0); + diff --git a/test/integration/rhel-7.8/gcc-static-local-var-4.test b/test/integration/rhel-7.8/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-7.8/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-7.8/gcc-static-local-var-5.patch b/test/integration/rhel-7.8/gcc-static-local-var-5.patch new file mode 100644 index 0000000..12a072a --- /dev/null +++ b/test/integration/rhel-7.8/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2020-03-10 10:35:55.059562843 -0400 ++++ src/kernel/audit.c 2020-03-10 10:36:41.387468209 -0400 +@@ -205,6 +205,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -215,6 +221,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -234,6 +241,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -282,6 +294,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-7.8/gcc-static-local-var-6.patch b/test/integration/rhel-7.8/gcc-static-local-var-6.patch new file mode 100644 index 0000000..81e7ad1 --- /dev/null +++ b/test/integration/rhel-7.8/gcc-static-local-var-6.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/net/ipv6/netfilter.c src/net/ipv6/netfilter.c +--- src.orig/net/ipv6/netfilter.c 2020-03-10 10:35:55.213562532 -0400 ++++ src/net/ipv6/netfilter.c 2020-03-10 10:36:47.062455553 -0400 +@@ -112,6 +112,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -125,6 +127,9 @@ static int nf_ip6_route(struct net *net, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-7.8/macro-callbacks.patch b/test/integration/rhel-7.8/macro-callbacks.patch new file mode 100644 index 0000000..0ca7098 --- /dev/null +++ b/test/integration/rhel-7.8/macro-callbacks.patch @@ -0,0 +1,163 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +diff -Nupr src.orig/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.orig/drivers/input/joydev.c 2020-03-10 10:35:52.635567738 -0400 ++++ src/drivers/input/joydev.c 2020-03-10 10:36:51.651445319 -0400 +@@ -954,3 +954,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.orig/drivers/input/misc/pcspkr.c 2020-03-10 10:35:52.650567707 -0400 ++++ src/drivers/input/misc/pcspkr.c 2020-03-10 10:36:51.652445316 -0400 +@@ -136,3 +136,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2020-03-10 10:35:54.403564168 -0400 ++++ src/fs/aio.c 2020-03-10 10:36:51.651445319 -0400 +@@ -42,6 +42,50 @@ + #include + #include + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/rhel-7.8/macro-printk.patch b/test/integration/rhel-7.8/macro-printk.patch new file mode 100644 index 0000000..f360d82 --- /dev/null +++ b/test/integration/rhel-7.8/macro-printk.patch @@ -0,0 +1,148 @@ +diff -Nupr src.orig/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c +--- src.orig/net/ipv4/fib_frontend.c 2020-03-10 10:35:55.194562570 -0400 ++++ src/net/ipv4/fib_frontend.c 2020-03-10 10:36:56.379434774 -0400 +@@ -690,6 +690,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) + { + struct net *net = sock_net(skb->sk); +@@ -708,6 +709,7 @@ static int inet_rtm_newroute(struct sk_b + } + + err = fib_table_insert(net, tb, &cfg); ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c +--- src.orig/net/ipv4/fib_semantics.c 2020-03-10 10:35:55.194562570 -0400 ++++ src/net/ipv4/fib_semantics.c 2020-03-10 10:36:56.380434772 -0400 +@@ -985,6 +985,7 @@ fib_convert_metrics(struct fib_info *fi, + return 0; + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg) + { + int err; +@@ -1009,6 +1010,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1029,6 +1031,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (fi == NULL) +@@ -1044,6 +1047,8 @@ struct fib_info *fib_create_info(struct + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1059,8 +1064,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1111,6 +1118,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) +@@ -1128,6 +1136,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) + goto err_inval; +@@ -1150,6 +1159,7 @@ struct fib_info *fib_create_info(struct + goto failure; + } endfor_nexthops(fi) + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc) { + if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || +@@ -1162,6 +1172,7 @@ struct fib_info *fib_create_info(struct + fib_info_update_nh_saddr(net, nexthop_nh); + fib_add_weight(fi, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1173,6 +1184,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + atomic_inc(&fi->fib_clntref); +@@ -1196,6 +1208,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1206,6 +1219,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff -Nupr src.orig/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c +--- src.orig/net/ipv4/fib_trie.c 2020-03-10 10:35:55.195562568 -0400 ++++ src/net/ipv4/fib_trie.c 2020-03-10 10:36:56.381434770 -0400 +@@ -1105,6 +1105,7 @@ static int fib_insert_alias(struct trie + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg) + { +@@ -1130,11 +1131,14 @@ int fib_table_insert(struct net *net, st + if ((plen < KEYLENGTH) && (key << plen)) + return -EINVAL; + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-7.8/meminfo-init-FAIL.patch b/test/integration/rhel-7.8/meminfo-init-FAIL.patch new file mode 100644 index 0000000..05f0c37 --- /dev/null +++ b/test/integration/rhel-7.8/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-03-10 10:35:54.568563834 -0400 ++++ src/fs/proc/meminfo.c 2020-03-10 10:37:07.161410729 -0400 +@@ -202,6 +202,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.8/meminfo-init2-FAIL.patch b/test/integration/rhel-7.8/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..ad0664b --- /dev/null +++ b/test/integration/rhel-7.8/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-03-10 10:35:54.568563834 -0400 ++++ src/fs/proc/meminfo.c 2020-03-10 10:37:00.986424500 -0400 +@@ -31,6 +31,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long pages[NR_LRU_LISTS]; + int lru; + ++ printk("a\n"); + /* + * display in kilobytes. + */ +@@ -202,6 +203,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.8/meminfo-string-LOADED.test b/test/integration/rhel-7.8/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-7.8/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-7.8/meminfo-string.patch b/test/integration/rhel-7.8/meminfo-string.patch new file mode 100644 index 0000000..33e3a17 --- /dev/null +++ b/test/integration/rhel-7.8/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr linux-3.10.0-1127.fc30.x86_64.orig/fs/proc/meminfo.c linux-3.10.0-1127.fc30.x86_64/fs/proc/meminfo.c +--- linux-3.10.0-1127.fc30.x86_64.orig/fs/proc/meminfo.c 2020-03-10 09:40:37.849666782 -0400 ++++ linux-3.10.0-1127.fc30.x86_64/fs/proc/meminfo.c 2020-03-10 09:50:26.871990501 -0400 +@@ -100,7 +100,7 @@ static int meminfo_proc_show(struct seq_ + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" +- "VmallocChunk: %8lu kB\n" ++ "VMALLOCCHUNK: %8lu kB\n" + "Percpu: %8lu kB\n" + #ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" diff --git a/test/integration/rhel-7.8/module-call-external.patch b/test/integration/rhel-7.8/module-call-external.patch new file mode 100644 index 0000000..b1c594d --- /dev/null +++ b/test/integration/rhel-7.8/module-call-external.patch @@ -0,0 +1,38 @@ +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2020-03-10 10:35:54.521563929 -0400 ++++ src/fs/nfsd/export.c 2020-03-10 10:37:11.704400597 -0400 +@@ -1184,7 +1184,13 @@ static void exp_flags(struct seq_file *m + } + } + ++extern char *kpatch_string(void); ++ ++#ifdef CONFIG_PPC64 ++static int __attribute__((optimize("-fno-optimize-sibling-calls"))) e_show(struct seq_file *m, void *p) ++#else + static int e_show(struct seq_file *m, void *p) ++#endif + { + struct cache_head *cp = p; + struct svc_export *exp = container_of(cp, struct svc_export, h); +@@ -1193,6 +1199,7 @@ static int e_show(struct seq_file *m, vo + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2020-03-10 10:35:56.447560040 -0400 ++++ src/net/netlink/af_netlink.c 2020-03-10 10:37:11.705400594 -0400 +@@ -2568,4 +2568,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-7.8/multiple.test b/test/integration/rhel-7.8/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-7.8/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-7.8/new-function.patch b/test/integration/rhel-7.8/new-function.patch new file mode 100644 index 0000000..bcd6643 --- /dev/null +++ b/test/integration/rhel-7.8/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2020-03-10 10:35:54.135564709 -0400 ++++ src/drivers/tty/n_tty.c 2020-03-10 10:37:16.551389787 -0400 +@@ -2175,7 +2175,7 @@ do_it_again: + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2264,6 +2264,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-7.8/new-globals.patch b/test/integration/rhel-7.8/new-globals.patch new file mode 100644 index 0000000..49e98e6 --- /dev/null +++ b/test/integration/rhel-7.8/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2020-03-10 10:35:54.567563836 -0400 ++++ src/fs/proc/cmdline.c 2020-03-10 10:37:21.219379377 -0400 +@@ -27,3 +27,10 @@ static int __init proc_cmdline_init(void + return 0; + } + module_init(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-03-10 10:35:54.568563834 -0400 ++++ src/fs/proc/meminfo.c 2020-03-10 10:37:21.220379374 -0400 +@@ -17,6 +17,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -54,6 +56,7 @@ static int meminfo_proc_show(struct seq_ + /* + * Tagged format, for easy grepping and expansion. + */ ++ kpatch_print_message(); + seq_printf(m, + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" diff --git a/test/integration/rhel-7.8/parainstructions-section.patch b/test/integration/rhel-7.8/parainstructions-section.patch new file mode 100644 index 0000000..5f801c4 --- /dev/null +++ b/test/integration/rhel-7.8/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2020-03-10 10:35:54.567563836 -0400 ++++ src/fs/proc/generic.c 2020-03-10 10:37:25.973368774 -0400 +@@ -194,6 +194,7 @@ int proc_alloc_inum(unsigned int *inum) + unsigned int i; + int error; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + retry: + if (!ida_pre_get(&proc_inum_ida, GFP_KERNEL)) + return -ENOMEM; diff --git a/test/integration/rhel-7.8/shadow-newpid-LOADED.test b/test/integration/rhel-7.8/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-7.8/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-7.8/shadow-newpid.patch b/test/integration/rhel-7.8/shadow-newpid.patch new file mode 100644 index 0000000..e8a4d78 --- /dev/null +++ b/test/integration/rhel-7.8/shadow-newpid.patch @@ -0,0 +1,69 @@ +diff -Nupr src.orig/fs/proc/array.c src/fs/proc/array.c +--- src.orig/fs/proc/array.c 2020-03-10 10:35:54.566563838 -0400 ++++ src/fs/proc/array.c 2020-03-10 10:37:30.688358259 -0400 +@@ -395,13 +395,20 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff -Nupr src.orig/kernel/exit.c src/kernel/exit.c +--- src.orig/kernel/exit.c 2020-03-10 10:35:55.068562825 -0400 ++++ src/kernel/exit.c 2020-03-10 10:37:30.689358257 -0400 +@@ -791,6 +791,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void do_exit(long code) + { + struct task_struct *tsk = current; +@@ -888,6 +889,8 @@ void do_exit(long code) + check_stack_usage(); + exit_thread(); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2020-03-10 10:35:55.068562825 -0400 ++++ src/kernel/fork.c 2020-03-10 10:37:30.690358255 -0400 +@@ -1784,6 +1784,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -1821,6 +1822,13 @@ long do_fork(unsigned long clone_flags, + if (!IS_ERR(p)) { + struct completion vfork; + struct pid *pid; ++ int *newpid; ++ static int ctr = 0; ++ ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; + + trace_sched_process_fork(current, p); + diff --git a/test/integration/rhel-7.8/smp-locks-section.patch b/test/integration/rhel-7.8/smp-locks-section.patch new file mode 100644 index 0000000..6f9700d --- /dev/null +++ b/test/integration/rhel-7.8/smp-locks-section.patch @@ -0,0 +1,14 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2020-03-10 10:35:54.155564668 -0400 ++++ src/drivers/tty/tty_buffer.c 2020-03-10 10:37:35.446347648 -0400 +@@ -217,6 +217,10 @@ int tty_buffer_request_room(struct tty_p + /* OPTIMISATION: We could keep a per tty "zero" sized buffer to + remove this conditional if its worth it. This would be invisible + to the callers */ ++ ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b != NULL) + left = b->size - b->used; diff --git a/test/integration/rhel-7.8/special-static.patch b/test/integration/rhel-7.8/special-static.patch new file mode 100644 index 0000000..02bd105 --- /dev/null +++ b/test/integration/rhel-7.8/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2020-03-10 10:35:55.068562825 -0400 ++++ src/kernel/fork.c 2020-03-10 10:37:40.182337086 -0400 +@@ -1153,10 +1153,18 @@ static void posix_cpu_timers_init_group( + INIT_LIST_HEAD(&sig->cpu_timers[2]); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-7.8/symvers-disagreement-FAIL.patch b/test/integration/rhel-7.8/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..300f8b9 --- /dev/null +++ b/test/integration/rhel-7.8/symvers-disagreement-FAIL.patch @@ -0,0 +1,49 @@ +From da109d66a890373d0aa831f97b49aaffcc4eeb45 Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes a function referencing a function whose CRC has changed, + causing the symbol and the new CRC to be included in the __version + section of the final module. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/base/core.c b/drivers/base/core.c +index b9a71137208..4af27e069c2 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -31,6 +31,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index 8b1c4a5ee78..1b7997b58c0 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -693,6 +693,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; +-- +2.21.3 + diff --git a/test/integration/rhel-7.8/tracepoints-section.patch b/test/integration/rhel-7.8/tracepoints-section.patch new file mode 100644 index 0000000..20e9b79 --- /dev/null +++ b/test/integration/rhel-7.8/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/timer.c src/kernel/timer.c +--- src.orig/kernel/timer.c 2020-03-10 10:35:55.092562776 -0400 ++++ src/kernel/timer.c 2020-03-10 10:37:44.918325578 -0400 +@@ -1454,6 +1454,9 @@ static void run_timer_softirq(struct sof + { + struct tvec_base *base = __this_cpu_read(tvec_bases); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + if (time_after_eq(jiffies, base->timer_jiffies)) + __run_timers(base); + } diff --git a/test/integration/rhel-7.8/warn-detect-FAIL.patch b/test/integration/rhel-7.8/warn-detect-FAIL.patch new file mode 100644 index 0000000..d2792db --- /dev/null +++ b/test/integration/rhel-7.8/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2020-03-10 10:35:51.514570002 -0400 ++++ src/arch/x86/kvm/x86.c 2020-03-10 10:37:49.745313774 -0400 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-7.9/bug-table-section.patch b/test/integration/rhel-7.9/bug-table-section.patch new file mode 100644 index 0000000..92e66d4 --- /dev/null +++ b/test/integration/rhel-7.9/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2020-09-03 11:48:30.497726123 -0400 ++++ src/fs/proc/proc_sysctl.c 2020-09-03 11:48:31.009727724 -0400 +@@ -331,6 +331,8 @@ static void start_unregistering(struct c + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-7.9/cmdline-string-LOADED.test b/test/integration/rhel-7.9/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-7.9/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-7.9/cmdline-string.patch b/test/integration/rhel-7.9/cmdline-string.patch new file mode 100644 index 0000000..719445d --- /dev/null +++ b/test/integration/rhel-7.9/cmdline-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2020-09-03 11:48:30.496726119 -0400 ++++ src/fs/proc/cmdline.c 2020-09-03 11:48:33.073734181 -0400 +@@ -5,7 +5,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_printf(m, "%s\n", saved_command_line); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-7.9/data-new-LOADED.test b/test/integration/rhel-7.9/data-new-LOADED.test new file mode 100755 index 0000000..598b6bb --- /dev/null +++ b/test/integration/rhel-7.9/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-7.9/data-new.patch b/test/integration/rhel-7.9/data-new.patch new file mode 100644 index 0000000..2e4e824 --- /dev/null +++ b/test/integration/rhel-7.9/data-new.patch @@ -0,0 +1,28 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-09-03 11:48:30.497726123 -0400 ++++ src/fs/proc/meminfo.c 2020-09-03 11:48:35.069740426 -0400 +@@ -21,6 +21,8 @@ void __attribute__((weak)) arch_report_m + { + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -112,6 +114,7 @@ static int meminfo_proc_show(struct seq_ + "CmaTotal: %8lu kB\n" + "CmaFree: %8lu kB\n" + #endif ++ "kpatch: %d" + , + K(i.totalram), + K(i.freeram), +@@ -178,6 +181,7 @@ static int meminfo_proc_show(struct seq_ + , K(totalcma_pages) + , K(global_page_state(NR_FREE_CMA_PAGES)) + #endif ++ ,foo + ); + + hugetlb_report_meminfo(m); diff --git a/test/integration/rhel-7.9/data-read-mostly.patch.disabled b/test/integration/rhel-7.9/data-read-mostly.patch.disabled new file mode 100644 index 0000000..50f4c9b --- /dev/null +++ b/test/integration/rhel-7.9/data-read-mostly.patch.disabled @@ -0,0 +1,11 @@ +diff -Nupr src.orig/net/core/dev.c src/net/core/dev.c +--- src.orig/net/core/dev.c 2020-09-03 11:48:30.763726955 -0400 ++++ src/net/core/dev.c 2020-09-03 11:49:21.514885728 -0400 +@@ -4327,6 +4327,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-7.9/fixup-section.patch b/test/integration/rhel-7.9/fixup-section.patch new file mode 100644 index 0000000..465e23a --- /dev/null +++ b/test/integration/rhel-7.9/fixup-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/readdir.c src/fs/readdir.c +--- src.orig/fs/readdir.c 2020-09-03 11:48:30.499726129 -0400 ++++ src/fs/readdir.c 2020-09-03 11:48:37.119746839 -0400 +@@ -176,6 +176,7 @@ static int filldir(void * __buf, const c + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-7.9/gcc-constprop.patch b/test/integration/rhel-7.9/gcc-constprop.patch new file mode 100644 index 0000000..4ab8c1b --- /dev/null +++ b/test/integration/rhel-7.9/gcc-constprop.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2020-09-03 11:48:30.726726839 -0400 ++++ src/kernel/time/timekeeping.c 2020-09-03 11:49:23.433891731 -0400 +@@ -852,6 +852,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-7.9/gcc-isra.patch b/test/integration/rhel-7.9/gcc-isra.patch new file mode 100644 index 0000000..6996eeb --- /dev/null +++ b/test/integration/rhel-7.9/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2020-09-03 11:48:30.497726123 -0400 ++++ src/fs/proc/proc_sysctl.c 2020-09-03 11:48:39.089753002 -0400 +@@ -46,6 +46,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-7.9/gcc-mangled-3.patch b/test/integration/rhel-7.9/gcc-mangled-3.patch new file mode 100644 index 0000000..5a7904e --- /dev/null +++ b/test/integration/rhel-7.9/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2020-09-03 11:48:30.747726905 -0400 ++++ src/mm/slub.c 2020-09-03 11:48:41.106759312 -0400 +@@ -5716,6 +5716,9 @@ void get_slabinfo(struct kmem_cache *s, + unsigned long nr_free = 0; + int node; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_online_node(node) { + struct kmem_cache_node *n = get_node(s, node); + diff --git a/test/integration/rhel-7.9/gcc-static-local-var-2.patch b/test/integration/rhel-7.9/gcc-static-local-var-2.patch new file mode 100644 index 0000000..a3e8366 --- /dev/null +++ b/test/integration/rhel-7.9/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/mmap.c src/mm/mmap.c +--- src.orig/mm/mmap.c 2020-09-03 11:48:30.745726898 -0400 ++++ src/mm/mmap.c 2020-09-03 11:48:43.078765482 -0400 +@@ -1721,6 +1721,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-7.9/gcc-static-local-var-3.patch b/test/integration/rhel-7.9/gcc-static-local-var-3.patch new file mode 100644 index 0000000..6d8ae91 --- /dev/null +++ b/test/integration/rhel-7.9/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/kernel/sys.c src/kernel/sys.c +--- src.orig/kernel/sys.c 2020-09-03 11:48:30.725726836 -0400 ++++ src/kernel/sys.c 2020-09-03 11:48:45.101771811 -0400 +@@ -559,8 +559,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-7.9/gcc-static-local-var-4.patch b/test/integration/rhel-7.9/gcc-static-local-var-4.patch new file mode 100644 index 0000000..d63af5b --- /dev/null +++ b/test/integration/rhel-7.9/gcc-static-local-var-4.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2020-09-03 11:48:30.426725900 -0400 ++++ src/fs/aio.c 2020-09-03 11:48:47.163778261 -0400 +@@ -223,9 +223,16 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(aio_ring_file->f_inode, 0); + diff --git a/test/integration/rhel-7.9/gcc-static-local-var-4.test b/test/integration/rhel-7.9/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-7.9/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-7.9/gcc-static-local-var-5.patch b/test/integration/rhel-7.9/gcc-static-local-var-5.patch new file mode 100644 index 0000000..52d7f8b --- /dev/null +++ b/test/integration/rhel-7.9/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2020-09-03 11:48:30.713726798 -0400 ++++ src/kernel/audit.c 2020-09-03 11:48:49.166784528 -0400 +@@ -205,6 +205,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -215,6 +221,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -234,6 +241,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -282,6 +294,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-7.9/gcc-static-local-var-6.patch b/test/integration/rhel-7.9/gcc-static-local-var-6.patch new file mode 100644 index 0000000..2b33da4 --- /dev/null +++ b/test/integration/rhel-7.9/gcc-static-local-var-6.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/net/ipv6/netfilter.c src/net/ipv6/netfilter.c +--- src.orig/net/ipv6/netfilter.c 2020-09-03 11:48:30.779727005 -0400 ++++ src/net/ipv6/netfilter.c 2020-09-03 11:48:51.172790803 -0400 +@@ -112,6 +112,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -125,6 +127,9 @@ static int nf_ip6_route(struct net *net, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-7.9/macro-callbacks.patch b/test/integration/rhel-7.9/macro-callbacks.patch new file mode 100644 index 0000000..96c1a3f --- /dev/null +++ b/test/integration/rhel-7.9/macro-callbacks.patch @@ -0,0 +1,163 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +diff -Nupr src.orig/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.orig/drivers/input/joydev.c 2020-09-03 11:48:29.019721499 -0400 ++++ src/drivers/input/joydev.c 2020-09-03 11:48:53.152796998 -0400 +@@ -954,3 +954,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.orig/drivers/input/misc/pcspkr.c 2020-09-03 11:48:29.025721517 -0400 ++++ src/drivers/input/misc/pcspkr.c 2020-09-03 11:48:53.152796998 -0400 +@@ -136,3 +136,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2020-09-03 11:48:30.426725900 -0400 ++++ src/fs/aio.c 2020-09-03 11:48:53.153797001 -0400 +@@ -42,6 +42,50 @@ + #include + #include + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/rhel-7.9/macro-printk.patch b/test/integration/rhel-7.9/macro-printk.patch new file mode 100644 index 0000000..28e618d --- /dev/null +++ b/test/integration/rhel-7.9/macro-printk.patch @@ -0,0 +1,148 @@ +diff -Nupr src.orig/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c +--- src.orig/net/ipv4/fib_frontend.c 2020-09-03 11:48:30.771726980 -0400 ++++ src/net/ipv4/fib_frontend.c 2020-09-03 11:48:55.130803186 -0400 +@@ -690,6 +690,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) + { + struct net *net = sock_net(skb->sk); +@@ -708,6 +709,7 @@ static int inet_rtm_newroute(struct sk_b + } + + err = fib_table_insert(net, tb, &cfg); ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c +--- src.orig/net/ipv4/fib_semantics.c 2020-09-03 11:48:30.771726980 -0400 ++++ src/net/ipv4/fib_semantics.c 2020-09-03 11:48:55.130803186 -0400 +@@ -985,6 +985,7 @@ fib_convert_metrics(struct fib_info *fi, + return 0; + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg) + { + int err; +@@ -1009,6 +1010,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1029,6 +1031,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (fi == NULL) +@@ -1044,6 +1047,8 @@ struct fib_info *fib_create_info(struct + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1059,8 +1064,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1111,6 +1118,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) +@@ -1128,6 +1136,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) + goto err_inval; +@@ -1150,6 +1159,7 @@ struct fib_info *fib_create_info(struct + goto failure; + } endfor_nexthops(fi) + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc) { + if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || +@@ -1162,6 +1172,7 @@ struct fib_info *fib_create_info(struct + fib_info_update_nh_saddr(net, nexthop_nh); + fib_add_weight(fi, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1173,6 +1184,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + atomic_inc(&fi->fib_clntref); +@@ -1196,6 +1208,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1206,6 +1219,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff -Nupr src.orig/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c +--- src.orig/net/ipv4/fib_trie.c 2020-09-03 11:48:30.771726980 -0400 ++++ src/net/ipv4/fib_trie.c 2020-09-03 11:48:55.131803189 -0400 +@@ -1105,6 +1105,7 @@ static int fib_insert_alias(struct trie + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg) + { +@@ -1130,11 +1131,14 @@ int fib_table_insert(struct net *net, st + if ((plen < KEYLENGTH) && (key << plen)) + return -EINVAL; + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-7.9/meminfo-init-FAIL.patch b/test/integration/rhel-7.9/meminfo-init-FAIL.patch new file mode 100644 index 0000000..ad677ca --- /dev/null +++ b/test/integration/rhel-7.9/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-09-03 11:48:30.497726123 -0400 ++++ src/fs/proc/meminfo.c 2020-09-03 11:48:59.106815625 -0400 +@@ -202,6 +202,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.9/meminfo-init2-FAIL.patch b/test/integration/rhel-7.9/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..792028e --- /dev/null +++ b/test/integration/rhel-7.9/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-09-03 11:48:30.497726123 -0400 ++++ src/fs/proc/meminfo.c 2020-09-03 11:48:57.163809546 -0400 +@@ -31,6 +31,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long pages[NR_LRU_LISTS]; + int lru; + ++ printk("a\n"); + /* + * display in kilobytes. + */ +@@ -202,6 +203,7 @@ static const struct file_operations memi + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create("meminfo", 0, NULL, &meminfo_proc_fops); + return 0; + } diff --git a/test/integration/rhel-7.9/meminfo-string-LOADED.test b/test/integration/rhel-7.9/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-7.9/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-7.9/meminfo-string.patch b/test/integration/rhel-7.9/meminfo-string.patch new file mode 100644 index 0000000..7c8114e --- /dev/null +++ b/test/integration/rhel-7.9/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-09-03 11:48:30.497726123 -0400 ++++ src/fs/proc/meminfo.c 2020-09-03 11:49:01.546823258 -0400 +@@ -100,7 +100,7 @@ static int meminfo_proc_show(struct seq_ + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" +- "VmallocChunk: %8lu kB\n" ++ "VMALLOCCHUNK: %8lu kB\n" + "Percpu: %8lu kB\n" + #ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" diff --git a/test/integration/rhel-7.9/module-call-external.patch b/test/integration/rhel-7.9/module-call-external.patch new file mode 100644 index 0000000..e346edc --- /dev/null +++ b/test/integration/rhel-7.9/module-call-external.patch @@ -0,0 +1,38 @@ +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2020-09-03 11:48:30.477726060 -0400 ++++ src/fs/nfsd/export.c 2020-09-03 11:49:03.743830132 -0400 +@@ -1184,7 +1184,13 @@ static void exp_flags(struct seq_file *m + } + } + ++extern char *kpatch_string(void); ++ ++#ifdef CONFIG_PPC64 ++static int __attribute__((optimize("-fno-optimize-sibling-calls"))) e_show(struct seq_file *m, void *p) ++#else + static int e_show(struct seq_file *m, void *p) ++#endif + { + struct cache_head *cp = p; + struct svc_export *exp = container_of(cp, struct svc_export, h); +@@ -1193,6 +1199,7 @@ static int e_show(struct seq_file *m, vo + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2020-09-03 11:48:30.802727077 -0400 ++++ src/net/netlink/af_netlink.c 2020-09-03 11:49:03.743830132 -0400 +@@ -2573,4 +2573,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-7.9/multiple.test b/test/integration/rhel-7.9/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-7.9/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-7.9/new-function.patch b/test/integration/rhel-7.9/new-function.patch new file mode 100644 index 0000000..e83487a --- /dev/null +++ b/test/integration/rhel-7.9/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2020-09-03 11:48:29.609723344 -0400 ++++ src/drivers/tty/n_tty.c 2020-09-03 11:49:05.751836414 -0400 +@@ -2175,7 +2175,7 @@ do_it_again: + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2264,6 +2264,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-7.9/new-globals.patch b/test/integration/rhel-7.9/new-globals.patch new file mode 100644 index 0000000..4840c97 --- /dev/null +++ b/test/integration/rhel-7.9/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2020-09-03 11:48:30.496726119 -0400 ++++ src/fs/proc/cmdline.c 2020-09-03 11:49:07.740842636 -0400 +@@ -27,3 +27,10 @@ static int __init proc_cmdline_init(void + return 0; + } + module_init(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-09-03 11:48:30.497726123 -0400 ++++ src/fs/proc/meminfo.c 2020-09-03 11:49:07.740842636 -0400 +@@ -17,6 +17,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -54,6 +56,7 @@ static int meminfo_proc_show(struct seq_ + /* + * Tagged format, for easy grepping and expansion. + */ ++ kpatch_print_message(); + seq_printf(m, + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" diff --git a/test/integration/rhel-7.9/parainstructions-section.patch b/test/integration/rhel-7.9/parainstructions-section.patch new file mode 100644 index 0000000..d7a02f9 --- /dev/null +++ b/test/integration/rhel-7.9/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2020-09-03 11:48:30.496726119 -0400 ++++ src/fs/proc/generic.c 2020-09-03 11:49:09.715848815 -0400 +@@ -194,6 +194,7 @@ int proc_alloc_inum(unsigned int *inum) + unsigned int i; + int error; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + retry: + if (!ida_pre_get(&proc_inum_ida, GFP_KERNEL)) + return -ENOMEM; diff --git a/test/integration/rhel-7.9/shadow-newpid-LOADED.test b/test/integration/rhel-7.9/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-7.9/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-7.9/shadow-newpid.patch b/test/integration/rhel-7.9/shadow-newpid.patch new file mode 100644 index 0000000..2c4ae45 --- /dev/null +++ b/test/integration/rhel-7.9/shadow-newpid.patch @@ -0,0 +1,69 @@ +diff -Nupr src.orig/fs/proc/array.c src/fs/proc/array.c +--- src.orig/fs/proc/array.c 2020-09-03 11:48:30.496726119 -0400 ++++ src/fs/proc/array.c 2020-09-03 11:49:11.696855012 -0400 +@@ -395,13 +395,20 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_printf(m, "voluntary_ctxt_switches:\t%lu\n" + "nonvoluntary_ctxt_switches:\t%lu\n", + p->nvcsw, + p->nivcsw); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff -Nupr src.orig/kernel/exit.c src/kernel/exit.c +--- src.orig/kernel/exit.c 2020-09-03 11:48:30.717726811 -0400 ++++ src/kernel/exit.c 2020-09-03 11:49:11.696855012 -0400 +@@ -791,6 +791,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void do_exit(long code) + { + struct task_struct *tsk = current; +@@ -888,6 +889,8 @@ void do_exit(long code) + check_stack_usage(); + exit_thread(); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2020-09-03 11:48:30.717726811 -0400 ++++ src/kernel/fork.c 2020-09-03 11:49:11.697855015 -0400 +@@ -1784,6 +1784,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -1821,6 +1822,13 @@ long do_fork(unsigned long clone_flags, + if (!IS_ERR(p)) { + struct completion vfork; + struct pid *pid; ++ int *newpid; ++ static int ctr = 0; ++ ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; + + trace_sched_process_fork(current, p); + diff --git a/test/integration/rhel-7.9/smp-locks-section.patch b/test/integration/rhel-7.9/smp-locks-section.patch new file mode 100644 index 0000000..a044e41 --- /dev/null +++ b/test/integration/rhel-7.9/smp-locks-section.patch @@ -0,0 +1,14 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2020-09-03 11:48:29.616723366 -0400 ++++ src/drivers/tty/tty_buffer.c 2020-09-03 11:49:13.626861050 -0400 +@@ -217,6 +217,10 @@ int tty_buffer_request_room(struct tty_p + /* OPTIMISATION: We could keep a per tty "zero" sized buffer to + remove this conditional if its worth it. This would be invisible + to the callers */ ++ ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b != NULL) + left = b->size - b->used; diff --git a/test/integration/rhel-7.9/special-static.patch b/test/integration/rhel-7.9/special-static.patch new file mode 100644 index 0000000..c098331 --- /dev/null +++ b/test/integration/rhel-7.9/special-static.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2020-09-03 11:48:30.717726811 -0400 ++++ src/kernel/fork.c 2020-09-03 11:49:15.602867232 -0400 +@@ -1153,10 +1153,18 @@ static void posix_cpu_timers_init_group( + INIT_LIST_HEAD(&sig->cpu_timers[2]); + } + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-7.9/symvers-disagreement-FAIL.patch b/test/integration/rhel-7.9/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..300f8b9 --- /dev/null +++ b/test/integration/rhel-7.9/symvers-disagreement-FAIL.patch @@ -0,0 +1,49 @@ +From da109d66a890373d0aa831f97b49aaffcc4eeb45 Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes a function referencing a function whose CRC has changed, + causing the symbol and the new CRC to be included in the __version + section of the final module. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/base/core.c b/drivers/base/core.c +index b9a71137208..4af27e069c2 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -31,6 +31,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index 8b1c4a5ee78..1b7997b58c0 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -693,6 +693,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; +-- +2.21.3 + diff --git a/test/integration/rhel-7.9/tracepoints-section.patch b/test/integration/rhel-7.9/tracepoints-section.patch new file mode 100644 index 0000000..33a15ed --- /dev/null +++ b/test/integration/rhel-7.9/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/timer.c src/kernel/timer.c +--- src.orig/kernel/timer.c 2020-09-03 11:48:30.726726839 -0400 ++++ src/kernel/timer.c 2020-09-03 11:49:17.588873445 -0400 +@@ -1454,6 +1454,9 @@ static void run_timer_softirq(struct sof + { + struct tvec_base *base = __this_cpu_read(tvec_bases); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + if (time_after_eq(jiffies, base->timer_jiffies)) + __run_timers(base); + } diff --git a/test/integration/rhel-7.9/warn-detect-FAIL.patch b/test/integration/rhel-7.9/warn-detect-FAIL.patch new file mode 100644 index 0000000..2f51ba7 --- /dev/null +++ b/test/integration/rhel-7.9/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2020-09-03 11:48:30.386725775 -0400 ++++ src/arch/x86/kvm/x86.c 2020-09-03 11:49:19.587879699 -0400 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-8.0/README b/test/integration/rhel-8.0/README new file mode 100644 index 0000000..d46fbcb --- /dev/null +++ b/test/integration/rhel-8.0/README @@ -0,0 +1 @@ +4.18.0-80.el8 diff --git a/test/integration/rhel-8.0/bug-table-section.patch b/test/integration/rhel-8.0/bug-table-section.patch new file mode 100644 index 0000000..842ae4a --- /dev/null +++ b/test/integration/rhel-8.0/bug-table-section.patch @@ -0,0 +1,13 @@ +diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c +index 89921a0..ac129b6 100644 +--- a/fs/proc/proc_sysctl.c ++++ b/fs/proc/proc_sysctl.c +@@ -333,6 +333,8 @@ static void start_unregistering(struct ctl_table_header *p) + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-8.0/cmdline-string-LOADED.test b/test/integration/rhel-8.0/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-8.0/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-8.0/cmdline-string.patch b/test/integration/rhel-8.0/cmdline-string.patch new file mode 100644 index 0000000..665c763 --- /dev/null +++ b/test/integration/rhel-8.0/cmdline-string.patch @@ -0,0 +1,14 @@ +diff --git a/fs/proc/cmdline.c b/fs/proc/cmdline.c +index fa762c5..bd66027 100644 +--- a/fs/proc/cmdline.c ++++ b/fs/proc/cmdline.c +@@ -6,8 +6,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_puts(m, saved_command_line); +- seq_putc(m, '\n'); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-8.0/data-new-LOADED.test b/test/integration/rhel-8.0/data-new-LOADED.test new file mode 100755 index 0000000..9f25744 --- /dev/null +++ b/test/integration/rhel-8.0/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-8.0/data-new.patch b/test/integration/rhel-8.0/data-new.patch new file mode 100644 index 0000000..78d1b97 --- /dev/null +++ b/test/integration/rhel-8.0/data-new.patch @@ -0,0 +1,21 @@ +diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c +index 2fb0484..79ead86 100644 +--- a/fs/proc/meminfo.c ++++ b/fs/proc/meminfo.c +@@ -30,6 +30,8 @@ static void show_val_kb(struct seq_file *m, const char *s, unsigned long num) + seq_write(m, " kB\n", 4); + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -141,6 +143,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) + show_val_kb(m, "CmaFree: ", + global_zone_page_state(NR_FREE_CMA_PAGES)); + #endif ++ seq_printf(m, "kpatch: %d\n", foo); + + hugetlb_report_meminfo(m); + diff --git a/test/integration/rhel-8.0/data-read-mostly.patch.disabled b/test/integration/rhel-8.0/data-read-mostly.patch.disabled new file mode 100644 index 0000000..ed4eef0 --- /dev/null +++ b/test/integration/rhel-8.0/data-read-mostly.patch.disabled @@ -0,0 +1,14 @@ +Disabled due to https://github.com/dynup/kpatch/issues/940 +--- +diff --git a/net/core/dev.c b/net/core/dev.c +index b6f9647..b376a48 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -4858,6 +4858,7 @@ static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc) + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-8.0/fixup-section.patch b/test/integration/rhel-8.0/fixup-section.patch new file mode 100644 index 0000000..78ab6dd --- /dev/null +++ b/test/integration/rhel-8.0/fixup-section.patch @@ -0,0 +1,12 @@ +diff --git a/fs/readdir.c b/fs/readdir.c +index d97f548..58863b6 100644 +--- a/fs/readdir.c ++++ b/fs/readdir.c +@@ -189,6 +189,7 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen, + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-8.0/gcc-constprop.patch.disabled b/test/integration/rhel-8.0/gcc-constprop.patch.disabled new file mode 100644 index 0000000..3fbc577 --- /dev/null +++ b/test/integration/rhel-8.0/gcc-constprop.patch.disabled @@ -0,0 +1,14 @@ +diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c +index 4786df9..c73a687 100644 +--- a/kernel/time/timekeeping.c ++++ b/kernel/time/timekeeping.c +@@ -1211,6 +1211,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-8.0/gcc-isra.patch b/test/integration/rhel-8.0/gcc-isra.patch new file mode 100644 index 0000000..0903f24 --- /dev/null +++ b/test/integration/rhel-8.0/gcc-isra.patch @@ -0,0 +1,12 @@ +diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c +index 89921a0..199b1d7 100644 +--- a/fs/proc/proc_sysctl.c ++++ b/fs/proc/proc_sysctl.c +@@ -48,6 +48,7 @@ void proc_sys_poll_notify(struct ctl_table_poll *poll) + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-8.0/gcc-mangled-3.patch b/test/integration/rhel-8.0/gcc-mangled-3.patch new file mode 100644 index 0000000..bcb5e62 --- /dev/null +++ b/test/integration/rhel-8.0/gcc-mangled-3.patch @@ -0,0 +1,14 @@ +diff --git a/mm/slub.c b/mm/slub.c +index 51258ef..3cb5264 100644 +--- a/mm/slub.c ++++ b/mm/slub.c +@@ -5852,6 +5852,9 @@ void get_slabinfo(struct kmem_cache *s, struct slabinfo *sinfo) + int node; + struct kmem_cache_node *n; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_kmem_cache_node(s, node, n) { + nr_slabs += node_nr_slabs(n); + nr_objs += node_nr_objs(n); diff --git a/test/integration/rhel-8.0/gcc-static-local-var-2.patch b/test/integration/rhel-8.0/gcc-static-local-var-2.patch new file mode 100644 index 0000000..a884bfa --- /dev/null +++ b/test/integration/rhel-8.0/gcc-static-local-var-2.patch @@ -0,0 +1,14 @@ +diff --git a/mm/mmap.c b/mm/mmap.c +index bf46096..4bc085c 100644 +--- a/mm/mmap.c ++++ b/mm/mmap.c +@@ -1684,6 +1684,9 @@ unsigned long mmap_region(struct file *file, unsigned long addr, + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, vm_flags, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-8.0/gcc-static-local-var-3.patch b/test/integration/rhel-8.0/gcc-static-local-var-3.patch new file mode 100644 index 0000000..da2acc0 --- /dev/null +++ b/test/integration/rhel-8.0/gcc-static-local-var-3.patch @@ -0,0 +1,20 @@ +diff --git a/kernel/reboot.c b/kernel/reboot.c +index e4ced88..0c0b5a2 100644 +--- a/kernel/reboot.c ++++ b/kernel/reboot.c +@@ -393,8 +393,15 @@ void kernel_power_off(void) + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-8.0/gcc-static-local-var-4.patch.disabled b/test/integration/rhel-8.0/gcc-static-local-var-4.patch.disabled new file mode 100644 index 0000000..17e269d --- /dev/null +++ b/test/integration/rhel-8.0/gcc-static-local-var-4.patch.disabled @@ -0,0 +1,25 @@ +Disabled due to https://github.com/dynup/kpatch/issues/940 +--- +diff --git a/fs/aio.c b/fs/aio.c +index e1f8f01..4dfb05c 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -263,11 +263,18 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; + struct address_space *i_mapping; + ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(file_inode(aio_ring_file), 0); + diff --git a/test/integration/rhel-8.0/gcc-static-local-var-4.test b/test/integration/rhel-8.0/gcc-static-local-var-4.test new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-8.0/gcc-static-local-var-4.test @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-8.0/gcc-static-local-var-5.patch b/test/integration/rhel-8.0/gcc-static-local-var-5.patch new file mode 100644 index 0000000..0e3e2d3 --- /dev/null +++ b/test/integration/rhel-8.0/gcc-static-local-var-5.patch @@ -0,0 +1,46 @@ +diff --git a/kernel/audit.c b/kernel/audit.c +index e7478cb..ed2546a 100644 +--- a/kernel/audit.c ++++ b/kernel/audit.c +@@ -325,6 +325,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -335,6 +341,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -354,6 +361,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -400,6 +412,8 @@ static int audit_log_config_change(char *function_name, u32 new, u32 old, + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-8.0/gcc-static-local-var-6.patch b/test/integration/rhel-8.0/gcc-static-local-var-6.patch new file mode 100644 index 0000000..91c72b6 --- /dev/null +++ b/test/integration/rhel-8.0/gcc-static-local-var-6.patch @@ -0,0 +1,23 @@ +diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c +index 531d695..a536c74 100644 +--- a/net/ipv6/netfilter.c ++++ b/net/ipv6/netfilter.c +@@ -84,6 +84,8 @@ static int nf_ip6_reroute(struct sk_buff *skb, + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -97,6 +99,9 @@ static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-8.0/macro-callbacks.patch b/test/integration/rhel-8.0/macro-callbacks.patch new file mode 100644 index 0000000..b7f0077 --- /dev/null +++ b/test/integration/rhel-8.0/macro-callbacks.patch @@ -0,0 +1,158 @@ +diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c +index 4c1e427..7e46aaf 100644 +--- a/drivers/input/joydev.c ++++ b/drivers/input/joydev.c +@@ -1068,3 +1068,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c +index 56ddba2..d188b12 100644 +--- a/drivers/input/misc/pcspkr.c ++++ b/drivers/input/misc/pcspkr.c +@@ -138,3 +138,46 @@ static void pcspkr_shutdown(struct platform_device *dev) + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff --git a/fs/aio.c b/fs/aio.c +index e1f8f01..5efe496 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -49,6 +49,50 @@ + + #define KIOCB_KEY 0 + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/rhel-8.0/macro-printk.patch.disabled b/test/integration/rhel-8.0/macro-printk.patch.disabled new file mode 100644 index 0000000..dfe0586 --- /dev/null +++ b/test/integration/rhel-8.0/macro-printk.patch.disabled @@ -0,0 +1,151 @@ +diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c +index 0113993..0ea4967 100644 +--- a/net/ipv4/fib_frontend.c ++++ b/net/ipv4/fib_frontend.c +@@ -767,6 +767,7 @@ static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) + { +@@ -788,6 +789,7 @@ static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + err = fib_table_insert(net, tb, &cfg, extack); + if (!err && cfg.fc_type == RTN_LOCAL) + net->ipv4.fib_has_custom_local_routes = true; ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c +index 446204c..5b73a01 100644 +--- a/net/ipv4/fib_semantics.c ++++ b/net/ipv4/fib_semantics.c +@@ -1025,6 +1025,7 @@ static bool fib_valid_prefsrc(struct fib_config *cfg, __be32 fib_prefsrc) + fi->fib_metrics->metrics); + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg, + struct netlink_ext_ack *extack) + { +@@ -1058,6 +1059,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1078,6 +1080,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (!fi) +@@ -1093,6 +1096,8 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1109,8 +1114,10 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1172,6 +1179,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) { +@@ -1193,6 +1201,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) { + NL_SET_ERR_MSG(extack, "Invalid scope"); +@@ -1231,6 +1240,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + if (linkdown == fi->fib_nhs) + fi->fib_flags |= RTNH_F_LINKDOWN; + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) { + NL_SET_ERR_MSG(extack, "Invalid prefsrc address"); +@@ -1240,6 +1250,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + change_nexthops(fi) { + fib_info_update_nh_saddr(net, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1251,6 +1262,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + refcount_set(&fi->fib_clntref, 1); +@@ -1274,6 +1286,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1284,6 +1297,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c +index 5bc0c89..d57b327 100644 +--- a/net/ipv4/fib_trie.c ++++ b/net/ipv4/fib_trie.c +@@ -1121,6 +1121,7 @@ static bool fib_valid_key_len(u32 key, u8 plen, struct netlink_ext_ack *extack) + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg, struct netlink_ext_ack *extack) + { +@@ -1143,11 +1144,14 @@ int fib_table_insert(struct net *net, struct fib_table *tb, + + pr_debug("Insert table=%u %08x/%d\n", tb->tb_id, key, plen); + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg, extack); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-8.0/meminfo-init-FAIL.patch b/test/integration/rhel-8.0/meminfo-init-FAIL.patch new file mode 100644 index 0000000..a3361ed --- /dev/null +++ b/test/integration/rhel-8.0/meminfo-init-FAIL.patch @@ -0,0 +1,12 @@ +diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c +index 2fb0484..b981fab 100644 +--- a/fs/proc/meminfo.c ++++ b/fs/proc/meminfo.c +@@ -151,6 +151,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.0/meminfo-init2-FAIL.patch b/test/integration/rhel-8.0/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..28f8445 --- /dev/null +++ b/test/integration/rhel-8.0/meminfo-init2-FAIL.patch @@ -0,0 +1,20 @@ +diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c +index 2fb0484..eb61884 100644 +--- a/fs/proc/meminfo.c ++++ b/fs/proc/meminfo.c +@@ -39,6 +39,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) + unsigned long pages[NR_LRU_LISTS]; + int lru; + ++ printk("a\n"); + si_meminfo(&i); + si_swapinfo(&i); + committed = percpu_counter_read_positive(&vm_committed_as); +@@ -151,6 +152,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.0/meminfo-string-LOADED.test b/test/integration/rhel-8.0/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-8.0/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-8.0/meminfo-string.patch b/test/integration/rhel-8.0/meminfo-string.patch new file mode 100644 index 0000000..e20210f --- /dev/null +++ b/test/integration/rhel-8.0/meminfo-string.patch @@ -0,0 +1,13 @@ +diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c +index 2fb0484..804bc35 100644 +--- a/fs/proc/meminfo.c ++++ b/fs/proc/meminfo.c +@@ -120,7 +120,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) + seq_printf(m, "VmallocTotal: %8lu kB\n", + (unsigned long)VMALLOC_TOTAL >> 10); + show_val_kb(m, "VmallocUsed: ", 0ul); +- show_val_kb(m, "VmallocChunk: ", 0ul); ++ show_val_kb(m, "VMALLOCCHUNK: ", 0ul); + + #ifdef CONFIG_MEMORY_FAILURE + seq_printf(m, "HardwareCorrupted: %5lu kB\n", diff --git a/test/integration/rhel-8.0/module-call-external.patch b/test/integration/rhel-8.0/module-call-external.patch new file mode 100644 index 0000000..44e546f --- /dev/null +++ b/test/integration/rhel-8.0/module-call-external.patch @@ -0,0 +1,35 @@ +diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c +index a1143f7..950403a 100644 +--- a/fs/nfsd/export.c ++++ b/fs/nfsd/export.c +@@ -1196,6 +1196,8 @@ static void exp_flags(struct seq_file *m, int flag, int fsid, + } + } + ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; +@@ -1205,6 +1207,7 @@ static int e_show(struct seq_file *m, void *p) + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index 56704d9..851d41d 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -2779,4 +2779,9 @@ static int __init netlink_proto_init(void) + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-8.0/multiple.test b/test/integration/rhel-8.0/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-8.0/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-8.0/new-function.patch b/test/integration/rhel-8.0/new-function.patch new file mode 100644 index 0000000..f1f21a7 --- /dev/null +++ b/test/integration/rhel-8.0/new-function.patch @@ -0,0 +1,26 @@ +diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c +index 3ad4602..f3cda7e 100644 +--- a/drivers/tty/n_tty.c ++++ b/drivers/tty/n_tty.c +@@ -2296,7 +2296,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2383,6 +2383,12 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-8.0/new-globals.patch b/test/integration/rhel-8.0/new-globals.patch new file mode 100644 index 0000000..38892a4 --- /dev/null +++ b/test/integration/rhel-8.0/new-globals.patch @@ -0,0 +1,36 @@ +diff --git a/fs/proc/cmdline.c b/fs/proc/cmdline.c +index fa762c5..cc67970 100644 +--- a/fs/proc/cmdline.c ++++ b/fs/proc/cmdline.c +@@ -17,3 +17,10 @@ static int __init proc_cmdline_init(void) + return 0; + } + fs_initcall(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c +index 2fb0484..7dd8350 100644 +--- a/fs/proc/meminfo.c ++++ b/fs/proc/meminfo.c +@@ -20,6 +20,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -53,6 +55,7 @@ static int meminfo_proc_show(struct seq_file *m, void *v) + + available = si_mem_available(); + ++ kpatch_print_message(); + show_val_kb(m, "MemTotal: ", i.totalram); + show_val_kb(m, "MemFree: ", i.freeram); + show_val_kb(m, "MemAvailable: ", available); diff --git a/test/integration/rhel-8.0/parainstructions-section.patch b/test/integration/rhel-8.0/parainstructions-section.patch new file mode 100644 index 0000000..d3af7a6 --- /dev/null +++ b/test/integration/rhel-8.0/parainstructions-section.patch @@ -0,0 +1,12 @@ +diff --git a/fs/proc/generic.c b/fs/proc/generic.c +index bb1c162..2b3f7ec 100644 +--- a/fs/proc/generic.c ++++ b/fs/proc/generic.c +@@ -205,6 +205,7 @@ int proc_alloc_inum(unsigned int *inum) + { + int i; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + i = ida_simple_get(&proc_inum_ida, 0, UINT_MAX - PROC_DYNAMIC_FIRST + 1, + GFP_KERNEL); + if (i < 0) diff --git a/test/integration/rhel-8.0/shadow-newpid-LOADED.test b/test/integration/rhel-8.0/shadow-newpid-LOADED.test new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-8.0/shadow-newpid-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-8.0/shadow-newpid.patch.disabled b/test/integration/rhel-8.0/shadow-newpid.patch.disabled new file mode 100644 index 0000000..19109a3 --- /dev/null +++ b/test/integration/rhel-8.0/shadow-newpid.patch.disabled @@ -0,0 +1,80 @@ +Disabled due to https://github.com/dynup/kpatch/issues/940 +--- +diff --git a/fs/proc/array.c b/fs/proc/array.c +index 0ceb3b6..1b44c7f 100644 +--- a/fs/proc/array.c ++++ b/fs/proc/array.c +@@ -370,12 +370,19 @@ static inline void task_seccomp(struct seq_file *m, struct task_struct *p) + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw); + seq_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw); + seq_putc(m, '\n'); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff --git a/kernel/exit.c b/kernel/exit.c +index deaa53a..01f5776 100644 +--- a/kernel/exit.c ++++ b/kernel/exit.c +@@ -760,6 +760,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void __noreturn do_exit(long code) + { + struct task_struct *tsk = current; +@@ -865,6 +866,8 @@ void __noreturn do_exit(long code) + exit_task_work(tsk); + exit_thread(tsk); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff --git a/kernel/fork.c b/kernel/fork.c +index 3311231..2c12c1a 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -2093,6 +2093,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long _do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -2105,6 +2106,8 @@ long _do_fork(unsigned long clone_flags, + struct task_struct *p; + int trace = 0; + long nr; ++ int *newpid; ++ static int ctr = 0; + + /* + * Determine whether and which event to report to ptracer. When +@@ -2131,6 +2134,11 @@ long _do_fork(unsigned long clone_flags, + if (IS_ERR(p)) + return PTR_ERR(p); + ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; ++ + /* + * Do this prior waking up the new thread - the thread pointer + * might get invalid after that point, if the thread exits quickly. diff --git a/test/integration/rhel-8.0/smp-locks-section.patch b/test/integration/rhel-8.0/smp-locks-section.patch new file mode 100644 index 0000000..5166614 --- /dev/null +++ b/test/integration/rhel-8.0/smp-locks-section.patch @@ -0,0 +1,14 @@ +diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c +index ae3ce33..37727fe 100644 +--- a/drivers/tty/tty_buffer.c ++++ b/drivers/tty/tty_buffer.c +@@ -256,6 +256,9 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size, + struct tty_buffer *b, *n; + int left, change; + ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b->flags & TTYB_NORMAL) + left = 2 * b->size - b->used; diff --git a/test/integration/rhel-8.0/special-static.patch b/test/integration/rhel-8.0/special-static.patch new file mode 100644 index 0000000..22502df --- /dev/null +++ b/test/integration/rhel-8.0/special-static.patch @@ -0,0 +1,23 @@ +diff --git a/kernel/fork.c b/kernel/fork.c +index 3311231..f6e66b7 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -1461,10 +1461,18 @@ static void posix_cpu_timers_init_group(struct signal_struct *sig) + static inline void posix_cpu_timers_init_group(struct signal_struct *sig) { } + #endif + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-8.0/symvers-disagreement-FAIL.patch b/test/integration/rhel-8.0/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..e3a672a --- /dev/null +++ b/test/integration/rhel-8.0/symvers-disagreement-FAIL.patch @@ -0,0 +1,51 @@ +From 7085a655b8d665b6314e8dab2f803bac0aea04ec Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes usb_get_dev() referencing a get_device() whose CRC has + changed, causing the symbol and the new CRC to be included in the + __version section of the final module. + +This makes the final module unloadable for the target kernel. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/base/core.c b/drivers/base/core.c +index df3e1a44707a..15c9d6e2e1e0 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -29,6 +29,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index 623be3174fb3..4ddd74ae0bb9 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -685,6 +685,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; +-- +2.21.3 + diff --git a/test/integration/rhel-8.0/tracepoints-section.patch b/test/integration/rhel-8.0/tracepoints-section.patch new file mode 100644 index 0000000..0dfea0c --- /dev/null +++ b/test/integration/rhel-8.0/tracepoints-section.patch @@ -0,0 +1,14 @@ +diff --git a/kernel/time/timer.c b/kernel/time/timer.c +index 786f8c0..1105697 100644 +--- a/kernel/time/timer.c ++++ b/kernel/time/timer.c +@@ -1692,6 +1692,9 @@ static __latent_entropy void run_timer_softirq(struct softirq_action *h) + { + struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + __run_timers(base); + if (IS_ENABLED(CONFIG_NO_HZ_COMMON)) + __run_timers(this_cpu_ptr(&timer_bases[BASE_DEF])); diff --git a/test/integration/rhel-8.0/warn-detect-FAIL.patch b/test/integration/rhel-8.0/warn-detect-FAIL.patch new file mode 100644 index 0000000..f57bd0e --- /dev/null +++ b/test/integration/rhel-8.0/warn-detect-FAIL.patch @@ -0,0 +1,9 @@ +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index 4fa858b..ef1a710 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-8.1/bug-table-section.patch b/test/integration/rhel-8.1/bug-table-section.patch new file mode 100644 index 0000000..b37f545 --- /dev/null +++ b/test/integration/rhel-8.1/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src/fs/proc/proc_sysctl.c 2020-03-11 11:23:26.886602663 +0000 ++++ src/fs/proc/proc_sysctl.c 2020-03-11 11:23:39.822895153 +0000 +@@ -333,6 +333,8 @@ static void start_unregistering(struct c + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-8.1/cmdline-string-LOADED.test b/test/integration/rhel-8.1/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-8.1/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-8.1/cmdline-string.patch b/test/integration/rhel-8.1/cmdline-string.patch new file mode 100644 index 0000000..2ba1762 --- /dev/null +++ b/test/integration/rhel-8.1/cmdline-string.patch @@ -0,0 +1,13 @@ +diff -Nupr src/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src/fs/proc/cmdline.c 2020-03-11 11:23:26.878602482 +0000 ++++ src/fs/proc/cmdline.c 2020-03-11 11:24:37.984218859 +0000 +@@ -6,8 +6,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_puts(m, saved_command_line); +- seq_putc(m, '\n'); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-8.1/data-new-LOADED.test b/test/integration/rhel-8.1/data-new-LOADED.test new file mode 100755 index 0000000..9f25744 --- /dev/null +++ b/test/integration/rhel-8.1/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-8.1/data-new.patch b/test/integration/rhel-8.1/data-new.patch new file mode 100644 index 0000000..a33a233 --- /dev/null +++ b/test/integration/rhel-8.1/data-new.patch @@ -0,0 +1,20 @@ +diff -Nupr src/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src/fs/proc/meminfo.c 2020-03-11 11:23:26.882602572 +0000 ++++ src/fs/proc/meminfo.c 2020-03-11 11:25:35.401538436 +0000 +@@ -30,6 +30,8 @@ static void show_val_kb(struct seq_file + seq_write(m, " kB\n", 4); + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -141,6 +143,7 @@ static int meminfo_proc_show(struct seq_ + show_val_kb(m, "CmaFree: ", + global_zone_page_state(NR_FREE_CMA_PAGES)); + #endif ++ seq_printf(m, "kpatch: %d\n", foo); + + hugetlb_report_meminfo(m); + diff --git a/test/integration/rhel-8.1/data-read-mostly.patch.disabled b/test/integration/rhel-8.1/data-read-mostly.patch.disabled new file mode 100644 index 0000000..3569b7d --- /dev/null +++ b/test/integration/rhel-8.1/data-read-mostly.patch.disabled @@ -0,0 +1,13 @@ +Disabled due to https:/github.com/dynup/kpatch/issues/940 +--- +diff -Nupr src/net/core/dev.c src/net/core/dev.c +--- src/net/core/dev.c 2020-03-11 11:23:32.550730639 +0000 ++++ src/net/core/dev.c 2020-03-11 11:43:53.348022834 +0000 +@@ -4893,6 +4893,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-8.1/fixup-section.patch b/test/integration/rhel-8.1/fixup-section.patch new file mode 100644 index 0000000..7446a29 --- /dev/null +++ b/test/integration/rhel-8.1/fixup-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src/fs/readdir.c src/fs/readdir.c +--- src/fs/readdir.c 2020-03-11 11:23:24.210542249 +0000 ++++ src/fs/readdir.c 2020-03-11 11:26:32.322857837 +0000 +@@ -189,6 +189,7 @@ static int filldir(struct dir_context *c + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-8.1/gcc-constprop.patch.disabled b/test/integration/rhel-8.1/gcc-constprop.patch.disabled new file mode 100644 index 0000000..1c8ca71 --- /dev/null +++ b/test/integration/rhel-8.1/gcc-constprop.patch.disabled @@ -0,0 +1,13 @@ +diff -Nupr src/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src/kernel/time/timekeeping.c 2020-03-11 11:23:30.538685163 +0000 ++++ src/kernel/time/timekeeping.c 2020-03-11 11:44:47.885367244 +0000 +@@ -1221,6 +1221,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-8.1/gcc-isra.patch b/test/integration/rhel-8.1/gcc-isra.patch new file mode 100644 index 0000000..3ae952f --- /dev/null +++ b/test/integration/rhel-8.1/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src/fs/proc/proc_sysctl.c 2020-03-11 11:23:26.886602663 +0000 ++++ src/fs/proc/proc_sysctl.c 2020-03-11 11:27:30.392214139 +0000 +@@ -48,6 +48,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-8.1/gcc-mangled-3.patch b/test/integration/rhel-8.1/gcc-mangled-3.patch new file mode 100644 index 0000000..65e757a --- /dev/null +++ b/test/integration/rhel-8.1/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src/mm/slub.c src/mm/slub.c +--- src/mm/slub.c 2020-03-11 11:23:32.406727384 +0000 ++++ src/mm/slub.c 2020-03-11 11:28:27.973568215 +0000 +@@ -5836,6 +5836,9 @@ void get_slabinfo(struct kmem_cache *s, + int node; + struct kmem_cache_node *n; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_kmem_cache_node(s, node, n) { + nr_slabs += node_nr_slabs(n); + nr_objs += node_nr_objs(n); diff --git a/test/integration/rhel-8.1/gcc-static-local-var-2.patch b/test/integration/rhel-8.1/gcc-static-local-var-2.patch new file mode 100644 index 0000000..a17a1be --- /dev/null +++ b/test/integration/rhel-8.1/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src/mm/mmap.c src/mm/mmap.c +--- src/mm/mmap.c 2020-03-11 11:23:32.386726932 +0000 ++++ src/mm/mmap.c 2020-03-11 11:29:26.610955502 +0000 +@@ -1685,6 +1685,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, vm_flags, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-8.1/gcc-static-local-var-3.patch b/test/integration/rhel-8.1/gcc-static-local-var-3.patch new file mode 100644 index 0000000..81a0b32 --- /dev/null +++ b/test/integration/rhel-8.1/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src/kernel/reboot.c src/kernel/reboot.c +--- src/kernel/reboot.c 2020-03-11 11:23:30.502684349 +0000 ++++ src/kernel/reboot.c 2020-03-11 11:30:24.412330397 +0000 +@@ -393,8 +393,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-8.1/gcc-static-local-var-4.patch.disabled b/test/integration/rhel-8.1/gcc-static-local-var-4.patch.disabled new file mode 100644 index 0000000..63481a7 --- /dev/null +++ b/test/integration/rhel-8.1/gcc-static-local-var-4.patch.disabled @@ -0,0 +1,24 @@ +Disabled due to https:/github.com/dynup/kpatch/issues/940 +--- +diff -Nupr src/fs/aio.c src/fs/aio.c +--- src/fs/aio.c 2020-03-11 11:23:24.150540895 +0000 ++++ src/fs/aio.c 2020-03-11 11:45:44.710769201 +0000 +@@ -251,11 +251,18 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; + struct address_space *i_mapping; + ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(file_inode(aio_ring_file), 0); + diff --git a/test/integration/rhel-8.1/gcc-static-local-var-4.test.disabled b/test/integration/rhel-8.1/gcc-static-local-var-4.test.disabled new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-8.1/gcc-static-local-var-4.test.disabled @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-8.1/gcc-static-local-var-5.patch b/test/integration/rhel-8.1/gcc-static-local-var-5.patch new file mode 100644 index 0000000..3f639ac --- /dev/null +++ b/test/integration/rhel-8.1/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src/kernel/audit.c src/kernel/audit.c +--- src/kernel/audit.c 2020-03-11 11:23:30.394681909 +0000 ++++ src/kernel/audit.c 2020-03-11 11:46:41.484170922 +0000 +@@ -325,6 +325,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -335,6 +341,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -354,6 +361,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -400,6 +412,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-8.1/gcc-static-local-var-6.patch b/test/integration/rhel-8.1/gcc-static-local-var-6.patch new file mode 100644 index 0000000..b970dd8 --- /dev/null +++ b/test/integration/rhel-8.1/gcc-static-local-var-6.patch @@ -0,0 +1,22 @@ +diff -Nupr src/net/ipv6/netfilter.c src/net/ipv6/netfilter.c +--- src/net/ipv6/netfilter.c 2020-03-11 11:23:32.702734076 +0000 ++++ src/net/ipv6/netfilter.c 2020-03-11 11:31:22.397716246 +0000 +@@ -87,6 +87,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -100,6 +102,9 @@ static int nf_ip6_route(struct net *net, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-8.1/macro-callbacks.patch b/test/integration/rhel-8.1/macro-callbacks.patch new file mode 100644 index 0000000..fd29ecf --- /dev/null +++ b/test/integration/rhel-8.1/macro-callbacks.patch @@ -0,0 +1,155 @@ +diff -Nupr src/drivers/input/joydev.c src/drivers/input/joydev.c +--- src/drivers/input/joydev.c 2020-03-11 11:23:07.890174500 +0000 ++++ src/drivers/input/joydev.c 2020-03-11 11:32:20.859119425 +0000 +@@ -1068,3 +1068,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src/drivers/input/misc/pcspkr.c 2020-03-11 11:23:07.962176120 +0000 ++++ src/drivers/input/misc/pcspkr.c 2020-03-11 11:32:20.859119425 +0000 +@@ -138,3 +138,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src/fs/aio.c src/fs/aio.c +--- src/fs/aio.c 2020-03-11 11:23:24.150540895 +0000 ++++ src/fs/aio.c 2020-03-11 11:32:20.859119425 +0000 +@@ -49,6 +49,50 @@ + + #define KIOCB_KEY 0 + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/rhel-8.1/macro-printk.patch.disabled b/test/integration/rhel-8.1/macro-printk.patch.disabled new file mode 100644 index 0000000..02926af --- /dev/null +++ b/test/integration/rhel-8.1/macro-printk.patch.disabled @@ -0,0 +1,148 @@ +diff -Nupr src/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c +--- src/net/ipv4/fib_frontend.c 2020-03-11 11:23:32.622732267 +0000 ++++ src/net/ipv4/fib_frontend.c 2020-03-11 11:47:37.897564680 +0000 +@@ -777,6 +777,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) + { +@@ -798,6 +799,7 @@ static int inet_rtm_newroute(struct sk_b + err = fib_table_insert(net, tb, &cfg, extack); + if (!err && cfg.fc_type == RTN_LOCAL) + net->ipv4.fib_has_custom_local_routes = true; ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff -Nupr src/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c +--- src/net/ipv4/fib_semantics.c 2020-03-11 11:23:32.626732358 +0000 ++++ src/net/ipv4/fib_semantics.c 2020-03-11 11:47:37.897564680 +0000 +@@ -1025,6 +1025,7 @@ fib_convert_metrics(struct fib_info *fi, + fi->fib_metrics->metrics); + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg, + struct netlink_ext_ack *extack) + { +@@ -1058,6 +1059,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1078,6 +1080,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (!fi) +@@ -1093,6 +1096,8 @@ struct fib_info *fib_create_info(struct + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1109,8 +1114,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1172,6 +1179,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) { +@@ -1193,6 +1201,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) { + NL_SET_ERR_MSG(extack, "Invalid scope"); +@@ -1231,6 +1240,7 @@ struct fib_info *fib_create_info(struct + if (linkdown == fi->fib_nhs) + fi->fib_flags |= RTNH_F_LINKDOWN; + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) { + NL_SET_ERR_MSG(extack, "Invalid prefsrc address"); +@@ -1240,6 +1250,7 @@ struct fib_info *fib_create_info(struct + change_nexthops(fi) { + fib_info_update_nh_saddr(net, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1251,6 +1262,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + refcount_set(&fi->fib_clntref, 1); +@@ -1274,6 +1286,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1284,6 +1297,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff -Nupr src/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c +--- src/net/ipv4/fib_trie.c 2020-03-11 11:23:32.626732358 +0000 ++++ src/net/ipv4/fib_trie.c 2020-03-11 11:47:37.897564680 +0000 +@@ -1121,6 +1121,7 @@ static bool fib_valid_key_len(u32 key, u + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg, struct netlink_ext_ack *extack) + { +@@ -1143,11 +1144,14 @@ int fib_table_insert(struct net *net, st + + pr_debug("Insert table=%u %08x/%d\n", tb->tb_id, key, plen); + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg, extack); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-8.1/meminfo-init-FAIL.patch b/test/integration/rhel-8.1/meminfo-init-FAIL.patch new file mode 100644 index 0000000..2334a0e --- /dev/null +++ b/test/integration/rhel-8.1/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src/fs/proc/meminfo.c 2020-03-11 11:23:26.882602572 +0000 ++++ src/fs/proc/meminfo.c 2020-03-11 11:34:17.189926874 +0000 +@@ -151,6 +151,7 @@ static int meminfo_proc_show(struct seq_ + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.1/meminfo-init2-FAIL.patch b/test/integration/rhel-8.1/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..937667b --- /dev/null +++ b/test/integration/rhel-8.1/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src/fs/proc/meminfo.c 2020-03-11 11:23:26.882602572 +0000 ++++ src/fs/proc/meminfo.c 2020-03-11 11:33:19.732537882 +0000 +@@ -40,6 +40,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long sreclaimable, sunreclaim; + int lru; + ++ printk("a\n"); + si_meminfo(&i); + si_swapinfo(&i); + committed = percpu_counter_read_positive(&vm_committed_as); +@@ -151,6 +152,7 @@ static int meminfo_proc_show(struct seq_ + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.1/meminfo-string-LOADED.test b/test/integration/rhel-8.1/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-8.1/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-8.1/meminfo-string.patch b/test/integration/rhel-8.1/meminfo-string.patch new file mode 100644 index 0000000..e3e7509 --- /dev/null +++ b/test/integration/rhel-8.1/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src/fs/proc/meminfo.c 2020-03-11 11:23:26.882602572 +0000 ++++ src/fs/proc/meminfo.c 2020-03-11 11:35:15.879349884 +0000 +@@ -120,7 +120,7 @@ static int meminfo_proc_show(struct seq_ + seq_printf(m, "VmallocTotal: %8lu kB\n", + (unsigned long)VMALLOC_TOTAL >> 10); + show_val_kb(m, "VmallocUsed: ", 0ul); +- show_val_kb(m, "VmallocChunk: ", 0ul); ++ show_val_kb(m, "VMALLOCCHUNK: ", 0ul); + + #ifdef CONFIG_MEMORY_FAILURE + seq_printf(m, "HardwareCorrupted: %5lu kB\n", diff --git a/test/integration/rhel-8.1/module-LOADED.test b/test/integration/rhel-8.1/module-LOADED.test new file mode 100755 index 0000000..72bb852 --- /dev/null +++ b/test/integration/rhel-8.1/module-LOADED.test @@ -0,0 +1,13 @@ +#!/bin/bash + +set -o errexit + +sudo modprobe nfsd +sleep 5 +grep -q kpatch /proc/fs/nfs/exports + +# TODO: This will trigger a printk on newer kernels which have the .klp.arch +# removal. Don't actually do the grep until running on a newer kernel. +echo "file fs/nfsd/export.c +p" > /sys/kernel/debug/dynamic_debug/control +cat /proc/fs/nfs/exports > /dev/null +# dmesg | grep -q "kpatch: pr_debug" diff --git a/test/integration/rhel-8.1/module.patch b/test/integration/rhel-8.1/module.patch new file mode 100644 index 0000000..8bfb621 --- /dev/null +++ b/test/integration/rhel-8.1/module.patch @@ -0,0 +1,90 @@ +From 08078d00ab1749a6f84148a00d8d26572af4ec97 Mon Sep 17 00:00:00 2001 +Message-Id: <08078d00ab1749a6f84148a00d8d26572af4ec97.1586900628.git.jpoimboe@redhat.com> +From: Josh Poimboeuf +Date: Tue, 14 Apr 2020 15:17:51 -0500 +Subject: [PATCH] kpatch module integration test + +This tests several things related to the patching of modules: + +- 'kpatch_string' tests the referencing of a symbol which is outside the + .o, but inside the patch module. + +- alternatives patching (.altinstructions) + +- paravirt patching (.parainstructions) + +- jump labels (5.8+ kernels only) -- including dynamic printk + +Signed-off-by: Josh Poimboeuf +--- + fs/nfsd/export.c | 30 ++++++++++++++++++++++++++++++ + net/netlink/af_netlink.c | 5 +++++ + 2 files changed, 35 insertions(+) + +diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c +index a1143f7c2201..253c15ad82b2 100644 +--- a/fs/nfsd/export.c ++++ b/fs/nfsd/export.c +@@ -1196,15 +1196,45 @@ static void exp_flags(struct seq_file *m, int flag, int fsid, + } + } + ++#include ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; + struct svc_export *exp = container_of(cp, struct svc_export, h); + struct cache_detail *cd = m->private; ++#ifdef CONFIG_X86_64 ++ unsigned long long sched_clock; ++ ++ alternative("ud2", "call yield", X86_FEATURE_ALWAYS); ++ alternative("call yield", "ud2", X86_FEATURE_IA64); ++ ++ sched_clock = paravirt_sched_clock(); ++ if (!jiffies) ++ printk("kpatch: sched_clock: %llu\n", sched_clock); ++#endif ++ ++ pr_debug("kpatch: pr_debug() test\n"); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) ++{ ++ static DEFINE_STATIC_KEY_TRUE(kpatch_key); ++ ++ if (static_branch_unlikely(&mcsafe_key)) ++ printk("kpatch: mcsafe_key\n"); ++ ++ BUG_ON(!static_branch_likely(&kpatch_key)); ++ static_branch_disable(&kpatch_key); ++ BUG_ON(static_branch_likely(&kpatch_key)); ++ static_branch_enable(&kpatch_key); ++} ++#endif + + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index 44fa4ea5df76..dc36ec2901b8 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -2809,4 +2809,9 @@ static int __init netlink_proto_init(void) + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); +-- +2.21.1 + diff --git a/test/integration/rhel-8.1/multiple.test b/test/integration/rhel-8.1/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-8.1/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-8.1/new-function.patch b/test/integration/rhel-8.1/new-function.patch new file mode 100644 index 0000000..3e446df --- /dev/null +++ b/test/integration/rhel-8.1/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src/drivers/tty/n_tty.c 2020-03-11 11:23:23.062516341 +0000 ++++ src/drivers/tty/n_tty.c 2020-03-11 11:37:13.226206161 +0000 +@@ -2296,7 +2296,7 @@ static ssize_t n_tty_read(struct tty_str + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2383,6 +2383,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-8.1/new-globals.patch b/test/integration/rhel-8.1/new-globals.patch new file mode 100644 index 0000000..07aafec --- /dev/null +++ b/test/integration/rhel-8.1/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src/fs/proc/cmdline.c 2020-03-11 11:23:26.878602482 +0000 ++++ src/fs/proc/cmdline.c 2020-03-11 11:38:10.295599825 +0000 +@@ -17,3 +17,10 @@ static int __init proc_cmdline_init(void + return 0; + } + fs_initcall(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src/fs/proc/meminfo.c 2020-03-11 11:23:26.882602572 +0000 ++++ src/fs/proc/meminfo.c 2020-03-11 11:38:10.295599825 +0000 +@@ -20,6 +20,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -56,6 +58,7 @@ static int meminfo_proc_show(struct seq_ + sreclaimable = global_node_page_state(NR_SLAB_RECLAIMABLE); + sunreclaim = global_node_page_state(NR_SLAB_UNRECLAIMABLE); + ++ kpatch_print_message(); + show_val_kb(m, "MemTotal: ", i.totalram); + show_val_kb(m, "MemFree: ", i.freeram); + show_val_kb(m, "MemAvailable: ", available); diff --git a/test/integration/rhel-8.1/parainstructions-section.patch b/test/integration/rhel-8.1/parainstructions-section.patch new file mode 100644 index 0000000..1183f45 --- /dev/null +++ b/test/integration/rhel-8.1/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src/fs/proc/generic.c src/fs/proc/generic.c +--- src/fs/proc/generic.c 2020-03-11 11:23:26.878602482 +0000 ++++ src/fs/proc/generic.c 2020-03-11 11:39:07.677003695 +0000 +@@ -205,6 +205,7 @@ int proc_alloc_inum(unsigned int *inum) + { + int i; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + i = ida_simple_get(&proc_inum_ida, 0, UINT_MAX - PROC_DYNAMIC_FIRST + 1, + GFP_KERNEL); + if (i < 0) diff --git a/test/integration/rhel-8.1/shadow-newpid-LOADED.test.disabled b/test/integration/rhel-8.1/shadow-newpid-LOADED.test.disabled new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-8.1/shadow-newpid-LOADED.test.disabled @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-8.1/shadow-newpid.patch.disabled b/test/integration/rhel-8.1/shadow-newpid.patch.disabled new file mode 100644 index 0000000..f571dc4 --- /dev/null +++ b/test/integration/rhel-8.1/shadow-newpid.patch.disabled @@ -0,0 +1,77 @@ +Disabled due to https:/github.com/dynup/kpatch/issues/940 +--- +diff -Nupr src/fs/proc/array.c src/fs/proc/array.c +--- src/fs/proc/array.c 2020-03-11 11:23:26.874602392 +0000 ++++ src/fs/proc/array.c 2020-03-11 11:48:34.514964309 +0000 +@@ -370,12 +370,19 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw); + seq_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw); + seq_putc(m, '\n'); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff -Nupr src/kernel/exit.c src/kernel/exit.c +--- src/kernel/exit.c 2020-03-11 11:23:30.434682812 +0000 ++++ src/kernel/exit.c 2020-03-11 11:48:34.514964309 +0000 +@@ -762,6 +762,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void __noreturn do_exit(long code) + { + struct task_struct *tsk = current; +@@ -868,6 +869,8 @@ void __noreturn do_exit(long code) + exit_thread(tsk); + exit_umh(tsk); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff -Nupr src/kernel/fork.c src/kernel/fork.c +--- src/kernel/fork.c 2020-03-11 11:23:30.438682903 +0000 ++++ src/kernel/fork.c 2020-03-11 11:48:34.514964309 +0000 +@@ -2210,6 +2210,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long _do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -2222,6 +2223,8 @@ long _do_fork(unsigned long clone_flags, + struct task_struct *p; + int trace = 0; + long nr; ++ int *newpid; ++ static int ctr = 0; + + /* + * Determine whether and which event to report to ptracer. When +@@ -2248,6 +2251,11 @@ long _do_fork(unsigned long clone_flags, + if (IS_ERR(p)) + return PTR_ERR(p); + ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; ++ + /* + * Do this prior waking up the new thread - the thread pointer + * might get invalid after that point, if the thread exits quickly. diff --git a/test/integration/rhel-8.1/smp-locks-section.patch b/test/integration/rhel-8.1/smp-locks-section.patch new file mode 100644 index 0000000..f0794a1 --- /dev/null +++ b/test/integration/rhel-8.1/smp-locks-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src/drivers/tty/tty_buffer.c 2020-03-11 11:23:23.138518056 +0000 ++++ src/drivers/tty/tty_buffer.c 2020-03-11 11:40:04.174388205 +0000 +@@ -256,6 +256,9 @@ static int __tty_buffer_request_room(str + struct tty_buffer *b, *n; + int left, change; + ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b->flags & TTYB_NORMAL) + left = 2 * b->size - b->used; diff --git a/test/integration/rhel-8.1/special-static.patch.disabled b/test/integration/rhel-8.1/special-static.patch.disabled new file mode 100644 index 0000000..992f8b9 --- /dev/null +++ b/test/integration/rhel-8.1/special-static.patch.disabled @@ -0,0 +1,22 @@ +diff -Nupr src/kernel/fork.c src/kernel/fork.c +--- src/kernel/fork.c 2020-03-11 11:23:30.438682903 +0000 ++++ src/kernel/fork.c 2020-03-11 11:41:01.567796732 +0000 +@@ -1524,10 +1524,18 @@ static void posix_cpu_timers_init_group( + static inline void posix_cpu_timers_init_group(struct signal_struct *sig) { } + #endif + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-8.1/symvers-disagreement-FAIL.patch b/test/integration/rhel-8.1/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..9527641 --- /dev/null +++ b/test/integration/rhel-8.1/symvers-disagreement-FAIL.patch @@ -0,0 +1,51 @@ +From c63702554e54b992793fe3598ea8c8c415bef908 Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes usb_get_dev() referencing a get_device() whose CRC has + changed, causing the symbol and the new CRC to be included in the + __version section of the final module. + +This makes the final module unloadable for the target kernel. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/base/core.c b/drivers/base/core.c +index 2ab316d85651..2ef19920f6ab 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -29,6 +29,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index 0a2b261a27c9..51a1868c9cea 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -685,6 +685,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; +-- +2.21.3 + diff --git a/test/integration/rhel-8.1/tracepoints-section.patch b/test/integration/rhel-8.1/tracepoints-section.patch new file mode 100644 index 0000000..ab5e316 --- /dev/null +++ b/test/integration/rhel-8.1/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src/kernel/time/timer.c src/kernel/time/timer.c +--- src/kernel/time/timer.c 2020-03-11 11:23:30.542685253 +0000 ++++ src/kernel/time/timer.c 2020-03-11 11:41:58.129186658 +0000 +@@ -1692,6 +1692,9 @@ static __latent_entropy void run_timer_s + { + struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + __run_timers(base); + if (IS_ENABLED(CONFIG_NO_HZ_COMMON)) + __run_timers(this_cpu_ptr(&timer_bases[BASE_DEF])); diff --git a/test/integration/rhel-8.1/warn-detect-FAIL.patch b/test/integration/rhel-8.1/warn-detect-FAIL.patch new file mode 100644 index 0000000..a00fe05 --- /dev/null +++ b/test/integration/rhel-8.1/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src/arch/x86/kvm/x86.c 2020-03-11 11:22:57.389938541 +0000 ++++ src/arch/x86/kvm/x86.c 2020-03-11 11:42:55.798605475 +0000 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/rhel-8.2/bug-table-section.patch b/test/integration/rhel-8.2/bug-table-section.patch new file mode 100644 index 0000000..5bc1e11 --- /dev/null +++ b/test/integration/rhel-8.2/bug-table-section.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/proc_sysctl.c 2020-05-12 11:14:36.220489794 -0400 +@@ -338,6 +338,8 @@ static void start_unregistering(struct c + + static struct ctl_table_header *sysctl_head_grab(struct ctl_table_header *head) + { ++ if (jiffies == 0) ++ printk("kpatch-test: testing __bug_table section changes\n"); + BUG_ON(!head); + spin_lock(&sysctl_lock); + if (!use_table(head)) diff --git a/test/integration/rhel-8.2/cmdline-string-LOADED.test b/test/integration/rhel-8.2/cmdline-string-LOADED.test new file mode 100755 index 0000000..a8e0a08 --- /dev/null +++ b/test/integration/rhel-8.2/cmdline-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep kpatch=1 /proc/cmdline diff --git a/test/integration/rhel-8.2/cmdline-string.patch b/test/integration/rhel-8.2/cmdline-string.patch new file mode 100644 index 0000000..4838935 --- /dev/null +++ b/test/integration/rhel-8.2/cmdline-string.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/cmdline.c 2020-05-12 11:14:40.110321212 -0400 +@@ -6,8 +6,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_puts(m, saved_command_line); +- seq_putc(m, '\n'); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + diff --git a/test/integration/rhel-8.2/data-new-LOADED.test b/test/integration/rhel-8.2/data-new-LOADED.test new file mode 100755 index 0000000..9f25744 --- /dev/null +++ b/test/integration/rhel-8.2/data-new-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep "kpatch: 5" /proc/meminfo diff --git a/test/integration/rhel-8.2/data-new.patch b/test/integration/rhel-8.2/data-new.patch new file mode 100644 index 0000000..6978e52 --- /dev/null +++ b/test/integration/rhel-8.2/data-new.patch @@ -0,0 +1,20 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/meminfo.c 2020-05-12 11:14:43.210186867 -0400 +@@ -31,6 +31,8 @@ static void show_val_kb(struct seq_file + seq_write(m, " kB\n", 4); + } + ++static int foo = 5; ++ + static int meminfo_proc_show(struct seq_file *m, void *v) + { + struct sysinfo i; +@@ -143,6 +145,7 @@ static int meminfo_proc_show(struct seq_ + show_val_kb(m, "CmaFree: ", + global_zone_page_state(NR_FREE_CMA_PAGES)); + #endif ++ seq_printf(m, "kpatch: %d\n", foo); + + hugetlb_report_meminfo(m); + diff --git a/test/integration/rhel-8.2/data-read-mostly.patch.disabled b/test/integration/rhel-8.2/data-read-mostly.patch.disabled new file mode 100644 index 0000000..5b1c87a --- /dev/null +++ b/test/integration/rhel-8.2/data-read-mostly.patch.disabled @@ -0,0 +1,13 @@ +Disabled due to https:/github.com/dynup/kpatch/issues/940 +--- +diff -Nupr src.orig/net/core/dev.c src/net/core/dev.c +--- src.orig/net/core/dev.c 2020-05-12 11:14:29.800768017 -0400 ++++ src/net/core/dev.c 2020-05-12 11:15:38.827776462 -0400 +@@ -4893,6 +4893,7 @@ skip_classify: + case RX_HANDLER_PASS: + break; + default: ++ printk("BUG!\n"); + BUG(); + } + } diff --git a/test/integration/rhel-8.2/fixup-section.patch b/test/integration/rhel-8.2/fixup-section.patch new file mode 100644 index 0000000..6632001 --- /dev/null +++ b/test/integration/rhel-8.2/fixup-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/readdir.c src/fs/readdir.c +--- src.orig/fs/readdir.c 2020-05-12 11:14:29.170795319 -0400 ++++ src/fs/readdir.c 2020-05-12 11:14:46.280053823 -0400 +@@ -189,6 +189,7 @@ static int filldir(struct dir_context *c + goto efault; + } + dirent = buf->current_dir; ++ asm("nop"); + if (__put_user(d_ino, &dirent->d_ino)) + goto efault; + if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/rhel-8.2/gcc-constprop.patch b/test/integration/rhel-8.2/gcc-constprop.patch new file mode 100644 index 0000000..1a17df4 --- /dev/null +++ b/test/integration/rhel-8.2/gcc-constprop.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timekeeping.c src/kernel/time/timekeeping.c +--- src.orig/kernel/time/timekeeping.c 2020-05-12 11:14:29.670773651 -0400 ++++ src/kernel/time/timekeeping.c 2020-05-12 11:15:41.897643417 -0400 +@@ -1221,6 +1221,9 @@ void do_gettimeofday(struct timeval *tv) + { + struct timespec64 now; + ++ if (!tv) ++ return; ++ + getnstimeofday64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; diff --git a/test/integration/rhel-8.2/gcc-isra.patch b/test/integration/rhel-8.2/gcc-isra.patch new file mode 100644 index 0000000..073b60f --- /dev/null +++ b/test/integration/rhel-8.2/gcc-isra.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/proc_sysctl.c src/fs/proc/proc_sysctl.c +--- src.orig/fs/proc/proc_sysctl.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/proc_sysctl.c 2020-05-12 11:14:49.359920345 -0400 +@@ -53,6 +53,7 @@ void proc_sys_poll_notify(struct ctl_tab + if (!poll) + return; + ++ printk("kpatch-test: testing gcc .isra function name mangling\n"); + atomic_inc(&poll->event); + wake_up_interruptible(&poll->wait); + } diff --git a/test/integration/rhel-8.2/gcc-mangled-3.patch b/test/integration/rhel-8.2/gcc-mangled-3.patch new file mode 100644 index 0000000..8d096e4 --- /dev/null +++ b/test/integration/rhel-8.2/gcc-mangled-3.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2020-05-12 11:14:32.110667908 -0400 ++++ src/mm/slub.c 2020-05-12 11:14:52.439786867 -0400 +@@ -5852,6 +5852,9 @@ void get_slabinfo(struct kmem_cache *s, + int node; + struct kmem_cache_node *n; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_kmem_cache_node(s, node, n) { + nr_slabs += node_nr_slabs(n); + nr_objs += node_nr_objs(n); diff --git a/test/integration/rhel-8.2/gcc-static-local-var-2.patch b/test/integration/rhel-8.2/gcc-static-local-var-2.patch new file mode 100644 index 0000000..a735a1f --- /dev/null +++ b/test/integration/rhel-8.2/gcc-static-local-var-2.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/mm/mmap.c src/mm/mmap.c +--- src.orig/mm/mmap.c 2020-05-12 11:14:32.110667908 -0400 ++++ src/mm/mmap.c 2020-05-12 11:14:55.529652955 -0400 +@@ -1679,6 +1679,9 @@ unsigned long mmap_region(struct file *f + struct rb_node **rb_link, *rb_parent; + unsigned long charged = 0; + ++ if (!jiffies) ++ printk("kpatch mmap foo\n"); ++ + /* Check against address space limit. */ + if (!may_expand_vm(mm, vm_flags, len >> PAGE_SHIFT)) { + unsigned long nr_pages; diff --git a/test/integration/rhel-8.2/gcc-static-local-var-3.patch b/test/integration/rhel-8.2/gcc-static-local-var-3.patch new file mode 100644 index 0000000..2b7af0f --- /dev/null +++ b/test/integration/rhel-8.2/gcc-static-local-var-3.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/kernel/reboot.c src/kernel/reboot.c +--- src.orig/kernel/reboot.c 2020-05-12 11:14:29.670773651 -0400 ++++ src/kernel/reboot.c 2020-05-12 11:14:58.579520777 -0400 +@@ -393,8 +393,15 @@ SYSCALL_DEFINE4(reboot, int, magic1, int + return ret; + } + ++void kpatch_bar(void) ++{ ++ if (!jiffies) ++ printk("kpatch_foo\n"); ++} ++ + static void deferred_cad(struct work_struct *dummy) + { ++ kpatch_bar(); + kernel_restart(NULL); + } + diff --git a/test/integration/rhel-8.2/gcc-static-local-var-4.patch.disabled b/test/integration/rhel-8.2/gcc-static-local-var-4.patch.disabled new file mode 100644 index 0000000..783f598 --- /dev/null +++ b/test/integration/rhel-8.2/gcc-static-local-var-4.patch.disabled @@ -0,0 +1,24 @@ +Disabled due to https:/github.com/dynup/kpatch/issues/940 +--- +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2020-05-12 11:14:29.130797053 -0400 ++++ src/fs/aio.c 2020-05-12 11:15:44.967510372 -0400 +@@ -251,11 +251,18 @@ static int __init aio_setup(void) + } + __initcall(aio_setup); + ++void kpatch_aio_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch aio foo\n"); ++} ++ + static void put_aio_ring_file(struct kioctx *ctx) + { + struct file *aio_ring_file = ctx->aio_ring_file; + struct address_space *i_mapping; + ++ kpatch_aio_foo(); + if (aio_ring_file) { + truncate_setsize(file_inode(aio_ring_file), 0); + diff --git a/test/integration/rhel-8.2/gcc-static-local-var-4.test.disabled b/test/integration/rhel-8.2/gcc-static-local-var-4.test.disabled new file mode 100755 index 0000000..e085f93 --- /dev/null +++ b/test/integration/rhel-8.2/gcc-static-local-var-4.test.disabled @@ -0,0 +1,8 @@ +#!/bin/bash + +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then + exit 1 +else + exit 0 +fi diff --git a/test/integration/rhel-8.2/gcc-static-local-var-5.patch b/test/integration/rhel-8.2/gcc-static-local-var-5.patch new file mode 100644 index 0000000..b6f492b --- /dev/null +++ b/test/integration/rhel-8.2/gcc-static-local-var-5.patch @@ -0,0 +1,45 @@ +diff -Nupr src.orig/kernel/audit.c src/kernel/audit.c +--- src.orig/kernel/audit.c 2020-05-12 11:24:26.314915742 -0400 ++++ src/kernel/audit.c 2020-05-12 11:24:37.024451603 -0400 +@@ -321,6 +321,12 @@ void audit_panic(const char *message) + } + } + ++void kpatch_audit_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch audit foo\n"); ++} ++ + static inline int audit_rate_check(void) + { + static unsigned long last_check = 0; +@@ -331,6 +337,7 @@ static inline int audit_rate_check(void) + unsigned long elapsed; + int retval = 0; + ++ kpatch_audit_foo(); + if (!audit_rate_limit) return 1; + + spin_lock_irqsave(&lock, flags); +@@ -350,6 +357,11 @@ static inline int audit_rate_check(void) + return retval; + } + ++noinline void kpatch_audit_check(void) ++{ ++ audit_rate_check(); ++} ++ + /** + * audit_log_lost - conditionally log lost audit message event + * @message: the message stating reason for lost audit message +@@ -396,6 +408,8 @@ static int audit_log_config_change(char + struct audit_buffer *ab; + int rc = 0; + ++ kpatch_audit_check(); ++ + ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (unlikely(!ab)) + return rc; diff --git a/test/integration/rhel-8.2/gcc-static-local-var-6.patch b/test/integration/rhel-8.2/gcc-static-local-var-6.patch new file mode 100644 index 0000000..378433e --- /dev/null +++ b/test/integration/rhel-8.2/gcc-static-local-var-6.patch @@ -0,0 +1,22 @@ +diff -Nupr src.orig/net/ipv6/netfilter.c src/net/ipv6/netfilter.c +--- src.orig/net/ipv6/netfilter.c 2020-05-12 11:14:29.820767150 -0400 ++++ src/net/ipv6/netfilter.c 2020-05-12 11:15:01.659387299 -0400 +@@ -87,6 +87,8 @@ static int nf_ip6_reroute(struct sk_buff + return 0; + } + ++#include "kpatch-macros.h" ++ + static int nf_ip6_route(struct net *net, struct dst_entry **dst, + struct flowi *fl, bool strict) + { +@@ -100,6 +102,9 @@ static int nf_ip6_route(struct net *net, + struct dst_entry *result; + int err; + ++ if (!jiffies) ++ printk("kpatch nf_ip6_route foo\n"); ++ + result = ip6_route_output(net, sk, &fl->u.ip6); + err = result->error; + if (err) diff --git a/test/integration/rhel-8.2/macro-callbacks.patch b/test/integration/rhel-8.2/macro-callbacks.patch new file mode 100644 index 0000000..e2c7df9 --- /dev/null +++ b/test/integration/rhel-8.2/macro-callbacks.patch @@ -0,0 +1,155 @@ +diff -Nupr src.orig/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.orig/drivers/input/joydev.c 2020-05-12 11:14:32.580647540 -0400 ++++ src/drivers/input/joydev.c 2020-05-12 11:15:04.909246454 -0400 +@@ -1084,3 +1084,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.orig/drivers/input/misc/pcspkr.c 2020-05-12 11:14:32.590647107 -0400 ++++ src/drivers/input/misc/pcspkr.c 2020-05-12 11:15:04.909246454 -0400 +@@ -133,3 +133,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.orig/fs/aio.c src/fs/aio.c +--- src.orig/fs/aio.c 2020-05-12 11:14:29.130797053 -0400 ++++ src/fs/aio.c 2020-05-12 11:15:04.909246454 -0400 +@@ -49,6 +49,50 @@ + + #define KIOCB_KEY 0 + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/rhel-8.2/macro-printk.patch b/test/integration/rhel-8.2/macro-printk.patch new file mode 100644 index 0000000..f2d2d4b --- /dev/null +++ b/test/integration/rhel-8.2/macro-printk.patch @@ -0,0 +1,148 @@ +diff -Nupr src.orig/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c +--- src.orig/net/ipv4/fib_frontend.c 2020-05-12 11:14:29.710771918 -0400 ++++ src/net/ipv4/fib_frontend.c 2020-05-12 11:15:48.047376894 -0400 +@@ -789,6 +789,7 @@ errout: + return err; + } + ++#include "kpatch-macros.h" + static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) + { +@@ -810,6 +811,7 @@ static int inet_rtm_newroute(struct sk_b + err = fib_table_insert(net, tb, &cfg, extack); + if (!err && cfg.fc_type == RTN_LOCAL) + net->ipv4.fib_has_custom_local_routes = true; ++ KPATCH_PRINTK("[inet_rtm_newroute]: err is %d\n", err); + errout: + return err; + } +diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c +--- src.orig/net/ipv4/fib_semantics.c 2020-05-12 11:14:29.710771918 -0400 ++++ src/net/ipv4/fib_semantics.c 2020-05-12 11:15:48.047376894 -0400 +@@ -1025,6 +1025,7 @@ fib_convert_metrics(struct fib_info *fi, + fi->fib_metrics->metrics); + } + ++#include "kpatch-macros.h" + struct fib_info *fib_create_info(struct fib_config *cfg, + struct netlink_ext_ack *extack) + { +@@ -1058,6 +1059,7 @@ struct fib_info *fib_create_info(struct + #endif + + err = -ENOBUFS; ++ KPATCH_PRINTK("[fib_create_info]: create error err is %d\n",err); + if (fib_info_cnt >= fib_info_hash_size) { + unsigned int new_size = fib_info_hash_size << 1; + struct hlist_head *new_info_hash; +@@ -1078,6 +1080,7 @@ struct fib_info *fib_create_info(struct + if (!fib_info_hash_size) + goto failure; + } ++ KPATCH_PRINTK("[fib_create_info]: 2 create error err is %d\n",err); + + fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); + if (!fi) +@@ -1093,6 +1096,8 @@ struct fib_info *fib_create_info(struct + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + fib_info_cnt++; ++ KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); ++ + fi->fib_net = net; + fi->fib_protocol = cfg->fc_protocol; + fi->fib_scope = cfg->fc_scope; +@@ -1109,8 +1114,10 @@ struct fib_info *fib_create_info(struct + if (!nexthop_nh->nh_pcpu_rth_output) + goto failure; + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 4 create error err is %d\n",err); + + err = fib_convert_metrics(fi, cfg); ++ KPATCH_PRINTK("[fib_create_info]: 5 create error err is %d\n",err); + if (err) + goto failure; + +@@ -1172,6 +1179,7 @@ struct fib_info *fib_create_info(struct + nh->nh_weight = 1; + #endif + } ++ KPATCH_PRINTK("[fib_create_info]: 6 create error err is %d\n",err); + + if (fib_props[cfg->fc_type].error) { + if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) { +@@ -1193,6 +1201,7 @@ struct fib_info *fib_create_info(struct + goto err_inval; + } + } ++ KPATCH_PRINTK("[fib_create_info]: 7 create error err is %d\n",err); + + if (cfg->fc_scope > RT_SCOPE_HOST) { + NL_SET_ERR_MSG(extack, "Invalid scope"); +@@ -1231,6 +1240,7 @@ struct fib_info *fib_create_info(struct + if (linkdown == fi->fib_nhs) + fi->fib_flags |= RTNH_F_LINKDOWN; + } ++ KPATCH_PRINTK("[fib_create_info]: 8 create error err is %d\n",err); + + if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) { + NL_SET_ERR_MSG(extack, "Invalid prefsrc address"); +@@ -1240,6 +1250,7 @@ struct fib_info *fib_create_info(struct + change_nexthops(fi) { + fib_info_update_nh_saddr(net, nexthop_nh); + } endfor_nexthops(fi) ++ KPATCH_PRINTK("[fib_create_info]: 9 create error err is %d\n",err); + + fib_rebalance(fi); + +@@ -1251,6 +1262,7 @@ link_it: + ofi->fib_treeref++; + return ofi; + } ++ KPATCH_PRINTK("[fib_create_info]: 10 create error err is %d\n",err); + + fi->fib_treeref++; + refcount_set(&fi->fib_clntref, 1); +@@ -1274,6 +1286,7 @@ link_it: + hlist_add_head(&nexthop_nh->nh_hash, head); + } endfor_nexthops(fi) + spin_unlock_bh(&fib_info_lock); ++ KPATCH_PRINTK("[fib_create_info]: 11 create error err is %d\n",err); + return fi; + + err_inval: +@@ -1284,6 +1297,7 @@ failure: + fi->fib_dead = 1; + free_fib_info(fi); + } ++ KPATCH_PRINTK("[fib_create_info]: 12 create error err is %d\n",err); + + return ERR_PTR(err); + } +diff -Nupr src.orig/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c +--- src.orig/net/ipv4/fib_trie.c 2020-05-12 11:14:29.710771918 -0400 ++++ src/net/ipv4/fib_trie.c 2020-05-12 11:15:48.047376894 -0400 +@@ -1121,6 +1121,7 @@ static bool fib_valid_key_len(u32 key, u + } + + /* Caller must hold RTNL. */ ++#include "kpatch-macros.h" + int fib_table_insert(struct net *net, struct fib_table *tb, + struct fib_config *cfg, struct netlink_ext_ack *extack) + { +@@ -1143,11 +1144,14 @@ int fib_table_insert(struct net *net, st + + pr_debug("Insert table=%u %08x/%d\n", tb->tb_id, key, plen); + ++ KPATCH_PRINTK("[fib_table_insert]: start\n"); + fi = fib_create_info(cfg, extack); + if (IS_ERR(fi)) { + err = PTR_ERR(fi); ++ KPATCH_PRINTK("[fib_table_insert]: create error err is %d\n",err); + goto err; + } ++ KPATCH_PRINTK("[fib_table_insert]: cross\n"); + + l = fib_find_node(t, &tp, key); + fa = l ? fib_find_alias(&l->leaf, slen, tos, fi->fib_priority, diff --git a/test/integration/rhel-8.2/meminfo-init-FAIL.patch b/test/integration/rhel-8.2/meminfo-init-FAIL.patch new file mode 100644 index 0000000..6f58adb --- /dev/null +++ b/test/integration/rhel-8.2/meminfo-init-FAIL.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/meminfo.c 2020-05-12 11:15:13.848859021 -0400 +@@ -153,6 +153,7 @@ static int meminfo_proc_show(struct seq_ + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.2/meminfo-init2-FAIL.patch b/test/integration/rhel-8.2/meminfo-init2-FAIL.patch new file mode 100644 index 0000000..28ba5bc --- /dev/null +++ b/test/integration/rhel-8.2/meminfo-init2-FAIL.patch @@ -0,0 +1,19 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/meminfo.c 2020-05-12 11:15:10.778992066 -0400 +@@ -41,6 +41,7 @@ static int meminfo_proc_show(struct seq_ + unsigned long sreclaimable, sunreclaim; + int lru; + ++ printk("a\n"); + si_meminfo(&i); + si_swapinfo(&i); + committed = percpu_counter_read_positive(&vm_committed_as); +@@ -153,6 +154,7 @@ static int meminfo_proc_show(struct seq_ + + static int __init proc_meminfo_init(void) + { ++ printk("a\n"); + proc_create_single("meminfo", 0, NULL, meminfo_proc_show); + return 0; + } diff --git a/test/integration/rhel-8.2/meminfo-string-LOADED.test b/test/integration/rhel-8.2/meminfo-string-LOADED.test new file mode 100755 index 0000000..10dc20b --- /dev/null +++ b/test/integration/rhel-8.2/meminfo-string-LOADED.test @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo diff --git a/test/integration/rhel-8.2/meminfo-string.patch b/test/integration/rhel-8.2/meminfo-string.patch new file mode 100644 index 0000000..278e0e5 --- /dev/null +++ b/test/integration/rhel-8.2/meminfo-string.patch @@ -0,0 +1,12 @@ +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-05-12 11:24:25.954931343 -0400 ++++ src/fs/proc/meminfo.c 2020-05-12 11:24:33.774592448 -0400 +@@ -121,7 +121,7 @@ static int meminfo_proc_show(struct seq_ + seq_printf(m, "VmallocTotal: %8lu kB\n", + (unsigned long)VMALLOC_TOTAL >> 10); + show_val_kb(m, "VmallocUsed: ", 0ul); +- show_val_kb(m, "VmallocChunk: ", 0ul); ++ show_val_kb(m, "VMALLOCCHUNK: ", 0ul); + show_val_kb(m, "Percpu: ", pcpu_nr_pages()); + + #ifdef CONFIG_MEMORY_FAILURE diff --git a/test/integration/rhel-8.2/module-LOADED.test b/test/integration/rhel-8.2/module-LOADED.test new file mode 100755 index 0000000..72bb852 --- /dev/null +++ b/test/integration/rhel-8.2/module-LOADED.test @@ -0,0 +1,13 @@ +#!/bin/bash + +set -o errexit + +sudo modprobe nfsd +sleep 5 +grep -q kpatch /proc/fs/nfs/exports + +# TODO: This will trigger a printk on newer kernels which have the .klp.arch +# removal. Don't actually do the grep until running on a newer kernel. +echo "file fs/nfsd/export.c +p" > /sys/kernel/debug/dynamic_debug/control +cat /proc/fs/nfs/exports > /dev/null +# dmesg | grep -q "kpatch: pr_debug" diff --git a/test/integration/rhel-8.2/module.patch b/test/integration/rhel-8.2/module.patch new file mode 100644 index 0000000..5dcee19 --- /dev/null +++ b/test/integration/rhel-8.2/module.patch @@ -0,0 +1,85 @@ +From 08078d00ab1749a6f84148a00d8d26572af4ec97 Mon Sep 17 00:00:00 2001 +Message-Id: <08078d00ab1749a6f84148a00d8d26572af4ec97.1586900628.git.jpoimboe@redhat.com> +From: Josh Poimboeuf +Date: Tue, 14 Apr 2020 15:17:51 -0500 +Subject: [PATCH] kpatch module integration test + +This tests several things related to the patching of modules: + +- 'kpatch_string' tests the referencing of a symbol which is outside the + .o, but inside the patch module. + +- alternatives patching (.altinstructions) + +- paravirt patching (.parainstructions) + +- jump labels (5.8+ kernels only) -- including dynamic printk + +Signed-off-by: Josh Poimboeuf +--- + fs/nfsd/export.c | 30 ++++++++++++++++++++++++++++++ + net/netlink/af_netlink.c | 5 +++++ + 2 files changed, 35 insertions(+) + +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2020-05-12 11:14:29.230792719 -0400 ++++ src/fs/nfsd/export.c 2020-05-12 11:15:17.078719042 -0400 +@@ -1196,15 +1196,45 @@ static void exp_flags(struct seq_file *m + } + } + ++#include ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; + struct svc_export *exp = container_of(cp, struct svc_export, h); + struct cache_detail *cd = m->private; ++#ifdef CONFIG_X86_64 ++ unsigned long long sched_clock; ++ ++ alternative("ud2", "call yield", X86_FEATURE_ALWAYS); ++ alternative("call yield", "ud2", X86_FEATURE_IA64); ++ ++ sched_clock = paravirt_sched_clock(); ++ if (!jiffies) ++ printk("kpatch: sched_clock: %llu\n", sched_clock); ++#endif ++ ++ pr_debug("kpatch: pr_debug() test\n"); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) ++{ ++ static DEFINE_STATIC_KEY_TRUE(kpatch_key); ++ ++ if (static_branch_unlikely(&mcsafe_key)) ++ printk("kpatch: mcsafe_key\n"); ++ ++ BUG_ON(!static_branch_likely(&kpatch_key)); ++ static_branch_disable(&kpatch_key); ++ BUG_ON(static_branch_likely(&kpatch_key)); ++ static_branch_enable(&kpatch_key); ++} ++#endif + + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2020-05-12 11:14:29.780768884 -0400 ++++ src/net/netlink/af_netlink.c 2020-05-12 11:15:17.078719042 -0400 +@@ -2788,4 +2788,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/rhel-8.2/multiple.test b/test/integration/rhel-8.2/multiple.test new file mode 100755 index 0000000..7e4b352 --- /dev/null +++ b/test/integration/rhel-8.2/multiple.test @@ -0,0 +1,7 @@ +#!/bin/bash + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" + +declare -a blacklist=(meminfo-string-LOADED.test) + +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/rhel-8.2/new-function.patch b/test/integration/rhel-8.2/new-function.patch new file mode 100644 index 0000000..079065d --- /dev/null +++ b/test/integration/rhel-8.2/new-function.patch @@ -0,0 +1,25 @@ +diff -Nupr src.orig/drivers/tty/n_tty.c src/drivers/tty/n_tty.c +--- src.orig/drivers/tty/n_tty.c 2020-05-12 11:14:32.770639306 -0400 ++++ src/drivers/tty/n_tty.c 2020-05-12 11:15:20.398575163 -0400 +@@ -2296,7 +2296,7 @@ static ssize_t n_tty_read(struct tty_str + * lock themselves) + */ + +-static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, ++static ssize_t noinline kpatch_n_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) + { + const unsigned char *b = buf; +@@ -2383,6 +2383,12 @@ break_out: + return (b - buf) ? b - buf : retval; + } + ++static ssize_t __attribute__((optimize("-fno-optimize-sibling-calls"))) n_tty_write(struct tty_struct *tty, struct file *file, ++ const unsigned char *buf, size_t nr) ++{ ++ return kpatch_n_tty_write(tty, file, buf, nr); ++} ++ + /** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device diff --git a/test/integration/rhel-8.2/new-globals.patch b/test/integration/rhel-8.2/new-globals.patch new file mode 100644 index 0000000..1f75685 --- /dev/null +++ b/test/integration/rhel-8.2/new-globals.patch @@ -0,0 +1,34 @@ +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/cmdline.c 2020-05-12 11:15:23.488441252 -0400 +@@ -17,3 +17,10 @@ static int __init proc_cmdline_init(void + return 0; + } + fs_initcall(proc_cmdline_init); ++ ++#include ++void kpatch_print_message(void) ++{ ++ if (!jiffies) ++ printk("hello there!\n"); ++} +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/meminfo.c 2020-05-12 11:15:23.488441252 -0400 +@@ -21,6 +21,8 @@ + #include + #include "internal.h" + ++void kpatch_print_message(void); ++ + void __attribute__((weak)) arch_report_meminfo(struct seq_file *m) + { + } +@@ -57,6 +59,7 @@ static int meminfo_proc_show(struct seq_ + sreclaimable = global_node_page_state(NR_SLAB_RECLAIMABLE); + sunreclaim = global_node_page_state(NR_SLAB_UNRECLAIMABLE); + ++ kpatch_print_message(); + show_val_kb(m, "MemTotal: ", i.totalram); + show_val_kb(m, "MemFree: ", i.freeram); + show_val_kb(m, "MemAvailable: ", available); diff --git a/test/integration/rhel-8.2/parainstructions-section.patch b/test/integration/rhel-8.2/parainstructions-section.patch new file mode 100644 index 0000000..185bf97 --- /dev/null +++ b/test/integration/rhel-8.2/parainstructions-section.patch @@ -0,0 +1,11 @@ +diff -Nupr src.orig/fs/proc/generic.c src/fs/proc/generic.c +--- src.orig/fs/proc/generic.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/generic.c 2020-05-12 11:15:26.558308207 -0400 +@@ -205,6 +205,7 @@ int proc_alloc_inum(unsigned int *inum) + { + int i; + ++ printk("kpatch-test: testing change to .parainstructions section\n"); + i = ida_simple_get(&proc_inum_ida, 0, UINT_MAX - PROC_DYNAMIC_FIRST + 1, + GFP_KERNEL); + if (i < 0) diff --git a/test/integration/rhel-8.2/shadow-newpid-LOADED.test.disabled b/test/integration/rhel-8.2/shadow-newpid-LOADED.test.disabled new file mode 100755 index 0000000..c07d112 --- /dev/null +++ b/test/integration/rhel-8.2/shadow-newpid-LOADED.test.disabled @@ -0,0 +1,3 @@ +#!/bin/bash + +grep -q newpid: /proc/$$/status diff --git a/test/integration/rhel-8.2/shadow-newpid.patch.disabled b/test/integration/rhel-8.2/shadow-newpid.patch.disabled new file mode 100644 index 0000000..18ae4c5 --- /dev/null +++ b/test/integration/rhel-8.2/shadow-newpid.patch.disabled @@ -0,0 +1,77 @@ +Disabled due to https:/github.com/dynup/kpatch/issues/940 +--- +diff -Nupr src.orig/fs/proc/array.c src/fs/proc/array.c +--- src.orig/fs/proc/array.c 2020-05-12 11:14:29.250791853 -0400 ++++ src/fs/proc/array.c 2020-05-12 11:15:51.127243416 -0400 +@@ -370,12 +370,19 @@ static inline void task_seccomp(struct s + seq_putc(m, '\n'); + } + ++#include + static inline void task_context_switch_counts(struct seq_file *m, + struct task_struct *p) + { ++ int *newpid; ++ + seq_put_decimal_ull(m, "voluntary_ctxt_switches:\t", p->nvcsw); + seq_put_decimal_ull(m, "\nnonvoluntary_ctxt_switches:\t", p->nivcsw); + seq_putc(m, '\n'); ++ ++ newpid = klp_shadow_get(p, 0); ++ if (newpid) ++ seq_printf(m, "newpid:\t%d\n", *newpid); + } + + static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) +diff -Nupr src.orig/kernel/exit.c src/kernel/exit.c +--- src.orig/kernel/exit.c 2020-05-12 11:14:29.670773651 -0400 ++++ src/kernel/exit.c 2020-05-12 11:15:51.127243416 -0400 +@@ -762,6 +762,7 @@ static void check_stack_usage(void) + static inline void check_stack_usage(void) {} + #endif + ++#include + void __noreturn do_exit(long code) + { + struct task_struct *tsk = current; +@@ -868,6 +869,8 @@ void __noreturn do_exit(long code) + exit_thread(tsk); + exit_umh(tsk); + ++ klp_shadow_free(tsk, 0, NULL); ++ + /* + * Flush inherited counters to the parent - before the parent + * gets woken up by child-exit notifications. +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2020-05-12 11:14:29.670773651 -0400 ++++ src/kernel/fork.c 2020-05-12 11:15:51.127243416 -0400 +@@ -2206,6 +2206,7 @@ struct task_struct *fork_idle(int cpu) + * It copies the process, and if successful kick-starts + * it and waits for it to finish using the VM if required. + */ ++#include + long _do_fork(unsigned long clone_flags, + unsigned long stack_start, + unsigned long stack_size, +@@ -2218,6 +2219,8 @@ long _do_fork(unsigned long clone_flags, + struct task_struct *p; + int trace = 0; + long nr; ++ int *newpid; ++ static int ctr = 0; + + /* + * Determine whether and which event to report to ptracer. When +@@ -2244,6 +2247,11 @@ long _do_fork(unsigned long clone_flags, + if (IS_ERR(p)) + return PTR_ERR(p); + ++ newpid = klp_shadow_get_or_alloc(p, 0, sizeof(*newpid), GFP_KERNEL, ++ NULL, NULL); ++ if (newpid) ++ *newpid = ctr++; ++ + /* + * Do this prior waking up the new thread - the thread pointer + * might get invalid after that point, if the thread exits quickly. diff --git a/test/integration/rhel-8.2/smp-locks-section.patch b/test/integration/rhel-8.2/smp-locks-section.patch new file mode 100644 index 0000000..562be3e --- /dev/null +++ b/test/integration/rhel-8.2/smp-locks-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/drivers/tty/tty_buffer.c src/drivers/tty/tty_buffer.c +--- src.orig/drivers/tty/tty_buffer.c 2020-05-12 11:14:32.780638873 -0400 ++++ src/drivers/tty/tty_buffer.c 2020-05-12 11:15:29.618175596 -0400 +@@ -256,6 +256,9 @@ static int __tty_buffer_request_room(str + struct tty_buffer *b, *n; + int left, change; + ++ if (!size) ++ printk("kpatch-test: testing .smp_locks section changes\n"); ++ + b = buf->tail; + if (b->flags & TTYB_NORMAL) + left = 2 * b->size - b->used; diff --git a/test/integration/rhel-8.2/special-static.patch.disabled b/test/integration/rhel-8.2/special-static.patch.disabled new file mode 100644 index 0000000..a58cbbc --- /dev/null +++ b/test/integration/rhel-8.2/special-static.patch.disabled @@ -0,0 +1,22 @@ +diff -Nupr src.orig/kernel/fork.c src/kernel/fork.c +--- src.orig/kernel/fork.c 2020-05-12 11:14:29.670773651 -0400 ++++ src/kernel/fork.c 2020-05-12 11:15:54.197110372 -0400 +@@ -1523,10 +1523,18 @@ static void posix_cpu_timers_init_group( + static inline void posix_cpu_timers_init_group(struct signal_struct *sig) { } + #endif + ++void kpatch_foo(void) ++{ ++ if (!jiffies) ++ printk("kpatch copy signal\n"); ++} ++ + static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + { + struct signal_struct *sig; + ++ kpatch_foo(); ++ + if (clone_flags & CLONE_THREAD) + return 0; + diff --git a/test/integration/rhel-8.2/symvers-disagreement-FAIL.patch b/test/integration/rhel-8.2/symvers-disagreement-FAIL.patch new file mode 100644 index 0000000..798d21f --- /dev/null +++ b/test/integration/rhel-8.2/symvers-disagreement-FAIL.patch @@ -0,0 +1,51 @@ +From 2d6b7bce089e52563bd9c67df62f48e90b48047d Mon Sep 17 00:00:00 2001 +From: Julien Thierry +Date: Wed, 6 May 2020 14:30:57 +0100 +Subject: [PATCH] Symbol version change + +This change causes: +1) Some exported symbols in drivers/base/core.c to see their CRCs + change. +2) Changes usb_get_dev() referencing a get_device() whose CRC has + changed, causing the symbol and the new CRC to be included in the + __version section of the final module. + +This makes the final module unloadable for the target kernel. + +See "Exported symbol versioning" of the patch author guide for more +detail. + +--- + drivers/base/core.c | 2 ++ + drivers/usb/core/usb.c | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/base/core.c b/drivers/base/core.c +index 26bae20f0553..506ebbf0a210 100644 +--- a/drivers/base/core.c ++++ b/drivers/base/core.c +@@ -30,6 +30,8 @@ + #include "base.h" + #include "power/power.h" + ++#include ++ + #ifdef CONFIG_SYSFS_DEPRECATED + #ifdef CONFIG_SYSFS_DEPRECATED_V2 + long sysfs_deprecated = 1; +diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c +index f74e6bda1788..86f7d453549c 100644 +--- a/drivers/usb/core/usb.c ++++ b/drivers/usb/core/usb.c +@@ -685,6 +685,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_dev); + */ + struct usb_device *usb_get_dev(struct usb_device *dev) + { ++ barrier(); ++ + if (dev) + get_device(&dev->dev); + return dev; +-- +2.21.3 + diff --git a/test/integration/rhel-8.2/tracepoints-section.patch b/test/integration/rhel-8.2/tracepoints-section.patch new file mode 100644 index 0000000..a15d065 --- /dev/null +++ b/test/integration/rhel-8.2/tracepoints-section.patch @@ -0,0 +1,13 @@ +diff -Nupr src.orig/kernel/time/timer.c src/kernel/time/timer.c +--- src.orig/kernel/time/timer.c 2020-05-12 11:14:29.670773651 -0400 ++++ src/kernel/time/timer.c 2020-05-12 11:15:32.688042552 -0400 +@@ -1696,6 +1696,9 @@ static __latent_entropy void run_timer_s + { + struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]); + ++ if (!base) ++ printk("kpatch-test: testing __tracepoints section changes\n"); ++ + __run_timers(base); + if (IS_ENABLED(CONFIG_NO_HZ_COMMON)) + __run_timers(this_cpu_ptr(&timer_bases[BASE_DEF])); diff --git a/test/integration/rhel-8.2/warn-detect-FAIL.patch b/test/integration/rhel-8.2/warn-detect-FAIL.patch new file mode 100644 index 0000000..5686019 --- /dev/null +++ b/test/integration/rhel-8.2/warn-detect-FAIL.patch @@ -0,0 +1,8 @@ +diff -Nupr src.orig/arch/x86/kvm/x86.c src/arch/x86/kvm/x86.c +--- src.orig/arch/x86/kvm/x86.c 2020-05-12 11:14:30.610732914 -0400 ++++ src/arch/x86/kvm/x86.c 2020-05-12 11:15:35.767909073 -0400 +@@ -1,3 +1,4 @@ ++ + /* + * Kernel-based Virtual Machine driver for Linux + * diff --git a/test/integration/test-vagrant b/test/integration/test-vagrant new file mode 100755 index 0000000..22a93cd --- /dev/null +++ b/test/integration/test-vagrant @@ -0,0 +1,42 @@ +#!/bin/bash + +SCRIPTDIR=$(readlink -f "$(dirname "$(type -p "${0}")")") +ROOTDIR=$(readlink -f "${SCRIPTDIR}/../..") +SLOWTEST=0 + +# shellcheck disable=SC1090 +source "${ROOTDIR}/test/integration/lib.sh" + +usage() +{ + echo "usage: $(basename "${0}") [options]" >&2 + echo "-h, --help This message" >&2 + echo "-s, --slow Run all of the tests" >&2 +} + +options="$(getopt -o hs -l "help,slow" -- "$@")" || "getopt failed" + +eval set -- "${options}" + +while [[ $# -gt 0 ]]; do + case "$1" in + -s|--slow) + SLOWTEST=1 + ;; + -h|--help) + usage + exit 0 + ;; + esac + shift +done + +declare -a distros=("fedora27" "centos7") + +ret=0 +for distro in "${distros[@]}"; do + kpatch_integration_tests_vagrant_distro "${distro}" "${ROOTDIR}/test/integration/vm-integration-run" "${SLOWTEST}" + rc=$? + ret=$((ret + rc)) +done +exit ${ret} diff --git a/test/integration/ubuntu-16.04/fixup-section.patch b/test/integration/ubuntu-16.04/fixup-section.patch index 59c5ff5..c818b08 100644 --- a/test/integration/ubuntu-16.04/fixup-section.patch +++ b/test/integration/ubuntu-16.04/fixup-section.patch @@ -1,12 +1,12 @@ -diff -Nupr src.orig/fs/readdir.c src/fs/readdir.c ---- src.orig/fs/readdir.c 2016-12-15 19:55:39.196000000 +0000 -+++ src/fs/readdir.c 2016-12-15 19:56:25.868000000 +0000 -@@ -173,6 +173,8 @@ static int filldir(struct dir_context *c +diff --git a/fs/readdir.c b/fs/readdir.c +index ced679179cac..7fb338324582 100644 +--- a/fs/readdir.c ++++ b/fs/readdir.c +@@ -173,6 +173,7 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen, goto efault; } dirent = buf->current_dir; -+ if (dirent->d_ino == 12345678) -+ printk("kpatch-test: testing .fixup section changes\n"); ++ asm("nop"); if (__put_user(d_ino, &dirent->d_ino)) goto efault; if (__put_user(reclen, &dirent->d_reclen)) diff --git a/test/integration/ubuntu-16.04/gcc-mangled-3.patch.disabled b/test/integration/ubuntu-16.04/gcc-mangled-3.patch.disabled new file mode 100644 index 0000000..a72b19b --- /dev/null +++ b/test/integration/ubuntu-16.04/gcc-mangled-3.patch.disabled @@ -0,0 +1,19 @@ +ensure that __cmpxchg_double_slab.isra.45 and +__cmpxchg_double_slab.isra.45.part.46 aren't correlated. + +Disabled: __flush_cpu_slab() is present in vmlinux.symtab but is optimized +out during kpatch builds + +diff -Nupr src.orig/mm/slub.c src/mm/slub.c +--- src.orig/mm/slub.c 2016-12-15 19:55:38.988000000 +0000 ++++ src/mm/slub.c 2016-12-15 19:56:39.068000000 +0000 +@@ -5531,6 +5531,9 @@ void get_slabinfo(struct kmem_cache *s, + int node; + struct kmem_cache_node *n; + ++ if (!jiffies) ++ printk("slabinfo\n"); ++ + for_each_kmem_cache_node(s, node, n) { + nr_slabs += node_nr_slabs(n); + nr_objs += node_nr_objs(n); diff --git a/test/integration/ubuntu-16.04/gcc-static-local-var-4.test b/test/integration/ubuntu-16.04/gcc-static-local-var-4.test index a1022ff..e085f93 100755 --- a/test/integration/ubuntu-16.04/gcc-static-local-var-4.test +++ b/test/integration/ubuntu-16.04/gcc-static-local-var-4.test @@ -1,6 +1,7 @@ #!/bin/bash -if $(nm kpatch-gcc-static-local-var-4.ko | grep -q free_ioctx); then +set -o pipefail +if ! $(eu-readelf --wide --symbols test-gcc-static-local-var-4.ko | awk '$NF == "free_ioctx" { exit 1 }'); then exit 1 else exit 0 diff --git a/test/integration/ubuntu-16.04/macro-callbacks.patch b/test/integration/ubuntu-16.04/macro-callbacks.patch new file mode 100644 index 0000000..569cc7c --- /dev/null +++ b/test/integration/ubuntu-16.04/macro-callbacks.patch @@ -0,0 +1,163 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +diff -Nupr src.old/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.old/drivers/input/joydev.c 2017-09-03 16:56:17.000000000 -0400 ++++ src/drivers/input/joydev.c 2018-03-22 16:32:40.963082354 -0400 +@@ -1010,3 +1010,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.old/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.old/drivers/input/misc/pcspkr.c 2018-03-22 16:29:27.716082354 -0400 ++++ src/drivers/input/misc/pcspkr.c 2018-03-22 16:32:40.963082354 -0400 +@@ -132,3 +132,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.old/fs/aio.c src/fs/aio.c +--- src.old/fs/aio.c 2017-09-03 16:56:17.000000000 -0400 ++++ src/fs/aio.c 2018-03-22 16:32:40.962082354 -0400 +@@ -46,6 +46,50 @@ + + #include "internal.h" + ++#include ++#include "kpatch-macros.h" ++ ++static const char *const module_state[] = { ++ [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", ++ [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", ++ [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", ++ [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", ++}; ++ ++static void callback_info(const char *callback, patch_object *obj) ++{ ++ if (obj->mod) ++ pr_info("%s: %s -> %s\n", callback, obj->mod->name, ++ module_state[obj->mod->state]); ++ else ++ pr_info("%s: vmlinux\n", callback); ++} ++ ++static int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/ubuntu-16.04/macro-printk.patch b/test/integration/ubuntu-16.04/macro-printk.patch index 02b4220..e0eb7d4 100644 --- a/test/integration/ubuntu-16.04/macro-printk.patch +++ b/test/integration/ubuntu-16.04/macro-printk.patch @@ -1,6 +1,7 @@ -diff -Nupr src.orig/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c ---- src.orig/net/ipv4/fib_frontend.c 2016-12-15 19:55:39.724000000 +0000 -+++ src/net/ipv4/fib_frontend.c 2016-12-15 19:57:09.672000000 +0000 +Index: src/net/ipv4/fib_frontend.c +=================================================================== +--- src.orig/net/ipv4/fib_frontend.c ++++ src/net/ipv4/fib_frontend.c @@ -728,6 +728,7 @@ errout: return err; } @@ -17,10 +18,11 @@ diff -Nupr src.orig/net/ipv4/fib_frontend.c src/net/ipv4/fib_frontend.c errout: return err; } -diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c ---- src.orig/net/ipv4/fib_semantics.c 2016-12-15 19:55:39.720000000 +0000 -+++ src/net/ipv4/fib_semantics.c 2016-12-15 19:57:09.672000000 +0000 -@@ -991,6 +991,7 @@ fib_convert_metrics(struct fib_info *fi, +Index: src/net/ipv4/fib_semantics.c +=================================================================== +--- src.orig/net/ipv4/fib_semantics.c ++++ src/net/ipv4/fib_semantics.c +@@ -998,6 +998,7 @@ fib_convert_metrics(struct fib_info *fi, return 0; } @@ -28,7 +30,7 @@ diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c struct fib_info *fib_create_info(struct fib_config *cfg) { int err; -@@ -1018,6 +1019,7 @@ struct fib_info *fib_create_info(struct +@@ -1025,6 +1026,7 @@ struct fib_info *fib_create_info(struct #endif err = -ENOBUFS; @@ -36,7 +38,7 @@ diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c if (fib_info_cnt >= fib_info_hash_size) { unsigned int new_size = fib_info_hash_size << 1; struct hlist_head *new_info_hash; -@@ -1038,6 +1040,7 @@ struct fib_info *fib_create_info(struct +@@ -1045,6 +1047,7 @@ struct fib_info *fib_create_info(struct if (!fib_info_hash_size) goto failure; } @@ -44,15 +46,15 @@ diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL); if (!fi) -@@ -1049,6 +1052,7 @@ struct fib_info *fib_create_info(struct - goto failure; - } else - fi->fib_metrics = (u32 *) dst_default_metrics; +@@ -1059,6 +1062,7 @@ struct fib_info *fib_create_info(struct + } else { + fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; + } + KPATCH_PRINTK("[fib_create_info]: 3 create error err is %d\n",err); - + fib_info_cnt++; fi->fib_net = net; fi->fib_protocol = cfg->fc_protocol; -@@ -1065,6 +1069,7 @@ struct fib_info *fib_create_info(struct +@@ -1075,6 +1079,7 @@ struct fib_info *fib_create_info(struct if (!nexthop_nh->nh_pcpu_rth_output) goto failure; } endfor_nexthops(fi) @@ -60,7 +62,7 @@ diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c err = fib_convert_metrics(fi, cfg); if (err) -@@ -1117,6 +1122,8 @@ struct fib_info *fib_create_info(struct +@@ -1127,6 +1132,8 @@ struct fib_info *fib_create_info(struct nh->nh_weight = 1; #endif } @@ -69,7 +71,7 @@ diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c if (fib_props[cfg->fc_type].error) { if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) -@@ -1134,6 +1141,7 @@ struct fib_info *fib_create_info(struct +@@ -1144,6 +1151,7 @@ struct fib_info *fib_create_info(struct goto err_inval; } } @@ -77,7 +79,7 @@ diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c if (cfg->fc_scope > RT_SCOPE_HOST) goto err_inval; -@@ -1162,6 +1170,7 @@ struct fib_info *fib_create_info(struct +@@ -1172,6 +1180,7 @@ struct fib_info *fib_create_info(struct if (linkdown == fi->fib_nhs) fi->fib_flags |= RTNH_F_LINKDOWN; } @@ -85,7 +87,7 @@ diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c if (fi->fib_prefsrc && !fib_valid_prefsrc(cfg, fi->fib_prefsrc)) goto err_inval; -@@ -1170,6 +1179,7 @@ struct fib_info *fib_create_info(struct +@@ -1180,6 +1189,7 @@ struct fib_info *fib_create_info(struct fib_info_update_nh_saddr(net, nexthop_nh); fib_add_weight(fi, nexthop_nh); } endfor_nexthops(fi) @@ -93,7 +95,7 @@ diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c fib_rebalance(fi); -@@ -1181,6 +1191,7 @@ link_it: +@@ -1191,6 +1201,7 @@ link_it: ofi->fib_treeref++; return ofi; } @@ -101,7 +103,7 @@ diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c fi->fib_treeref++; atomic_inc(&fi->fib_clntref); -@@ -1204,6 +1215,7 @@ link_it: +@@ -1214,6 +1225,7 @@ link_it: hlist_add_head(&nexthop_nh->nh_hash, head); } endfor_nexthops(fi) spin_unlock_bh(&fib_info_lock); @@ -109,7 +111,7 @@ diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c return fi; err_inval: -@@ -1214,6 +1226,7 @@ failure: +@@ -1224,6 +1236,7 @@ failure: fi->fib_dead = 1; free_fib_info(fi); } @@ -117,9 +119,10 @@ diff -Nupr src.orig/net/ipv4/fib_semantics.c src/net/ipv4/fib_semantics.c return ERR_PTR(err); } -diff -Nupr src.orig/net/ipv4/fib_trie.c src/net/ipv4/fib_trie.c ---- src.orig/net/ipv4/fib_trie.c 2016-12-15 19:55:39.720000000 +0000 -+++ src/net/ipv4/fib_trie.c 2016-12-15 19:57:09.676000000 +0000 +Index: src/net/ipv4/fib_trie.c +=================================================================== +--- src.orig/net/ipv4/fib_trie.c ++++ src/net/ipv4/fib_trie.c @@ -1078,6 +1078,7 @@ static int fib_insert_alias(struct trie } diff --git a/test/integration/ubuntu-16.04/meminfo-cmdline-rebuild-SLOW-LOADED.test.disabled b/test/integration/ubuntu-16.04/meminfo-cmdline-rebuild-SLOW-LOADED.test.disabled new file mode 100755 index 0000000..e2b647d --- /dev/null +++ b/test/integration/ubuntu-16.04/meminfo-cmdline-rebuild-SLOW-LOADED.test.disabled @@ -0,0 +1,3 @@ +#!/bin/bash + +grep VMALLOCCHUNK /proc/meminfo && grep kpatch=1 /proc/cmdline diff --git a/test/integration/ubuntu-16.04/meminfo-cmdline-rebuild-SLOW.patch.disabled b/test/integration/ubuntu-16.04/meminfo-cmdline-rebuild-SLOW.patch.disabled new file mode 100644 index 0000000..664b9b3 --- /dev/null +++ b/test/integration/ubuntu-16.04/meminfo-cmdline-rebuild-SLOW.patch.disabled @@ -0,0 +1,42 @@ +Disabled: +kpatch-build currently fails with "invalid ancestor" error. This happens +with at least drivers/gpu/drm/i2c/adv7511.o and drivers/hwmon/htu21.o +files. The problem is their .ko counterparts are never built for some +reason, This looks like a kernel bug since in both cases there are files +with same name but in different paths that have .ko module built. +--- +diff -Nupr src.orig/fs/proc/cmdline.c src/fs/proc/cmdline.c +--- src.orig/fs/proc/cmdline.c 2016-12-15 19:55:39.084000000 +0000 ++++ src/fs/proc/cmdline.c 2016-12-15 19:57:13.988000000 +0000 +@@ -5,7 +5,7 @@ + + static int cmdline_proc_show(struct seq_file *m, void *v) + { +- seq_printf(m, "%s\n", saved_command_line); ++ seq_printf(m, "%s kpatch=1\n", saved_command_line); + return 0; + } + +diff -Nupr src.orig/fs/proc/meminfo.c src/fs/proc/meminfo.c +--- src.orig/fs/proc/meminfo.c 2016-12-15 19:55:39.084000000 +0000 ++++ src/fs/proc/meminfo.c 2016-12-15 19:57:13.988000000 +0000 +@@ -99,7 +99,7 @@ static int meminfo_proc_show(struct seq_ + "Committed_AS: %8lu kB\n" + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" +- "VmallocChunk: %8lu kB\n" ++ "VMALLOCCHUNK: %8lu kB\n" + #ifdef CONFIG_MEMORY_FAILURE + "HardwareCorrupted: %5lu kB\n" + #endif +diff -Nupr src.orig/include/linux/kernel.h src/include/linux/kernel.h +--- src.orig/include/linux/kernel.h 2016-12-15 19:55:56.996000000 +0000 ++++ src/include/linux/kernel.h 2016-12-15 19:57:13.992000000 +0000 +@@ -2,6 +2,7 @@ + #define _LINUX_KERNEL_H + + ++ + #include + #include + #include diff --git a/test/integration/ubuntu-16.04/module-call-external.patch.disable b/test/integration/ubuntu-16.04/module-call-external.patch.disable new file mode 100644 index 0000000..b9f51a7 --- /dev/null +++ b/test/integration/ubuntu-16.04/module-call-external.patch.disable @@ -0,0 +1,38 @@ +Disabled: +Original build includes "kzalloc" in af_netlink.c's symbol list, this +does not happen during kpatch build so create-diff-object fails with +find_local_syms. +--- +diff -Nupr src.orig/fs/nfsd/export.c src/fs/nfsd/export.c +--- src.orig/fs/nfsd/export.c 2016-12-15 19:55:39.012000000 +0000 ++++ src/fs/nfsd/export.c 2016-12-15 19:57:31.068000000 +0000 +@@ -1183,6 +1183,8 @@ static void exp_flags(struct seq_file *m + } + } + ++extern char *kpatch_string(void); ++ + static int e_show(struct seq_file *m, void *p) + { + struct cache_head *cp = p; +@@ -1192,6 +1194,7 @@ static int e_show(struct seq_file *m, vo + if (p == SEQ_START_TOKEN) { + seq_puts(m, "# Version 1.1\n"); + seq_puts(m, "# Path Client(Flags) # IPs\n"); ++ seq_puts(m, kpatch_string()); + return 0; + } + +diff -Nupr src.orig/net/netlink/af_netlink.c src/net/netlink/af_netlink.c +--- src.orig/net/netlink/af_netlink.c 2016-12-15 19:55:39.772000000 +0000 ++++ src/net/netlink/af_netlink.c 2016-12-15 19:57:31.072000000 +0000 +@@ -3353,4 +3353,9 @@ panic: + panic("netlink_init: Cannot allocate nl_table\n"); + } + ++char *kpatch_string(void) ++{ ++ return "# kpatch\n"; ++} ++ + core_initcall(netlink_proto_init); diff --git a/test/integration/ubuntu-16.04/multiple.test b/test/integration/ubuntu-16.04/multiple.test index 2d5ed9f..70a3df3 100755 --- a/test/integration/ubuntu-16.04/multiple.test +++ b/test/integration/ubuntu-16.04/multiple.test @@ -1,45 +1,7 @@ #!/bin/bash SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" -ROOTDIR="$(readlink -f $SCRIPTDIR/../../..)" -KPATCH="sudo $ROOTDIR/kpatch/kpatch" -set -o errexit +declare -a blacklist=(data-new-LOADED.test meminfo-cmdline-rebuild-SLOW-LOADED.test) -die() { - echo "ERROR: $@" >&2 - exit 1 -} - -ko_to_test() { - tmp=${1%.ko}-LOADED.test - echo ${tmp#kpatch-} -} - -# make sure any modules added here are disjoint -declare -a modules=(kpatch-cmdline-string.ko kpatch-meminfo-string.ko) - -for mod in "${modules[@]}"; do - testprog=$(ko_to_test $mod) - $SCRIPTDIR/$testprog && die "$SCRIPTDIR/$testprog succeeded before loading any modules" -done - -for mod in "${modules[@]}"; do - $KPATCH load $mod -done - -for mod in "${modules[@]}"; do - testprog=$(ko_to_test $mod) - $SCRIPTDIR/$testprog || die "$SCRIPTDIR/$testprog failed after loading modules" -done - -for mod in "${modules[@]}"; do - $KPATCH unload $mod -done - -for mod in "${modules[@]}"; do - testprog=$(ko_to_test $mod) - $SCRIPTDIR/$testprog && die "$SCRIPTDIR/$testprog succeeded after unloading modules" -done - -exit 0 +source ${SCRIPTDIR}/../common/multiple.template diff --git a/test/integration/vm-integration-run b/test/integration/vm-integration-run new file mode 100755 index 0000000..d5cb560 --- /dev/null +++ b/test/integration/vm-integration-run @@ -0,0 +1,85 @@ +#!/bin/bash + +KPATCH_SLOW=0 +KPATCH_GIT=${KPATCH_GIT:-https://github.com/dynup/kpatch.git} +KPATCH_REV=${KPATCH_REV:-HEAD} +LOGDIR="/vagrant/logs" + +usage() +{ + echo "usage: $(basename "${0}") [options]" >&2 + echo "-h, --help This message" >&2 + echo "-s, --slow Run all of the tests" >&2 + echo "-g, --git Git url to clone from" >&2 + echo "-r, --revision Revision to use (HEAD by default)" >&2 +} + +options="$(getopt -o "shg:r:" -l "slow,help,git:,revision:" -- "$@")" || "getopt failed" + +eval set -- "${options}" + +while [[ $# -gt 0 ]]; do + case "$1" in + -s|--slow) + KPATCH_SLOW=1 + shift + ;; + -g|--git) + KPATCH_GIT="${2}" + shift 2 + ;; + -r|--revision) + KPATCH_REV="${2}" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + --) + shift + break + ;; + esac +done + +git clone "${KPATCH_GIT}" || exit 1 + +cd kpatch || exit 1 + +git fetch origin +refs/pull/*:refs/pull/* +git reset --hard "${KPATCH_REV}" || exit 1 + +# shellcheck disable=SC1091 +source test/integration/lib.sh + +kpatch_dependencies +kpatch_separate_disk_cache /dev/vdb /mnt/build +kpatch_set_ccache_max_size 10G + +# Check if we have predownloaded sources and move them to ~/.kpatch dir which +# is a symlink to a dir on a separate (bigger) volume, suitable for building. +if [[ -d "${HOME}/src" && -f "${HOME}/src/version" ]]; then + cp "${HOME}/src/version" "${HOME}/.kpatch/" + mv "${HOME}/src" "${HOME}/.kpatch/" +fi + +# shellcheck disable=SC1091 +source /etc/os-release + +if [[ "${NAME}" == "Fedora" ]] && [[ "${VERSION_ID}" -lt 30 ]]; then + export BUILDMOD=yes +fi + +if [ ${KPATCH_SLOW} -eq 1 ]; then + make integration-slow 2>&1 +else + make integration-quick 2>&1 +fi + +rc=${PIPESTATUS[0]} +rm -rf "${LOGDIR}" +mkdir -p "${LOGDIR}" +cp ./test/integration/*.log "${LOGDIR}" + +exit "${rc}" diff --git a/test/unit/Makefile b/test/unit/Makefile index 5d5b0db..2123e59 100644 --- a/test/unit/Makefile +++ b/test/unit/Makefile @@ -4,7 +4,9 @@ OBJDIR ?= objs/$(ARCH) .PHONY: all clean all: Makefile.include - cd $(shell git rev-parse --show-toplevel) && git submodule update --init --rebase + @cd $(shell git rev-parse --show-toplevel) && \ + git diff-index --quiet HEAD test/unit/objs || \ + echo -e "\nWARNING: unit tests are out of date - run \"git submodule update\"\n" $(MAKE) -C $(OBJDIR) clean: Makefile.include diff --git a/test/unit/Makefile.include b/test/unit/Makefile.include index 75b550f..21e6be8 100644 --- a/test/unit/Makefile.include +++ b/test/unit/Makefile.include @@ -6,6 +6,7 @@ EXT_OUTPUT ?= OUTPUT.o EXT_TEST_OUTPUT ?= test.out EXT_SYMTAB ?= symtab EXT_SYMVERS ?= symvers +EXT_ENV ?= env TNAME = $(@:.$(EXT_OUTPUT)=) ifndef VERBOSE @@ -32,9 +33,10 @@ define check_stripped = endef define check_all = - $(call check_stripped,$(1)) + $(if $(findstring NOSTRIP,$(1)), , $(call check_stripped,$(1))) endef +.DELETE_ON_ERROR: %.$(EXT_OUTPUT) all: $(TARGETS) $(TEST_TARGETS) @@ -42,7 +44,8 @@ clean: rm -f *.$(EXT_TEST_OUTPUT) *.$(EXT_OUTPUT) %.$(EXT_SYMTAB): - eu-readelf -s $(patsubst %.$(EXT_SYMTAB),%.$(EXT_ORIG),$(@)) >$@ + readelf -s --wide $(patsubst %.$(EXT_SYMTAB),%.$(EXT_ORIG),$(@)) | \ + sed -r 's/\s+\[: 8\]//' >$@ %.$(EXT_TEST_OUTPUT): %.$(EXT_OUTPUT) %.$(EXT_TEST) $(TEST_LIBRARY) @echo "TEST $(@:.$(EXT_TEST_OUTPUT)=)" @@ -54,7 +57,7 @@ clean: @echo "BUILD $(TNAME)" $(call check_all,$(TNAME).$(EXT_ORIG)) $(call check_all,$(TNAME).$(EXT_PATCHED)) - $(CDO_ENV) $(CDO) $(TNAME).$(EXT_ORIG) $(TNAME).$(EXT_PATCHED) \ + $(CDO_ENV) $(shell cat $(TNAME).$(EXT_ENV) 2>/dev/null) $(CDO) $(TNAME).$(EXT_ORIG) $(TNAME).$(EXT_PATCHED) \ vmlinux $(TNAME).$(EXT_SYMTAB) $(SYMVERS_FILE) \ test_$(TNAME) $@ $(MUTE_PASS) @@ -62,7 +65,7 @@ clean: @echo "BUILD $(TNAME)-FAIL" $(call check_all,$(TNAME).$(EXT_ORIG)) $(call check_all,$(TNAME).$(EXT_FAIL)) - ! $(CDO_ENV) $(CDO) $(TNAME).$(EXT_ORIG) $(TNAME).$(EXT_FAIL) \ + ! $(CDO_ENV) $(shell cat $(TNAME).$(EXT_ENV) 2>/dev/null) $(CDO) $(TNAME).$(EXT_ORIG) $(TNAME).$(EXT_FAIL) \ vmlinux $(TNAME).$(EXT_SYMTAB) $(SYMVERS_FILE) \ test_$(TNAME) $@ $(MUTE_FAIL) # Expecting to fail, thus create output file manually so we won't rerun the