Kernel pool buffer overflow when setting extended attributes (uint16 size wraparound)
Audit finding against master @ a0648190 (v1.10).
Impact: kernel pool buffer overflow with attacker-controlled contents, triggerable by an unprivileged local user (only write access to a file is needed).
drv_set_ea (src/fileinfo.c, ~line 6220) and file_create_parse_ea (src/create.c, ~line 2165) accumulate the total EA buffer size into a uint16_t:
uint16_t size = 0;
...
size += (uint16_t)offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + item->name.Length + 1 + item->value.Length;
buf = ExAllocatePoolWithTag(PagedPool, size, ALLOC_TAG);but the fill loop then copies each entry at its real length:
RtlCopyMemory(ea->EaName, item->name.Buffer, item->name.Length);
ea->EaName[item->name.Length] = 0;
RtlCopyMemory(&ea->EaName[item->name.Length + 1], item->value.Buffer, item->value.Length);EaValueLength is a USHORT, so a single entry can legally be up to 65535 bytes; two entries of ~60000 bytes each make size wrap (60000 + 60000 -> 54464). The pool block is then much smaller than what is written.
Reachable via:
ZwSetEaFile/NtSetEaFile(src/fileinfo.c) — this path first merges the file's existing on-disk EAs into the list, so a second call adding another large EA is enough;NtCreateFilewith anEaBuffer(src/create.c).
Side bug in the same code: NextEntryOffset is computed as offsetof(FILE_FULL_EA_INFORMATION, EaName[0]) + EaNameLength + EaValueLength — missing the + 1 for the NUL terminator after the name — so on disk each entry's tail collides with the next entry's header (EA data corruption).
Suggested fix: do the size accounting in 32/64-bit and reject totals above the desired cap (e.g. 0xffff); include the + 1 in NextEntryOffset.
Source: maharmstone/btrfs