The branch stable/15 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=01f6dff4ac58d4e9573d0b698276bcbd9e2cd643
commit 01f6dff4ac58d4e9573d0b698276bcbd9e2cd643 Author: Mark Johnston <[email protected]> AuthorDate: 2026-07-24 21:12:10 +0000 Commit: Mark Johnston <[email protected]> CommitDate: 2026-08-24 15:17:55 +0000 uma: Make an effort to defer reuse of items when KASAN is enabled When KASAN is configured, make uma_zfree_arg() free items to the per-CPU free bucket, rather than to the alloc bucket. This means that the item won't be recycled immediately the next time a thread goes to allocate an item from that zone on the same CPU. In other words, the item will stay in a quarantine state longer, which helps make KASAN's use-after-free detection more reliable. Reviewed by: rlibby MFC after: 1 month Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D58269 (cherry picked from commit 990989c31b4637a23e64598a3d9929079bb9a8de) --- sys/vm/uma_core.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/sys/vm/uma_core.c b/sys/vm/uma_core.c index f5500e507ccc..b3bf654d27e9 100644 --- a/sys/vm/uma_core.c +++ b/sys/vm/uma_core.c @@ -4461,8 +4461,11 @@ fail: return (NULL); } +/* + * Try to free an item to the per-CPU cache, promoting its quick reuse. + */ static __always_inline bool -cache_free_item(uma_zone_t zone, int uz_flags, void *item, void *udata) +cache_free_reuse(uma_zone_t zone, int uz_flags, void *item, void *udata) { uma_cache_t cache; int itemdomain; @@ -4518,8 +4521,14 @@ cache_free_item(uma_zone_t zone, int uz_flags, void *item, void *udata) return (false); } +/* + * Try to free an object to the per-CPU cache, deferring its reuse. This is + * used by the SMR-protected allocator, which cannot reuse the item until + * smr_poll() guarantees that no threads are still accessing it, and by + * sanitizers, which wish to defer reuse to make UAF detection more effective. + */ static __always_inline bool -cache_free_smr(uma_zone_t zone, void *item, void *udata) +cache_free_defer(uma_zone_t zone, void *item, void *udata) { uma_cache_t cache; int itemdomain; @@ -4538,7 +4547,6 @@ cache_free_smr(uma_zone_t zone, void *item, void *udata) uma_cache_bucket_t bucket; cache = &zone->uz_cpu[curcpu]; - /* SMR Zones must free to the free bucket. */ bucket = &cache->uc_freebucket; #ifdef NUMA if ((uz_flags & UMA_ZONE_FIRSTTOUCH) != 0 && @@ -4573,7 +4581,7 @@ uma_zfree_smr(uma_zone_t zone, void *item) return; #endif - if (cache_free_smr(zone, item, NULL)) + if (cache_free_defer(zone, item, NULL)) return; /* @@ -4621,16 +4629,22 @@ uma_zfree_arg(uma_zone_t zone, void *item, void *udata) * a little longer for the limits to be reset. */ if (__predict_false(uz_flags & UMA_ZFLAG_LIMIT) && - atomic_load_32(&zone->uz_sleepers) > 0) - goto zfree_item; - - if (cache_free_item(zone, uz_flags, item, udata)) + atomic_load_32(&zone->uz_sleepers) > 0) { + /* We will free directly to the zone. */ + } +#ifdef KASAN + else if ((uz_flags & UMA_ZONE_NOKASAN) == 0) { + if (cache_free_defer(zone, item, udata)) + return; + } +#endif + else if (cache_free_reuse(zone, uz_flags, item, udata)) { return; + } /* * If nothing else caught this, we'll just do an internal free. */ -zfree_item: zone_free_item(zone, item, udata, SKIP_DTOR); }
