Re: [PATCH v8 4/5] treewide: Switch memcpy() users of 'task->comm' to a more safer implementation
From: David Laight <hidden>
Date: 2025-09-06 11:14:10
Also in:
bpf, linux-fsdevel, linux-mm, linux-perf-users, lkml
On Mon, 1 Sep 2025 10:58:17 +0530 Bhupesh Sharma [off-list ref] wrote:
Hi Kees, On 8/25/25 7:31 PM, Kees Cook wrote:quoted
On Thu, Aug 21, 2025 at 03:51:51PM +0530, Bhupesh wrote:quoted
As Linus mentioned in [1], currently we have several memcpy() use-cases which use 'current->comm' to copy the task name over to local copies. For an example: ... char comm[TASK_COMM_LEN]; memcpy(comm, current->comm, TASK_COMM_LEN); ... These should be rather calling a wrappper like "get_task_array()", which is implemented as: static __always_inline void __cstr_array_copy(char *dst, const char *src, __kernel_size_t size) { memcpy(dst, src, size); dst[size] = 0; } #define get_task_array(dst,src) \ __cstr_array_copy(dst, src, __must_be_array(dst)) The relevant 'memcpy()' users were identified using the following search pattern: $ git grep 'memcpy.*->comm\>' Link:https://lore.kernel.org/all/CAHk-=wi5c=_-FBGo_88CowJd_F-Gi6Ud9d=TALm65ReN7YjrMw@mail.gmail.com/ (local) #1 Signed-off-by: Bhupesh<redacted> --- include/linux/coredump.h | 2 +- include/linux/sched.h | 32 +++++++++++++++++++ include/linux/tracepoint.h | 4 +-- include/trace/events/block.h | 10 +++--- include/trace/events/oom.h | 2 +- include/trace/events/osnoise.h | 2 +- include/trace/events/sched.h | 13 ++++---- include/trace/events/signal.h | 2 +- include/trace/events/task.h | 4 +-- tools/bpf/bpftool/pids.c | 6 ++-- .../bpf/test_kmods/bpf_testmod-events.h | 2 +- 11 files changed, 54 insertions(+), 25 deletions(-)diff --git a/include/linux/coredump.h b/include/linux/coredump.h index 68861da4cf7c..bcee0afc5eaf 100644 --- a/include/linux/coredump.h +++ b/include/linux/coredump.h@@ -54,7 +54,7 @@ extern void vfs_coredump(const kernel_siginfo_t *siginfo); do { \ char comm[TASK_COMM_LEN]; \ /* This will always be NUL terminated. */ \ - memcpy(comm, current->comm, sizeof(comm)); \ + get_task_array(comm, current->comm); \ printk_ratelimited(Level "coredump: %d(%*pE): " Format "\n", \ task_tgid_vnr(current), (int)strlen(comm), comm, ##__VA_ARGS__); \ } while (0) \diff --git a/include/linux/sched.h b/include/linux/sched.h index 5a58c1270474..d26d1dfb9904 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h@@ -1960,12 +1960,44 @@ extern void wake_up_new_task(struct task_struct *tsk); extern void kick_process(struct task_struct *tsk); +/* + * - Why not use task_lock()? + * User space can randomly change their names anyway, so locking for readers + * doesn't make sense. For writers, locking is probably necessary, as a race + * condition could lead to long-term mixed results. + * The logic inside __set_task_comm() should ensure that the task comm is + * always NUL-terminated and zero-padded. Therefore the race condition between + * reader and writer is not an issue. + */ + extern void __set_task_comm(struct task_struct *tsk, const char *from, bool exec); #define set_task_comm(tsk, from) ({ \ BUILD_BUG_ON(sizeof(from) < TASK_COMM_LEN); \ __set_task_comm(tsk, from, false); \ }) +/* + * 'get_task_array' can be 'data-racy' in the destination and + * should not be used for cases where a 'stable NUL at the end' + * is needed. Its better to use strscpy and friends for such + * use-cases. + * + * It is suited mainly for a 'just copy comm to a constant-sized + * array' case - especially in performance sensitive use-cases, + * like tracing. + */ + +static __always_inline void + __cstr_array_copy(char *dst, const char *src, + __kernel_size_t size) +{ + memcpy(dst, src, size); + dst[size] = 0; +}Please don't reinvent the wheel. :) We already have memtostr, please use that (or memtostr_pad).Sure, but wouldn't we get a static assertion failure: "must be array" for memtostr() usage, because of the following: #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr)) I think it would be easier just to set: memcpy(dst, src, size); dst[size -1] = 0; instead as Linus and Steven suggested.
The compiler is still likely to make a mess of it. You really want: *(u64 *)dst = *(u64 *)src; *(u64 *)(dst + 8) = *(u64 *)(src + 8) & ~htobe64(0xff); That may need something to force 8 byte alignment. Or force 4 byte alignment and use a u64 type with 4 byte alignment. David
Thanks, Bhupeshquoted
quoted
+ +#define get_task_array(dst, src) \ + __cstr_array_copy(dst, src, __must_be_array(dst))Uh, __must_be_array(dst) returns 0 on success. :P Are you sure you tested this?