Module Name:    src
Committed By:   maxv
Date:           Sat Apr 13 08:41:37 UTC 2019

Modified Files:
        src/sys/arch/amd64/conf: GENERIC
        src/sys/conf: files
        src/sys/kern: subr_pool.c
        src/sys/sys: pool.h

Log Message:
Introduce POOL_QUARANTINE, a feature that creates a window during which a
freed buffer cannot be reallocated. This greatly helps detecting
use-after-frees, because they are not short-lived anymore.

We maintain a per-pool fifo of 128 buffers. On each pool_put, we do a real
free of the oldest buffer, and insert the new buffer. Before insertion, we
mark the buffer as invalid with KASAN. On each pool_cache_put, we destruct
the object, so it lands in pool_put, and the quarantine is handled there.

POOL_QUARANTINE can be used in conjunction with KASAN to detect more
use-after-free bugs.


To generate a diff of this commit:
cvs rdiff -u -r1.521 -r1.522 src/sys/arch/amd64/conf/GENERIC
cvs rdiff -u -r1.1233 -r1.1234 src/sys/conf/files
cvs rdiff -u -r1.248 -r1.249 src/sys/kern/subr_pool.c
cvs rdiff -u -r1.87 -r1.88 src/sys/sys/pool.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/amd64/conf/GENERIC
diff -u src/sys/arch/amd64/conf/GENERIC:1.521 src/sys/arch/amd64/conf/GENERIC:1.522
--- src/sys/arch/amd64/conf/GENERIC:1.521	Thu Mar 28 19:00:40 2019
+++ src/sys/arch/amd64/conf/GENERIC	Sat Apr 13 08:41:37 2019
@@ -1,4 +1,4 @@
-# $NetBSD: GENERIC,v 1.521 2019/03/28 19:00:40 maxv Exp $
+# $NetBSD: GENERIC,v 1.522 2019/04/13 08:41:37 maxv Exp $
 #
 # GENERIC machine description file
 #
@@ -22,7 +22,7 @@ include 	"arch/amd64/conf/std.amd64"
 
 options 	INCLUDE_CONFIG_FILE	# embed config file in kernel binary
 
-#ident		"GENERIC-$Revision: 1.521 $"
+#ident		"GENERIC-$Revision: 1.522 $"
 
 maxusers	64		# estimated number of users
 
@@ -122,9 +122,11 @@ makeoptions	DEBUG="-g"	# compile full sy
 options 	KDTRACE_HOOKS	# kernel DTrace hooks
 
 # Kernel Address Sanitizer (kASan). You need to disable SVS to use it.
+# The quarantine is optional and can help KASAN find more use-after-frees.
 #makeoptions 	KASAN=1		# Kernel Address Sanitizer
 #options 	KASAN
 #no options	SVS
+#options	POOL_QUARANTINE
 
 # Kernel Info Leak Detector.
 #makeoptions 	KLEAK=1

Index: src/sys/conf/files
diff -u src/sys/conf/files:1.1233 src/sys/conf/files:1.1234
--- src/sys/conf/files:1.1233	Tue Apr  9 22:05:27 2019
+++ src/sys/conf/files	Sat Apr 13 08:41:36 2019
@@ -1,4 +1,4 @@
-#	$NetBSD: files,v 1.1233 2019/04/09 22:05:27 pgoyette Exp $
+#	$NetBSD: files,v 1.1234 2019/04/13 08:41:36 maxv Exp $
 #	@(#)files.newconf	7.5 (Berkeley) 5/10/93
 
 version 	20171118
@@ -32,6 +32,7 @@ defflag				GPROF
 defflag				KASAN
 defflag				KLEAK
 defflag				KCOV
+defflag opt_pool.h		POOL_QUARANTINE
 
 defparam opt_copy_symtab.h	makeoptions_COPY_SYMTAB
 

Index: src/sys/kern/subr_pool.c
diff -u src/sys/kern/subr_pool.c:1.248 src/sys/kern/subr_pool.c:1.249
--- src/sys/kern/subr_pool.c:1.248	Sun Apr  7 09:20:04 2019
+++ src/sys/kern/subr_pool.c	Sat Apr 13 08:41:36 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: subr_pool.c,v 1.248 2019/04/07 09:20:04 maxv Exp $	*/
+/*	$NetBSD: subr_pool.c,v 1.249 2019/04/13 08:41:36 maxv Exp $	*/
 
 /*
  * Copyright (c) 1997, 1999, 2000, 2002, 2007, 2008, 2010, 2014, 2015, 2018
@@ -33,11 +33,12 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: subr_pool.c,v 1.248 2019/04/07 09:20:04 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: subr_pool.c,v 1.249 2019/04/13 08:41:36 maxv Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ddb.h"
 #include "opt_lockdebug.h"
+#include "opt_pool.h"
 #include "opt_kleak.h"
 #endif
 
@@ -111,6 +112,19 @@ static void pool_cache_kleak_fill(pool_c
 #define pool_cache_kleak_fill(pc, ptr)	__nothing
 #endif
 
+#ifdef POOL_QUARANTINE
+static void pool_quarantine_init(struct pool *);
+static void pool_quarantine_flush(struct pool *);
+static bool pool_put_quarantine(struct pool *, void *,
+    struct pool_pagelist *);
+static bool pool_cache_put_quarantine(pool_cache_t, void *, paddr_t);
+#else
+#define pool_quarantine_init(a)			__nothing
+#define pool_quarantine_flush(a)		__nothing
+#define pool_put_quarantine(a, b, c)		false
+#define pool_cache_put_quarantine(a, b, c)	false
+#endif
+
 #define pc_has_ctor(pc) \
 	(pc->pc_ctor != (int (*)(void *, void *, int))nullop)
 #define pc_has_dtor(pc) \
@@ -733,6 +747,7 @@ pool_init(struct pool *pp, size_t size, 
 	pp->pr_drain_hook_arg = NULL;
 	pp->pr_freecheck = NULL;
 	pool_redzone_init(pp, size);
+	pool_quarantine_init(pp);
 
 	/*
 	 * Decide whether to put the page header off-page to avoid wasting too
@@ -844,6 +859,8 @@ pool_destroy(struct pool *pp)
 	struct pool_pagelist pq;
 	struct pool_item_header *ph;
 
+	pool_quarantine_flush(pp);
+
 	/* Remove from global pool list */
 	mutex_enter(&pool_head_lock);
 	while (pp->pr_refcnt != 0)
