diff options
author | Akash Hadke <akash.hadke@kpit.com> | 2021-12-24 16:12:28 +0530 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2022-01-22 17:56:53 +0000 |
commit | 6348d2d8a03a5763778b6b9c75bbb51423ab7bfd (patch) | |
tree | 0193e6ba3274aebd801eed9742a7d08d261dd8d0 /meta/recipes-core/glibc/glibc/0032-elf-Use-relaxed-atomics-for-racy-accesses-BZ-19329.patch | |
parent | 7a4fa2864255cb015f7da8e6b9def25ca2de1ea1 (diff) | |
download | poky-6348d2d8a03a5763778b6b9c75bbb51423ab7bfd.tar.gz |
glibc: Add fix for data races in pthread_create and TLS access
Inconsistency detected by ld.so: dl-tls.c: 493: _dl_allocate_tls_init: Assertion `listp->slotinfo[cnt].gen <= _rtld_local._dl_tls_generation' failed!
caused by dlopen (in _dl_add_to_slotinfo and in dl_open_worker) doing
listp->slotinfo[idx].gen = GL(dl_tls_generation) + 1;
//...
if (any_tls && __builtin_expect (++GL(dl_tls_generation) == 0, 0))
while pthread_create (in _dl_allocate_tls_init) concurrently doing
assert (listp->slotinfo[cnt].gen <= GL(dl_tls_generation));
Backported below patch that can fix the following bugs with a lock
that prevents DTV setup running concurrently with dlopen or dlclose.
Bug 19329: https://sourceware.org/bugzilla/show_bug.cgi?id=19329
Bug 27111: https://sourceware.org/bugzilla/show_bug.cgi?id=27111
Patch: 0031-elf-Fix-data-races-in-pthread_create-and-TLS-access-BZ-19329.patch
Link: https://sourceware.org/git/?p=glibc.git;a=patch;h=1387ad6225c2222f027790e3f460e31aa5dd2c54
It requires a supporting patch
0030-elf-Refactor_dl_update-slotinfo-to-avoid-use-after-free.patch
Link: https://sourceware.org/git/?p=glibc.git;a=patch;h=c0669ae1a629e16b536bf11cdd0865e0dbcf4bee
After adding the above fix there is a number of racy read accesses
to globals that will be changed to relaxed MO atomics in follow-up
patch given below.
This fixes the regressions and avoids cluttering the main part
of the fix.
0032-elf-Use-relaxed-atomics-for-racy-accesses-BZ-19329.patch
Link: https://sourceware.org/git/?p=glibc.git;a=patch;h=f4f8f4d4e0f92488431b268c8cd9555730b9afe9
Backported the below patch to add the test to check the added fix.
0033-elf-Add-test-case-for-BZ-19329.patch
Link: https://sourceware.org/git/?p=glibc.git;a=patch;h=9d0e30329c23b5ad736fda3f174208c25970dbce
Previously modids were never resused for a
different module, but after dlopen failure all gaps are reused
not just the ones caused by the unfinished dlopened.
The code has to handle reused modids already which seems to
work, however the data races at thread creation and tls access
(see bug 19329 and bug 27111) may be more severe if slots are
reused. Fixing the races are not simpler if reuse is disallowed
and reuse has other benefits so upstream added fix
https://sourceware.org/git/?p=glibc.git;a=commit;h=572bd547d57a39b6cf0ea072545dc4048921f4c3
for the following bug.
Bug 27135: https://sourceware.org/bugzilla/show_bug.cgi?id=27135
But in glibc upstream the commit 572bd547d57a was reverted as the
issue with 572bd547d57a patch was the DTV entry only updated on
dl_open_worker() with the update_tls_slotinfo() call after all
dependencies are being processed by _dl_map_object_deps(). However
_dl_map_object_deps() itself might call _dl_next_tls_modid(),
and since the _dl_tls_dtv_slotinfo_list::map was not yet set the
entry can be wrongly reused.
So added below patch to fix Bug 27135.
0034-elf-Fix-DTV-gap-reuse-logic-BZ-27135.patch
Link: https://sourceware.org/git/?p=glibc.git;a=patch;h=ba33937be210da5d07f7f01709323743f66011ce
Not all TLS access related data races got fixed by adding
0031-elf-Fix-data-races-in-pthread_create-and-TLS-access-BZ-19329.patch,
there are additional races at lazy tlsdesc relocations.
Bug 27137: https://sourceware.org/bugzilla/show_bug.cgi?id=27137
Backported below patches to fix this issue.
0035-x86_64-Avoid-lazy-relocation-of-tlsdesc-BZ-27137.patch
Link: https://sourceware.org/git/?p=glibc.git;a=patch;h=8f7e09f4dbdb5c815a18b8285fbc5d5d7bc17d86
0036-i386-Avoid-lazy-relocation-of-tlsdesc-BZ-27137.patch
Link: https://sourceware.org/git/?p=glibc.git;a=patch;h=ddcacd91cc10ff92d6201eda87047d029c14158d
The fix 0031-elf-Fix-data-races-in-pthread_create-and-TLS-access-BZ-19329.patch
for bug 19329 caused a regression such that pthread_create can
deadlock when concurrent ctors from dlopen are waiting for it
to finish.
Bug 28357: https://sourceware.org/bugzilla/show_bug.cgi?id=28357
Backported below patch to fix this issue.
0037-Avoid-deadlock-between-pthread_create-and-ctors.patch
Link: https://sourceware.org/git/?p=glibc.git;a=patch;h=024a7640ab9ecea80e527f4e4d7f7a1868e952c5
(From OE-Core rev: 01f256bc72fb45c80b6a6c77506bc4c375965a3a)
Signed-off-by: Akash Hadke <akash.hadke@kpit.com>
Signed-off-by: Akash Hadke <hadkeakash4@gmail.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/recipes-core/glibc/glibc/0032-elf-Use-relaxed-atomics-for-racy-accesses-BZ-19329.patch')
-rw-r--r-- | meta/recipes-core/glibc/glibc/0032-elf-Use-relaxed-atomics-for-racy-accesses-BZ-19329.patch | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/meta/recipes-core/glibc/glibc/0032-elf-Use-relaxed-atomics-for-racy-accesses-BZ-19329.patch b/meta/recipes-core/glibc/glibc/0032-elf-Use-relaxed-atomics-for-racy-accesses-BZ-19329.patch new file mode 100644 index 0000000000..eb8ef3161c --- /dev/null +++ b/meta/recipes-core/glibc/glibc/0032-elf-Use-relaxed-atomics-for-racy-accesses-BZ-19329.patch | |||
@@ -0,0 +1,206 @@ | |||
1 | From f4f8f4d4e0f92488431b268c8cd9555730b9afe9 Mon Sep 17 00:00:00 2001 | ||
2 | From: Szabolcs Nagy <szabolcs.nagy@arm.com> | ||
3 | Date: Wed, 30 Dec 2020 19:19:37 +0000 | ||
4 | Subject: [PATCH] elf: Use relaxed atomics for racy accesses [BZ #19329] | ||
5 | |||
6 | This is a follow up patch to the fix for bug 19329. This adds relaxed | ||
7 | MO atomics to accesses that were previously data races but are now | ||
8 | race conditions, and where relaxed MO is sufficient. | ||
9 | |||
10 | The race conditions all follow the pattern that the write is behind the | ||
11 | dlopen lock, but a read can happen concurrently (e.g. during tls access) | ||
12 | without holding the lock. For slotinfo entries the read value only | ||
13 | matters if it reads from a synchronized write in dlopen or dlclose, | ||
14 | otherwise the related dtv entry is not valid to access so it is fine | ||
15 | to leave it in an inconsistent state. The same applies for | ||
16 | GL(dl_tls_max_dtv_idx) and GL(dl_tls_generation), but there the | ||
17 | algorithm relies on the fact that the read of the last synchronized | ||
18 | write is an increasing value. | ||
19 | |||
20 | Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org> | ||
21 | --- | ||
22 | elf/dl-close.c | 20 +++++++++++++------- | ||
23 | elf/dl-open.c | 5 ++++- | ||
24 | elf/dl-tls.c | 31 +++++++++++++++++++++++-------- | ||
25 | sysdeps/x86_64/dl-tls.c | 3 ++- | ||
26 | 4 files changed, 42 insertions(+), 17 deletions(-) | ||
27 | --- | ||
28 | Upstream-Status: Backport [https://sourceware.org/git/?p=glibc.git;a=patch;h=f4f8f4d4e0f92488431b268c8cd9555730b9afe9] | ||
29 | Comment: Hunks from elf/dl-open.c and elf/dl-tls.c are refreshed due to offset change. | ||
30 | Signed-off-by: Akash Hadke <akash.hadke@kpit.com> | ||
31 | Signed-off-by: Akash Hadke <hadkeakash4@gmail.com> | ||
32 | --- | ||
33 | diff --git a/elf/dl-close.c b/elf/dl-close.c | ||
34 | index c51becd06b..3720e47dd1 100644 | ||
35 | --- a/elf/dl-close.c | ||
36 | +++ b/elf/dl-close.c | ||
37 | @@ -79,9 +79,10 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp, | ||
38 | { | ||
39 | assert (old_map->l_tls_modid == idx); | ||
40 | |||
41 | - /* Mark the entry as unused. */ | ||
42 | - listp->slotinfo[idx - disp].gen = GL(dl_tls_generation) + 1; | ||
43 | - listp->slotinfo[idx - disp].map = NULL; | ||
44 | + /* Mark the entry as unused. These can be read concurrently. */ | ||
45 | + atomic_store_relaxed (&listp->slotinfo[idx - disp].gen, | ||
46 | + GL(dl_tls_generation) + 1); | ||
47 | + atomic_store_relaxed (&listp->slotinfo[idx - disp].map, NULL); | ||
48 | } | ||
49 | |||
50 | /* If this is not the last currently used entry no need to look | ||
51 | @@ -96,8 +97,8 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp, | ||
52 | |||
53 | if (listp->slotinfo[idx - disp].map != NULL) | ||
54 | { | ||
55 | - /* Found a new last used index. */ | ||
56 | - GL(dl_tls_max_dtv_idx) = idx; | ||
57 | + /* Found a new last used index. This can be read concurrently. */ | ||
58 | + atomic_store_relaxed (&GL(dl_tls_max_dtv_idx), idx); | ||
59 | return true; | ||
60 | } | ||
61 | } | ||
62 | @@ -571,7 +572,9 @@ _dl_close_worker (struct link_map *map, bool force) | ||
63 | GL(dl_tls_dtv_slotinfo_list), 0, | ||
64 | imap->l_init_called)) | ||
65 | /* All dynamically loaded modules with TLS are unloaded. */ | ||
66 | - GL(dl_tls_max_dtv_idx) = GL(dl_tls_static_nelem); | ||
67 | + /* Can be read concurrently. */ | ||
68 | + atomic_store_relaxed (&GL(dl_tls_max_dtv_idx), | ||
69 | + GL(dl_tls_static_nelem)); | ||
70 | |||
71 | if (imap->l_tls_offset != NO_TLS_OFFSET | ||
72 | && imap->l_tls_offset != FORCED_DYNAMIC_TLS_OFFSET) | ||
73 | @@ -769,8 +772,11 @@ _dl_close_worker (struct link_map *map, bool force) | ||
74 | /* If we removed any object which uses TLS bump the generation counter. */ | ||
75 | if (any_tls) | ||
76 | { | ||
77 | - if (__glibc_unlikely (++GL(dl_tls_generation) == 0)) | ||
78 | + size_t newgen = GL(dl_tls_generation) + 1; | ||
79 | + if (__glibc_unlikely (newgen == 0)) | ||
80 | _dl_fatal_printf ("TLS generation counter wrapped! Please report as described in "REPORT_BUGS_TO".\n"); | ||
81 | + /* Can be read concurrently. */ | ||
82 | + atomic_store_relaxed (&GL(dl_tls_generation), newgen); | ||
83 | |||
84 | if (tls_free_end == GL(dl_tls_static_used)) | ||
85 | GL(dl_tls_static_used) = tls_free_start; | ||
86 | diff --git a/elf/dl-open.c b/elf/dl-open.c | ||
87 | index 09f0df7d38..bb79ef00f1 100644 | ||
88 | --- a/elf/dl-open.c | ||
89 | +++ b/elf/dl-open.c | ||
90 | @@ -387,9 +387,12 @@ | ||
91 | } | ||
92 | } | ||
93 | |||
94 | - if (__builtin_expect (++GL(dl_tls_generation) == 0, 0)) | ||
95 | + size_t newgen = GL(dl_tls_generation) + 1; | ||
96 | + if (__glibc_unlikely (newgen == 0)) | ||
97 | _dl_fatal_printf (N_("\ | ||
98 | TLS generation counter wrapped! Please report this.")); | ||
99 | + /* Can be read concurrently. */ | ||
100 | + atomic_store_relaxed (&GL(dl_tls_generation), newgen); | ||
101 | |||
102 | /* We need a second pass for static tls data, because | ||
103 | _dl_update_slotinfo must not be run while calls to | ||
104 | diff --git a/elf/dl-tls.c b/elf/dl-tls.c | ||
105 | index 94f3cdbae0..dc69cd984e 100644 | ||
106 | --- a/elf/dl-tls.c | ||
107 | +++ b/elf/dl-tls.c | ||
108 | @@ -96,7 +96,9 @@ | ||
109 | /* No gaps, allocate a new entry. */ | ||
110 | nogaps: | ||
111 | |||
112 | - result = ++GL(dl_tls_max_dtv_idx); | ||
113 | + result = GL(dl_tls_max_dtv_idx) + 1; | ||
114 | + /* Can be read concurrently. */ | ||
115 | + atomic_store_relaxed (&GL(dl_tls_max_dtv_idx), result); | ||
116 | } | ||
117 | |||
118 | return result; | ||
119 | @@ -279,10 +281,12 @@ | ||
120 | dtv_t *dtv; | ||
121 | size_t dtv_length; | ||
122 | |||
123 | + /* Relaxed MO, because the dtv size is later rechecked, not relied on. */ | ||
124 | + size_t max_modid = atomic_load_relaxed (&GL(dl_tls_max_dtv_idx)); | ||
125 | /* We allocate a few more elements in the dtv than are needed for the | ||
126 | initial set of modules. This should avoid in most cases expansions | ||
127 | of the dtv. */ | ||
128 | - dtv_length = GL(dl_tls_max_dtv_idx) + DTV_SURPLUS; | ||
129 | + dtv_length = max_modid + DTV_SURPLUS; | ||
130 | dtv = calloc (dtv_length + 2, sizeof (dtv_t)); | ||
131 | if (dtv != NULL) | ||
132 | { | ||
133 | @@ -687,7 +691,7 @@ | ||
134 | if (modid > max_modid) | ||
135 | break; | ||
136 | |||
137 | - size_t gen = listp->slotinfo[cnt].gen; | ||
138 | + size_t gen = atomic_load_relaxed (&listp->slotinfo[cnt].gen); | ||
139 | |||
140 | if (gen > new_gen) | ||
141 | /* Not relevant. */ | ||
142 | @@ -699,7 +703,8 @@ | ||
143 | continue; | ||
144 | |||
145 | /* If there is no map this means the entry is empty. */ | ||
146 | - struct link_map *map = listp->slotinfo[cnt].map; | ||
147 | + struct link_map *map | ||
148 | + = atomic_load_relaxed (&listp->slotinfo[cnt].map); | ||
149 | /* Check whether the current dtv array is large enough. */ | ||
150 | if (dtv[-1].counter < modid) | ||
151 | { | ||
152 | @@ -843,7 +848,12 @@ | ||
153 | { | ||
154 | dtv_t *dtv = THREAD_DTV (); | ||
155 | |||
156 | - if (__glibc_unlikely (dtv[0].counter != GL(dl_tls_generation))) | ||
157 | + /* Update is needed if dtv[0].counter < the generation of the accessed | ||
158 | + module. The global generation counter is used here as it is easier | ||
159 | + to check. Synchronization for the relaxed MO access is guaranteed | ||
160 | + by user code, see CONCURRENCY NOTES in _dl_update_slotinfo. */ | ||
161 | + size_t gen = atomic_load_relaxed (&GL(dl_tls_generation)); | ||
162 | + if (__glibc_unlikely (dtv[0].counter != gen)) | ||
163 | return update_get_addr (GET_ADDR_PARAM); | ||
164 | |||
165 | void *p = dtv[GET_ADDR_MODULE].pointer.val; | ||
166 | @@ -866,7 +876,10 @@ | ||
167 | return NULL; | ||
168 | |||
169 | dtv_t *dtv = THREAD_DTV (); | ||
170 | - if (__glibc_unlikely (dtv[0].counter != GL(dl_tls_generation))) | ||
171 | + /* This may be called without holding the GL(dl_load_lock). Reading | ||
172 | + arbitrary gen value is fine since this is best effort code. */ | ||
173 | + size_t gen = atomic_load_relaxed (&GL(dl_tls_generation)); | ||
174 | + if (__glibc_unlikely (dtv[0].counter != gen)) | ||
175 | { | ||
176 | /* This thread's DTV is not completely current, | ||
177 | but it might already cover this module. */ | ||
178 | @@ -961,7 +974,9 @@ | ||
179 | /* Add the information into the slotinfo data structure. */ | ||
180 | if (do_add) | ||
181 | { | ||
182 | - listp->slotinfo[idx].map = l; | ||
183 | - listp->slotinfo[idx].gen = GL(dl_tls_generation) + 1; | ||
184 | + /* Can be read concurrently. See _dl_update_slotinfo. */ | ||
185 | + atomic_store_relaxed (&listp->slotinfo[idx].map, l); | ||
186 | + atomic_store_relaxed (&listp->slotinfo[idx].gen, | ||
187 | + GL(dl_tls_generation) + 1); | ||
188 | } | ||
189 | } | ||
190 | |||
191 | diff --git a/sysdeps/x86_64/dl-tls.c b/sysdeps/x86_64/dl-tls.c | ||
192 | index 6595f6615b..24ef560b71 100644 | ||
193 | --- a/sysdeps/x86_64/dl-tls.c | ||
194 | +++ b/sysdeps/x86_64/dl-tls.c | ||
195 | @@ -40,7 +40,8 @@ __tls_get_addr_slow (GET_ADDR_ARGS) | ||
196 | { | ||
197 | dtv_t *dtv = THREAD_DTV (); | ||
198 | |||
199 | - if (__glibc_unlikely (dtv[0].counter != GL(dl_tls_generation))) | ||
200 | + size_t gen = atomic_load_relaxed (&GL(dl_tls_generation)); | ||
201 | + if (__glibc_unlikely (dtv[0].counter != gen)) | ||
202 | return update_get_addr (GET_ADDR_PARAM); | ||
203 | |||
204 | return tls_get_addr_tail (GET_ADDR_PARAM, dtv, NULL); | ||
205 | -- | ||
206 | 2.27.0 | ||