[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <202411301244.381F2B8D17@keescook>
Date: Sat, 30 Nov 2024 13:05:18 -0800
From: Kees Cook <kees@...nel.org>
To: Linus Torvalds <torvalds@...ux-foundation.org>
Cc: Eric Biederman <ebiederm@...ssion.com>,
Alexander Viro <viro@...iv.linux.org.uk>,
Christian Brauner <brauner@...nel.org>, Jan Kara <jack@...e.cz>,
linux-mm@...ck.org, linux-fsdevel@...r.kernel.org,
Ingo Molnar <mingo@...hat.com>,
Peter Zijlstra <peterz@...radead.org>,
Juri Lelli <juri.lelli@...hat.com>,
Vincent Guittot <vincent.guittot@...aro.org>,
Dietmar Eggemann <dietmar.eggemann@....com>,
Steven Rostedt <rostedt@...dmis.org>,
Ben Segall <bsegall@...gle.com>, Mel Gorman <mgorman@...e.de>,
Valentin Schneider <vschneid@...hat.com>,
Jens Axboe <axboe@...nel.dk>,
Pavel Begunkov <asml.silence@...il.com>,
Andrew Morton <akpm@...ux-foundation.org>,
Chen Yu <yu.c.chen@...el.com>,
Shuah Khan <skhan@...uxfoundation.org>,
Mickaël Salaün <mic@...ikod.net>,
linux-kernel@...r.kernel.org, io-uring@...r.kernel.org,
linux-hardening@...r.kernel.org
Subject: Re: [PATCH] exec: Make sure task->comm is always NUL-terminated
On Fri, Nov 29, 2024 at 11:15:44PM -0800, Linus Torvalds wrote:
> Edited down to just the end result:
>
> On Fri, 29 Nov 2024 at 20:49, Kees Cook <kees@...nel.org> wrote:
> >
> > void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec)
> > {
> > size_t len = min(strlen(buf), sizeof(tsk->comm) - 1);
> >
> > trace_task_rename(tsk, buf);
> > memcpy(tsk->comm, buf, len);
> > memset(&tsk->comm[len], 0, sizeof(tsk->comm) - len);
> > perf_event_comm(tsk, exec);
> > }
>
> I actually don't think that's super-safe either. Yeah, it works in
> practice, and the last byte is certainly always going to be 0, but it
> might not be reliably padded.
Right, my concern over comm is strictly about unterminated reads (i.e.
exposing memory contents stored after "comm" in the task_struct). I've not
been worried about "uninitialized content" exposure because the starting
contents have always been wiped and will (now) always end with a NUL,
so the worst exposure is seeing prior or racing bytes of whatever is
being written into comm concurrently.
> Why? It walks over the source twice. First at strlen() time, then at
> memcpy. So if the source isn't stable, the end result might have odd
> results with NUL characters in the middle.
Yeah, this just means it has greater potential to be garbled.
> And strscpy() really was *supposed* to be safe even in this case, and
> I thought it was until I looked closer.
>
> But I think strscpy() can be saved.
Yeah, fixing the final NUL byte write is needed.
> Something (UNTESTED!) like the attached I think does the right thing.
> I added a couple of "READ_ONCE()" things to make it really super-clear
> that strscpy() reads the source exactly once, and to not allow any
> compiler re-materialization of the reads (although I think that when I
> asked people, it turns out neither gcc nor clang rematerialize memory
> accesses, so that READ_ONCE is likely more a documentation ad
> theoretical thing than a real thing).
This is fine, but it doesn't solve either an unstable source nor
concurrent writers to dest. If source changes out from under strscpy,
we can still copy a "torn" write. If destination changes out from under
strscpy, we just get a potentially interleaved output (but with the
NUL-write change, we never have a dest that _lacks_ a NUL terminator).
So yeah, let's change the loop as you have it. I'm fine with the
READ_ONCE() additions, but I'm not clear on what benefit it has.
> Hmm? I don't think your version is wrong, but I also think we'd be
> better off making our 'strscpy()' infrastructure explicitly safe wrt
> unstable source strings.
Agreed. I'll get this tested against our string handling selftests...
--
Kees Cook
Powered by blists - more mailing lists