We export kasan_poison_shadow and kasan_unpoison_shadow for use by allocators, but provide no API to query whether a region is poisoned without triggering a stack trace. This can be useful however for more unconventional "allocators" like the DMA API debug code, which can use it to detect double poisons/unpoisons that should not occur if the DMA API is correctly used. Signed-off-by: Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx> --- include/linux/kasan.h | 2 ++ lib/kasan/generic.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/include/linux/kasan.h b/include/linux/kasan.h index 5fa0bebb796b..7812e0fa805b 100644 --- a/include/linux/kasan.h +++ b/include/linux/kasan.h @@ -69,6 +69,7 @@ extern void kasan_disable_current(void); void kasan_poison_shadow(const void *address, size_t size, u8 value); void kasan_unpoison_shadow(const void *address, size_t size); +int kasan_is_poisoned_shadow(const void *address, size_t size); bool kasan_save_enable_multi_shot(void); void kasan_restore_multi_shot(bool enabled); @@ -77,6 +78,7 @@ void kasan_restore_multi_shot(bool enabled); static inline void kasan_poison_shadow(const void *address, size_t size, u8 value) {} static inline void kasan_unpoison_shadow(const void *address, size_t size) {} +static inline int kasan_is_poisoned_shadow(const void *address, size_t size) { return -1; } static inline void kasan_enable_current(void) {} static inline void kasan_disable_current(void) {} diff --git a/lib/kasan/generic.c b/lib/kasan/generic.c index 3709b8da9aae..2432f9274401 100644 --- a/lib/kasan/generic.c +++ b/lib/kasan/generic.c @@ -177,6 +177,43 @@ static __always_inline bool check_memory_region_inline(unsigned long addr, return !kasan_report(addr, size, write, ret_ip); } +int kasan_is_poisoned_shadow(const void *_addr, size_t size) +{ + unsigned long addr = (unsigned long)_addr; + unsigned long ret; + + if (!kasan_initialized) + return -1; + + if (addr < kasan_shadow_start) + return -1; + + if (addr > kasan_shadowed_end) + return -1; + + if (unlikely(size == 0)) + return -1; + + if (unlikely(addr + size < addr)) + return -1; + + if (addr < kasan_shadow_base) + return -1; + + ret = memory_is_nonzero(kasan_mem_to_shadow((void *)addr), + kasan_mem_to_shadow((void *)addr + size - 1) + 1); + + if (unlikely(ret)) { + unsigned long last_byte = addr + size - 1; + s8 *last_shadow = (s8 *)kasan_mem_to_shadow((void *)last_byte); + + if (unlikely(ret != (unsigned long)last_shadow || + ((long)(last_byte & KASAN_SHADOW_MASK) >= *last_shadow))) + return 1; + } + return 0; +} + void kasan_init(unsigned long membase, unsigned long memsize, unsigned long shadow_base) { -- 2.39.2