본문 바로가기

리눅스 커널의 구조와 원리/14. 메모리 관리

[Debug] Simple slub configuration

If you enable the following configuration, the slub debugging feature is enabled without using SLUB_DEBUG.

CONFIG_POMPEII_SLUB_SIMPLE_DEBUG=y
CONFIG_POMPEII_SLUB_FREEPOINTER_DEBUG=y

The advantage of this patch is that you are not going through any performance degradation. 
This patch is generated based on v4.9.

[Part.1]

slub: simple debug feature added
---
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index 8def55e..21fa6c4 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -95,6 +95,20 @@
  return __change_memory_common(start, size, set_mask, clear_mask);
 }


+#ifdef CONFIG_POMPEII_SLUB_UAF_DEBUG
+int set_memory_valid(unsigned long addr, int numpages, int enable)
+{
+if (enable)
+ return __change_memory_common(addr, PAGE_SIZE * numpages,
+ __pgprot(PTE_VALID),
+ __pgprot(0));
+else
+ return __change_memory_common(addr, PAGE_SIZE * numpages,
+ __pgprot(0),
+ __pgprot(PTE_VALID));
+}
+#endif
+
 int set_memory_ro(unsigned long addr, int numpages)
 {
  return change_memory_common(addr, numpages,

diff --git a/drivers/soc/qcom/POMPEII/Kconfig b/drivers/soc/qcom/POMPEII/Kconfig
index 5c56cf7..e2962a3 100644
--- a/drivers/soc/qcom/POMPEII/Kconfig
+++ b/drivers/soc/qcom/POMPEII/Kconfig
@@ -426,6 +426,26 @@
         help
           Say 'y" here to include support for dual screen.
 

+config POMPEII_SLUB_SIMPLE_DEBUG
+        bool "Support SLUB UAF debug"
+ depends on MACH_POMPEII && !SLUB_DEBUG
+ default n
+ help
+   Say 'y' here to include SLUB debug feature for UAF.
+
+config POMPEII_SLUB_SIMPLE_DEBUG
+        bool "Support SLUB freepointer debug"
+ depends on POMPEII_SLUB_SIMPLE_DEBUG
+ default y
+ help
+   Say 'y' here to include SLUB freepointer debug feature.
+config POMPEII_SLUB_SIMPLE_DEBUG
+        bool "Support SLUB UAF debug using page attribute"
+ depends on POMPEII_SLUB_SIMPLE_DEBUG
+ default n
+ help
+   Say 'y' here to include another SLUB UAF feature"
+
 endmenu

 menu "POMPEII QFPROM Values"
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 467cf11..324900f 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c

@@ -806,6 +806,11 @@
  s->size = s->object_size = size;
  s->align = calculate_alignment(flags, ARCH_KMALLOC_MINALIGN, size);


+#ifdef CONFIG_SLUB_UAF_DEBUG
+ if (s->object_size >= PAGE_SIZE)
+ s->align = PAGE_SIZE;
+#endif
+
  slab_init_memcg_params(s);
  err = __kmem_cache_create(s, flags);

diff --git a/mm/slub.c b/mm/slub.c
index 80e83e2..e7b7115 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -236,14 +236,101 @@
  *  Core slab cache functions
  *******************************************************************/

+#ifdef CONFIG_POMPEII_SLUB_UAF_DEBUG
+int set_memory_valid(unsigned long addr, int numpages, int enable);
+
+static inline void slab_set_page_valid(struct kmem_cache *s, void *object, int enable)
+{
+ int numpages = (s->object_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ set_memory_valid(((unsigned long)object), numpages, enable);
+}
+#endif
+
+#ifdef CONFIG_POMPEII_SLUB_SIMPLE_DEBUG
+
+#define SIMPLE_TRACK_ADDRS_COUNT 10
+struct simple_track {
+ void **fp1;
+#ifdef CONFIG_POMPEII_SLUB_FREEPOINTER_DEBUG
+ void **fp2;
+#endif
+ unsigned long addr;
+#ifdef CONFIG_STACKTRACE
+ unsigned long addrs[SIMPLE_TRACK_ADDRS_COUNT]; /* Called from address */
+#endif
+ int cpu; /* Was running on cpu */
+ int pid; /* Pid context */
+};
+
+static noinline struct simple_track *get_simple_track(struct kmem_cache *s, void *object)
+{
+ if(unlikely(s->offset) || s->object_size < sizeof(struct simple_track))
+ return NULL;
+ return object;
+}
+
+static void set_simple_track(struct kmem_cache *s, void *object, unsigned long addr)
+{
+ struct simple_track *p = get_simple_track(s, object);
+
+ if(unlikely(!p))
+ return;
+
+ if (addr) {
+#ifdef CONFIG_STACKTRACE
+ struct stack_trace trace;
+ int i;
+
+ trace.nr_entries = 0;
+ trace.max_entries = SIMPLE_TRACK_ADDRS_COUNT;
+ trace.entries = p->addrs;
+ trace.skip = 3;
+ kasan_disable_current();
+ save_stack_trace(&trace);
+ kasan_enable_current();
+
+ /* See rant in lockdep.c */
+ if (trace.nr_entries != 0 &&
+     trace.entries[trace.nr_entries - 1] == ULONG_MAX)
+ trace.nr_entries--;
+
+ for (i = trace.nr_entries; i < SIMPLE_TRACK_ADDRS_COUNT; i++)
+ p->addrs[i] = 0;
+#endif
+ p->addr = addr;
+ p->cpu = smp_processor_id();
+ p->pid = current->pid;
+ }
+ else {
+ p->addr = 0xDEAD000000000001;
+ p->cpu = -1;
+ p->pid = -1;
+ }
+}
+#endif
+

 static inline void *get_freepointer(struct kmem_cache *s, void *object)
 {
+#ifdef CONFIG_POMPEII_SLUB_FREEPOINTER_DEBUG
+ struct simple_track *p = get_simple_track(s, object);
+
+ if(unlikely(!p))
+ return *(void **)(object + s->offset);
+
+ BUG_ON(p->fp1 != p->fp2);
+ return p->fp2;
+#else
  return *(void **)(object + s->offset);
+#endif
 }
 
 static void prefetch_freepointer(const struct kmem_cache *s, void *object)
 {
+#ifdef CONFIG_POMPEII_SLUB_FREEPOINTER_DEBUG
+ // Do nothing...
+#else
  prefetch(object + s->offset);
+#endif
 }

 static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
@@ -259,9 +346,77 @@

 static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
 {
+#ifdef CONFIG_POMPEII_SLUB_FREEPOINTER_DEBUG
+ struct simple_track *p = get_simple_track(s, object);
+ if(unlikely(!p)) {
+ *(void **)(object + s->offset) = fp;
+ return;
+ }
+
+ p->fp1 = p->fp2 = fp;
+#else
  *(void **)(object + s->offset) = fp;
+#endif
 }
 
+#ifdef CONFIG_POMPEII_SLUB_SIMPLE_DEBUG
+/* Supports checking bulk free of a constructed freelist */
+static noinline void free_simple_debug_processing(
+ struct kmem_cache *s, struct page *page,
+ void *head, void *tail, int bulk_cnt,
+ unsigned long addr)
+{
+ struct kmem_cache_node *n = get_node(s, page_to_nid(page));
+ void *object = head;
+ int cnt = 0;
+ unsigned long uninitialized_var(flags);
+
+ spin_lock_irqsave(&n->list_lock, flags);
+ bit_spin_lock(PG_locked, &page->flags);
+
+simple_next_object:
+ cnt++;
+
+ set_simple_track(s, object, addr);
+
+ /* Reached end of constructed freelist yet? */
+ if (object != tail) {
+ object = get_freepointer(s, object);
+ goto simple_next_object;
+ }
+
+ if (cnt != bulk_cnt)
+ BUG();
+
+ __bit_spin_unlock(PG_locked, &page->flags);
+ spin_unlock_irqrestore(&n->list_lock, flags);
+
+#ifdef CONFIG_ POMPEII_SLUB_UAF_DEBUG
+ if (s->object_size >= PAGE_SIZE)
+ slab_set_page_valid(s, head, 0);
+#endif
+
+ return;
+}
+
+#ifdef CONFIG_POMPEII_SLUB_FREEPOINTER_DEBUG
+static noinline void slab_alloc_debugging(struct kmem_cache *s, void *object)
+{
+ struct simple_track *p = get_simple_track(s, object);
+
+ if(unlikely(!object || !p))
+ return;
+
+ p->fp1 = NULL;
+ p->fp2 = NULL;
+ p->addr = 0xDEAD000000000002;
+ p->cpu = -1;
+ p->pid = -1;
+}
+#endif
+
+#endif
+

 /* Loop over all objects in a slab */
 #define for_each_object(__p, __s, __addr, __objects) \
  for (__p = fixup_red_left(__s, __addr); \
@@ -1665,8 +1820,16 @@

  slab_pad_check(s, page);
  for_each_object(p, s, page_address(page),
+#ifdef CONFIG_ POMPEII_SLUB_UAF_DEBUG
+ page->objects) {
+ if (s->object_size >= PAGE_SIZE)
+ slab_set_page_valid(s, p, 1);
+ check_object(s, page, p, SLUB_RED_INACTIVE);
+ }
+#else
  page->objects)
  check_object(s, page, p, SLUB_RED_INACTIVE);
+#endif
  }


  kmemcheck_free_shadow(page, compound_order(page));
@@ -2604,6 +2767,11 @@
  if (likely(!kmem_cache_debug(s) && pfmemalloc_match(page, gfpflags)))
  goto load_freelist;

+#ifdef CONFIG_ POMPEII_SLUB_UAF_DEBUG
+ if (s->object_size >= PAGE_SIZE)
+ slab_set_page_valid(s, freelist, 1);
+#endif
+
  /* Only entered in the debug case */
  if (kmem_cache_debug(s) &&
  !alloc_debug_processing(s, page, freelist, addr))
@@ -2636,6 +2804,11 @@
 #endif

  p = ___slab_alloc(s, gfpflags, node, addr, c);
+
+#ifdef CONFIG_POMPEII_SLUB_FREEPOINTER_DEBUG
+ slab_alloc_debugging(s, p);
+#endif
+
  local_irq_restore(flags);
  return p;
 }

@@ -2816,9 +2989,13 @@

  stat(s, FREE_SLOWPATH);

+#ifdef CONFIG_POMPEII_SLUB_SIMPLE_DEBUG
+ free_simple_debug_processing(s, page, head, tail, cnt, addr);
+#else
  if (kmem_cache_debug(s) &&
      !free_debug_processing(s, page, head, tail, cnt, addr))
  return;
+#endif

  do {
  if (unlikely(n)) {
@@ -3143,6 +3320,10 @@
  if (unlikely(!p[i]))
  goto error;

+#ifdef CONFIG_POMPEII_SLUB_FREEPOINTER_DEBUG
+ slab_alloc_debugging(s, p[i]);
+#endif
+
  c = this_cpu_ptr(s->cpu_slab);
  continue; /* goto for-loop */
  }

[Part.2]
slub: fix the bug of freepointer debug
---

diff --git a/mm/slub.c b/mm/slub.c
index e7b7115..7b1d813 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -262,7 +262,7 @@
  int pid; /* Pid context */
 };

 
-static noinline struct simple_track *get_simple_track(struct kmem_cache *s, void *object)
+static noinline struct simple_track *get_simple_track(const struct kmem_cache *s, void *object)
 {
  if(unlikely(s->offset) || s->object_size < sizeof(struct simple_track))
  return NULL;
@@ -311,26 +311,12 @@

 static inline void *get_freepointer(struct kmem_cache *s, void *object)
 {
-#ifdef CONFIG_POMPEII_SLUB_FREEPOINTER_DEBUG
- struct simple_track *p = get_simple_track(s, object);
-
- if(unlikely(!p))
- return *(void **)(object + s->offset);
-
- BUG_ON(p->fp1 != p->fp2);
- return p->fp2;
-#else
  return *(void **)(object + s->offset);
-#endif
 }

 static void prefetch_freepointer(const struct kmem_cache *s, void *object)
 {
-#ifdef CONFIG_POMPEII_SLUB_FREEPOINTER_DEBUG
- // Do nothing...
-#else
  prefetch(object + s->offset);
-#endif
 }

 static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
@@ -2907,6 +2893,15 @@

  slab_post_alloc_hook(s, gfpflags, 1, &object);
+#ifdef CONFIG_POMPEII_SLUB_FREEPOINTER_DEBUG
+ do {
+ struct simple_track *p = get_simple_track(s, object);
+ if(unlikely(!p))
+ return object;
+ BUG_ON(p->fp1 != p->fp2);
+ } while (0);
+#endif
+
  return object;
 }

 

06/15/2024