From patchwork Mon Aug 31 08:34:40 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Gert Doering X-Patchwork-Id: 5301 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:7000:c317:b0:87d:ab56:3700 with SMTP id jk23csp2874799mab; Mon, 31 Aug 2026 01:35:16 -0700 (PDT) X-Forwarded-Encrypted: i=2; AHgh+Rpy30cJpxYPUythjhuuZRmPSzp2YKf+pO2QUHNU2p/laO4dyqfSgD7YzhZWeKWUqyK9tNhUTdg4Kas=@openvpn.net X-Received: by 2002:a05:6870:3313:b0:448:3eb2:8c8c with SMTP id 586e51a60fabf-46835d7d79fmr24250094fac.6.1788165316434; Mon, 31 Aug 2026 01:35:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1788165316; cv=none; d=google.com; s=arc-20260327; b=c9IbsPiSud497cjL8RTu92iKKZMdx3qj4wwNqgMZiA5/3BT8ZS6dNi9JfjFROXfUbg /kKoKYUeJ3NI75N5FStJdzUF+IpwHsBh9kCuJh5cvALg7nkN3q3EbKbs+iQukNXrpoLu YdSqz/+7W0uA5k+viqXvbBVHOoTgjy3hx7AsjCVmO2TpfXeq+uNyaBjwwCcji/OuF6nz aVXayUQYko+YLbiWqByfvl1/LozMIB/ux/ZyWsFOFSSZNrjPz6l8AXaopm7hZjLOLJMZ 57imOA5Mxi//9c/+NhC0Y0iieZ5Yj5iLyA+XG4gFTtw706sCyS7/V1/EkAX/YaPR0LQo 2edw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=errors-to:content-transfer-encoding:list-subscribe:list-help :list-post:list-archive:list-unsubscribe:list-id:precedence:subject :mime-version:references:in-reply-to:message-id:date:to:from :dkim-signature:dkim-signature:dkim-signature; bh=NcgfHFoJFyNLt10cjua4HuAuuZLXJon6Xy7nNZWLY4Q=; fh=4NbAC/LsuMLI0S0hprUlLSLCiHwg6SCAifhH718Jh0Q=; b=Uyoj0nItt1kVqK75Fz+tX7EfLJ+r70fQokTki1GzU9x+ye5OQoFLiUaFSZygNixxBd SmQufA9HyrKAvQ04e6MkgV05yQDJHpIWwE0Z3ke4kBZu2+MNWqPBGnsvt6nfeMPxYnVG lwED29RNx9CjDN1EdAKmvCWgoYyNLQnPwv9fZgCR3jvVD3Od9qnmVEmIOqG5aoS/Xh0L bkI+NaoZT3103AqNEM4pZLfs4pFBG8+thOi1Ad/LhEivLS0JGLxuKvZ87AWvVeSFklTw AiS9WDgOAOv513GrlsbdUXmlgtwckX7NntE9s8XY5N6c3sEZCal6InHMx0NvzwU+Npy4 mpFA==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=cIcJ6Jv5; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=R6RqvzGf; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=Z1VYsyiu; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=muc.de Received: from lists.sourceforge.net (lists.sourceforge.net. [216.105.38.7]) by mx.google.com with ESMTPS id 586e51a60fabf-468a6145b1asi12140406fac.234.2026.08.31.01.35.16 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 31 Aug 2026 01:35:16 -0700 (PDT) Received-SPF: pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) client-ip=216.105.38.7; Authentication-Results: mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=cIcJ6Jv5; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=R6RqvzGf; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=Z1VYsyiu; spf=pass (google.com: domain of openvpn-devel-bounces@lists.sourceforge.net designates 216.105.38.7 as permitted sender) smtp.mailfrom=openvpn-devel-bounces@lists.sourceforge.net; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=muc.de DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.sourceforge.net; s=beta; h=Content-Transfer-Encoding:Content-Type: List-Subscribe:List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id: Subject:MIME-Version:References:In-Reply-To:Message-ID:Date:To:From:Sender: Reply-To:Cc:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=NcgfHFoJFyNLt10cjua4HuAuuZLXJon6Xy7nNZWLY4Q=; b=cIcJ6Jv5JfCyZXaVzjDfqG7DEo DrjROxC+ZhYkVUZdBYw93WUD6yE/V80w03BQSEEpRfS3GvuUH/ceeRXNfpc12NRbe/Q52rwRTzb5H fKvC/TJEb46D/egIoy9W72sJ5WfEETrEQWgXVO5mZO6Qz77jQ9tX6/xbpiFwRDx87uL0=; Received: from [127.0.0.1] (helo=sfs-ml-1.v29.lw.sourceforge.com) by sfs-ml-1.v29.lw.sourceforge.com with esmtp (Exim 4.95) (envelope-from ) id 1x0xU3-0002Ne-MD; Mon, 31 Aug 2026 08:35:13 +0000 Received: from [172.30.29.66] (helo=mx.sourceforge.net) by sfs-ml-1.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1x0xTp-0002EI-CP for openvpn-devel@lists.sourceforge.net; Mon, 31 Aug 2026 08:34:58 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:Content-Type:MIME-Version :References:In-Reply-To:Message-ID:Date:Subject:To:From:Sender:Reply-To:Cc: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=HFxOmFIwtRqOQH39In1CEfYNbimN1app4sxa1ALyKm4=; b=R6RqvzGfLJ/SLju8lXZOFukgW5 bIMpIHseOXeqkrNctBr+RG3gNGGkt/9GwXZA/LZkH1Q8og27IjUwdgo0xL0Ios8kqp8rmGA2rqDLn P3CQB2n4HXOPCcs167M11BEBhvxQFqcdXYGDADfPL8ze16Rs6oPnbjD2ybjqG5DT1qRo=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:Content-Type:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Sender:Reply-To:Cc:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=HFxOmFIwtRqOQH39In1CEfYNbimN1app4sxa1ALyKm4=; b=Z1VYsyiutXpWUk5kMWxdyKncwI ozWE6veKU56dLpVjFAOnocIBBSoeZK7Ohl4qLC5cEDP8zrVkuv2LZrA/enEHusSnVhNmOGH8evLjH 8z5Zfwvhahpf3WKZPAOLyWxzJ0/BngMda71os1LjOAQUjHVBTFiVYGoqrDYfSCQJWfQ8=; Received: from [193.149.48.129] (helo=blue.greenie.muc.de) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.95) id 1x0xTl-0003lg-6N for openvpn-devel@lists.sourceforge.net; Mon, 31 Aug 2026 08:34:58 +0000 Received: from blue.greenie.muc.de (localhost [127.0.0.1]) by blue.greenie.muc.de (8.18.1/8.18.1) with ESMTP id 67V8Yo42012507 for ; Mon, 31 Aug 2026 10:34:50 +0200 Received: (from gert@localhost) by blue.greenie.muc.de (8.18.2/8.18.1/Submit) id 67V8YoZh012506 for openvpn-devel@lists.sourceforge.net; Mon, 31 Aug 2026 10:34:50 +0200 From: Gert Doering To: openvpn-devel@lists.sourceforge.net Date: Mon, 31 Aug 2026 10:34:40 +0200 Message-ID: <20260831083449.12484-1-gert@greenie.muc.de> X-Mailer: git-send-email 2.53.0 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Score: 1.3 (+) X-Spam-Report: Spam detection software, running on the system "sfi-spamd-2.hosts.colo.sdot.me", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: From: Nexory Each search list entry consumes strlen(ptr) + 2 bytes of tmp_buf: one leading label length byte, the domain characters, and one trailing NUL. The guard only accounted for strlen(ptr) + 1, so a sequenc [...] Content analysis details: (1.3 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 1.3 RDNS_NONE Delivered to internal network by a host with no rDNS X-Headers-End: 1x0xTl-0003lg-6N Subject: [Openvpn-devel] [PATCH v2] dhcp: Fix off-by-one in write_dhcp_search_str() temp buffer guard X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox X-GMAIL-THRID: 1875027234599605865 X-GMAIL-MSGID: 1875027234599605865 From: Nexory Each search list entry consumes strlen(ptr) + 2 bytes of tmp_buf: one leading label length byte, the domain characters, and one trailing NUL. The guard only accounted for strlen(ptr) + 1, so a sequence of entries whose accumulated length lands exactly on the boundary passed the check and then wrote tmp_buf[256], one byte past the 256 byte array. The existing "len > 255" check enforces the correct upper bound, but it runs after that write has already happened. The entries can be pushed by the server: --dhcp-option falls under OPT_P_DHCPDNS, which pull_permission_mask() includes, and validate_domain() imposes no length limit. Reproduced under AddressSanitizer, which reports a one byte stack-buffer-overflow at dhcp.c:308. The added unit test covers the boundary; it fails before this change and passes after it. The two existing cases marked "maximum length" are unaffected, since a 253 character domain still satisfies 253 + 0 + 2 <= 256. This was reported independently by Andre Kropp and Chính Nguyễn Văn. Patch author is Andre Kropp, recording both reports in the Reported-By: CVE: 2026-81738 Change-Id: I6a886a1cac2d4725859dab2325cd362312ddc659 Signed-off-by: Nexory Acked-by: Gert Doering Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1884 Reported-By: Andre Kropp (Nexory) Reported-By: ChinhNguyen --- This change was reviewed on Gerrit and approved by at least one developer. I request to merge it to master. Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1884 This mail reflects revision 2 of this Change. Acked-by according to Gerrit (reflected above): Gert Doering diff --git a/src/openvpn/dhcp.c b/src/openvpn/dhcp.c index a54ab3f..5cdcfcf 100644 --- a/src/openvpn/dhcp.c +++ b/src/openvpn/dhcp.c @@ -277,7 +277,9 @@ { const char *ptr = str_array[i]; - if (strlen(ptr) + len + 1 > sizeof(tmp_buf)) + /* Each entry consumes strlen(ptr) + 2 bytes: one leading label length + * byte and one trailing NUL. */ + if (strlen(ptr) + len + 2 > sizeof(tmp_buf)) { *error = true; msg(M_WARN, "write_dhcp_search_str: temp buffer overflow building DHCP options"); diff --git a/tests/unit_tests/openvpn/test_dhcp.c b/tests/unit_tests/openvpn/test_dhcp.c index 104fc9a..3a84e1e 100644 --- a/tests/unit_tests/openvpn/test_dhcp.c +++ b/tests/unit_tests/openvpn/test_dhcp.c @@ -120,6 +120,24 @@ assert_memory_equal(BPTR(&out_buf), output_5, sizeof(output_5)); assert_false(error); + /* Several entries whose accumulated length lands exactly on the guard + * boundary. Each entry consumes strlen()+2 bytes of tmp_buf (one length + * prefix plus one trailing NUL), but the guard only accounts for + * strlen()+1, so the last entry writes one byte past tmp_buf[256]. + * Sizes: 4 x 50 leaves len == 208, the final 47 makes + * 47 + 208 + 1 == 256, which the guard still accepts. */ +#define D50 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +#define D47 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + const char *overflow_list[] = { D50, D50, D50, D50, D47 }; + assert_int_equal(strlen(D50), 50); + assert_int_equal(strlen(D47), 47); + buf_clear(&out_buf); + write_dhcp_search_str(&out_buf, DHCP_DOMAIN_SEARCH, overflow_list, 5, &error); + /* total is 257 > 255, so the option must be rejected -- the point of this + * case is that tmp_buf must not be written out of bounds on the way. */ + assert_true(error); + error = false; + gc_free(&gc); }