diff options
author | glebius <glebius@FreeBSD.org> | 2014-09-12 08:39:15 +0000 |
---|---|---|
committer | glebius <glebius@FreeBSD.org> | 2014-09-12 08:39:15 +0000 |
commit | 759eeea22084e40138ba3e69e139b171b6133e56 (patch) | |
tree | b149135f1bc99fbe27116376df6f4ffab95e3df9 /sys/netpfil/pf/pf_ioctl.c | |
parent | 56921b198b1439b3c414287406ce6a0b6e2448b2 (diff) | |
download | FreeBSD-src-759eeea22084e40138ba3e69e139b171b6133e56.zip FreeBSD-src-759eeea22084e40138ba3e69e139b171b6133e56.tar.gz |
- Provide a sleepable lock to protect against ioctl() vs ioctl() races.
- Use the new lock to protect against simultaneous DIOCSTART and/or
DIOCSTOP ioctls.
Reported & tested by: jmallett
Sponsored by: Nginx, Inc.
Diffstat (limited to 'sys/netpfil/pf/pf_ioctl.c')
-rw-r--r-- | sys/netpfil/pf/pf_ioctl.c | 17 |
1 files changed, 7 insertions, 10 deletions
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c index 195aeb3..dba5674 100644 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -191,6 +191,7 @@ static volatile VNET_DEFINE(int, pf_pfil_hooked); VNET_DEFINE(int, pf_end_threads); struct rwlock pf_rules_lock; +struct sx pf_ioctl_lock; /* pfsync */ pfsync_state_import_t *pfsync_state_import_ptr = NULL; @@ -1095,20 +1096,18 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td switch (cmd) { case DIOCSTART: - PF_RULES_WLOCK(); + sx_xlock(&pf_ioctl_lock); if (V_pf_status.running) error = EEXIST; else { int cpu; - PF_RULES_WUNLOCK(); error = hook_pf(); if (error) { DPFPRINTF(PF_DEBUG_MISC, ("pf: pfil registration failed\n")); break; } - PF_RULES_WLOCK(); V_pf_status.running = 1; V_pf_status.since = time_second; @@ -1117,27 +1116,23 @@ pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td DPFPRINTF(PF_DEBUG_MISC, ("pf: started\n")); } - PF_RULES_WUNLOCK(); break; case DIOCSTOP: - PF_RULES_WLOCK(); + sx_xlock(&pf_ioctl_lock); if (!V_pf_status.running) error = ENOENT; else { V_pf_status.running = 0; - PF_RULES_WUNLOCK(); error = dehook_pf(); if (error) { V_pf_status.running = 1; DPFPRINTF(PF_DEBUG_MISC, ("pf: pfil unregistration failed\n")); } - PF_RULES_WLOCK(); V_pf_status.since = time_second; DPFPRINTF(PF_DEBUG_MISC, ("pf: stopped\n")); } - PF_RULES_WUNLOCK(); break; case DIOCADDRULE: { @@ -3261,6 +3256,8 @@ DIOCCHANGEADDR_error: break; } fail: + if (sx_xlocked(&pf_ioctl_lock)) + sx_xunlock(&pf_ioctl_lock); CURVNET_RESTORE(); return (error); @@ -3734,6 +3731,7 @@ pf_load(void) VNET_LIST_RUNLOCK(); rw_init(&pf_rules_lock, "pf rulesets"); + sx_init(&pf_ioctl_lock, "pf ioctl"); pf_dev = make_dev(&pf_cdevsw, 0, 0, 0, 0600, PF_NAME); if ((error = pfattach()) != 0) @@ -3747,9 +3745,7 @@ pf_unload(void) { int error = 0; - PF_RULES_WLOCK(); V_pf_status.running = 0; - PF_RULES_WUNLOCK(); swi_remove(V_pf_swi_cookie); error = dehook_pf(); if (error) { @@ -3778,6 +3774,7 @@ pf_unload(void) PF_RULES_WUNLOCK(); destroy_dev(pf_dev); rw_destroy(&pf_rules_lock); + sx_destroy(&pf_ioctl_lock); return (error); } |