The branch main has been updated by jhb:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=828df4d36d9d5a6ca0dcc294d65572b4a0474142

commit 828df4d36d9d5a6ca0dcc294d65572b4a0474142
Author:     John Baldwin <[email protected]>
AuthorDate: 2025-11-24 15:55:05 +0000
Commit:     John Baldwin <[email protected]>
CommitDate: 2025-11-24 15:55:05 +0000

    netlink: Fix overallocation of netlink message buffers
    
    Prior to commit 0c511bafdd5b309505c13c8dc7c6816686d1e103, each time
    snl_realloc_msg_buffer was called, it called snl_allocz to request a
    new buffer.  If an existing linear buffer was used, then after the
    call, the linear buffer effectively contained the old buffer contents
    followed by the new buffer (so there was definitely wasted memory),
    but the linear buffer state was consistent (lb->offset correctly
    accounted for both copies).  For example, if the initial linear buffer
    was 256 bytes in size, lb->size would be 256.  Using 16 bytes followed
    by 32 bytes would first set lb->offset to 16, then the second realloc
    would allocate 48 bytes (16 + 32) setting lb->offset to 64 (16 + 48).
    
    Commit 0c511bafdd5b309505c13c8dc7c6816686d1e103 aimed to avoid this
    memory waste by resetting the base pointer to the start of the
    existing linear buffer if the new allocation was later in the same
    linear buffer.  This avoided some of the waste, but broke the
    accounting.  Using the same example above, the second realloc would
    reuse the pointer at an offset of 0, but the linear buffer would still
    claim that 64 bytes was allocated via lb->offset rather than the true
    allocation of 48 bytes.
    
    One approach to fix this would be to "extend" the allocation of an
    existing linear buffer where a realloc would try to increase
    lb->offset without setting a new base pointer so long as there was
    still room remaining in the linear buffer for the new request.
    
    However, this change takes a simpler approach.  If snl_allocz()
    returned an allocation from a new linear buffer, just claim the entire
    linear buffer for use by the snl_writer ensuring the accounting is
    correct in both the linear buffer and the snl writer.  With this
    approach, the initial snl_writer size would be 256 bytes for a 256
    byte linear buffer and would only grow if it needs to allocate an
    entirely new linear buffer.
    
    Reviewed by:    igoro
    Fixes:          0c511bafdd5b ("netlink: fix snl_writer and linear_buffer 
re-allocation logic")
    Sponsored by:   AFRL, DARPA
    Differential Revision:  https://reviews.freebsd.org/D53697
---
 sys/netlink/netlink_snl.h | 27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/sys/netlink/netlink_snl.h b/sys/netlink/netlink_snl.h
index 57f7e1e29d08..1e560e029718 100644
--- a/sys/netlink/netlink_snl.h
+++ b/sys/netlink/netlink_snl.h
@@ -1082,6 +1082,7 @@ snl_init_writer(struct snl_state *ss, struct snl_writer 
*nw)
 static inline bool
 snl_realloc_msg_buffer(struct snl_writer *nw, size_t sz)
 {
+       void *new_base;
        uint32_t new_size = nw->size * 2;
 
        while (new_size < nw->size + sz)
@@ -1090,23 +1091,27 @@ snl_realloc_msg_buffer(struct snl_writer *nw, size_t sz)
        if (nw->error)
                return (false);
 
-       if (snl_allocz(nw->ss, new_size) == NULL) {
+       new_base = snl_allocz(nw->ss, new_size);
+       if (new_base == NULL) {
                nw->error = true;
                return (false);
        }
-       nw->size = new_size;
 
-       void *new_base = nw->ss->lb->base;
-       if (new_base != nw->base) {
-               memcpy(new_base, nw->base, nw->offset);
-               if (nw->hdr != NULL) {
-                       int hdr_off = (char *)(nw->hdr) - nw->base;
+       if (new_base == nw->ss->lb->base) {
+               /* Claim the entire linear buffer. */
+               nw->size = nw->ss->lb->size;
+               nw->ss->lb->offset = nw->ss->lb->size;
+       } else
+               nw->size = new_size;
 
-                       nw->hdr = (struct nlmsghdr *)
-                           (void *)((char *)new_base + hdr_off);
-               }
-               nw->base = (char *)new_base;
+       memcpy(new_base, nw->base, nw->offset);
+       if (nw->hdr != NULL) {
+               int hdr_off = (char *)(nw->hdr) - nw->base;
+
+               nw->hdr = (struct nlmsghdr *)
+                   (void *)((char *)new_base + hdr_off);
        }
+       nw->base = (char *)new_base;
 
        return (true);
 }

Reply via email to