Skip to content

std: fix stack buffer overflow in Windows junction_point - #158147

Merged
rust-bors[bot] merged 1 commit into
rust-lang:mainfrom
devnexen:windows_fs_oflow_fix
Aug 5, 2026
Merged

std: fix stack buffer overflow in Windows junction_point#158147
rust-bors[bot] merged 1 commit into
rust-lang:mainfrom
devnexen:windows_fs_oflow_fix

Conversation

@devnexen

@devnexen devnexen commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

View all comments

The guard checked data_len > u16::MAX, allowing paths far larger than PathBuffer (a fixed 16384-element array), which the subsequent single copy_from then overflows. Bound against MAXIMUM_REPARSE_DATA_BUFFER_SIZE plus header instead, matching the kernel's limit.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jun 19, 2026
@rustbot

rustbot commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

r? @Darksonn

rustbot has assigned @Darksonn.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: @ChrisDenton, libs
  • @ChrisDenton, libs expanded to 12 candidates
  • Random selection from Darksonn, Mark-Simulacrum, clarfonthey, jhpratt

@Darksonn Darksonn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Comment thread library/std/src/sys/fs/windows.rs Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It sounds like this should be changed to ptr[..abs_path.len()].copy_from_slice(abs_path) or similar so that we actually perform a bounds check here.

Also, it would be really nice with a test that'd catch this (may be easier to check that the test is failing if we insert a bounds check first).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In fact, maybe the bounds check should be entirely rewritten to

ptr.get(..abs_path.len())
    .ok_or_else(|| io::const_error!(io::ErrorKind::InvalidInput, "`original` path is too long"))?
    .copy_from_slice(abs_path)

This way there's no risk that the two bounds checks are not kept in sync. For instance, why is there a + 8 in the previous check? I don't understand that part.

