git: 569798e0824a - stable/13 - pf: bound DIOCGETSTATESV2 memory use

Kristof Provost kp at FreeBSD.org
Fri Jul 16 11:54:02 UTC 2021


The branch stable/13 has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=569798e0824a61f7f5308da4619322110523471b

commit 569798e0824a61f7f5308da4619322110523471b
Author:     Kristof Provost <kp at FreeBSD.org>
AuthorDate: 2021-07-08 13:01:19 +0000
Commit:     Kristof Provost <kp at FreeBSD.org>
CommitDate: 2021-07-16 09:08:28 +0000

    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)
---
 sys/netpfil/pf/pf_ioctl.c | 48 +++++++++++++++++++++++++++++++++++------------
 1 file changed, 36 insertions(+), 12 deletions(-)

diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 24e40c1c716c..c9105bf22385 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -2879,6 +2879,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;
@@ -2891,38 +2893,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;


More information about the dev-commits-src-all mailing list