diff options
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.patch | 191 |
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 @@ | |||
1 | From 1387ad6225c2222f027790e3f460e31aa5dd2c54 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: Fix data races in pthread_create and TLS access [BZ | ||
5 | #19329] | ||
6 | |||
7 | DTV setup at thread creation (_dl_allocate_tls_init) is changed | ||
8 | to take the dlopen lock, GL(dl_load_lock). Avoiding data races | ||
9 | here without locks would require design changes: the map that is | ||
10 | accessed for static TLS initialization here may be concurrently | ||
11 | freed by dlclose. That use after free may be solved by only | ||
12 | locking around static TLS setup or by ensuring dlclose does not | ||
13 | free modules with static TLS, however currently every link map | ||
14 | with TLS has to be accessed at least to see if it needs static | ||
15 | TLS. And even if that's solved, still a lot of atomics would be | ||
16 | needed to synchronize DTV related globals without a lock. So fix | ||
17 | both bug 19329 and bug 27111 with a lock that prevents DTV setup | ||
18 | running concurrently with dlopen or dlclose. | ||
19 | |||
20 | _dl_update_slotinfo at TLS access still does not use any locks | ||
21 | so CONCURRENCY NOTES are added to explain the synchronization. | ||
22 | The early exit from the slotinfo walk when max_modid is reached | ||
23 | is not strictly necessary, but does not hurt either. | ||
24 | |||
25 | An incorrect acquire load was removed from _dl_resize_dtv: it | ||
26 | did not synchronize with any release store or fence and | ||
27 | synchronization is now handled separately at thread creation | ||
28 | and TLS access time. | ||
29 | |||
30 | There are still a number of racy read accesses to globals that | ||
31 | will be changed to relaxed MO atomics in a followup patch. This | ||
32 | should not introduce regressions compared to existing behaviour | ||
33 | and avoid cluttering the main part of the fix. | ||
34 | |||
35 | Not all TLS access related data races got fixed here: there are | ||
36 | additional races at lazy tlsdesc relocations see bug 27137. | ||
37 | |||
38 | Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org> | ||
39 | --- | ||
40 | elf/dl-tls.c | 63 +++++++++++++++++++++++++++++++++++++++------------- | ||
41 | 1 file changed, 47 insertions(+), 16 deletions(-) | ||
42 | --- | ||
43 | Upstream-Status: Backport [https://sourceware.org/git/?p=glibc.git;a=patch;h=1387ad6225c2222f027790e3f460e31aa5dd2c54] | ||
44 | Signed-off-by: Akash Hadke <akash.hadke@kpit.com> | ||
45 | Signed-off-by: Akash Hadke <hadkeakash4@gmail.com> | ||
46 | --- | ||
47 | diff --git a/elf/dl-tls.c b/elf/dl-tls.c | ||
48 | index 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 | -- | ||
191 | 2.27.0 | ||