Re: [syzbot] [ext4?] WARNING in __fortify_report

From: Theodore Ts'o
Date: Sat May 25 2024 - 00:11:48 EST


On Thu, May 23, 2024 at 03:48:01PM -0700, Kees Cook wrote:
> It looks like this is another case of a non-terminated string being made
> terminated by strncpy into a string with 1 extra byte at the end:
>
> char label[EXT4_LABEL_MAX + 1];
> ...
> - memset(label, 0, sizeof(label));
> lock_buffer(sbi->s_sbh);
> - strncpy(label, sbi->s_es->s_volume_name, EXT4_LABEL_MAX);
> + strscpy_pad(label, sbi->s_es->s_volume_name);
> unlock_buffer(sbi->s_sbh);
>
> This should be using memtostr_pad() as:
>
> memtostr_pad(label, sbi->s_es->s_volume_name);
>

Ah... I see what is going on. The two argument variants of
memtostr_pad() and strscpy_pad() are confusing and dangerous. These
don't exist in the original OpenBSD strscpy() function, because the
size in the third argument is explicit, while with strscpy_pad(), the
automagic size is intuited from the first argument (the destination),
while with memtostr_pad(), the size is automagically intuited from the
second argument (the source).

This confused me, and I couldn't figure out the bug even when I was
given the stack trace from syzkaller. So it's an accident waiting to
happen, I clearly I was not smart enough not to fall into the trap,

So perhaps it might be nice if the descriptions of strscpy() is moved
out of process/deprecated.rst (and BTW, memstrtopad isn't mentioned at
all), and moved inta separate doumentation which safe string handling
in C --- so instead of talking about what functions *shouldn't* used,
such as strncpy(), it talks about how the various functions *should*
be used instead.

I'll also note that figuring out what was going on from looking at
include/linu/string.h was confusing, because there is so much #define
magic to provide the magic multiple argument handling. Personally, I
was never a fan of C++'s function overloading where different function
signatures could result in different implementations being called, and
doing with C preprocessor magic makes it even worse. To be fair,
there is the kernel-doc inline documentation, but my eyes were drawn
to the C++ implementation, and the kernel-doc documentation is more of
a reference and not a tutorial style "this is how you should do
things".

Anyway, thanks for sending the patch. I spent a good 30 minutes
trying to figure out the bug, and was half-tempted to just revert the
patch and go back to strncpy(), which at least I could obviously see
was correct, unlike the strscpy_pad() transmogrification.

> It looks like __nonstring markings from commit 072ebb3bffe6 ("ext4:
> add nonstring annotations to ext4.h") were incomplete.

Yes, I'll patch ext4.h to add a __nonstring annotation to
s_volume_name. As I recall, the reason why we had added the
__nonstring markings was to shut up gcc's -Wstringpop-truncation
warnings, and apparently it was needed for s_volume_name, which is why
it was never annotated.

Out of curiosity, though, would this have caused some analysis tool to
trigger a warning when the strscpy_pad() commit was added? I've
tried, and it doesn't seem to have triggered any kind of warning with
either gcc, clang, or sparse.

Anyway, since I'm an old fart, it was pretty obvious to *me* that the
how strcnpy() was used was obviouly correct, whereas I actually have
to do more careful thinking and analysis with strscpy() and
memtostr(). So it would be nice if there were some automated tools
that warn if those new functions aren't used correctly, because this
bug shows that these functions are definitely not fool proof --- both
by the original patch submitter, and the fool who reviewed the patch
and missed the problem. :-)

- Ted