Skip to content

Commit b8c8ba7

Browse files
thejhtehcaster
authored andcommitted
slub: Introduce CONFIG_SLUB_RCU_DEBUG
Currently, KASAN is unable to catch use-after-free in SLAB_TYPESAFE_BY_RCU slabs because use-after-free is allowed within the RCU grace period by design. Add a SLUB debugging feature which RCU-delays every individual kmem_cache_free() before either actually freeing the object or handing it off to KASAN, and change KASAN to poison freed objects as normal when this option is enabled. For now I've configured Kconfig.debug to default-enable this feature in the KASAN GENERIC and SW_TAGS modes; I'm not enabling it by default in HW_TAGS mode because I'm not sure if it might have unwanted performance degradation effects there. Note that this is mostly useful with KASAN in the quarantine-based GENERIC mode; SLAB_TYPESAFE_BY_RCU slabs are basically always also slabs with a ->ctor, and KASAN's assign_tag() currently has to assign fixed tags for those, reducing the effectiveness of SW_TAGS/HW_TAGS mode. (A possible future extension of this work would be to also let SLUB call the ->ctor() on every allocation instead of only when the slab page is allocated; then tag-based modes would be able to assign new tags on every reallocation.) Tested-by: [email protected] Reviewed-by: Andrey Konovalov <[email protected]> Acked-by: Marco Elver <[email protected]> Acked-by: Vlastimil Babka <[email protected]> #slab Signed-off-by: Jann Horn <[email protected]> Signed-off-by: Vlastimil Babka <[email protected]>
1 parent b3c3424 commit b8c8ba7

File tree

6 files changed

+182
-19
lines changed

6 files changed

+182
-19
lines changed

include/linux/kasan.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,15 +196,18 @@ static __always_inline bool kasan_slab_pre_free(struct kmem_cache *s,
196196
return false;
197197
}
198198

199-
bool __kasan_slab_free(struct kmem_cache *s, void *object, bool init);
199+
bool __kasan_slab_free(struct kmem_cache *s, void *object, bool init,
200+
bool still_accessible);
200201
/**
201202
* kasan_slab_free - Poison, initialize, and quarantine a slab object.
202203
* @object: Object to be freed.
203204
* @init: Whether to initialize the object.
205+
* @still_accessible: Whether the object contents are still accessible.
204206
*
205207
* This function informs that a slab object has been freed and is not
206-
* supposed to be accessed anymore, except for objects in
207-
* SLAB_TYPESAFE_BY_RCU caches.
208+
* supposed to be accessed anymore, except when @still_accessible is set
209+
* (indicating that the object is in a SLAB_TYPESAFE_BY_RCU cache and an RCU
210+
* grace period might not have passed yet).
208211
*
209212
* For KASAN modes that have integrated memory initialization
210213
* (kasan_has_integrated_init() == true), this function also initializes
@@ -220,10 +223,11 @@ bool __kasan_slab_free(struct kmem_cache *s, void *object, bool init);
220223
* @Return true if KASAN took ownership of the object; false otherwise.
221224
*/
222225
static __always_inline bool kasan_slab_free(struct kmem_cache *s,
223-
void *object, bool init)
226+
void *object, bool init,
227+
bool still_accessible)
224228
{
225229
if (kasan_enabled())
226-
return __kasan_slab_free(s, object, init);
230+
return __kasan_slab_free(s, object, init, still_accessible);
227231
return false;
228232
}
229233

@@ -419,7 +423,8 @@ static inline bool kasan_slab_pre_free(struct kmem_cache *s, void *object)
419423
return false;
420424
}
421425

422-
static inline bool kasan_slab_free(struct kmem_cache *s, void *object, bool init)
426+
static inline bool kasan_slab_free(struct kmem_cache *s, void *object,
427+
bool init, bool still_accessible)
423428
{
424429
return false;
425430
}

mm/Kconfig.debug

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,38 @@ config SLUB_DEBUG_ON
7070
off in a kernel built with CONFIG_SLUB_DEBUG_ON by specifying
7171
"slab_debug=-".
7272

