[BUG] crypto: rfc4309 default authsize mismatch causes unprivileged AF_ALG panic

From: Seohyeon Maeng

Date: Sat Jul 18 2026 - 14:01:35 EST


Hello,

I found an output-length contract mismatch in the RFC4309 AEAD template.
An unprivileged user can reach it through AF_ALG and panic the kernel.

Summary
-------

crypto_rfc4309_init_tfm() spawns its child AEAD but does not synchronize the
child's initial authsize with the RFC4309 parent's initial authsize.

The generic AEAD initialization code initializes each transform's authsize to
that algorithm's own maxauthsize. RFC4309 hardcodes maxauthsize to 16, while
the template accepts any child AEAD with IV size 16 and block size 1. The
parent and child can therefore start with different authsizes until an
explicit crypto_aead_setauthsize() call is made.

AF_ALG does not require ALG_SET_AEAD_AUTHSIZE, so the inconsistent default
state is reachable without capabilities or user namespaces.

I confirmed two directions of the mismatch:

1. Child authsize larger than the parent

rfc4309(authenc(hmac(sha256),ctr(aes)))

- RFC4309 parent authsize: 16
- AUTHENC child authsize: 32

During encryption, AF_ALG creates the receive scatterlist using the
parent's 16-byte expansion, but AUTHENC appends a 32-byte tag. The first
16 tag bytes fit and the scatterlist walker then dereferences the NULL
entry after the marked end of the receive scatterlist.

2. Child authsize smaller than the parent

rfc4309(authenc(michael_mic,ctr(aes)))

- RFC4309 parent authsize: 16
- AUTHENC child authsize: 8

During decryption, the parent computes the plaintext length by subtracting
16, while the child subtracts only 8. With a valid child authentication
tag, the child attempts to decrypt eight bytes more than the destination
scatterlist represents. The CTR slow path copies the represented bytes
and then dereferences the NULL entry after the destination SG end.

Affected code
-------------

crypto/ccm.c:

static int crypto_rfc4309_init_tfm(struct crypto_aead *tfm)
{
...
aead = crypto_spawn_aead(spawn);
if (IS_ERR(aead))
return PTR_ERR(aead);

ctx->child = aead;
...
}

crypto_rfc4309_setauthsize() would synchronize the child, but it is only
called when a consumer explicitly sets an authsize.

The RFC4309 instance creation code verifies the child IV size and block size,
but does not verify that its inherited/default authsize is compatible:

if (crypto_aead_alg_ivsize(alg) != 16)
goto err_free_inst;
if (alg->base.cra_blocksize != 1)
goto err_free_inst;
...
inst->alg.maxauthsize = 16;

Reachability
------------

The reproducers drop to UID/GID 65534 before opening AF_ALG sockets.

No capability, user namespace, io_uring, network configuration, or device node
is required. The relevant configuration options are enabled in the tested
kernel:

CONFIG_CRYPTO_AUTHENC=y
CONFIG_CRYPTO_AES=y
CONFIG_CRYPTO_CTR=y
CONFIG_CRYPTO_CCM=y
CONFIG_CRYPTO_HMAC=y
CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_MICHAEL_MIC=y
CONFIG_CRYPTO_USER_API_AEAD=y

Runtime results
---------------

The small-child decryption case was reproduced on the official Linux 6.12.95
kernel image.

Control, with ALG_SET_AEAD_AUTHSIZE set to 8:

uid=65534 gid=65534 mode=control
sendmsg op=1 returned 32; recv capacity=40
recv op=1 returned 40
sendmsg op=0 returned 40; recv capacity=32
recv op=0 returned 32

Trigger, leaving the RFC4309 parent and child at their defaults:

uid=65534 gid=65534 mode=trigger
sendmsg op=1 returned 32; recv capacity=40
recv op=1 returned 40
sendmsg op=0 returned 40; recv capacity=32
BUG: kernel NULL pointer dereference, address: 0000000000000008
CPU: 1 UID: 65534 PID: 129 Comm: repro_rfc4309_s
RIP: scatterwalk_copychunks+0xd6/0x150
Call Trace:
skcipher_walk_done+0x231/0x240
crypto_ctr_crypt+0x100/0x280
aead_recvmsg+0x3d4/0x510
sock_recvmsg+0x8e/0x90
__sys_recvfrom+0xc3/0x140
Kernel panic - not syncing: Fatal exception

The large-child encryption case was also reproduced as UID/GID 65534. It
faults while AUTHENC copies the oversized tag beyond the parent-sized receive
scatterlist.

Version status
--------------

The vulnerable initialization shape was present when checked in:

- Torvalds master: 1229e2e57a5c2980ccd457b9b53ea0eed5a22ab3
- crypto maintainer master: 8173f7e2ce67e6ca1d4763f3da14e5b01ce77456
- Linux 6.12.96: 6d15a1029d425b15c59463910ebdccc4afe760d6
- tested Linux 6.12.95: 296aabce459470a4c1b68ffd0c0c0920e563aaad

The standard RFC4309 test coverage does not expose this state: testmgr covers
rfc4309(ccm(aes)), and the AEAD test path calls crypto_aead_setauthsize()
before executing each vector.

Impact
------

Confirmed impact is an unprivileged local kernel panic/denial of service on a
PANIC_ON_OOPS kernel.

I have not demonstrated an adjacent-object write or privilege-escalation
primitive. The AF_ALG receive path marks the exact parent-sized destination
SG as ended, so the tested software walkers fault at the NULL next entry
instead of continuing into adjacent memory.

Suggested fix direction
-----------------------

RFC4309 initialization should apply the parent's inherited authsize to the
newly spawned child and fail transform initialization if the child rejects
that size. Conceptually:

aead = crypto_spawn_aead(spawn);
if (IS_ERR(aead))
return PTR_ERR(aead);

err = crypto_aead_setauthsize(aead, crypto_aead_authsize(tfm));
if (err) {
crypto_free_aead(aead);
return err;
}

ctx->child = aead;

This preserves compatible children and rejects child algorithms whose output
contract cannot represent RFC4309's inherited default.

This issue was found with AI-assisted source review. Both paths and the
runtime results above were manually validated. Minimal source reproducers are
available privately on request, but are not attached to this public report.

Regards,
Seohyeon Maeng