Skip to content

Commit 63b85ac

Browse files
xairyakpm00
authored andcommitted
kasan: stop leaking stack trace handles
Commit 773688a ("kasan: use stack_depot_put for Generic mode") added support for stack trace eviction for Generic KASAN. However, that commit didn't evict stack traces when the object is not put into quarantine. As a result, some stack traces are never evicted from the stack depot. In addition, with the "kasan: save mempool stack traces" series, the free stack traces for mempool objects are also not properly evicted from the stack depot. Fix both issues by: 1. Evicting all stack traces when an object if freed if it was not put into quarantine; 2. Always evicting an existing free stack trace when a new one is saved. Also do a few related clean-ups: - Do not zero out free track when initializing/invalidating free meta: set a value in shadow memory instead; - Rename KASAN_SLAB_FREETRACK to KASAN_SLAB_FREE_META; - Drop the kasan_init_cache_meta function as it's not used by KASAN; - Add comments for the kasan_alloc_meta and kasan_free_meta structs. [akpm@linux-foundation.org: make release_free_meta() and release_alloc_meta() static] Link: https://lkml.kernel.org/r/20231226225121.235865-1-andrey.konovalov@linux.dev Fixes: 773688a ("kasan: use stack_depot_put for Generic mode") Signed-off-by: Andrey Konovalov <andreyknvl@google.com> Cc: Alexander Potapenko <glider@google.com> Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: Marco Elver <elver@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent 7eb2d01 commit 63b85ac

File tree

5 files changed

+97
-41
lines changed

5 files changed

+97
-41
lines changed

