diff options
Diffstat (limited to 'freebsd/sys/netpfil/pf/pf_ioctl.c')
-rw-r--r-- | freebsd/sys/netpfil/pf/pf_ioctl.c | 192 |
1 files changed, 150 insertions, 42 deletions
diff --git a/freebsd/sys/netpfil/pf/pf_ioctl.c b/freebsd/sys/netpfil/pf/pf_ioctl.c index 9be57273..837ad31c 100644 --- a/freebsd/sys/netpfil/pf/pf_ioctl.c +++ b/freebsd/sys/netpfil/pf/pf_ioctl.c @@ -61,7 +61,6 @@ __FBSDID("$FreeBSD$"); #include <sys/mbuf.h> #include <sys/module.h> #include <sys/proc.h> -#include <sys/rwlock.h> #include <sys/smp.h> #include <sys/socket.h> #include <sys/sysctl.h> @@ -204,7 +203,7 @@ VNET_DEFINE(int, pf_vnet_active); int pf_end_threads; struct proc *pf_purge_proc; -struct rwlock pf_rules_lock; +struct rmlock pf_rules_lock; struct sx pf_ioctl_lock; struct sx pf_end_lock; @@ -218,6 +217,8 @@ pfsync_defer_t *pfsync_defer_ptr = NULL; /* pflog */ pflog_packet_t *pflog_packet_ptr = NULL; +extern u_long pf_ioctl_maxcount; + static void pfattach_vnet(void) { @@ -995,6 +996,7 @@ static int pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { int error = 0; + PF_RULES_RLOCK_TRACKER; /* XXX keep in sync with switch() below */ if (securelevel_gt(td->td_ucred, 2)) @@ -2542,13 +2544,16 @@ DIOCCHANGEADDR_error: error = ENODEV; break; } - totlen = io->pfrio_size * sizeof(struct pfr_table); - pfrts = mallocarray(io->pfrio_size, sizeof(struct pfr_table), - M_TEMP, M_WAITOK); - if (! pfrts) { + + if (io->pfrio_size < 0 || io->pfrio_size > pf_ioctl_maxcount || + WOULD_OVERFLOW(io->pfrio_size, sizeof(struct pfr_table))) { error = ENOMEM; break; } + + totlen = io->pfrio_size * sizeof(struct pfr_table); + pfrts = mallocarray(io->pfrio_size, sizeof(struct pfr_table), + M_TEMP, M_WAITOK); error = copyin(io->pfrio_buffer, pfrts, totlen); if (error) { free(pfrts, M_TEMP); @@ -2571,13 +2576,16 @@ DIOCCHANGEADDR_error: error = ENODEV; break; } - totlen = io->pfrio_size * sizeof(struct pfr_table); - pfrts = mallocarray(io->pfrio_size, sizeof(struct pfr_table), - M_TEMP, M_WAITOK); - if (! pfrts) { + + if (io->pfrio_size < 0 || io->pfrio_size > pf_ioctl_maxcount || + WOULD_OVERFLOW(io->pfrio_size, sizeof(struct pfr_table))) { error = ENOMEM; break; } + + totlen = io->pfrio_size * sizeof(struct pfr_table); + pfrts = mallocarray(io->pfrio_size, sizeof(struct pfr_table), + M_TEMP, M_WAITOK); error = copyin(io->pfrio_buffer, pfrts, totlen); if (error) { free(pfrts, M_TEMP); @@ -2594,20 +2602,25 @@ DIOCCHANGEADDR_error: case DIOCRGETTABLES: { struct pfioc_table *io = (struct pfioc_table *)addr; struct pfr_table *pfrts; - size_t totlen; + size_t totlen, n; if (io->pfrio_esize != sizeof(struct pfr_table)) { error = ENODEV; break; } + PF_RULES_RLOCK(); + n = pfr_table_count(&io->pfrio_table, io->pfrio_flags); + io->pfrio_size = min(io->pfrio_size, n); + totlen = io->pfrio_size * sizeof(struct pfr_table); + pfrts = mallocarray(io->pfrio_size, sizeof(struct pfr_table), - M_TEMP, M_WAITOK); - if (! pfrts) { + M_TEMP, M_NOWAIT); + if (pfrts == NULL) { error = ENOMEM; + PF_RULES_RUNLOCK(); break; } - PF_RULES_RLOCK(); error = pfr_get_tables(&io->pfrio_table, pfrts, &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); PF_RULES_RUNLOCK(); @@ -2620,20 +2633,24 @@ DIOCCHANGEADDR_error: case DIOCRGETTSTATS: { struct pfioc_table *io = (struct pfioc_table *)addr; struct pfr_tstats *pfrtstats; - size_t totlen; + size_t totlen, n; if (io->pfrio_esize != sizeof(struct pfr_tstats)) { error = ENODEV; break; } + PF_RULES_WLOCK(); + n = pfr_table_count(&io->pfrio_table, io->pfrio_flags); + io->pfrio_size = min(io->pfrio_size, n); + totlen = io->pfrio_size * sizeof(struct pfr_tstats); pfrtstats = mallocarray(io->pfrio_size, - sizeof(struct pfr_tstats), M_TEMP, M_WAITOK); - if (! pfrtstats) { + sizeof(struct pfr_tstats), M_TEMP, M_NOWAIT); + if (pfrtstats == NULL) { error = ENOMEM; + PF_RULES_WUNLOCK(); break; } - PF_RULES_WLOCK(); error = pfr_get_tstats(&io->pfrio_table, pfrtstats, &io->pfrio_size, io->pfrio_flags | PFR_FLAG_USERIOCTL); PF_RULES_WUNLOCK(); @@ -2646,25 +2663,31 @@ DIOCCHANGEADDR_error: case DIOCRCLRTSTATS: { struct pfioc_table *io = (struct pfioc_table *)addr; struct pfr_table *pfrts; - size_t totlen; + size_t totlen, n; if (io->pfrio_esize != sizeof(struct pfr_table)) { error = ENODEV; break; } + + PF_RULES_WLOCK(); + n = pfr_table_count(&io->pfrio_table, io->pfrio_flags); + io->pfrio_size = min(io->pfrio_size, n); + totlen = io->pfrio_size * sizeof(struct pfr_table); pfrts = mallocarray(io->pfrio_size, sizeof(struct pfr_table), - M_TEMP, M_WAITOK); - if (! pfrts) { + M_TEMP, M_NOWAIT); + if (pfrts == NULL) { error = ENOMEM; + PF_RULES_WUNLOCK(); break; } error = copyin(io->pfrio_buffer, pfrts, totlen); if (error) { free(pfrts, M_TEMP); + PF_RULES_WUNLOCK(); break; } - PF_RULES_WLOCK(); error = pfr_clr_tstats(pfrts, io->pfrio_size, &io->pfrio_nzero, io->pfrio_flags | PFR_FLAG_USERIOCTL); PF_RULES_WUNLOCK(); @@ -2675,25 +2698,31 @@ DIOCCHANGEADDR_error: case DIOCRSETTFLAGS: { struct pfioc_table *io = (struct pfioc_table *)addr; struct pfr_table *pfrts; - size_t totlen; + size_t totlen, n; if (io->pfrio_esize != sizeof(struct pfr_table)) { error = ENODEV; break; } + + PF_RULES_WLOCK(); + n = pfr_table_count(&io->pfrio_table, io->pfrio_flags); + io->pfrio_size = min(io->pfrio_size, n); + totlen = io->pfrio_size * sizeof(struct pfr_table); pfrts = mallocarray(io->pfrio_size, sizeof(struct pfr_table), - M_TEMP, M_WAITOK); - if (! pfrts) { + M_TEMP, M_NOWAIT); + if (pfrts == NULL) { error = ENOMEM; + PF_RULES_WUNLOCK(); break; } error = copyin(io->pfrio_buffer, pfrts, totlen); if (error) { free(pfrts, M_TEMP); + PF_RULES_WUNLOCK(); break; } - PF_RULES_WLOCK(); error = pfr_set_tflags(pfrts, io->pfrio_size, io->pfrio_setflag, io->pfrio_clrflag, &io->pfrio_nchange, &io->pfrio_ndel, io->pfrio_flags | PFR_FLAG_USERIOCTL); @@ -2725,9 +2754,15 @@ DIOCCHANGEADDR_error: error = ENODEV; break; } + if (io->pfrio_size < 0 || + io->pfrio_size > pf_ioctl_maxcount || + WOULD_OVERFLOW(io->pfrio_size, sizeof(struct pfr_addr))) { + error = EINVAL; + break; + } totlen = io->pfrio_size * sizeof(struct pfr_addr); pfras = mallocarray(io->pfrio_size, sizeof(struct pfr_addr), - M_TEMP, M_WAITOK); + M_TEMP, M_NOWAIT); if (! pfras) { error = ENOMEM; break; @@ -2757,9 +2792,15 @@ DIOCCHANGEADDR_error: error = ENODEV; break; } + if (io->pfrio_size < 0 || + io->pfrio_size > pf_ioctl_maxcount || + WOULD_OVERFLOW(io->pfrio_size, sizeof(struct pfr_addr))) { + error = EINVAL; + break; + } totlen = io->pfrio_size * sizeof(struct pfr_addr); pfras = mallocarray(io->pfrio_size, sizeof(struct pfr_addr), - M_TEMP, M_WAITOK); + M_TEMP, M_NOWAIT); if (! pfras) { error = ENOMEM; break; @@ -2789,10 +2830,19 @@ DIOCCHANGEADDR_error: error = ENODEV; break; } + if (io->pfrio_size < 0 || io->pfrio_size2 < 0) { + error = EINVAL; + break; + } count = max(io->pfrio_size, io->pfrio_size2); + if (count > pf_ioctl_maxcount || + WOULD_OVERFLOW(count, sizeof(struct pfr_addr))) { + error = EINVAL; + break; + } totlen = count * sizeof(struct pfr_addr); pfras = mallocarray(count, sizeof(struct pfr_addr), M_TEMP, - M_WAITOK); + M_NOWAIT); if (! pfras) { error = ENOMEM; break; @@ -2823,9 +2873,15 @@ DIOCCHANGEADDR_error: error = ENODEV; break; } + if (io->pfrio_size < 0 || + io->pfrio_size > pf_ioctl_maxcount || + WOULD_OVERFLOW(io->pfrio_size, sizeof(struct pfr_addr))) { + error = EINVAL; + break; + } totlen = io->pfrio_size * sizeof(struct pfr_addr); pfras = mallocarray(io->pfrio_size, sizeof(struct pfr_addr), - M_TEMP, M_WAITOK); + M_TEMP, M_NOWAIT); if (! pfras) { error = ENOMEM; break; @@ -2849,9 +2905,15 @@ DIOCCHANGEADDR_error: error = ENODEV; break; } + if (io->pfrio_size < 0 || + io->pfrio_size > pf_ioctl_maxcount || + WOULD_OVERFLOW(io->pfrio_size, sizeof(struct pfr_astats))) { + error = EINVAL; + break; + } totlen = io->pfrio_size * sizeof(struct pfr_astats); pfrastats = mallocarray(io->pfrio_size, - sizeof(struct pfr_astats), M_TEMP, M_WAITOK); + sizeof(struct pfr_astats), M_TEMP, M_NOWAIT); if (! pfrastats) { error = ENOMEM; break; @@ -2875,9 +2937,15 @@ DIOCCHANGEADDR_error: error = ENODEV; break; } + if (io->pfrio_size < 0 || + io->pfrio_size > pf_ioctl_maxcount || + WOULD_OVERFLOW(io->pfrio_size, sizeof(struct pfr_addr))) { + error = EINVAL; + break; + } totlen = io->pfrio_size * sizeof(struct pfr_addr); pfras = mallocarray(io->pfrio_size, sizeof(struct pfr_addr), - M_TEMP, M_WAITOK); + M_TEMP, M_NOWAIT); if (! pfras) { error = ENOMEM; break; @@ -2907,9 +2975,15 @@ DIOCCHANGEADDR_error: error = ENODEV; break; } + if (io->pfrio_size < 0 || + io->pfrio_size > pf_ioctl_maxcount || + WOULD_OVERFLOW(io->pfrio_size, sizeof(struct pfr_addr))) { + error = EINVAL; + break; + } totlen = io->pfrio_size * sizeof(struct pfr_addr); pfras = mallocarray(io->pfrio_size, sizeof(struct pfr_addr), - M_TEMP, M_WAITOK); + M_TEMP, M_NOWAIT); if (! pfras) { error = ENOMEM; break; @@ -2939,9 +3013,15 @@ DIOCCHANGEADDR_error: error = ENODEV; break; } + if (io->pfrio_size < 0 || + io->pfrio_size > pf_ioctl_maxcount || + WOULD_OVERFLOW(io->pfrio_size, sizeof(struct pfr_addr))) { + error = EINVAL; + break; + } totlen = io->pfrio_size * sizeof(struct pfr_addr); pfras = mallocarray(io->pfrio_size, sizeof(struct pfr_addr), - M_TEMP, M_WAITOK); + M_TEMP, M_NOWAIT); if (! pfras) { error = ENOMEM; break; @@ -2986,9 +3066,15 @@ DIOCCHANGEADDR_error: error = ENODEV; break; } + if (io->size < 0 || + io->size > pf_ioctl_maxcount || + WOULD_OVERFLOW(io->size, sizeof(struct pfioc_trans_e))) { + error = EINVAL; + break; + } totlen = sizeof(struct pfioc_trans_e) * io->size; ioes = mallocarray(io->size, sizeof(struct pfioc_trans_e), - M_TEMP, M_WAITOK); + M_TEMP, M_NOWAIT); if (! ioes) { error = ENOMEM; break; @@ -3057,9 +3143,15 @@ DIOCCHANGEADDR_error: error = ENODEV; break; } + if (io->size < 0 || + io->size > pf_ioctl_maxcount || + WOULD_OVERFLOW(io->size, sizeof(struct pfioc_trans_e))) { + error = EINVAL; + break; + } totlen = sizeof(struct pfioc_trans_e) * io->size; ioes = mallocarray(io->size, sizeof(struct pfioc_trans_e), - M_TEMP, M_WAITOK); + M_TEMP, M_NOWAIT); if (! ioes) { error = ENOMEM; break; @@ -3128,10 +3220,18 @@ DIOCCHANGEADDR_error: error = ENODEV; break; } + + if (io->size < 0 || + io->size > pf_ioctl_maxcount || + WOULD_OVERFLOW(io->size, sizeof(struct pfioc_trans_e))) { + error = EINVAL; + break; + } + totlen = sizeof(struct pfioc_trans_e) * io->size; ioes = mallocarray(io->size, sizeof(struct pfioc_trans_e), - M_TEMP, M_WAITOK); - if (! ioes) { + M_TEMP, M_NOWAIT); + if (ioes == NULL) { error = ENOMEM; break; } @@ -3334,13 +3434,21 @@ DIOCCHANGEADDR_error: break; } + if (io->pfiio_size < 0 || + io->pfiio_size > pf_ioctl_maxcount || + WOULD_OVERFLOW(io->pfiio_size, sizeof(struct pfi_kif))) { + error = EINVAL; + break; + } + bufsiz = io->pfiio_size * sizeof(struct pfi_kif); ifstore = mallocarray(io->pfiio_size, sizeof(struct pfi_kif), - M_TEMP, M_WAITOK); - if (! ifstore) { + M_TEMP, M_NOWAIT); + if (ifstore == NULL) { error = ENOMEM; break; } + PF_RULES_RLOCK(); pfi_get_ifaces(io->pfiio_name, ifstore, &io->pfiio_size); PF_RULES_RUNLOCK(); @@ -3828,7 +3936,7 @@ pf_load(void) { int error; - rw_init(&pf_rules_lock, "pf rulesets"); + rm_init(&pf_rules_lock, "pf rulesets"); sx_init(&pf_ioctl_lock, "pf ioctl"); sx_init(&pf_end_lock, "pf end thread"); @@ -3901,7 +4009,7 @@ pf_unload(void) pfi_cleanup(); - rw_destroy(&pf_rules_lock); + rm_destroy(&pf_rules_lock); sx_destroy(&pf_ioctl_lock); sx_destroy(&pf_end_lock); } |