Message ID | 20230323114012.2162285-1-mark.rutland@arm.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:604a:0:0:0:0:0 with SMTP id j10csp2859139wrt; Thu, 23 Mar 2023 04:43:47 -0700 (PDT) X-Google-Smtp-Source: AK7set/ziXQHlAlq3iNSQTBmZvakL95uFM/4dDXbHemSOGQ6bIbyDMtqgJGXodE+08wU1XvTAETP X-Received: by 2002:a17:906:1baa:b0:8b1:7fa:6588 with SMTP id r10-20020a1709061baa00b008b107fa6588mr9336264ejg.12.1679571827599; Thu, 23 Mar 2023 04:43:47 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1679571827; cv=none; d=google.com; s=arc-20160816; b=EcIM8D6pGYdvXHRQM6sAC/EPmFImgvl1O7+CIEw1lUbYeqcXYtRkltgNQIhqLYmjIf 8LfLpPp588ycGnvtmSdwh0Wnv6zeFgkuNItFbhRn9KKU+FiXTULWh2Mjdo3G40HYoW6K EpY9CMpq0PrmtYjZN9rkkQhHGiYnnGcAei9hiFTGwPA3+J7c8+hJXOjLbo+p22+8nOAR BWyVjBTN8ucJG5VFoNUe5M4vvo2yraSrgqtIh8WrCSDRHh66ly97DbqANyuTGyS7HqF8 wzQOBly/tzJSbG4Q5HIEDMuf8770vx8zkVcX7jUZTFVqJPi5AsJszss34vGcZDfah9M+ RG0Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from; bh=cUiK1PiKoKQMqxnL3Lq6MR1OOvhTz8I8V8LRTGqMveU=; b=0tM36DAUfQz35zNjGM+C4KtXP8e1tB0S4wSo1VLYGLH4MjN8VWwBM6XRh8MQkby9fV c08bt2XwYkEqQYJ2L/HkXko5PA1uiz+J0x+lfFNNKAeJ7eltfRHLFVxWRCPC2g8vWpgg AtMnXdl6D/sNYlTu6CMGK/HfxZHc2vlFN6fpPgIZjQqS8OdEyThQx0gduqbLTNhvsY2g YZnisNiHMC3x0b1APKkcncPL4Khy/n+Ch/D4m+3nQhmAkTuqziqiXfvSoUlZ6xa7JM3u t6rQegwnVEY970F589rdV7GtaDRy15HB256KN9Y2D4EAQnWKs6zfnus9gGPM3rkyCxS8 KHlg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=arm.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id f7-20020a056402160700b005020d1700a6si1837237edv.8.2023.03.23.04.43.24; Thu, 23 Mar 2023 04:43:47 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=arm.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230016AbjCWLkZ (ORCPT <rfc822;ezelljr.billy@gmail.com> + 99 others); Thu, 23 Mar 2023 07:40:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34244 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229532AbjCWLkX (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Thu, 23 Mar 2023 07:40:23 -0400 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id DAAFEF746 for <linux-kernel@vger.kernel.org>; Thu, 23 Mar 2023 04:40:21 -0700 (PDT) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id AB6D34B3; Thu, 23 Mar 2023 04:41:05 -0700 (PDT) Received: from lakrids.cambridge.arm.com (usa-sjc-imap-foss1.foss.arm.com [10.121.207.14]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id E13953F766; Thu, 23 Mar 2023 04:40:20 -0700 (PDT) From: Mark Rutland <mark.rutland@arm.com> To: linux-kernel@vger.kernel.org Cc: mark.rutland@arm.com, mhiramat@kernel.org, rostedt@goodmis.org Subject: [PATCH] tracepoint: Fix CFI failures with tp_stub_func Date: Thu, 23 Mar 2023 11:40:12 +0000 Message-Id: <20230323114012.2162285-1-mark.rutland@arm.com> X-Mailer: git-send-email 2.30.2 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-2.3 required=5.0 tests=RCVD_IN_DNSWL_MED, SPF_HELO_NONE,SPF_NONE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1761158708812024330?= X-GMAIL-MSGID: =?utf-8?q?1761158708812024330?= |
Series |
tracepoint: Fix CFI failures with tp_stub_func
|
|
Commit Message
Mark Rutland
March 23, 2023, 11:40 a.m. UTC
When CLANG_CFI is in use, using tracing will occasionally result in
CFI failures, e.g.
| CFI failure at syscall_trace_enter+0x66c/0x7d0 (target: tp_stub_func+0x0/0x2c; expected type: 0x4877830c)
| Internal error: Oops - CFI: 00000000f200823a [#1] PREEMPT SMP
| CPU: 2 PID: 118 Comm: klogd Not tainted 6.3.0-rc3-00002-gc242ea6f2f98 #2
| Hardware name: linux,dummy-virt (DT)
| pstate: 30400005 (nzCV daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
| pc : syscall_trace_enter+0x66c/0x7d0
| lr : syscall_trace_enter+0x5e8/0x7d0
| sp : ffff800015ce7d80
| x29: ffff800015ce7d80 x28: ffff000017538000 x27: 0000000000000003
| x26: ffff8000084c9454 x25: ffff00001182bd10 x24: dfff800000000000
| x23: 1fffe00002ea7001 x22: ffff00001182bd10 x21: ffff000017538008
| x20: ffff000017538000 x19: ffff800015ce7eb0 x18: 0000000000000000
| x17: 000000004877830c x16: 00000000a540670c x15: 0000000000000000
| x14: 0000000000000000 x13: 0000000000000000 x12: ff80800008039d8c
| x11: ff80800008039dd0 x10: 0000000000000000 x9 : 0000000000000000
| x8 : 0000000000000000 x7 : 0000000000000000 x6 : 0000000000000000
| x5 : 0000000000000000 x4 : 0000000000000000 x3 : 0000000000000000
| x2 : 00000000000000ce x1 : ffff800015ce7eb0 x0 : ffff800013d55000
| Call trace:
| syscall_trace_enter+0x66c/0x7d0
| el0_svc_common+0x1dc/0x268
| do_el0_svc+0x70/0x1a4
| el0_svc+0x58/0x14c
| el0t_64_sync_handler+0x84/0xf0
| el0t_64_sync+0x190/0x194
| Code: 72906191 72a90ef1 6b11021f 54000040 (d4304740)
| ---[ end trace 0000000000000000 ]---
This happens because the function prototype of tp_sub_func doesn't match
the prototype of the tracepoint function. As each tracepoint may have a
distinct prototype, it's not possible to share a common stub function.
Avoid this by creating a stub function for each tracepoint with the
correct prototype, and using these rather than tp_stub_func.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Masami Hiramatsu <mhiramat@kernel.org
---
include/linux/tracepoint-defs.h | 1 +
include/linux/tracepoint.h | 5 +++++
kernel/tracepoint.c | 31 ++++++++++++++-----------------
3 files changed, 20 insertions(+), 17 deletions(-)
Comments
On Thu, 23 Mar 2023 11:40:12 +0000 Mark Rutland <mark.rutland@arm.com> wrote: > When CLANG_CFI is in use, using tracing will occasionally result in > CFI failures, e.g. > > | CFI failure at syscall_trace_enter+0x66c/0x7d0 (target: tp_stub_func+0x0/0x2c; expected type: 0x4877830c) > | Internal error: Oops - CFI: 00000000f200823a [#1] PREEMPT SMP > | CPU: 2 PID: 118 Comm: klogd Not tainted 6.3.0-rc3-00002-gc242ea6f2f98 #2 > | Hardware name: linux,dummy-virt (DT) > | pstate: 30400005 (nzCV daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) > | pc : syscall_trace_enter+0x66c/0x7d0 > | lr : syscall_trace_enter+0x5e8/0x7d0 > | sp : ffff800015ce7d80 > | x29: ffff800015ce7d80 x28: ffff000017538000 x27: 0000000000000003 > | x26: ffff8000084c9454 x25: ffff00001182bd10 x24: dfff800000000000 > | x23: 1fffe00002ea7001 x22: ffff00001182bd10 x21: ffff000017538008 > | x20: ffff000017538000 x19: ffff800015ce7eb0 x18: 0000000000000000 > | x17: 000000004877830c x16: 00000000a540670c x15: 0000000000000000 > | x14: 0000000000000000 x13: 0000000000000000 x12: ff80800008039d8c > | x11: ff80800008039dd0 x10: 0000000000000000 x9 : 0000000000000000 > | x8 : 0000000000000000 x7 : 0000000000000000 x6 : 0000000000000000 > | x5 : 0000000000000000 x4 : 0000000000000000 x3 : 0000000000000000 > | x2 : 00000000000000ce x1 : ffff800015ce7eb0 x0 : ffff800013d55000 > | Call trace: > | syscall_trace_enter+0x66c/0x7d0 > | el0_svc_common+0x1dc/0x268 > | do_el0_svc+0x70/0x1a4 > | el0_svc+0x58/0x14c > | el0t_64_sync_handler+0x84/0xf0 > | el0t_64_sync+0x190/0x194 > | Code: 72906191 72a90ef1 6b11021f 54000040 (d4304740) > | ---[ end trace 0000000000000000 ]--- > > This happens because the function prototype of tp_sub_func doesn't match > the prototype of the tracepoint function. As each tracepoint may have a > distinct prototype, it's not possible to share a common stub function. I hate C! > > Avoid this by creating a stub function for each tracepoint with the > correct prototype, and using these rather than tp_stub_func. > > Signed-off-by: Mark Rutland <mark.rutland@arm.com> > Cc: Steven Rostedt <rostedt@goodmis.org> > Cc: Masami Hiramatsu <mhiramat@kernel.org > --- > include/linux/tracepoint-defs.h | 1 + > include/linux/tracepoint.h | 5 +++++ > kernel/tracepoint.c | 31 ++++++++++++++----------------- > 3 files changed, 20 insertions(+), 17 deletions(-) > > diff --git a/include/linux/tracepoint-defs.h b/include/linux/tracepoint-defs.h > index e7c2276be33eb..a306ac7255220 100644 > --- a/include/linux/tracepoint-defs.h > +++ b/include/linux/tracepoint-defs.h > @@ -35,6 +35,7 @@ struct tracepoint { > struct static_call_key *static_call_key; > void *static_call_tramp; > void *iterator; > + void *stub; > int (*regfunc)(void); > void (*unregfunc)(void); > struct tracepoint_func __rcu *funcs; > diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h > index 6811e43c1b5c2..1640926441910 100644 > --- a/include/linux/tracepoint.h > +++ b/include/linux/tracepoint.h > @@ -303,6 +303,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > __section("__tracepoints_strings") = #_name; \ > extern struct static_call_key STATIC_CALL_KEY(tp_func_##_name); \ > int __traceiter_##_name(void *__data, proto); \ > + void __tracestub_##_name(void *, proto); \ > struct tracepoint __tracepoint_##_name __used \ > __section("__tracepoints") = { \ > .name = __tpstrtab_##_name, \ > @@ -310,6 +311,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > .static_call_key = &STATIC_CALL_KEY(tp_func_##_name), \ > .static_call_tramp = STATIC_CALL_TRAMP_ADDR(tp_func_##_name), \ > .iterator = &__traceiter_##_name, \ > + .stub = &__tracestub_##_name, \ > .regfunc = _reg, \ > .unregfunc = _unreg, \ > .funcs = NULL }; \ > @@ -330,6 +332,9 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > } \ > return 0; \ > } \ > + void __tracestub_##_name(void *__data, proto) \ > + { \ > + } \ I purposely did not do this because this adds over a thousand stub functions! It adds one for *every* tracepoint (and that is a superset of trace events). Is there some other way we could do this? C really really needs a way to make a generic void do_nothing(...) function! I added some compiler folks to the Cc to hear our grievances. -- Steve > DEFINE_STATIC_CALL(tp_func_##_name, __traceiter_##_name); > > #define DEFINE_TRACE(name, proto, args) \ > diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c > index 8d1507dd07246..d9186629a0095 100644 > --- a/kernel/tracepoint.c > +++ b/kernel/tracepoint.c > @@ -98,12 +98,6 @@ struct tp_probes { > struct tracepoint_func probes[]; > }; > > -/* Called in removal of a func but failed to allocate a new tp_funcs */ > -static void tp_stub_func(void) > -{ > - return; > -} > - > static inline void *allocate_probes(int count) > { > struct tp_probes *p = kmalloc(struct_size(p, probes, count), > @@ -177,13 +171,15 @@ static void debug_print_probes(struct tracepoint_func *funcs) > } > > static struct tracepoint_func * > -func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func, > +func_add(struct tracepoint *tp, struct tracepoint_func **funcs, > + struct tracepoint_func *tp_func, > int prio) > { > struct tracepoint_func *old, *new; > int iter_probes; /* Iterate over old probe array. */ > int nr_probes = 0; /* Counter for probes */ > int pos = -1; /* Insertion position into new array */ > + void *stub_func = tp->stub; > > if (WARN_ON(!tp_func->func)) > return ERR_PTR(-EINVAL); > @@ -193,7 +189,7 @@ func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func, > if (old) { > /* (N -> N+1), (N != 0, 1) probes */ > for (iter_probes = 0; old[iter_probes].func; iter_probes++) { > - if (old[iter_probes].func == tp_stub_func) > + if (old[iter_probes].func == stub_func) > continue; /* Skip stub functions. */ > if (old[iter_probes].func == tp_func->func && > old[iter_probes].data == tp_func->data) > @@ -208,7 +204,7 @@ func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func, > if (old) { > nr_probes = 0; > for (iter_probes = 0; old[iter_probes].func; iter_probes++) { > - if (old[iter_probes].func == tp_stub_func) > + if (old[iter_probes].func == stub_func) > continue; > /* Insert before probes of lower priority */ > if (pos < 0 && old[iter_probes].prio < prio) > @@ -229,11 +225,12 @@ func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func, > return old; > } > > -static void *func_remove(struct tracepoint_func **funcs, > - struct tracepoint_func *tp_func) > +static void *func_remove(struct tracepoint *tp, struct tracepoint_func **funcs, > + struct tracepoint_func *tp_func) > { > int nr_probes = 0, nr_del = 0, i; > struct tracepoint_func *old, *new; > + void *stub_func = tp->stub; > > old = *funcs; > > @@ -246,7 +243,7 @@ static void *func_remove(struct tracepoint_func **funcs, > for (nr_probes = 0; old[nr_probes].func; nr_probes++) { > if ((old[nr_probes].func == tp_func->func && > old[nr_probes].data == tp_func->data) || > - old[nr_probes].func == tp_stub_func) > + old[nr_probes].func == stub_func) > nr_del++; > } > } > @@ -269,7 +266,7 @@ static void *func_remove(struct tracepoint_func **funcs, > for (i = 0; old[i].func; i++) { > if ((old[i].func != tp_func->func || > old[i].data != tp_func->data) && > - old[i].func != tp_stub_func) > + old[i].func != stub_func) > new[j++] = old[i]; > } > new[nr_probes - nr_del].func = NULL; > @@ -277,12 +274,12 @@ static void *func_remove(struct tracepoint_func **funcs, > } else { > /* > * Failed to allocate, replace the old function > - * with calls to tp_stub_func. > + * with calls to stub_func. > */ > for (i = 0; old[i].func; i++) { > if (old[i].func == tp_func->func && > old[i].data == tp_func->data) > - WRITE_ONCE(old[i].func, tp_stub_func); > + WRITE_ONCE(old[i].func, stub_func); > } > *funcs = old; > } > @@ -335,7 +332,7 @@ static int tracepoint_add_func(struct tracepoint *tp, > > tp_funcs = rcu_dereference_protected(tp->funcs, > lockdep_is_held(&tracepoints_mutex)); > - old = func_add(&tp_funcs, func, prio); > + old = func_add(tp, &tp_funcs, func, prio); > if (IS_ERR(old)) { > WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM); > return PTR_ERR(old); > @@ -400,7 +397,7 @@ static int tracepoint_remove_func(struct tracepoint *tp, > > tp_funcs = rcu_dereference_protected(tp->funcs, > lockdep_is_held(&tracepoints_mutex)); > - old = func_remove(&tp_funcs, func); > + old = func_remove(tp, &tp_funcs, func); > if (WARN_ON_ONCE(IS_ERR(old))) > return PTR_ERR(old); >
[adding Sami and Peter] On Thu, Mar 23, 2023 at 08:53:21AM -0400, Steven Rostedt wrote: > On Thu, 23 Mar 2023 11:40:12 +0000 > Mark Rutland <mark.rutland@arm.com> wrote: > > > When CLANG_CFI is in use, using tracing will occasionally result in > > CFI failures, e.g. > > > > | CFI failure at syscall_trace_enter+0x66c/0x7d0 (target: tp_stub_func+0x0/0x2c; expected type: 0x4877830c) > > | Internal error: Oops - CFI: 00000000f200823a [#1] PREEMPT SMP > > | CPU: 2 PID: 118 Comm: klogd Not tainted 6.3.0-rc3-00002-gc242ea6f2f98 #2 > > | Hardware name: linux,dummy-virt (DT) > > | pstate: 30400005 (nzCV daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) > > | pc : syscall_trace_enter+0x66c/0x7d0 > > | lr : syscall_trace_enter+0x5e8/0x7d0 > > | sp : ffff800015ce7d80 > > | x29: ffff800015ce7d80 x28: ffff000017538000 x27: 0000000000000003 > > | x26: ffff8000084c9454 x25: ffff00001182bd10 x24: dfff800000000000 > > | x23: 1fffe00002ea7001 x22: ffff00001182bd10 x21: ffff000017538008 > > | x20: ffff000017538000 x19: ffff800015ce7eb0 x18: 0000000000000000 > > | x17: 000000004877830c x16: 00000000a540670c x15: 0000000000000000 > > | x14: 0000000000000000 x13: 0000000000000000 x12: ff80800008039d8c > > | x11: ff80800008039dd0 x10: 0000000000000000 x9 : 0000000000000000 > > | x8 : 0000000000000000 x7 : 0000000000000000 x6 : 0000000000000000 > > | x5 : 0000000000000000 x4 : 0000000000000000 x3 : 0000000000000000 > > | x2 : 00000000000000ce x1 : ffff800015ce7eb0 x0 : ffff800013d55000 > > | Call trace: > > | syscall_trace_enter+0x66c/0x7d0 > > | el0_svc_common+0x1dc/0x268 > > | do_el0_svc+0x70/0x1a4 > > | el0_svc+0x58/0x14c > > | el0t_64_sync_handler+0x84/0xf0 > > | el0t_64_sync+0x190/0x194 > > | Code: 72906191 72a90ef1 6b11021f 54000040 (d4304740) > > | ---[ end trace 0000000000000000 ]--- > > > > This happens because the function prototype of tp_sub_func doesn't match > > the prototype of the tracepoint function. As each tracepoint may have a > > distinct prototype, it's not possible to share a common stub function. > > I hate C! > > > > > Avoid this by creating a stub function for each tracepoint with the > > correct prototype, and using these rather than tp_stub_func. > > > > Signed-off-by: Mark Rutland <mark.rutland@arm.com> > > Cc: Steven Rostedt <rostedt@goodmis.org> > > Cc: Masami Hiramatsu <mhiramat@kernel.org > > --- > > include/linux/tracepoint-defs.h | 1 + > > include/linux/tracepoint.h | 5 +++++ > > kernel/tracepoint.c | 31 ++++++++++++++----------------- > > 3 files changed, 20 insertions(+), 17 deletions(-) > > > > diff --git a/include/linux/tracepoint-defs.h b/include/linux/tracepoint-defs.h > > index e7c2276be33eb..a306ac7255220 100644 > > --- a/include/linux/tracepoint-defs.h > > +++ b/include/linux/tracepoint-defs.h > > @@ -35,6 +35,7 @@ struct tracepoint { > > struct static_call_key *static_call_key; > > void *static_call_tramp; > > void *iterator; > > + void *stub; > > int (*regfunc)(void); > > void (*unregfunc)(void); > > struct tracepoint_func __rcu *funcs; > > diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h > > index 6811e43c1b5c2..1640926441910 100644 > > --- a/include/linux/tracepoint.h > > +++ b/include/linux/tracepoint.h > > @@ -303,6 +303,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > > __section("__tracepoints_strings") = #_name; \ > > extern struct static_call_key STATIC_CALL_KEY(tp_func_##_name); \ > > int __traceiter_##_name(void *__data, proto); \ > > + void __tracestub_##_name(void *, proto); \ > > struct tracepoint __tracepoint_##_name __used \ > > __section("__tracepoints") = { \ > > .name = __tpstrtab_##_name, \ > > @@ -310,6 +311,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > > .static_call_key = &STATIC_CALL_KEY(tp_func_##_name), \ > > .static_call_tramp = STATIC_CALL_TRAMP_ADDR(tp_func_##_name), \ > > .iterator = &__traceiter_##_name, \ > > + .stub = &__tracestub_##_name, \ > > .regfunc = _reg, \ > > .unregfunc = _unreg, \ > > .funcs = NULL }; \ > > @@ -330,6 +332,9 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > > } \ > > return 0; \ > > } \ > > + void __tracestub_##_name(void *__data, proto) \ > > + { \ > > + } \ > > I purposely did not do this because this adds over a thousand stub > functions! It adds one for *every* tracepoint (and that is a superset of > trace events). > > Is there some other way we could do this? > > C really really needs a way to make a generic void do_nothing(...) function! > > I added some compiler folks to the Cc to hear our grievances. I pulled in Sami, who did much of the kCFI work, and PeterZ too... We can't have a generic function that's compatible will all function prototypes, since that mechanism would undermine the CFI scheme. Either callers would always have to omit the check, or we're have to have a special "always compatible" type hash, and both would be gigantic targets for attack. Can we avoid the stub entirely? e.g. make hte call conditional on the func pointer not being some bad value (e.g. like the error pointers?). That way we could avoid the call, and we wouldn't need the stub implementation. Another option would be to make the tracepoint function prototypes *actually* compatible. If each tracepoint had something like: struct tracepoint_##_name##_data { unsigned long arg; void *ptr; char whatver[]; }; ... then we could have tracepoint functions consistently take a void pointer to that, and extract the args. We can wrap that in something like the syscall wrappers so that we don't have to write that manually. ... or maybe we can somehow drop the stubs into a magic section and deduplicate them? Thanks. Mark. > > -- Steve > > > > DEFINE_STATIC_CALL(tp_func_##_name, __traceiter_##_name); > > > > #define DEFINE_TRACE(name, proto, args) \ > > diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c > > index 8d1507dd07246..d9186629a0095 100644 > > --- a/kernel/tracepoint.c > > +++ b/kernel/tracepoint.c > > @@ -98,12 +98,6 @@ struct tp_probes { > > struct tracepoint_func probes[]; > > }; > > > > -/* Called in removal of a func but failed to allocate a new tp_funcs */ > > -static void tp_stub_func(void) > > -{ > > - return; > > -} > > - > > static inline void *allocate_probes(int count) > > { > > struct tp_probes *p = kmalloc(struct_size(p, probes, count), > > @@ -177,13 +171,15 @@ static void debug_print_probes(struct tracepoint_func *funcs) > > } > > > > static struct tracepoint_func * > > -func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func, > > +func_add(struct tracepoint *tp, struct tracepoint_func **funcs, > > + struct tracepoint_func *tp_func, > > int prio) > > { > > struct tracepoint_func *old, *new; > > int iter_probes; /* Iterate over old probe array. */ > > int nr_probes = 0; /* Counter for probes */ > > int pos = -1; /* Insertion position into new array */ > > + void *stub_func = tp->stub; > > > > if (WARN_ON(!tp_func->func)) > > return ERR_PTR(-EINVAL); > > @@ -193,7 +189,7 @@ func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func, > > if (old) { > > /* (N -> N+1), (N != 0, 1) probes */ > > for (iter_probes = 0; old[iter_probes].func; iter_probes++) { > > - if (old[iter_probes].func == tp_stub_func) > > + if (old[iter_probes].func == stub_func) > > continue; /* Skip stub functions. */ > > if (old[iter_probes].func == tp_func->func && > > old[iter_probes].data == tp_func->data) > > @@ -208,7 +204,7 @@ func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func, > > if (old) { > > nr_probes = 0; > > for (iter_probes = 0; old[iter_probes].func; iter_probes++) { > > - if (old[iter_probes].func == tp_stub_func) > > + if (old[iter_probes].func == stub_func) > > continue; > > /* Insert before probes of lower priority */ > > if (pos < 0 && old[iter_probes].prio < prio) > > @@ -229,11 +225,12 @@ func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func, > > return old; > > } > > > > -static void *func_remove(struct tracepoint_func **funcs, > > - struct tracepoint_func *tp_func) > > +static void *func_remove(struct tracepoint *tp, struct tracepoint_func **funcs, > > + struct tracepoint_func *tp_func) > > { > > int nr_probes = 0, nr_del = 0, i; > > struct tracepoint_func *old, *new; > > + void *stub_func = tp->stub; > > > > old = *funcs; > > > > @@ -246,7 +243,7 @@ static void *func_remove(struct tracepoint_func **funcs, > > for (nr_probes = 0; old[nr_probes].func; nr_probes++) { > > if ((old[nr_probes].func == tp_func->func && > > old[nr_probes].data == tp_func->data) || > > - old[nr_probes].func == tp_stub_func) > > + old[nr_probes].func == stub_func) > > nr_del++; > > } > > } > > @@ -269,7 +266,7 @@ static void *func_remove(struct tracepoint_func **funcs, > > for (i = 0; old[i].func; i++) { > > if ((old[i].func != tp_func->func || > > old[i].data != tp_func->data) && > > - old[i].func != tp_stub_func) > > + old[i].func != stub_func) > > new[j++] = old[i]; > > } > > new[nr_probes - nr_del].func = NULL; > > @@ -277,12 +274,12 @@ static void *func_remove(struct tracepoint_func **funcs, > > } else { > > /* > > * Failed to allocate, replace the old function > > - * with calls to tp_stub_func. > > + * with calls to stub_func. > > */ > > for (i = 0; old[i].func; i++) { > > if (old[i].func == tp_func->func && > > old[i].data == tp_func->data) > > - WRITE_ONCE(old[i].func, tp_stub_func); > > + WRITE_ONCE(old[i].func, stub_func); > > } > > *funcs = old; > > } > > @@ -335,7 +332,7 @@ static int tracepoint_add_func(struct tracepoint *tp, > > > > tp_funcs = rcu_dereference_protected(tp->funcs, > > lockdep_is_held(&tracepoints_mutex)); > > - old = func_add(&tp_funcs, func, prio); > > + old = func_add(tp, &tp_funcs, func, prio); > > if (IS_ERR(old)) { > > WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM); > > return PTR_ERR(old); > > @@ -400,7 +397,7 @@ static int tracepoint_remove_func(struct tracepoint *tp, > > > > tp_funcs = rcu_dereference_protected(tp->funcs, > > lockdep_is_held(&tracepoints_mutex)); > > - old = func_remove(&tp_funcs, func); > > + old = func_remove(tp, &tp_funcs, func); > > if (WARN_ON_ONCE(IS_ERR(old))) > > return PTR_ERR(old); > > >
On Thu, 23 Mar 2023 08:53:21 -0400 Steven Rostedt <rostedt@goodmis.org> wrote: > > --- a/include/linux/tracepoint.h > > +++ b/include/linux/tracepoint.h > > @@ -303,6 +303,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > > __section("__tracepoints_strings") = #_name; \ > > extern struct static_call_key STATIC_CALL_KEY(tp_func_##_name); \ > > int __traceiter_##_name(void *__data, proto); \ > > + void __tracestub_##_name(void *, proto); \ > > struct tracepoint __tracepoint_##_name __used \ > > __section("__tracepoints") = { \ > > .name = __tpstrtab_##_name, \ > > @@ -310,6 +311,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > > .static_call_key = &STATIC_CALL_KEY(tp_func_##_name), \ > > .static_call_tramp = STATIC_CALL_TRAMP_ADDR(tp_func_##_name), \ > > .iterator = &__traceiter_##_name, \ > > + .stub = &__tracestub_##_name, \ > > .regfunc = _reg, \ > > .unregfunc = _unreg, \ > > .funcs = NULL }; \ > > @@ -330,6 +332,9 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > > } \ > > return 0; \ > > } \ > > + void __tracestub_##_name(void *__data, proto) \ > > + { \ > > + } \ > > I purposely did not do this because this adds over a thousand stub > functions! It adds one for *every* tracepoint (and that is a superset of > trace events). And the commit that added this code: befe6d946551 ("tracepoint: Do not fail unregistering a probe due to memory failure") Has this in the change log: [ Note, this version does use undefined compiler behavior (assuming that a stub function with no parameters or return, can be called by a location that thinks it has parameters but still no return value. Static calls do the same thing, so this trick is not without precedent. There's another solution that uses RCU tricks and is more complex, but can be an alternative if this solution becomes an issue. Link: https://lore.kernel.org/lkml/20210127170721.58bce7cc@gandalf.local.home/ ] -- Steve
On Thu, Mar 23, 2023 at 01:45:34PM +0000, Mark Rutland wrote: > On Thu, Mar 23, 2023 at 08:53:21AM -0400, Steven Rostedt wrote: > > On Thu, 23 Mar 2023 11:40:12 +0000 > > Mark Rutland <mark.rutland@arm.com> wrote: > > > diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h > > > index 6811e43c1b5c2..1640926441910 100644 > > > --- a/include/linux/tracepoint.h > > > +++ b/include/linux/tracepoint.h > > > @@ -303,6 +303,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > > > __section("__tracepoints_strings") = #_name; \ > > > extern struct static_call_key STATIC_CALL_KEY(tp_func_##_name); \ > > > int __traceiter_##_name(void *__data, proto); \ > > > + void __tracestub_##_name(void *, proto); \ > > > struct tracepoint __tracepoint_##_name __used \ > > > __section("__tracepoints") = { \ > > > .name = __tpstrtab_##_name, \ > > > @@ -310,6 +311,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > > > .static_call_key = &STATIC_CALL_KEY(tp_func_##_name), \ > > > .static_call_tramp = STATIC_CALL_TRAMP_ADDR(tp_func_##_name), \ > > > .iterator = &__traceiter_##_name, \ > > > + .stub = &__tracestub_##_name, \ > > > .regfunc = _reg, \ > > > .unregfunc = _unreg, \ > > > .funcs = NULL }; \ > > > @@ -330,6 +332,9 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > > > } \ > > > return 0; \ > > > } \ > > > + void __tracestub_##_name(void *__data, proto) \ > > > + { \ > > > + } \ > > > > I purposely did not do this because this adds over a thousand stub > > functions! It adds one for *every* tracepoint (and that is a superset of > > trace events). > > > > Is there some other way we could do this? > > > > C really really needs a way to make a generic void do_nothing(...) function! > > > > I added some compiler folks to the Cc to hear our grievances. > > I pulled in Sami, who did much of the kCFI work, and PeterZ too... > > We can't have a generic function that's compatible will all function > prototypes, since that mechanism would undermine the CFI scheme. Either callers > would always have to omit the check, or we're have to have a special "always > compatible" type hash, and both would be gigantic targets for attack. > > Can we avoid the stub entirely? e.g. make hte call conditional on the func > pointer not being some bad value (e.g. like the error pointers?). That way we > could avoid the call, and we wouldn't need the stub implementation. Along those lines (and as Peter also suggested over IRC), would something like the below be preferable? Mark. ---->8---- diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 6811e43c1b5c..b8017e906049 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -33,6 +33,8 @@ struct trace_eval_map { #define TRACEPOINT_DEFAULT_PRIO 10 +void tp_stub_func(void); + extern struct srcu_struct tracepoint_srcu; extern int @@ -324,6 +326,8 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) if (it_func_ptr) { \ do { \ it_func = READ_ONCE((it_func_ptr)->func); \ + if (it_func == tp_stub_func) \ + continue; \ __data = (it_func_ptr)->data; \ ((void(*)(void *, proto))(it_func))(__data, args); \ } while ((++it_func_ptr)->func); \ diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 8d1507dd0724..dcf5a637429f 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -99,7 +99,7 @@ struct tp_probes { }; /* Called in removal of a func but failed to allocate a new tp_funcs */ -static void tp_stub_func(void) +void tp_stub_func(void) { return; }
On Thu, Mar 23, 2023 at 12:26:50PM -0400, Steven Rostedt wrote: > On Thu, 23 Mar 2023 08:53:21 -0400 > Steven Rostedt <rostedt@goodmis.org> wrote: > > > > --- a/include/linux/tracepoint.h > > > +++ b/include/linux/tracepoint.h > > > @@ -303,6 +303,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > > > __section("__tracepoints_strings") = #_name; \ > > > extern struct static_call_key STATIC_CALL_KEY(tp_func_##_name); \ > > > int __traceiter_##_name(void *__data, proto); \ > > > + void __tracestub_##_name(void *, proto); \ > > > struct tracepoint __tracepoint_##_name __used \ > > > __section("__tracepoints") = { \ > > > .name = __tpstrtab_##_name, \ > > > @@ -310,6 +311,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > > > .static_call_key = &STATIC_CALL_KEY(tp_func_##_name), \ > > > .static_call_tramp = STATIC_CALL_TRAMP_ADDR(tp_func_##_name), \ > > > .iterator = &__traceiter_##_name, \ > > > + .stub = &__tracestub_##_name, \ > > > .regfunc = _reg, \ > > > .unregfunc = _unreg, \ > > > .funcs = NULL }; \ > > > @@ -330,6 +332,9 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > > > } \ > > > return 0; \ > > > } \ > > > + void __tracestub_##_name(void *__data, proto) \ > > > + { \ > > > + } \ > > > > I purposely did not do this because this adds over a thousand stub > > functions! It adds one for *every* tracepoint (and that is a superset of > > trace events). > > And the commit that added this code: > > befe6d946551 ("tracepoint: Do not fail unregistering a probe due to memory failure") > > Has this in the change log: > > [ Note, this version does use undefined compiler behavior (assuming that > a stub function with no parameters or return, can be called by a location > that thinks it has parameters but still no return value. Static calls > do the same thing, so this trick is not without precedent. > > There's another solution that uses RCU tricks and is more complex, but > can be an alternative if this solution becomes an issue. > > Link: https://lore.kernel.org/lkml/20210127170721.58bce7cc@gandalf.local.home/ > ] FWIW, I'd be happy with that approach too -- we just happened to race with our last replies. :) Mark.
On Thu, 23 Mar 2023 16:27:37 +0000 Mark Rutland <mark.rutland@arm.com> wrote: > On Thu, Mar 23, 2023 at 01:45:34PM +0000, Mark Rutland wrote: > > On Thu, Mar 23, 2023 at 08:53:21AM -0400, Steven Rostedt wrote: > > > On Thu, 23 Mar 2023 11:40:12 +0000 > > > Mark Rutland <mark.rutland@arm.com> wrote: > > > > > diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h > > > > index 6811e43c1b5c2..1640926441910 100644 > > > > --- a/include/linux/tracepoint.h > > > > +++ b/include/linux/tracepoint.h > > > > @@ -303,6 +303,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > > > > __section("__tracepoints_strings") = #_name; \ > > > > extern struct static_call_key STATIC_CALL_KEY(tp_func_##_name); \ > > > > int __traceiter_##_name(void *__data, proto); \ > > > > + void __tracestub_##_name(void *, proto); \ > > > > struct tracepoint __tracepoint_##_name __used \ > > > > __section("__tracepoints") = { \ > > > > .name = __tpstrtab_##_name, \ > > > > @@ -310,6 +311,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > > > > .static_call_key = &STATIC_CALL_KEY(tp_func_##_name), \ > > > > .static_call_tramp = STATIC_CALL_TRAMP_ADDR(tp_func_##_name), \ > > > > .iterator = &__traceiter_##_name, \ > > > > + .stub = &__tracestub_##_name, \ > > > > .regfunc = _reg, \ > > > > .unregfunc = _unreg, \ > > > > .funcs = NULL }; \ > > > > @@ -330,6 +332,9 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > > > > } \ > > > > return 0; \ > > > > } \ > > > > + void __tracestub_##_name(void *__data, proto) \ > > > > + { \ > > > > + } \ > > > > > > I purposely did not do this because this adds over a thousand stub > > > functions! It adds one for *every* tracepoint (and that is a superset of > > > trace events). > > > > > > Is there some other way we could do this? > > > > > > C really really needs a way to make a generic void do_nothing(...) function! > > > > > > I added some compiler folks to the Cc to hear our grievances. > > > > I pulled in Sami, who did much of the kCFI work, and PeterZ too... > > > > We can't have a generic function that's compatible will all function > > prototypes, since that mechanism would undermine the CFI scheme. Either callers > > would always have to omit the check, or we're have to have a special "always > > compatible" type hash, and both would be gigantic targets for attack. > > > > Can we avoid the stub entirely? e.g. make hte call conditional on the func > > pointer not being some bad value (e.g. like the error pointers?). That way we > > could avoid the call, and we wouldn't need the stub implementation. > > Along those lines (and as Peter also suggested over IRC), would something like > the below be preferable? So we had this discussion a while ago: See befe6d946551 ("tracepoint: Do not fail unregistering a probe due to memory failure") Where I believe one answer was to use NULL instead of a stub. I have to go back and re-read that thread. Mathieu was involved with all this too. And as I mentioned in my other reply. There was a more complex solution that could handle this if the stub solution ended up being an issue. Repeated again so Mathieu doesn't have to search for it. [ Note, this version does use undefined compiler behavior (assuming that a stub function with no parameters or return, can be called by a location that thinks it has parameters but still no return value. Static calls do the same thing, so this trick is not without precedent. There's another solution that uses RCU tricks and is more complex, but can be an alternative if this solution becomes an issue. Link: https://lore.kernel.org/lkml/20210127170721.58bce7cc@gandalf.local.home/ ] -- Steve > > Mark. > > ---->8---- > diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h > index 6811e43c1b5c..b8017e906049 100644 > --- a/include/linux/tracepoint.h > +++ b/include/linux/tracepoint.h > @@ -33,6 +33,8 @@ struct trace_eval_map { > > #define TRACEPOINT_DEFAULT_PRIO 10 > > +void tp_stub_func(void); > + > extern struct srcu_struct tracepoint_srcu; > > extern int > @@ -324,6 +326,8 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) > if (it_func_ptr) { \ > do { \ > it_func = READ_ONCE((it_func_ptr)->func); \ > + if (it_func == tp_stub_func) \ > + continue; \ > __data = (it_func_ptr)->data; \ > ((void(*)(void *, proto))(it_func))(__data, args); \ > } while ((++it_func_ptr)->func); \ > diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c > index 8d1507dd0724..dcf5a637429f 100644 > --- a/kernel/tracepoint.c > +++ b/kernel/tracepoint.c > @@ -99,7 +99,7 @@ struct tp_probes { > }; > > /* Called in removal of a func but failed to allocate a new tp_funcs */ > -static void tp_stub_func(void) > +void tp_stub_func(void) > { > return; > }
On 2023-03-23 12:34, Steven Rostedt wrote: > On Thu, 23 Mar 2023 16:27:37 +0000 > Mark Rutland <mark.rutland@arm.com> wrote: > >> On Thu, Mar 23, 2023 at 01:45:34PM +0000, Mark Rutland wrote: >>> On Thu, Mar 23, 2023 at 08:53:21AM -0400, Steven Rostedt wrote: >>>> On Thu, 23 Mar 2023 11:40:12 +0000 >>>> Mark Rutland <mark.rutland@arm.com> wrote: >> >>>>> diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h >>>>> index 6811e43c1b5c2..1640926441910 100644 >>>>> --- a/include/linux/tracepoint.h >>>>> +++ b/include/linux/tracepoint.h >>>>> @@ -303,6 +303,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) >>>>> __section("__tracepoints_strings") = #_name; \ >>>>> extern struct static_call_key STATIC_CALL_KEY(tp_func_##_name); \ >>>>> int __traceiter_##_name(void *__data, proto); \ >>>>> + void __tracestub_##_name(void *, proto); \ >>>>> struct tracepoint __tracepoint_##_name __used \ >>>>> __section("__tracepoints") = { \ >>>>> .name = __tpstrtab_##_name, \ >>>>> @@ -310,6 +311,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) >>>>> .static_call_key = &STATIC_CALL_KEY(tp_func_##_name), \ >>>>> .static_call_tramp = STATIC_CALL_TRAMP_ADDR(tp_func_##_name), \ >>>>> .iterator = &__traceiter_##_name, \ >>>>> + .stub = &__tracestub_##_name, \ >>>>> .regfunc = _reg, \ >>>>> .unregfunc = _unreg, \ >>>>> .funcs = NULL }; \ >>>>> @@ -330,6 +332,9 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) >>>>> } \ >>>>> return 0; \ >>>>> } \ >>>>> + void __tracestub_##_name(void *__data, proto) \ >>>>> + { \ >>>>> + } \ >>>> >>>> I purposely did not do this because this adds over a thousand stub >>>> functions! It adds one for *every* tracepoint (and that is a superset of >>>> trace events). >>>> >>>> Is there some other way we could do this? >>>> >>>> C really really needs a way to make a generic void do_nothing(...) function! >>>> >>>> I added some compiler folks to the Cc to hear our grievances. >>> >>> I pulled in Sami, who did much of the kCFI work, and PeterZ too... >>> >>> We can't have a generic function that's compatible will all function >>> prototypes, since that mechanism would undermine the CFI scheme. Either callers >>> would always have to omit the check, or we're have to have a special "always >>> compatible" type hash, and both would be gigantic targets for attack. >>> >>> Can we avoid the stub entirely? e.g. make hte call conditional on the func >>> pointer not being some bad value (e.g. like the error pointers?). That way we >>> could avoid the call, and we wouldn't need the stub implementation. >> >> Along those lines (and as Peter also suggested over IRC), would something like >> the below be preferable? > > So we had this discussion a while ago: > > See befe6d946551 ("tracepoint: Do not fail unregistering a probe due to memory failure") > > Where I believe one answer was to use NULL instead of a stub. > > I have to go back and re-read that thread. Mathieu was involved with all > this too. > > And as I mentioned in my other reply. There was a more complex solution > that could handle this if the stub solution ended up being an issue. > > Repeated again so Mathieu doesn't have to search for it. > > [ Note, this version does use undefined compiler behavior (assuming that > a stub function with no parameters or return, can be called by a location > that thinks it has parameters but still no return value. Static calls > do the same thing, so this trick is not without precedent. > > There's another solution that uses RCU tricks and is more complex, but > can be an alternative if this solution becomes an issue. > > Link: https://lore.kernel.org/lkml/20210127170721.58bce7cc@gandalf.local.home/ > ] Ugh. The last thing we need there is more RCU complexity. My brain is still recovering from fixing the last time the introduction of static calls special-cases ended up subtly breaking tracepoints. > > -- Steve > > >> >> Mark. >> >> ---->8---- >> diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h >> index 6811e43c1b5c..b8017e906049 100644 >> --- a/include/linux/tracepoint.h >> +++ b/include/linux/tracepoint.h >> @@ -33,6 +33,8 @@ struct trace_eval_map { >> >> #define TRACEPOINT_DEFAULT_PRIO 10 >> >> +void tp_stub_func(void); >> + >> extern struct srcu_struct tracepoint_srcu; >> >> extern int >> @@ -324,6 +326,8 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) >> if (it_func_ptr) { \ >> do { \ >> it_func = READ_ONCE((it_func_ptr)->func); \ >> + if (it_func == tp_stub_func) \ >> + continue; \ Along the lines of my earlier proposal: https://lore.kernel.org/all/1889971276.46615.1605559047845.JavaMail.zimbra@efficios.com/ We could reserve (void *)0x1 as a stub value rather than adding an explicit stub function if we end up skipping the call anyway. The check could be: #define TP_STUB_FN ((void *)0x1) if (unlikely(it_func == TP_STUB_FN)) continue; And we would only need that check for CONFIG_HAVE_STATIC_CALL=y right ? Thanks, Mathieu >> __data = (it_func_ptr)->data; \ >> ((void(*)(void *, proto))(it_func))(__data, args); \ >> } while ((++it_func_ptr)->func); \ >> diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c >> index 8d1507dd0724..dcf5a637429f 100644 >> --- a/kernel/tracepoint.c >> +++ b/kernel/tracepoint.c >> @@ -99,7 +99,7 @@ struct tp_probes { >> }; >> >> /* Called in removal of a func but failed to allocate a new tp_funcs */ >> -static void tp_stub_func(void) >> +void tp_stub_func(void) >> { >> return; >> } >
On Thu, 23 Mar 2023 14:37:15 -0400 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> wrote: > > Repeated again so Mathieu doesn't have to search for it. > > > > [ Note, this version does use undefined compiler behavior (assuming that > > a stub function with no parameters or return, can be called by a location > > that thinks it has parameters but still no return value. Static calls > > do the same thing, so this trick is not without precedent. > > > > There's another solution that uses RCU tricks and is more complex, but > > can be an alternative if this solution becomes an issue. > > > > Link: https://lore.kernel.org/lkml/20210127170721.58bce7cc@gandalf.local.home/ > > ] > > Ugh. The last thing we need there is more RCU complexity. My brain is still recovering > from fixing the last time the introduction of static calls special-cases ended up subtly > breaking tracepoints. Well, we can go back to your approach with doing the check in the iterator. Setting it to 0x1UL I think is what you wanted (to know it's a removed function and not a random NULL). I could write that up if you want. -- Steve
On 2023-03-23 14:42, Steven Rostedt wrote: > On Thu, 23 Mar 2023 14:37:15 -0400 > Mathieu Desnoyers <mathieu.desnoyers@efficios.com> wrote: > >>> Repeated again so Mathieu doesn't have to search for it. >>> >>> [ Note, this version does use undefined compiler behavior (assuming that >>> a stub function with no parameters or return, can be called by a location >>> that thinks it has parameters but still no return value. Static calls >>> do the same thing, so this trick is not without precedent. >>> >>> There's another solution that uses RCU tricks and is more complex, but >>> can be an alternative if this solution becomes an issue. >>> >>> Link: https://lore.kernel.org/lkml/20210127170721.58bce7cc@gandalf.local.home/ >>> ] >> >> Ugh. The last thing we need there is more RCU complexity. My brain is still recovering >> from fixing the last time the introduction of static calls special-cases ended up subtly >> breaking tracepoints. > > Well, we can go back to your approach with doing the check in the iterator. > Setting it to 0x1UL I think is what you wanted (to know it's a removed > function and not a random NULL). > > I could write that up if you want. I can write the patch and send an RFC, won't take long. Thanks, Mathieu > > -- Steve
diff --git a/include/linux/tracepoint-defs.h b/include/linux/tracepoint-defs.h index e7c2276be33eb..a306ac7255220 100644 --- a/include/linux/tracepoint-defs.h +++ b/include/linux/tracepoint-defs.h @@ -35,6 +35,7 @@ struct tracepoint { struct static_call_key *static_call_key; void *static_call_tramp; void *iterator; + void *stub; int (*regfunc)(void); void (*unregfunc)(void); struct tracepoint_func __rcu *funcs; diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 6811e43c1b5c2..1640926441910 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -303,6 +303,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) __section("__tracepoints_strings") = #_name; \ extern struct static_call_key STATIC_CALL_KEY(tp_func_##_name); \ int __traceiter_##_name(void *__data, proto); \ + void __tracestub_##_name(void *, proto); \ struct tracepoint __tracepoint_##_name __used \ __section("__tracepoints") = { \ .name = __tpstrtab_##_name, \ @@ -310,6 +311,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) .static_call_key = &STATIC_CALL_KEY(tp_func_##_name), \ .static_call_tramp = STATIC_CALL_TRAMP_ADDR(tp_func_##_name), \ .iterator = &__traceiter_##_name, \ + .stub = &__tracestub_##_name, \ .regfunc = _reg, \ .unregfunc = _unreg, \ .funcs = NULL }; \ @@ -330,6 +332,9 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) } \ return 0; \ } \ + void __tracestub_##_name(void *__data, proto) \ + { \ + } \ DEFINE_STATIC_CALL(tp_func_##_name, __traceiter_##_name); #define DEFINE_TRACE(name, proto, args) \ diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 8d1507dd07246..d9186629a0095 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -98,12 +98,6 @@ struct tp_probes { struct tracepoint_func probes[]; }; -/* Called in removal of a func but failed to allocate a new tp_funcs */ -static void tp_stub_func(void) -{ - return; -} - static inline void *allocate_probes(int count) { struct tp_probes *p = kmalloc(struct_size(p, probes, count), @@ -177,13 +171,15 @@ static void debug_print_probes(struct tracepoint_func *funcs) } static struct tracepoint_func * -func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func, +func_add(struct tracepoint *tp, struct tracepoint_func **funcs, + struct tracepoint_func *tp_func, int prio) { struct tracepoint_func *old, *new; int iter_probes; /* Iterate over old probe array. */ int nr_probes = 0; /* Counter for probes */ int pos = -1; /* Insertion position into new array */ + void *stub_func = tp->stub; if (WARN_ON(!tp_func->func)) return ERR_PTR(-EINVAL); @@ -193,7 +189,7 @@ func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func, if (old) { /* (N -> N+1), (N != 0, 1) probes */ for (iter_probes = 0; old[iter_probes].func; iter_probes++) { - if (old[iter_probes].func == tp_stub_func) + if (old[iter_probes].func == stub_func) continue; /* Skip stub functions. */ if (old[iter_probes].func == tp_func->func && old[iter_probes].data == tp_func->data) @@ -208,7 +204,7 @@ func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func, if (old) { nr_probes = 0; for (iter_probes = 0; old[iter_probes].func; iter_probes++) { - if (old[iter_probes].func == tp_stub_func) + if (old[iter_probes].func == stub_func) continue; /* Insert before probes of lower priority */ if (pos < 0 && old[iter_probes].prio < prio) @@ -229,11 +225,12 @@ func_add(struct tracepoint_func **funcs, struct tracepoint_func *tp_func, return old; } -static void *func_remove(struct tracepoint_func **funcs, - struct tracepoint_func *tp_func) +static void *func_remove(struct tracepoint *tp, struct tracepoint_func **funcs, + struct tracepoint_func *tp_func) { int nr_probes = 0, nr_del = 0, i; struct tracepoint_func *old, *new; + void *stub_func = tp->stub; old = *funcs; @@ -246,7 +243,7 @@ static void *func_remove(struct tracepoint_func **funcs, for (nr_probes = 0; old[nr_probes].func; nr_probes++) { if ((old[nr_probes].func == tp_func->func && old[nr_probes].data == tp_func->data) || - old[nr_probes].func == tp_stub_func) + old[nr_probes].func == stub_func) nr_del++; } } @@ -269,7 +266,7 @@ static void *func_remove(struct tracepoint_func **funcs, for (i = 0; old[i].func; i++) { if ((old[i].func != tp_func->func || old[i].data != tp_func->data) && - old[i].func != tp_stub_func) + old[i].func != stub_func) new[j++] = old[i]; } new[nr_probes - nr_del].func = NULL; @@ -277,12 +274,12 @@ static void *func_remove(struct tracepoint_func **funcs, } else { /* * Failed to allocate, replace the old function - * with calls to tp_stub_func. + * with calls to stub_func. */ for (i = 0; old[i].func; i++) { if (old[i].func == tp_func->func && old[i].data == tp_func->data) - WRITE_ONCE(old[i].func, tp_stub_func); + WRITE_ONCE(old[i].func, stub_func); } *funcs = old; } @@ -335,7 +332,7 @@ static int tracepoint_add_func(struct tracepoint *tp, tp_funcs = rcu_dereference_protected(tp->funcs, lockdep_is_held(&tracepoints_mutex)); - old = func_add(&tp_funcs, func, prio); + old = func_add(tp, &tp_funcs, func, prio); if (IS_ERR(old)) { WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM); return PTR_ERR(old); @@ -400,7 +397,7 @@ static int tracepoint_remove_func(struct tracepoint *tp, tp_funcs = rcu_dereference_protected(tp->funcs, lockdep_is_held(&tracepoints_mutex)); - old = func_remove(&tp_funcs, func); + old = func_remove(tp, &tp_funcs, func); if (WARN_ON_ONCE(IS_ERR(old))) return PTR_ERR(old);