Commit 90e9b23a authored by Vlastimil Babka's avatar Vlastimil Babka
Browse files

Merge branch 'slab/for-6.2/kmalloc_redzone' into slab/for-next

kmalloc() redzone improvements by Feng Tang

From cover letter [1]:

kmalloc's API family is critical for mm, and one of its nature is that
it will round up the request size to a fixed one (mostly power of 2).
When user requests memory for '2^n + 1' bytes, actually 2^(n+1) bytes
could be allocated, so there is an extra space than what is originally
requested.

This patchset tries to extend the redzone sanity check to the extra
kmalloced buffer than requested, to better detect un-legitimate access
to it. (depends on SLAB_STORE_USER & SLAB_RED_ZONE)

[1] https://lore.kernel.org/all/20221021032405.1825078-1-feng.tang@intel.com/
parents 76537db3 946fa0db
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -302,7 +302,7 @@ static inline void kasan_unpoison_task_stack(struct task_struct *task) {}

#ifdef CONFIG_KASAN_GENERIC

size_t kasan_metadata_size(struct kmem_cache *cache);
size_t kasan_metadata_size(struct kmem_cache *cache, bool in_object);
slab_flags_t kasan_never_merge(void);
void kasan_cache_create(struct kmem_cache *cache, unsigned int *size,
			slab_flags_t *flags);
@@ -315,7 +315,8 @@ void kasan_record_aux_stack_noalloc(void *ptr);
#else /* CONFIG_KASAN_GENERIC */

/* Tag-based KASAN modes do not use per-object metadata. */
static inline size_t kasan_metadata_size(struct kmem_cache *cache)
static inline size_t kasan_metadata_size(struct kmem_cache *cache,
						bool in_object)
{
	return 0;
}
+13 −6
Original line number Diff line number Diff line
@@ -450,14 +450,21 @@ void kasan_init_object_meta(struct kmem_cache *cache, const void *object)
		__memset(alloc_meta, 0, sizeof(*alloc_meta));
}

size_t kasan_metadata_size(struct kmem_cache *cache)
size_t kasan_metadata_size(struct kmem_cache *cache, bool in_object)
{
	struct kasan_cache *info = &cache->kasan_info;

	if (!kasan_requires_meta())
		return 0;
	return (cache->kasan_info.alloc_meta_offset ?

	if (in_object)
		return (info->free_meta_offset ?
			0 : sizeof(struct kasan_free_meta));
	else
		return (info->alloc_meta_offset ?
			sizeof(struct kasan_alloc_meta) : 0) +
		((cache->kasan_info.free_meta_offset &&
		  cache->kasan_info.free_meta_offset != KASAN_NO_FREE_META) ?
			((info->free_meta_offset &&
			info->free_meta_offset != KASAN_NO_FREE_META) ?
			sizeof(struct kasan_free_meta) : 0);
}

+4 −3
Original line number Diff line number Diff line
@@ -3258,7 +3258,8 @@ slab_alloc_node(struct kmem_cache *cachep, struct list_lru *lru, gfp_t flags,
	init = slab_want_init_on_alloc(flags, cachep);

out:
	slab_post_alloc_hook(cachep, objcg, flags, 1, &objp, init);
	slab_post_alloc_hook(cachep, objcg, flags, 1, &objp, init,
				cachep->object_size);
	return objp;
}

@@ -3501,13 +3502,13 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
	 * Done outside of the IRQ disabled section.
	 */
	slab_post_alloc_hook(s, objcg, flags, size, p,
				slab_want_init_on_alloc(flags, s));
			slab_want_init_on_alloc(flags, s), s->object_size);
	/* FIXME: Trace call missing. Christoph would like a bulk variant */
	return size;
error:
	local_irq_enable();
	cache_alloc_debugcheck_after_bulk(s, flags, i, p, _RET_IP_);
	slab_post_alloc_hook(s, objcg, flags, i, p, false);
	slab_post_alloc_hook(s, objcg, flags, i, p, false, s->object_size);
	kmem_cache_free_bulk(s, i, p);
	return 0;
}
+20 −2
Original line number Diff line number Diff line
@@ -730,12 +730,26 @@ static inline struct kmem_cache *slab_pre_alloc_hook(struct kmem_cache *s,

static inline void slab_post_alloc_hook(struct kmem_cache *s,
					struct obj_cgroup *objcg, gfp_t flags,
					size_t size, void **p, bool init)
					size_t size, void **p, bool init,
					unsigned int orig_size)
{
	unsigned int zero_size = s->object_size;
	size_t i;

	flags &= gfp_allowed_mask;

	/*
	 * For kmalloc object, the allocated memory size(object_size) is likely
	 * larger than the requested size(orig_size). If redzone check is
	 * enabled for the extra space, don't zero it, as it will be redzoned
	 * soon. The redzone operation for this extra space could be seen as a
	 * replacement of current poisoning under certain debug option, and
	 * won't break other sanity checks.
	 */
	if (kmem_cache_debug_flags(s, SLAB_STORE_USER | SLAB_RED_ZONE) &&
	    (s->flags & SLAB_KMALLOC))
		zero_size = orig_size;

	/*
	 * As memory initialization might be integrated into KASAN,
	 * kasan_slab_alloc and initialization memset must be
@@ -746,7 +760,7 @@ static inline void slab_post_alloc_hook(struct kmem_cache *s,
	for (i = 0; i < size; i++) {
		p[i] = kasan_slab_alloc(s, p[i], flags, init);
		if (p[i] && init && !kasan_has_integrated_init())
			memset(p[i], 0, s->object_size);
			memset(p[i], 0, zero_size);
		kmemleak_alloc_recursive(p[i], s->object_size, 1,
					 s->flags, flags);
		kmsan_slab_alloc(s, p[i], flags);
@@ -881,4 +895,8 @@ void __check_heap_object(const void *ptr, unsigned long n,
}
#endif

#ifdef CONFIG_SLUB_DEBUG
void skip_orig_size_check(struct kmem_cache *s, const void *object);
#endif

#endif /* MM_SLAB_H */
+4 −0
Original line number Diff line number Diff line
@@ -1037,6 +1037,10 @@ size_t __ksize(const void *object)
		return folio_size(folio);
	}

#ifdef CONFIG_SLUB_DEBUG
	skip_orig_size_check(folio_slab(folio)->slab_cache, object);
#endif

	return slab_ksize(folio_slab(folio)->slab_cache);
}

Loading