Commit 2d552463 authored by Andrey Konovalov's avatar Andrey Konovalov Committed by Andrew Morton

slub, kasan: improve interaction of KASAN and slub_debug poisoning

When both KASAN and slub_debug are enabled, when a free object is being
prepared in setup_object, slub_debug poisons the object data before KASAN
initializes its per-object metadata.

Right now, in setup_object, KASAN only initializes the alloc metadata,
which is always stored outside of the object.  slub_debug is aware of this
and it skips poisoning and checking that memory area.

However, with the following patch in this series, KASAN also starts
initializing its free medata in setup_object.  As this metadata might be
stored within the object, this initialization might overwrite the
slub_debug poisoning.  This leads to slub_debug reports.

Thus, skip checking slub_debug poisoning of the object data area that
overlaps with the in-object KASAN free metadata.

Also make slub_debug poisoning of tail kmalloc redzones more precise when
KASAN is enabled: slub_debug can still poison and check the tail kmalloc
allocation area that comes after the KASAN free metadata.

Link: https://lkml.kernel.org/r/20231122231202.121277-1-andrey.konovalov@linux.devSigned-off-by: default avatarAndrey Konovalov <andreyknvl@google.com>
Tested-by: default avatarHyeonggon Yoo <42.hyeyoo@gmail.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Evgenii Stepanov <eugenis@google.com>
Cc: Feng Tang <feng.tang@intel.com>
Cc: Marco Elver <elver@google.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent f816938b
...@@ -870,20 +870,20 @@ static inline void set_orig_size(struct kmem_cache *s, ...@@ -870,20 +870,20 @@ static inline void set_orig_size(struct kmem_cache *s,
void *object, unsigned int orig_size) void *object, unsigned int orig_size)
{ {
void *p = kasan_reset_tag(object); void *p = kasan_reset_tag(object);
unsigned int kasan_meta_size;
if (!slub_debug_orig_size(s)) if (!slub_debug_orig_size(s))
return; return;
#ifdef CONFIG_KASAN_GENERIC
/* /*
* KASAN could save its free meta data in object's data area at * KASAN can save its free meta data inside of the object at offset 0.
* offset 0, if the size is larger than 'orig_size', it will * If this meta data size is larger than 'orig_size', it will overlap
* overlap the data redzone in [orig_size+1, object_size], and * the data redzone in [orig_size+1, object_size]. Thus, we adjust
* the check should be skipped. * 'orig_size' to be as at least as big as KASAN's meta data.
*/ */
if (kasan_metadata_size(s, true) > orig_size) kasan_meta_size = kasan_metadata_size(s, true);
orig_size = s->object_size; if (kasan_meta_size > orig_size)
#endif orig_size = kasan_meta_size;
p += get_info_end(s); p += get_info_end(s);
p += sizeof(struct track) * 2; p += sizeof(struct track) * 2;
...@@ -1192,7 +1192,7 @@ static int check_object(struct kmem_cache *s, struct slab *slab, ...@@ -1192,7 +1192,7 @@ static int check_object(struct kmem_cache *s, struct slab *slab,
{ {
u8 *p = object; u8 *p = object;
u8 *endobject = object + s->object_size; u8 *endobject = object + s->object_size;
unsigned int orig_size; unsigned int orig_size, kasan_meta_size;
if (s->flags & SLAB_RED_ZONE) { if (s->flags & SLAB_RED_ZONE) {
if (!check_bytes_and_report(s, slab, object, "Left Redzone", if (!check_bytes_and_report(s, slab, object, "Left Redzone",
...@@ -1222,12 +1222,23 @@ static int check_object(struct kmem_cache *s, struct slab *slab, ...@@ -1222,12 +1222,23 @@ static int check_object(struct kmem_cache *s, struct slab *slab,
} }
if (s->flags & SLAB_POISON) { if (s->flags & SLAB_POISON) {
if (val != SLUB_RED_ACTIVE && (s->flags & __OBJECT_POISON) && if (val != SLUB_RED_ACTIVE && (s->flags & __OBJECT_POISON)) {
(!check_bytes_and_report(s, slab, p, "Poison", p, /*
POISON_FREE, s->object_size - 1) || * KASAN can save its free meta data inside of the
!check_bytes_and_report(s, slab, p, "End Poison", * object at offset 0. Thus, skip checking the part of
p + s->object_size - 1, POISON_END, 1))) * the redzone that overlaps with the meta data.
return 0; */
kasan_meta_size = kasan_metadata_size(s, true);
if (kasan_meta_size < s->object_size - 1 &&
!check_bytes_and_report(s, slab, p, "Poison",
p + kasan_meta_size, POISON_FREE,
s->object_size - kasan_meta_size - 1))
return 0;
if (kasan_meta_size < s->object_size &&
!check_bytes_and_report(s, slab, p, "End Poison",
p + s->object_size - 1, POISON_END, 1))
return 0;
}
/* /*
* check_pad_bytes cleans up on its own. * check_pad_bytes cleans up on its own.
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment