mirror of
https://sourceware.org/git/glibc.git
synced 2026-10-28 10:25:13 +08:00
powerpc: Fix one byte overread in strncasecmp (bug 34683, CVE-2026-97399)
The Power8 strncasecmp (strcasecmp.S with USE_AS_STRNCASECMP) touches s1[n]/s2[n] in three places when the first n bytes compare equal and contain no NUL: L(loop) preloads the next byte before its bdnz; L(loop1) increments then loads before its bdnz; and L(bytebybyte) loads s1[0]/s2[0] before checking r5. A caller that maps exactly n readable bytes can get SIGSEGV if byte n falls on an unmapped page. The L(bytebybyte) site was identified by Florian Weimer; the original AISLE report covered only the two loops. Fix all three sites: guard L(bytebybyte) with a r5 == 0 check, split the bdnz/load pairs in L(loop) and L(loop1) so the load only runs when the count is still positive, and preload rCHAR1/rCHAR2 in L(lessthan4) after confirming r5 != 0. strcasecmp is unaffected; its generated code is unchanged. Add string/test-strncasecmp-nonarray.c, built on the test-Xncmp-nonarray.c skeleton: maps a PROT_NONE page past both buffers and sweeps all start offsets and lengths. Runs as an xtest. Reported-by: AISLE in partnership with Red Hat Suggested-by: Florian Weimer <fweimer@redhat.com>
This commit is contained in:
1 parent
30f988fbe7
commit
e622f82ea8
3 files changed
+26
-7
No files matched your search
+2
-2
@@ -250,8 +250,8 @@ endif
|
||||
# This test allocates a lot of memory and can run for a long time.
|
||||
xtests += tst-strcoll-overflow
|
||||
|
||||
# This test runs for a long time.
|
||||
xtests += test-strncmp-nonarray
|
||||
# These tests run for a long time.
|
||||
xtests += test-strncasecmp-nonarray test-strncmp-nonarray
|
||||
|
||||
# This test needs libdl.
|
||||
ifeq (yes,$(build-shared))
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
#define TEST_IDENTIFIER strncasecmp
|
||||
#define TEST_NAME "strncasecmp"
|
||||
typedef char CHAR;
|
||||
#include "test-Xncmp-nonarray.c"
|
||||
@@ -355,6 +355,14 @@ L(bytebybyte):
|
||||
offset and string descriptors are only updated in the end
|
||||
of loop unrolling. */
|
||||
ld rLOC, LOCALE_CTYPE_TOLOWER(rLOC)
|
||||
#ifdef USE_AS_STRNCASECMP
|
||||
/* This entry point can be reached from the vector loop with
|
||||
r5 == 0 (the last vector chunk ended exactly at the `n'
|
||||
boundary). Do not read s1[0]/s2[0] in that case: those
|
||||
bytes are s1[n]/s2[n], one past the caller's bound. */
|
||||
cmpdi cr7, r5, 0
|
||||
beq cr7, L(retnull)
|
||||
#endif
|
||||
lbz rCHAR1, 0(rSTR1) /* Load char from s1 */
|
||||
lbz rCHAR2, 0(rSTR2) /* Load char from s2 */
|
||||
#ifdef USE_AS_STRNCASECMP
|
||||
@@ -405,19 +413,23 @@ L(loop):
|
||||
cmpw cr1, rLWR1, rLWR2
|
||||
crorc 4*cr1+eq,eq,4*cr1+eq
|
||||
beq cr1, L(done)
|
||||
#ifdef USE_AS_STRNCASECMP
|
||||
/* Test the remaining count before touching the next byte. */
|
||||
bdnz L(loop_load)
|
||||
b L(lessthan4)
|
||||
L(loop_load):
|
||||
#endif
|
||||
lbz rCHAR1, 0(rSTR1) /* Load char from s1 */
|
||||
lbz rCHAR2, 0(rSTR2) /* Load char from s2 */
|
||||
#ifdef USE_AS_STRNCASECMP
|
||||
bdnz L(loop)
|
||||
#else
|
||||
b L(loop)
|
||||
#endif
|
||||
#ifdef USE_AS_STRNCASECMP
|
||||
L(lessthan4):
|
||||
clrldi r5, r5, 62
|
||||
cmpdi cr7, r5, 0
|
||||
beq cr7, L(retnull)
|
||||
mtctr r5
|
||||
lbz rCHAR1, 0(rSTR1)
|
||||
lbz rCHAR2, 0(rSTR2)
|
||||
L(loop1):
|
||||
cmpdi rCHAR1, 0
|
||||
sldi rADDR1, rCHAR1, 2
|
||||
@@ -427,11 +439,14 @@ L(loop1):
|
||||
cmpw cr1, rLWR1, rLWR2
|
||||
crorc 4*cr1+eq,eq,4*cr1+eq
|
||||
beq cr1, L(done)
|
||||
bdnz L(loop1_load)
|
||||
b L(retnull)
|
||||
L(loop1_load):
|
||||
addi rSTR1, rSTR1, 1
|
||||
addi rSTR2, rSTR2, 1
|
||||
lbz rCHAR1, 0(rSTR1)
|
||||
lbz rCHAR2, 0(rSTR2)
|
||||
bdnz L(loop1)
|
||||
b L(loop1)
|
||||
#endif
|
||||
L(done):
|
||||
subf r0, rLWR2, rLWR1
|
||||
|
||||
Reference in new issue
Block a user