73+
config SLUB_RCU_DEBUG
74+
bool "Enable UAF detection in TYPESAFE_BY_RCU caches (for KASAN)"
75+
depends on SLUB_DEBUG
76+
# SLUB_RCU_DEBUG should build fine without KASAN, but is currently useless
77+
# without KASAN, so mark it as a dependency of KASAN for now.
78+
depends on KASAN
79+
default KASAN_GENERIC || KASAN_SW_TAGS
80+
help
81+
Make SLAB_TYPESAFE_BY_RCU caches behave approximately as if the cache
82+
was not marked as SLAB_TYPESAFE_BY_RCU and every caller used
83+
kfree_rcu() instead.
84+
85+
This is intended for use in combination with KASAN, to enable KASAN to
86+
detect use-after-free accesses in such caches.
87+
(KFENCE is able to do that independent of this flag.)
88+
89+
This might degrade performance.
90+
Unfortunately this also prevents a very specific bug pattern from
91+
triggering (insufficient checks against an object being recycled
92+
within the RCU grace period); so this option can be turned off even on
93+
KASAN builds, in case you want to test for such a bug.
94+
95+
If you're using this for testing bugs / fuzzing and care about
96+
catching all the bugs WAY more than performance, you might want to
97+
also turn on CONFIG_RCU_STRICT_GRACE_PERIOD.
98+
99+
WARNING:
100+
This is designed as a debugging feature, not a security feature.
101+
Objects are sometimes recycled without RCU delay under memory pressure.
102+
103+
If unsure, say N.
104+
73105
config PAGE_OWNER
74106
bool "Track page owner"
75107
depends on DEBUG_KERNEL && STACKTRACE_SUPPORT

mm/kasan/common.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,14 +230,14 @@ static bool check_slab_allocation(struct kmem_cache *cache, void *object,
230230
}
231231

