x86-64 memcmp: Use unsigned Jcc instructions on size [BZ #24155]

Since the size argument is unsigned. we should use unsigned Jcc
instructions, instead of signed, to check size.

Tested on x86-64 and x32, with and without --disable-multi-arch.

	[BZ #24155]
	CVE-2019-7309
	* NEWS: Updated for CVE-2019-7309.
	* sysdeps/x86_64/memcmp.S: Use RDX_LP for size.  Clear the
	upper 32 bits of RDX register for x32.  Use unsigned Jcc
	instructions, instead of signed.
	* sysdeps/x86_64/x32/Makefile (tests): Add tst-size_t-memcmp-2.
	* sysdeps/x86_64/x32/tst-size_t-memcmp-2.c: New test.
This commit is contained in:
H.J. Lu 2019-02-04 06:31:01 -08:00
parent 2ab5741b8a
commit 3f635fb433
5 changed files with 111 additions and 10 deletions

View File

@ -1,3 +1,14 @@
2019-02-04 H.J. Lu <hongjiu.lu@intel.com>
[BZ #24155]
CVE-2019-7309
* NEWS: Updated for CVE-2019-7309.
* sysdeps/x86_64/memcmp.S: Use RDX_LP for size. Clear the
upper 32 bits of RDX register for x32. Use unsigned Jcc
instructions, instead of signed.
* sysdeps/x86_64/x32/Makefile (tests): Add tst-size_t-memcmp-2.
* sysdeps/x86_64/x32/tst-size_t-memcmp-2.c: New test.
2019-02-04 Florian Weimer <fweimer@redhat.com>
* posix/spawn.h (posix_spawn, posix_spawnp): Add __nonnull attribute.

8
NEWS
View File

@ -25,7 +25,13 @@ Changes to build and runtime requirements:
Security related changes:
[Add security related changes here]
CVE-2019-7309: x86-64 memcmp used signed Jcc instructions to check
size. For x86-64, memcmp on an object size larger than SSIZE_MAX
has undefined behavior. On x32, the size_t argument may be passed
in the lower 32 bits of the 64-bit RDX register with non-zero upper
32 bits. When it happened with the sign bit of RDX register set,
memcmp gave the wrong result since it treated the size argument as
zero. Reported by H.J. Lu.
The following bugs are resolved with this release:

View File

@ -21,14 +21,18 @@
.text
ENTRY (memcmp)
test %rdx, %rdx
#ifdef __ILP32__
/* Clear the upper 32 bits. */
movl %edx, %edx
#endif
test %RDX_LP, %RDX_LP
jz L(finz)
cmpq $1, %rdx
jle L(finr1b)
jbe L(finr1b)
subq %rdi, %rsi
movq %rdx, %r10
cmpq $32, %r10
jge L(gt32)
jae L(gt32)
/* Handle small chunks and last block of less than 32 bytes. */
L(small):
testq $1, %r10
@ -156,7 +160,7 @@ L(A32):
movq %r11, %r10
andq $-32, %r10
cmpq %r10, %rdi
jge L(mt16)
jae L(mt16)
/* Pre-unroll to be ready for unrolled 64B loop. */
testq $32, %rdi
jz L(A64)
@ -178,7 +182,7 @@ L(A64):
movq %r11, %r10
andq $-64, %r10
cmpq %r10, %rdi
jge L(mt32)
jae L(mt32)
L(A64main):
movdqu (%rdi,%rsi), %xmm0
@ -216,7 +220,7 @@ L(mt32):
movq %r11, %r10
andq $-32, %r10
cmpq %r10, %rdi
jge L(mt16)
jae L(mt16)
L(A32main):
movdqu (%rdi,%rsi), %xmm0
@ -254,7 +258,7 @@ L(ATR):
movq %r11, %r10
andq $-32, %r10
cmpq %r10, %rdi
jge L(mt16)
jae L(mt16)
testq $16, %rdi
jz L(ATR32)
@ -325,7 +329,7 @@ L(ATR64main):
movq %r11, %r10
andq $-32, %r10
cmpq %r10, %rdi
jge L(mt16)
jae L(mt16)
L(ATR32res):
movdqa (%rdi,%rsi), %xmm0

View File

@ -8,7 +8,8 @@ endif
ifeq ($(subdir),string)
tests += tst-size_t-memchr tst-size_t-memcmp tst-size_t-memcpy \
tst-size_t-memrchr tst-size_t-memset tst-size_t-strncasecmp \
tst-size_t-strncmp tst-size_t-strncpy tst-size_t-strnlen
tst-size_t-strncmp tst-size_t-strncpy tst-size_t-strnlen \
tst-size_t-memcmp-2
endif
ifeq ($(subdir),wcsmbs)

View File

@ -0,0 +1,79 @@
/* Test memcmp with size_t in the lower 32 bits of 64-bit register.
Copyright (C) 2019 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#define TEST_MAIN
#ifdef WIDE
# define TEST_NAME "wmemcmp"
#else
# define TEST_NAME "memcmp"
#endif
#include "test-size_t.h"
#ifdef WIDE
# include <inttypes.h>
# include <wchar.h>
# define MEMCMP wmemcmp
# define CHAR wchar_t
#else
# define MEMCMP memcmp
# define CHAR char
#endif
IMPL (MEMCMP, 1)
typedef int (*proto_t) (const CHAR *, const CHAR *, size_t);
static int
__attribute__ ((noinline, noclone))
do_memcmp (parameter_t a, parameter_t b)
{
return CALL (&b, a.p, b.p, a.len);
}
static int
test_main (void)
{
test_init ();
parameter_t dest = { { page_size / sizeof (CHAR) }, buf1 };
parameter_t src = { { 0 }, buf2 };
memcpy (buf1, buf2, page_size);
CHAR *p = (CHAR *) buf1;
p[page_size / sizeof (CHAR) - 1] = (CHAR) 1;
int ret = 0;
FOR_EACH_IMPL (impl, 0)
{
src.fn = impl->fn;
int res = do_memcmp (dest, src);
if (res >= 0)
{
error (0, 0, "Wrong result in function %s: %i >= 0",
impl->name, res);
ret = 1;
}
}
return ret ? EXIT_FAILURE : EXIT_SUCCESS;
}
#include <support/test-driver.c>