summaryrefslogtreecommitdiffstats
path: root/meta/recipes-core/glibc/glibc/0031-elf-Fix-data-races-in-pthread_create-and-TLS-access-BZ-19329.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-core/glibc/glibc/0031-elf-Fix-data-races-in-pthread_create-and-TLS-access-BZ-19329.patch')
-rw-r--r--meta/recipes-core/glibc/glibc/0031-elf-Fix-data-races-in-pthread_create-and-TLS-access-BZ-19329.patch191
1 files changed, 191 insertions, 0 deletions
diff --git a/meta/recipes-core/glibc/glibc/0031-elf-Fix-data-races-in-pthread_create-and-TLS-access-BZ-19329.patch b/meta/recipes-core/glibc/glibc/0031-elf-Fix-data-races-in-pthread_create-and-TLS-access-BZ-19329.patch
new file mode 100644
index 0000000000..25beee1d50
--- /dev/null
+++ b/meta/recipes-core/glibc/glibc/0031-elf-Fix-data-races-in-pthread_create-and-TLS-access-BZ-19329.patch
@@ -0,0 +1,191 @@
1From 1387ad6225c2222f027790e3f460e31aa5dd2c54 Mon Sep 17 00:00:00 2001
2From: Szabolcs Nagy <szabolcs.nagy@arm.com>
3Date: Wed, 30 Dec 2020 19:19:37 +0000
4Subject: [PATCH] elf: Fix data races in pthread_create and TLS access [BZ
5 #19329]
6
7DTV setup at thread creation (_dl_allocate_tls_init) is changed
8to take the dlopen lock, GL(dl_load_lock). Avoiding data races
9here without locks would require design changes: the map that is
10accessed for static TLS initialization here may be concurrently
11freed by dlclose. That use after free may be solved by only
12locking around static TLS setup or by ensuring dlclose does not
13free modules with static TLS, however currently every link map
14with TLS has to be accessed at least to see if it needs static
15TLS. And even if that's solved, still a lot of atomics would be
16needed to synchronize DTV related globals without a lock. So fix
17both bug 19329 and bug 27111 with a lock that prevents DTV setup
18running concurrently with dlopen or dlclose.
19
20_dl_update_slotinfo at TLS access still does not use any locks
21so CONCURRENCY NOTES are added to explain the synchronization.
22The early exit from the slotinfo walk when max_modid is reached
23is not strictly necessary, but does not hurt either.
24
25An incorrect acquire load was removed from _dl_resize_dtv: it
26did not synchronize with any release store or fence and
27synchronization is now handled separately at thread creation
28and TLS access time.
29
30There are still a number of racy read accesses to globals that
31will be changed to relaxed MO atomics in a followup patch. This
32should not introduce regressions compared to existing behaviour
33and avoid cluttering the main part of the fix.
34
35Not all TLS access related data races got fixed here: there are
36additional races at lazy tlsdesc relocations see bug 27137.
37
38Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
39---
40 elf/dl-tls.c | 63 +++++++++++++++++++++++++++++++++++++++-------------
41 1 file changed, 47 insertions(+), 16 deletions(-)
42---
43Upstream-Status: Backport [https://sourceware.org/git/?p=glibc.git;a=patch;h=1387ad6225c2222f027790e3f460e31aa5dd2c54]
44Signed-off-by: Akash Hadke <akash.hadke@kpit.com>
45Signed-off-by: Akash Hadke <hadkeakash4@gmail.com>
46---
47diff --git a/elf/dl-tls.c b/elf/dl-tls.c
48index 6baff0c1ea..94f3cdbae0 100644
49--- a/elf/dl-tls.c
50+++ b/elf/dl-tls.c
51@@ -475,14 +475,11 @@ extern dtv_t _dl_static_dtv[];
52 #endif
53
54 static dtv_t *
55-_dl_resize_dtv (dtv_t *dtv)
56+_dl_resize_dtv (dtv_t *dtv, size_t max_modid)
57 {
58 /* Resize the dtv. */
59 dtv_t *newp;
60- /* Load GL(dl_tls_max_dtv_idx) atomically since it may be written to by
61- other threads concurrently. */
62- size_t newsize
63- = atomic_load_acquire (&GL(dl_tls_max_dtv_idx)) + DTV_SURPLUS;
64+ size_t newsize = max_modid + DTV_SURPLUS;
65 size_t oldsize = dtv[-1].counter;
66
67 if (dtv == GL(dl_initial_dtv))
68@@ -528,11 +525,14 @@ _dl_allocate_tls_init (void *result)
69 size_t total = 0;
70 size_t maxgen = 0;
71
72+ /* Protects global dynamic TLS related state. */
73+ __rtld_lock_lock_recursive (GL(dl_load_lock));
74+
75 /* Check if the current dtv is big enough. */
76 if (dtv[-1].counter < GL(dl_tls_max_dtv_idx))
77 {
78 /* Resize the dtv. */
79- dtv = _dl_resize_dtv (dtv);
80+ dtv = _dl_resize_dtv (dtv, GL(dl_tls_max_dtv_idx));
81
82 /* Install this new dtv in the thread data structures. */
83 INSTALL_DTV (result, &dtv[-1]);
84@@ -600,6 +600,7 @@ _dl_allocate_tls_init (void *result)
85 listp = listp->next;
86 assert (listp != NULL);
87 }
88+ __rtld_lock_unlock_recursive (GL(dl_load_lock));
89
90 /* The DTV version is up-to-date now. */
91 dtv[0].counter = maxgen;
92@@ -734,12 +735,29 @@ _dl_update_slotinfo (unsigned long int req_modid)
93
94 if (dtv[0].counter < listp->slotinfo[idx].gen)
95 {
96- /* The generation counter for the slot is higher than what the
97- current dtv implements. We have to update the whole dtv but
98- only those entries with a generation counter <= the one for
99- the entry we need. */
100+ /* CONCURRENCY NOTES:
101+
102+ Here the dtv needs to be updated to new_gen generation count.
103+
104+ This code may be called during TLS access when GL(dl_load_lock)
105+ is not held. In that case the user code has to synchronize with
106+ dlopen and dlclose calls of relevant modules. A module m is
107+ relevant if the generation of m <= new_gen and dlclose of m is
108+ synchronized: a memory access here happens after the dlopen and
109+ before the dlclose of relevant modules. The dtv entries for
110+ relevant modules need to be updated, other entries can be
111+ arbitrary.
112+
113+ This e.g. means that the first part of the slotinfo list can be
114+ accessed race free, but the tail may be concurrently extended.
115+ Similarly relevant slotinfo entries can be read race free, but
116+ other entries are racy. However updating a non-relevant dtv
117+ entry does not affect correctness. For a relevant module m,
118+ max_modid >= modid of m. */
119 size_t new_gen = listp->slotinfo[idx].gen;
120 size_t total = 0;
121+ size_t max_modid = atomic_load_relaxed (&GL(dl_tls_max_dtv_idx));
122+ assert (max_modid >= req_modid);
123
124 /* We have to look through the entire dtv slotinfo list. */
125 listp = GL(dl_tls_dtv_slotinfo_list);
126@@ -749,12 +767,14 @@ _dl_update_slotinfo (unsigned long int req_modid)
127 {
128 size_t modid = total + cnt;
129
130+ /* Later entries are not relevant. */
131+ if (modid > max_modid)
132+ break;
133+
134 size_t gen = listp->slotinfo[cnt].gen;
135
136 if (gen > new_gen)
137- /* This is a slot for a generation younger than the
138- one we are handling now. It might be incompletely
139- set up so ignore it. */
140+ /* Not relevant. */
141 continue;
142
143 /* If the entry is older than the current dtv layout we
144@@ -771,7 +791,7 @@ _dl_update_slotinfo (unsigned long int req_modid)
145 continue;
146
147 /* Resize the dtv. */
148- dtv = _dl_resize_dtv (dtv);
149+ dtv = _dl_resize_dtv (dtv, max_modid);
150
151 assert (modid <= dtv[-1].counter);
152
153@@ -793,8 +813,17 @@ _dl_update_slotinfo (unsigned long int req_modid)
154 }
155
156 total += listp->len;
157+ if (total > max_modid)
158+ break;
159+
160+ /* Synchronize with _dl_add_to_slotinfo. Ideally this would
161+ be consume MO since we only need to order the accesses to
162+ the next node after the read of the address and on most
163+ hardware (other than alpha) a normal load would do that
164+ because of the address dependency. */
165+ listp = atomic_load_acquire (&listp->next);
166 }
167- while ((listp = listp->next) != NULL);
168+ while (listp != NULL);
169
170 /* This will be the new maximum generation counter. */
171 dtv[0].counter = new_gen;
172@@ -986,7 +1015,7 @@ _dl_add_to_slotinfo (struct link_map *l, bool do_add)
173 the first slot. */
174 assert (idx == 0);
175
176- listp = prevp->next = (struct dtv_slotinfo_list *)
177+ listp = (struct dtv_slotinfo_list *)
178 malloc (sizeof (struct dtv_slotinfo_list)
179 + TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo));
180 if (listp == NULL)
181@@ -1000,6 +1029,8 @@ cannot create TLS data structures"));
182 listp->next = NULL;
183 memset (listp->slotinfo, '\0',
184 TLS_SLOTINFO_SURPLUS * sizeof (struct dtv_slotinfo));
185+ /* Synchronize with _dl_update_slotinfo. */
186+ atomic_store_release (&prevp->next, listp);
187 }
188
189 /* Add the information into the slotinfo data structure. */
190--
1912.27.0