linux-kselftest.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Miroslav Benes <mbenes@suse.cz>
To: jpoimboe@redhat.com, jikos@kernel.org, pmladek@suse.com,
	joe.lawrence@redhat.com
Cc: peterz@infradead.org, linux-kernel@vger.kernel.org,
	live-patching@vger.kernel.org, shuah@kernel.org,
	linux-kselftest@vger.kernel.org, Miroslav Benes <mbenes@suse.cz>
Subject: [PATCH v2 2/2] selftests/livepatch: Test of the API for specifying functions to search for on a stack
Date: Fri, 10 Dec 2021 13:44:49 +0100	[thread overview]
Message-ID: <20211210124449.21537-3-mbenes@suse.cz> (raw)
In-Reply-To: <20211210124449.21537-1-mbenes@suse.cz>

Add tests for the API which allows the user to specify functions which
are then searched for on any tasks's stack during a transition process.

Signed-off-by: Miroslav Benes <mbenes@suse.cz>
---
 lib/livepatch/Makefile                        |   5 +-
 lib/livepatch/test_klp_func_stack_only_demo.c |  66 ++++++++
 .../test_klp_func_stack_only_demo2.c          |  61 +++++++
 lib/livepatch/test_klp_func_stack_only_mod.c  |  70 ++++++++
 tools/testing/selftests/livepatch/Makefile    |   3 +-
 .../livepatch/test-func-stack-only.sh         | 159 ++++++++++++++++++
 6 files changed, 362 insertions(+), 2 deletions(-)
 create mode 100644 lib/livepatch/test_klp_func_stack_only_demo.c
 create mode 100644 lib/livepatch/test_klp_func_stack_only_demo2.c
 create mode 100644 lib/livepatch/test_klp_func_stack_only_mod.c
 create mode 100755 tools/testing/selftests/livepatch/test-func-stack-only.sh

diff --git a/lib/livepatch/Makefile b/lib/livepatch/Makefile
index dcc912b3478f..ee149b74b20d 100644
--- a/lib/livepatch/Makefile
+++ b/lib/livepatch/Makefile
@@ -11,4 +11,7 @@ obj-$(CONFIG_TEST_LIVEPATCH) += test_klp_atomic_replace.o \
 				test_klp_shadow_vars.o \
 				test_klp_state.o \
 				test_klp_state2.o \
