#812·btrfs

Buffer overflows from uint16 truncation in symlink reparse handling (get_reparse_block / get_reparse_point)

Author: xfcyhuangCreated Sep 4, 2026Updated Sep 4, 2026

Audit finding against master @ a0648190 (v1.10).

Impact: kernel pool overflow (opening a symlink) and user-buffer overflow (FSCTL_GET_REPARSE_POINT), with attacker-controlled contents; an unprivileged user only needs to create a symlink with a long enough target and then open/query it.

Both get_reparse_block (src/create.c 3523-3578, reached when opening a symlink) and get_reparse_point (src/reparse.c 60-139) do:

c
utf8_to_utf16(NULL, 0, &stringlen, (char*)*data, bytes_read);   // stringlen up to ~131070
subnamelen = printnamelen = (USHORT)stringlen;                  // truncates!
reqlen = offsetof(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + subnamelen + printnamelen;
rdb = ExAllocatePoolWithTag(PagedPool, reqlen, ALLOC_TAG);      // sized from the TRUNCATED value
...
utf8_to_utf16(&rdb->SymbolicLinkReparseBuffer.PathBuffer[...], stringlen, &stringlen, ...); // writes up to the FULL value

When the symlink target's UTF-16 form is between 65536 and ~131071 bytes (e.g. a ~64 KB all-ASCII target — note get_reparse_point's size > 0xffff check on the file size does not prevent this, since 65535 ASCII bytes expand to 131070 UTF-16 bytes), the allocation is based on 2 * (stringlen & 0xffff) while the write length is the untruncated stringlen — overflowing by up to 64 KB. get_reparse_point overflows the user-supplied output buffer the same way (its buflen < reqlen check also uses the truncated value).

Suggested fix: keep subnamelen/printnamelen as ULONG, and reject targets whose UTF-16 length exceeds 0xffff up front (REPARSE_DATA_BUFFER lengths are USHORT anyway).