From patchwork Fri Sep 25 13:14:11 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gert Doering X-Patchwork-Id: 5408 Return-Path: Delivered-To: patchwork@openvpn.net Received: by 2002:a05:7000:5189:b0:8b3:6e77:b38b with SMTP id g9csp167206mae; Fri, 25 Sep 2026 06:14:34 -0700 (PDT) X-Forwarded-Encrypted: i=2; AKwUvBztPY78fdBwwpFGoZHRVi6L7kIf3aAsYbCAqYTYn+tokSnlHTegi7zq7Z3MaM8qUSgX+PHkOJpvrDM=@openvpn.net X-Received: by 2002:a05:6871:3a0c:b0:47d:a38a:6218 with SMTP id 586e51a60fabf-491e8402dbcmr5405616fac.25.1790342074644; Fri, 25 Sep 2026 06:14:34 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1790342074; cv=none; d=google.com; s=arc-20260327; b=SrbOB4jDoPCO5rnc34CxV1MJLyktPnNMKLTiYBhybXJd4tQ7QrqgsFo+0JtY3/zRHp Q/3wyP92mat0dOuTTqBa+RlZPRHFsr6OVTUulmkamvvgh3HcDgVwRgmCHCpOrAI3YcRy t0OA3liqRerdDOqs3mmkCztWxWf17U8WDjNiZTHMSzUXKq496/UtPWYEk1i3wd/5I841 IP27TEVJyf0VVNJFs8a1HSlsW2cAB+uO2mWIKqPBnXQWJKz/npeaiUuuIEcr+fR2qZ54 bVU705ljc4oSH+JBn8Wjg9yLWKfqURfMe8wYpuq2fFQDF+odM/F+esOoM1BsU5k89uGK 2SCw== 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=QRd/nsm6KvShHEqEOB7OnxNeLXHiiCZJj3joHdNX+a4=; fh=4NbAC/LsuMLI0S0hprUlLSLCiHwg6SCAifhH718Jh0Q=; b=ckVct/tLSrTDlDWTqYeEQYTcevqMmXaFtOB5R+FqOK1gPu6eroKubGR2lA4nm+guDQ 5lQNysm6y6OfRkrMrIHnIx+zJvF+yG2p2rMZgtrsT82SKGpNANziMJHJJsN/4bCVRA1+ qibyTopxNofKuglTxIR6Ov01+gaWC0XbI+zuT7C/CxqZN78sb4qGPjKP9Cq5B0o9qjgE Vel7tmbwgf8zepwDTUhuCst0Mv5SspR1MmikEYCWx6oXabhAIX01BxiBxT8fUfHLmakx DEBDycYwCP8o7DEZI2wtoVc5aZ3VGtFpGzbhz67F1KqwL/tG0t0INPD0yAVUH9L8QYRA Zvpw==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@lists.sourceforge.net header.s=beta header.b=QZEU9a5v; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=coUxGc32; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=Ml2kYHHT; 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-493363a78ebsi3707780fac.260.2026.09.25.06.14.34 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Fri, 25 Sep 2026 06:14:34 -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=QZEU9a5v; dkim=neutral (body hash did not verify) header.i=@sourceforge.net header.s=x header.b=coUxGc32; dkim=neutral (body hash did not verify) header.i=@sf.net header.s=x header.b=Ml2kYHHT; 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=QRd/nsm6KvShHEqEOB7OnxNeLXHiiCZJj3joHdNX+a4=; b=QZEU9a5vbihgHT+ytJgVqs0Cn4 +ZGZASZtj33tLY3RZggLIjS1QY26c88ON2tSrbi/bYjfHRn/75CJdRpuWjw+wuF2/NCqB1kZ7RCes 9dHiMiCK/dCn1gHCsyLfPWAE20+axUTa5gJGp4zkV3FgZGQ/a9skvQF6765TkhqpWOK0=; Received: from [127.0.0.1] (helo=sfs-ml-3.v29.lw.sourceforge.com) by sfs-ml-3.v29.lw.sourceforge.com with esmtp (Exim 4.95) (envelope-from ) id 1xA5kz-0007Vg-5P; Fri, 25 Sep 2026 13:14:29 +0000 Received: from [172.30.29.66] (helo=mx.sourceforge.net) by sfs-ml-3.v29.lw.sourceforge.com with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.95) (envelope-from ) id 1xA5kx-0007VZ-Oz for openvpn-devel@lists.sourceforge.net; Fri, 25 Sep 2026 13:14:28 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=Content-Transfer-Encoding:MIME-Version:References: In-Reply-To:Message-ID:Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type: 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=s4EjK/cCFqkYa2P+IY5Mz9OOPwdEZWL1A/yK/eYA2gc=; b=coUxGc32RUFNiI1BFWvKBHZCh+ QK9zM1mNnVpb2dtr5B89t7+CUkGm2aFMw1+/rpP+QolpAXr7X0p6QBBhWTa/CEyk79gqvfcgAhPDa MPGsS9eFwqmAABiaQmd50UWRjWGtNJ4xDDnVhcKiuozWmnXXlOWjDDWpmb++7h9u78AA=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To:Message-ID: Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type: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=s4EjK/cCFqkYa2P+IY5Mz9OOPwdEZWL1A/yK/eYA2gc=; b=Ml2kYHHTXtSrfuJDAHuEkuK7NE ihfSTCXHNmF66jxN3X8jahTIt4gkweD+VXSogMSbBqYm6g3D2GY9sXD8DOxg4UNnzut2FdraKAtoN +KDi8SMjoXzlPqjM7GzQCwJWwgR+Dp3txMEB8vCAwZtBgkKnmrl0C6m+jWJTrBpB08IE=; Received: from [193.149.48.129] (helo=blue.greenie.muc.de) by sfi-mx-1.v28.lw.sourceforge.com with esmtps (TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.95) id 1xA5kv-00060r-7T for openvpn-devel@lists.sourceforge.net; Fri, 25 Sep 2026 13:14:28 +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 68PDEHte010554 for ; Fri, 25 Sep 2026 15:14:17 +0200 Received: (from gert@localhost) by blue.greenie.muc.de (8.18.2/8.18.1/Submit) id 68PDEH67010553 for openvpn-devel@lists.sourceforge.net; Fri, 25 Sep 2026 15:14:17 +0200 From: Gert Doering To: openvpn-devel@lists.sourceforge.net Date: Fri, 25 Sep 2026 15:14:11 +0200 Message-ID: <20260925131417.10540-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: Antonio Quartulli ovpn-dco shared a single netlink socket for both synchronous request/reply transactions (peer create/delete/get, key operations, stats) and the asynchronous multicast notification group (peer del/floa [...] 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: 1xA5kv-00060r-7T Subject: [Openvpn-devel] [PATCH v2] dco_linux: read multicast notifications on a dedicated netlink socket 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: 1877282724267696110 X-GMAIL-MSGID: 1877309731394878305 From: Antonio Quartulli ovpn-dco shared a single netlink socket for both synchronous request/reply transactions (peer create/delete/get, key operations, stats) and the asynchronous multicast notification group (peer del/float, key swap). Because every ovpn_nl_msg_send() drains its reply with nl_recvmsgs() on that same socket, a notification queued by the kernel could be dispatched re-entrantly in the middle of an unrelated request/reply: the NL_CB_VALID handler runs ovpn_handle_msg() -> multi_process_incoming_dco() -> multi_close_instance() while the caller is still walking an instance collection. For example multi_print_status() refreshes stats with dco_get_peer_stats_multi() and then iterates m->hash, and multi_delete_dup() iterates m->instances[] while closing duplicates; an instance freed underneath either iterator becomes a use-after-free and the server crashes (observed under mass simultaneous client reconnects). Subscribe the multicast group on a dedicated socket (nl_sock_notify) and read it only from dco_read_and_process(), the top-level event-loop point where closing an instance is safe. The request/reply socket is no longer a member of the group, so parsing a command or stats reply can never dispatch a notification. The event loop now monitors the notification socket; the request/reply socket keeps being drained synchronously by ovpn_nl_msg_send(). Change-Id: Ia7894fc360b12c5a0f63fad2abb9bbb1ad9c2bd5 GitHub: closes OpenVPN/openvpn#1067 Signed-off-by: Antonio Quartulli Acked-by: Razvan Cojocaru Gerrit URL: https://gerrit.openvpn.net/c/openvpn/+/1737 --- 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/+/1737 This mail reflects revision 2 of this Change. Acked-by according to Gerrit (reflected above): Razvan Cojocaru diff --git a/src/openvpn/dco_linux.c b/src/openvpn/dco_linux.c index 56f6259..27f4f99 100644 --- a/src/openvpn/dco_linux.c +++ b/src/openvpn/dco_linux.c @@ -134,12 +134,8 @@ } static int -ovpn_nl_recvmsgs(dco_context_t *dco, const char *prefix) +ovpn_nl_recvmsgs_report(int ret, const char *prefix) { - __is_locked = true; - int ret = nl_recvmsgs(dco->nl_sock, dco->nl_cb); - __is_locked = false; - switch (ret) { case -NLE_INTR: @@ -174,6 +170,16 @@ return ret; } +static int +ovpn_nl_recvmsgs(dco_context_t *dco, const char *prefix) +{ + __is_locked = true; + int ret = nl_recvmsgs(dco->nl_sock, dco->nl_cb); + __is_locked = false; + + return ovpn_nl_recvmsgs_report(ret, prefix); +} + /** * Send a prepared netlink message. * @@ -399,9 +405,11 @@ } /* Register for ovpn-dco specific multicast messages that the kernel may - * send + * send. These are subscribed on the dedicated notification socket only, so + * that they are never delivered while a request/reply transaction on + * dco->nl_sock is being parsed. */ - int ret = nl_socket_add_membership(dco->nl_sock, dco->ovpn_dco_mcast_id); + int ret = nl_socket_add_membership(dco->nl_sock_notify, dco->ovpn_dco_mcast_id); if (ret) { msg(M_FATAL, "%s: failed to join groups: %d", __func__, ret); @@ -450,17 +458,49 @@ nl_cb_set(dco->nl_cb, NL_CB_ACK, NL_CB_CUSTOM, ovpn_nl_cb_finish, &dco->status); nl_cb_set(dco->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, ovpn_handle_msg, dco); + /* Set up the dedicated multicast notification socket. It shares the message + * handler (ovpn_handle_msg) with the request/reply socket, but is read only + * from dco_read_and_process() in the event loop, so notifications never + * fire while a request/reply transaction is being parsed. */ + dco->nl_sock_notify = nl_socket_alloc(); + if (!dco->nl_sock_notify) + { + msg(M_FATAL, "Cannot create netlink notification socket"); + } + + ret = genl_connect(dco->nl_sock_notify); + if (ret) + { + msg(M_FATAL, "Cannot connect to generic netlink (notify): %s", nl_geterror(ret)); + } + + set_cloexec(nl_socket_get_fd(dco->nl_sock_notify)); + set_nonblock(nl_socket_get_fd(dco->nl_sock_notify)); + + dco->nl_cb_notify = nl_cb_alloc(NL_CB_DEFAULT); + if (!dco->nl_cb_notify) + { + msg(M_FATAL, "failed to allocate netlink notification callback"); + } + + nl_socket_set_cb(dco->nl_sock_notify, dco->nl_cb_notify); + nl_cb_err(dco->nl_cb_notify, NL_CB_CUSTOM, ovpn_nl_cb_error, &dco->status); + nl_cb_set(dco->nl_cb_notify, NL_CB_VALID, NL_CB_CUSTOM, ovpn_handle_msg, dco); + ovpn_dco_register(dco); /* The async PACKET messages confuse libnl and it will drop them with * wrong sequence numbers (NLE_SEQ_MISMATCH), so disable libnl's sequence - * number check */ + * number check. Multicast notifications are unsolicited and likewise carry + * no matching sequence number, so disable the check on both sockets. */ nl_socket_disable_seq_check(dco->nl_sock); + nl_socket_disable_seq_check(dco->nl_sock_notify); /* nl library sets the buffer size to 32k/32k by default which is sometimes * overrun with very fast connecting/disconnecting clients. * TODO: fix this in a better and more reliable way */ ASSERT(!nl_socket_set_buffer_size(dco->nl_sock, 1024 * 1024, 1024 * 1024)); + ASSERT(!nl_socket_set_buffer_size(dco->nl_sock_notify, 1024 * 1024, 1024 * 1024)); } bool @@ -496,8 +536,12 @@ nl_socket_free(dco->nl_sock); dco->nl_sock = NULL; + nl_socket_free(dco->nl_sock_notify); + dco->nl_sock_notify = NULL; + /* Decrease reference count */ nl_cb_put(dco->nl_cb); + nl_cb_put(dco->nl_cb_notify); CLEAR(dco); } @@ -1165,7 +1209,13 @@ { msg(D_DCO_DEBUG, __func__); - return ovpn_nl_recvmsgs(dco, __func__); + /* Drain the multicast notification socket. This is the only place where + * asynchronous notifications (peer del/float, key swap) are processed, so + * the multi_close_instance() they may trigger happens at a safe point in + * the event loop rather than re-entrantly inside a request/reply. */ + int ret = nl_recvmsgs(dco->nl_sock_notify, dco->nl_cb_notify); + + return ovpn_nl_recvmsgs_report(ret, __func__); } static int @@ -1327,9 +1377,12 @@ void dco_event_set(dco_context_t *dco, struct event_set *es, void *arg) { - if (dco && dco->nl_sock) + if (dco && dco->nl_sock_notify) { - event_ctl(es, nl_socket_get_fd(dco->nl_sock), EVENT_READ, arg); + /* Only the notification socket is monitored by the event loop. The + * request/reply socket (dco->nl_sock) is drained synchronously by + * ovpn_nl_msg_send(). */ + event_ctl(es, nl_socket_get_fd(dco->nl_sock_notify), EVENT_READ, arg); } } diff --git a/src/openvpn/dco_linux.h b/src/openvpn/dco_linux.h index e3e4824..c487581 100644 --- a/src/openvpn/dco_linux.h +++ b/src/openvpn/dco_linux.h @@ -62,8 +62,18 @@ typedef struct { + /* socket used for synchronous request/reply transactions (commands and + * stats queries). It is NOT subscribed to the multicast group, so its + * recvmsgs never dispatch an asynchronous notification. */ struct nl_sock *nl_sock; struct nl_cb *nl_cb; + /* socket dedicated to multicast notifications (peer del/float, key swap). + * It is read only at the top-level dco_read_and_process() point, where it + * is safe to close instances. Keeping it separate from nl_sock prevents + * notifications from being processed re-entrantly while a request/reply is + * in flight. */ + struct nl_sock *nl_sock_notify; + struct nl_cb *nl_cb_notify; int status; struct context *c;