* [RFC 1/2] kasan: introduce mem track feature base on kasan
2024-01-18 12:41 [RFC 0/2] kasan: introduce mem track feature lizhe.67
@ 2024-01-18 12:41 ` lizhe.67
2024-01-18 12:41 ` [RFC 2/2] kasan: add mem track interface and its test cases lizhe.67
` (2 subsequent siblings)
3 siblings, 0 replies; 11+ messages in thread
From: lizhe.67 @ 2024-01-18 12:41 UTC (permalink / raw)
To: ryabinin.a.a, glider, andreyknvl, dvyukov, vincenzo.frascino, akpm
Cc: kasan-dev, linux-mm, lizefan.x, lizhe.67
From: Li Zhe <lizhe.67@bytedance.com>
This is a feature based on KASAN_GENERIC.
The current implementation of kasan can help us locate memory's
problems such as out-of-bounds, use-after-free, etc. But it cannot
identify memory tramples on allocated memory by software. This type
of problem may appear in our daily development. Generally, the
phenomenon is rather strange and problem is difficult to locate.
With this tool, we can easily locate memory corruption on allocated
memory.
In the current kernel implementation, we use bits 0-2 of each shadow
memory byte to store how many bytes in the 8 byte memory corresponding
to the shadow memory byte can be accessed. In addition, for inaccessible
memory, the highest bit of its shadow mem is 1. Therefore, we can use the
free bits 3-6 of shadow mem to record the track information corresponding
to 8-byte of memory, that is, one bit records track information of 2 bytes.
If the track bit of the shadow mem corresponding to a certain memory is
1, it means that the corresponding 2-byte memory is tracked. Of course,
if we configure a byte to be tracked, when we access its paired byte,
the track check will also be successfully triggered, which will cause us
some interference. But for this type of false positives, we can easily
identify them by checking kasan logs. And I think this shortcoming should
not overshadow the convenience that this feature brings to our debugging.
Signed-off-by: Li Zhe <lizhe.67@bytedance.com>
---
lib/Kconfig.kasan | 9 ++
mm/kasan/generic.c | 276 +++++++++++++++++++++++++++++++++++---
mm/kasan/report_generic.c | 6 +
3 files changed, 275 insertions(+), 16 deletions(-)
diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan
index e6eda054ab27..d96e28757fb7 100644
--- a/lib/Kconfig.kasan
+++ b/lib/Kconfig.kasan
@@ -183,6 +183,15 @@ config KASAN_VMALLOC
With Hardware Tag-Based KASAN, only non-executable VM_ALLOC mappings
are checked. There is no additional memory usage.
+config KASAN_MEM_TRACK
+ bool "Capture allocated memory corruption based on KASAN"
+ depends on KASAN_GENERIC && KASAN_OUTLINE
+ help
+ Enable memory tracking bases on kasan. This is a tools to capture
+ memory corruption on allocated memory.
+
+ If unsure, say N.
+
config KASAN_KUNIT_TEST
tristate "KUnit-compatible tests of KASAN bug detection capabilities" if !KUNIT_ALL_TESTS
depends on KASAN && KUNIT && TRACEPOINTS
diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c
index 24c13dfb1e94..a204ddcbaa3f 100644
--- a/mm/kasan/generic.c
+++ b/mm/kasan/generic.c
@@ -42,9 +42,94 @@
* depending on memory access size X.
*/
-static __always_inline bool memory_is_poisoned_1(const void *addr)
+#ifdef CONFIG_KASAN_MEM_TRACK
+#define KASAN_SHADOW_VALUE_MASK_ONE_BYTE 0x07
+#define KASAN_TRACK_VALUE_MASK_ONE_BYTE 0x78
+#define KASAN_SHADOW_VALUE_MASK_TWO_BYTE 0x0707
+#define KASAN_SHADOW_VALUE_MASK_EIGHT_BYTE 0x0707070707070707
+#define KASAN_TRACK_VALUE_MASK_EIGHT_BYTE 0x7878787878787878
+#define KASAN_TRACK_VALUE_OFFSET 3
+static __always_inline bool is_poison_value_1_byte(s8 shadow_value)
+{
+ if (shadow_value & 0x80)
+ return true;
+ return false;
+}
+
+static __always_inline bool is_poison_value_8_byte(u64 shadow_value)
+{
+ if (shadow_value & 0x8080808080808080)
+ return true;
+ return false;
+}
+
+static __always_inline s8 to_shadow_value_1_byte(s8 shadow_value)
+{
+ if (is_poison_value_1_byte(shadow_value))
+ return shadow_value;
+ return shadow_value & KASAN_SHADOW_VALUE_MASK_ONE_BYTE;
+}
+
+static __always_inline s8 to_track_value_1_byte(s8 shadow_value)
+{
+ if (is_poison_value_1_byte(shadow_value))
+ return shadow_value;
+ return (shadow_value & KASAN_TRACK_VALUE_MASK_ONE_BYTE) >>
+ KASAN_TRACK_VALUE_OFFSET;
+}
+
+static __always_inline u64 to_shadow_value_8_byte(u64 shadow_value)
+{
+ if (is_poison_value_8_byte(shadow_value))
+ return shadow_value;
+ return shadow_value & KASAN_SHADOW_VALUE_MASK_EIGHT_BYTE;
+}
+
+static __always_inline u64 to_track_value_8_byte(u64 shadow_value)
+{
+ if (is_poison_value_8_byte(shadow_value))
+ return shadow_value;
+ return shadow_value & KASAN_TRACK_VALUE_MASK_EIGHT_BYTE;
+}
+
+static __always_inline s8 get_shadow_value_1_byte(const void *addr)
{
s8 shadow_value = *(s8 *)kasan_mem_to_shadow(addr);
+ return to_shadow_value_1_byte(shadow_value);
+}
+
+static __always_inline u16 get_shadow_value_2_byte(const void *addr)
+{
+ u16 shadow_value = *(u16 *)kasan_mem_to_shadow(addr);
+
+ return shadow_value & KASAN_SHADOW_VALUE_MASK_TWO_BYTE;
+}
+#else
+static __always_inline s8 to_shadow_value_1_byte(s8 shadow_value)
+{
+ return shadow_value;
+}
+static __always_inline u64 to_shadow_value_8_byte(u64 shadow_value)
+{
+ return shadow_value;
+}
+static __always_inline s8 get_shadow_value_1_byte(const void *addr)
+{
+ return *(s8 *)kasan_mem_to_shadow(addr);
+}
+static __always_inline u16 get_shadow_value_2_byte(const void *addr)
+{
+ return *(u16 *)kasan_mem_to_shadow(addr);
+}
+static __always_inline bool memory_is_tracked(const void *addr, size_t size)
+{
+ return 0;
+}
+#endif
+
+static __always_inline bool memory_is_poisoned_1(const void *addr)
+{
+ s8 shadow_value = get_shadow_value_1_byte(addr);
if (unlikely(shadow_value)) {
s8 last_accessible_byte = (unsigned long)addr & KASAN_GRANULE_MASK;
@@ -57,34 +142,30 @@ static __always_inline bool memory_is_poisoned_1(const void *addr)
static __always_inline bool memory_is_poisoned_2_4_8(const void *addr,
unsigned long size)
{
- u8 *shadow_addr = (u8 *)kasan_mem_to_shadow(addr);
-
/*
* Access crosses 8(shadow size)-byte boundary. Such access maps
* into 2 shadow bytes, so we need to check them both.
*/
if (unlikely((((unsigned long)addr + size - 1) & KASAN_GRANULE_MASK) < size - 1))
- return *shadow_addr || memory_is_poisoned_1(addr + size - 1);
+ return get_shadow_value_1_byte(addr) || memory_is_poisoned_1(addr + size - 1);
return memory_is_poisoned_1(addr + size - 1);
}
static __always_inline bool memory_is_poisoned_16(const void *addr)
{
- u16 *shadow_addr = (u16 *)kasan_mem_to_shadow(addr);
-
/* Unaligned 16-bytes access maps into 3 shadow bytes. */
if (unlikely(!IS_ALIGNED((unsigned long)addr, KASAN_GRANULE_SIZE)))
- return *shadow_addr || memory_is_poisoned_1(addr + 15);
+ return get_shadow_value_2_byte(addr) || memory_is_poisoned_1(addr + 15);
- return *shadow_addr;
+ return get_shadow_value_2_byte(addr);
}
-static __always_inline unsigned long bytes_is_nonzero(const u8 *start,
+static __always_inline unsigned long bytes_is_nonzero(const s8 *start,
size_t size)
{
while (size) {
- if (unlikely(*start))
+ if (unlikely(to_shadow_value_1_byte(*start)))
return (unsigned long)start;
start++;
size--;
@@ -93,7 +174,7 @@ static __always_inline unsigned long bytes_is_nonzero(const u8 *start,
return 0;
}
-static __always_inline unsigned long memory_is_nonzero(const void *start,
+static __always_inline unsigned long shadow_val_is_nonzero(const void *start,
const void *end)
{
unsigned int words;
@@ -113,7 +194,7 @@ static __always_inline unsigned long memory_is_nonzero(const void *start,
words = (end - start) / 8;
while (words) {
- if (unlikely(*(u64 *)start))
+ if (unlikely(to_shadow_value_8_byte(*(u64 *)start)))
return bytes_is_nonzero(start, 8);
start += 8;
words--;
@@ -126,7 +207,7 @@ static __always_inline bool memory_is_poisoned_n(const void *addr, size_t size)
{
unsigned long ret;
- ret = memory_is_nonzero(kasan_mem_to_shadow(addr),
+ ret = shadow_val_is_nonzero(kasan_mem_to_shadow(addr),
kasan_mem_to_shadow(addr + size - 1) + 1);
if (unlikely(ret)) {
@@ -135,7 +216,7 @@ static __always_inline bool memory_is_poisoned_n(const void *addr, size_t size)
s8 last_accessible_byte = (unsigned long)last_byte & KASAN_GRANULE_MASK;
if (unlikely(ret != (unsigned long)last_shadow ||
- last_accessible_byte >= *last_shadow))
+ last_accessible_byte >= to_shadow_value_1_byte(*last_shadow)))
return true;
}
return false;
@@ -161,6 +242,168 @@ static __always_inline bool memory_is_poisoned(const void *addr, size_t size)
return memory_is_poisoned_n(addr, size);
}
+#ifdef CONFIG_KASAN_MEM_TRACK
+static __always_inline s8 get_track_value(const void *addr)
+{
+ s8 shadow_value = *(s8 *)kasan_mem_to_shadow(addr);
+
+ /* In the early stages of system startup, when Kasan is not fully ready,
+ * some illegal values may be obtained. Ignore it.
+ */
+ if (unlikely(shadow_value & 0x80))
+ return 0;
+ return (shadow_value >> KASAN_TRACK_VALUE_OFFSET);
+}
+
+/* ================================== size : 1 2 3 4 5 6 7 8 */
+static const s8 kasan_track_mask_odd_array[] = {0x01, 0x03, 0x03, 0x07, 0x07, 0x0f, 0x0f};
+static const s8 kasan_track_mask_even_array[] = {-1, 0x01, -1, 0x03, -1, 0x07, -1, 0x0f};
+static s8 kasan_track_mask_odd(size_t size)
+{
+ return kasan_track_mask_odd_array[size - 1];
+}
+
+static s8 kasan_track_mask_even(size_t size)
+{
+ return kasan_track_mask_even_array[size - 1];
+}
+
+/* check with addr do not cross 8(shadow size)-byte boundary */
+static __always_inline bool _memory_is_tracked(const void *addr, size_t size)
+{
+ s8 mask;
+ u8 offset = (unsigned long)addr & KASAN_GRANULE_MASK;
+
+ if ((unsigned long)addr & 0x01)
+ mask = kasan_track_mask_odd(size);
+ else
+ mask = kasan_track_mask_even(size);
+
+ return unlikely(get_track_value(addr) & (mask << (offset >> 1)));
+}
+
+static __always_inline bool memory_is_tracked_1(const void *addr)
+{
+ u8 last_accessible_byte = (unsigned long)addr & KASAN_GRANULE_MASK;
+
+ return unlikely(get_track_value(addr) & (0x01 << (last_accessible_byte >> 1)));
+}
+
+static __always_inline bool memory_is_tracked_2_4_8(const void *addr, size_t size)
+{
+ /*
+ * Access crosses 8(shadow size)-byte boundary. Such access maps
+ * into 2 shadow bytes, so we need to check them both.
+ */
+ if (unlikely((((unsigned long)addr + size - 1) & KASAN_GRANULE_MASK) < size - 1)) {
+ u8 part = (unsigned long)addr & KASAN_GRANULE_MASK;
+
+ part = 8 - part;
+ return ((unlikely(get_track_value(addr)) && _memory_is_tracked(addr, part)) ||
+ _memory_is_tracked(addr + part, size - part));
+ }
+
+ return _memory_is_tracked(addr, size);
+}
+
+static __always_inline bool memory_is_tracked_16(const void *addr)
+{
+ /* Unaligned 16-bytes access maps into 3 shadow bytes. */
+ if (unlikely(!IS_ALIGNED((unsigned long)addr, KASAN_GRANULE_SIZE))) {
+ u8 part = (unsigned long)addr & KASAN_GRANULE_MASK;
+
+ part = 8 - part;
+ return ((unlikely(get_track_value(addr)) && _memory_is_tracked(addr, part)) ||
+ _memory_is_tracked(addr + part, 8) ||
+ _memory_is_tracked(addr + part + 8, 8 - part));
+ }
+
+ return unlikely(get_track_value(addr) || get_track_value(addr + 8));
+}
+
+static __always_inline unsigned long track_bytes_is_nonzero(const s8 *start,
+ size_t size)
+{
+ while (size) {
+ if (unlikely(to_track_value_1_byte(*start)))
+ return (unsigned long)start;
+ start++;
+ size--;
+ }
+
+ return 0;
+}
+
+static __always_inline unsigned long track_val_is_nonzero(const void *start,
+ const void *end)
+{
+ unsigned int words;
+ unsigned long ret;
+ unsigned int prefix = (unsigned long)start % 8;
+
+ if (end - start <= 16)
+ return track_bytes_is_nonzero(start, end - start);
+
+ if (prefix) {
+ prefix = 8 - prefix;
+ ret = track_bytes_is_nonzero(start, prefix);
+ if (unlikely(ret))
+ return ret;
+ start += prefix;
+ }
+
+ words = (end - start) / 8;
+ while (words) {
+ if (unlikely(to_track_value_8_byte(*(u64 *)start)))
+ return track_bytes_is_nonzero(start, 8);
+ start += 8;
+ words--;
+ }
+
+ return track_bytes_is_nonzero(start, (end - start) % 8);
+}
+
+static __always_inline bool memory_is_tracked_n(const void *addr, size_t size)
+{
+ unsigned long ret;
+
+ ret = track_val_is_nonzero(kasan_mem_to_shadow(addr),
+ kasan_mem_to_shadow(addr + size - 1) + 1);
+
+ if (unlikely(ret)) {
+ const void *last_byte = addr + size - 1;
+ s8 *last_shadow = (s8 *)kasan_mem_to_shadow(last_byte);
+
+ if (unlikely(ret != (unsigned long)last_shadow ||
+ _memory_is_tracked(
+ (void *)((unsigned long)last_byte & ~KASAN_GRANULE_MASK),
+ ((unsigned long)last_byte & KASAN_GRANULE_MASK) + 1)))
+ return true;
+ }
+ return false;
+}
+
+static __always_inline bool memory_is_tracked(const void *addr, size_t size)
+{
+ if (__builtin_constant_p(size)) {
+ switch (size) {
+ case 1:
+ return memory_is_tracked_1(addr);
+ case 2:
+ case 4:
+ case 8:
+ return memory_is_tracked_2_4_8(addr, size);
+ case 16:
+ return memory_is_tracked_16(addr);
+ default:
+ BUILD_BUG();
+ }
+ }
+
+ return memory_is_tracked_n(addr, size);
+}
+#endif
+
static __always_inline bool check_region_inline(const void *addr,
size_t size, bool write,
unsigned long ret_ip)
@@ -177,7 +420,8 @@ static __always_inline bool check_region_inline(const void *addr,
if (unlikely(!addr_has_metadata(addr)))
return !kasan_report(addr, size, write, ret_ip);
- if (likely(!memory_is_poisoned(addr, size)))
+ if ((likely(!memory_is_poisoned(addr, size))) &&
+ (!write || likely(!memory_is_tracked(addr, size))))
return true;
return !kasan_report(addr, size, write, ret_ip);
@@ -196,7 +440,7 @@ bool kasan_byte_accessible(const void *addr)
if (!kasan_arch_is_ready())
return true;
- shadow_byte = READ_ONCE(*(s8 *)kasan_mem_to_shadow(addr));
+ shadow_byte = (s8)to_shadow_value_1_byte(READ_ONCE(*(s8 *)kasan_mem_to_shadow(addr)));
return shadow_byte >= 0 && shadow_byte < KASAN_GRANULE_SIZE;
}
diff --git a/mm/kasan/report_generic.c b/mm/kasan/report_generic.c
index f5b8e37b3805..e264c5f3c3e6 100644
--- a/mm/kasan/report_generic.c
+++ b/mm/kasan/report_generic.c
@@ -120,6 +120,12 @@ static const char *get_shadow_bug_type(struct kasan_report_info *info)
case KASAN_VMALLOC_INVALID:
bug_type = "vmalloc-out-of-bounds";
break;
+#ifdef CONFIG_KASAN_MEM_TRACK
+ default:
+ if (!((*shadow_addr) & 0x80))
+ bug_type = "memory-track";
+ break;
+#endif
}
return bug_type;
--
2.20.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC 2/2] kasan: add mem track interface and its test cases
2024-01-18 12:41 [RFC 0/2] kasan: introduce mem track feature lizhe.67
2024-01-18 12:41 ` [RFC 1/2] kasan: introduce mem track feature base on kasan lizhe.67
@ 2024-01-18 12:41 ` lizhe.67
2024-01-18 13:28 ` [RFC 0/2] kasan: introduce mem track feature Marco Elver
2024-01-22 4:49 ` Dmitry Vyukov
3 siblings, 0 replies; 11+ messages in thread
From: lizhe.67 @ 2024-01-18 12:41 UTC (permalink / raw)
To: ryabinin.a.a, glider, andreyknvl, dvyukov, vincenzo.frascino, akpm
Cc: kasan-dev, linux-mm, lizefan.x, lizhe.67
From: Li Zhe <lizhe.67@bytedance.com>
kasan_track_memory() and kasan_untrack_memory() are two interfaces
used to track memory write operations. We can use them to locate
problems where memory has been accidentally rewritten. Examples of
interface usages are shown in kasan_test_module.c
Signed-off-by: Li Zhe <lizhe.67@bytedance.com>
---
include/linux/kasan.h | 5 ++
mm/kasan/generic.c | 161 +++++++++++++++++++++++++++++++++++
mm/kasan/kasan_test_module.c | 26 ++++++
3 files changed, 192 insertions(+)
diff --git a/include/linux/kasan.h b/include/linux/kasan.h
index dbb06d789e74..ca5d93629ccf 100644
--- a/include/linux/kasan.h
+++ b/include/linux/kasan.h
@@ -604,4 +604,9 @@ void kasan_non_canonical_hook(unsigned long addr);
static inline void kasan_non_canonical_hook(unsigned long addr) { }
#endif /* CONFIG_KASAN_GENERIC || CONFIG_KASAN_SW_TAGS */
+#ifdef CONFIG_KASAN_MEM_TRACK
+int kasan_track_memory(const void *addr, size_t size);
+int kasan_untrack_memory(const void *addr, size_t size);
+#endif
+
#endif /* LINUX_KASAN_H */
diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c
index a204ddcbaa3f..61f3f5125338 100644
--- a/mm/kasan/generic.c
+++ b/mm/kasan/generic.c
@@ -402,6 +402,167 @@ static __always_inline bool memory_is_tracked(const void *addr, size_t size)
return memory_is_tracked_n(addr, size);
}
+
+/* deal with addr do not cross 8(shadow size)-byte boundary */
+static void __kasan_track_memory(const void *shadow_addr, size_t offset, size_t size)
+{
+ s8 mask;
+
+ if ((offset & 0x01) || (size & 0x01))
+ mask = kasan_track_mask_odd(size);
+ else
+ mask = kasan_track_mask_even(size);
+ offset = offset >> 1;
+ *(s8 *)shadow_addr |= mask << (KASAN_TRACK_VALUE_OFFSET + offset);
+}
+
+static void _kasan_track_memory(const void *addr, size_t size)
+{
+ unsigned int words;
+ const void *start = kasan_mem_to_shadow(addr);
+ unsigned int prefix = (unsigned long)addr % 8;
+
+ if (prefix) {
+ unsigned int tmp_size = (unsigned int)size;
+
+ tmp_size = min(8 - prefix, tmp_size);
+ __kasan_track_memory(start, prefix, tmp_size);
+ start++;
+ size -= tmp_size;
+ }
+
+ words = size / 8;
+ while (words) {
+ __kasan_track_memory(start, 0, 8);
+ start++;
+ words--;
+ }
+
+ if (size % 8)
+ __kasan_track_memory(start, 0, size % 8);
+}
+
+static inline bool is_cpu_entry_area_addr(unsigned long addr)
+{
+ return ((addr >= CPU_ENTRY_AREA_BASE) &&
+ (addr < CPU_ENTRY_AREA_BASE + CPU_ENTRY_AREA_MAP_SIZE));
+}
+
+static inline bool is_kernel_text_data(unsigned long addr)
+{
+ return ((addr >= (unsigned long)_stext) && (addr < (unsigned long)_end));
+}
+
+static bool can_track(unsigned long addr)
+{
+ if (!virt_addr_valid(addr) &&
+ !is_module_address(addr) &&
+#ifdef CONFIG_KASAN_VMALLOC
+ !is_vmalloc_addr((const void *)addr) &&
+#endif
+ !is_cpu_entry_area_addr(addr) &&
+ !is_kernel_text_data(addr)
+ )
+ return false;
+
+ return true;
+}
+
+int kasan_track_memory(const void *addr, size_t size)
+{
+ if (!kasan_arch_is_ready())
+ return -EINVAL;
+
+ if (unlikely(size == 0))
+ return -EINVAL;
+
+ if (unlikely(addr + size < addr))
+ return -EINVAL;
+
+ if (unlikely(!addr_has_metadata(addr)))
+ return -EINVAL;
+
+ if (likely(memory_is_poisoned(addr, size)))
+ return -EINVAL;
+
+ if (!can_track((unsigned long)addr))
+ return -EINVAL;
+
+ _kasan_track_memory(addr, size);
+ return 0;
+}
+EXPORT_SYMBOL(kasan_track_memory);
+
+/* deal with addr do not cross 8(shadow size)-byte boundary */
+static void __kasan_untrack_memory(const void *shadow_addr, size_t offset, size_t size)
+{
+ s8 mask;
+
+ if (size % 0x01) {
+ offset = (offset - 1) >> 1;
+ mask = kasan_track_mask_odd(size);
+ /*
+ * SIZE is odd, which means we may clear someone else's tracking flags of
+ * nearby tracked memory.
+ */
+ pr_info("It's possible to clear someone else's tracking flags\n");
+ } else {
+ offset = offset >> 1;
+ mask = kasan_track_mask_even(size);
+ }
+ *(s8 *)shadow_addr &= ~(mask << (KASAN_TRACK_VALUE_OFFSET + offset));
+}
+
+static void _kasan_untrack_memory(const void *addr, size_t size)
+{
+ unsigned int words;
+ const void *start = kasan_mem_to_shadow(addr);
+ unsigned int prefix = (unsigned long)addr % 8;
+
+ if (prefix) {
+ unsigned int tmp_size = (unsigned int)size;
+
+ tmp_size = min(8 - prefix, tmp_size);
+ __kasan_untrack_memory(start, prefix, tmp_size);
+ start++;
+ size -= tmp_size;
+ }
+
+ words = size / 8;
+ while (words) {
+ __kasan_untrack_memory(start, 0, 8);
+ start++;
+ words--;
+ }
+
+ if (size % 8)
+ __kasan_untrack_memory(start, 0, size % 8);
+}
+
+int kasan_untrack_memory(const void *addr, size_t size)
+{
+ if (!kasan_arch_is_ready())
+ return -EINVAL;
+
+ if (unlikely(size == 0))
+ return -EINVAL;
+
+ if (unlikely(addr + size < addr))
+ return -EINVAL;
+
+ if (unlikely(!addr_has_metadata(addr)))
+ return -EINVAL;
+
+ if (likely(memory_is_poisoned(addr, size)))
+ return -EINVAL;
+
+ if (!can_track((unsigned long)addr))
+ return -EINVAL;
+
+ _kasan_untrack_memory(addr, size);
+ return 0;
+}
+EXPORT_SYMBOL(kasan_untrack_memory);
#endif
static __always_inline bool check_region_inline(const void *addr,
diff --git a/mm/kasan/kasan_test_module.c b/mm/kasan/kasan_test_module.c
index 8b7b3ea2c74e..1dba44dbfc81 100644
--- a/mm/kasan/kasan_test_module.c
+++ b/mm/kasan/kasan_test_module.c
@@ -62,6 +62,31 @@ static noinline void __init copy_user_test(void)
kfree(kmem);
}
+#ifdef CONFIG_KASAN_MEM_TRACK
+static noinline void __init mem_track_test(void)
+{
+ int ret;
+ int *ptr = kmalloc(sizeof(int), GFP_KERNEL);
+
+ if (!ptr)
+ return;
+
+ ret = kasan_track_memory(ptr, sizeof(int));
+ if (ret) {
+ pr_warn("There is a bug of mem_track\n");
+ goto out;
+ }
+ pr_info("trigger mem_track\n");
+ WRITE_ONCE(*ptr, 1);
+ kasan_untrack_memory(ptr, sizeof(int));
+
+out:
+ kfree(ptr);
+}
+#else
+static inline void __init mem_track_test(void) {}
+#endif
+
static int __init test_kasan_module_init(void)
{
/*
@@ -72,6 +97,7 @@ static int __init test_kasan_module_init(void)
bool multishot = kasan_save_enable_multi_shot();
copy_user_test();
+ mem_track_test();
kasan_restore_multi_shot(multishot);
return -EAGAIN;
--
2.20.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [RFC 0/2] kasan: introduce mem track feature
2024-01-18 12:41 [RFC 0/2] kasan: introduce mem track feature lizhe.67
2024-01-18 12:41 ` [RFC 1/2] kasan: introduce mem track feature base on kasan lizhe.67
2024-01-18 12:41 ` [RFC 2/2] kasan: add mem track interface and its test cases lizhe.67
@ 2024-01-18 13:28 ` Marco Elver
2024-01-18 14:30 ` lizhe.67
2024-01-22 4:49 ` Dmitry Vyukov
3 siblings, 1 reply; 11+ messages in thread
From: Marco Elver @ 2024-01-18 13:28 UTC (permalink / raw)
To: lizhe.67
Cc: ryabinin.a.a, glider, andreyknvl, dvyukov, vincenzo.frascino,
akpm, kasan-dev, linux-mm, lizefan.x
On Thu, 18 Jan 2024 at 13:41, lizhe.67 via kasan-dev
<kasan-dev@googlegroups.com> wrote:
>
> From: Li Zhe <lizhe.67@bytedance.com>
>
> 1. Problem
> ==========
> KASAN is a tools for detecting memory bugs like out-of-bounds and
> use-after-free. In Generic KASAN mode, it use shadow memory to record
> the accessible information of the memory. After we allocate a memory
> from kernel, the shadow memory corresponding to this memory will be
> marked as accessible.
> In our daily development, memory problems often occur. If a task
> accidentally modifies memory that does not belong to itself but has
> been allocated, some strange phenomena may occur. This kind of problem
> brings a lot of trouble to our development, and unluckily, this kind of
> problem cannot be captured by KASAN. This is because as long as the
> accessible information in shadow memory shows that the corresponding
> memory can be accessed, KASAN considers the memory access to be legal.
>
> 2. Solution
> ===========
> We solve this problem by introducing mem track feature base on KASAN
> with Generic KASAN mode. In the current kernel implementation, we use
> bits 0-2 of each shadow memory byte to store how many bytes in the 8
> byte memory corresponding to the shadow memory byte can be accessed.
> When a 8-byte-memory is inaccessible, the highest bit of its
> corresponding shadow memory value is 1. Therefore, the key idea is that
> we can use the currently unused four bits 3-6 in the shadow memory to
> record relevant track information. Which means, we can use one bit to
> track 2 bytes of memory. If the track bit of the shadow mem corresponding
> to a certain memory is 1, it means that the corresponding 2-byte memory
> is tracked. By adding this check logic to KASAN's callback function, we
> can use KASAN's ability to capture allocated memory corruption.
Note: "track" is already an overloaded word with KASAN, meaning some
allocation/free stack trace info + CPU id, task etc.
> 3. Simple usage
> ===========
> The first step is to mark the memory as tracked after the allocation is
> completed.
> The second step is to remove the tracked mark of the memory before the
> legal access process and re-mark the memory as tracked after finishing
> the legal access process.
It took me several readings to understand what problem you're actually
trying to solve. AFAIK, you're trying to add custom poison/unpoison
functions.
From what I can tell this is duplicating functionality: it is
perfectly legal to poison and unpoison memory while it is already
allocated. I think it used to be the case the kasan_poison/unpoison()
were API functions, but since tag-based KASAN modes this was changed
to hide the complexity here.
But you could simply expose a simpler variant of kasan_{un,}poison,
e.g. kasan_poison/unpoison_custom(). You'd have to introduce another
type (see where KASAN_PAGE_FREE, KASAN_SLAB_FREE is defined) to
distinguish this custom type from other poisoned memory.
Obviously it would be invalid to kasan_poison_custom() memory that is
already poisoned, because that would discard the pre-existing poison
type.
With that design, I believe it would also work for the inline version
of KASAN and not just outline version.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 0/2] kasan: introduce mem track feature
2024-01-18 13:28 ` [RFC 0/2] kasan: introduce mem track feature Marco Elver
@ 2024-01-18 14:30 ` lizhe.67
2024-01-19 16:06 ` Andrey Konovalov
0 siblings, 1 reply; 11+ messages in thread
From: lizhe.67 @ 2024-01-18 14:30 UTC (permalink / raw)
To: elver
Cc: akpm, andreyknvl, dvyukov, glider, kasan-dev, linux-mm,
lizefan.x, lizhe.67, ryabinin.a.a, vincenzo.frascino
On Thu, 18 Jan 2024 14:28:00, elver@google.com wrote:
>> 1. Problem
>> ==========
>> KASAN is a tools for detecting memory bugs like out-of-bounds and
>> use-after-free. In Generic KASAN mode, it use shadow memory to record
>> the accessible information of the memory. After we allocate a memory
>> from kernel, the shadow memory corresponding to this memory will be
>> marked as accessible.
>> In our daily development, memory problems often occur. If a task
>> accidentally modifies memory that does not belong to itself but has
>> been allocated, some strange phenomena may occur. This kind of problem
>> brings a lot of trouble to our development, and unluckily, this kind of
>> problem cannot be captured by KASAN. This is because as long as the
>> accessible information in shadow memory shows that the corresponding
>> memory can be accessed, KASAN considers the memory access to be legal.
>>
>> 2. Solution
>> ===========
>> We solve this problem by introducing mem track feature base on KASAN
>> with Generic KASAN mode. In the current kernel implementation, we use
>> bits 0-2 of each shadow memory byte to store how many bytes in the 8
>> byte memory corresponding to the shadow memory byte can be accessed.
>> When a 8-byte-memory is inaccessible, the highest bit of its
>> corresponding shadow memory value is 1. Therefore, the key idea is that
>> we can use the currently unused four bits 3-6 in the shadow memory to
>> record relevant track information. Which means, we can use one bit to
>> track 2 bytes of memory. If the track bit of the shadow mem corresponding
>> to a certain memory is 1, it means that the corresponding 2-byte memory
>> is tracked. By adding this check logic to KASAN's callback function, we
>> can use KASAN's ability to capture allocated memory corruption.
>
>Note: "track" is already an overloaded word with KASAN, meaning some
>allocation/free stack trace info + CPU id, task etc.
Thanks for the reminder, I will change it to another name in the v2 patch.
>> 3. Simple usage
>> ===========
>> The first step is to mark the memory as tracked after the allocation is
>> completed.
>> The second step is to remove the tracked mark of the memory before the
>> legal access process and re-mark the memory as tracked after finishing
>> the legal access process.
>
>It took me several readings to understand what problem you're actually
>trying to solve. AFAIK, you're trying to add custom poison/unpoison
>functions.
>
>From what I can tell this is duplicating functionality: it is
>perfectly legal to poison and unpoison memory while it is already
>allocated. I think it used to be the case the kasan_poison/unpoison()
>were API functions, but since tag-based KASAN modes this was changed
>to hide the complexity here.
>
>But you could simply expose a simpler variant of kasan_{un,}poison,
>e.g. kasan_poison/unpoison_custom(). You'd have to introduce another
>type (see where KASAN_PAGE_FREE, KASAN_SLAB_FREE is defined) to
>distinguish this custom type from other poisoned memory.
>
>Obviously it would be invalid to kasan_poison_custom() memory that is
>already poisoned, because that would discard the pre-existing poison
>type.
>
>With that design, I believe it would also work for the inline version
>of KASAN and not just outline version.
Thank you for your review!
Yes I am trying to add custom poison/unpoison functions which can monitor
memory in a fine-grained manner, and not affect the original functionality
of kasan. For example, for a 100-byte variable, I may only want to monitor
certain two bytes (byte 3 and 4) in it. According to my understanding,
kasan_poison/unpoison() can not detect the middle bytes individually. So I
don't think function kasan_poison/unpoison() can do what I want.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 0/2] kasan: introduce mem track feature
2024-01-18 14:30 ` lizhe.67
@ 2024-01-19 16:06 ` Andrey Konovalov
2024-01-22 3:03 ` lizhe.67
0 siblings, 1 reply; 11+ messages in thread
From: Andrey Konovalov @ 2024-01-19 16:06 UTC (permalink / raw)
To: lizhe.67, glider
Cc: elver, akpm, dvyukov, kasan-dev, linux-mm, lizefan.x,
ryabinin.a.a, vincenzo.frascino
On Thu, Jan 18, 2024 at 3:30 PM <lizhe.67@bytedance.com> wrote:
>
> Yes I am trying to add custom poison/unpoison functions which can monitor
> memory in a fine-grained manner, and not affect the original functionality
> of kasan. For example, for a 100-byte variable, I may only want to monitor
> certain two bytes (byte 3 and 4) in it. According to my understanding,
> kasan_poison/unpoison() can not detect the middle bytes individually. So I
> don't think function kasan_poison/unpoison() can do what I want.
I'm not sure this type of tracking belongs within KASAN.
If there are only a few locations you want to monitor, perhaps a
separate tools based on watchpoints would make more sense?
Another alternative is to base this functionality on KMSAN: it already
allows for bit-level precision. Plus, it would allow to only report
when the marked memory is actually being used, not when it's just
being copied. Perhaps Alexander can comment on whether this makes
sense.
If we decide to add this to KASAN or KMSAN, we need to least also add
some in-tree users to demonstrate the functionality. And it would be
great to find some bugs with it, but perhaps syzbot will be able to
take care of that.
Thank you!
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 0/2] kasan: introduce mem track feature
2024-01-19 16:06 ` Andrey Konovalov
@ 2024-01-22 3:03 ` lizhe.67
0 siblings, 0 replies; 11+ messages in thread
From: lizhe.67 @ 2024-01-22 3:03 UTC (permalink / raw)
To: andreyknvl
Cc: akpm, dvyukov, elver, glider, kasan-dev, linux-mm, lizefan.x,
lizhe.67, ryabinin.a.a, vincenzo.frascino
On Thu, 18 Jan 2024 14:28:00, andreyknvl@gmail.com wrote:
>
>> Yes I am trying to add custom poison/unpoison functions which can monitor
>> memory in a fine-grained manner, and not affect the original functionality
>> of kasan. For example, for a 100-byte variable, I may only want to monitor
>> certain two bytes (byte 3 and 4) in it. According to my understanding,
>> kasan_poison/unpoison() can not detect the middle bytes individually. So I
>> don't think function kasan_poison/unpoison() can do what I want.
>
>I'm not sure this type of tracking belongs within KASAN.
>
>If there are only a few locations you want to monitor, perhaps a
>separate tools based on watchpoints would make more sense?
Thank you for your review!
Yes hardware breakpoint is a method to monitor a few locations. However,
this depends on the hardware implementation and there will be a problem of
limited number of hardware watchpoints, and software solution does not have
these problems.
>
>Another alternative is to base this functionality on KMSAN: it already
>allows for bit-level precision. Plus, it would allow to only report
>when the marked memory is actually being used, not when it's just
>being copied. Perhaps Alexander can comment on whether this makes
>sense.
>
>If we decide to add this to KASAN or KMSAN, we need to least also add
>some in-tree users to demonstrate the functionality. And it would be
>great to find some bugs with it, but perhaps syzbot will be able to
>take care of that.
>
>Thank you!
In my opinion, currently this feature will only appear in our daily debugging
process. Maybe this feature can be used in perf later. Or do you have any
suggestions for in-tree users?
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 0/2] kasan: introduce mem track feature
2024-01-18 12:41 [RFC 0/2] kasan: introduce mem track feature lizhe.67
` (2 preceding siblings ...)
2024-01-18 13:28 ` [RFC 0/2] kasan: introduce mem track feature Marco Elver
@ 2024-01-22 4:49 ` Dmitry Vyukov
2024-01-22 6:26 ` lizhe.67
3 siblings, 1 reply; 11+ messages in thread
From: Dmitry Vyukov @ 2024-01-22 4:49 UTC (permalink / raw)
To: lizhe.67
Cc: ryabinin.a.a, glider, andreyknvl, vincenzo.frascino, akpm,
kasan-dev, linux-mm, lizefan.x
On Thu, 18 Jan 2024 at 13:41, <lizhe.67@bytedance.com> wrote:
>
> From: Li Zhe <lizhe.67@bytedance.com>
>
> 1. Problem
> ==========
> KASAN is a tools for detecting memory bugs like out-of-bounds and
> use-after-free. In Generic KASAN mode, it use shadow memory to record
> the accessible information of the memory. After we allocate a memory
> from kernel, the shadow memory corresponding to this memory will be
> marked as accessible.
> In our daily development, memory problems often occur. If a task
> accidentally modifies memory that does not belong to itself but has
> been allocated, some strange phenomena may occur. This kind of problem
> brings a lot of trouble to our development, and unluckily, this kind of
> problem cannot be captured by KASAN. This is because as long as the
> accessible information in shadow memory shows that the corresponding
> memory can be accessed, KASAN considers the memory access to be legal.
>
> 2. Solution
> ===========
> We solve this problem by introducing mem track feature base on KASAN
> with Generic KASAN mode. In the current kernel implementation, we use
> bits 0-2 of each shadow memory byte to store how many bytes in the 8
> byte memory corresponding to the shadow memory byte can be accessed.
> When a 8-byte-memory is inaccessible, the highest bit of its
> corresponding shadow memory value is 1. Therefore, the key idea is that
> we can use the currently unused four bits 3-6 in the shadow memory to
> record relevant track information. Which means, we can use one bit to
> track 2 bytes of memory. If the track bit of the shadow mem corresponding
> to a certain memory is 1, it means that the corresponding 2-byte memory
> is tracked. By adding this check logic to KASAN's callback function, we
> can use KASAN's ability to capture allocated memory corruption.
>
> 3. Simple usage
> ===========
> The first step is to mark the memory as tracked after the allocation is
> completed.
> The second step is to remove the tracked mark of the memory before the
> legal access process and re-mark the memory as tracked after finishing
> the legal access process.
KASAN already has a notion of memory poisoning/unpoisoning.
See kasan_unpoison_range function. We don't export kasan_poison_range,
but if you do local debuggng, you can export it locally.
> The first patch completes the implementation of the mem track, and the
> second patch provides an interface for using this facility, as well as
> a testcase for the interface.
>
> Li Zhe (2):
> kasan: introduce mem track feature base on kasan
> kasan: add mem track interface and its test cases
>
> include/linux/kasan.h | 5 +
> lib/Kconfig.kasan | 9 +
> mm/kasan/generic.c | 437 +++++++++++++++++++++++++++++++++--
> mm/kasan/kasan_test_module.c | 26 +++
> mm/kasan/report_generic.c | 6 +
> 5 files changed, 467 insertions(+), 16 deletions(-)
>
> --
> 2.20.1
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 0/2] kasan: introduce mem track feature
2024-01-22 4:49 ` Dmitry Vyukov
@ 2024-01-22 6:26 ` lizhe.67
2024-01-22 7:03 ` Dmitry Vyukov
0 siblings, 1 reply; 11+ messages in thread
From: lizhe.67 @ 2024-01-22 6:26 UTC (permalink / raw)
To: dvyukov
Cc: akpm, andreyknvl, glider, kasan-dev, linux-mm, lizefan.x,
lizhe.67, ryabinin.a.a, vincenzo.frascino
On Mon, 22 Jan 2024 05:49:29 dvyukov@google.com wrote:
>>
>> From: Li Zhe <lizhe.67@bytedance.com>
>>
>> 1. Problem
>> ==========
>> KASAN is a tools for detecting memory bugs like out-of-bounds and
>> use-after-free. In Generic KASAN mode, it use shadow memory to record
>> the accessible information of the memory. After we allocate a memory
>> from kernel, the shadow memory corresponding to this memory will be
>> marked as accessible.
>> In our daily development, memory problems often occur. If a task
>> accidentally modifies memory that does not belong to itself but has
>> been allocated, some strange phenomena may occur. This kind of problem
>> brings a lot of trouble to our development, and unluckily, this kind of
>> problem cannot be captured by KASAN. This is because as long as the
>> accessible information in shadow memory shows that the corresponding
>> memory can be accessed, KASAN considers the memory access to be legal.
>>
>> 2. Solution
>> ===========
>> We solve this problem by introducing mem track feature base on KASAN
>> with Generic KASAN mode. In the current kernel implementation, we use
>> bits 0-2 of each shadow memory byte to store how many bytes in the 8
>> byte memory corresponding to the shadow memory byte can be accessed.
>> When a 8-byte-memory is inaccessible, the highest bit of its
>> corresponding shadow memory value is 1. Therefore, the key idea is that
>> we can use the currently unused four bits 3-6 in the shadow memory to
>> record relevant track information. Which means, we can use one bit to
>> track 2 bytes of memory. If the track bit of the shadow mem corresponding
>> to a certain memory is 1, it means that the corresponding 2-byte memory
>> is tracked. By adding this check logic to KASAN's callback function, we
>> can use KASAN's ability to capture allocated memory corruption.
>>
>> 3. Simple usage
>> ===========
>> The first step is to mark the memory as tracked after the allocation is
>> completed.
>> The second step is to remove the tracked mark of the memory before the
>> legal access process and re-mark the memory as tracked after finishing
>> the legal access process.
>
>KASAN already has a notion of memory poisoning/unpoisoning.
>See kasan_unpoison_range function. We don't export kasan_poison_range,
>but if you do local debuggng, you can export it locally.
Thank you for your review!
For example, for a 100-byte variable, I may only want to monitor certain
two bytes (byte 3 and 4) in it. According to my understanding,
kasan_poison/unpoison() can not detect the middle bytes individually. So I
don't think function kasan_poison_range() can do what I want.
>
>> The first patch completes the implementation of the mem track, and the
>> second patch provides an interface for using this facility, as well as
>> a testcase for the interface.
>>
>> Li Zhe (2):
>> kasan: introduce mem track feature base on kasan
>> kasan: add mem track interface and its test cases
>>
>> include/linux/kasan.h | 5 +
>> lib/Kconfig.kasan | 9 +
>> mm/kasan/generic.c | 437 +++++++++++++++++++++++++++++++++--
>> mm/kasan/kasan_test_module.c | 26 +++
>> mm/kasan/report_generic.c | 6 +
>> 5 files changed, 467 insertions(+), 16 deletions(-)
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 0/2] kasan: introduce mem track feature
2024-01-22 6:26 ` lizhe.67
@ 2024-01-22 7:03 ` Dmitry Vyukov
2024-01-23 6:27 ` lizhe.67
0 siblings, 1 reply; 11+ messages in thread
From: Dmitry Vyukov @ 2024-01-22 7:03 UTC (permalink / raw)
To: lizhe.67
Cc: akpm, andreyknvl, glider, kasan-dev, linux-mm, lizefan.x,
ryabinin.a.a, vincenzo.frascino
On Mon, 22 Jan 2024 at 07:26, <lizhe.67@bytedance.com> wrote:
> >> From: Li Zhe <lizhe.67@bytedance.com>
> >>
> >> 1. Problem
> >> ==========
> >> KASAN is a tools for detecting memory bugs like out-of-bounds and
> >> use-after-free. In Generic KASAN mode, it use shadow memory to record
> >> the accessible information of the memory. After we allocate a memory
> >> from kernel, the shadow memory corresponding to this memory will be
> >> marked as accessible.
> >> In our daily development, memory problems often occur. If a task
> >> accidentally modifies memory that does not belong to itself but has
> >> been allocated, some strange phenomena may occur. This kind of problem
> >> brings a lot of trouble to our development, and unluckily, this kind of
> >> problem cannot be captured by KASAN. This is because as long as the
> >> accessible information in shadow memory shows that the corresponding
> >> memory can be accessed, KASAN considers the memory access to be legal.
> >>
> >> 2. Solution
> >> ===========
> >> We solve this problem by introducing mem track feature base on KASAN
> >> with Generic KASAN mode. In the current kernel implementation, we use
> >> bits 0-2 of each shadow memory byte to store how many bytes in the 8
> >> byte memory corresponding to the shadow memory byte can be accessed.
> >> When a 8-byte-memory is inaccessible, the highest bit of its
> >> corresponding shadow memory value is 1. Therefore, the key idea is that
> >> we can use the currently unused four bits 3-6 in the shadow memory to
> >> record relevant track information. Which means, we can use one bit to
> >> track 2 bytes of memory. If the track bit of the shadow mem corresponding
> >> to a certain memory is 1, it means that the corresponding 2-byte memory
> >> is tracked. By adding this check logic to KASAN's callback function, we
> >> can use KASAN's ability to capture allocated memory corruption.
> >>
> >> 3. Simple usage
> >> ===========
> >> The first step is to mark the memory as tracked after the allocation is
> >> completed.
> >> The second step is to remove the tracked mark of the memory before the
> >> legal access process and re-mark the memory as tracked after finishing
> >> the legal access process.
> >
> >KASAN already has a notion of memory poisoning/unpoisoning.
> >See kasan_unpoison_range function. We don't export kasan_poison_range,
> >but if you do local debuggng, you can export it locally.
>
> Thank you for your review!
>
> For example, for a 100-byte variable, I may only want to monitor certain
> two bytes (byte 3 and 4) in it. According to my understanding,
> kasan_poison/unpoison() can not detect the middle bytes individually. So I
> don't think function kasan_poison_range() can do what I want.
That's something to note in the description/comments.
How many ranges do you intend to protect this way?
If that's not too many, then a better option would be to poison these
ranges normally and store ranges that a thread can access currently on
a side.
This will give both 1-byte precision, filtering for reads/writes
separately and better diagnostics.
> >> The first patch completes the implementation of the mem track, and the
> >> second patch provides an interface for using this facility, as well as
> >> a testcase for the interface.
> >>
> >> Li Zhe (2):
> >> kasan: introduce mem track feature base on kasan
> >> kasan: add mem track interface and its test cases
> >>
> >> include/linux/kasan.h | 5 +
> >> lib/Kconfig.kasan | 9 +
> >> mm/kasan/generic.c | 437 +++++++++++++++++++++++++++++++++--
> >> mm/kasan/kasan_test_module.c | 26 +++
> >> mm/kasan/report_generic.c | 6 +
> >> 5 files changed, 467 insertions(+), 16 deletions(-)
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC 0/2] kasan: introduce mem track feature
2024-01-22 7:03 ` Dmitry Vyukov
@ 2024-01-23 6:27 ` lizhe.67
0 siblings, 0 replies; 11+ messages in thread
From: lizhe.67 @ 2024-01-23 6:27 UTC (permalink / raw)
To: dvyukov
Cc: akpm, andreyknvl, glider, kasan-dev, linux-mm, lizefan.x,
lizhe.67, ryabinin.a.a, vincenzo.frascino
On Mon, 22 Jan 2024 08:03:17, dvyukov@google.com wrote:
>> >> From: Li Zhe <lizhe.67@bytedance.com>
>> >>
>> >> 1. Problem
>> >> ==========
>> >> KASAN is a tools for detecting memory bugs like out-of-bounds and
>> >> use-after-free. In Generic KASAN mode, it use shadow memory to record
>> >> the accessible information of the memory. After we allocate a memory
>> >> from kernel, the shadow memory corresponding to this memory will be
>> >> marked as accessible.
>> >> In our daily development, memory problems often occur. If a task
>> >> accidentally modifies memory that does not belong to itself but has
>> >> been allocated, some strange phenomena may occur. This kind of problem
>> >> brings a lot of trouble to our development, and unluckily, this kind of
>> >> problem cannot be captured by KASAN. This is because as long as the
>> >> accessible information in shadow memory shows that the corresponding
>> >> memory can be accessed, KASAN considers the memory access to be legal.
>> >>
>> >> 2. Solution
>> >> ===========
>> >> We solve this problem by introducing mem track feature base on KASAN
>> >> with Generic KASAN mode. In the current kernel implementation, we use
>> >> bits 0-2 of each shadow memory byte to store how many bytes in the 8
>> >> byte memory corresponding to the shadow memory byte can be accessed.
>> >> When a 8-byte-memory is inaccessible, the highest bit of its
>> >> corresponding shadow memory value is 1. Therefore, the key idea is that
>> >> we can use the currently unused four bits 3-6 in the shadow memory to
>> >> record relevant track information. Which means, we can use one bit to
>> >> track 2 bytes of memory. If the track bit of the shadow mem corresponding
>> >> to a certain memory is 1, it means that the corresponding 2-byte memory
>> >> is tracked. By adding this check logic to KASAN's callback function, we
>> >> can use KASAN's ability to capture allocated memory corruption.
>> >>
>> >> 3. Simple usage
>> >> ===========
>> >> The first step is to mark the memory as tracked after the allocation is
>> >> completed.
>> >> The second step is to remove the tracked mark of the memory before the
>> >> legal access process and re-mark the memory as tracked after finishing
>> >> the legal access process.
>> >
>> >KASAN already has a notion of memory poisoning/unpoisoning.
>> >See kasan_unpoison_range function. We don't export kasan_poison_range,
>> >but if you do local debuggng, you can export it locally.
>>
>> Thank you for your review!
>>
>> For example, for a 100-byte variable, I may only want to monitor certain
>> two bytes (byte 3 and 4) in it. According to my understanding,
>> kasan_poison/unpoison() can not detect the middle bytes individually. So I
>> don't think function kasan_poison_range() can do what I want.
>
>That's something to note in the description/comments.
>
>How many ranges do you intend to protect this way?
>If that's not too many, then a better option would be to poison these
>ranges normally and store ranges that a thread can access currently on
>a side.
>This will give both 1-byte precision, filtering for reads/writes
>separately and better diagnostics.
OK I will find a better method to solve this problem.
Thank you!
>
>> >> The first patch completes the implementation of the mem track, and the
>> >> second patch provides an interface for using this facility, as well as
>> >> a testcase for the interface.
>> >>
>> >> Li Zhe (2):
>> >> kasan: introduce mem track feature base on kasan
>> >> kasan: add mem track interface and its test cases
>> >>
>> >> include/linux/kasan.h | 5 +
>> >> lib/Kconfig.kasan | 9 +
>> >> mm/kasan/generic.c | 437 +++++++++++++++++++++++++++++++++--
>> >> mm/kasan/kasan_test_module.c | 26 +++
>> >> mm/kasan/report_generic.c | 6 +
>> >> 5 files changed, 467 insertions(+), 16 deletions(-)
^ permalink raw reply [flat|nested] 11+ messages in thread