mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
![Walter Wu](/assets/img/avatar_default.png)
Patch series "fix the missing underflow in memory operation function", v4. The patchset helps to produce a KASAN report when size is negative in memory operation functions. It is helpful for programmer to solve an undefined behavior issue. Patch 1 based on Dmitry's review and suggestion, patch 2 is a test in order to verify the patch 1. [1]https://bugzilla.kernel.org/show_bug.cgi?id=199341 [2]https://lore.kernel.org/linux-arm-kernel/20190927034338.15813-1-walter-zh.wu@mediatek.com/ This patch (of 2): KASAN missed detecting size is a negative number in memset(), memcpy(), and memmove(), it will cause out-of-bounds bug. So needs to be detected by KASAN. If size is a negative number, then it has a reason to be defined as out-of-bounds bug type. Casting negative numbers to size_t would indeed turn up as a large size_t and its value will be larger than ULONG_MAX/2, so that this can qualify as out-of-bounds. KASAN report is shown below: BUG: KASAN: out-of-bounds in kmalloc_memmove_invalid_size+0x70/0xa0 Read of size 18446744073709551608 at addr ffffff8069660904 by task cat/72 CPU: 2 PID: 72 Comm: cat Not tainted 5.4.0-rc1-next-20191004ajb-00001-gdb8af2f372b2-dirty #1 Hardware name: linux,dummy-virt (DT) Call trace: dump_backtrace+0x0/0x288 show_stack+0x14/0x20 dump_stack+0x10c/0x164 print_address_description.isra.9+0x68/0x378 __kasan_report+0x164/0x1a0 kasan_report+0xc/0x18 check_memory_region+0x174/0x1d0 memmove+0x34/0x88 kmalloc_memmove_invalid_size+0x70/0xa0 [1] https://bugzilla.kernel.org/show_bug.cgi?id=199341 [cai@lca.pw: fix -Wdeclaration-after-statement warn] Link: http://lkml.kernel.org/r/1583509030-27939-1-git-send-email-cai@lca.pw [peterz@infradead.org: fix objtool warning] Link: http://lkml.kernel.org/r/20200305095436.GV2596@hirez.programming.kicks-ass.net Reported-by: kernel test robot <lkp@intel.com> Reported-by: Dmitry Vyukov <dvyukov@google.com> Suggested-by: Dmitry Vyukov <dvyukov@google.com> Signed-off-by: Walter Wu <walter-zh.wu@mediatek.com> Signed-off-by: Qian Cai <cai@lca.pw> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Reviewed-by: Dmitry Vyukov <dvyukov@google.com> Reviewed-by: Andrey Ryabinin <aryabinin@virtuozzo.com> Cc: Alexander Potapenko <glider@google.com> Link: http://lkml.kernel.org/r/20191112065302.7015-1-walter-zh.wu@mediatek.com Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
237 lines
7.1 KiB
C
237 lines
7.1 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _LINUX_KASAN_H
|
|
#define _LINUX_KASAN_H
|
|
|
|
#include <linux/types.h>
|
|
|
|
struct kmem_cache;
|
|
struct page;
|
|
struct vm_struct;
|
|
struct task_struct;
|
|
|
|
#ifdef CONFIG_KASAN
|
|
|
|
#include <asm/kasan.h>
|
|
#include <asm/pgtable.h>
|
|
|
|
extern unsigned char kasan_early_shadow_page[PAGE_SIZE];
|
|
extern pte_t kasan_early_shadow_pte[PTRS_PER_PTE];
|
|
extern pmd_t kasan_early_shadow_pmd[PTRS_PER_PMD];
|
|
extern pud_t kasan_early_shadow_pud[PTRS_PER_PUD];
|
|
extern p4d_t kasan_early_shadow_p4d[MAX_PTRS_PER_P4D];
|
|
|
|
int kasan_populate_early_shadow(const void *shadow_start,
|
|
const void *shadow_end);
|
|
|
|
static inline void *kasan_mem_to_shadow(const void *addr)
|
|
{
|
|
return (void *)((unsigned long)addr >> KASAN_SHADOW_SCALE_SHIFT)
|
|
+ KASAN_SHADOW_OFFSET;
|
|
}
|
|
|
|
/* Enable reporting bugs after kasan_disable_current() */
|
|
extern void kasan_enable_current(void);
|
|
|
|
/* Disable reporting bugs for current task */
|
|
extern void kasan_disable_current(void);
|
|
|
|
void kasan_unpoison_shadow(const void *address, size_t size);
|
|
|
|
void kasan_unpoison_task_stack(struct task_struct *task);
|
|
void kasan_unpoison_stack_above_sp_to(const void *watermark);
|
|
|
|
void kasan_alloc_pages(struct page *page, unsigned int order);
|
|
void kasan_free_pages(struct page *page, unsigned int order);
|
|
|
|
void kasan_cache_create(struct kmem_cache *cache, unsigned int *size,
|
|
slab_flags_t *flags);
|
|
|
|
void kasan_poison_slab(struct page *page);
|
|
void kasan_unpoison_object_data(struct kmem_cache *cache, void *object);
|
|
void kasan_poison_object_data(struct kmem_cache *cache, void *object);
|
|
void * __must_check kasan_init_slab_obj(struct kmem_cache *cache,
|
|
const void *object);
|
|
|
|
void * __must_check kasan_kmalloc_large(const void *ptr, size_t size,
|
|
gfp_t flags);
|
|
void kasan_kfree_large(void *ptr, unsigned long ip);
|
|
void kasan_poison_kfree(void *ptr, unsigned long ip);
|
|
void * __must_check kasan_kmalloc(struct kmem_cache *s, const void *object,
|
|
size_t size, gfp_t flags);
|
|
void * __must_check kasan_krealloc(const void *object, size_t new_size,
|
|
gfp_t flags);
|
|
|
|
void * __must_check kasan_slab_alloc(struct kmem_cache *s, void *object,
|
|
gfp_t flags);
|
|
bool kasan_slab_free(struct kmem_cache *s, void *object, unsigned long ip);
|
|
|
|
struct kasan_cache {
|
|
int alloc_meta_offset;
|
|
int free_meta_offset;
|
|
};
|
|
|
|
/*
|
|
* These functions provide a special case to support backing module
|
|
* allocations with real shadow memory. With KASAN vmalloc, the special
|
|
* case is unnecessary, as the work is handled in the generic case.
|
|
*/
|
|
#ifndef CONFIG_KASAN_VMALLOC
|
|
int kasan_module_alloc(void *addr, size_t size);
|
|
void kasan_free_shadow(const struct vm_struct *vm);
|
|
#else
|
|
static inline int kasan_module_alloc(void *addr, size_t size) { return 0; }
|
|
static inline void kasan_free_shadow(const struct vm_struct *vm) {}
|
|
#endif
|
|
|
|
int kasan_add_zero_shadow(void *start, unsigned long size);
|
|
void kasan_remove_zero_shadow(void *start, unsigned long size);
|
|
|
|
size_t __ksize(const void *);
|
|
static inline void kasan_unpoison_slab(const void *ptr)
|
|
{
|
|
kasan_unpoison_shadow(ptr, __ksize(ptr));
|
|
}
|
|
size_t kasan_metadata_size(struct kmem_cache *cache);
|
|
|
|
bool kasan_save_enable_multi_shot(void);
|
|
void kasan_restore_multi_shot(bool enabled);
|
|
|
|
#else /* CONFIG_KASAN */
|
|
|
|
static inline void kasan_unpoison_shadow(const void *address, size_t size) {}
|
|
|
|
static inline void kasan_unpoison_task_stack(struct task_struct *task) {}
|
|
static inline void kasan_unpoison_stack_above_sp_to(const void *watermark) {}
|
|
|
|
static inline void kasan_enable_current(void) {}
|
|
static inline void kasan_disable_current(void) {}
|
|
|
|
static inline void kasan_alloc_pages(struct page *page, unsigned int order) {}
|
|
static inline void kasan_free_pages(struct page *page, unsigned int order) {}
|
|
|
|
static inline void kasan_cache_create(struct kmem_cache *cache,
|
|
unsigned int *size,
|
|
slab_flags_t *flags) {}
|
|
|
|
static inline void kasan_poison_slab(struct page *page) {}
|
|
static inline void kasan_unpoison_object_data(struct kmem_cache *cache,
|
|
void *object) {}
|
|
static inline void kasan_poison_object_data(struct kmem_cache *cache,
|
|
void *object) {}
|
|
static inline void *kasan_init_slab_obj(struct kmem_cache *cache,
|
|
const void *object)
|
|
{
|
|
return (void *)object;
|
|
}
|
|
|
|
static inline void *kasan_kmalloc_large(void *ptr, size_t size, gfp_t flags)
|
|
{
|
|
return ptr;
|
|
}
|
|
static inline void kasan_kfree_large(void *ptr, unsigned long ip) {}
|
|
static inline void kasan_poison_kfree(void *ptr, unsigned long ip) {}
|
|
static inline void *kasan_kmalloc(struct kmem_cache *s, const void *object,
|
|
size_t size, gfp_t flags)
|
|
{
|
|
return (void *)object;
|
|
}
|
|
static inline void *kasan_krealloc(const void *object, size_t new_size,
|
|
gfp_t flags)
|
|
{
|
|
return (void *)object;
|
|
}
|
|
|
|
static inline void *kasan_slab_alloc(struct kmem_cache *s, void *object,
|
|
gfp_t flags)
|
|
{
|
|
return object;
|
|
}
|
|
static inline bool kasan_slab_free(struct kmem_cache *s, void *object,
|
|
unsigned long ip)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static inline int kasan_module_alloc(void *addr, size_t size) { return 0; }
|
|
static inline void kasan_free_shadow(const struct vm_struct *vm) {}
|
|
|
|
static inline int kasan_add_zero_shadow(void *start, unsigned long size)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline void kasan_remove_zero_shadow(void *start,
|
|
unsigned long size)
|
|
{}
|
|
|
|
static inline void kasan_unpoison_slab(const void *ptr) { }
|
|
static inline size_t kasan_metadata_size(struct kmem_cache *cache) { return 0; }
|
|
|
|
#endif /* CONFIG_KASAN */
|
|
|
|
#ifdef CONFIG_KASAN_GENERIC
|
|
|
|
#define KASAN_SHADOW_INIT 0
|
|
|
|
void kasan_cache_shrink(struct kmem_cache *cache);
|
|
void kasan_cache_shutdown(struct kmem_cache *cache);
|
|
|
|
#else /* CONFIG_KASAN_GENERIC */
|
|
|
|
static inline void kasan_cache_shrink(struct kmem_cache *cache) {}
|
|
static inline void kasan_cache_shutdown(struct kmem_cache *cache) {}
|
|
|
|
#endif /* CONFIG_KASAN_GENERIC */
|
|
|
|
#ifdef CONFIG_KASAN_SW_TAGS
|
|
|
|
#define KASAN_SHADOW_INIT 0xFF
|
|
|
|
void kasan_init_tags(void);
|
|
|
|
void *kasan_reset_tag(const void *addr);
|
|
|
|
bool kasan_report(unsigned long addr, size_t size,
|
|
bool is_write, unsigned long ip);
|
|
|
|
#else /* CONFIG_KASAN_SW_TAGS */
|
|
|
|
static inline void kasan_init_tags(void) { }
|
|
|
|
static inline void *kasan_reset_tag(const void *addr)
|
|
{
|
|
return (void *)addr;
|
|
}
|
|
|
|
#endif /* CONFIG_KASAN_SW_TAGS */
|
|
|
|
#ifdef CONFIG_KASAN_VMALLOC
|
|
int kasan_populate_vmalloc(unsigned long addr, unsigned long size);
|
|
void kasan_poison_vmalloc(const void *start, unsigned long size);
|
|
void kasan_unpoison_vmalloc(const void *start, unsigned long size);
|
|
void kasan_release_vmalloc(unsigned long start, unsigned long end,
|
|
unsigned long free_region_start,
|
|
unsigned long free_region_end);
|
|
#else
|
|
static inline int kasan_populate_vmalloc(unsigned long start,
|
|
unsigned long size)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline void kasan_poison_vmalloc(const void *start, unsigned long size)
|
|
{ }
|
|
static inline void kasan_unpoison_vmalloc(const void *start, unsigned long size)
|
|
{ }
|
|
static inline void kasan_release_vmalloc(unsigned long start,
|
|
unsigned long end,
|
|
unsigned long free_region_start,
|
|
unsigned long free_region_end) {}
|
|
#endif
|
|
|
|
#ifdef CONFIG_KASAN_INLINE
|
|
void kasan_non_canonical_hook(unsigned long addr);
|
|
#else /* CONFIG_KASAN_INLINE */
|
|
static inline void kasan_non_canonical_hook(unsigned long addr) { }
|
|
#endif /* CONFIG_KASAN_INLINE */
|
|
|
|
#endif /* LINUX_KASAN_H */
|