Skip to content

fix(vfs): harden getdirentries and APR filepath resolution#77

Open
MikeSaito wants to merge 2 commits into
par274:mainfrom
MikeSaito:main
Open

fix(vfs): harden getdirentries and APR filepath resolution#77
MikeSaito wants to merge 2 commits into
par274:mainfrom
MikeSaito:main

Conversation

@MikeSaito

@MikeSaito MikeSaito commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Follow-up VFS hardening applying the same guest-writes-first discipline from the time subsystem to directory enumeration and APR filepath resolution.

Getdirentries (KernelGetdirentriesCore):

  • Write the 512-byte dirent buffer first via TryWriteCompat, update basep second (when non-null) via TryWriteUInt64Compat, and advance directory.NextIndex only after both guest writes succeed.
  • Removed the early basep write at method entry that could mutate guest memory before buffer validation and advance the host cursor before a successful dirent delivery, causing permanent entry loss on MEMORY_FAULT at bufferAddress.
  • On EOF (currentIndex >= Entries.Length), write basep with the final offset and return 0 without mutating NextIndex, matching FreeBSD getdirentries(2) semantics. For KernelGetdents, basePointerAddress is 0, so only the buffer write and host cursor advance run.

APR (sceKernelAprResolveFilepathsToIdsAndFileSizes):

  • Stopped writing ids and sizes into guest memory inside the loop; path resolution and file size lookup fill host-side local buffers first (stackalloc for small counts, heap arrays when count exceeds the thresholds). On EFAULT or NOT_FOUND, guest arrays are untouched and AmprFileRegistry is not updated.
  • Pack localSizes and localIds into contiguous byte buffers and write each output array with one TryWriteCompat call instead of per-element TryWriteUInt32Compat / TryWriteUInt64Compat pairs.
  • AmprFileRegistry.Register runs only after guest writes succeed. AmprFileRegistry.ComputeFileId is internal so ids can be computed during the resolve loop without premature registry mutation. Removed the uint.MaxValue placeholder pre-write.

Out of scope: NetCtl connected-state stubs; writable range validation pre-probes for split outputs.

Files: KernelMemoryCompatExports.cs, AmprFileRegistry.cs

@MikeSaito MikeSaito changed the title fix(vfs): defer host cursor commit in getdirentries fix(vfs): defer host cursor commit in getdirentries, resolve-first bulk commit in APR filepath resolution Jul 12, 2026
@MikeSaito MikeSaito changed the title fix(vfs): defer host cursor commit in getdirentries, resolve-first bulk commit in APR filepath resolution fix(vfs): harden getdirentries and APR filepath resolution Jul 12, 2026
Follow-up VFS hardening task applying the same guest-writes-first discipline established in the time subsystem to directory enumeration.

Deferred Commit in KernelGetdirentriesCore:

- Reordered guest output so the 512-byte dirent buffer is written first via TryWriteCompat, basep is updated second (when non-null) via TryWriteUInt64Compat, and directory.NextIndex is advanced only after both guest writes succeed.

- Removed the early basep write at method entry that could mutate guest memory before buffer validation and advance the host cursor before a successful dirent delivery, causing permanent entry loss on MEMORY_FAULT at bufferAddress.

- EOF handling: when currentIndex >= Entries.Length, write basep with the final offset and return 0 without mutating NextIndex, matching FreeBSD getdirentries(2) semantics and preventing infinite retry loops.

KernelGetdents path: basePointerAddress is passed as 0, so the transaction collapses to buffer write then host cursor advance with no basep side effect.

Out of scope: coalesced {id, size} writes in sceKernelAprResolveFilepathsToIdsAndFileSizes; NetCtl connected-state stubs.

Files: KernelMemoryCompatExports.cs
Refactored sceKernelAprResolveFilepathsToIdsAndFileSizes to stop writing ids and sizes into guest memory one element at a time.

- Removed the uint.MaxValue placeholder write at the start of each loop iteration.

- Path resolution and file size lookup now fill host-side buffers first; on EFAULT or NOT_FOUND the guest ids/sizes arrays are left untouched.

- ids and sizes are packed into contiguous byte buffers and written with one TryWriteCompat call per output array instead of separate TryWriteUInt32Compat / TryWriteUInt64Compat per index.

- AmprFileRegistry.Register is called only after guest writes succeed.

- AmprFileRegistry.ComputeFileId is internal so ids can be computed without registering paths during the resolve loop.

Files: KernelMemoryCompatExports.cs, AmprFileRegistry.cs
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.

1 participant