Message ID | 20221214200303.4169289-1-hjl.tools@gmail.com |
---|---|
State | New |
Headers | show |
Series | [v3] x86: Add a testcase for BZ #29863 | expand |
On Wed, Dec 14, 2022 at 12:03 PM H.J. Lu <hjl.tools@gmail.com> wrote: > > When a thread is updating the memory area of memcmp and another thread > is doing memcmp, the memcmp return value is undefined. Add a testcase > to verify that memcmp won't segfault in this case. > --- > sysdeps/x86/Makefile | 8 +++ > sysdeps/x86/tst-memcmp-race.c | 118 ++++++++++++++++++++++++++++++++++ > 2 files changed, 126 insertions(+) > create mode 100644 sysdeps/x86/tst-memcmp-race.c > > diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile > index 56fd5fc805..c3f05a06ab 100644 > --- a/sysdeps/x86/Makefile > +++ b/sysdeps/x86/Makefile > @@ -257,3 +257,11 @@ tests += \ > tests-static += \ > tst-sysconf-cache-linesize-static > endif > + > +ifeq ($(subdir),nptl) > +tests += \ > + tst-memcmp-race \ > +# tests > + > +CFLAGS-tst-memcmp-race.c += -O0 > +endif > diff --git a/sysdeps/x86/tst-memcmp-race.c b/sysdeps/x86/tst-memcmp-race.c > new file mode 100644 > index 0000000000..f28b80ef82 > --- /dev/null > +++ b/sysdeps/x86/tst-memcmp-race.c > @@ -0,0 +1,118 @@ > +/* Test case for memcmp with race condition. > + Copyright (C) 2022 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 > + <https://www.gnu.org/licenses/>. */ > + > +/* Verify that there is no segfault when one thread is updating the > + memory block of memcmp and the other thread is doing memcmp. */ > + > +#define TEST_MAIN > +#define TEST_NAME "memcmp" > + > +#include <stdio.h> > +#include <stdint.h> > +#include <string.h> > +#include <string/test-string.h> > +#include <support/xthread.h> > + > +#define NUM_THREADS 2 > +#define STR "abcdefghijklmnopq" not needed. > +#define LOOP1 100 The test is relatively fast, this value can probably be increased a bit. > +#define LOOP2 1000000 > + > +typedef int (*proto_t) (const void *, const void *, size_t); > + > +IMPL (memcmp, 1) > + > +struct arg > +{ > + proto_t func; > + size_t len; > + int i; > +}; > + > +static void * > +childThread (void *tArgs) > +{ > + struct arg *a = (struct arg *) tArgs; > + int i; > + > + if (0 == a->i % 2) > + { > + for (i = 0; i < LOOP1; i++) > + { > + int result = a->func (buf1, buf2, a->len); > + printf ("i = %d : result = %d\n", i, result); remove print, make `result` volatile. > + } > + } > + else > + { > + size_t offset = a->len > 16 ? a->len - 16 : 1; > + for (i = 0; i < LOOP2; i++) > + buf1[offset] = (0 == (i % 2)) ? 'b' : 'c'; buf1[offset] = (i & 1); > + } > + > + return NULL; > +} > + > +static void > +do_one_test (proto_t func, size_t len) > +{ > + int i; > + pthread_t threads[NUM_THREADS]; > + struct arg a[NUM_THREADS]; > + > + for (i = 0; i < NUM_THREADS; ++i) > + { > + a[i].func = func; > + a[i].len = len; > + a[i].i = i; > + threads[i] = xpthread_create (NULL, childThread, (void *)&a[i]); > + } > + > + for (i = 0; i < NUM_THREADS; ++i) > + xpthread_join (threads[i]); > +} > + > +int > +test_main (void) > +{ > + test_init (); > + > + size_t remain = page_size; > + size_t offset = 0; > + do > + { > + size_t len = sizeof STR - 1; > + if (len > remain) > + len = remain; > + memcpy (buf1 + offset, STR, len); > + memcpy (buf2 + offset, STR, len); > + offset += len; > + remain -= len; > + } > + while (remain > 0); remove. memset(buf1, 1, page_size); memset(buf2, 1, page_size); > + > + for (size_t i = 0; i <= 11; i++) > + { > + FOR_EACH_IMPL (impl, 0) > + do_one_test ((proto_t) impl->fn, (1 << i) + 1); > + } > + > + return 0; > +} > + > +#include <support/test-driver.c> > -- > 2.38.1 >
diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile index 56fd5fc805..c3f05a06ab 100644 --- a/sysdeps/x86/Makefile +++ b/sysdeps/x86/Makefile @@ -257,3 +257,11 @@ tests += \ tests-static += \ tst-sysconf-cache-linesize-static endif + +ifeq ($(subdir),nptl) +tests += \ + tst-memcmp-race \ +# tests + +CFLAGS-tst-memcmp-race.c += -O0 +endif diff --git a/sysdeps/x86/tst-memcmp-race.c b/sysdeps/x86/tst-memcmp-race.c new file mode 100644 index 0000000000..f28b80ef82 --- /dev/null +++ b/sysdeps/x86/tst-memcmp-race.c @@ -0,0 +1,118 @@ +/* Test case for memcmp with race condition. + Copyright (C) 2022 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 + <https://www.gnu.org/licenses/>. */ + +/* Verify that there is no segfault when one thread is updating the + memory block of memcmp and the other thread is doing memcmp. */ + +#define TEST_MAIN +#define TEST_NAME "memcmp" + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <string/test-string.h> +#include <support/xthread.h> + +#define NUM_THREADS 2 +#define STR "abcdefghijklmnopq" +#define LOOP1 100 +#define LOOP2 1000000 + +typedef int (*proto_t) (const void *, const void *, size_t); + +IMPL (memcmp, 1) + +struct arg +{ + proto_t func; + size_t len; + int i; +}; + +static void * +childThread (void *tArgs) +{ + struct arg *a = (struct arg *) tArgs; + int i; + + if (0 == a->i % 2) + { + for (i = 0; i < LOOP1; i++) + { + int result = a->func (buf1, buf2, a->len); + printf ("i = %d : result = %d\n", i, result); + } + } + else + { + size_t offset = a->len > 16 ? a->len - 16 : 1; + for (i = 0; i < LOOP2; i++) + buf1[offset] = (0 == (i % 2)) ? 'b' : 'c'; + } + + return NULL; +} + +static void +do_one_test (proto_t func, size_t len) +{ + int i; + pthread_t threads[NUM_THREADS]; + struct arg a[NUM_THREADS]; + + for (i = 0; i < NUM_THREADS; ++i) + { + a[i].func = func; + a[i].len = len; + a[i].i = i; + threads[i] = xpthread_create (NULL, childThread, (void *)&a[i]); + } + + for (i = 0; i < NUM_THREADS; ++i) + xpthread_join (threads[i]); +} + +int +test_main (void) +{ + test_init (); + + size_t remain = page_size; + size_t offset = 0; + do + { + size_t len = sizeof STR - 1; + if (len > remain) + len = remain; + memcpy (buf1 + offset, STR, len); + memcpy (buf2 + offset, STR, len); + offset += len; + remain -= len; + } + while (remain > 0); + + for (size_t i = 0; i <= 11; i++) + { + FOR_EACH_IMPL (impl, 0) + do_one_test ((proto_t) impl->fn, (1 << i) + 1); + } + + return 0; +} + +#include <support/test-driver.c>