Re: FYI: An example type of UBSAN failure during kyua test -k /usr/tests/Kyuafile

From: Mark Millard <marklmi_at_yahoo.com>
Date: Fri, 07 Jan 2022 22:28:47 UTC

On 2022-Jan-7, at 04:31, Stefan Esser <se@freebsd.org> wrote:

> Am 07.01.22 um 12:49 schrieb Mark Millard:
>> Having done a buildworld with both WITH_ASAN= and WITH_UBSAN=
>> after finding what to control to allow the build, I installed
>> it in a directory tree for chroot use and have
>> "kyua test -k /usr/tests/Kyuafile" running.
>> 
>> I see evidence of various examples of one type of undefined
>> behavior: "applying zero offset to null pointer"
>> 
>> # more /usr/obj/DESTDIRs/main-amd64-xSAN-chroot/tmp/kyua.FKD2vh/356/stderr.txt 
>> /usr/main-src/lib/libc/stdio/fread.c:133:10: runtime error: applying zero offset to null pointer
>> SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/main-src/lib/libc/stdio/fread.c:133:10 in 
>> /usr/main-src/lib/libc/stdio/fread.c:133:10: runtime error: applying zero offset to null pointer
>> SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/main-src/lib/libc/stdio/fread.c:133:10 in 
>> /usr/main-src/usr.bin/sed/process.c:715:18: runtime error: applying zero offset to null pointer
>> SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/main-src/usr.bin/sed/process.c:715:18 in 
>> /usr/main-src/lib/libc/stdio/fread.c:133:10: runtime error: applying zero offset to null pointer
>> SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/main-src/lib/libc/stdio/fread.c:133:10 in 
>> Fail: stderr not empty
>> --- /dev/null   2022-01-07 10:29:57.182903000 +0000
>> +++ /tmp/kyua.FKD2vh/356/work/check.Mk9llD/stderr       2022-01-07 10:29:57.173100000 +0000
>> @@ -0,0 +1,2 @@
>> +/usr/main-src/lib/libc/stdio/fread.c:133:10: runtime error: applying zero offset to null pointer
>> +SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/main-src/lib/libc/stdio/fread.c:133:10 in 
>> Files left in work directory after failure: mntpt, mounterr
>> 
>> 
>> In general the lib/libc/stdio/fread.c:133:10 example seems to
>> be in a place that would make it fairly common.
> 
> Interesting find:
> 
>        while (resid > (r = fp->_r)) {
>                (void)memcpy((void *)p, (void *)fp->_p, (size_t)r);
>                fp->_p += r; /* line 133 */
>                /* fp->_r = 0 ... done in __srefill */
>                p += r;
>                resid -= r;
> 
> If fp->_p == NULL in line 133, then NULL has been passed as source address
> in memcpy() in the line above, and I'd think that is undefined behavior,
> even if a length of 0 is passed at the same time.

My copy of ISO/IEC 9899:2011 (E) only explicitly mentions such a
limitation for the memcpy_s variant.  It does say "[t]he memcpy
function returns the value of s1". The only mentioned "behavior
is undefined" is for copying between objects that overlap.

But there is more general wording in 7.24.1 (of 7.24 String
handling <strings.h>):

QUOTE
Where an argument declared as size_t n specifies the length
of the array for a function, n can have the value zero on a
call to that function. Unless explicitly stated otherwise in
the description of a particular function in this subclause,
pointer arguments on such a call still shall have valid values,
as described in 7.1.4. On such a call, . . . a function that
copies characters copies zero characters.
END QUOTE

But I've not noticed anything in 7.1.4 is that explicit about
NULL arguments with zero sizes or that bans NULL arguments
in any generality.

In other words, I believe that the lack of a report for memcpy's
argument values is consistent with what ISO/IEC 9899:2011
is explicit about for such things.

I've not tried going through POSIX material or any other
potential standards.

> Maybe the code block quoted above (line 132 to 136) should be made wrapped
> into "if (r > 0) {}"?
> 


===
Mark Millard
marklmi at yahoo.com