mm/kasan/common.c

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,14 +255,33 @@ static inline bool poison_slab_object(struct kmem_cache *cache, void *object,
255255
bool __kasan_slab_free(struct kmem_cache *cache, void *object,
256256
unsigned long ip, bool init)
257257
{
258-
bool buggy_object;
259-
260258
if (is_kfence_address(object))
261259
return false;
262260

263-
buggy_object = poison_slab_object(cache, object, ip, init);
261+
/*
262+
* If the object is buggy, do not let slab put the object onto the
263+
* freelist. The object will thus never be allocated again and its
264+
* metadata will never get released.
265+
*/
266+
if (poison_slab_object(cache, object, ip, init))
267+
return true;
268+
269+
/*
270+
* If the object is put into quarantine, do not let slab put the object
271+
* onto the freelist for now. The object's metadata is kept until the
272+
* object gets evicted from quarantine.
273+
*/
274+
if (kasan_quarantine_put(cache, object))
275+
return true;
276+
277+
/*
278+
* If the object is not put into quarantine, it will likely be quickly
279+
* reallocated. Thus, release its metadata now.
280+
*/
281+
kasan_release_object_meta(cache, object);
264282

265-
return buggy_object ? true : kasan_quarantine_put(cache, object);
283+
/* Let slab put the object onto the freelist. */
284+
return false;
266285
}
267286

268287
static inline bool check_page_allocation(void *ptr, unsigned long ip)

mm/kasan/generic.c

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -480,10 +480,10 @@ struct kasan_free_meta *kasan_get_free_meta(struct kmem_cache *cache,
480480
void kasan_init_object_meta(struct kmem_cache *cache, const void *object)
481481
{
482482
struct kasan_alloc_meta *alloc_meta;
483-
struct kasan_free_meta *free_meta;
484483

485484
alloc_meta = kasan_get_alloc_meta(cache, object);
486485
if (alloc_meta) {
486+
/* Zero out alloc meta to mark it as invalid. */
487487
__memset(alloc_meta, 0, sizeof(*alloc_meta));
488488

489489
/*
@@ -495,9 +495,50 @@ void kasan_init_object_meta(struct kmem_cache *cache, const void *object)
495495
raw_spin_lock_init(&alloc_meta->aux_lock);
496496
kasan_enable_current();
497497
}
498+
499+
/*
500+
* Explicitly marking free meta as invalid is not required: the shadow
501+
* value for the first 8 bytes of a newly allocated object is not
502+
* KASAN_SLAB_FREE_META.
503+
*/
504+
}
505+
506+
static void release_alloc_meta(struct kasan_alloc_meta *meta)
507+
{
508+
/* Evict the stack traces from stack depot. */
509+
stack_depot_put(meta->alloc_track.stack);
510+
stack_depot_put(meta->aux_stack[0]);
511+
stack_depot_put(meta->aux_stack[1]);
512+
513+
/* Zero out alloc meta to mark it as invalid. */
514+
__memset(meta, 0, sizeof(*meta));
515+
}
516+
517+
static void release_free_meta(const void *object, struct kasan_free_meta *meta)
518+
{
519+
/* Check if free meta is valid. */
520+
if (*(u8 *)kasan_mem_to_shadow(object) != KASAN_SLAB_FREE_META)
521+
return;
522+
523+
/* Evict the stack trace from the stack depot. */
524+
stack_depot_put(meta->free_track.stack);
525+
526+
/* Mark free meta as invalid. */
527+
*(u8 *)kasan_mem_to_shadow(object) = KASAN_SLAB_FREE;
528+
}
529+
530+
void kasan_release_object_meta(struct kmem_cache *cache, const void *object)
531+
{
532+
struct kasan_alloc_meta *alloc_meta;
533+
struct kasan_free_meta *free_meta;
534+
535+
alloc_meta = kasan_get_alloc_meta(cache, object);
536+
if (alloc_meta)
537+
release_alloc_meta(alloc_meta);
538+
498539
free_meta = kasan_get_free_meta(cache, object);
499540
if (free_meta)
500-
__memset(free_meta, 0, sizeof(*free_meta));
541+
release_free_meta(object, free_meta);
501542
}
502543

503544
size_t kasan_metadata_size(struct kmem_cache *cache, bool in_object)
@@ -573,11 +614,8 @@ void kasan_save_alloc_info(struct kmem_cache *cache, void *object, gfp_t flags)
573614
if (!alloc_meta)
574615
return;
575616

576-
/* Evict previous stack traces (might exist for krealloc). */
577-
stack_depot_put(alloc_meta->alloc_track.stack);
578-
stack_depot_put(alloc_meta->aux_stack[0]);
579-
stack_depot_put(alloc_meta->aux_stack[1]);
580-
__memset(alloc_meta, 0, sizeof(*alloc_meta));
617+
/* Evict previous stack traces (might exist for krealloc or mempool). */
618+
release_alloc_meta(alloc_meta);
581619

582620
kasan_save_track(&alloc_meta->alloc_track, flags);
583621
}
@@ -590,7 +628,11 @@ void kasan_save_free_info(struct kmem_cache *cache, void *object)
590628
if (!free_meta)
591629
return;
592630

631+
/* Evict previous stack trace (might exist for mempool). */
632+
release_free_meta(object, free_meta);
633+
593634
kasan_save_track(&free_meta->free_track, 0);
594-
/* The object was freed and has free track set. */
595-
*(u8 *)kasan_mem_to_shadow(object) = KASAN_SLAB_FREETRACK;
635+
636+
/* Mark free meta as valid. */
637+
*(u8 *)kasan_mem_to_shadow(object) = KASAN_SLAB_FREE_META;
596638
}

mm/kasan/kasan.h

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ static inline bool kasan_requires_meta(void)
156156

157157
#ifdef CONFIG_KASAN_GENERIC
158158

159-
#define KASAN_SLAB_FREETRACK 0xFA /* freed slab object with free track */
159+
#define KASAN_SLAB_FREE_META 0xFA /* freed slab object with free meta */
160160
#define KASAN_GLOBAL_REDZONE 0xF9 /* redzone for global variable */
161161

162162
/* Stack redzone shadow values. Compiler ABI, do not change. */
@@ -253,6 +253,15 @@ struct kasan_global {
253253

254254
#ifdef CONFIG_KASAN_GENERIC
255255

256+
/*
257+
* Alloc meta contains the allocation-related information about a slab object.
258+
* Alloc meta is saved when an object is allocated and is kept until either the
259+
* object returns to the slab freelist (leaves quarantine for quarantined
260+
* objects or gets freed for the non-quarantined ones) or reallocated via
261+
* krealloc or through a mempool.
262+
* Alloc meta is stored inside of the object's redzone.
263+
* Alloc meta is considered valid whenever it contains non-zero data.
264+
*/
256265
struct kasan_alloc_meta {
257266
struct kasan_track alloc_track;
258267
/* Free track is stored in kasan_free_meta. */
@@ -278,8 +287,12 @@ struct qlist_node {
278287
#define KASAN_NO_FREE_META INT_MAX
279288

280289
/*
281-
* Free meta is only used by Generic mode while the object is in quarantine.
282-
* After that, slab allocator stores the freelist pointer in the object.
290+
* Free meta contains the freeing-related information about a slab object.
291+
* Free meta is only kept for quarantined objects and for mempool objects until
292+
* the object gets allocated again.
293+
* Free meta is stored within the object's memory.
294+
* Free meta is considered valid whenever the value of the shadow byte that
295+
* corresponds to the first 8 bytes of the object is KASAN_SLAB_FREE_META.
283296
*/
284297
struct kasan_free_meta {
285298
struct qlist_node quarantine_link;
@@ -380,15 +393,15 @@ void kasan_report_invalid_free(void *object, unsigned long ip, enum kasan_report
380393
struct slab *kasan_addr_to_slab(const void *addr);
381394

382395
#ifdef CONFIG_KASAN_GENERIC
383-
void kasan_init_cache_meta(struct kmem_cache *cache, unsigned int *size);
384-
void kasan_init_object_meta(struct kmem_cache *cache, const void *object);
385396
struct kasan_alloc_meta *kasan_get_alloc_meta(struct kmem_cache *cache,
386397
const void *object);
387398
struct kasan_free_meta *kasan_get_free_meta(struct kmem_cache *cache,
388399
const void *object);
400+
void kasan_init_object_meta(struct kmem_cache *cache, const void *object);
401+
void kasan_release_object_meta(struct kmem_cache *cache, const void *object);
389402
#else
390-
static inline void kasan_init_cache_meta(struct kmem_cache *cache, unsigned int *size) { }
391403
static inline void kasan_init_object_meta(struct kmem_cache *cache, const void *object) { }
404+
static inline void kasan_release_object_meta(struct kmem_cache *cache, const void *object) { }
392405
#endif
393406

394407
depot_stack_handle_t kasan_save_stack(gfp_t flags, depot_flags_t depot_flags);

mm/kasan/quarantine.c

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -143,22 +143,10 @@ static void *qlink_to_object(struct qlist_node *qlink, struct kmem_cache *cache)
143143
static void qlink_free(struct qlist_node *qlink, struct kmem_cache *cache)
144144
{
145145
void *object = qlink_to_object(qlink, cache);
146-
struct kasan_alloc_meta *alloc_meta = kasan_get_alloc_meta(cache, object);
147146
struct kasan_free_meta *free_meta = kasan_get_free_meta(cache, object);
148147
unsigned long flags;
149148

150-
if (alloc_meta) {
151-
stack_depot_put(alloc_meta->alloc_track.stack);
152-
stack_depot_put(alloc_meta->aux_stack[0]);
153-
stack_depot_put(alloc_meta->aux_stack[1]);
154-
__memset(alloc_meta, 0, sizeof(*alloc_meta));
155-
}
156-
157-
if (free_meta &&
158-
*(u8 *)kasan_mem_to_shadow(object) == KASAN_SLAB_FREETRACK) {
159-
stack_depot_put(free_meta->free_track.stack);
160-
__memset(&free_meta->free_track, 0, sizeof(free_meta->free_track));
161-
}
149+
kasan_release_object_meta(cache, object);
162150

163151
/*
164152
* If init_on_free is enabled and KASAN's free metadata is stored in
@@ -170,12 +158,6 @@ static void qlink_free(struct qlist_node *qlink, struct kmem_cache *cache)
170158
cache->kasan_info.free_meta_offset == 0)
171159
memzero_explicit(free_meta, sizeof(*free_meta));
172160

173-
/*
174-
* As the object now gets freed from the quarantine,
175-
* take note that its free track is no longer exists.
176-
*/
177-
*(u8 *)kasan_mem_to_shadow(object) = KASAN_SLAB_FREE;
178-
179161
if (IS_ENABLED(CONFIG_SLAB))
180162
local_irq_save(flags);
181163

mm/kasan/report_generic.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ static const char *get_shadow_bug_type(struct kasan_report_info *info)
110110
bug_type = "use-after-free";
111111
break;
112112
case KASAN_SLAB_FREE:
113-
case KASAN_SLAB_FREETRACK:
113+
case KASAN_SLAB_FREE_META:
114114
bug_type = "slab-use-after-free";
115115
break;
116116
case KASAN_ALLOCA_LEFT:
@@ -173,8 +173,8 @@ void kasan_complete_mode_report_info(struct kasan_report_info *info)
173173
memcpy(&info->alloc_track, &alloc_meta->alloc_track,
174174
sizeof(info->alloc_track));
175175

176-
if (*(u8 *)kasan_mem_to_shadow(info->object) == KASAN_SLAB_FREETRACK) {
177-
/* Free meta must be present with KASAN_SLAB_FREETRACK. */
176+
if (*(u8 *)kasan_mem_to_shadow(info->object) == KASAN_SLAB_FREE_META) {
177+
/* Free meta must be present with KASAN_SLAB_FREE_META. */
178178
free_meta = kasan_get_free_meta(info->cache, info->object);
179179
memcpy(&info->free_track, &free_meta->free_track,
180180
sizeof(info->free_track));

0 commit comments

Comments
 (0)