summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netpfil/pf/pf_ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netpfil/pf/pf_ioctl.c')
-rw-r--r--freebsd/sys/netpfil/pf/pf_ioctl.c192
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);
}