@@ -1184,7 +1201,9 @@ pool_put(struct pool *pp, void *v)
 	LIST_INIT(&pq);
 
 	mutex_enter(&pp->pr_lock);
-	pool_do_put(pp, v, &pq);
+	if (!pool_put_quarantine(pp, v, &pq)) {
+		pool_do_put(pp, v, &pq);
+	}
 	mutex_exit(&pp->pr_lock);
 
 	pr_pagelist_free(pp, &pq);
@@ -2586,6 +2605,10 @@ pool_cache_put_paddr(pool_cache_t pc, vo
 	pool_cache_redzone_check(pc, object);
 	FREECHECK_IN(&pc->pc_freecheck, object);
 
+	if (pool_cache_put_quarantine(pc, object, pa)) {
+		return;
+	}
+
 	/* Lock out interrupts and disable preemption. */
 	s = splvm();
 	while (/* CONSTCOND */ true) {
@@ -2850,6 +2873,64 @@ pool_cache_kleak_fill(pool_cache_t pc, v
 }
 #endif
 
+#ifdef POOL_QUARANTINE
+static void
+pool_quarantine_init(struct pool *pp)
+{
+	pp->pr_quar.rotor = 0;
+	memset(&pp->pr_quar, 0, sizeof(pp->pr_quar));
+}
+
+static void
+pool_quarantine_flush(struct pool *pp)
+{
+	pool_quar_t *quar = &pp->pr_quar;
+	struct pool_pagelist pq;
+	size_t i;
+
+	LIST_INIT(&pq);
+
+	mutex_enter(&pp->pr_lock);
+	for (i = 0; i < POOL_QUARANTINE_DEPTH; i++) {
+		if (quar->list[i] == 0)
+			continue;
+		pool_do_put(pp, (void *)quar->list[i], &pq);
+	}
+	mutex_exit(&pp->pr_lock);
+
+	pr_pagelist_free(pp, &pq);
+}
+
+static bool
+pool_put_quarantine(struct pool *pp, void *v, struct pool_pagelist *pq)
+{
+	pool_quar_t *quar = &pp->pr_quar;
+	uintptr_t old;
+
+	if (pp->pr_roflags & PR_NOTOUCH) {
+		return false;
+	}
+
+	pool_redzone_check(pp, v);
+
+	old = quar->list[quar->rotor];
+	quar->list[quar->rotor] = (uintptr_t)v;
+	quar->rotor = (quar->rotor + 1) % POOL_QUARANTINE_DEPTH;
+	if (old != 0) {
+		pool_do_put(pp, (void *)old, pq);
+	}
+
+	return true;
+}
+
+static bool
+pool_cache_put_quarantine(pool_cache_t pc, void *p, paddr_t pa)
+{
+	pool_cache_destruct_object(pc, p);
+	return true;
+}
+#endif
+
 #ifdef POOL_REDZONE
 #if defined(_LP64)
 # define PRIME 0x9e37fffffffc0000UL

Index: src/sys/sys/pool.h
diff -u src/sys/sys/pool.h:1.87 src/sys/sys/pool.h:1.88
--- src/sys/sys/pool.h:1.87	Wed Mar 27 18:27:47 2019
+++ src/sys/sys/pool.h	Sat Apr 13 08:41:37 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: pool.h,v 1.87 2019/03/27 18:27:47 maxv Exp $	*/
+/*	$NetBSD: pool.h,v 1.88 2019/04/13 08:41:37 maxv Exp $	*/
 
 /*-
  * Copyright (c) 1997, 1998, 1999, 2000, 2007 The NetBSD Foundation, Inc.
@@ -81,6 +81,10 @@ struct pool_sysctl {
 #include <sys/tree.h>
 #include <sys/callback.h>
 
+#ifdef _KERNEL_OPT
+#include "opt_pool.h"
+#endif
+
 #define	POOL_PADDR_INVALID	((paddr_t) -1)
 
 struct pool;
@@ -101,6 +105,12 @@ struct pool_allocator {
 LIST_HEAD(pool_pagelist,pool_item_header);
 SPLAY_HEAD(phtree, pool_item_header);
 
+#define POOL_QUARANTINE_DEPTH	128
+typedef struct {
+	size_t rotor;
+	intptr_t list[POOL_QUARANTINE_DEPTH];
+} pool_quar_t;
+
 struct pool {
 	TAILQ_ENTRY(pool)
 			pr_poollist;
@@ -198,6 +208,9 @@ struct pool {
 	bool		pr_redzone;
 	size_t		pr_reqsize;
 	size_t		pr_reqsize_with_redzone;
+#ifdef POOL_QUARANTINE
+	pool_quar_t	pr_quar;
+#endif
 };
 
 /*

Reply via email to