Re: Post-SA-26:52 audit of crypto_dispatch()/crp_etype consumers in src

Luis Castellanos <[email protected]>
Newsgroups gmane.os.freebsd.security.general
Message-ID <CAJ86RmueLpXqSeBZAp+R6gr8cOP+-0b7U7y1wOKb2xyfs+aOBg@mail.gmail.com>
Following up on item 2(b) from my previous mail.

I've submitted a patch for the crp/mbuf/refcount leak on
crypto_dispatch() failure in if_ovpn.c:

    https://reviews.freebsd.org/D58754

Summary:
- On crypto_dispatch() != 0, crp was not freed in either
  ovpn_transmit_to_peer() or ovpn_udp_input()
- peer->refcount (encrypt) and sc->refcount (decrypt) were
  incremented before dispatch but never decremented on failure
- mbuf was not freed in either path
- This is a resource leak, not an authentication bypass like SA-26:52
- crp_etype handling via callback remains correct

Luis F. Castellanos

On Sun, Aug 9, 2026 at 4:02 PM Luis Castellanos
<[email protected]> wrote:
>
> Following the publication of FreeBSD-SA-26:52.if_wg (CVE-2026-58085), I
> audited all in-tree consumers of crypto_dispatch() in sys/ to determine
> whether the crp_etype read omission was isolated to wg_crypto.c or present
> in other subsystems.
>
> The files examined against the stable/14 tree are:
>
>   sys/dev/wg/wg_crypto.c
>   sys/geom/eli/g_eli_crypto.c
>   sys/geom/eli/g_eli_integrity.c
>   sys/geom/eli/g_eli_privacy.c
>   sys/net/if_ovpn.c
>   sys/netipsec/xform_ah.c
>   sys/netipsec/xform_esp.c
>   sys/netipsec/xform_ipcomp.c
>   sys/opencrypto/ktls_ocf.c
>
> Summary: the pattern from SA-26:52 (CRYPTO_F_CBIMM + reading only the
> dispatch return value without checking crp_etype) is not present in any
> other consumer. All remaining files route crp_etype through their callback
> or read it explicitly after a synchronous wait. The bug was contained.
>
> However, sys/net/if_ovpn.c exhibits a related fragility worth noting.
>
>
> 1. CONFIRMED ISOLATION OF SA-26:52 PATTERN
> -------------------------------------------
>
> The vulnerable pattern in wg_crypto.c prior to the fix was:
>
>   crp.crp_flags = CRYPTO_F_IV_SEPARATE | CRYPTO_F_CBIMM;
>   ret = crypto_dispatch(&crp);
>   crypto_destroyreq(&crp);
>   return (ret);                   /* crp_etype never read */
>
> This pattern does not appear elsewhere. All other consumers use one of:
>
>   (a) CRYPTO_F_CBIFSYNC with a registered callback that reads crp_etype
>       (xform_ah.c, xform_esp.c, xform_ipcomp.c, if_ovpn.c)
>
>   (b) CRYPTO_F_CBIFSYNC with an explicit synchronous wait on crp_opaque
>       followed by reading crp_etype (g_eli_crypto.c)
>
>   (c) Dynamic sync/async detection via CRYPTO_SESS_SYNC() with separate
>       code paths for each (ktls_ocf.c)
>
> xform_esp.c additionally distinguishes EBADMSG from transport errors at
> line 538, which is the correct behavior for an AEAD consumer.
>
>
> 2. HARDENING OBSERVATION IN sys/net/if_ovpn.c
> ----------------------------------------------
>
> if_ovpn.c does not share the SA-26:52 bug: crp_etype is correctly read
> inside ovpn_decrypt_rx_cb() and ovpn_encrypt_tx_cb(). However, two
> related issues are observable:
>
> (a) No CRYPTO_SESS_SYNC() guard at session creation.
>
>     wg_crypto.c post-fix enforces synchronous dispatch at init time:
>
>       if (!CRYPTO_SESS_SYNC(chacha20_poly1305_sid)) {
>               crypto_freesession(chacha20_poly1305_sid);
>               return (ENXIO);
>       }
>
>     if_ovpn.c creates sessions with CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE
>     (if_ovpn.c:939-940) without this guard. Under CRYPTO_F_CBIFSYNC semantics,
>     an async hardware provider will queue the callback rather than invoke it
>     immediately. The callback does read crp_etype correctly, so this is not
>     currently exploitable as an authentication bypass. However, the session is
>     not guaranteed to be synchronous, which differs from the defensive posture
>     now established in wg_crypto.c.
>
> (b) crp not freed on dispatch error in the receive path.
>
>     In the receive path (if_ovpn.c, around line 2635-2644):
>
>       if (V_async_crypto)
>               ret = crypto_dispatch_async(crp, CRYPTO_ASYNC_ORDERED);
>       else
>               ret = crypto_dispatch(crp);
>       if (ret != 0) {
>               OVPN_COUNTER_ADD(sc, lost_data_pkts_in, 1);
>               if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1);
>       }
>       return (true);
>
>     When crypto_dispatch() returns non-zero (dispatch failure, not crypto
>     failure), crp is not freed before the function returns. The encrypt
>     path (around line 2187-2196) frees crp only when ret != 0 in a
>     different branch that does not cover all error cases uniformly.
>
>     This is distinct from crp_etype handling and is a resource management
>     issue rather than an authentication bypass. Reproducibility depends on
>     whether the selected OCF provider can fail dispatch under memory
>     pressure.
>
>
> 3. METHODOLOGY NOTE
> --------------------
>
> crp_etype handling was traced per consumer through:
>   - crp_flags selection (CBIMM vs CBIFSYNC vs explicit sync wait)
>   - crypto_dispatch() / crypto_dispatch_async() call sites
>   - presence of callback registration vs inline result read
>   - session creation flags (CRYPTOCAP_F_SOFTWARE enforces sync;
>     CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE permits async providers)
>
> All observations above are directly traceable to stable/14 source as of
> commit 825c6f45b147 (stable/14-n274644).
>
> I am happy to prepare a patch for the if_ovpn crp free-on-error path if
> that would be useful.
>
> Luis F. Castellanos
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.