Skip to content

Commit 2dfe63e

Browse files
melvertorvalds
authored andcommitted
mm, kfence: support kmem_dump_obj() for KFENCE objects
Calling kmem_obj_info() via kmem_dump_obj() on KFENCE objects has been producing garbage data due to the object not actually being maintained by SLAB or SLUB. Fix this by implementing __kfence_obj_info() that copies relevant information to struct kmem_obj_info when the object was allocated by KFENCE; this is called by a common kmem_obj_info(), which also calls the slab/slub/slob specific variant now called __kmem_obj_info(). For completeness, kmem_dump_obj() now displays if the object was allocated by KFENCE. Link: https://lore.kernel.org/all/20220323090520.GG16885@xsang-OptiPlex-9020/ Link: https://lkml.kernel.org/r/20220406131558.3558585-1-elver@google.com Fixes: b89fb5e ("mm, kfence: insert KFENCE hooks for SLUB") Fixes: d3fb45f ("mm, kfence: insert KFENCE hooks for SLAB") Signed-off-by: Marco Elver <elver@google.com> Reviewed-by: Hyeonggon Yoo <42.hyeyoo@gmail.com> Reported-by: kernel test robot <oliver.sang@intel.com> Acked-by: Vlastimil Babka <vbabka@suse.cz> [slab] Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent b1add41 commit 2dfe63e

9 files changed

Lines changed: 105 additions & 25 deletions

File tree

include/linux/kfence.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,22 @@ static __always_inline __must_check bool kfence_free(void *addr)
204204
*/
205205
bool __must_check kfence_handle_page_fault(unsigned long addr, bool is_write, struct pt_regs *regs);
206206

207+
#ifdef CONFIG_PRINTK
208+
struct kmem_obj_info;
209+
/**
210+
* __kfence_obj_info() - fill kmem_obj_info struct
211+
* @kpp: kmem_obj_info to be filled
212+
* @object: the object
213+
*
214+
* Return:
215+
* * false - not a KFENCE object
216+
* * true - a KFENCE object, filled @kpp
217+
*
218+
* Copies information to @kpp for KFENCE objects.
219+
*/
220+
bool __kfence_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab);
221+
#endif
222+
207223
#else /* CONFIG_KFENCE */
208224

209225
static inline bool is_kfence_address(const void *addr) { return false; }
@@ -221,6 +237,14 @@ static inline bool __must_check kfence_handle_page_fault(unsigned long addr, boo
221237
return false;
222238
}
223239

240+
#ifdef CONFIG_PRINTK
241+
struct kmem_obj_info;
242+
static inline bool __kfence_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab)
243+
{
244+
return false;
245+
}
246+
#endif
247+
224248
#endif
225249

226250
#endif /* _LINUX_KFENCE_H */

mm/kfence/core.c

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -231,27 +231,6 @@ static bool kfence_unprotect(unsigned long addr)
231231
return !KFENCE_WARN_ON(!kfence_protect_page(ALIGN_DOWN(addr, PAGE_SIZE), false));
232232
}
233233

