git: e1e847374bcc - main - Add DEBUG_POISON_POINTER

From: Mateusz Guzik <mjg_at_FreeBSD.org>
Date: Tue, 28 Nov 2023 16:35:40 UTC
The branch main has been updated by mjg:

URL: https://cgit.FreeBSD.org/src/commit/?id=e1e847374bcc287ca97e6cb16cb5e07a697cf055

commit e1e847374bcc287ca97e6cb16cb5e07a697cf055
Author:     Mateusz Guzik <mjg@FreeBSD.org>
AuthorDate: 2023-11-28 15:23:25 +0000
Commit:     Mateusz Guzik <mjg@FreeBSD.org>
CommitDate: 2023-11-28 16:33:46 +0000

    Add DEBUG_POISON_POINTER
    
    If you have a pointer which you know points to stale data, you can
    fill it with junk so that dereference later will trap
    
    Reviewed by:    kib
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D40946
---
 sys/kern/vfs_bio.c |  6 ++++++
 sys/sys/kassert.h  | 31 +++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+)

diff --git a/sys/kern/vfs_bio.c b/sys/kern/vfs_bio.c
index 31059f1c0a94..4e0832475c3e 100644
--- a/sys/kern/vfs_bio.c
+++ b/sys/kern/vfs_bio.c
@@ -159,6 +159,9 @@ nbufp(unsigned i)
 }
 
 caddr_t __read_mostly unmapped_buf;
+#ifdef INVARIANTS
+caddr_t	poisoned_buf = (void *)-1;
+#endif
 
 /* Used below and for softdep flushing threads in ufs/ffs/ffs_softdep.c */
 struct proc *bufdaemonproc;
@@ -1211,6 +1214,9 @@ bufinit(void)
 	mtx_init(&bdirtylock, "dirty buf lock", NULL, MTX_DEF);
 
 	unmapped_buf = (caddr_t)kva_alloc(maxphys);
+#ifdef INVARIANTS
+	poisoned_buf = unmapped_buf;
+#endif
 
 	/* finally, initialize each buffer header and stick on empty q */
 	for (i = 0; i < nbuf; i++) {
diff --git a/sys/sys/kassert.h b/sys/sys/kassert.h
index d7c1a21385f9..7b54ac6ae519 100644
--- a/sys/sys/kassert.h
+++ b/sys/sys/kassert.h
@@ -38,6 +38,37 @@ extern const char *panicstr;	/* panic message */
 extern bool panicked;
 #define	KERNEL_PANICKED()	__predict_false(panicked)
 
+/*
+ * Trap accesses going through a pointer. Moreover if kasan is available trap
+ * reading the pointer itself.
+ *
+ * Sample usage: you have a struct with numerous fields and by API contract
+ * only some of them get populated, even if the implementation temporary writes
+ * to them. You can use DEBUG_POISON_POINTER so that the consumer which should
+ * no be looking at the field gets caught.
+ *
+ * DEBUG_POISON_POINTER(obj->ptr);
+ * ....
+ * if (obj->ptr != NULL) // traps with kasan, does not trap otherwise
+ * ....
+ * if (obj->ptr->field) // traps with and without kasan
+ */
+#ifdef	INVARIANTS
+
+#include <sys/asan.h>
+
+extern caddr_t poisoned_buf;
+#define DEBUG_POISON_POINTER_VALUE poisoned_buf
+
+#define DEBUG_POISON_POINTER(x) ({				\
+	x = (void *)(DEBUG_POISON_POINTER_VALUE);		\
+	kasan_mark(&x, 0, sizeof(x), KASAN_GENERIC_REDZONE);	\
+})
+
+#else
+#define DEBUG_POISON_POINTER(x)
+#endif
+
 #ifdef	INVARIANTS		/* The option is always available */
 #define	VNASSERT(exp, vp, msg) do {					\
 	if (__predict_false(!(exp))) {					\