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
};
/*