From nobody Sun Oct 11 23:59:34 2026 X-Original-To: dev-commits-src-main@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 4j2yJ62lVHz6wKVM for ; Sun, 11 Oct 2026 23:59:34 +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 4j2yJ62CQlz3M6j for ; Sun, 11 Oct 2026 23:59:34 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791763174; 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=V8LRyBbf3cbm9B/BkHXcpsjNFctl4Ih70rGGUh84sLGccZoZHts4OiFL8qQnbn45GpoBaS r3V0pYRZ51hG9D5pxJ+nB83JxbNtRJGsDReSmWImJy2d6WBBWZKspa4us7NCZXx83jKE7F P1W8TsPqhdlt2Q/TtpD6LJL5VObcFaK4yof2F1KDvvQJI1diyWbhjmDuecdXT9dyfjPybc CN3fKJmjKoqAjbhS2kt+t/ceiyy503hj28N2emBQ7g0lS0jP2h9QYXpYtPLWv+/129RihM SQVI4bj9HIQSB1BVCEsw6y/kGF/oT2gL2ZRa8w8k6qTriRv8Ov9ziPIBC3aZ9g== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1791763174; b=GB79LPBhdq4+Kv5rT+OhNY9Cxa+gdhTLz30YAIz1Ge3MUz8Zu6CtJLEtBH5F9/BgGV/OHY Itg61u7axZOxppj4IZvFudwgQBiM/ND2VL/NXpv14fimc4qzpHg3wdaZvPL76B73Uzb66j NBP9KnkhzHs2emaLsqyPYfPS1lykbZ44NgOhP0n8bNKB28/756JxYn6Z9Tj/UhKaoD7AKQ 1Zew1by9BFVeL1amWn/1tKubclxLUGabF6nPwAvNTeVruR2fruoWUyF3XnlFlYni053xvZ +ZTtTFzYs7SRzcriEYibHOgeMZCCXKtjv07VyU07RfupRjU9f/c247/GF/PUbw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791763174; 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=qlZqkNMecnR4LjlUYs/4KX6A0NMxwkFUYdbdzZMbki5dKjljPhDvO8jKm7X/e9r3+Gn+Lv q9iwXJF5sgiTbkvLmlFaA6Yk0BPL0/Ov5SyaOpfgj3yFD1k4Q3QhcMAdfqyg7STJgZZsek w6AOl3TkdtbxKY7hmwWSpglyCxs5ribVHZk2NFMuX1NZxyg6x1PxlHoJNOkJBtcrpsVh6c PJToIndnAio0glmqgwreMNcrb+oC0knzhL41no2+CossZgvFeXXEgteZ4KGEniQaaxFxRT nuloJPmnbt0Vh+N55tWyTS2C+JRgcT6zs+M07tL8OvudJNkb2JilWOPMBeO+cw== 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 4j2yJ60yBcz13vr for ; Sun, 11 Oct 2026 23:59:34 +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-main X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org List-Id: Commit messages for the main branch 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)