Sun Mar 8 00:31:19 2020 UTC ()
KMEM_SIZE: append the size_t to the allocated buffer, rather than
prepending, so it doesn't screw up the alignment of the buffer.

Reported-by: syzbot+c024c50570cccac51532@syzkaller.appspotmail.com


(ad)
diff -r1.78 -r1.79 src/sys/kern/subr_kmem.c

cvs diff -r1.78 -r1.79 src/sys/kern/subr_kmem.c (expand / switch to context diff)
--- src/sys/kern/subr_kmem.c 2020/01/25 15:08:40 1.78
+++ src/sys/kern/subr_kmem.c 2020/03/08 00:31:19 1.79
@@ -1,4 +1,4 @@
-/*	$NetBSD: subr_kmem.c,v 1.78 2020/01/25 15:08:40 ad Exp $	*/
+/*	$NetBSD: subr_kmem.c,v 1.79 2020/03/08 00:31:19 ad Exp $	*/
 
 /*
  * Copyright (c) 2009-2020 The NetBSD Foundation, Inc.
@@ -62,23 +62,23 @@
 
 /*
  * KMEM_SIZE: detect alloc/free size mismatch bugs.
- *	Prefix each allocations with a fixed-sized, aligned header and record
- *	the exact user-requested allocation size in it. When freeing, compare
- *	it with kmem_free's "size" argument.
+ *	Append to each allocation a fixed-sized footer and record the exact
+ *	user-requested allocation size in it.  When freeing, compare it with
+ *	kmem_free's "size" argument.
  *
  * This option is enabled on DIAGNOSTIC.
  *
- *  |CHUNK|CHUNK|CHUNK|CHUNK|CHUNK|CHUNK|CHUNK|CHUNK|CHUNK|CHUNK|
- *  +-----+-----+-----+-----+-----+-----+-----+-----+-----+---+-+
- *  |/////|     |     |     |     |     |     |     |     |   |U|
- *  |/HSZ/|     |     |     |     |     |     |     |     |   |U|
- *  |/////|     |     |     |     |     |     |     |     |   |U|
- *  +-----+-----+-----+-----+-----+-----+-----+-----+-----+---+-+
- *  |Size |    Buffer usable by the caller (requested size)   |Unused\
+ *  |CHUNK|CHUNK|CHUNK|CHUNK|CHUNK|CHUNK|CHUNK|CHUNK|CHUNK| |
+ *  +-----+-----+-----+-----+-----+-----+-----+-----+-----+-+
+ *  |     |     |     |     |     |     |     |     |/////|U|
+ *  |     |     |     |     |     |     |     |     |/HSZ/|U|
+ *  |     |     |     |     |     |     |     |     |/////|U|
+ *  +-----+-----+-----+-----+-----+-----+-----+-----+-----+-+
+ *  | Buffer usable by the caller (requested size)  |Size |Unused
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_kmem.c,v 1.78 2020/01/25 15:08:40 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_kmem.c,v 1.79 2020/03/08 00:31:19 ad Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_kmem.h"
@@ -168,10 +168,7 @@
 #endif
 
 #if defined(KMEM_SIZE)
-struct kmem_header {
-	size_t		size;
-} __aligned(KMEM_ALIGN);
-#define	SIZE_SIZE	sizeof(struct kmem_header)
+#define	SIZE_SIZE	sizeof(size_t)
 static void kmem_size_set(void *, size_t);
 static void kmem_size_check(void *, size_t);
 #else
@@ -229,7 +226,6 @@
 	if (__predict_true(p != NULL)) {
 		FREECHECK_OUT(&kmem_freecheck, p);
 		kmem_size_set(p, requested_size);
-		p += SIZE_SIZE;
 		kasan_mark(p, origsize, size, KASAN_KMEM_REDZONE);
 		return p;
 	}
@@ -283,7 +279,6 @@
 
 	kasan_mark(p, size, size, 0);
 
-	p = (uint8_t *)p - SIZE_SIZE;
 	kmem_size_check(p, requested_size);
 	FREECHECK_IN(&kmem_freecheck, p);
 	LOCKDEBUG_MEM_CHECK(p, size);
@@ -485,25 +480,21 @@
 static void
 kmem_size_set(void *p, size_t sz)
 {
-	struct kmem_header *hd;
-	hd = (struct kmem_header *)p;
-	hd->size = sz;
+	memcpy((size_t *)((uintptr_t)p + sz), &sz, sizeof(size_t));
 }
 
 static void
 kmem_size_check(void *p, size_t sz)
 {
-	struct kmem_header *hd;
 	size_t hsz;
 
-	hd = (struct kmem_header *)p;
-	hsz = hd->size;
+	memcpy(&hsz, (size_t *)((uintptr_t)p + sz), sizeof(size_t));
 
 	if (hsz != sz) {
-		panic("kmem_free(%p, %zu) != allocated size %zu",
-		    (const uint8_t *)p + SIZE_SIZE, sz, hsz);
+		panic("kmem_free(%p, %zu) != allocated size %zu; overwrote?",
+		    p, sz, hsz);
 	}
 
-	hd->size = -1;
+	memset((size_t *)((uintptr_t)p + sz), 0xff, sizeof(size_t));
 }
 #endif /* defined(KMEM_SIZE) */