* [PATCH v5 0/9] Improve the copy of task comm
@ 2024-08-04 7:56 Yafang Shao
2024-08-04 7:56 ` [PATCH v5 1/9] fs/exec: Drop task_lock() inside __get_task_comm() Yafang Shao
` (9 more replies)
0 siblings, 10 replies; 17+ messages in thread
From: Yafang Shao @ 2024-08-04 7:56 UTC (permalink / raw)
To: akpm
Cc: torvalds, ebiederm, alexei.starovoitov, rostedt, catalin.marinas,
penguin-kernel, linux-mm, linux-fsdevel, linux-trace-kernel,
audit, linux-security-module, selinux, bpf, netdev, dri-devel,
Yafang Shao
Using {memcpy,strncpy,strcpy,kstrdup} to copy the task comm relies on the
length of task comm. Changes in the task comm could result in a destination
string that is overflow. Therefore, we should explicitly ensure the destination
string is always NUL-terminated, regardless of the task comm. This approach
will facilitate future extensions to the task comm.
As suggested by Linus [0], we can identify all relevant code with the
following git grep command:
git grep 'memcpy.*->comm\>'
git grep 'kstrdup.*->comm\>'
git grep 'strncpy.*->comm\>'
git grep 'strcpy.*->comm\>'
PATCH #2~#4: memcpy
PATCH #5~#6: kstrdup
PATCH #7: strncpy
PATCH #8~#9: strcpy
There is a BUILD_BUG_ON() inside get_task_comm(), so when you use
get_task_comm(), it implies that the BUILD_BUG_ON() is necessary. However,
we don't want to impose this restriction on code where the length can be
changed, so we use __get_task_comm(), rather than the get_task_comm().
One use case of get_task_comm() is in code that has already exposed the
length to userspace. In such cases, we specifically add the BUILD_BUG_ON()
to prevent developers from changing it. For more information, see
commit 95af469c4f60 ("fs/binfmt_elf: replace open-coded string copy with
get_task_comm").
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Link: https://lore.kernel.org/all/CAHk-=wjAmmHUg6vho1KjzQi2=psR30+CogFd4aXrThr2gsiS4g@mail.gmail.com/ [0]
Changes:
v4->v5:
- Drop changes in the mm/kmemleak.c as it was fixed by
commit 0b84780134fb ("mm/kmemleak: replace strncpy() with strscpy()")
- Drop changes in kernel/tsacct.c as it was fixed by
commmit 0fe2356434e ("tsacct: replace strncpy() with strscpy()")
v3->v4: https://lore.kernel.org/linux-mm/20240729023719.1933-1-laoar.shao@gmail.com/
- Rename __kstrndup() to __kmemdup_nul() and define it inside mm/util.c
(Matthew)
- Remove unused local varaible (Simon)
v2->v3: https://lore.kernel.org/all/20240621022959.9124-1-laoar.shao@gmail.com/
- Deduplicate code around kstrdup (Andrew)
- Add commit log for dropping task_lock (Catalin)
v1->v2: https://lore.kernel.org/bpf/20240613023044.45873-1-laoar.shao@gmail.com/
- Add comment for dropping task_lock() in __get_task_comm() (Alexei)
- Drop changes in trace event (Steven)
- Fix comment on task comm (Matus)
v1: https://lore.kernel.org/all/20240602023754.25443-1-laoar.shao@gmail.com/
Yafang Shao (9):
fs/exec: Drop task_lock() inside __get_task_comm()
auditsc: Replace memcpy() with __get_task_comm()
security: Replace memcpy() with __get_task_comm()
bpftool: Ensure task comm is always NUL-terminated
mm/util: Fix possible race condition in kstrdup()
mm/util: Deduplicate code in {kstrdup,kstrndup,kmemdup_nul}
tracing: Replace strncpy() with __get_task_comm()
net: Replace strcpy() with __get_task_comm()
drm: Replace strcpy() with __get_task_comm()
drivers/gpu/drm/drm_framebuffer.c | 2 +-
drivers/gpu/drm/i915/i915_gpu_error.c | 2 +-
fs/exec.c | 10 ++++-
include/linux/sched.h | 4 +-
kernel/auditsc.c | 6 +--
kernel/trace/trace.c | 2 +-
kernel/trace/trace_events_hist.c | 2 +-
mm/util.c | 61 ++++++++++++---------------
net/ipv6/ndisc.c | 2 +-
security/lsm_audit.c | 4 +-
security/selinux/selinuxfs.c | 2 +-
tools/bpf/bpftool/pids.c | 2 +
12 files changed, 49 insertions(+), 50 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v5 1/9] fs/exec: Drop task_lock() inside __get_task_comm()
2024-08-04 7:56 [PATCH v5 0/9] Improve the copy of task comm Yafang Shao
@ 2024-08-04 7:56 ` Yafang Shao
2024-08-04 7:56 ` [PATCH v5 2/9] auditsc: Replace memcpy() with __get_task_comm() Yafang Shao
` (8 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Yafang Shao @ 2024-08-04 7:56 UTC (permalink / raw)
To: akpm
Cc: torvalds, ebiederm, alexei.starovoitov, rostedt, catalin.marinas,
penguin-kernel, linux-mm, linux-fsdevel, linux-trace-kernel,
audit, linux-security-module, selinux, bpf, netdev, dri-devel,
Yafang Shao, Alexander Viro, Christian Brauner, Jan Kara,
Kees Cook, Matus Jokay
Quoted from Linus [0]:
Since user space can randomly change their names anyway, using locking
was always wrong for readers (for writers it probably does make sense
to have some lock - although practically speaking nobody cares there
either, but at least for a writer some kind of race could have
long-term mixed results
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Link: https://lore.kernel.org/all/CAHk-=wivfrF0_zvf+oj6==Sh=-npJooP8chLPEfaFV0oNYTTBA@mail.gmail.com [0]
Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Jan Kara <jack@suse.cz>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Cc: Matus Jokay <matus.jokay@stuba.sk>
---
fs/exec.c | 10 ++++++++--
include/linux/sched.h | 4 ++--
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/fs/exec.c b/fs/exec.c
index e55efc761947..6a0ff2e3631f 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1195,12 +1195,18 @@ static int unshare_sighand(struct task_struct *me)
return 0;
}
+/*
+ * 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 strscpy_pad() in __set_task_comm() can ensure that the task comm is
+ * always NUL-terminated. Therefore the race condition between reader and writer
+ * is not an issue.
+ */
char *__get_task_comm(char *buf, size_t buf_size, struct task_struct *tsk)
{
- task_lock(tsk);
/* Always NUL terminated and zero-padded */
strscpy_pad(buf, tsk->comm, buf_size);
- task_unlock(tsk);
return buf;
}
EXPORT_SYMBOL_GPL(__get_task_comm);
diff --git a/include/linux/sched.h b/include/linux/sched.h
index f8d150343d42..71002f0fc085 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1096,9 +1096,9 @@ struct task_struct {
/*
* executable name, excluding path.
*
- * - normally initialized setup_new_exec()
+ * - normally initialized begin_new_exec()
* - access it with [gs]et_task_comm()
- * - lock it with task_lock()
+ * - lock it with task_lock() for writing
*/
char comm[TASK_COMM_LEN];
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v5 2/9] auditsc: Replace memcpy() with __get_task_comm()
2024-08-04 7:56 [PATCH v5 0/9] Improve the copy of task comm Yafang Shao
2024-08-04 7:56 ` [PATCH v5 1/9] fs/exec: Drop task_lock() inside __get_task_comm() Yafang Shao
@ 2024-08-04 7:56 ` Yafang Shao
2024-08-04 7:56 ` [PATCH v5 3/9] security: " Yafang Shao
` (7 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Yafang Shao @ 2024-08-04 7:56 UTC (permalink / raw)
To: akpm
Cc: torvalds, ebiederm, alexei.starovoitov, rostedt, catalin.marinas,
penguin-kernel, linux-mm, linux-fsdevel, linux-trace-kernel,
audit, linux-security-module, selinux, bpf, netdev, dri-devel,
Yafang Shao, Paul Moore, Eric Paris
Using __get_task_comm() to read the task comm ensures that the name is
always NUL-terminated, regardless of the source string. This approach also
facilitates future extensions to the task comm.
Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
Acked-by: Paul Moore <paul@paul-moore.com>
Cc: Eric Paris <eparis@redhat.com>
---
kernel/auditsc.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 6f0d6fb6523f..0459a141dc86 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -2730,7 +2730,7 @@ void __audit_ptrace(struct task_struct *t)
context->target_uid = task_uid(t);
context->target_sessionid = audit_get_sessionid(t);
security_task_getsecid_obj(t, &context->target_sid);
- memcpy(context->target_comm, t->comm, TASK_COMM_LEN);
+ __get_task_comm(context->target_comm, TASK_COMM_LEN, t);
}
/**
@@ -2757,7 +2757,7 @@ int audit_signal_info_syscall(struct task_struct *t)
ctx->target_uid = t_uid;
ctx->target_sessionid = audit_get_sessionid(t);
security_task_getsecid_obj(t, &ctx->target_sid);
- memcpy(ctx->target_comm, t->comm, TASK_COMM_LEN);
+ __get_task_comm(ctx->target_comm, TASK_COMM_LEN, t);
return 0;
}
@@ -2778,7 +2778,7 @@ int audit_signal_info_syscall(struct task_struct *t)
axp->target_uid[axp->pid_count] = t_uid;
axp->target_sessionid[axp->pid_count] = audit_get_sessionid(t);
security_task_getsecid_obj(t, &axp->target_sid[axp->pid_count]);
- memcpy(axp->target_comm[axp->pid_count], t->comm, TASK_COMM_LEN);
+ __get_task_comm(axp->target_comm[axp->pid_count], TASK_COMM_LEN, t);
axp->pid_count++;
return 0;
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v5 3/9] security: Replace memcpy() with __get_task_comm()
2024-08-04 7:56 [PATCH v5 0/9] Improve the copy of task comm Yafang Shao
2024-08-04 7:56 ` [PATCH v5 1/9] fs/exec: Drop task_lock() inside __get_task_comm() Yafang Shao
2024-08-04 7:56 ` [PATCH v5 2/9] auditsc: Replace memcpy() with __get_task_comm() Yafang Shao
@ 2024-08-04 7:56 ` Yafang Shao
2024-08-04 7:56 ` [PATCH v5 4/9] bpftool: Ensure task comm is always NUL-terminated Yafang Shao
` (6 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Yafang Shao @ 2024-08-04 7:56 UTC (permalink / raw)
To: akpm
Cc: torvalds, ebiederm, alexei.starovoitov, rostedt, catalin.marinas,
penguin-kernel, linux-mm, linux-fsdevel, linux-trace-kernel,
audit, linux-security-module, selinux, bpf, netdev, dri-devel,
Yafang Shao, Paul Moore, James Morris, Serge E. Hallyn,
Stephen Smalley, Ondrej Mosnacek
Quoted from Linus [0]:
selinux never wanted a lock, and never wanted any kind of *consistent*
result, it just wanted a *stable* result.
Using __get_task_comm() to read the task comm ensures that the name is
always NUL-terminated, regardless of the source string. This approach also
facilitates future extensions to the task comm.
Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
LINK: https://lore.kernel.org/all/CAHk-=wivfrF0_zvf+oj6==Sh=-npJooP8chLPEfaFV0oNYTTBA@mail.gmail.com/ [0]
Acked-by: Paul Moore <paul@paul-moore.com>
Cc: James Morris <jmorris@namei.org>
Cc: "Serge E. Hallyn" <serge@hallyn.com>
Cc: Stephen Smalley <stephen.smalley.work@gmail.com>
Cc: Ondrej Mosnacek <omosnace@redhat.com>
---
security/lsm_audit.c | 4 ++--
security/selinux/selinuxfs.c | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/security/lsm_audit.c b/security/lsm_audit.c
index 849e832719e2..a922e4339dd5 100644
--- a/security/lsm_audit.c
+++ b/security/lsm_audit.c
@@ -207,7 +207,7 @@ static void dump_common_audit_data(struct audit_buffer *ab,
BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2);
audit_log_format(ab, " pid=%d comm=", task_tgid_nr(current));
- audit_log_untrustedstring(ab, memcpy(comm, current->comm, sizeof(comm)));
+ audit_log_untrustedstring(ab, __get_task_comm(comm, sizeof(comm), current));
switch (a->type) {
case LSM_AUDIT_DATA_NONE:
@@ -302,7 +302,7 @@ static void dump_common_audit_data(struct audit_buffer *ab,
char comm[sizeof(tsk->comm)];
audit_log_format(ab, " opid=%d ocomm=", pid);
audit_log_untrustedstring(ab,
- memcpy(comm, tsk->comm, sizeof(comm)));
+ __get_task_comm(comm, sizeof(comm), tsk));
}
}
break;
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index e172f182b65c..a8a2ec742576 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -708,7 +708,7 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
if (new_value) {
char comm[sizeof(current->comm)];
- memcpy(comm, current->comm, sizeof(comm));
+ __get_task_comm(comm, sizeof(comm), current);
pr_err("SELinux: %s (%d) set checkreqprot to 1. This is no longer supported.\n",
comm, current->pid);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v5 4/9] bpftool: Ensure task comm is always NUL-terminated
2024-08-04 7:56 [PATCH v5 0/9] Improve the copy of task comm Yafang Shao
` (2 preceding siblings ...)
2024-08-04 7:56 ` [PATCH v5 3/9] security: " Yafang Shao
@ 2024-08-04 7:56 ` Yafang Shao
2024-08-04 7:56 ` [PATCH v5 5/9] mm/util: Fix possible race condition in kstrdup() Yafang Shao
` (5 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Yafang Shao @ 2024-08-04 7:56 UTC (permalink / raw)
To: akpm
Cc: torvalds, ebiederm, alexei.starovoitov, rostedt, catalin.marinas,
penguin-kernel, linux-mm, linux-fsdevel, linux-trace-kernel,
audit, linux-security-module, selinux, bpf, netdev, dri-devel,
Yafang Shao, Quentin Monnet
Let's explicitly ensure the destination string is NUL-terminated. This way,
it won't be affected by changes to the source string.
Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
Reviewed-by: Quentin Monnet <qmo@kernel.org>
---
tools/bpf/bpftool/pids.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tools/bpf/bpftool/pids.c b/tools/bpf/bpftool/pids.c
index 9b898571b49e..23f488cf1740 100644
--- a/tools/bpf/bpftool/pids.c
+++ b/tools/bpf/bpftool/pids.c
@@ -54,6 +54,7 @@ static void add_ref(struct hashmap *map, struct pid_iter_entry *e)
ref = &refs->refs[refs->ref_cnt];
ref->pid = e->pid;
memcpy(ref->comm, e->comm, sizeof(ref->comm));
+ ref->comm[sizeof(ref->comm) - 1] = '\0';
refs->ref_cnt++;
return;
@@ -77,6 +78,7 @@ static void add_ref(struct hashmap *map, struct pid_iter_entry *e)
ref = &refs->refs[0];
ref->pid = e->pid;
memcpy(ref->comm, e->comm, sizeof(ref->comm));
+ ref->comm[sizeof(ref->comm) - 1] = '\0';
refs->ref_cnt = 1;
refs->has_bpf_cookie = e->has_bpf_cookie;
refs->bpf_cookie = e->bpf_cookie;
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v5 5/9] mm/util: Fix possible race condition in kstrdup()
2024-08-04 7:56 [PATCH v5 0/9] Improve the copy of task comm Yafang Shao
` (3 preceding siblings ...)
2024-08-04 7:56 ` [PATCH v5 4/9] bpftool: Ensure task comm is always NUL-terminated Yafang Shao
@ 2024-08-04 7:56 ` Yafang Shao
2024-08-04 7:56 ` [PATCH v5 6/9] mm/util: Deduplicate code in {kstrdup,kstrndup,kmemdup_nul} Yafang Shao
` (4 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Yafang Shao @ 2024-08-04 7:56 UTC (permalink / raw)
To: akpm
Cc: torvalds, ebiederm, alexei.starovoitov, rostedt, catalin.marinas,
penguin-kernel, linux-mm, linux-fsdevel, linux-trace-kernel,
audit, linux-security-module, selinux, bpf, netdev, dri-devel,
Yafang Shao
In kstrdup(), it is critical to ensure that the dest string is always
NUL-terminated. However, potential race condidtion can occur between a
writer and a reader.
Consider the following scenario involving task->comm:
reader writer
len = strlen(s) + 1;
strlcpy(tsk->comm, buf, sizeof(tsk->comm));
memcpy(buf, s, len);
In this case, there is a race condition between the reader and the
writer. The reader calculate the length of the string `s` based on the
old value of task->comm. However, during the memcpy(), the string `s`
might be updated by the writer to a new value of task->comm.
If the new task->comm is larger than the old one, the `buf` might not be
NUL-terminated. This can lead to undefined behavior and potential
security vulnerabilities.
Let's fix it by explicitly adding a NUL-terminator.
Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
---
mm/util.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/mm/util.c b/mm/util.c
index 678c647b778f..912d64ede234 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -62,8 +62,14 @@ char *kstrdup(const char *s, gfp_t gfp)
len = strlen(s) + 1;
buf = kmalloc_track_caller(len, gfp);
- if (buf)
+ if (buf) {
memcpy(buf, s, len);
+ /* During memcpy(), the string might be updated to a new value,
+ * which could be longer than the string when strlen() is
+ * called. Therefore, we need to add a null termimator.
+ */
+ buf[len - 1] = '\0';
+ }
return buf;
}
EXPORT_SYMBOL(kstrdup);
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v5 6/9] mm/util: Deduplicate code in {kstrdup,kstrndup,kmemdup_nul}
2024-08-04 7:56 [PATCH v5 0/9] Improve the copy of task comm Yafang Shao
` (4 preceding siblings ...)
2024-08-04 7:56 ` [PATCH v5 5/9] mm/util: Fix possible race condition in kstrdup() Yafang Shao
@ 2024-08-04 7:56 ` Yafang Shao
2024-08-04 7:56 ` [PATCH v5 7/9] tracing: Replace strncpy() with __get_task_comm() Yafang Shao
` (3 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Yafang Shao @ 2024-08-04 7:56 UTC (permalink / raw)
To: akpm
Cc: torvalds, ebiederm, alexei.starovoitov, rostedt, catalin.marinas,
penguin-kernel, linux-mm, linux-fsdevel, linux-trace-kernel,
audit, linux-security-module, selinux, bpf, netdev, dri-devel,
Yafang Shao, Simon Horman, Matthew Wilcox
These three functions follow the same pattern. To deduplicate the code,
let's introduce a common helper __kmemdup_nul().
Suggested-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
Cc: Simon Horman <horms@kernel.org>
Cc: Matthew Wilcox <willy@infradead.org>
---
mm/util.c | 67 +++++++++++++++++++++----------------------------------
1 file changed, 26 insertions(+), 41 deletions(-)
diff --git a/mm/util.c b/mm/util.c
index 912d64ede234..2c5addabd6f7 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -45,33 +45,40 @@ void kfree_const(const void *x)
EXPORT_SYMBOL(kfree_const);
/**
- * kstrdup - allocate space for and copy an existing string
- * @s: the string to duplicate
+ * __kmemdup_nul - Create a NUL-terminated string from @s, which might be unterminated.
+ * @s: The data to copy
+ * @len: The size of the data, including the null terminator
* @gfp: the GFP mask used in the kmalloc() call when allocating memory
*
- * Return: newly allocated copy of @s or %NULL in case of error
+ * Return: newly allocated copy of @s with NUL-termination or %NULL in
+ * case of error
*/
-noinline
-char *kstrdup(const char *s, gfp_t gfp)
+static __always_inline char *__kmemdup_nul(const char *s, size_t len, gfp_t gfp)
{
- size_t len;
char *buf;
- if (!s)
+ buf = kmalloc_track_caller(len, gfp);
+ if (!buf)
return NULL;
- len = strlen(s) + 1;
- buf = kmalloc_track_caller(len, gfp);
- if (buf) {
- memcpy(buf, s, len);
- /* During memcpy(), the string might be updated to a new value,
- * which could be longer than the string when strlen() is
- * called. Therefore, we need to add a null termimator.
- */
- buf[len - 1] = '\0';
- }
+ memcpy(buf, s, len);
+ /* Ensure the buf is always NUL-terminated, regardless of @s. */
+ buf[len - 1] = '\0';
return buf;
}
+
+/**
+ * kstrdup - allocate space for and copy an existing string
+ * @s: the string to duplicate
+ * @gfp: the GFP mask used in the kmalloc() call when allocating memory
+ *
+ * Return: newly allocated copy of @s or %NULL in case of error
+ */
+noinline
+char *kstrdup(const char *s, gfp_t gfp)
+{
+ return s ? __kmemdup_nul(s, strlen(s) + 1, gfp) : NULL;
+}
EXPORT_SYMBOL(kstrdup);
/**
@@ -106,19 +113,7 @@ EXPORT_SYMBOL(kstrdup_const);
*/
char *kstrndup(const char *s, size_t max, gfp_t gfp)
{
- size_t len;
- char *buf;
-
- if (!s)
- return NULL;
-
- len = strnlen(s, max);
- buf = kmalloc_track_caller(len+1, gfp);
- if (buf) {
- memcpy(buf, s, len);
- buf[len] = '\0';
- }
- return buf;
+ return s ? __kmemdup_nul(s, strnlen(s, max) + 1, gfp) : NULL;
}
EXPORT_SYMBOL(kstrndup);
@@ -192,17 +187,7 @@ EXPORT_SYMBOL(kvmemdup);
*/
char *kmemdup_nul(const char *s, size_t len, gfp_t gfp)
{
- char *buf;
-
- if (!s)
- return NULL;
-
- buf = kmalloc_track_caller(len + 1, gfp);
- if (buf) {
- memcpy(buf, s, len);
- buf[len] = '\0';
- }
- return buf;
+ return s ? __kmemdup_nul(s, len + 1, gfp) : NULL;
}
EXPORT_SYMBOL(kmemdup_nul);
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v5 7/9] tracing: Replace strncpy() with __get_task_comm()
2024-08-04 7:56 [PATCH v5 0/9] Improve the copy of task comm Yafang Shao
` (5 preceding siblings ...)
2024-08-04 7:56 ` [PATCH v5 6/9] mm/util: Deduplicate code in {kstrdup,kstrndup,kmemdup_nul} Yafang Shao
@ 2024-08-04 7:56 ` Yafang Shao
2024-08-04 7:56 ` [PATCH v5 8/9] net: Replace strcpy() " Yafang Shao
` (2 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Yafang Shao @ 2024-08-04 7:56 UTC (permalink / raw)
To: akpm
Cc: torvalds, ebiederm, alexei.starovoitov, rostedt, catalin.marinas,
penguin-kernel, linux-mm, linux-fsdevel, linux-trace-kernel,
audit, linux-security-module, selinux, bpf, netdev, dri-devel,
Yafang Shao, Masami Hiramatsu, Mathieu Desnoyers
Using __get_task_comm() to read the task comm ensures that the name is
always NUL-terminated, regardless of the source string. This approach also
facilitates future extensions to the task comm.
Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
---
kernel/trace/trace.c | 2 +-
kernel/trace/trace_events_hist.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 10cd38bce2f1..985d2bf2bbc5 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -1907,7 +1907,7 @@ __update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu)
max_data->critical_start = data->critical_start;
max_data->critical_end = data->critical_end;
- strncpy(max_data->comm, tsk->comm, TASK_COMM_LEN);
+ __get_task_comm(max_data->comm, TASK_COMM_LEN, tsk);
max_data->pid = tsk->pid;
/*
* If tsk == current, then use current_uid(), as that does not use
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 6ece1308d36a..721d4758a79f 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -1599,7 +1599,7 @@ static inline void save_comm(char *comm, struct task_struct *task)
return;
}
- strncpy(comm, task->comm, TASK_COMM_LEN);
+ __get_task_comm(comm, TASK_COMM_LEN, task);
}
static void hist_elt_data_free(struct hist_elt_data *elt_data)
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v5 8/9] net: Replace strcpy() with __get_task_comm()
2024-08-04 7:56 [PATCH v5 0/9] Improve the copy of task comm Yafang Shao
` (6 preceding siblings ...)
2024-08-04 7:56 ` [PATCH v5 7/9] tracing: Replace strncpy() with __get_task_comm() Yafang Shao
@ 2024-08-04 7:56 ` Yafang Shao
2024-08-04 7:56 ` [PATCH v5 9/9] drm: " Yafang Shao
2024-08-05 21:28 ` [PATCH v5 0/9] Improve the copy of task comm Linus Torvalds
9 siblings, 0 replies; 17+ messages in thread
From: Yafang Shao @ 2024-08-04 7:56 UTC (permalink / raw)
To: akpm
Cc: torvalds, ebiederm, alexei.starovoitov, rostedt, catalin.marinas,
penguin-kernel, linux-mm, linux-fsdevel, linux-trace-kernel,
audit, linux-security-module, selinux, bpf, netdev, dri-devel,
Yafang Shao, David S. Miller, David Ahern, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
To prevent errors from occurring when the src string is longer than the dst
string in strcpy(), we should use __get_task_comm() instead. This approach
also facilitates future extensions to the task comm.
Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: David Ahern <dsahern@kernel.org>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Paolo Abeni <pabeni@redhat.com>
---
net/ipv6/ndisc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 70a0b2ad6bd7..fa3a91e36ba0 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1942,7 +1942,7 @@ static void ndisc_warn_deprecated_sysctl(const struct ctl_table *ctl,
static char warncomm[TASK_COMM_LEN];
static int warned;
if (strcmp(warncomm, current->comm) && warned < 5) {
- strcpy(warncomm, current->comm);
+ __get_task_comm(warncomm, TASK_COMM_LEN, current);
pr_warn("process `%s' is using deprecated sysctl (%s) net.ipv6.neigh.%s.%s - use net.ipv6.neigh.%s.%s_ms instead\n",
warncomm, func,
dev_name, ctl->procname,
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v5 9/9] drm: Replace strcpy() with __get_task_comm()
2024-08-04 7:56 [PATCH v5 0/9] Improve the copy of task comm Yafang Shao
` (7 preceding siblings ...)
2024-08-04 7:56 ` [PATCH v5 8/9] net: Replace strcpy() " Yafang Shao
@ 2024-08-04 7:56 ` Yafang Shao
2024-08-05 21:28 ` [PATCH v5 0/9] Improve the copy of task comm Linus Torvalds
9 siblings, 0 replies; 17+ messages in thread
From: Yafang Shao @ 2024-08-04 7:56 UTC (permalink / raw)
To: akpm
Cc: torvalds, ebiederm, alexei.starovoitov, rostedt, catalin.marinas,
penguin-kernel, linux-mm, linux-fsdevel, linux-trace-kernel,
audit, linux-security-module, selinux, bpf, netdev, dri-devel,
Yafang Shao, Daniel Vetter, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie
To prevent erros from occurring when the src string is longer than the
dst string in strcpy(), we should use __get_task_comm() instead. This
approach also facilitates future extensions to the task comm.
Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: David Airlie <airlied@gmail.com>
---
drivers/gpu/drm/drm_framebuffer.c | 2 +-
drivers/gpu/drm/i915/i915_gpu_error.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c
index 888aadb6a4ac..25262b07ffaf 100644
--- a/drivers/gpu/drm/drm_framebuffer.c
+++ b/drivers/gpu/drm/drm_framebuffer.c
@@ -868,7 +868,7 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
INIT_LIST_HEAD(&fb->filp_head);
fb->funcs = funcs;
- strcpy(fb->comm, current->comm);
+ __get_task_comm(fb->comm, sizeof(fb->comm), current);
ret = __drm_mode_object_add(dev, &fb->base, DRM_MODE_OBJECT_FB,
false, drm_framebuffer_free);
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index 96c6cafd5b9e..163457a6e484 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -1412,7 +1412,7 @@ static bool record_context(struct i915_gem_context_coredump *e,
rcu_read_lock();
task = pid_task(ctx->pid, PIDTYPE_PID);
if (task) {
- strcpy(e->comm, task->comm);
+ __get_task_comm(e->comm, sizeof(e->comm), task);
e->pid = task->pid;
}
rcu_read_unlock();
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v5 0/9] Improve the copy of task comm
2024-08-04 7:56 [PATCH v5 0/9] Improve the copy of task comm Yafang Shao
` (8 preceding siblings ...)
2024-08-04 7:56 ` [PATCH v5 9/9] drm: " Yafang Shao
@ 2024-08-05 21:28 ` Linus Torvalds
2024-08-06 3:00 ` Yafang Shao
9 siblings, 1 reply; 17+ messages in thread
From: Linus Torvalds @ 2024-08-05 21:28 UTC (permalink / raw)
To: Yafang Shao
Cc: akpm, ebiederm, alexei.starovoitov, rostedt, catalin.marinas,
penguin-kernel, linux-mm, linux-fsdevel, linux-trace-kernel,
audit, linux-security-module, selinux, bpf, netdev, dri-devel
On Sun, 4 Aug 2024 at 00:56, Yafang Shao <laoar.shao@gmail.com> wrote:
>
> There is a BUILD_BUG_ON() inside get_task_comm(), so when you use
> get_task_comm(), it implies that the BUILD_BUG_ON() is necessary.
Let's just remove that silly BUILD_BUG_ON(). I don't think it adds any
value, and honestly, it really only makes this patch-series uglier
when reasonable uses suddenly pointlessly need that double-underscore
version..
So let's aim at
(a) documenting that the last byte in 'tsk->comm{}' is always
guaranteed to be NUL, so that the thing can always just be treated as
a string. Yes, it may change under us, but as long as we know there is
always a stable NUL there *somewhere*, we really really don't care.
(b) removing __get_task_comm() entirely, and replacing it with a
plain 'str*cpy*()' functions
The whole (a) thing is a requirement anyway, since the *bulk* of
tsk->comm really just seems to be various '%s' things in printk
strings etc.
And once we just admit that we can use the string functions, all the
get_task_comm() stuff is just unnecessary.
And yes, some people may want to use the strscpy_pad() function
because they want to fill the whole destination buffer. But that's
entirely about the *destination* use, not the tsk->comm[] source, so
it has nothing to do with any kind of "get_task_comm()" logic, and it
was always wrong to care about the buffer sizes magically matching.
Hmm?
Linus
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v5 0/9] Improve the copy of task comm
2024-08-05 21:28 ` [PATCH v5 0/9] Improve the copy of task comm Linus Torvalds
@ 2024-08-06 3:00 ` Yafang Shao
2024-08-06 3:09 ` Linus Torvalds
0 siblings, 1 reply; 17+ messages in thread
From: Yafang Shao @ 2024-08-06 3:00 UTC (permalink / raw)
To: Linus Torvalds
Cc: akpm, ebiederm, alexei.starovoitov, rostedt, catalin.marinas,
penguin-kernel, linux-mm, linux-fsdevel, linux-trace-kernel,
audit, linux-security-module, selinux, bpf, netdev, dri-devel
On Tue, Aug 6, 2024 at 5:28 AM Linus Torvalds
<torvalds@linux-foundation.org> wrote:
>
> On Sun, 4 Aug 2024 at 00:56, Yafang Shao <laoar.shao@gmail.com> wrote:
> >
> > There is a BUILD_BUG_ON() inside get_task_comm(), so when you use
> > get_task_comm(), it implies that the BUILD_BUG_ON() is necessary.
>
> Let's just remove that silly BUILD_BUG_ON(). I don't think it adds any
> value, and honestly, it really only makes this patch-series uglier
> when reasonable uses suddenly pointlessly need that double-underscore
> version..
>
> So let's aim at
>
> (a) documenting that the last byte in 'tsk->comm{}' is always
> guaranteed to be NUL, so that the thing can always just be treated as
> a string. Yes, it may change under us, but as long as we know there is
> always a stable NUL there *somewhere*, we really really don't care.
>
> (b) removing __get_task_comm() entirely, and replacing it with a
> plain 'str*cpy*()' functions
>
> The whole (a) thing is a requirement anyway, since the *bulk* of
> tsk->comm really just seems to be various '%s' things in printk
> strings etc.
>
> And once we just admit that we can use the string functions, all the
> get_task_comm() stuff is just unnecessary.
>
> And yes, some people may want to use the strscpy_pad() function
> because they want to fill the whole destination buffer. But that's
> entirely about the *destination* use, not the tsk->comm[] source, so
> it has nothing to do with any kind of "get_task_comm()" logic, and it
> was always wrong to care about the buffer sizes magically matching.
>
> Hmm?
One concern about removing the BUILD_BUG_ON() is that if we extend
TASK_COMM_LEN to a larger size, such as 24, the caller with a
hardcoded 16-byte buffer may overflow. This could be an issue with
code in include/linux/elfcore.h and include/linux/elfcore-compat.h,
posing a risk. However, I believe it is the caller's responsibility to
explicitly add a null terminator if it uses a fixed buffer that cannot
be changed. Therefore, the following code change is necessary:
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 5ae8045f4df4..e4b0b7cf0c1f 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1579,6 +1579,7 @@ static int fill_psinfo(struct elf_prpsinfo
*psinfo, struct task_struct *p,
SET_GID(psinfo->pr_gid, from_kgid_munged(cred->user_ns, cred->gid));
rcu_read_unlock();
get_task_comm(psinfo->pr_fname, p);
+ psinfo->pr_fname[15] = '\0';
return 0;
}
However, it is currently safe to remove the BUILD_BUG_ON() since
TASK_COMM_LEN is still 16.
--
Regards
Yafang
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v5 0/9] Improve the copy of task comm
2024-08-06 3:00 ` Yafang Shao
@ 2024-08-06 3:09 ` Linus Torvalds
2024-08-06 3:50 ` Yafang Shao
0 siblings, 1 reply; 17+ messages in thread
From: Linus Torvalds @ 2024-08-06 3:09 UTC (permalink / raw)
To: Yafang Shao
Cc: akpm, ebiederm, alexei.starovoitov, rostedt, catalin.marinas,
penguin-kernel, linux-mm, linux-fsdevel, linux-trace-kernel,
audit, linux-security-module, selinux, bpf, netdev, dri-devel
On Mon, 5 Aug 2024 at 20:01, Yafang Shao <laoar.shao@gmail.com> wrote:
>
> One concern about removing the BUILD_BUG_ON() is that if we extend
> TASK_COMM_LEN to a larger size, such as 24, the caller with a
> hardcoded 16-byte buffer may overflow.
No, not at all. Because get_task_comm() - and the replacements - would
never use TASK_COMM_LEN.
They'd use the size of the *destination*. That's what the code already does:
#define get_task_comm(buf, tsk) ({ \
...
__get_task_comm(buf, sizeof(buf), tsk); \
note how it uses "sizeof(buf)".
Now, it might be a good idea to also verify that 'buf' is an actual
array, and that this code doesn't do some silly "sizeof(ptr)" thing.
We do have a helper for that, so we could do something like
#define get_task_comm(buf, tsk) \
strscpy_pad(buf, __must_be_array(buf)+sizeof(buf), (tsk)->comm)
as a helper macro for this all.
(Although I'm not convinced we generally want the "_pad()" version,
but whatever).
Linus
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v5 0/9] Improve the copy of task comm
2024-08-06 3:09 ` Linus Torvalds
@ 2024-08-06 3:50 ` Yafang Shao
0 siblings, 0 replies; 17+ messages in thread
From: Yafang Shao @ 2024-08-06 3:50 UTC (permalink / raw)
To: Linus Torvalds
Cc: akpm, ebiederm, alexei.starovoitov, rostedt, catalin.marinas,
penguin-kernel, linux-mm, linux-fsdevel, linux-trace-kernel,
audit, linux-security-module, selinux, bpf, netdev, dri-devel
On Tue, Aug 6, 2024 at 11:10 AM Linus Torvalds
<torvalds@linux-foundation.org> wrote:
>
> On Mon, 5 Aug 2024 at 20:01, Yafang Shao <laoar.shao@gmail.com> wrote:
> >
> > One concern about removing the BUILD_BUG_ON() is that if we extend
> > TASK_COMM_LEN to a larger size, such as 24, the caller with a
> > hardcoded 16-byte buffer may overflow.
>
> No, not at all. Because get_task_comm() - and the replacements - would
> never use TASK_COMM_LEN.
>
> They'd use the size of the *destination*. That's what the code already does:
>
> #define get_task_comm(buf, tsk) ({ \
> ...
> __get_task_comm(buf, sizeof(buf), tsk); \
>
> note how it uses "sizeof(buf)".
>
> Now, it might be a good idea to also verify that 'buf' is an actual
> array, and that this code doesn't do some silly "sizeof(ptr)" thing.
>
> We do have a helper for that, so we could do something like
>
> #define get_task_comm(buf, tsk) \
> strscpy_pad(buf, __must_be_array(buf)+sizeof(buf), (tsk)->comm)
>
> as a helper macro for this all.
>
> (Although I'm not convinced we generally want the "_pad()" version,
> but whatever).
>
Will do it.
Thanks for your explanation.
--
Regards
Yafang
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v5 0/9] Improve the copy of task comm
@ 2024-08-06 17:28 Alejandro Colomar
2024-08-08 2:49 ` Yafang Shao
0 siblings, 1 reply; 17+ messages in thread
From: Alejandro Colomar @ 2024-08-06 17:28 UTC (permalink / raw)
To: torvalds
Cc: akpm, alexei.starovoitov, audit, bpf, catalin.marinas, dri-devel,
ebiederm, laoar.shao, linux-fsdevel, linux-mm,
linux-security-module, linux-trace-kernel, netdev, penguin-kernel,
rostedt, selinux, serge
[-- Attachment #1: Type: text/plain, Size: 5129 bytes --]
Hi Linus,
Serge let me know about this thread earlier today.
On 2024-08-05, Linus Torvalds <torvalds@linux-foundation.org> wrote:
> On Mon, 5 Aug 2024 at 20:01, Yafang Shao <laoar.shao@gmail.com> wrote:
> >
> > One concern about removing the BUILD_BUG_ON() is that if we extend
> > TASK_COMM_LEN to a larger size, such as 24, the caller with a
> > hardcoded 16-byte buffer may overflow.
>
> No, not at all. Because get_task_comm() - and the replacements - would
> never use TASK_COMM_LEN.
>
> They'd use the size of the *destination*. That's what the code already does:
>
> #define get_task_comm(buf, tsk) ({ \
> ...
> __get_task_comm(buf, sizeof(buf), tsk); \
>
> note how it uses "sizeof(buf)".
In shadow.git, we also implemented macros that are named after functions
and calculate the appropriate number of elements internally.
$ grepc -h STRNCAT .
#define STRNCAT(dst, src) strncat(dst, src, NITEMS(src))
$ grepc -h STRNCPY .
#define STRNCPY(dst, src) strncpy(dst, src, NITEMS(dst))
$ grepc -h STRTCPY .
#define STRTCPY(dst, src) strtcpy(dst, src, NITEMS(dst))
$ grepc -h STRFTIME .
#define STRFTIME(dst, fmt, tm) strftime(dst, NITEMS(dst), fmt, tm)
$ grepc -h DAY_TO_STR .
#define DAY_TO_STR(str, day, iso) day_to_str(NITEMS(str), str, day, iso)
They're quite useful, and when implementing them we found and fixed
several bugs thanks to them.
> Now, it might be a good idea to also verify that 'buf' is an actual
> array, and that this code doesn't do some silly "sizeof(ptr)" thing.
I decided to use NITEMS() instead of sizeof() for that reason.
(NITEMS() is just our name for ARRAY_SIZE().)
$ grepc -h NITEMS .
#define NITEMS(a) (SIZEOF_ARRAY((a)) / sizeof((a)[0]))
> We do have a helper for that, so we could do something like
>
> #define get_task_comm(buf, tsk) \
> strscpy_pad(buf, __must_be_array(buf)+sizeof(buf), (tsk)->comm)
We have SIZEOF_ARRAY() for when you want the size of an array:
$ grepc -h SIZEOF_ARRAY .
#define SIZEOF_ARRAY(a) (sizeof(a) + must_be_array(a))
However, I don't think you want sizeof(). Let me explain why:
- Let's say you want to call wcsncpy(3) (I know nobody should be using
that function, not strncpy(3), but I'm using it as a standard example
of a wide-character string function).
You should call wcsncpy(dst, src, NITEMS(dst)).
A call wcsncpy(dst, src, sizeof(dst)) is bogus, since the argument is
the number of wide characters, not the number of bytes.
When translating that to normal characters, you want conceptually the
same operation, but on (normal) characters. That is, you want
strncpy(dst, src, NITEMS(dst)). While strncpy(3) with sizeof() works
just fine because sizeof(char)==1 by definition, it is conceptually
wrong to use it.
By using NITEMS() (i.e., ARRAY_SIZE()), you get the __must_be_array()
check for free.
In the end, SIZEOF_ARRAY() is something we very rarely use. It's there
only used in the following two cases at the moment:
#define NITEMS(a) (SIZEOF_ARRAY((a)) / sizeof((a)[0]))
#define MEMZERO(arr) memzero(arr, SIZEOF_ARRAY(arr))
Does that sound convincing?
For memcpy(3) for example, you do want sizeof(), because you're copying
raw bytes, but with strings, in which characters are conceptually
meaningful elements, NITEMS() makes more sense.
BTW, I'm working on a __lengthof__ operator that will soon allow using
it on function parameters declared with array notation. That is,
size_t
f(size_t n, int a[n])
{
return __lengthof__(a); // This will return n.
}
If you're interested in it, I'm developing and discussing it here:
<https://inbox.sourceware.org/gcc-patches/20240806122218.3827577-1-alx@kernel.org/>
>
> as a helper macro for this all.
>
> (Although I'm not convinced we generally want the "_pad()" version,
> but whatever).
We had problems with it in shadow recently. In user-space, it's similar
to strncpy(3) (at least if you wrap it in a macro that makes sure that
it terminates the string with a null byte).
We had a lot of uses of strncpy(3), from old times where that was used
to copy strings with truncation. I audited all of that code (and
haven't really finished yet), and translated to calls similar to
strscpy(9) (we call it strtcpy(), as it _t_runcates). The problem was
that in some cases the padding was necessary, and in others it was not,
and it was very hard to distinguish those.
I recommend not zeroing strings unnecessarily, since that will make it
hard to review the code later. E.g., twenty years from now, someone
takes a piece of code with a _pad() call, and has no clue if the zeroing
was for a reason, or for no reason.
On the other hand, not zeroing may make it easier to explot bugs, so
whatever you think best. In the kernel you may need to be more worried
than in user space. Whatever. :)
>
> Linus
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v5 0/9] Improve the copy of task comm
2024-08-06 17:28 Alejandro Colomar
@ 2024-08-08 2:49 ` Yafang Shao
2024-08-08 7:58 ` Alejandro Colomar
0 siblings, 1 reply; 17+ messages in thread
From: Yafang Shao @ 2024-08-08 2:49 UTC (permalink / raw)
To: Alejandro Colomar
Cc: torvalds, akpm, alexei.starovoitov, audit, bpf, catalin.marinas,
dri-devel, ebiederm, linux-fsdevel, linux-mm,
linux-security-module, linux-trace-kernel, netdev, penguin-kernel,
rostedt, selinux, serge
On Wed, Aug 7, 2024 at 1:28 AM Alejandro Colomar <alx@kernel.org> wrote:
>
> Hi Linus,
>
> Serge let me know about this thread earlier today.
>
> On 2024-08-05, Linus Torvalds <torvalds@linux-foundation.org> wrote:
> > On Mon, 5 Aug 2024 at 20:01, Yafang Shao <laoar.shao@gmail.com> wrote:
> > >
> > > One concern about removing the BUILD_BUG_ON() is that if we extend
> > > TASK_COMM_LEN to a larger size, such as 24, the caller with a
> > > hardcoded 16-byte buffer may overflow.
> >
> > No, not at all. Because get_task_comm() - and the replacements - would
> > never use TASK_COMM_LEN.
> >
> > They'd use the size of the *destination*. That's what the code already does:
> >
> > #define get_task_comm(buf, tsk) ({ \
> > ...
> > __get_task_comm(buf, sizeof(buf), tsk); \
> >
> > note how it uses "sizeof(buf)".
>
> In shadow.git, we also implemented macros that are named after functions
> and calculate the appropriate number of elements internally.
>
> $ grepc -h STRNCAT .
> #define STRNCAT(dst, src) strncat(dst, src, NITEMS(src))
> $ grepc -h STRNCPY .
> #define STRNCPY(dst, src) strncpy(dst, src, NITEMS(dst))
> $ grepc -h STRTCPY .
> #define STRTCPY(dst, src) strtcpy(dst, src, NITEMS(dst))
> $ grepc -h STRFTIME .
> #define STRFTIME(dst, fmt, tm) strftime(dst, NITEMS(dst), fmt, tm)
> $ grepc -h DAY_TO_STR .
> #define DAY_TO_STR(str, day, iso) day_to_str(NITEMS(str), str, day, iso)
>
> They're quite useful, and when implementing them we found and fixed
> several bugs thanks to them.
>
> > Now, it might be a good idea to also verify that 'buf' is an actual
> > array, and that this code doesn't do some silly "sizeof(ptr)" thing.
>
> I decided to use NITEMS() instead of sizeof() for that reason.
> (NITEMS() is just our name for ARRAY_SIZE().)
>
> $ grepc -h NITEMS .
> #define NITEMS(a) (SIZEOF_ARRAY((a)) / sizeof((a)[0]))
>
> > We do have a helper for that, so we could do something like
> >
> > #define get_task_comm(buf, tsk) \
> > strscpy_pad(buf, __must_be_array(buf)+sizeof(buf), (tsk)->comm)
>
> We have SIZEOF_ARRAY() for when you want the size of an array:
>
> $ grepc -h SIZEOF_ARRAY .
> #define SIZEOF_ARRAY(a) (sizeof(a) + must_be_array(a))
There is already a similar macro in Linux:
/**
* ARRAY_SIZE - get the number of elements in array @arr
* @arr: array to be sized
*/
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) +
__must_be_array(arr))
will use it instead of the sizeof().
>
> However, I don't think you want sizeof(). Let me explain why:
>
> - Let's say you want to call wcsncpy(3) (I know nobody should be using
> that function, not strncpy(3), but I'm using it as a standard example
> of a wide-character string function).
>
> You should call wcsncpy(dst, src, NITEMS(dst)).
> A call wcsncpy(dst, src, sizeof(dst)) is bogus, since the argument is
> the number of wide characters, not the number of bytes.
>
> When translating that to normal characters, you want conceptually the
> same operation, but on (normal) characters. That is, you want
> strncpy(dst, src, NITEMS(dst)). While strncpy(3) with sizeof() works
> just fine because sizeof(char)==1 by definition, it is conceptually
> wrong to use it.
>
> By using NITEMS() (i.e., ARRAY_SIZE()), you get the __must_be_array()
> check for free.
>
> In the end, SIZEOF_ARRAY() is something we very rarely use. It's there
> only used in the following two cases at the moment:
>
> #define NITEMS(a) (SIZEOF_ARRAY((a)) / sizeof((a)[0]))
> #define MEMZERO(arr) memzero(arr, SIZEOF_ARRAY(arr))
>
> Does that sound convincing?
>
> For memcpy(3) for example, you do want sizeof(), because you're copying
> raw bytes, but with strings, in which characters are conceptually
> meaningful elements, NITEMS() makes more sense.
>
> BTW, I'm working on a __lengthof__ operator that will soon allow using
> it on function parameters declared with array notation. That is,
>
> size_t
> f(size_t n, int a[n])
> {
> return __lengthof__(a); // This will return n.
> }
>
> If you're interested in it, I'm developing and discussing it here:
> <https://inbox.sourceware.org/gcc-patches/20240806122218.3827577-1-alx@kernel.org/>
>
> >
> > as a helper macro for this all.
> >
> > (Although I'm not convinced we generally want the "_pad()" version,
> > but whatever).
>
> We had problems with it in shadow recently. In user-space, it's similar
> to strncpy(3) (at least if you wrap it in a macro that makes sure that
> it terminates the string with a null byte).
>
> We had a lot of uses of strncpy(3), from old times where that was used
> to copy strings with truncation. I audited all of that code (and
> haven't really finished yet), and translated to calls similar to
> strscpy(9) (we call it strtcpy(), as it _t_runcates). The problem was
> that in some cases the padding was necessary, and in others it was not,
> and it was very hard to distinguish those.
>
> I recommend not zeroing strings unnecessarily, since that will make it
> hard to review the code later. E.g., twenty years from now, someone
> takes a piece of code with a _pad() call, and has no clue if the zeroing
> was for a reason, or for no reason.
>
> On the other hand, not zeroing may make it easier to explot bugs, so
> whatever you think best. In the kernel you may need to be more worried
> than in user space. Whatever. :)
Good point.
I will avoid using the _pad().
--
Regards
Yafang
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v5 0/9] Improve the copy of task comm
2024-08-08 2:49 ` Yafang Shao
@ 2024-08-08 7:58 ` Alejandro Colomar
0 siblings, 0 replies; 17+ messages in thread
From: Alejandro Colomar @ 2024-08-08 7:58 UTC (permalink / raw)
To: Yafang Shao
Cc: torvalds, akpm, alexei.starovoitov, audit, bpf, catalin.marinas,
dri-devel, ebiederm, linux-fsdevel, linux-mm,
linux-security-module, linux-trace-kernel, netdev, penguin-kernel,
rostedt, selinux, serge
[-- Attachment #1: Type: text/plain, Size: 1441 bytes --]
Hi Yafang,
On Thu, Aug 08, 2024 at 10:49:17AM GMT, Yafang Shao wrote:
> > > Now, it might be a good idea to also verify that 'buf' is an actual
> > > array, and that this code doesn't do some silly "sizeof(ptr)" thing.
> >
> > I decided to use NITEMS() instead of sizeof() for that reason.
> > (NITEMS() is just our name for ARRAY_SIZE().)
> >
> > $ grepc -h NITEMS .
> > #define NITEMS(a) (SIZEOF_ARRAY((a)) / sizeof((a)[0]))
> >
> > > We do have a helper for that, so we could do something like
> > >
> > > #define get_task_comm(buf, tsk) \
> > > strscpy_pad(buf, __must_be_array(buf)+sizeof(buf), (tsk)->comm)
> >
> > We have SIZEOF_ARRAY() for when you want the size of an array:
> >
> > $ grepc -h SIZEOF_ARRAY .
> > #define SIZEOF_ARRAY(a) (sizeof(a) + must_be_array(a))
>
> There is already a similar macro in Linux:
>
> /**
> * ARRAY_SIZE - get the number of elements in array @arr
> * @arr: array to be sized
> */
> #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) +
> __must_be_array(arr))
This is actually the same as our NITEMS(), not SIZEOF_ARRAY().
> will use it instead of the sizeof().
But yeah, indeed I think you should use ARRAY_SIZE() in
get_task_comm(). :)
>
> Good point.
> I will avoid using the _pad().
Nice. :)
Have a lovely day!
Alex
--
<https://www.alejandro-colomar.es/>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2024-08-08 7:58 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-08-04 7:56 [PATCH v5 0/9] Improve the copy of task comm Yafang Shao
2024-08-04 7:56 ` [PATCH v5 1/9] fs/exec: Drop task_lock() inside __get_task_comm() Yafang Shao
2024-08-04 7:56 ` [PATCH v5 2/9] auditsc: Replace memcpy() with __get_task_comm() Yafang Shao
2024-08-04 7:56 ` [PATCH v5 3/9] security: " Yafang Shao
2024-08-04 7:56 ` [PATCH v5 4/9] bpftool: Ensure task comm is always NUL-terminated Yafang Shao
2024-08-04 7:56 ` [PATCH v5 5/9] mm/util: Fix possible race condition in kstrdup() Yafang Shao
2024-08-04 7:56 ` [PATCH v5 6/9] mm/util: Deduplicate code in {kstrdup,kstrndup,kmemdup_nul} Yafang Shao
2024-08-04 7:56 ` [PATCH v5 7/9] tracing: Replace strncpy() with __get_task_comm() Yafang Shao
2024-08-04 7:56 ` [PATCH v5 8/9] net: Replace strcpy() " Yafang Shao
2024-08-04 7:56 ` [PATCH v5 9/9] drm: " Yafang Shao
2024-08-05 21:28 ` [PATCH v5 0/9] Improve the copy of task comm Linus Torvalds
2024-08-06 3:00 ` Yafang Shao
2024-08-06 3:09 ` Linus Torvalds
2024-08-06 3:50 ` Yafang Shao
-- strict thread matches above, loose matches on Subject: below --
2024-08-06 17:28 Alejandro Colomar
2024-08-08 2:49 ` Yafang Shao
2024-08-08 7:58 ` Alejandro Colomar
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).