[Openvpn-devel,v2] options: fix unsigned underflow when clearing domain_search_list

Message ID 20260913132322.2283-1-gert@greenie.muc.de
State New
Headers
Series [Openvpn-devel,v2] options: fix unsigned underflow when clearing domain_search_list |

Commit Message

Gert Doering Sept. 13, 2026, 1:23 p.m. UTC
  From: Cole Munz <Munzzyy1@proton.me>

remove_option() and update_option() clear the domain search list with

    while (o->domain_search_list_len-- > 0)

domain_search_list_len is unsigned, and the post-decrement runs on the
final test too. When the length reaches 0 the condition is false but
the decrement has already wrapped it to UINT_MAX, so the field is left
corrupted. The next reset then does

    o->domain_search_list[UINT_MAX] = NULL

and walks far out of bounds, writing NULL through each slot. A server
can drive this reset path against a client with PUSH_UPDATE, so on
Windows and Android this is a remotely reachable out-of-bounds write.

v2:
  Change to the semantics used for all the other lists there - set
  length to 0 and clear the list with CLEAR().

Change-Id: I3724236d4acc3d05d786274d6baf34a21c570594
Signed-off-by: Cole Munz <Munzzyy1@proton.me>
Acked-by: Razvan Cojocaru <razvanc@mailbox.org>
Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1914
Github: OpenVPN/openvpn-private-issues#178
CVE: 2026-88964
---

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/+/1914
This mail reflects revision 2 of this Change.

Acked-by according to Gerrit (reflected above):
Razvan Cojocaru <razvanc@mailbox.org>
  

Patch

diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 25a3746..f6a5fe4 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3778,12 +3778,12 @@ 
         memset(o->ntp, 0, sizeof(o->ntp));
         o->nbdd_len = 0;
         memset(o->nbdd, 0, sizeof(o->nbdd));
-        while (o->domain_search_list_len-- > 0)
-        {
-            o->domain_search_list[o->domain_search_list_len] = NULL;
-        }
+        o->domain_search_list_len = 0;
+        CLEAR(o->domain_search_list);
         o->disable_nbt = 0;
         o->dhcp_options = 0;
+        CLEAR(options->dns_options.from_dhcp);
+
 #if defined(TARGET_ANDROID)
         o->http_proxy_port = 0;
         o->http_proxy = NULL;
@@ -4082,10 +4082,8 @@ 
             CLEAR(o->ntp);
             o->nbdd_len = 0;
             CLEAR(o->nbdd);
-            while (o->domain_search_list_len-- > 0)
-            {
-                o->domain_search_list[o->domain_search_list_len] = NULL;
-            }
+            o->domain_search_list_len = 0;
+            CLEAR(o->domain_search_list);
             o->disable_nbt = 0;
             o->dhcp_options = 0;