Commit 2cdbed63 authored by Andrey Konovalov's avatar Andrey Konovalov Committed by Linus Torvalds
Browse files

kasan: hide invalid free check implementation

This is a preparatory commit for the upcoming addition of a new hardware
tag-based (MTE-based) KASAN mode.

For software KASAN modes the check is based on the value in the shadow
memory.  Hardware tag-based KASAN won't be using shadow, so hide the
implementation of the check in check_invalid_free().

Also simplify the code for software tag-based mode.

No functional changes for software modes.

Link: https://lkml.kernel.org/r/d01534a4b977f97d87515dc590e6348e1406de81.1606161801.git.andreyknvl@google.com


Signed-off-by: default avatarAndrey Konovalov <andreyknvl@google.com>
Signed-off-by: default avatarVincenzo Frascino <vincenzo.frascino@arm.com>
Reviewed-by: default avatarMarco Elver <elver@google.com>
Reviewed-by: default avatarAlexander Potapenko <glider@google.com>
Tested-by: default avatarVincenzo Frascino <vincenzo.frascino@arm.com>
Cc: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Branislav Rankov <Branislav.Rankov@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Evgenii Stepanov <eugenis@google.com>
Cc: Kevin Brodsky <kevin.brodsky@arm.com>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent ffcc5cea
Loading
Loading
Loading
Loading
+1 −18
Original line number Original line Diff line number Diff line
@@ -277,25 +277,9 @@ void * __must_check kasan_init_slab_obj(struct kmem_cache *cache,
	return (void *)object;
	return (void *)object;
}
}


static inline bool shadow_invalid(u8 tag, s8 shadow_byte)
{
	if (IS_ENABLED(CONFIG_KASAN_GENERIC))
		return shadow_byte < 0 ||
			shadow_byte >= KASAN_GRANULE_SIZE;

	/* else CONFIG_KASAN_SW_TAGS: */
	if ((u8)shadow_byte == KASAN_TAG_INVALID)
		return true;
	if ((tag != KASAN_TAG_KERNEL) && (tag != (u8)shadow_byte))
		return true;

	return false;
}

static bool __kasan_slab_free(struct kmem_cache *cache, void *object,
static bool __kasan_slab_free(struct kmem_cache *cache, void *object,
			      unsigned long ip, bool quarantine)
			      unsigned long ip, bool quarantine)
{
{
	s8 shadow_byte;
	u8 tag;
	u8 tag;
	void *tagged_object;
	void *tagged_object;
	unsigned long rounded_up_size;
	unsigned long rounded_up_size;
@@ -314,8 +298,7 @@ static bool __kasan_slab_free(struct kmem_cache *cache, void *object,
	if (unlikely(cache->flags & SLAB_TYPESAFE_BY_RCU))
	if (unlikely(cache->flags & SLAB_TYPESAFE_BY_RCU))
		return false;
		return false;


	shadow_byte = READ_ONCE(*(s8 *)kasan_mem_to_shadow(object));
	if (check_invalid_free(tagged_object)) {
	if (shadow_invalid(tag, shadow_byte)) {
		kasan_report_invalid_free(tagged_object, ip);
		kasan_report_invalid_free(tagged_object, ip);
		return true;
		return true;
	}
	}
+7 −0
Original line number Original line Diff line number Diff line
@@ -187,6 +187,13 @@ bool check_memory_region(unsigned long addr, size_t size, bool write,
	return check_memory_region_inline(addr, size, write, ret_ip);
	return check_memory_region_inline(addr, size, write, ret_ip);
}
}


bool check_invalid_free(void *addr)
{
	s8 shadow_byte = READ_ONCE(*(s8 *)kasan_mem_to_shadow(addr));

	return shadow_byte < 0 || shadow_byte >= KASAN_GRANULE_SIZE;
}

void kasan_cache_shrink(struct kmem_cache *cache)
void kasan_cache_shrink(struct kmem_cache *cache)
{
{
	quarantine_remove_cache(cache);
	quarantine_remove_cache(cache);
+2 −0
Original line number Original line Diff line number Diff line
@@ -166,6 +166,8 @@ void unpoison_range(const void *address, size_t size);
bool check_memory_region(unsigned long addr, size_t size, bool write,
bool check_memory_region(unsigned long addr, size_t size, bool write,
				unsigned long ret_ip);
				unsigned long ret_ip);


bool check_invalid_free(void *addr);

void *find_first_bad_addr(void *addr, size_t size);
void *find_first_bad_addr(void *addr, size_t size);
const char *get_bug_type(struct kasan_access_info *info);
const char *get_bug_type(struct kasan_access_info *info);


+9 −0
Original line number Original line Diff line number Diff line
@@ -121,6 +121,15 @@ bool check_memory_region(unsigned long addr, size_t size, bool write,
	return true;
	return true;
}
}


bool check_invalid_free(void *addr)
{
	u8 tag = get_tag(addr);
	u8 shadow_byte = READ_ONCE(*(u8 *)kasan_mem_to_shadow(reset_tag(addr)));

	return (shadow_byte == KASAN_TAG_INVALID) ||
		(tag != KASAN_TAG_KERNEL && tag != shadow_byte);
}

#define DEFINE_HWASAN_LOAD_STORE(size)					\
#define DEFINE_HWASAN_LOAD_STORE(size)					\
	void __hwasan_load##size##_noabort(unsigned long addr)		\
	void __hwasan_load##size##_noabort(unsigned long addr)		\
	{								\
	{								\