Skip to content

Commit db5216c

Browse files
dtccccopsiff
authored andcommitted
kfence: save freeing stack trace at calling time instead of freeing time
commit c36be0c upstream. For kmem_cache with SLAB_TYPESAFE_BY_RCU, the freeing trace stack at calling kmem_cache_free() is more useful. While the following stack is meaningless and provides no help: freed by task 46 on cpu 0 at 656.840729s: rcu_do_batch+0x1ab/0x540 nocb_cb_wait+0x8f/0x260 rcu_nocb_cb_kthread+0x25/0x80 kthread+0xd2/0x100 ret_from_fork+0x34/0x50 ret_from_fork_asm+0x1a/0x30 Link: https://lkml.kernel.org/r/20240812095517.2357-1-dtcccc@linux.alibaba.com Signed-off-by: Tianchen Ding <dtcccc@linux.alibaba.com> Reviewed-by: Marco Elver <elver@google.com> Cc: Alexander Potapenko <glider@google.com> Cc: Dmitry Vyukov <dvyukov@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> [ Backport from v6.12-rc1 ] Link: https://gitee.com/anolis/cloud-kernel/pulls/3901 Signed-off-by: WangYuli <wangyuli@uniontech.com>
1 parent 349c642 commit db5216c

3 files changed

Lines changed: 36 additions & 13 deletions

File tree

mm/kfence/core.c

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,13 @@ static inline unsigned long metadata_to_pageaddr(const struct kfence_metadata *m
270270
return pageaddr;
271271
}
272272

273+
static inline bool kfence_obj_allocated(const struct kfence_metadata *meta)
274+
{
275+
enum kfence_object_state state = READ_ONCE(meta->state);
276+
277+
return state == KFENCE_OBJECT_ALLOCATED || state == KFENCE_OBJECT_RCU_FREEING;
278+
}
279+
273280
/*
274281
* Update the object's metadata state, including updating the alloc/free stacks
275282
* depending on the state transition.
@@ -279,7 +286,13 @@ metadata_update_state(struct kfence_metadata *meta, enum kfence_object_state nex
279286
unsigned long *stack_entries, size_t num_stack_entries)
280287
{
281288
struct kfence_track *track =
282-
next == KFENCE_OBJECT_FREED ? &meta->free_track : &meta->alloc_track;
289+
next == KFENCE_OBJECT_ALLOCATED ? &meta->alloc_track : &meta->free_track;
290+
291+
lockdep_assert_held(&meta->lock);
292+
293+
/* Stack has been saved when calling rcu, skip. */
294+
if (READ_ONCE(meta->state) == KFENCE_OBJECT_RCU_FREEING)
295+
goto out;
283296

284297
lockdep_assert_held(&meta->lock);
285298

@@ -298,6 +311,7 @@ metadata_update_state(struct kfence_metadata *meta, enum kfence_object_state nex
298311
track->cpu = raw_smp_processor_id();
299312
track->ts_nsec = local_clock(); /* Same source as printk timestamps. */
300313

314+
out:
301315
/*
302316
* Pairs with READ_ONCE() in
303317
* kfence_shutdown_cache(),
@@ -500,7 +514,7 @@ static void kfence_guarded_free(void *addr, struct kfence_metadata *meta, bool z
500514

501515
raw_spin_lock_irqsave(&meta->lock, flags);
502516

503-
if (meta->state != KFENCE_OBJECT_ALLOCATED || meta->addr != (unsigned long)addr) {
517+
if (!kfence_obj_allocated(meta) || meta->addr != (unsigned long)addr) {
504518
/* Invalid or double-free, bail out. */
505519
atomic_long_inc(&counters[KFENCE_COUNTER_BUGS]);
506520
kfence_report_error((unsigned long)addr, false, NULL, meta,
@@ -778,7 +792,7 @@ static void kfence_check_all_canary(void)
778792
for (i = 0; i < CONFIG_KFENCE_NUM_OBJECTS; i++) {
779793
struct kfence_metadata *meta = &kfence_metadata[i];
780794

781-
if (meta->state == KFENCE_OBJECT_ALLOCATED)
795+
if (kfence_obj_allocated(meta))
782796
check_canary(meta);
783797
}
784798
}
@@ -1004,12 +1018,11 @@ void kfence_shutdown_cache(struct kmem_cache *s)
10041018
* the lock will not help, as different critical section
10051019
* serialization will have the same outcome.
10061020
*/
1007-
if (READ_ONCE(meta->cache) != s ||
1008-
READ_ONCE(meta->state) != KFENCE_OBJECT_ALLOCATED)
1021+
if (READ_ONCE(meta->cache) != s || !kfence_obj_allocated(meta))
10091022
continue;
10101023

10111024
raw_spin_lock_irqsave(&meta->lock, flags);
1012-
in_use = meta->cache == s && meta->state == KFENCE_OBJECT_ALLOCATED;
1025+
in_use = meta->cache == s && kfence_obj_allocated(meta);
10131026
raw_spin_unlock_irqrestore(&meta->lock, flags);
10141027

10151028
if (in_use) {
@@ -1153,11 +1166,19 @@ void __kfence_free(void *addr)
11531166
* the object, as the object page may be recycled for other-typed
11541167
* objects once it has been freed. meta->cache may be NULL if the cache
11551168
* was destroyed.
1169+
* Save the stack trace here so that reports show where the user freed
1170+
* the object.
11561171
*/
1157-
if (unlikely(meta->cache && (meta->cache->flags & SLAB_TYPESAFE_BY_RCU)))
1172+
if (unlikely(meta->cache && (meta->cache->flags & SLAB_TYPESAFE_BY_RCU))) {
1173+
unsigned long flags;
1174+
1175+
raw_spin_lock_irqsave(&meta->lock, flags);
1176+
metadata_update_state(meta, KFENCE_OBJECT_RCU_FREEING, NULL, 0);
1177+
raw_spin_unlock_irqrestore(&meta->lock, flags);
11581178
call_rcu(&meta->rcu_head, rcu_guarded_free);
1159-
else
1179+
} else {
11601180
kfence_guarded_free(addr, meta, false);
1181+
}
11611182
}
11621183

11631184
bool kfence_handle_page_fault(unsigned long addr, bool is_write, struct pt_regs *regs)
@@ -1181,14 +1202,14 @@ bool kfence_handle_page_fault(unsigned long addr, bool is_write, struct pt_regs
11811202
int distance = 0;
11821203

11831204
meta = addr_to_metadata(addr - PAGE_SIZE);
1184-
if (meta && READ_ONCE(meta->state) == KFENCE_OBJECT_ALLOCATED) {
1205+
if (meta && kfence_obj_allocated(meta)) {
11851206
to_report = meta;
11861207
/* Data race ok; distance calculation approximate. */
11871208
distance = addr - data_race(meta->addr + meta->size);
11881209
}
11891210

11901211
meta = addr_to_metadata(addr + PAGE_SIZE);
1191-
if (meta && READ_ONCE(meta->state) == KFENCE_OBJECT_ALLOCATED) {
1212+
if (meta && kfence_obj_allocated(meta)) {
11921213
/* Data race ok; distance calculation approximate. */
11931214
if (!to_report || distance > data_race(meta->addr) - addr)
11941215
to_report = meta;

mm/kfence/kfence.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
enum kfence_object_state {
3939
KFENCE_OBJECT_UNUSED, /* Object is unused. */
4040
KFENCE_OBJECT_ALLOCATED, /* Object is currently allocated. */
41+
KFENCE_OBJECT_RCU_FREEING, /* Object was allocated, and then being freed by rcu. */
4142
KFENCE_OBJECT_FREED, /* Object was allocated, and then freed. */
4243
};
4344

mm/kfence/report.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ static void kfence_print_stack(struct seq_file *seq, const struct kfence_metadat
111111

112112
/* Timestamp matches printk timestamp format. */
113113
seq_con_printf(seq, "%s by task %d on cpu %d at %lu.%06lus:\n",
114-
show_alloc ? "allocated" : "freed", track->pid,
114+
show_alloc ? "allocated" : meta->state == KFENCE_OBJECT_RCU_FREEING ?
115+
"rcu freeing" : "freed", track->pid,
115116
track->cpu, (unsigned long)ts_sec, rem_nsec / 1000);
116117

117118
if (track->num_stack_entries) {
@@ -145,7 +146,7 @@ void kfence_print_object(struct seq_file *seq, const struct kfence_metadata *met
145146

146147
kfence_print_stack(seq, meta, true);
147148

148-
if (meta->state == KFENCE_OBJECT_FREED) {
149+
if (meta->state == KFENCE_OBJECT_FREED || meta->state == KFENCE_OBJECT_RCU_FREEING) {
149150
seq_con_printf(seq, "\n");
150151
kfence_print_stack(seq, meta, false);
151152
}
@@ -314,7 +315,7 @@ bool __kfence_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *sla
314315
kpp->kp_slab_cache = meta->cache;
315316
kpp->kp_objp = (void *)meta->addr;
316317
kfence_to_kp_stack(&meta->alloc_track, kpp->kp_stack);
317-
if (meta->state == KFENCE_OBJECT_FREED)
318+
if (meta->state == KFENCE_OBJECT_FREED || meta->state == KFENCE_OBJECT_RCU_FREEING)
318319
kfence_to_kp_stack(&meta->free_track, kpp->kp_free_stack);
319320
/* get_stack_skipnr() ensures the first entry is outside allocator. */
320321
kpp->kp_ret = kpp->kp_stack[0];

0 commit comments

Comments
 (0)