From nobody Sun Oct 11 23:59:34 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 4j2yJC5nvbz6wKd7 for ; Sun, 11 Oct 2026 23:59: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 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR2" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4j2yJC43RSz3MPc for ; Sun, 11 Oct 2026 23:59:39 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791763179; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=sshgOSEldD8JqrpdzC48x90RgsAnaj1Zyiw/3KO+FY8=; b=kPVi576CIg9jXDTkjc1jKiq4I8+CZwd3o3kRGkZauDpkwtvcGL2CbJSxS/0XJjwVXnTnaa HXDAaSekQp7H+dKNpz5zzA2b+ECQwK5Vpghhz5mwJNdVrA9/UHh3Hk7SMubXESeIWA4hNB tGiu+jNInqMxDdQMCVeeCasCD6BXIvn1XE9bb6DpNDQbchlTkcTgqEWXnEm1CpuhUPpMSY Na3wyw/OagMCvvZdvWKr30YO+T86jrhlrhIZXF8goZq4dIT1FLpi18pP7OxPffXD3kvaRH 7TnOMZ+eHQOVDL3GEKUihpCuKsX11IaxB17RHHdbC3ds875XjwUo6ifQ8ZQPoQ== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1791763179; b=Zf0auue7jry71e50JWQijDR63Yt/iYrq0s8w4THPTq/MQ1gZB87ZBmhKlh3IWlom8xbnO+ hwBp/1knv64V3WxVoKtuCdN5HWyUj0/XzUTUXx7jwDWDKV30MPgugu/XZNY4O3e8XxoTUu EoqM7uT5JCLO9jd/FFBwP3IGJNGhe2u5ABy7q4Jrea4Af6JBtRJR00wSbG5Df0JAuVkptK ydnyJXkqz+IUGO5IkqSwsnbKiUgnBmlwX6hV1BCT1UsH24AgdCHBfjQNyAgkjycisyOhAT wEapfdRlZ2hLRgPz5C+i1QcxAXuiwvWtcBs6guJEpBIRdxUrjf84Lv+BtFqn3A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791763179; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=sshgOSEldD8JqrpdzC48x90RgsAnaj1Zyiw/3KO+FY8=; b=dO14CdpEOKAGyvz9xHUFvoR6NIZZ+2e22W+Z5NwKZ4v0QVeqlNILkOcNytuJmbAw102nyq uO+MrC0dDFLdu8xcyVjK3vdH8b1XA8u3QRugWWYwE4s53KUfHc9FVVgQYET+jlxNS/cmFS cuS0kYW8ec4htsn38jQEGChUfbLGOVo+DoYusNRNldSjukswExbA4erScBiTzc5o8Qz7xY BoCJACUGG7e4o9Ytuo+mp1O8QYWPl+PpFDMpxB7ZXQCfi7pxat56bonyEHbTp6dZKashX+ FnlVgNA4FGHAoii8eEgYiYA76mYrxKt9+jdBAYCuLemE8yEQMMuIRCXPyW9KEw== 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 4j2yJC2Z0Kz142S for ; Sun, 11 Oct 2026 23:59:39 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 26bb0 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sun, 11 Oct 2026 23:59:34 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: R. Christian McDonald Subject: git: 5180fce1cf7e - main - 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: rcm X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 5180fce1cf7ec7430ccf2adb88358602e5ccee1e Auto-Submitted: auto-generated Date: Sun, 11 Oct 2026 23:59:34 +0000 Message-Id: <6acc22e6.26bb0.7eb3a80@gitrepo.freebsd.org> The branch main has been updated by rcm: URL: https://cgit.FreeBSD.org/src/commit/?id=5180fce1cf7ec7430ccf2adb88358602e5ccee1e commit 5180fce1cf7ec7430ccf2adb88358602e5ccee1e Author: R. Christian McDonald AuthorDate: 2026-10-11 20:46:56 +0000 Commit: R. Christian McDonald CommitDate: 2026-10-11 23:58:48 +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 --- 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)