All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kees Cook <kees@kernel.org>
To: Vlastimil Babka <vbabka@suse.cz>
Cc: Kees Cook <kees@kernel.org>, Christoph Lameter <cl@linux.com>,
	Pekka Enberg <penberg@kernel.org>,
	David Rientjes <rientjes@google.com>,
	Joonsoo Kim <iamjoonsoo.kim@lge.com>,
	Andrew Morton <akpm@linux-foundation.org>,
	Roman Gushchin <roman.gushchin@linux.dev>,
	Hyeonggon Yoo <42.hyeyoo@gmail.com>,
	linux-mm@kvack.org, Miguel Ojeda <ojeda@kernel.org>,
	Nathan Chancellor <nathan@kernel.org>,
	Marco Elver <elver@google.com>,
	Nick Desaulniers <ndesaulniers@google.com>,
	Przemek Kitszel <przemyslaw.kitszel@intel.com>,
	linux-kernel@vger.kernel.org, linux-hardening@vger.kernel.org
Subject: [PATCH 4/5] slab: Set freed variables to NULL by default
Date: Fri, 21 Mar 2025 13:41:00 -0700	[thread overview]
Message-ID: <20250321204105.1898507-4-kees@kernel.org> (raw)
In-Reply-To: <20250321202620.work.175-kees@kernel.org>

To defang a subset of "dangling pointer" use-after-free flaws[1], take the
address of any lvalues passed to kfree() and set them to NULL after
freeing.

To do this manually, kfree_and_null() (and the "sensitive" variant)
are introduced.

Link: https://github.com/KSPP/linux/issues/87 [1]
Signed-off-by: Kees Cook <kees@kernel.org>
---
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Christoph Lameter <cl@linux.com>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: David Rientjes <rientjes@google.com>
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Hyeonggon Yoo <42.hyeyoo@gmail.com>
Cc: linux-mm@kvack.org
---
 include/linux/slab.h | 30 +++++++++++++++++++++++++++---
 mm/slab_common.c     |  8 ++++----
 mm/slub.c            |  6 +++---
 3 files changed, 34 insertions(+), 10 deletions(-)

diff --git a/include/linux/slab.h b/include/linux/slab.h
index 3e807ccc8583..2717ad238fa2 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -465,11 +465,35 @@ void * __must_check krealloc_noprof(const void *objp, size_t new_size,
 				    gfp_t flags) __realloc_size(2);
 #define krealloc(...)				alloc_hooks(krealloc_noprof(__VA_ARGS__))
 
-void kfree(const void *objp);
-void kfree_sensitive(const void *objp);
+void __kfree(const void *objp);
+void __kfree_sensitive(const void *objp);
 size_t __ksize(const void *objp);
 
-#define __kfree(x)	kfree(x)
+static inline void kfree_and_null(void **ptr)
+{
+	__kfree(*ptr);
+	*ptr = NULL;
+}
+static inline void kfree_sensitive_and_null(void **ptr)
+{
+	__kfree_sensitive(*ptr);
+	*ptr = NULL;
+}
+
+#define __force_lvalue_expr(x)	\
+	__builtin_choose_expr(__is_lvalue(x), x, (void *){ NULL })
+
+#define __free_and_null(__how, x)	\
+({					\
+	typeof(x) *__ptr = &(x);	\
+	__how ## _and_null((void **)__ptr);	\
+})
+#define __free_and_maybe_null(__how, x)	\
+	__builtin_choose_expr(__is_lvalue(x), \
+		__free_and_null(__how, __force_lvalue_expr(x)), \
+		__kfree(x))
+#define kfree(x)	   __free_and_maybe_null(kfree, x)
+#define kfree_sensitive(x) __free_and_maybe_null(kfree_sensitive, x)
 
 DEFINE_FREE(kfree, void *, if (!IS_ERR_OR_NULL(_T)) kfree(_T))
 DEFINE_FREE(kfree_sensitive, void *, if (_T) kfree_sensitive(_T))
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 4030907b6b7d..9a82952ec266 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -1211,7 +1211,7 @@ module_init(slab_proc_init);
 #endif /* CONFIG_SLUB_DEBUG */
 
 /**
- * kfree_sensitive - Clear sensitive information in memory before freeing
+ * __kfree_sensitive - Clear sensitive information in memory before freeing
  * @p: object to free memory of
  *
  * The memory of the object @p points to is zeroed before freed.
@@ -1221,7 +1221,7 @@ module_init(slab_proc_init);
  * deal bigger than the requested buffer size passed to kmalloc(). So be
  * careful when using this function in performance sensitive code.
  */