-				test_klp_state3.o
+				test_klp_state3.o \
+				test_klp_func_stack_only_mod.o \
+				test_klp_func_stack_only_demo.o \
+				test_klp_func_stack_only_demo2.o
diff --git a/lib/livepatch/test_klp_func_stack_only_demo.c b/lib/livepatch/test_klp_func_stack_only_demo.c
new file mode 100644
index 000000000000..db0a85d57f2e
--- /dev/null
+++ b/lib/livepatch/test_klp_func_stack_only_demo.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2021 Miroslav Benes <mbenes@suse.cz>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+
+static int func_stack_only;
+module_param(func_stack_only, int, 0644);
+MODULE_PARM_DESC(func_stack_only, "func_stack_only (default=0)");
+
+static void livepatch_child_function(void)
+{
+	pr_info("%s\n", __func__);
+}
+
+static struct klp_func funcs[] = {
+	{
+		.old_name = "child_function",
+		.new_func = livepatch_child_function,
+	}, {}
+};
+
+/* Used if func_stack_only module parameter is true */
+static struct klp_func funcs_stack_only[] = {
+	{
+		.old_name = "child_function",
+		.new_func = livepatch_child_function,
+	}, {
+		.old_name = "parent_function",
+		.stack_only = true,
+	}, {}
+};
+
+static struct klp_object objs[] = {
+	{
+		.name = "test_klp_func_stack_only_mod",
+		.funcs = funcs,
+	}, {}
+};
+
+static struct klp_patch patch = {
+	.mod = THIS_MODULE,
+	.objs = objs,
+};
+
+static int test_klp_func_stack_only_demo_init(void)
+{
+	if (func_stack_only)
+		objs[0].funcs = funcs_stack_only;
+
+	return klp_enable_patch(&patch);
+}
+
+static void test_klp_func_stack_only_demo_exit(void)
+{
+}
+
+module_init(test_klp_func_stack_only_demo_init);
+module_exit(test_klp_func_stack_only_demo_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
+MODULE_AUTHOR("Miroslav Benes <mbenes@suse.cz>");
+MODULE_DESCRIPTION("Livepatch test: func_stack_only demo");
diff --git a/lib/livepatch/test_klp_func_stack_only_demo2.c b/lib/livepatch/test_klp_func_stack_only_demo2.c
new file mode 100644
index 000000000000..e1e166db73e6
--- /dev/null
+++ b/lib/livepatch/test_klp_func_stack_only_demo2.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2021 Miroslav Benes <mbenes@suse.cz>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+
+static void livepatch_child_function(void)
+{
+	pr_info("%s\n", __func__);
+}
+
+static struct klp_func funcs_stack_only[] = {
+	{
+		.old_name = "child_function",
+		.new_func = livepatch_child_function,
+	}, {
+		.old_name = "parent_function",
+		.stack_only = true,
+	}, {}
+};
+
+static struct klp_func busymod_funcs[] = {
+	{
+		.old_name = "busymod_work_func",
+		.stack_only = true,
+	}, {}
+};
+
+static struct klp_object objs[] = {
+	{
+		.name = "test_klp_func_stack_only_mod",
+		.funcs = funcs_stack_only,
+	}, {
+		.name = "test_klp_callback_busy",
+		.funcs = busymod_funcs,
+	}, {}
+};
+
+static struct klp_patch patch = {
+	.mod = THIS_MODULE,
+	.objs = objs,
+};
+
+static int test_klp_func_stack_only_demo2_init(void)
+{
+	return klp_enable_patch(&patch);
+}
+
+static void test_klp_func_stack_only_demo2_exit(void)
+{
+}
+
+module_init(test_klp_func_stack_only_demo2_init);
+module_exit(test_klp_func_stack_only_demo2_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
+MODULE_AUTHOR("Miroslav Benes <mbenes@suse.cz>");
+MODULE_DESCRIPTION("Livepatch test: func_stack_only demo 2");
diff --git a/lib/livepatch/test_klp_func_stack_only_mod.c b/lib/livepatch/test_klp_func_stack_only_mod.c
new file mode 100644
index 000000000000..0876c7bcc671
--- /dev/null
+++ b/lib/livepatch/test_klp_func_stack_only_mod.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2021 Miroslav Benes <mbenes@suse.cz>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+
+/* Controls whether parent_function() waits for completion */
+static bool block_transition;
+module_param(block_transition, bool, 0644);
+MODULE_PARM_DESC(block_transition, "block_transition (default=false)");
+
+/*
+ * work_started completion allows the _init function to make sure that the work
+ *              (parent_function() is really scheduled and executed before
+ *              returning. It solves a possible race.
+ * finish completion causes parent_function() to wait (if block_transition is
+ *        true) and thus it might block the live patching transition if
+ *        parent_function() is specified as stack_only function.
+ */
+static DECLARE_COMPLETION(work_started);
+static DECLARE_COMPLETION(finish);
+
+static noinline void child_function(void)
+{
+	pr_info("%s\n", __func__);
+}
+
+static void parent_function(struct work_struct *work)
+{
+	pr_info("%s enter\n", __func__);
+
+	complete(&work_started);
+
+	child_function();
+
+	if (block_transition)
+		wait_for_completion(&finish);
+
+	pr_info("%s exit\n", __func__);
+}
+
+static DECLARE_WORK(work, parent_function);
+
+static int test_klp_func_stack_only_mod_init(void)
+{
+	pr_info("%s\n", __func__);
+
+	schedule_work(&work);
+	wait_for_completion(&work_started);
+
+	return 0;
+}
+
+static void test_klp_func_stack_only_mod_exit(void)
+{
+	pr_info("%s\n", __func__);
+
+	complete(&finish);
+	flush_work(&work);
+}
+
+module_init(test_klp_func_stack_only_mod_init);
+module_exit(test_klp_func_stack_only_mod_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Miroslav Benes <mbenes@suse.cz>");
+MODULE_DESCRIPTION("Livepatch test: func_stack_only module");
diff --git a/tools/testing/selftests/livepatch/Makefile b/tools/testing/selftests/livepatch/Makefile
index 1acc9e1fa3fb..1223e90d9f05 100644
--- a/tools/testing/selftests/livepatch/Makefile
+++ b/tools/testing/selftests/livepatch/Makefile
@@ -6,7 +6,8 @@ TEST_PROGS := \
 	test-callbacks.sh \
 	test-shadow-vars.sh \
 	test-state.sh \
-	test-ftrace.sh
+	test-ftrace.sh \
+	test-func-stack-only.sh
 
 TEST_FILES := settings
 
diff --git a/tools/testing/selftests/livepatch/test-func-stack-only.sh b/tools/testing/selftests/livepatch/test-func-stack-only.sh
new file mode 100755
index 000000000000..326b002f9297
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test-func-stack-only.sh
@@ -0,0 +1,159 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2021 Miroslav Benes <mbenes@suse.cz>
+
+# The tests for stack_only API which allows users to specify functions to be
+# search for on a stack.
+
+. $(dirname $0)/functions.sh
+
+MOD_TARGET=test_klp_func_stack_only_mod
+MOD_TARGET_BUSY=test_klp_callbacks_busy
+MOD_LIVEPATCH=test_klp_func_stack_only_demo
+MOD_LIVEPATCH2=test_klp_func_stack_only_demo2
+MOD_REPLACE=test_klp_atomic_replace
+
+setup_config
+
+# Non-blocking test. parent_function() calls child_function() and sleeps. The
+# live patch patches child_function(). The test does not use stack_only API and
+# the live patching transition finishes immediately.
+#
+# - load a target module and let its parent_function() sleep
+# - load a live patch which patches child_function()
+# - the transition does not block, because parent_function() is not checked for
+#   its presence on a stack
+# - clean up afterwards
+
+start_test "non-blocking patching without the function on a stack"
+
+load_mod $MOD_TARGET block_transition=1
+load_lp $MOD_LIVEPATCH
+disable_lp $MOD_LIVEPATCH
+unload_lp $MOD_LIVEPATCH
+unload_mod $MOD_TARGET
+
+check_result "% modprobe $MOD_TARGET block_transition=1
+$MOD_TARGET: ${MOD_TARGET}_init
+$MOD_TARGET: parent_function enter
+$MOD_TARGET: child_function
+% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+livepatch: '$MOD_LIVEPATCH': completing patching transition
+livepatch: '$MOD_LIVEPATCH': patching complete
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit
+$MOD_TARGET: parent_function exit"
+
+# Blocking version of the previous test. stack_only is now set for
+# parent_function(). The transition is blocked.
+#
+# - load a target module and let its parent_function() sleep
+# - load a live patch which patches child_function() and specifies
+#   parent_function() as a stack_only function
+# - the transition blocks, because parent_function() is present on a stack
+#   while sleeping there
+# - clean up afterwards
+
+start_test "patching blocked due to the function on a stack"
+
+load_mod $MOD_TARGET block_transition=1
+load_lp_nowait $MOD_LIVEPATCH func_stack_only=1
+
+# Wait until the livepatch reports in-transition state, i.e. that it's
+# stalled on $MOD_TARGET::parent_function()
+loop_until 'grep -q '^1$' /sys/kernel/livepatch/$MOD_LIVEPATCH/transition' ||
+	die "failed to stall transition"
+
+disable_lp $MOD_LIVEPATCH
+unload_lp $MOD_LIVEPATCH
+unload_mod $MOD_TARGET
+
+check_result "% modprobe $MOD_TARGET block_transition=1
+$MOD_TARGET: ${MOD_TARGET}_init
+$MOD_TARGET: parent_function enter
+$MOD_TARGET: child_function
+% modprobe $MOD_LIVEPATCH func_stack_only=1
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+livepatch: '$MOD_LIVEPATCH': starting patching transition
+% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
+livepatch: '$MOD_LIVEPATCH': reversing transition from patching to unpatching
+livepatch: '$MOD_LIVEPATCH': starting unpatching transition
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+% rmmod $MOD_LIVEPATCH
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit
+$MOD_TARGET: parent_function exit"
+
+# Test an atomic replace live patch on top of stack_only live patch. The aim is
+# to test the correct handling of nop functions in stack_only environment.
+#
+# - load a target module and do not let its parent_function() sleep
+# - load a busy target module but do not let its busymod_work_func() sleep.
+#   This is only to have another target module loaded for the next steps.
+# - load a stack_only live patch. It patches the first target module and
+#   defines parent_function() to be stack_only. So there is a klp_object with
+#   both !stack_only and stack_only functions. The live patch also has another
+#   klp_object with busymod_work_func() as stack_only function (and nothing
+#   else). The live patch is smoothly applied because there is no blocking
+#   involved.
+# - load atomic replace live patch which patches a function in vmlinux. No nop
+#   function should be created for stack_only functions
+# - clean up afterwards
+
+start_test "atomic replace on top of a stack_only live patch"
+
+load_mod $MOD_TARGET
+load_mod $MOD_TARGET_BUSY
+load_lp $MOD_LIVEPATCH2
+load_lp $MOD_REPLACE replace=1
+disable_lp $MOD_REPLACE
+unload_lp $MOD_REPLACE
+unload_lp $MOD_LIVEPATCH2
+unload_mod $MOD_TARGET_BUSY
+unload_mod $MOD_TARGET
+
+check_result "% modprobe $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_init
+$MOD_TARGET: parent_function enter
+$MOD_TARGET: child_function
+$MOD_TARGET: parent_function exit
+% modprobe $MOD_TARGET_BUSY
+$MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_init
+$MOD_TARGET_BUSY: busymod_work_func enter
+$MOD_TARGET_BUSY: busymod_work_func exit
+% modprobe $MOD_LIVEPATCH2
+livepatch: enabling patch '$MOD_LIVEPATCH2'
+livepatch: '$MOD_LIVEPATCH2': initializing patching transition
+livepatch: '$MOD_LIVEPATCH2': starting patching transition
+livepatch: '$MOD_LIVEPATCH2': completing patching transition
+livepatch: '$MOD_LIVEPATCH2': patching complete
+% modprobe $MOD_REPLACE replace=1
+livepatch: enabling patch '$MOD_REPLACE'
+livepatch: '$MOD_REPLACE': initializing patching transition
+livepatch: '$MOD_REPLACE': starting patching transition
+livepatch: '$MOD_REPLACE': completing patching transition
+livepatch: '$MOD_REPLACE': patching complete
+% echo 0 > /sys/kernel/livepatch/$MOD_REPLACE/enabled
+livepatch: '$MOD_REPLACE': initializing unpatching transition
+livepatch: '$MOD_REPLACE': starting unpatching transition
+livepatch: '$MOD_REPLACE': completing unpatching transition
+livepatch: '$MOD_REPLACE': unpatching complete
+% rmmod $MOD_REPLACE
+% rmmod $MOD_LIVEPATCH2
+% rmmod $MOD_TARGET_BUSY
+$MOD_TARGET_BUSY: ${MOD_TARGET_BUSY}_exit
+% rmmod $MOD_TARGET
+$MOD_TARGET: ${MOD_TARGET}_exit"
+
+exit 0
-- 
2.34.1


      parent reply	other threads:[~2021-12-10 12:44 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-10 12:44 [PATCH v2 0/2] livepatch: Allow user to specify functions to search for on a stack Miroslav Benes
2021-12-10 12:44 ` [PATCH v2 1/2] " Miroslav Benes
2021-12-13 19:00   ` Josh Poimboeuf
2021-12-14  8:47     ` Miroslav Benes
2021-12-14 12:27       ` Petr Mladek
2021-12-14 15:40         ` Petr Mladek
2021-12-14 23:48           ` Josh Poimboeuf
2021-12-15 14:37             ` Petr Mladek
2021-12-15 18:47               ` Josh Poimboeuf
2021-12-16  9:15                 ` Miroslav Benes
2021-12-10 12:44 ` Miroslav Benes [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20211210124449.21537-3-mbenes@suse.cz \
    --to=mbenes@suse.cz \
    --cc=jikos@kernel.org \
    --cc=joe.lawrence@redhat.com \
    --cc=jpoimboe@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=live-patching@vger.kernel.org \
    --cc=peterz@infradead.org \
    --cc=pmladek@suse.com \
    --cc=shuah@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).