232232
static inline void poison_slab_object(struct kmem_cache *cache, void *object,
233-
bool init)
233+
bool init, bool still_accessible)
234234
{
235235
void *tagged_object = object;
236236

237237
object = kasan_reset_tag(object);
238238

239239
/* RCU slabs could be legally used after free within the RCU period. */
240-
if (unlikely(cache->flags & SLAB_TYPESAFE_BY_RCU))
240+
if (unlikely(still_accessible))
241241
return;
242242

243243
kasan_poison(object, round_up(cache->object_size, KASAN_GRANULE_SIZE),
@@ -255,12 +255,13 @@ bool __kasan_slab_pre_free(struct kmem_cache *cache, void *object,
255255
return check_slab_allocation(cache, object, ip);
256256
}
257257

258-
bool __kasan_slab_free(struct kmem_cache *cache, void *object, bool init)
258+
bool __kasan_slab_free(struct kmem_cache *cache, void *object, bool init,
259+
bool still_accessible)
259260
{
260261
if (!kasan_arch_is_ready() || is_kfence_address(object))
261262
return false;
262263

263-
poison_slab_object(cache, object, init);
264+
poison_slab_object(cache, object, init, still_accessible);
264265

265266
/*
266267
* If the object is put into quarantine, do not let slab put the object
@@ -518,7 +519,7 @@ bool __kasan_mempool_poison_object(void *ptr, unsigned long ip)
518519
if (check_slab_allocation(slab->slab_cache, ptr, ip))
519520
return false;
520521

521-
poison_slab_object(slab->slab_cache, ptr, false);
522+
poison_slab_object(slab->slab_cache, ptr, false, false);
522523
return true;
523524
}
524525

mm/kasan/kasan_test.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,51 @@ static void kmem_cache_invalid_free(struct kunit *test)
996996
kmem_cache_destroy(cache);
997997
}
998998

999+
static void kmem_cache_rcu_uaf(struct kunit *test)
1000+
{
1001+
char *p;
1002+
size_t size = 200;
1003+
struct kmem_cache *cache;
1004+
1005+
KASAN_TEST_NEEDS_CONFIG_ON(test, CONFIG_SLUB_RCU_DEBUG);
1006+
1007+
cache = kmem_cache_create("test_cache", size, 0, SLAB_TYPESAFE_BY_RCU,
1008+
NULL);
1009+
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cache);
1010+
1011+
p = kmem_cache_alloc(cache, GFP_KERNEL);
1012+
if (!p) {
1013+
kunit_err(test, "Allocation failed: %s\n", __func__);
1014+
kmem_cache_destroy(cache);
1015+
return;
1016+
}
1017+
*p = 1;
1018+
1019+
rcu_read_lock();
1020+
1021+
/* Free the object - this will internally schedule an RCU callback. */
1022+
kmem_cache_free(cache, p);
1023+
1024+
/*
1025+
* We should still be allowed to access the object at this point because
1026+
* the cache is SLAB_TYPESAFE_BY_RCU and we've been in an RCU read-side
1027+
* critical section since before the kmem_cache_free().
1028+
*/
1029+
READ_ONCE(*p);
1030+
1031+
rcu_read_unlock();
1032+
1033+
/*
1034+
* Wait for the RCU callback to execute; after this, the object should
1035+
* have actually been freed from KASAN's perspective.
1036+
*/
1037+
rcu_barrier();
1038+
1039+
KUNIT_EXPECT_KASAN_FAIL(test, READ_ONCE(*p));
1040+
1041+
kmem_cache_destroy(cache);
1042+
}
1043+
9991044
static void empty_cache_ctor(void *object) { }
10001045

10011046
static void kmem_cache_double_destroy(struct kunit *test)
@@ -1937,6 +1982,7 @@ static struct kunit_case kasan_kunit_test_cases[] = {
19371982
KUNIT_CASE(kmem_cache_oob),
19381983
KUNIT_CASE(kmem_cache_double_free),
19391984
KUNIT_CASE(kmem_cache_invalid_free),
1985+
KUNIT_CASE(kmem_cache_rcu_uaf),
19401986
KUNIT_CASE(kmem_cache_double_destroy),
19411987
KUNIT_CASE(kmem_cache_accounted),
19421988
KUNIT_CASE(kmem_cache_bulk),

mm/slab_common.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,22 @@ void kmem_cache_destroy(struct kmem_cache *s)
511511
/* in-flight kfree_rcu()'s may include objects from our cache */
512512
kvfree_rcu_barrier();
513513

514+
if (IS_ENABLED(CONFIG_SLUB_RCU_DEBUG) &&
515+
(s->flags & SLAB_TYPESAFE_BY_RCU)) {
516+
/*
517+
* Under CONFIG_SLUB_RCU_DEBUG, when objects in a
518+
* SLAB_TYPESAFE_BY_RCU slab are freed, SLUB will internally
519+
* defer their freeing with call_rcu().
520+
* Wait for such call_rcu() invocations here before actually
521+
* destroying the cache.
522+
*
523+
* It doesn't matter that we haven't looked at the slab refcount
524+
* yet - slabs with SLAB_TYPESAFE_BY_RCU can't be merged, so
525+
* the refcount should be 1 here.
526+
*/
527+
rcu_barrier();
528+
}
529+
514530
cpus_read_lock();
515531
mutex_lock(&slab_mutex);
516532

mm/slub.c

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2200,16 +2200,30 @@ static inline void memcg_slab_free_hook(struct kmem_cache *s, struct slab *slab,
22002200
}
22012201
#endif /* CONFIG_MEMCG */
22022202

2203+
#ifdef CONFIG_SLUB_RCU_DEBUG
2204+
static void slab_free_after_rcu_debug(struct rcu_head *rcu_head);
2205+
2206+
struct rcu_delayed_free {
2207+
struct rcu_head head;
2208+
void *object;
2209+
};
2210+
#endif
2211+
22032212
/*
22042213
* Hooks for other subsystems that check memory allocations. In a typical
22052214
* production configuration these hooks all should produce no code at all.
22062215
*
22072216
* Returns true if freeing of the object can proceed, false if its reuse
2208-
* was delayed by KASAN quarantine, or it was returned to KFENCE.
2217+
* was delayed by CONFIG_SLUB_RCU_DEBUG or KASAN quarantine, or it was returned
2218+
* to KFENCE.
22092219
*/
22102220
static __always_inline
2211-
bool slab_free_hook(struct kmem_cache *s, void *x, bool init)
2221+
bool slab_free_hook(struct kmem_cache *s, void *x, bool init,
2222+
bool after_rcu_delay)
22122223
{
2224+
/* Are the object contents still accessible? */
2225+
bool still_accessible = (s->flags & SLAB_TYPESAFE_BY_RCU) && !after_rcu_delay;
2226+
22132227
kmemleak_free_recursive(x, s->flags);
22142228
kmsan_slab_free(s, x);
22152229

@@ -2219,7 +2233,7 @@ bool slab_free_hook(struct kmem_cache *s, void *x, bool init)
22192233
debug_check_no_obj_freed(x, s->object_size);
22202234

22212235
/* Use KCSAN to help debug racy use-after-free. */
2222-
if (!(s->flags & SLAB_TYPESAFE_BY_RCU))
2236+
if (!still_accessible)
22232237
__kcsan_check_access(x, s->object_size,
22242238
KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ASSERT);
22252239

@@ -2233,6 +2247,28 @@ bool slab_free_hook(struct kmem_cache *s, void *x, bool init)
22332247
if (kasan_slab_pre_free(s, x))
22342248
return false;
22352249

2250+
#ifdef CONFIG_SLUB_RCU_DEBUG
2251+
if (still_accessible) {
2252+
struct rcu_delayed_free *delayed_free;
2253+
2254+
delayed_free = kmalloc(sizeof(*delayed_free), GFP_NOWAIT);
2255+
if (delayed_free) {
2256+
/*
2257+
* Let KASAN track our call stack as a "related work
2258+
* creation", just like if the object had been freed
2259+
* normally via kfree_rcu().
2260+
* We have to do this manually because the rcu_head is
2261+
* not located inside the object.
2262+
*/
2263+
kasan_record_aux_stack_noalloc(x);
2264+
2265+
delayed_free->object = x;
2266+
call_rcu(&delayed_free->head, slab_free_after_rcu_debug);
2267+
return false;
2268+
}
2269+
}
2270+
#endif /* CONFIG_SLUB_RCU_DEBUG */
2271+
22362272
/*
22372273
* As memory initialization might be integrated into KASAN,
22382274
* kasan_slab_free and initialization memset's must be
@@ -2256,7 +2292,7 @@ bool slab_free_hook(struct kmem_cache *s, void *x, bool init)
22562292
s->size - inuse - rsize);
22572293
}
22582294
/* KASAN might put x into memory quarantine, delaying its reuse. */
2259-
return !kasan_slab_free(s, x, init);
2295+
return !kasan_slab_free(s, x, init, still_accessible);
22602296
}
22612297

22622298
static __fastpath_inline
@@ -2270,7 +2306,7 @@ bool slab_free_freelist_hook(struct kmem_cache *s, void **head, void **tail,
22702306
bool init;
22712307

22722308
if (is_kfence_address(next)) {
2273-
slab_free_hook(s, next, false);
2309+
slab_free_hook(s, next, false, false);
22742310
return false;
22752311
}
22762312

@@ -2285,7 +2321,7 @@ bool slab_free_freelist_hook(struct kmem_cache *s, void **head, void **tail,
22852321
next = get_freepointer(s, object);
22862322

22872323
/* If object's reuse doesn't have to be delayed */
2288-
if (likely(slab_free_hook(s, object, init))) {
2324+
if (likely(slab_free_hook(s, object, init, false))) {
22892325
/* Move object to the new freelist */
22902326
set_freepointer(s, object, *head);
22912327
*head = object;
@@ -4477,7 +4513,7 @@ void slab_free(struct kmem_cache *s, struct slab *slab, void *object,
44774513
memcg_slab_free_hook(s, slab, &object, 1);
44784514
alloc_tagging_slab_free_hook(s, slab, &object, 1);
44794515

4480-
if (likely(slab_free_hook(s, object, slab_want_init_on_free(s))))
4516+
if (likely(slab_free_hook(s, object, slab_want_init_on_free(s), false)))
44814517
do_slab_free(s, slab, object, object, 1, addr);
44824518
}
44834519

@@ -4486,7 +4522,7 @@ void slab_free(struct kmem_cache *s, struct slab *slab, void *object,
44864522
static noinline
44874523
void memcg_alloc_abort_single(struct kmem_cache *s, void *object)
44884524
{
4489-
if (likely(slab_free_hook(s, object, slab_want_init_on_free(s))))
4525+
if (likely(slab_free_hook(s, object, slab_want_init_on_free(s), false)))
44904526
do_slab_free(s, virt_to_slab(object), object, object, 1, _RET_IP_);
44914527
}
44924528
#endif
@@ -4505,6 +4541,33 @@ void slab_free_bulk(struct kmem_cache *s, struct slab *slab, void *head,
45054541
do_slab_free(s, slab, head, tail, cnt, addr);
45064542
}
45074543

4544+
#ifdef CONFIG_SLUB_RCU_DEBUG
4545+
static void slab_free_after_rcu_debug(struct rcu_head *rcu_head)
4546+
{
4547+
struct rcu_delayed_free *delayed_free =
4548+
container_of(rcu_head, struct rcu_delayed_free, head);
4549+
void *object = delayed_free->object;
4550+
struct slab *slab = virt_to_slab(object);
4551+
struct kmem_cache *s;
4552+
4553+
kfree(delayed_free);
4554+
4555+
if (WARN_ON(is_kfence_address(object)))
4556+
return;
4557+
4558+
/* find the object and the cache again */
4559+
if (WARN_ON(!slab))
4560+
return;
4561+
s = slab->slab_cache;
4562+
if (WARN_ON(!(s->flags & SLAB_TYPESAFE_BY_RCU)))
4563+
return;
4564+
4565+
/* resume freeing */
4566+
if (slab_free_hook(s, object, slab_want_init_on_free(s), true))
4567+
do_slab_free(s, slab, object, object, 1, _THIS_IP_);
4568+
}
4569+
#endif /* CONFIG_SLUB_RCU_DEBUG */
4570+
45084571
#ifdef CONFIG_KASAN_GENERIC
45094572
void ___cache_free(struct kmem_cache *cache, void *x, unsigned long addr)
45104573
{

0 commit comments

Comments
 (0)