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@xxxxxxxxxx> 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