From nobody Tue Apr 7 04:58:32 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0E3ADC43217 for ; Tue, 11 Oct 2022 21:27:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229736AbiJKV1a (ORCPT ); Tue, 11 Oct 2022 17:27:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44928 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229591AbiJKV05 (ORCPT ); Tue, 11 Oct 2022 17:26:57 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9832B9AFA4; Tue, 11 Oct 2022 14:26:48 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 1A37B612DA; Tue, 11 Oct 2022 21:26:48 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 69A51C433B5; Tue, 11 Oct 2022 21:26:47 +0000 (UTC) Received: from rostedt by gandalf.local.home with local (Exim 4.96) (envelope-from ) id 1oiMmE-003Pwn-0f; Tue, 11 Oct 2022 17:27:02 -0400 Message-ID: <20221011212702.044448459@goodmis.org> User-Agent: quilt/0.66 Date: Tue, 11 Oct 2022 17:25:02 -0400 From: Steven Rostedt To: linux-kernel@vger.kernel.org Cc: Masami Hiramatsu , Andrew Morton , Tom Zanussi , stable@vger.kernel.org Subject: [PATCH 1/2] tracing: Move duplicate code of trace_kprobe/eprobe.c into header References: <20221011212501.773319898@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Steven Rostedt (Google)" The functions: fetch_store_strlen_user() fetch_store_strlen() fetch_store_string_user() fetch_store_string() are identical in both trace_kprobe.c and trace_eprobe.c. Move them into a new header file trace_probe_kernel.h to share it. This code will later be used by the synthetic events as well. Marked for stable as a fix for a crash in synthetic events requires it. Cc: stable@vger.kernel.org Fixes: bd82631d7ccdc ("tracing: Add support for dynamic strings to syntheti= c events") Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace_eprobe.c | 60 ++----------------- kernel/trace/trace_kprobe.c | 60 ++----------------- kernel/trace/trace_probe_kernel.h | 96 +++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 110 deletions(-) create mode 100644 kernel/trace/trace_probe_kernel.h diff --git a/kernel/trace/trace_eprobe.c b/kernel/trace/trace_eprobe.c index c08bde9871ec..5dd0617e5df6 100644 --- a/kernel/trace/trace_eprobe.c +++ b/kernel/trace/trace_eprobe.c @@ -16,6 +16,7 @@ #include "trace_dynevent.h" #include "trace_probe.h" #include "trace_probe_tmpl.h" +#include "trace_probe_kernel.h" =20 #define EPROBE_EVENT_SYSTEM "eprobes" =20 @@ -456,29 +457,14 @@ NOKPROBE_SYMBOL(process_fetch_insn) static nokprobe_inline int fetch_store_strlen_user(unsigned long addr) { - const void __user *uaddr =3D (__force const void __user *)addr; - - return strnlen_user_nofault(uaddr, MAX_STRING_SIZE); + return kern_fetch_store_strlen_user(addr); } =20 /* Return the length of string -- including null terminal byte */ static nokprobe_inline int fetch_store_strlen(unsigned long addr) { - int ret, len =3D 0; - u8 c; - -#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE - if (addr < TASK_SIZE) - return fetch_store_strlen_user(addr); -#endif - - do { - ret =3D copy_from_kernel_nofault(&c, (u8 *)addr + len, 1); - len++; - } while (c && ret =3D=3D 0 && len < MAX_STRING_SIZE); - - return (ret < 0) ? ret : len; + return kern_fetch_store_strlen(addr); } =20 /* @@ -488,21 +474,7 @@ fetch_store_strlen(unsigned long addr) static nokprobe_inline int fetch_store_string_user(unsigned long addr, void *dest, void *base) { - const void __user *uaddr =3D (__force const void __user *)addr; - int maxlen =3D get_loc_len(*(u32 *)dest); - void *__dest; - long ret; - - if (unlikely(!maxlen)) - return -ENOMEM; - - __dest =3D get_loc_data(dest, base); - - ret =3D strncpy_from_user_nofault(__dest, uaddr, maxlen); - if (ret >=3D 0) - *(u32 *)dest =3D make_data_loc(ret, __dest - base); - - return ret; + return kern_fetch_store_string_user(addr, dest, base); } =20 /* @@ -512,29 +484,7 @@ fetch_store_string_user(unsigned long addr, void *dest= , void *base) static nokprobe_inline int fetch_store_string(unsigned long addr, void *dest, void *base) { - int maxlen =3D get_loc_len(*(u32 *)dest); - void *__dest; - long ret; - -#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE - if ((unsigned long)addr < TASK_SIZE) - return fetch_store_string_user(addr, dest, base); -#endif - - if (unlikely(!maxlen)) - return -ENOMEM; - - __dest =3D get_loc_data(dest, base); - - /* - * Try to get string again, since the string can be changed while - * probing. - */ - ret =3D strncpy_from_kernel_nofault(__dest, (void *)addr, maxlen); - if (ret >=3D 0) - *(u32 *)dest =3D make_data_loc(ret, __dest - base); - - return ret; + return kern_fetch_store_string(addr, dest, base); } =20 static nokprobe_inline int diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 23f7f0ec4f4c..5a75b039e586 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -20,6 +20,7 @@ #include "trace_kprobe_selftest.h" #include "trace_probe.h" #include "trace_probe_tmpl.h" +#include "trace_probe_kernel.h" =20 #define KPROBE_EVENT_SYSTEM "kprobes" #define KRETPROBE_MAXACTIVE_MAX 4096 @@ -1223,29 +1224,14 @@ static const struct file_operations kprobe_profile_= ops =3D { static nokprobe_inline int fetch_store_strlen_user(unsigned long addr) { - const void __user *uaddr =3D (__force const void __user *)addr; - - return strnlen_user_nofault(uaddr, MAX_STRING_SIZE); + return kern_fetch_store_strlen_user(addr); } =20 /* Return the length of string -- including null terminal byte */ static nokprobe_inline int fetch_store_strlen(unsigned long addr) { - int ret, len =3D 0; - u8 c; - -#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE - if (addr < TASK_SIZE) - return fetch_store_strlen_user(addr); -#endif - - do { - ret =3D copy_from_kernel_nofault(&c, (u8 *)addr + len, 1); - len++; - } while (c && ret =3D=3D 0 && len < MAX_STRING_SIZE); - - return (ret < 0) ? ret : len; + return kern_fetch_store_strlen(addr); } =20 /* @@ -1255,21 +1241,7 @@ fetch_store_strlen(unsigned long addr) static nokprobe_inline int fetch_store_string_user(unsigned long addr, void *dest, void *base) { - const void __user *uaddr =3D (__force const void __user *)addr; - int maxlen =3D get_loc_len(*(u32 *)dest); - void *__dest; - long ret; - - if (unlikely(!maxlen)) - return -ENOMEM; - - __dest =3D get_loc_data(dest, base); - - ret =3D strncpy_from_user_nofault(__dest, uaddr, maxlen); - if (ret >=3D 0) - *(u32 *)dest =3D make_data_loc(ret, __dest - base); - - return ret; + return kern_fetch_store_string_user(addr, dest, base); } =20 /* @@ -1279,29 +1251,7 @@ fetch_store_string_user(unsigned long addr, void *de= st, void *base) static nokprobe_inline int fetch_store_string(unsigned long addr, void *dest, void *base) { - int maxlen =3D get_loc_len(*(u32 *)dest); - void *__dest; - long ret; - -#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE - if ((unsigned long)addr < TASK_SIZE) - return fetch_store_string_user(addr, dest, base); -#endif - - if (unlikely(!maxlen)) - return -ENOMEM; - - __dest =3D get_loc_data(dest, base); - - /* - * Try to get string again, since the string can be changed while - * probing. - */ - ret =3D strncpy_from_kernel_nofault(__dest, (void *)addr, maxlen); - if (ret >=3D 0) - *(u32 *)dest =3D make_data_loc(ret, __dest - base); - - return ret; + return kern_fetch_store_string(addr, dest, base); } =20 static nokprobe_inline int diff --git a/kernel/trace/trace_probe_kernel.h b/kernel/trace/trace_probe_k= ernel.h new file mode 100644 index 000000000000..1d43df29a1f8 --- /dev/null +++ b/kernel/trace/trace_probe_kernel.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __TRACE_PROBE_KERNEL_H_ +#define __TRACE_PROBE_KERNEL_H_ + +/* + * This depends on trace_probe.h, but can not include it due to + * the way trace_probe_tmpl.h is used by trace_kprobe.c and trace_eprobe.c. + * Which means that any other user must include trace_probe.h before inclu= ding + * this file. + */ +/* Return the length of string -- including null terminal byte */ +static nokprobe_inline int +kern_fetch_store_strlen_user(unsigned long addr) +{ + const void __user *uaddr =3D (__force const void __user *)addr; + + return strnlen_user_nofault(uaddr, MAX_STRING_SIZE); +} + +/* Return the length of string -- including null terminal byte */ +static nokprobe_inline int +kern_fetch_store_strlen(unsigned long addr) +{ + int ret, len =3D 0; + u8 c; + +#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE + if (addr < TASK_SIZE) + return kern_fetch_store_strlen_user(addr); +#endif + + do { + ret =3D copy_from_kernel_nofault(&c, (u8 *)addr + len, 1); + len++; + } while (c && ret =3D=3D 0 && len < MAX_STRING_SIZE); + + return (ret < 0) ? ret : len; +} + +/* + * Fetch a null-terminated string from user. Caller MUST set *(u32 *)buf + * with max length and relative data location. + */ +static nokprobe_inline int +kern_fetch_store_string_user(unsigned long addr, void *dest, void *base) +{ + const void __user *uaddr =3D (__force const void __user *)addr; + int maxlen =3D get_loc_len(*(u32 *)dest); + void *__dest; + long ret; + + if (unlikely(!maxlen)) + return -ENOMEM; + + __dest =3D get_loc_data(dest, base); + + ret =3D strncpy_from_user_nofault(__dest, uaddr, maxlen); + if (ret >=3D 0) + *(u32 *)dest =3D make_data_loc(ret, __dest - base); + + return ret; +} + +/* + * Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max + * length and relative data location. + */ +static nokprobe_inline int +kern_fetch_store_string(unsigned long addr, void *dest, void *base) +{ + int maxlen =3D get_loc_len(*(u32 *)dest); + void *__dest; + long ret; + +#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE + if ((unsigned long)addr < TASK_SIZE) + return kern_fetch_store_string_user(addr, dest, base); +#endif + + if (unlikely(!maxlen)) + return -ENOMEM; + + __dest =3D get_loc_data(dest, base); + + /* + * Try to get string again, since the string can be changed while + * probing. + */ + ret =3D strncpy_from_kernel_nofault(__dest, (void *)addr, maxlen); + if (ret >=3D 0) + *(u32 *)dest =3D make_data_loc(ret, __dest - base); + + return ret; +} + +#endif /* __TRACE_PROBE_KERNEL_H_ */ --=20 2.35.1 From nobody Tue Apr 7 04:58:32 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 51241C4332F for ; Tue, 11 Oct 2022 21:27:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229672AbiJKV1Y (ORCPT ); Tue, 11 Oct 2022 17:27:24 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44996 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229827AbiJKV05 (ORCPT ); Tue, 11 Oct 2022 17:26:57 -0400 Received: from ams.source.kernel.org (ams.source.kernel.org [IPv6:2604:1380:4601:e00::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 298569AFBF; Tue, 11 Oct 2022 14:26:50 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id DBE87B81714; Tue, 11 Oct 2022 21:26:48 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 88F8BC43470; Tue, 11 Oct 2022 21:26:47 +0000 (UTC) Received: from rostedt by gandalf.local.home with local (Exim 4.96) (envelope-from ) id 1oiMmE-003PxL-1E; Tue, 11 Oct 2022 17:27:02 -0400 Message-ID: <20221011212702.220048293@goodmis.org> User-Agent: quilt/0.66 Date: Tue, 11 Oct 2022 17:25:03 -0400 From: Steven Rostedt To: linux-kernel@vger.kernel.org Cc: Masami Hiramatsu , Andrew Morton , Tom Zanussi , stable@vger.kernel.org Subject: [PATCH 2/2] tracing: Fix reading strings from synthetic events References: <20221011212501.773319898@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: "Steven Rostedt (Google)" The follow commands caused a crash: # cd /sys/kernel/tracing # echo 's:open char file[]' > dynamic_events # echo 'hist:keys=3Dcommon_pid:file=3Dfilename:onchange($file).trace(open= ,$file)' > events/syscalls/sys_enter_openat/trigger' # echo 1 > events/synthetic/open/enable BOOM! The problem is that the synthetic event field "char file[]" will read the value given to it as a string without any memory checks to make sure the address is valid. The above example will pass in the user space address and the sythetic event code will happily call strlen() on it and then strscpy() where either one will cause an oops when accessing user space addresses. Use the helper functions from trace_kprobe and trace_eprobe that can read strings safely (and actually succeed when the address is from user space and the memory is mapped in). Cc: stable@vger.kernel.org Fixes: bd82631d7ccdc ("tracing: Add support for dynamic strings to syntheti= c events") Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace_events_synth.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/kernel/trace/trace_events_synth.c b/kernel/trace/trace_events_= synth.c index 5e8c07aef071..eae15bde883d 100644 --- a/kernel/trace/trace_events_synth.c +++ b/kernel/trace/trace_events_synth.c @@ -17,6 +17,8 @@ /* for gfp flag names */ #include #include +#include "trace_probe.h" +#include "trace_probe_kernel.h" =20 #include "trace_synth.h" =20 @@ -409,6 +411,7 @@ static unsigned int trace_string(struct synth_trace_eve= nt *entry, { unsigned int len =3D 0; char *str_field; + int ret; =20 if (is_dynamic) { u32 data_offset; @@ -417,19 +420,28 @@ static unsigned int trace_string(struct synth_trace_e= vent *entry, data_offset +=3D event->n_u64 * sizeof(u64); data_offset +=3D data_size; =20 - str_field =3D (char *)entry + data_offset; - - len =3D strlen(str_val) + 1; - strscpy(str_field, str_val, len); - + len =3D kern_fetch_store_strlen(str_val) + 1; + if (len =3D=3D 1) + len =3D strlen("fault") + 1; data_offset |=3D len << 16; *(u32 *)&entry->fields[*n_u64] =3D data_offset; =20 + kern_fetch_store_string((unsigned long)str_val, &entry->fields[*n_u64], = entry); + (*n_u64)++; } else { str_field =3D (char *)&entry->fields[*n_u64]; =20 - strscpy(str_field, str_val, STR_VAR_LEN_MAX); +#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE + if ((unsigned long)str_val < TASK_SIZE) + ret =3D strncpy_from_user_nofault(str_field, str_val, STR_VAR_LEN_MAX); + else +#endif + ret =3D strncpy_from_kernel_nofault(str_field, str_val, STR_VAR_LEN_MAX= ); + + if (ret < 0) + strcpy(str_field, "(fault)"); + (*n_u64) +=3D STR_VAR_LEN_MAX / sizeof(u64); } =20 @@ -462,7 +474,9 @@ static notrace void trace_event_raw_event_synth(void *_= _data, val_idx =3D var_ref_idx[field_pos]; str_val =3D (char *)(long)var_ref_vals[val_idx]; =20 - len =3D strlen(str_val) + 1; + len =3D kern_fetch_store_strlen(str_val) + 1; + if (len =3D=3D 1) + len =3D strlen("(fault)") + 1; =20 fields_size +=3D len; } --=20 2.35.1