Comment thread library/std/src/sys/fs/windows.rs Outdated
Comment on lines 1679 to 1683
PathBuffer: [MaybeUninit<u16>; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize],
}
let data_len = 12 + (abs_path.len() * 2);
if data_len > u16::MAX as usize {
if data_len + 8 > c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize {
return Err(io::const_error!(io::ErrorKind::InvalidInput, "`original` path is too long"));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

data_len counts the number of bytes, but MAXIMUM_REPARSE_DATA_BUFFER_SIZE counts the number of u16s. This doesn't sound right.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 19, 2026
@rustbot

rustbot commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@rust-log-analyzer

This comment has been minimized.

@devnexen

Copy link
Copy Markdown
Contributor Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jun 19, 2026

@Darksonn Darksonn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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


#[test]
#[cfg(windows)]
fn junction_point_overlong_path() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd like to see this regression test fail without the change. Do you mind opening a second temporary PR containing just the test so that we can run it through CI? We can close it again when we've confirmed the regression test catches the bug.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Confirmed failure in #158201.

Comment thread library/std/src/sys/fs/windows.rs Outdated
Comment on lines +1694 to +1700
PathBuffer: [MaybeUninit::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize],
PathBuffer: [MaybeUninit::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize / 2],
};
// The path is followed by its null terminator and the (empty) print name's
// null terminator, so the buffer must hold two extra `u16`s. This single
// bounds check keeps the buffer, the copy, and `data_len` below in sync; if
// the path doesn't fit, fail rather than overflow the buffer.
let Some(ptr) = header.PathBuffer.get_mut(..abs_path.len() + 2) else {
return Err(io::const_error!(io::ErrorKind::InvalidInput, "`original` path is too long"));
};
ptr[..abs_path.len()].write_copy_of_slice(&abs_path);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This buffer is uninitialized. Don't you need to set path[abs_path.len()] and path[abs_path.len()+1] to zero?

@ChrisDenton ChrisDenton Jun 19, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The path has an explicit length so it doesn't need to be null terminated at all. I'm uncertain where that idea came from but it's probably just a misunderstanding? I mean, I guess there's no harm in writing a null but if so it shouldn't be part of the length.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 19, 2026
@ChrisDenton

Copy link
Copy Markdown
Member

To give some context here, this was originally a hacky function used only in tests. It's currently publicly exposed as a nightly-only API but it's not considered ready for stabilisation. E.g. 16kb is way too much for a stack buffer (though admittedly using a stack buffer for shorter paths would be useful).

PathBuffer: [MaybeUninit<u16>; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize],
// `MAXIMUM_REPARSE_DATA_BUFFER_SIZE` is a size in bytes; halve it for a
// count of `u16`s (the `readlink` path uses it as a byte buffer).
PathBuffer: [MaybeUninit<u16>; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize / 2],

@ChrisDenton ChrisDenton Jun 19, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This doesn't sound right at all. What makes you say MAXIMUM_REPARSE_DATA_BUFFER_SIZE is in bytes?

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Huh, apparently it is.

The protocol docs make no mention of a maximum size, only that it's a 16 bit unsigned integer (hence my surprise at 16kb being a limit). But the public headers for the kernel API do have MAXIMUM_REPARSE_DATA_BUFFER_SIZE in bytes of 16kb.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should it be changed to an u8 array as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I would say no. It's the mount-point WCHAR path, and u16 keeps the copy a clean write_copy_of_slice(&abs_path).

@devnexen
devnexen force-pushed the windows_fs_oflow_fix branch from d842451 to 28748e5 Compare June 21, 2026 06:05
rust-bors Bot pushed a commit that referenced this pull request Jun 21, 2026
Temporary CI-verification test for #158147. Without the fix,
a >16 KiB junction target passes the old `> u16::MAX` length check yet
overflows the inline reparse stack buffer. This test must fail on master
and pass once the fix lands.
@Darksonn Darksonn added O-windows Operating system: Windows A-filesystem Area: `std::fs` labels Jun 21, 2026
@devnexen

Copy link
Copy Markdown
Contributor Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jun 21, 2026

@Darksonn Darksonn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Comment thread library/std/src/sys/fs/windows.rs Outdated
// Size of the reparse data after the 8-byte ReparseTag/ReparseDataLength/
// Reserved header: the four name offset/length `u16` fields (8 bytes) plus
// the path.
let data_len = 8 + abs_path.len() * 2;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Isn't it 16 bytes?

  • ReparseTag is 4 bytes
  • ReparseDataLength is 2 bytes
  • Reserved is 2 bytes
  • SubstituteNameOffset is 2 bytes
  • SubstituteNameLength is 2 bytes
  • PrintNameOffset is 2 bytes
  • PrintNameLength is 2 bytes

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Right, it's 16 bytes. Switched to offset_of! so both lengths derive from the struct.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 22, 2026
@devnexen
devnexen force-pushed the windows_fs_oflow_fix branch from 28748e5 to b7af0d1 Compare June 22, 2026 23:24
@devnexen

Copy link
Copy Markdown
Contributor Author

@rustbot ready

rust-bors Bot pushed a commit that referenced this pull request Jul 1, 2026
…uwer

Rollup of 6 pull requests

Successful merges:

 - #156737 (Implement `DoubleEndedIterator::next_chunk_back`)
 - #158147 (std: fix stack buffer overflow in Windows junction_point)
 - #158180 (std: use `OnceLock` for SGX environment variable storage)
 - #158427 (Implement `ptr::{read,write}_unaligned` via `repr(packed)`)
 - #158531 (Change `adjust_ident_and_get_scope` arg to `LocalDefId`)
 - #158364 (rustc_target/asm: add LoongArch LSX/LASX inline asm register support)
@jhpratt

jhpratt commented Jul 1, 2026

Copy link
Copy Markdown
Member

@bors r-

#158670 (comment)

@rust-bors rust-bors Bot added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Jul 1, 2026
@rust-bors

rust-bors Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

This pull request was unapproved.

View changes since this unapproval

@rust-bors rust-bors Bot removed the S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. label Jul 1, 2026
@Darksonn

Copy link
Copy Markdown
Member

@devnexen Any update on this?

@devnexen
devnexen force-pushed the windows_fs_oflow_fix branch from b7af0d1 to 74afb0a Compare July 25, 2026 15:16
@rustbot

This comment has been minimized.

The guard checked `data_len > u16::MAX`, allowing paths far larger than
`PathBuffer` (a fixed 16384-element array), which the subsequent single
`copy_from` then overflows. Bound against `MAXIMUM_REPARSE_DATA_BUFFER_SIZE`
plus header instead, matching the kernel's limit.
@devnexen
devnexen force-pushed the windows_fs_oflow_fix branch from 74afb0a to 3313cd6 Compare July 25, 2026 15:32
@rustbot

rustbot commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@Darksonn

Darksonn commented Aug 5, 2026

Copy link
Copy Markdown
Member

Please remember to use @rustbot ready when this is ready for review.

@bors try

@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Aug 5, 2026
std: fix stack buffer overflow in Windows junction_point
@rust-bors

rust-bors Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 409d767 (409d767a80e64b1f76845dfd4cd80eaa8eb67050)
Base parent: 7218ebe (7218ebe93668f51a94a572b690c433dfdbdc2c3d)

@Darksonn Darksonn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@rust-bors

rust-bors Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📌 Commit 3313cd6 has been approved by Darksonn

It is now in the queue for this repository.

🌲 The tree is currently closed for pull requests below priority 10. This pull request will be tested once the tree is reopened.

Reason for tree closure: manually handling queue due to backlog

@rust-bors rust-bors Bot added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Aug 5, 2026
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Aug 5, 2026
…arksonn

std: fix stack buffer overflow in Windows junction_point

The guard checked `data_len > u16::MAX`, allowing paths far larger than `PathBuffer` (a fixed 16384-element array), which the subsequent single `copy_from` then overflows. Bound against `MAXIMUM_REPARSE_DATA_BUFFER_SIZE` plus header instead, matching the kernel's limit.
rust-bors Bot pushed a commit that referenced this pull request Aug 5, 2026
…uwer

Rollup of 22 pull requests

Successful merges:

 - #158147 (std: fix stack buffer overflow in Windows junction_point)
 - #159784 (Hint that memchr returns an in-bounds index)
 - #160130 (Select cache values to verify by key fingerprint, not value fingerprint)
 - #160343 (Rename `OutlivesPredicate` to `OutlivesClause`)
 - #160360 (Remove rustc_middle dependency on rustc_hir_pretty)
 - #160387 (rustc_codegen_ssa: Correctly apply the static `--jobs-backend` limit to backend parallelism)
 - #160422 (move mir-opt miri tests to CI logic)
 - #160444 (Avoid resolving path keywords outside `TypeNS`)
 - #160510 (Resolver: (un)tracked borrows for `CmRefCell` made safe my unsafe speculative flag)
 - #155424 ([blocked] Link to proposed LLM policy in CONTRIBUTING and pull request template)
 - #158726 (std: move futex implementations into sys::sync::futex)
 - #159225 (Split IncrCompSession out of Session)
 - #159820 (Make the `rustc_unsafe_specialization_marker` attribute actually `unsafe`)
 - #160198 (Rework `smallest_range_containing` to handle duplicates)
 - #160362 (Split `SpannedTypeVisitor` into its own crate, `rustc_ty_walk`)
 - #160390 (autodiff: Handle slice-tailed DSTs in type trees)
 - #160420 (Suggest `cast_signed()` for overflowing signed integer literals)
 - #160520 (Add some tests for specialization)
 - #160522 (fix(bootstrap): Normalize the names of proc macro dependency crates)
 - #160531 (docs: fix typo in AllowExprMetavar comment)
 - #160538 (Update expect messages in tcp.rs doc examples to follow the style guide)
 - #160548 (bootstrap: Register `coverage-map` and `coverage-run` aliases via a separate step)
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Aug 5, 2026
…arksonn

std: fix stack buffer overflow in Windows junction_point

The guard checked `data_len > u16::MAX`, allowing paths far larger than `PathBuffer` (a fixed 16384-element array), which the subsequent single `copy_from` then overflows. Bound against `MAXIMUM_REPARSE_DATA_BUFFER_SIZE` plus header instead, matching the kernel's limit.
rust-bors Bot pushed a commit that referenced this pull request Aug 5, 2026
…uwer

Rollup of 25 pull requests

Successful merges:

 - #154585 (treat no_mangle_generic_items as hard error instead of lint warning)
 - #158147 (std: fix stack buffer overflow in Windows junction_point)
 - #160130 (Select cache values to verify by key fingerprint, not value fingerprint)
 - #160343 (Rename `OutlivesPredicate` to `OutlivesClause`)
 - #160360 (Remove rustc_middle dependency on rustc_hir_pretty)
 - #160387 (rustc_codegen_ssa: Correctly apply the static `--jobs-backend` limit to backend parallelism)
 - #160422 (move mir-opt miri tests to CI logic)
 - #160444 (Avoid resolving path keywords outside `TypeNS`)
 - #160510 (Resolver: (un)tracked borrows for `CmRefCell` made safe my unsafe speculative flag)
 - #155424 ([blocked] Link to proposed LLM policy in CONTRIBUTING and pull request template)
 - #158726 (std: move futex implementations into sys::sync::futex)
 - #158762 (Emit thumb code on VEX V5)
 - #159225 (Split IncrCompSession out of Session)
 - #159820 (Make the `rustc_unsafe_specialization_marker` attribute actually `unsafe`)
 - #160198 (Rework `smallest_range_containing` to handle duplicates)
 - #160362 (Split `SpannedTypeVisitor` into its own crate, `rustc_ty_walk`)
 - #160390 (autodiff: Handle slice-tailed DSTs in type trees)
 - #160420 (Suggest `cast_signed()` for overflowing signed integer literals)
 - #160516 (Add regression test for HRTB projection in closure)
 - #160520 (Add some tests for specialization)
 - #160522 (fix(bootstrap): Normalize the names of proc macro dependency crates)
 - #160523 (Add regression test for opaque type)
 - #160531 (docs: fix typo in AllowExprMetavar comment)
 - #160538 (Update expect messages in tcp.rs doc examples to follow the style guide)
 - #160548 (bootstrap: Register `coverage-map` and `coverage-run` aliases via a separate step)
rust-bors Bot pushed a commit that referenced this pull request Aug 5, 2026
…uwer

Rollup of 25 pull requests

Successful merges:

 - #158147 (std: fix stack buffer overflow in Windows junction_point)
 - #160130 (Select cache values to verify by key fingerprint, not value fingerprint)
 - #160343 (Rename `OutlivesPredicate` to `OutlivesClause`)
 - #160360 (Remove rustc_middle dependency on rustc_hir_pretty)
 - #160387 (rustc_codegen_ssa: Correctly apply the static `--jobs-backend` limit to backend parallelism)
 - #160422 (move mir-opt miri tests to CI logic)
 - #160444 (Avoid resolving path keywords outside `TypeNS`)
 - #160510 (Resolver: (un)tracked borrows for `CmRefCell` made safe by unsafe speculative flag)
 - #155424 ([blocked] Link to proposed LLM policy in CONTRIBUTING and pull request template)
 - #158726 (std: move futex implementations into sys::sync::futex)
 - #158762 (Emit thumb code on VEX V5)
 - #159225 (Split IncrCompSession out of Session)
 - #159820 (Make the `rustc_unsafe_specialization_marker` attribute actually `unsafe`)
 - #160198 (Rework `smallest_range_containing` to handle duplicates)
 - #160362 (Split `SpannedTypeVisitor` into its own crate, `rustc_ty_walk`)
 - #160390 (autodiff: Handle slice-tailed DSTs in type trees)
 - #160420 (Suggest `cast_signed()` for overflowing signed integer literals)
 - #160501 (Add bootstrap CLI snapshot test for testing miri)
 - #160516 (Add regression test for HRTB projection in closure)
 - #160520 (Add some tests for specialization)
 - #160522 (fix(bootstrap): Normalize the names of proc macro dependency crates)
 - #160523 (Add regression test for opaque type)
 - #160531 (docs: fix typo in AllowExprMetavar comment)
 - #160538 (Update expect messages in tcp.rs doc examples to follow the style guide)
 - #160548 (bootstrap: Register `coverage-map` and `coverage-run` aliases via a separate step)
@rust-bors
rust-bors Bot merged commit ebab4cf into rust-lang:main Aug 5, 2026
14 checks passed
rust-timer added a commit that referenced this pull request Aug 5, 2026
Rollup merge of #158147 - devnexen:windows_fs_oflow_fix, r=Darksonn

std: fix stack buffer overflow in Windows junction_point

The guard checked `data_len > u16::MAX`, allowing paths far larger than `PathBuffer` (a fixed 16384-element array), which the subsequent single `copy_from` then overflows. Bound against `MAXIMUM_REPARSE_DATA_BUFFER_SIZE` plus header instead, matching the kernel's limit.
@rustbot rustbot added this to the 1.99.0 milestone Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-filesystem Area: `std::fs` O-windows Operating system: Windows S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants