Tcaches: Fix a subtle race condition.

Without a lock held continuously between checking tcaches_past and incrementing
it, it's possible for two threads to go down manual creation path
simultaneously.  If the number of tcaches is one less than the maximum, it's
possible for both to create a tcache and increment tcaches_past, with the second
thread returning a value larger than TCACHES_MAX.
This commit is contained in:
David Goldblatt 2020-10-13 12:40:34 -07:00 committed by David Goldblatt
parent a9aa6f6d0f
commit be9548f2be

View File

@ -767,7 +767,7 @@ static bool
tcaches_create_prep(tsd_t *tsd, base_t *base) { tcaches_create_prep(tsd_t *tsd, base_t *base) {
bool err; bool err;
malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx); malloc_mutex_assert_owner(tsd_tsdn(tsd), &tcaches_mtx);
if (tcaches == NULL) { if (tcaches == NULL) {
tcaches = base_alloc(tsd_tsdn(tsd), base, tcaches = base_alloc(tsd_tsdn(tsd), base,
@ -785,7 +785,6 @@ tcaches_create_prep(tsd_t *tsd, base_t *base) {
err = false; err = false;
label_return: label_return:
malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);
return err; return err;
} }
@ -795,6 +794,8 @@ tcaches_create(tsd_t *tsd, base_t *base, unsigned *r_ind) {
bool err; bool err;
malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx);
if (tcaches_create_prep(tsd, base)) { if (tcaches_create_prep(tsd, base)) {
err = true; err = true;
goto label_return; goto label_return;
@ -807,7 +808,6 @@ tcaches_create(tsd_t *tsd, base_t *base, unsigned *r_ind) {
} }
tcaches_t *elm; tcaches_t *elm;
malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx);
if (tcaches_avail != NULL) { if (tcaches_avail != NULL) {
elm = tcaches_avail; elm = tcaches_avail;
tcaches_avail = tcaches_avail->next; tcaches_avail = tcaches_avail->next;
@ -819,10 +819,10 @@ tcaches_create(tsd_t *tsd, base_t *base, unsigned *r_ind) {
*r_ind = tcaches_past; *r_ind = tcaches_past;
tcaches_past++; tcaches_past++;
} }
malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);
err = false; err = false;
label_return: label_return:
malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);
witness_assert_depth(tsdn_witness_tsdp_get(tsd_tsdn(tsd)), 0); witness_assert_depth(tsdn_witness_tsdp_get(tsd_tsdn(tsd)), 0);
return err; return err;
} }