aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristof Provost <kp@FreeBSD.org>2021-07-08 13:01:19 +0000
committerKristof Provost <kp@FreeBSD.org>2021-07-16 08:04:14 +0000
commit4feb5667f59d7542395b021d1852e792b4baf90a (patch)
tree22568a9acb3ca93f46555c7aa61e6d19c940bdeb
parentfa3da0e87de7e9c654881aedd7cf04d417768d50 (diff)
pf: bound DIOCGETSTATESV2 memory use
Rather than allocating however much memory userspace asks for we only allocate enough for a handful of states, and copy to userspace for each completed row. We start out with enough space for 16 states (per row), but grow that as required. In most configurations we expect at most a handful of states per row (more than that would have other negative effects on packet processing performance). Reviewed by: mjg MFC after: 1 week Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D31111 (cherry picked from commit 3fc12ae042040192aa43984106a75663aaa9e0f5)
-rw-r--r--sys/netpfil/pf/pf_ioctl.c48
1 files changed, 36 insertions, 12 deletions
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index c8d7805b3fce..74f347ac1fa6 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -2892,6 +2892,8 @@ DIOCGETSTATES_full:
struct pf_kstate *s;
struct pf_state_export *pstore, *p;
int i, nr;
+ size_t slice_count = 16, count;
+ void *out;
if (ps->ps_req_version > PF_STATE_VERSION) {
error = ENOTSUP;
@@ -2904,38 +2906,60 @@ DIOCGETSTATES_full:
break;
}
- p = pstore = malloc(ps->ps_len, M_TEMP, M_WAITOK | M_ZERO);
+ out = ps->ps_states;
+ pstore = mallocarray(slice_count,
+ sizeof(struct pf_state_export), M_TEMP, M_WAITOK | M_ZERO);
nr = 0;
for (i = 0; i <= pf_hashmask; i++) {
struct pf_idhash *ih = &V_pf_idhash[i];
+DIOCGETSTATESV2_retry:
+ p = pstore;
+
if (LIST_EMPTY(&ih->states))
continue;
PF_HASHROW_LOCK(ih);
+ count = 0;
+ LIST_FOREACH(s, &ih->states, entry) {
+ if (s->timeout == PFTM_UNLINKED)
+ continue;
+ count++;
+ }
+
+ if (count > slice_count) {
+ PF_HASHROW_UNLOCK(ih);
+ free(pstore, M_TEMP);
+ slice_count = count * 2;
+ pstore = mallocarray(slice_count,
+ sizeof(struct pf_state_export), M_TEMP,
+ M_WAITOK | M_ZERO);
+ goto DIOCGETSTATESV2_retry;
+ }
+
+ if ((nr+count) * sizeof(*p) > ps->ps_len) {
+ PF_HASHROW_UNLOCK(ih);
+ goto DIOCGETSTATESV2_full;
+ }
+
LIST_FOREACH(s, &ih->states, entry) {
if (s->timeout == PFTM_UNLINKED)
continue;
- if ((nr+1) * sizeof(*p) > ps->ps_len) {
- PF_HASHROW_UNLOCK(ih);
- goto DIOCGETSTATESV2_full;
- }
pf_state_export(p, s);
p++;
nr++;
}
PF_HASHROW_UNLOCK(ih);
+ error = copyout(pstore, out,
+ sizeof(struct pf_state_export) * count);
+ if (error)
+ break;
+ out = ps->ps_states + nr;
}
DIOCGETSTATESV2_full:
- error = copyout(pstore, ps->ps_states,
- sizeof(struct pf_state_export) * nr);
- if (error) {
- free(pstore, M_TEMP);
- break;
- }
- ps->ps_len = sizeof(struct pf_state_export) * nr;
+ ps->ps_len = nr * sizeof(struct pf_state_export);
free(pstore, M_TEMP);
break;