git: 977ed30681c3 - stable/13 - fread.c: fix undefined behavior

From: Stefan Eßer <se_at_FreeBSD.org>
Date: Fri, 04 Mar 2022 19:48:18 UTC
The branch stable/13 has been updated by se:

URL: https://cgit.FreeBSD.org/src/commit/?id=977ed30681c346a0b6be76a2e03b6651b94b58aa

commit 977ed30681c346a0b6be76a2e03b6651b94b58aa
Author:     Stefan Eßer <se@FreeBSD.org>
AuthorDate: 2022-01-15 23:30:04 +0000
Commit:     Stefan Eßer <se@FreeBSD.org>
CommitDate: 2022-03-04 19:47:23 +0000

    fread.c: fix undefined behavior
    
    A case of undefined behavior in __fread() has been detected by UBSAN
    and reported by Mark Millard:
    
    /usr/main-src/lib/libc/stdio/fread.c:133:10: runtime error: applying
    zero offset to null pointer
    SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior in
    /usr/main-src/lib/libc/stdio/fread.c:133:10
    
    While being benign (the NULL pointer is later passed to memcpy() with
    a length argument of 0), this issue causes in the order of 600 Kyua
    test cases to fail on systems running a world built with WITH_UBSAN
    and WITH_ASAN.
    
    The undefined behavior can be prevented by skipping operations that
    have no effect for r == 0. Mark Millard has suggested to only skip
    this code segment if fp->_p == NULL, but I have verified that for the
    case of r == 0 no further argument checking is performed on the
    addresses passed to memcpy() and thus no bugs are hidden from the
    sanitizers due to the simpler condition chosen.
    
    (cherry picked from commit 10af8e45a89818754b80315539e167ae49599f17)
---
 lib/libc/stdio/fread.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/lib/libc/stdio/fread.c b/lib/libc/stdio/fread.c
index 11f8d13f0caf..cafe86fe7961 100644
--- a/lib/libc/stdio/fread.c
+++ b/lib/libc/stdio/fread.c
@@ -129,11 +129,13 @@ __fread(void * __restrict buf, size_t size, size_t count, FILE * __restrict fp)
 	}
 
 	while (resid > (r = fp->_r)) {
-		(void)memcpy((void *)p, (void *)fp->_p, (size_t)r);
-		fp->_p += r;
-		/* fp->_r = 0 ... done in __srefill */
-		p += r;
-		resid -= r;
+		if (r != 0) {
+			(void)memcpy((void *)p, (void *)fp->_p, (size_t)r);
+			fp->_p += r;
+			/* fp->_r = 0 ... done in __srefill */
+			p += r;
+			resid -= r;
+		}
 		if (__srefill(fp)) {
 			/* no more input: return partial result */
 			return ((total - resid) / size);