glibc/nptl/tst-audit-threads.c
Tulio Magno Quites Machado Filho d8ca7a0c71 Fix _dl_profile_fixup data-dependency issue (Bug 23690)
There is a data-dependency between the fields of struct l_reloc_result
and the field used as the initialization guard. Users of the guard
expect writes to the structure to be observable when they also observe
the guard initialized. The solution for this problem is to use an acquire
and release load and store to ensure previous writes to the structure are
observable if the guard is initialized.

The previous implementation used DL_FIXUP_VALUE_ADDR (l_reloc_result->addr)
as the initialization guard, making it impossible for some architectures
to load and store it atomically, i.e. hppa and ia64, due to its larger size.

This commit adds an unsigned int to l_reloc_result to be used as the new
initialization guard of the struct, making it possible to load and store
it atomically in all architectures. The fix ensures that the values
observed in l_reloc_result are consistent and do not lead to crashes.
The algorithm is documented in the code in elf/dl-runtime.c
(_dl_profile_fixup). Not all data races have been eliminated.

Tested with build-many-glibcs and on powerpc, powerpc64, and powerpc64le.

	[BZ #23690]
	* elf/dl-runtime.c (_dl_profile_fixup): Guarantee memory
	modification order when accessing reloc_result->addr.
	* include/link.h (reloc_result): Add field init.
	* nptl/Makefile (tests): Add tst-audit-threads.
	(modules-names): Add tst-audit-threads-mod1 and
	tst-audit-threads-mod2.
	Add rules to build tst-audit-threads.
	* nptl/tst-audit-threads-mod1.c: New file.
	* nptl/tst-audit-threads-mod2.c: Likewise.
	* nptl/tst-audit-threads.c: Likewise.
	* nptl/tst-audit-threads.h: Likewise.

Signed-off-by: Tulio Magno Quites Machado Filho <tuliom@linux.ibm.com>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
(cherry picked from commit e5d262effe)
2018-12-13 06:41:41 +01:00

98 lines
2.6 KiB
C

/* Test multi-threading using LD_AUDIT.
Copyright (C) 2018 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/>. */
/* This test uses a dummy LD_AUDIT library (test-audit-threads-mod1) and a
library with a huge number of functions in order to validate lazy symbol
binding with an audit library. We use one thread per CPU to test that
concurrent lazy resolution does not have any defects which would cause
the process to fail. We use an LD_AUDIT library to force the testing of
the relocation resolution caching code in the dynamic loader i.e.
_dl_runtime_profile and _dl_profile_fixup. */
#include <support/xthread.h>
#include <strings.h>
#include <stdlib.h>
#include <sys/sysinfo.h>
static int do_test (void);
/* This test usually takes less than 3s to run. However, there are cases that
take up to 30s. */
#define TIMEOUT 60
#define TEST_FUNCTION do_test ()
#include "../test-skeleton.c"
/* Declare the functions we are going to call. */
#define externnum
#include "tst-audit-threads.h"
#undef externnum
int num_threads;
pthread_barrier_t barrier;
void
sync_all (int num)
{
pthread_barrier_wait (&barrier);
}
void
call_all_ret_nums (void)
{
/* Call each function one at a time from all threads. */
#define callnum
#include "tst-audit-threads.h"
#undef callnum
}
void *
thread_main (void *unused)
{
call_all_ret_nums ();
return NULL;
}
#define STR2(X) #X
#define STR(X) STR2(X)
static int
do_test (void)
{
int i;
pthread_t *threads;
num_threads = get_nprocs ();
if (num_threads <= 1)
num_threads = 2;
/* Used to synchronize all the threads after calling each retNumN. */
xpthread_barrier_init (&barrier, NULL, num_threads);
threads = (pthread_t *) xcalloc (num_threads, sizeof(pthread_t));
for (i = 0; i < num_threads; i++)
threads[i] = xpthread_create(NULL, thread_main, NULL);
for (i = 0; i < num_threads; i++)
xpthread_join(threads[i]);
free (threads);
return 0;
}