234-
static inline struct kfence_metadata *addr_to_metadata(unsigned long addr)
235-
{
236-
long index;
237-
238-
/* The checks do not affect performance; only called from slow-paths. */
239-
240-
if (!is_kfence_address((void *)addr))
241-
return NULL;
242-
243-
/*
244-
* May be an invalid index if called with an address at the edge of
245-
* __kfence_pool, in which case we would report an "invalid access"
246-
* error.
247-
*/
248-
index = (addr - (unsigned long)__kfence_pool) / (PAGE_SIZE * 2) - 1;
249-
if (index < 0 || index >= CONFIG_KFENCE_NUM_OBJECTS)
250-
return NULL;
251-
252-
return &kfence_metadata[index];
253-
}
254-
255234
static inline unsigned long metadata_to_pageaddr(const struct kfence_metadata *meta)
256235
{
257236
unsigned long offset = (meta - kfence_metadata + 1) * PAGE_SIZE * 2;

mm/kfence/kfence.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,27 @@ struct kfence_metadata {
9696

9797
extern struct kfence_metadata kfence_metadata[CONFIG_KFENCE_NUM_OBJECTS];
9898

99+
static inline struct kfence_metadata *addr_to_metadata(unsigned long addr)
100+
{
101+
long index;
102+
103+
/* The checks do not affect performance; only called from slow-paths. */
104+
105+
if (!is_kfence_address((void *)addr))
106+
return NULL;
107+
108+
/*
109+
* May be an invalid index if called with an address at the edge of
110+
* __kfence_pool, in which case we would report an "invalid access"
111+
* error.
112+
*/
113+
index = (addr - (unsigned long)__kfence_pool) / (PAGE_SIZE * 2) - 1;
114+
if (index < 0 || index >= CONFIG_KFENCE_NUM_OBJECTS)
115+
return NULL;
116+
117+
return &kfence_metadata[index];
118+
}
119+
99120
/* KFENCE error types for report generation. */
100121
enum kfence_error_type {
101122
KFENCE_ERROR_OOB, /* Detected a out-of-bounds access. */

mm/kfence/report.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,50 @@ void kfence_report_error(unsigned long address, bool is_write, struct pt_regs *r
273273
/* We encountered a memory safety error, taint the kernel! */
274274
add_taint(TAINT_BAD_PAGE, LOCKDEP_STILL_OK);
275275
}
276+
277+
#ifdef CONFIG_PRINTK
278+
static void kfence_to_kp_stack(const struct kfence_track *track, void **kp_stack)
279+
{
280+
int i, j;
281+
282+
i = get_stack_skipnr(track->stack_entries, track->num_stack_entries, NULL);
283+
for (j = 0; i < track->num_stack_entries && j < KS_ADDRS_COUNT; ++i, ++j)
284+
kp_stack[j] = (void *)track->stack_entries[i];
285+
if (j < KS_ADDRS_COUNT)
286+
kp_stack[j] = NULL;
287+
}
288+
289+
bool __kfence_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab)
290+
{
291+
struct kfence_metadata *meta = addr_to_metadata((unsigned long)object);
292+
unsigned long flags;
293+
294+
if (!meta)
295+
return false;
296+
297+
/*
298+
* If state is UNUSED at least show the pointer requested; the rest
299+
* would be garbage data.
300+
*/
301+
kpp->kp_ptr = object;
302+
303+
/* Requesting info an a never-used object is almost certainly a bug. */
304+
if (WARN_ON(meta->state == KFENCE_OBJECT_UNUSED))
305+
return true;
306+
307+
raw_spin_lock_irqsave(&meta->lock, flags);
308+
309+
kpp->kp_slab = slab;
310+
kpp->kp_slab_cache = meta->cache;
311+
kpp->kp_objp = (void *)meta->addr;
312+
kfence_to_kp_stack(&meta->alloc_track, kpp->kp_stack);
313+
if (meta->state == KFENCE_OBJECT_FREED)
314+
kfence_to_kp_stack(&meta->free_track, kpp->kp_free_stack);
315+
/* get_stack_skipnr() ensures the first entry is outside allocator. */
316+
kpp->kp_ret = kpp->kp_stack[0];
317+
318+
raw_spin_unlock_irqrestore(&meta->lock, flags);
319+
320+
return true;
321+
}
322+
#endif

mm/slab.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3665,7 +3665,7 @@ EXPORT_SYMBOL(__kmalloc_node_track_caller);
36653665
#endif /* CONFIG_NUMA */
36663666

36673667
#ifdef CONFIG_PRINTK
3668-
void kmem_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab)
3668+
void __kmem_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab)
36693669
{
36703670
struct kmem_cache *cachep;
36713671
unsigned int objnr;

mm/slab.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,7 @@ struct kmem_obj_info {
868868
void *kp_stack[KS_ADDRS_COUNT];
869869
void *kp_free_stack[KS_ADDRS_COUNT];
870870
};
871-
void kmem_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab);
871+
void __kmem_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab);
872872
#endif
873873

874874
#ifdef CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR

mm/slab_common.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,13 @@ bool kmem_valid_obj(void *object)
555555
}
556556
EXPORT_SYMBOL_GPL(kmem_valid_obj);
557557

558+
static void kmem_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab)
559+
{
560+
if (__kfence_obj_info(kpp, object, slab))
561+
return;
562+
__kmem_obj_info(kpp, object, slab);
563+
}
564+
558565
/**
559566
* kmem_dump_obj - Print available slab provenance information
560567
* @object: slab object for which to find provenance information.
@@ -590,6 +597,8 @@ void kmem_dump_obj(void *object)
590597
pr_cont(" slab%s %s", cp, kp.kp_slab_cache->name);
591598
else
592599
pr_cont(" slab%s", cp);
600+
if (is_kfence_address(object))
601+
pr_cont(" (kfence)");
593602
if (kp.kp_objp)
594603
pr_cont(" start %px", kp.kp_objp);
595604
if (kp.kp_data_offset)

mm/slob.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ static void slob_free(void *block, int size)
463463
}
464464

465465
#ifdef CONFIG_PRINTK
466-
void kmem_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab)
466+
void __kmem_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab)
467467
{
468468
kpp->kp_ptr = object;
469469
kpp->kp_slab = slab;

mm/slub.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4312,7 +4312,7 @@ int __kmem_cache_shutdown(struct kmem_cache *s)
43124312
}
43134313

43144314
#ifdef CONFIG_PRINTK
4315-
void kmem_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab)
4315+
void __kmem_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab)
43164316
{
43174317
void *base;
43184318
int __maybe_unused i;

0 commit comments

Comments
 (0)