-void kfree_sensitive(const void *p)
+void __kfree_sensitive(const void *p)
 {
 	size_t ks;
 	void *mem = (void *)p;
@@ -1231,9 +1231,9 @@ void kfree_sensitive(const void *p)
 		kasan_unpoison_range(mem, ks);
 		memzero_explicit(mem, ks);
 	}
-	kfree(mem);
+	__kfree(mem);
 }
-EXPORT_SYMBOL(kfree_sensitive);
+EXPORT_SYMBOL(__kfree_sensitive);
 
 size_t ksize(const void *objp)
 {
diff --git a/mm/slub.c b/mm/slub.c
index 1f50129dcfb3..38dd898667bf 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -4729,12 +4729,12 @@ static void free_large_kmalloc(struct folio *folio, void *object)
 }
 
 /**
- * kfree - free previously allocated memory
+ * __kfree - free previously allocated memory
  * @object: pointer returned by kmalloc() or kmem_cache_alloc()
  *
  * If @object is NULL, no operation is performed.
  */
-void kfree(const void *object)
+void __kfree(const void *object)
 {
 	struct folio *folio;
 	struct slab *slab;
@@ -4756,7 +4756,7 @@ void kfree(const void *object)
 	s = slab->slab_cache;
 	slab_free(s, slab, x, _RET_IP_);
 }
-EXPORT_SYMBOL(kfree);
+EXPORT_SYMBOL(__kfree);
 
 static __always_inline __realloc_size(2) void *
 __do_krealloc(const void *p, size_t new_size, gfp_t flags)
-- 
2.34.1


  parent reply	other threads:[~2025-03-21 20:41 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-03-21 20:40 [RFC 0/5] slab: Set freed variables to NULL by default Kees Cook
2025-03-21 20:40 ` [PATCH 1/5] treewide: Replace kfree() casts with union members Kees Cook
2025-03-23 10:26   ` David Laight
2025-03-21 20:40 ` [PATCH 2/5] treewide: Prepare for kfree() to __kfree() rename Kees Cook
2025-03-21 20:40 ` [PATCH 3/5] compiler_types: Introduce __is_lvalue() Kees Cook
2025-03-22  3:38   ` Jann Horn
2025-03-22  7:03     ` Kees Cook
2025-03-21 20:41 ` Kees Cook [this message]
2025-03-22  1:50   ` [PATCH 4/5] slab: Set freed variables to NULL by default Jann Horn
2025-03-22  7:18     ` Kees Cook
2025-03-27 19:23       ` Jann Horn
2025-03-27 19:42   ` Matthew Wilcox
2025-03-21 20:41 ` [PATCH 5/5] [DEBUG] slab: Report number of NULLings Kees Cook
2025-03-24 16:16   ` Christoph Lameter (Ampere)
2025-03-25 19:45     ` Kees Cook
2025-03-27 13:00 ` [RFC 0/5] slab: Set freed variables to NULL by default Harry Yoo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250321204105.1898507-4-kees@kernel.org \
    --to=kees@kernel.org \
    --cc=42.hyeyoo@gmail.com \
    --cc=akpm@linux-foundation.org \
    --cc=cl@linux.com \
    --cc=elver@google.com \
    --cc=iamjoonsoo.kim@lge.com \
    --cc=linux-hardening@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=nathan@kernel.org \
    --cc=ndesaulniers@google.com \
    --cc=ojeda@kernel.org \
    --cc=penberg@kernel.org \
    --cc=przemyslaw.kitszel@intel.com \
    --cc=rientjes@google.com \
    --cc=roman.gushchin@linux.dev \
    --cc=vbabka@suse.cz \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.