From 02b36f75721aa61d48e3031c71695837d7a8a410 Mon Sep 17 00:00:00 2001 From: KJ Tsanaktsidis Date: Sat, 21 Sep 2024 21:11:27 +1000 Subject: [PATCH] Unpoison page->freelist before trying to assert on it Otherwise trying to deref the pointer can cause an ASAN crash, even though the only reason we're dereferencing it is so that we can assert on it. --- gc/default.c | 10 +++------- internal/sanitizers.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/gc/default.c b/gc/default.c index d60fba3ff0..addaf01fc2 100644 --- a/gc/default.c +++ b/gc/default.c @@ -788,7 +788,9 @@ heap_page_in_global_empty_pages_pool(rb_objspace_t *objspace, struct heap_page * GC_ASSERT(page->slot_size == 0); GC_ASSERT(page->size_pool == NULL); GC_ASSERT(page->free_slots == 0); - GC_ASSERT(page->freelist == NULL); + asan_unpoisoning_memory_region(&page->freelist, sizeof(&page->freelist)) { + GC_ASSERT(page->freelist == NULL); + } return true; } @@ -1177,12 +1179,6 @@ tick(void) #define MEASURE_LINE(expr) expr #endif /* USE_TICK_T */ -#define asan_unpoisoning_object(obj) \ - for (void *poisoned = asan_unpoison_object_temporary(obj), \ - *unpoisoning = &poisoned; /* flag to loop just once */ \ - unpoisoning; \ - unpoisoning = asan_poison_object_restore(obj, poisoned)) - #define FL_CHECK2(name, x, pred) \ ((RGENGC_CHECK_MODE && SPECIAL_CONST_P(x)) ? \ (rb_bug(name": SPECIAL_CONST (%p)", (void *)(x)), 0) : (pred)) diff --git a/internal/sanitizers.h b/internal/sanitizers.h index b0eb1fc851..94f4e4165a 100644 --- a/internal/sanitizers.h +++ b/internal/sanitizers.h @@ -206,6 +206,35 @@ asan_poison_object_restore(VALUE obj, void *ptr) return NULL; } +#define asan_unpoisoning_object(obj) \ + for (void *poisoned = asan_unpoison_object_temporary(obj), \ + *unpoisoning = &poisoned; /* flag to loop just once */ \ + unpoisoning; \ + unpoisoning = asan_poison_object_restore(obj, poisoned)) + + +static inline void * +asan_unpoison_memory_region_temporary(void *ptr, size_t len) +{ + void *poisoned_ptr = __asan_region_is_poisoned(ptr, len); + asan_unpoison_memory_region(ptr, len, false); + return poisoned_ptr; +} + +static inline void * +asan_poison_memory_region_restore(void *ptr, size_t len, void *poisoned_ptr) +{ + if (poisoned_ptr) { + asan_poison_memory_region(ptr, len); + } + return NULL; +} + +#define asan_unpoisoning_memory_region(ptr, len) \ + for (void *poisoned = asan_unpoison_memory_region_temporary(ptr, len), \ + *unpoisoning = &poisoned; /* flag to loop just once */ \ + unpoisoning; \ + unpoisoning = asan_poison_memory_region_restore(ptr, len, poisoned)) /** * Checks if the given pointer is on an ASAN fake stack. If so, it returns the