Skip to content

fix(dua-core): avoid reading past HEADER_SIZE in Windows ReadDir::entry_at - #416

Merged
Byron merged 1 commit into
Byron:mainfrom
kiranani:fix-windows-entry-at-oob-read
Oct 24, 2026
Merged

Byron merged 1 commit into
Byron:mainfrom
kiranani:fix-windows-entry-at-oob-read

Conversation

@kiranani

Copy link
Copy Markdown
Contributor

Summary

In windows_sys, FILE_ID_BOTH_DIR_INFO has a 104-byte fixed header (HEADER_SIZE = std::mem::offset_of!(FILE_ID_BOTH_DIR_INFO, FileName) == 104) followed by pub FileName: [u16; 1] (2 bytes) and 6 bytes of trailing alignment padding (align_of::<FILE_ID_BOTH_DIR_INFO>() == 8, so size_of::<FILE_ID_BOTH_DIR_INFO>() == 112).

In ReadDir::entry_at:

  1. The bounds check offset > buffer_len.saturating_sub(HEADER_SIZE) verified that 104 bytes were available starting at offset, and then called info_ptr.read_unaligned(), which reads size_of::<FILE_ID_BOTH_DIR_INFO>() (112 bytes). When a valid directory entry with a 1–3 UTF-16 code unit filename (106, 108, or 110 bytes total) sits at the end of self.buffer, read_unaligned() reads up to 8 bytes past the end of the Vec<u64> allocation.
  2. (&raw const (*info_ptr).FileName).cast::<u16>() projected through *const FILE_ID_BOTH_DIR_INFO whose 112-byte struct extent could exceed self.buffer.
  3. offset > buffer_len.saturating_sub(HEADER_SIZE) saturated to 0 when buffer_len < HEADER_SIZE, allowing offset == 0 on a buffer smaller than HEADER_SIZE.

Fix

  • Copy only HEADER_SIZE (104) bytes into FILE_ID_BOTH_DIR_INFO::default() using std::ptr::copy_nonoverlapping instead of reading 112 bytes with info_ptr.read_unaligned(). Unlike widening the bounds check to size_of::<FILE_ID_BOTH_DIR_INFO>() (112 bytes), this still accepts valid 106–110 byte entries (1–3 character filenames) at the end of the buffer without reading past HEADER_SIZE.
  • Compute name_ptr directly via self.buffer.as_ptr().byte_add(offset + HEADER_SIZE).cast::<u16>().
  • Validate header bounds with checked_add(HEADER_SIZE) instead of saturating_sub(HEADER_SIZE).
  • Add a unit test covering a 106-byte entry (1-character filename) placed at the end of the buffer as well as a buffer smaller than HEADER_SIZE.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The focused changes address the reported memory-access errors, cover the boundary cases, and have no identified blocking issues.

No open issues 🎉

What changed in this PR

This PR fixes out-of-bounds reads in the Windows directory-entry parser while preserving support for short filenames at the end of the buffer.

Changes:

  • Copies only the fixed header and computes the filename pointer directly from the buffer.
  • Uses checked arithmetic to validate current and next-entry header bounds.
  • Adds regression coverage for a short filename at the buffer boundary and an undersized buffer.
File Description
crates/​dua-lib/​src/​windows.rs Bounds directory-record reads and adds boundary-case tests.

Review effort: Balanced

@Byron Byron left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! unsafe is hard.

…ry_at

`FILE_ID_BOTH_DIR_INFO` has a 104-byte fixed header (`HEADER_SIZE`) followed by `FileName: [u16; 1]` (2 bytes) and 6 bytes of trailing alignment padding (`size_of::<FILE_ID_BOTH_DIR_INFO>() == 112`).

`ReadDir::entry_at` validated `offset <= buffer_len.saturating_sub(HEADER_SIZE)` and then called `info_ptr.read_unaligned()`, which reads 112 bytes and can read up to 8 bytes past the end of `self.buffer` when a valid entry with a 1-3 UTF-16 code unit filename (106-110 bytes) sits at the end of the buffer.

Copy only `HEADER_SIZE` bytes into `FILE_ID_BOTH_DIR_INFO::default()` via `std::ptr::copy_nonoverlapping`, compute `name_ptr` directly from `offset + HEADER_SIZE`, and use `checked_add(HEADER_SIZE)` instead of `saturating_sub(HEADER_SIZE)` so buffers smaller than `HEADER_SIZE` are also rejected.
@Byron
Byron force-pushed the fix-windows-entry-at-oob-read branch from c0db977 to b5ef287 Compare October 24, 2026 03:21
@Byron
Byron merged commit 48c91c7 into Byron:main Oct 24, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants