summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkp <kp@FreeBSD.org>2017-03-22 21:18:18 +0000
committerLuiz Souza <luiz@netgate.com>2017-07-17 20:12:49 -0500
commit8995c199362d57c049f278008c30703cffa626dd (patch)
treecd4d54c13d8a0e899e438844eead5830f846e66c
parent29a346c21cd9bf2913cd4e7d0feaf38ccebcab25 (diff)
downloadFreeBSD-src-8995c199362d57c049f278008c30703cffa626dd.zip
FreeBSD-src-8995c199362d57c049f278008c30703cffa626dd.tar.gz
pf: Fix possible shutdown race
Prevent possible races in the pf_unload() / pf_purge_thread() shutdown code. Lock the pf_purge_thread() with the new pf_end_lock to prevent these races. Use a shared/exclusive lock, as we need to also acquire another sx lock (VNET_LIST_RLOCK). It's fine for both pf_purge_thread() and pf_unload() to sleep, Pointed out by: eri, glebius, jhb Differential Revision: https://reviews.freebsd.org/D10026 (cherry picked from commit 6f8b05d841a4f034b227995ff8e33cbe42c9cd30)
-rw-r--r--sys/net/pfvar.h2
-rw-r--r--sys/netpfil/pf/pf.c17
-rw-r--r--sys/netpfil/pf/pf_ioctl.c10
3 files changed, 18 insertions, 11 deletions
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index d025c8a..99f44b0 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -154,6 +154,8 @@ extern struct rwlock pf_rules_lock;
#define PF_RULES_RASSERT() rw_assert(&pf_rules_lock, RA_RLOCKED)
#define PF_RULES_WASSERT() rw_assert(&pf_rules_lock, RA_WLOCKED)
+extern struct sx pf_end_lock;
+
#define PF_MODVER 1
#define PFLOG_MODVER 1
#define PFSYNC_MODVER 1
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 7c7d5f3..61cc864 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -311,6 +311,7 @@ static void pf_route6(struct mbuf **, struct pf_rule *, int,
int in4_cksum(struct mbuf *m, u_int8_t nxt, int off, int len);
extern int pf_end_threads;
+extern struct proc *pf_purge_proc;
VNET_DEFINE(struct pf_limit, pf_limits[PF_LIMIT_MAX]);
@@ -1592,19 +1593,14 @@ pf_purge_thread(void *unused __unused)
VNET_ITERATOR_DECL(vnet_iter);
u_int idx = 0;
- for (;;) {
- tsleep(pf_purge_thread, 0, "pftm", hz / 10);
+ sx_xlock(&pf_end_lock);
+ while (pf_end_threads == 0) {
+ sx_sleep(pf_purge_thread, &pf_end_lock, 0, "pftm", hz / 10);
VNET_LIST_RLOCK();
VNET_FOREACH(vnet_iter) {
CURVNET_SET(vnet_iter);
- if (pf_end_threads) {
- pf_end_threads++;
- wakeup(pf_purge_thread);
- VNET_LIST_RUNLOCK();
- kproc_exit(0);
- }
/* Wait while V_pf_default_rule.timeout is initialized. */
if (V_pf_vnet_active == 0) {
@@ -1638,7 +1634,10 @@ pf_purge_thread(void *unused __unused)
}
VNET_LIST_RUNLOCK();
}
- /* not reached */
+
+ pf_end_threads++;
+ sx_xunlock(&pf_end_lock);
+ kproc_exit(0);
}
void
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 9f96c46..8ca8184 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -198,9 +198,11 @@ VNET_DEFINE(int, pf_vnet_active);
#define V_pf_vnet_active VNET(pf_vnet_active)
int pf_end_threads;
+struct proc *pf_purge_proc;
struct rwlock pf_rules_lock;
struct sx pf_ioctl_lock;
+struct sx pf_end_lock;
/* pfsync */
pfsync_state_import_t *pfsync_state_import_ptr = NULL;
@@ -3780,6 +3782,7 @@ pf_load(void)
rw_init(&pf_rules_lock, "pf rulesets");
sx_init(&pf_ioctl_lock, "pf ioctl");
+ sx_init(&pf_end_lock, "pf end thread");
pf_mtag_initialize();
@@ -3788,7 +3791,7 @@ pf_load(void)
return (ENOMEM);
pf_end_threads = 0;
- error = kproc_create(pf_purge_thread, NULL, NULL, 0, 0, "pf purge");
+ error = kproc_create(pf_purge_thread, NULL, &pf_purge_proc, 0, 0, "pf purge");
if (error != 0)
return (error);
@@ -3838,11 +3841,13 @@ pf_unload(void)
{
int error = 0;
+ sx_xlock(&pf_end_lock);
pf_end_threads = 1;
while (pf_end_threads < 2) {
wakeup_one(pf_purge_thread);
- tsleep(pf_purge_thread, 0, "pftmo", 0);
+ sx_sleep(pf_purge_proc, &pf_end_lock, 0, "pftmo", 0);
}
+ sx_xunlock(&pf_end_lock);
if (pf_dev != NULL)
destroy_dev(pf_dev);
@@ -3851,6 +3856,7 @@ pf_unload(void)
rw_destroy(&pf_rules_lock);
sx_destroy(&pf_ioctl_lock);
+ sx_destroy(&pf_end_lock);
return (error);
}
OpenPOWER on IntegriCloud