Skip to content

Commit 539be98

Browse files
committed
Fix race between critnib_release() and free_leaf() in critnib
Fix race between critnib_release() and free_leaf() in critnib: critnib_release() decremented ref_count to 0 and (before it called c->cb_free_leaf(k->to_be_freed)) free_leaf() added this leaf to the c->deleted_leaf list and alloc_leaf() reused it and zeroed k->to_be_freed before it could be freed in critnib_release(). This patch fixes this issue. Signed-off-by: Lukasz Dorau <[email protected]>
1 parent e309c77 commit 539be98

File tree

1 file changed

+32
-24
lines changed

1 file changed

+32
-24
lines changed

src/critnib/critnib.c

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -331,18 +331,26 @@ static void free_leaf(struct critnib *__restrict c,
331331
return;
332332
}
333333

334+
// k should be added to the c->deleted_leaf list here
335+
// or in critnib_release() when the reference count drops to 0.
336+
utils_atomic_store_release_u8(&k->pending_deleted_leaf, 1);
337+
334338
if (c->cb_free_leaf) {
335339
uint64_t ref_count;
336340
utils_atomic_load_acquire_u64(&k->ref_count, &ref_count);
337341
if (ref_count > 0) {
338-
// k will be added to c->deleted_leaf in critnib_release()
342+
// k will be added to the c->deleted_leaf list in critnib_release()
339343
// when the reference count drops to 0.
340-
utils_atomic_store_release_u8(&k->pending_deleted_leaf, 1);
341344
return;
342345
}
343346
}
344347

345-
add_to_deleted_leaf_list(c, k);
348+
uint8_t expected = 1;
349+
uint8_t desired = 0;
350+
if (utils_compare_exchange_u8(&k->pending_deleted_leaf, &expected,
351+
&desired)) {
352+
add_to_deleted_leaf_list(c, k);
353+
}
346354
}
347355

348356
/*
@@ -392,8 +400,8 @@ int critnib_insert(struct critnib *c, word key, void *value, int update) {
392400
utils_atomic_store_release_u8(&k->pending_deleted_leaf, 0);
393401

394402
if (c->cb_free_leaf) {
395-
// mark the leaf as valid (ref_count == 1)
396-
utils_atomic_store_release_u64(&k->ref_count, 1ULL);
403+
// mark the leaf as valid (ref_count == 2)
404+
utils_atomic_store_release_u64(&k->ref_count, 2ULL);
397405
} else {
398406
// the reference counter is not used in this case
399407
utils_atomic_store_release_u64(&k->ref_count, 0ULL);
@@ -602,36 +610,36 @@ int critnib_release(struct critnib *c, void *ref) {
602610
struct critnib_leaf *k = (struct critnib_leaf *)ref;
603611

604612
uint64_t ref_count;
605-
utils_atomic_load_acquire_u64(&k->ref_count, &ref_count);
606-
607-
if (ref_count == 0) {
608-
return -1;
609-
}
610-
613+
uint64_t ref_desired;
611614
/* decrement the reference count */
612-
if (utils_atomic_decrement_u64(&k->ref_count) == 0) {
615+
do {
616+
utils_atomic_load_acquire_u64(&k->ref_count, &ref_count);
617+
if (ref_count == 0) {
618+
return -1;
619+
}
620+
ref_desired = ref_count - 1;
621+
} while (
622+
!utils_compare_exchange_u64(&k->ref_count, &ref_count, &ref_desired));
623+
624+
if (ref_desired == 1) {
613625
void *to_be_freed = NULL;
614626
utils_atomic_load_acquire_ptr(&k->to_be_freed, &to_be_freed);
615627
if (to_be_freed) {
616628
utils_atomic_store_release_ptr(&k->to_be_freed, NULL);
617629
c->cb_free_leaf(c->leaf_allocator, to_be_freed);
618630
}
619-
uint8_t pending_deleted_leaf;
620-
utils_atomic_load_acquire_u8(&k->pending_deleted_leaf,
621-
&pending_deleted_leaf);
622-
if (pending_deleted_leaf) {
623-
utils_atomic_store_release_u8(&k->pending_deleted_leaf, 0);
631+
632+
// mark the leaf as not used (ref_count == 0)
633+
utils_atomic_store_release_u64(&k->ref_count, 0ULL);
634+
635+
uint8_t expected = 1;
636+
uint8_t desired = 0;
637+
if (utils_compare_exchange_u8(&k->pending_deleted_leaf, &expected,
638+
&desired)) {
624639
add_to_deleted_leaf_list(c, k);
625640
}
626641
}
627642

628-
#ifndef NDEBUG
629-
// check if the reference count is overflowed
630-
utils_atomic_load_acquire_u64(&k->ref_count, &ref_count);
631-
assert((ref_count & (1ULL << 63)) == 0);
632-
assert(ref_count != (uint64_t)(0 - 1ULL));
633-
#endif
634-
635643
return 0;
636644
}
637645

0 commit comments

Comments
 (0)