NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/60340: npf(4): quadratic-time state/config export
>Number: 60340
>Category: kern
>Synopsis: npf(4): quadratic-time state/config export
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Thu Jun 18 20:20:01 +0000 2026
>Originator: Taylor R Campbell
>Release: current, 11, 10, 9, ...
>Organization:
The NetBSD Packet Foundation, Esquared
>Environment:
>Description:
The ioctl to export the npf state, IOC_NPF_SAVE runs in
quadratic time because it uses realloc in a loop with a fixed
increment, and the kernel realloc(9) only allocates exactly as
much as the caller requested, no more. So the kth increment
costs k time to memcpy all the data so far, and hence the whole
process for n items takes O(n^2) time.
870 case IOC_NPF_SAVE:
871 error = npfctl_save(npf, req, resp);
https://nxr.netbsd.org/xref/src/sys/net/npf/npf_ctl.c?r=1.62#870
625 static int
626 npfctl_save(npf_t *npf, const nvlist_t *req, nvlist_t *resp)
627 {
...
640 error = npf_conndb_export(npf, resp);
https://nxr.netbsd.org/xref/src/sys/net/npf/npf_ctl.c?r=1.62#620
789 con = head;
790 while (con) {
791 nvlist_t *con_nvl;
792
793 con_nvl = nvlist_create(0);
794 if (npf_conn_export(npf, con, con_nvl) == 0) {
795 nvlist_append_nvlist_array(nvl, "conn-list", con_nvl);
796 }
797 nvlist_destroy(con_nvl);
798
799 if ((con = npf_conndb_getnext(conn_db, con)) == head) {
https://nxr.netbsd.org/xref/src/sys/net/npf/npf_conn.c?r=1.35#768
1647 #define NVLIST_APPEND_ARRAY(vtype, type, TYPE) \
1648 void \
1649 nvlist_append_##type##_array(nvlist_t *nvl, const char *name, vtype value)\
1650 { \
...
1662 if (nvpair_append_##type##_array(nvp, value) == -1) { \
https://nxr.netbsd.org/xref/src/sys/external/bsd/libnv/dist/nvlist.c?r=1.11#1647
170 static int
171 nvpair_append(nvpair_t *nvp, const void *value, size_t valsize, size_t datasize)
172 {
173 void *olddata, *data, *valp;
174 size_t oldlen;
175
176 oldlen = nvp->nvp_nitems * valsize;
177 olddata = (void *)(uintptr_t)nvp->nvp_data;
178 data = nv_realloc(olddata, oldlen + valsize);
https://nxr.netbsd.org/xref/src/sys/external/bsd/libnv/dist/nvpair.c?r=1.14#170
66 # define nv_realloc(buf, size) realloc((buf), (size), M_NVLIST, \
67 M_WAITOK)
https://nxr.netbsd.org/xref/src/sys/external/bsd/libnv/dist/nv_impl.h?r=1.7#66
72 #define realloc(ptr, size, type, flags) kern_realloc(ptr, size, flags)
https://nxr.netbsd.org/xref/src/sys/sys/malloc.h?r=1.117#72
174 void *
175 kern_realloc(void *curaddr, unsigned long newsize, int flags)
176 {
...
208 /*
209 * If we already actually have as much as they want, we're done.
210 */
211 if (newsize <= cursize)
212 return curaddr;
213
214 /*
215 * Can't satisfy the allocation with the existing block.
216 * Allocate a new one and copy the data.
217 */
218 newaddr = malloc(newsize, ksp, flags);
https://nxr.netbsd.org/xref/src/sys/kern/kern_malloc.c?r=1.158#174
>How-To-Repeat:
1. set up a stateful firewall with tens of thousands of connections
2. npfctl list
>Fix:
Increase the size geometrically, not linearly: double, don't
add an increment, each time there's no space for a new item, so
this takes amortized linear time instead of quadratic time.
Home |
Main Index |
Thread Index |
Old Index