exec: Make sure task->comm is always NUL-terminated

Using strscpy() meant that the final character in task->comm may be
non-NUL for a moment before the "string too long" truncation happens.

Instead of adding a new use of the ambiguous strncpy(), we'd want to
use memtostr_pad() which enforces being able to check at compile time
that sizes are sensible, but this requires being able to see string
buffer lengths. Instead of trying to inline __set_task_comm() (which
needs to call trace and perf functions), just open-code it. But to
make sure we're always safe, add compile-time checking like we already
do for get_task_comm().

Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Suggested-by: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: Kees Cook <kees@kernel.org>
This commit is contained in:
Kees Cook 2024-11-29 20:06:55 -08:00
parent fa1bdca98d
commit 3a3f61ce5e
5 changed files with 14 additions and 14 deletions

View File

@ -1200,16 +1200,16 @@ char *__get_task_comm(char *buf, size_t buf_size, struct task_struct *tsk)
EXPORT_SYMBOL_GPL(__get_task_comm); EXPORT_SYMBOL_GPL(__get_task_comm);
/* /*
* These functions flushes out all traces of the currently running executable * This is unlocked -- the string will always be NUL-terminated, but
* so that a new one can be started * may show overlapping contents if racing concurrent reads.
*/ */
void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec) void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec)
{ {
task_lock(tsk); size_t len = min(strlen(buf), sizeof(tsk->comm) - 1);
trace_task_rename(tsk, buf); trace_task_rename(tsk, buf);
strscpy_pad(tsk->comm, buf, sizeof(tsk->comm)); memcpy(tsk->comm, buf, len);
task_unlock(tsk); memset(&tsk->comm[len], 0, sizeof(tsk->comm) - len);
perf_event_comm(tsk, exec); perf_event_comm(tsk, exec);
} }

View File

@ -1932,11 +1932,10 @@ static inline void kick_process(struct task_struct *tsk) { }
#endif #endif
extern void __set_task_comm(struct task_struct *tsk, const char *from, bool exec); extern void __set_task_comm(struct task_struct *tsk, const char *from, bool exec);
#define set_task_comm(tsk, from) ({ \
static inline void set_task_comm(struct task_struct *tsk, const char *from) BUILD_BUG_ON(sizeof(from) != TASK_COMM_LEN); \
{ __set_task_comm(tsk, from, false); \
__set_task_comm(tsk, from, false); })
}
extern char *__get_task_comm(char *to, size_t len, struct task_struct *tsk); extern char *__get_task_comm(char *to, size_t len, struct task_struct *tsk);
#define get_task_comm(buf, tsk) ({ \ #define get_task_comm(buf, tsk) ({ \

View File

@ -634,7 +634,7 @@ static int io_wq_worker(void *data)
struct io_wq_acct *acct = io_wq_get_acct(worker); struct io_wq_acct *acct = io_wq_get_acct(worker);
struct io_wq *wq = worker->wq; struct io_wq *wq = worker->wq;
bool exit_mask = false, last_timeout = false; bool exit_mask = false, last_timeout = false;
char buf[TASK_COMM_LEN]; char buf[TASK_COMM_LEN] = {};
set_mask_bits(&worker->flags, 0, set_mask_bits(&worker->flags, 0,
BIT(IO_WORKER_F_UP) | BIT(IO_WORKER_F_RUNNING)); BIT(IO_WORKER_F_UP) | BIT(IO_WORKER_F_RUNNING));

View File

@ -271,7 +271,7 @@ static int io_sq_thread(void *data)
struct io_ring_ctx *ctx; struct io_ring_ctx *ctx;
struct rusage start; struct rusage start;
unsigned long timeout = 0; unsigned long timeout = 0;
char buf[TASK_COMM_LEN]; char buf[TASK_COMM_LEN] = {};
DEFINE_WAIT(wait); DEFINE_WAIT(wait);
/* offload context creation failed, just exit */ /* offload context creation failed, just exit */

View File

@ -736,10 +736,11 @@ EXPORT_SYMBOL(kthread_stop_put);
int kthreadd(void *unused) int kthreadd(void *unused)
{ {
static const char comm[TASK_COMM_LEN] = "kthreadd";
struct task_struct *tsk = current; struct task_struct *tsk = current;
/* Setup a clean context for our children to inherit. */ /* Setup a clean context for our children to inherit. */
set_task_comm(tsk, "kthreadd"); set_task_comm(tsk, comm);
ignore_signals(tsk); ignore_signals(tsk);
set_cpus_allowed_ptr(tsk, housekeeping_cpumask(HK_TYPE_KTHREAD)); set_cpus_allowed_ptr(tsk, housekeeping_cpumask(HK_TYPE_KTHREAD));
set_mems_allowed(node_states[N_MEMORY]); set_mems_allowed(node_states[N_MEMORY]);