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
This commit is contained in:
Kristof Provost 2021-07-08 15:01:19 +02:00
parent be70c7a50d
commit 3fc12ae042

View File

@ -2878,6 +2878,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;
@ -2890,38 +2892,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;