From: Joe Lawrence <joe.lawrence@redhat.com>
To: Miroslav Benes <mbenes@suse.cz>
Cc: rostedt@goodmis.org, mingo@redhat.com, jpoimboe@redhat.com,
jikos@kernel.org, pmladek@suse.com, linux-kernel@vger.kernel.org,
live-patching@vger.kernel.org
Subject: Re: [PATCH v2] ftrace: Introduce PERMANENT ftrace_ops flag
Date: Mon, 14 Oct 2019 18:31:00 -0400 [thread overview]
Message-ID: <20191014223100.GA16608@redhat.com> (raw)
In-Reply-To: <20191014105923.29607-1-mbenes@suse.cz>
On Mon, Oct 14, 2019 at 12:59:23PM +0200, Miroslav Benes wrote:
> Livepatch uses ftrace for redirection to new patched functions. It means
> that if ftrace is disabled, all live patched functions are disabled as
> well. Toggling global 'ftrace_enabled' sysctl thus affect it directly.
> It is not a problem per se, because only administrator can set sysctl
> values, but it still may be surprising.
>
> Introduce PERMANENT ftrace_ops flag to amend this. If the
> FTRACE_OPS_FL_PERMANENT is set on any ftrace ops, the tracing cannot be
> disabled by disabling ftrace_enabled. Equally, a callback with the flag
> set cannot be registered if ftrace_enabled is disabled.
>
> Signed-off-by: Miroslav Benes <mbenes@suse.cz>
> ---
> v1->v2:
> - different logic, proposed by Joe Lawrence
>
> Two things I am not sure about much:
>
> - return codes. I chose EBUSY, because it seemed the least
> inappropriate. I usually pick the wrong one, so suggestions are
> welcome.
> - I did not add any pr_* reporting the problem to make it consistent
> with the existing code.
>
> Documentation/trace/ftrace-uses.rst | 8 ++++++++
> Documentation/trace/ftrace.rst | 4 +++-
> include/linux/ftrace.h | 3 +++
> kernel/livepatch/patch.c | 3 ++-
> kernel/trace/ftrace.c | 23 +++++++++++++++++++++--
> 5 files changed, 37 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/trace/ftrace-uses.rst b/Documentation/trace/ftrace-uses.rst
> index 1fbc69894eed..740bd0224d35 100644
> --- a/Documentation/trace/ftrace-uses.rst
> +++ b/Documentation/trace/ftrace-uses.rst
> @@ -170,6 +170,14 @@ FTRACE_OPS_FL_RCU
> a callback may be executed and RCU synchronization will not protect
> it.
>
> +FTRACE_OPS_FL_PERMANENT
> + If this is set on any ftrace ops, then the tracing cannot disabled by
> + writing 0 to the proc sysctl ftrace_enabled. Equally, a callback with
> + the flag set cannot be registered if ftrace_enabled is 0.
> +
> + Livepatch uses it not to lose the function redirection, so the system
> + stays protected.
> +
>
> Filtering which functions to trace
> ==================================
> diff --git a/Documentation/trace/ftrace.rst b/Documentation/trace/ftrace.rst
> index e3060eedb22d..d2b5657ed33e 100644
> --- a/Documentation/trace/ftrace.rst
> +++ b/Documentation/trace/ftrace.rst
> @@ -2976,7 +2976,9 @@ Note, the proc sysctl ftrace_enable is a big on/off switch for the
> function tracer. By default it is enabled (when function tracing is
> enabled in the kernel). If it is disabled, all function tracing is
> disabled. This includes not only the function tracers for ftrace, but
> -also for any other uses (perf, kprobes, stack tracing, profiling, etc).
> +also for any other uses (perf, kprobes, stack tracing, profiling, etc). It
> +cannot be disabled if there is a callback with FTRACE_OPS_FL_PERMANENT set
> +registered.
>
> Please disable this with care.
>
> diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
> index 8a8cb3c401b2..c2cad29dc557 100644
> --- a/include/linux/ftrace.h
> +++ b/include/linux/ftrace.h
> @@ -142,6 +142,8 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops);
> * PID - Is affected by set_ftrace_pid (allows filtering on those pids)
> * RCU - Set when the ops can only be called when RCU is watching.
> * TRACE_ARRAY - The ops->private points to a trace_array descriptor.
> + * PERMAMENT - Set when the ops is permanent and should not be affected by
> + * ftrace_enabled.
> */
> enum {
> FTRACE_OPS_FL_ENABLED = 1 << 0,
> @@ -160,6 +162,7 @@ enum {
> FTRACE_OPS_FL_PID = 1 << 13,
> FTRACE_OPS_FL_RCU = 1 << 14,
> FTRACE_OPS_FL_TRACE_ARRAY = 1 << 15,
> + FTRACE_OPS_FL_PERMANENT = 1 << 16,
> };
>
> #ifdef CONFIG_DYNAMIC_FTRACE
> diff --git a/kernel/livepatch/patch.c b/kernel/livepatch/patch.c
> index bd43537702bd..b552cf2d85f8 100644
> --- a/kernel/livepatch/patch.c
> +++ b/kernel/livepatch/patch.c
> @@ -196,7 +196,8 @@ static int klp_patch_func(struct klp_func *func)
> ops->fops.func = klp_ftrace_handler;
> ops->fops.flags = FTRACE_OPS_FL_SAVE_REGS |
> FTRACE_OPS_FL_DYNAMIC |
> - FTRACE_OPS_FL_IPMODIFY;
> + FTRACE_OPS_FL_IPMODIFY |
> + FTRACE_OPS_FL_PERMANENT;
>
> list_add(&ops->node, &klp_ops);
>
> diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
> index 62a50bf399d6..d2992ea29fe1 100644
> --- a/kernel/trace/ftrace.c
> +++ b/kernel/trace/ftrace.c
> @@ -325,6 +325,8 @@ int __register_ftrace_function(struct ftrace_ops *ops)
> if (ops->flags & FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED)
> ops->flags |= FTRACE_OPS_FL_SAVE_REGS;
> #endif
> + if (!ftrace_enabled && (ops->flags & FTRACE_OPS_FL_PERMANENT))
> + return -EBUSY;
>
> if (!core_kernel_data((unsigned long)ops))
> ops->flags |= FTRACE_OPS_FL_DYNAMIC;
> @@ -6723,6 +6725,18 @@ int unregister_ftrace_function(struct ftrace_ops *ops)
> }
> EXPORT_SYMBOL_GPL(unregister_ftrace_function);
>
> +static bool is_permanent_ops_registered(void)
> +{
> + struct ftrace_ops *op;
> +
> + do_for_each_ftrace_op(op, ftrace_ops_list) {
> + if (op->flags & FTRACE_OPS_FL_PERMANENT)
> + return true;
> + } while_for_each_ftrace_op(op);
> +
> + return false;
> +}
> +
> int
> ftrace_enable_sysctl(struct ctl_table *table, int write,
> void __user *buffer, size_t *lenp,
> @@ -6740,8 +6754,6 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
> if (ret || !write || (last_ftrace_enabled == !!ftrace_enabled))
> goto out;
>
> - last_ftrace_enabled = !!ftrace_enabled;
> -
> if (ftrace_enabled) {
>
> /* we are starting ftrace again */
> @@ -6752,12 +6764,19 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
> ftrace_startup_sysctl();
>
> } else {
> + if (is_permanent_ops_registered()) {
> + ftrace_enabled = last_ftrace_enabled;
> + ret = -EBUSY;
> + goto out;
> + }
> +
> /* stopping ftrace calls (just send to ftrace_stub) */
> ftrace_trace_function = ftrace_stub;
>
> ftrace_shutdown_sysctl();
> }
>
> + last_ftrace_enabled = !!ftrace_enabled;
> out:
> mutex_unlock(&ftrace_lock);
> return ret;
> --
> 2.23.0
>
Hi Miroslav,
Maybe we should add a test to verify this new behavior? See sample
version below (lightly tested). We can add to this one, or patch
seperately if you prefer.
-- Joe
-->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8--
From c8c9f22e3816ca4c90ab7e7159d2ce536eaa5fad Mon Sep 17 00:00:00 2001
From: Joe Lawrence <joe.lawrence@redhat.com>
Date: Mon, 14 Oct 2019 18:25:01 -0400
Subject: [PATCH] selftests/livepatch: test interaction with ftrace_enabled
Since livepatching depends upon ftrace handlers to implement "patched"
functionality, verify that the ftrace_enabled sysctl value interacts
with livepatch registration as expected.
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
---
tools/testing/selftests/livepatch/Makefile | 3 +-
.../testing/selftests/livepatch/functions.sh | 18 +++++
.../selftests/livepatch/test-ftrace.sh | 65 +++++++++++++++++++
3 files changed, 85 insertions(+), 1 deletion(-)
create mode 100755 tools/testing/selftests/livepatch/test-ftrace.sh
diff --git a/tools/testing/selftests/livepatch/Makefile b/tools/testing/selftests/livepatch/Makefile
index fd405402c3ff..1886d9d94b88 100644
--- a/tools/testing/selftests/livepatch/Makefile
+++ b/tools/testing/selftests/livepatch/Makefile
@@ -4,6 +4,7 @@ TEST_PROGS_EXTENDED := functions.sh
TEST_PROGS := \
test-livepatch.sh \
test-callbacks.sh \
- test-shadow-vars.sh
+ test-shadow-vars.sh \
+ test-ftrace.sh
include ../lib.mk
diff --git a/tools/testing/selftests/livepatch/functions.sh b/tools/testing/selftests/livepatch/functions.sh
index 79b0affd21fb..556252efece0 100644
--- a/tools/testing/selftests/livepatch/functions.sh
+++ b/tools/testing/selftests/livepatch/functions.sh
@@ -52,6 +52,24 @@ function set_dynamic_debug() {
EOF
}
+function push_ftrace_enabled() {
+ FTRACE_ENABLED=$(sysctl --values kernel.ftrace_enabled)
+}
+function pop_ftrace_enabled() {
+ if [[ -n "$FTRACE_ENABLED" ]]; then
+ sysctl kernel.ftrace_enabled="$FTRACE_ENABLED"
+ fi
+}
+# set_ftrace_enabled() - save the current ftrace_enabled config and tweak
+# it for the self-tests. Set a script exit trap
+# that restores the original value.
+function set_ftrace_enabled() {
+ local sysctl="$1"
+ trap pop_ftrace_enabled EXIT INT TERM HUP
+ result=$(sysctl kernel.ftrace_enabled="$1" 2>&1 | paste --serial --delimiters=' ')
+ echo "livepatch: $result" > /dev/kmsg
+}
+
# loop_until(cmd) - loop a command until it is successful or $MAX_RETRIES,
# sleep $RETRY_INTERVAL between attempts
# cmd - command and its arguments to run
diff --git a/tools/testing/selftests/livepatch/test-ftrace.sh b/tools/testing/selftests/livepatch/test-ftrace.sh
new file mode 100755
index 000000000000..016576883a33
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test-ftrace.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2019 Joe Lawrence <joe.lawrence@redhat.com>
+
+. $(dirname $0)/functions.sh
+
+MOD_LIVEPATCH=test_klp_livepatch
+
+set_dynamic_debug
+
+
+# TEST: livepatch interaction with ftrace_enabled sysctl
+# - turn ftrace_enabled OFF and verify livepatches can't load
+# - turn ftrace_enabled ON and verify livepatch can load
+# - verify that ftrace_enabled can't be turned OFF while a livepatch is loaded
+
+echo -n "TEST: livepatch interaction with ftrace_enabled sysctl ... "
+dmesg -C
+
+set_ftrace_enabled 0
+load_failing_mod $MOD_LIVEPATCH
+
+set_ftrace_enabled 1
+load_lp $MOD_LIVEPATCH
+if [[ "$(cat /proc/cmdline)" != "$MOD_LIVEPATCH: this has been live patched" ]] ; then
+ echo -e "FAIL\n\n"
+ die "livepatch kselftest(s) failed"
+fi
+
+set_ftrace_enabled 0
+if [[ "$(cat /proc/cmdline)" != "$MOD_LIVEPATCH: this has been live patched" ]] ; then
+ echo -e "FAIL\n\n"
+ die "livepatch kselftest(s) failed"
+fi
+disable_lp $MOD_LIVEPATCH
+unload_lp $MOD_LIVEPATCH
+
+check_result "livepatch: kernel.ftrace_enabled = 0
+% modprobe $MOD_LIVEPATCH
+livepatch: enabling patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': initializing patching transition
+livepatch: failed to register ftrace handler for function 'cmdline_proc_show' (-16)
+livepatch: failed to patch object 'vmlinux'
+livepatch: failed to enable patch '$MOD_LIVEPATCH'
+livepatch: '$MOD_LIVEPATCH': canceling patching transition, going to unpatch
+livepatch: '$MOD_LIVEPATCH': completing unpatching transition
+livepatch: '$MOD_LIVEPATCH': unpatching complete
+modprobe: ERROR: could not insert '$MOD_LIVEPATCH': Device or resource busy
+livepatch: kernel.ftrace_enabled = 1
+% 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
+livepatch: sysctl: setting key \"kernel.ftrace_enabled\": Device or resource busy kernel.ftrace_enabled = 0
+% 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"
+
+
+exit 0
--
2.21.0
next prev parent reply other threads:[~2019-10-14 22:33 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-10-14 10:59 [PATCH v2] ftrace: Introduce PERMANENT ftrace_ops flag Miroslav Benes
2019-10-14 14:26 ` Petr Mladek
2019-10-14 15:17 ` Steven Rostedt
2019-10-15 7:45 ` Petr Mladek
2019-10-15 10:50 ` Miroslav Benes
2019-10-15 13:36 ` Steven Rostedt
2019-10-14 22:31 ` Joe Lawrence [this message]
2019-10-15 11:23 ` Miroslav Benes
2019-10-15 13:25 ` Joe Lawrence
2019-10-15 14:02 ` Miroslav Benes
2019-10-15 14:58 ` Joe Lawrence
2019-10-16 5:02 ` Kamalesh Babulal
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=20191014223100.GA16608@redhat.com \
--to=joe.lawrence@redhat.com \
--cc=jikos@kernel.org \
--cc=jpoimboe@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=live-patching@vger.kernel.org \
--cc=mbenes@suse.cz \
--cc=mingo@redhat.com \
--cc=pmladek@suse.com \
--cc=rostedt@goodmis.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.