diff --git a/sys/sys/asan.h b/sys/sys/asan.h --- a/sys/sys/asan.h +++ b/sys/sys/asan.h @@ -65,4 +65,6 @@ #define kasan_mark(p, s, l, c) #endif /* !KASAN */ +#define kasan_poison_pointer(x) kasan_mark(&x, 0, sizeof(x), KASAN_GENERIC_REDZONE) + #endif /* !_SYS_ASAN_H_ */ diff --git a/sys/sys/buf.h b/sys/sys/buf.h --- a/sys/sys/buf.h +++ b/sys/sys/buf.h @@ -522,8 +522,7 @@ extern int dirtybufferflushes; extern int altbufferflushes; extern int nswbuf; /* Number of swap I/O buffer headers. */ -extern caddr_t __read_mostly unmapped_buf; /* Data address for unmapped - buffers. */ +extern caddr_t unmapped_buf; /* Data address for unmapped buffers. */ static inline int buf_mapped(struct buf *bp) diff --git a/sys/sys/kassert.h b/sys/sys/kassert.h --- a/sys/sys/kassert.h +++ b/sys/sys/kassert.h @@ -38,6 +38,39 @@ 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 DEBUG_POISON_POINTER so 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 + * .... + * obj->ptr->field // traps with and without kasan + */ +#ifdef INVARIANTS + +#include + +#ifndef DEBUG_POISON_POINTER_VALUE +extern caddr_t unmapped_buf; +#define DEBUG_POISON_POINTER_VALUE unmapped_buf +#endif + +#define DEBUG_POISON_POINTER(x) ({ \ + x = (void *)(DEBUG_POISON_POINTER_VALUE); \ + kasan_poison_pointer(x); \ +}) + +#else +#define DEBUG_POISON_POINTER(x) +#endif + #ifdef INVARIANTS /* The option is always available */ #define VNASSERT(exp, vp, msg) do { \ if (__predict_false(!(exp))) { \