From nobody Sat Oct 31 18:58:38 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4jH6gg0DmXz6x64n for ; Sat, 31 Oct 2026 18:58:39 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 4jH6gf6LXPz3J9h for ; Sat, 31 Oct 2026 18:58:38 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1793473118; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=IC+SfvgbheQCvg2N968U3MI4tvRu5gwz0ywUzWwsXQY=; b=ucE/lxfnaVRwnIOxJMkYkoY9x9KvmXhs6v7mPNcPj3DSZrwES5ZzxQaPrzs3tObxmgOY34 UwK9NMX1PGCEZ2uG06YktwA7c0uBpjo4RI0fkQs+5DKK0P20Z+a3X2dRp4kWxoxoCQxmlg D0XckDEiNnetGTuPSEliszsgY4dRqf0BBCylBA4YLD3fYT0F/V3PPpKC+KrbizeOU9J11E dgiN1CDVyZj7lM5TAqqpuWf5nV/KRGrf+tT+nxD3TPzf79HUm9G+hh6GlpFa+LwC4PfaWy e4QB3jyevrjd1fFdkWx4Dj/p3Q0wFXAKDzd5pgu7ZrL1rwD0ITzHMoOGojWZmg== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1793473118; b=Ffqg7d578NKl60fncHGfIGCX5RCDxpbErvcEnlWC9tC6SN/qA+8pBNEblhwOwQV9pBlkpd 2InRe/orpkHUJd9sXNdlA/GdWGuqm6ijDlJguU13VAjWy7GvfGv+7Z3irSnVJ+e6MA8ewf eOwR4CQOPFB3W5XYRRxruGiq8cHc4P2/6lU08Tx0Vs3oZyydy1quor43uxEmapb+7m+vII OzpfrbDLmFO2LruzgzDkHIpz3hpuXPV+zHc1eL1ziFQqdeKOXxh/UxRl8J/pQBlOK4o4Kh HIUHIPrRuQUqE9O1uc9BvpvO8W3l005biS4+92TLFTN0jMYjpFbW4lRUpucc8Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1793473118; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=IC+SfvgbheQCvg2N968U3MI4tvRu5gwz0ywUzWwsXQY=; b=b8emXmiabmTFNC72vCHFHa/EE0l2anc/hyE4BqAhSySiorLxpm4kUNfh5gzsGaLn3zDcS5 KQx/N6GuZJcXh3PAcf1vSoaHT8aNvZC4TBXBwdlRzWPxjDp6K566RRqD4e6UyQk3VTOPmp vbvALN9qDY4za2CkqNVoeSSk/mfwZrYfYLyDrWRFVWGGjEQsoKKBh/V6XYN2ACyJeOMBnl wzUrx03/4813qyNMY+4nBRiM+nGcA4dWXOu3lw9ya+Ii7i55h2aRrRblOTwQ3TilLubK8+ 9+68YX3rTOfAE3chyYsVGCQP70Lgu2X8tR1TS4+9QVZhuQKOOLRRlNjIBr6/AA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4jH6gf5j5BzPCM for ; Sat, 31 Oct 2026 18:58:38 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 23144 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sat, 31 Oct 2026 18:58:38 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Cc: R. Christian McDonald From: Kevin Bowling Subject: git: de1d079d6a3a - stable/15 - netlink: Make room for the unfinished message when growing the buffer List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: Commit messages for all branches of the src repository List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kbowling X-Git-Repository: src X-Git-Refname: refs/heads/stable/15 X-Git-Reftype: branch X-Git-Commit: de1d079d6a3a84642adf7690017f897f65947d28 Auto-Submitted: auto-generated Date: Sat, 31 Oct 2026 18:58:38 +0000 Message-Id: <6ae63a5e.23144.3cb3147e@gitrepo.freebsd.org> The branch stable/15 has been updated by kbowling: URL: https://cgit.FreeBSD.org/src/commit/?id=de1d079d6a3a84642adf7690017f897f65947d28 commit de1d079d6a3a84642adf7690017f897f65947d28 Author: R. Christian McDonald AuthorDate: 2026-10-11 20:46:56 +0000 Commit: Kevin Bowling CommitDate: 2026-10-31 18:57:03 +0000 netlink: Make room for the unfinished message when growing the buffer When a message outgrows the writer's buffer, _nlmsg_refill_buffer() allocates a larger one and moves the unfinished message there. The new buffer was sized for the data about to be added alone, so with enough of the message already written the caller wrote past its end and left it with datalen > buflen. A 20-byte message plus an attribute of 8176 bytes got a buffer of 8192 bytes for 8200 bytes of data, and reading it panicked an INVARIANTS kernel in nl_soreceive(). Size the new buffer for both, and add a ktest for it. Reviewed by: glebius Fixes: 7e5bf68495cc ("netlink: add netlink support") MFC after: 1 week Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D60541 (cherry picked from commit 5180fce1cf7ec7430ccf2adb88358602e5ccee1e) --- sys/netlink/ktest_netlink_message_writer.c | 97 ++++++++++++++++++++++++ sys/netlink/netlink_message_writer.c | 7 +- tests/sys/netlink/test_netlink_message_writer.py | 21 +++++ 3 files changed, 122 insertions(+), 3 deletions(-) diff --git a/sys/netlink/ktest_netlink_message_writer.c b/sys/netlink/ktest_netlink_message_writer.c index 0e8b962bbd34..aecda0e9545d 100644 --- a/sys/netlink/ktest_netlink_message_writer.c +++ b/sys/netlink/ktest_netlink_message_writer.c @@ -97,6 +97,97 @@ test_nlbuf_writer_allocation(struct ktest_test_context *ctx) return (0); } +struct test_nlbuf_refill_attrs { + uint32_t size; + uint32_t completed; + uint32_t unfinished; + uint32_t attr_len; +}; + +#define _OUT(_field) offsetof(struct test_nlbuf_refill_attrs, _field) +static const struct nlattr_parser nla_p_nlbuf_refill[] = { + { .type = 1, .off = _OUT(size), .cb = nlattr_get_uint32 }, + { .type = 2, .off = _OUT(completed), .cb = nlattr_get_uint32 }, + { .type = 3, .off = _OUT(unfinished), .cb = nlattr_get_uint32 }, + { .type = 4, .off = _OUT(attr_len), .cb = nlattr_get_uint32 }, +}; +#undef _OUT +NL_DECLARE_ATTR_PARSER(nlbuf_refill_parser, nla_p_nlbuf_refill); + +static int +test_nlbuf_refill_parser(struct ktest_test_context *ctx, struct nlattr *nla) +{ + struct test_nlbuf_refill_attrs *attrs; + + attrs = npt_alloc(ctx->npt, sizeof(*attrs)); + ctx->arg = attrs; + if (attrs != NULL) + return (nl_parse_nested(nla, &nlbuf_refill_parser, ctx->npt, + attrs)); + return (ENOMEM); +} + +static bool +test_nlbuf_discard(struct nl_writer *nw) +{ + nl_buf_free(nw->buf); + nw->buf = NULL; + return (true); +} + +/* + * Add an attribute that does not fit into the buffer to an unfinished + * message, optionally preceded by a completed one. + */ +static int +test_nlbuf_writer_refill(struct ktest_test_context *ctx) +{ + struct test_nlbuf_refill_attrs *attrs = ctx->arg; + struct nl_writer nw = {}; + void *data; + u_int expected; + int error = 0; + + if (attrs == NULL || attrs->unfinished < sizeof(struct nlmsghdr) || + (attrs->completed != 0 && + attrs->completed < sizeof(struct nlmsghdr))) + return (EINVAL); + if (!nlmsg_get_buf_wrapper(&nw, attrs->size, true)) + return (EINVAL); + nw.cb = test_nlbuf_discard; + data = malloc(attrs->attr_len, M_TEMP, M_WAITOK | M_ZERO); + + if (attrs->completed != 0) { + if (!nlmsg_add(&nw, 0, 0, NLMSG_NOOP, 0, 0) || + nlmsg_reserve_data(&nw, attrs->completed - + sizeof(struct nlmsghdr), void *) == NULL || + !nlmsg_end(&nw)) { + error = EINVAL; + goto out; + } + } + if (!nlmsg_add(&nw, 0, 0, NLMSG_NOOP, 0, 0) || + nlmsg_reserve_data(&nw, attrs->unfinished - + sizeof(struct nlmsghdr), void *) == NULL || + !nlattr_add(&nw, 1, attrs->attr_len, data)) { + error = EINVAL; + goto out; + } + + expected = NETLINK_ALIGN(attrs->unfinished) + + NLA_ALIGN(attrs->attr_len + sizeof(struct nlattr)); + KTEST_LOG(ctx, "message of %u bytes in a buffer of %u, expected %u", + nw.buf->datalen, nw.buf->buflen, expected); + if (nw.buf->datalen > nw.buf->buflen || nw.buf->datalen != expected) + error = EINVAL; +out: + if (nw.buf != NULL) + nl_buf_free(nw.buf); + free(data, M_TEMP); + + return (error); +} + static const struct ktest_test_info tests[] = { { .name = "test_nlbuf_writer_allocation", @@ -104,5 +195,11 @@ static const struct ktest_test_info tests[] = { .func = &test_nlbuf_writer_allocation, .parse = &test_nlbuf_parser, }, + { + .name = "test_nlbuf_writer_refill", + .desc = "test growing the buffer under an unfinished message", + .func = &test_nlbuf_writer_refill, + .parse = &test_nlbuf_refill_parser, + }, }; KTEST_MODULE_DECLARE(ktest_netlink_message_writer, tests); diff --git a/sys/netlink/netlink_message_writer.c b/sys/netlink/netlink_message_writer.c index 8c5b3ec14058..bcb109f29cc8 100644 --- a/sys/netlink/netlink_message_writer.c +++ b/sys/netlink/netlink_message_writer.c @@ -156,6 +156,7 @@ _nlmsg_refill_buffer(struct nl_writer *nw, size_t required_len) /* Calculate new buffer size and allocate it. */ completed_len = (nw->hdr != NULL) ? (char *)nw->hdr - nw->buf->data : nw->buf->datalen; + last_len = nw->buf->datalen - completed_len; if (completed_len > 0 && required_len < NLMBUFSIZE) { /* We already ran out of space, use largest effective size. */ new_len = max(nw->buf->buflen, NLMBUFSIZE); @@ -165,9 +166,10 @@ _nlmsg_refill_buffer(struct nl_writer *nw, size_t required_len) new_len = NLMBUFSIZE; else new_len = nw->buf->buflen * 2; - while (new_len < required_len) - new_len *= 2; } + /* The unfinished message is copied over and needs room as well. */ + while (new_len < last_len + required_len) + new_len *= 2; new = nl_buf_alloc(new_len, nw->malloc_flag | M_ZERO); if (__predict_false(new == NULL)) { @@ -177,7 +179,6 @@ _nlmsg_refill_buffer(struct nl_writer *nw, size_t required_len) } /* Copy last (unfinished) header to the new storage. */ - last_len = nw->buf->datalen - completed_len; if (last_len > 0) { memcpy(new->data, nw->hdr, last_len); new->datalen = last_len; diff --git a/tests/sys/netlink/test_netlink_message_writer.py b/tests/sys/netlink/test_netlink_message_writer.py index 5f854b14ca45..5a0788184a7d 100644 --- a/tests/sys/netlink/test_netlink_message_writer.py +++ b/tests/sys/netlink/test_netlink_message_writer.py @@ -37,3 +37,24 @@ class TestNetlinkMessageWriter(BaseKernelTest): NlAttrU32(3, malloc_flags), ] self.runtest(test_meta) + + @pytest.mark.parametrize( + "sz", + [ + # buffer size, completed message, unfinished message, attribute + pytest.param([NLMSG_SMALL, 0, 20, 112], id="small"), + pytest.param([NLMSG_SMALL, 0, 20, 8176], id="large_attr"), + pytest.param([NLMSG_LARGE, 0, 2000, 4092], id="large_msg"), + pytest.param([NLMSG_LARGE, 100, 1900, 196], id="after_completed"), + ], + ) + def test_nlbuf_writer_refill(self, sz): + """override to parametrize""" + + test_meta = [ + NlAttrU32(1, sz[0]), # size + NlAttrU32(2, sz[1]), # completed + NlAttrU32(3, sz[2]), # unfinished + NlAttrU32(4, sz[3]), # attr_len + ] + self.runtest(test_meta)