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