tipc: fix slab-use-after-free Read in tipc_aead_decrypt_done

JIRA: https://redhat.atlassian.net/browse/RHEL-228882
CVE: CVE-2026-63801

commit bda3348872a2ef0d19f2df6aa8cb5025adce2f20
Author: Doruk Tan Ozturk <doruk@0sec.ai>
Date:   Wed Jun 17 09:58:18 2026 +0200

    tipc: fix slab-use-after-free Read in tipc_aead_decrypt_done

    tipc_aead_decrypt() goes straight from tipc_bearer_hold(b) to
    crypto_aead_decrypt(req) without taking a reference on the netns, unlike
    the encrypt path. When crypto_aead_decrypt() is offloaded asynchronously
    (e.g. the SIMD aead wrapper queuing to cryptd), the cryptd worker runs
    tipc_aead_decrypt_done() later. If the bearer's netns is torn down in the
    meantime, cleanup_net() -> tipc_exit_net() -> tipc_crypto_stop() frees the
    per-netns tipc_crypto, and the completion then reads it:
    tipc_aead_decrypt_done() dereferences aead->crypto->stats and
    aead->crypto->net, and tipc_crypto_rcv_complete() dereferences
    aead->crypto->aead[] and the node table -- reading freed memory.

    Decoded KASAN splat (v7.1-rc7, CONFIG_KASAN_INLINE + TIPC + TIPC_CRYPTO):

      BUG: KASAN: slab-use-after-free in tipc_aead_decrypt_done (net/tipc/crypto.c:999)
      Read of size 8 at addr ffff8881056258a8 by task kworker/u16:2/51
      Workqueue: events_unbound
      Call Trace:
       tipc_aead_decrypt_done (net/tipc/crypto.c:999)
       process_one_work (kernel/workqueue.c:3314)
       worker_thread (kernel/workqueue.c:3397 kernel/workqueue.c:3478)
       kthread (kernel/kthread.c:436)
       ret_from_fork (arch/x86/kernel/process.c:158)
       ret_from_fork_asm (arch/x86/entry/entry_64.S:245)

      Allocated by task 169:
       __kasan_kmalloc (mm/kasan/common.c:398 mm/kasan/common.c:415)
       tipc_crypto_start (net/tipc/crypto.c:1502)
       tipc_init_net (net/tipc/core.c:72)
       ops_init (net/core/net_namespace.c:137)
       setup_net (net/core/net_namespace.c:446)
       copy_net_ns (net/core/net_namespace.c:579)
       create_new_namespaces (kernel/nsproxy.c:132)
       __x64_sys_unshare (kernel/fork.c:3316)
       do_syscall_64 (arch/x86/entry/syscall_64.c:63)
       entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)

      Freed by task 8:
       kfree (mm/slub.c:6566)
       tipc_exit_net (net/tipc/core.c:119)
       cleanup_net (net/core/net_namespace.c:704)
       process_one_work (kernel/workqueue.c:3314)
       kthread (kernel/kthread.c:436)

    This is the same class of bug that commit e279024617134 ("net/tipc: fix
    slab-use-after-free Read in tipc_aead_encrypt_done") fixed for the encrypt
    side. The encrypt path takes maybe_get_net(aead->crypto->net) before
    crypto_aead_encrypt() and drops it with put_net() on the synchronous
    return paths and in tipc_aead_encrypt_done(); the -EINPROGRESS/-EBUSY
    return keeps the reference for the async callback to release. The decrypt
    path was left without the equivalent guard.

    Mirror the encrypt-side fix on the decrypt path: take a net reference
    before crypto_aead_decrypt() (failing with -ENODEV and the matching
    bearer put if it cannot be acquired), keep it across the
    -EINPROGRESS/-EBUSY async return, and drop it with put_net() on the
    synchronous success/error return and at the end of
    tipc_aead_decrypt_done().

    Reproduced under KASAN on v7.1-rc7: a UDP bearer with a cluster key is
    flooded with crafted encrypted frames from an unknown peer (driving the
    cluster-key decrypt path) while the bearer's netns is repeatedly torn
    down. The completion must run asynchronously to outlive
    tipc_crypto_stop(); on x86 the stock aesni gcm(aes) now decrypts
    synchronously, so the async path was exercised via cryptd offload. The
    unguarded aead->crypto dereference in tipc_aead_decrypt_done() is the
    unpatched upstream path; tipc_aead_decrypt() still lacks
    maybe_get_net(aead->crypto->net), so the completion can outlive the free
    on any config where crypto_aead_decrypt() goes async.

    Found by 0sec automated security-research tooling (https://0sec.ai).

    Fixes: fc1b6d6de2 ("tipc: introduce TIPC encryption & authentication")
    Cc: stable@vger.kernel.org
    Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
    Reviewed-by: Alexander Lobakin <aleksander.lobakin@intel.com>
    Reviewed-by: Tung Nguyen <tung.quang.nguyen@est.tech>
    Reviewed-by: Simon Horman <horms@kernel.org>
    Link: https://patch.msgid.link/20260617075818.37431-1-doruk@0sec.ai
    Signed-off-by: Jakub Kicinski <kuba@kernel.org>

Signed-off-by: Xin Long <lxin@redhat.com>
This commit is contained in:
Xin Long
2026-08-13 16:34:14 -04:00
parent 8bcd228dd3
commit d5a1f5f60d
+9
View File
@@ -950,12 +950,20 @@ static int tipc_aead_decrypt(struct net *net, struct tipc_aead *aead,
goto exit; goto exit;
} }
/* Get net to avoid freed tipc_crypto when delete namespace */
if (!maybe_get_net(net)) {
tipc_bearer_put(b);
rc = -ENODEV;
goto exit;
}
/* Now, do decrypt */ /* Now, do decrypt */
rc = crypto_aead_decrypt(req); rc = crypto_aead_decrypt(req);
if (rc == -EINPROGRESS || rc == -EBUSY) if (rc == -EINPROGRESS || rc == -EBUSY)
return rc; return rc;
tipc_bearer_put(b); tipc_bearer_put(b);
put_net(net);
exit: exit:
kfree(ctx); kfree(ctx);
@@ -993,6 +1001,7 @@ static void tipc_aead_decrypt_done(struct crypto_async_request *base, int err)
} }
tipc_bearer_put(b); tipc_bearer_put(b);
put_net(net);
} }
static inline int tipc_ehdr_size(struct tipc_ehdr *ehdr) static inline int tipc_ehdr_size(struct tipc_ehdr *ehdr)