Skip to content

fix(screenshot): bounds-check staging texture mapped region#25

Merged
alandtse merged 3 commits into
devfrom
fix/screenshot-staging-memcpy
May 22, 2026
Merged

fix(screenshot): bounds-check staging texture mapped region#25
alandtse merged 3 commits into
devfrom
fix/screenshot-staging-memcpy

Conversation

@alandtse
Copy link
Copy Markdown
Owner

Summary

Crash 2026-05-19 01:20:48 — EXCEPTION_ACCESS_VIOLATION inside rep movsb at ScreenshotFeature.cpp:84. The worker thread's per-row memcpy ran past the driver-mapped region of the staging texture; the source pointer hit unreadable memory at a page boundary (0x7FFA329B3DFF).

Root cause

PopulateScratchImageFromStagingTexture assumed:

  1. mapped.RowPitch * destImage->height always covered the full mapped subresource.
  2. destImage->rowPitch was always <= mapped.RowPitch.

Neither is guaranteed. Drivers can return a smaller mapped region for alignment reasons, and DirectXTex can compute a destination row pitch that exceeds the source pitch on some formats. Either case made the inner memcpy step past valid memory.

Fix

  • Reject the call early if Map() returns success with a null pData or zero RowPitch.
  • Cap row count by mapped.DepthPitch / mapped.RowPitch (falling back to the prior assumption only when DepthPitch == 0).
  • Copy the smaller of destImage->rowPitch and mapped.RowPitch per row.

No-op for the common case where the driver returns expected values.

Test plan

  • Build clean on ALL preset.
  • Capture screenshot in flat (SE/AE) at 2560-wide (crash-reproducing format).
  • Capture screenshot in VR (SBS path).
  • Verify saved BMP byte-identical to pre-fix output on a normal driver.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Warning

Rate limit exceeded

@alandtse has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 60 minutes before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cb289c67-ea87-4ff8-93c1-5f3d92730cff

📥 Commits

Reviewing files that changed from the base of the PR and between fe7195f and 4ec9de2.

📒 Files selected for processing (1)
  • src/Features/ScreenshotFeature.cpp
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/screenshot-staging-memcpy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

No actionable suggestions for changed features.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 19, 2026

✅ A pre-release build is available for this PR:
Download

@alandtse
Copy link
Copy Markdown
Owner Author

Seems functional; unlikely to get a tester so will merge as soon as ci confirmed stable.

@alandtse alandtse closed this May 20, 2026
@alandtse alandtse reopened this May 20, 2026
Copilot AI review requested due to automatic review settings May 20, 2026 08:37
@alandtse alandtse force-pushed the fix/screenshot-staging-memcpy branch 2 times, most recently from 451c7c6 to fb800fc Compare May 20, 2026 08:39
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the screenshot worker’s CPU readback path to prevent out-of-bounds reads when copying from a mapped D3D11 staging texture into a DirectXTex ScratchImage, addressing an access violation reported in ScreenshotFeature.cpp.

Changes:

  • Adds validation for Map() results (null pData / zero RowPitch) before proceeding.
  • Computes a safe row count based on mapped region sizing (DepthPitch/RowPitch) and clamps per-row copy size to the smaller of source/destination pitches.
  • Refactors the copy loop to use a cached source pointer and bounded memcpy.

Comment thread src/Features/ScreenshotFeature.cpp
Comment thread src/Features/ScreenshotFeature.cpp
alandtse added a commit that referenced this pull request May 21, 2026
Two Copilot findings on PR #25:

- ScratchImage::Initialize2D leaves the pixel buffer uninitialized.
  When the driver mapped a short region (rowsToCopy < height or
  bytesPerRow < destImage->rowPitch) the unfilled bytes encoded as
  whatever was on the heap, producing nondeterministic/corrupted
  screenshots from SaveToWICFile. Zero-fill the pixel buffer up
  front so any uncopied bytes encode as deterministic black.
- File used std::min via transitive include — make the dependency
  explicit by adding <algorithm>.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
alandtse added a commit that referenced this pull request May 21, 2026
Two Copilot findings on PR #25:

- ScratchImage::Initialize2D leaves the pixel buffer uninitialized.
  When the driver mapped a short region (rowsToCopy < height or
  bytesPerRow < destImage->rowPitch) the unfilled bytes encoded as
  whatever was on the heap, producing nondeterministic/corrupted
  screenshots from SaveToWICFile. Zero-fill the pixel buffer up
  front so any uncopied bytes encode as deterministic black.
- File used std::min via transitive include — make the dependency
  explicit by adding <algorithm>.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 21, 2026 07:19
@alandtse alandtse force-pushed the fix/screenshot-staging-memcpy branch from 9b94c67 to 7f357eb Compare May 21, 2026 07:19
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

Comment thread src/Features/ScreenshotFeature.cpp
alandtse and others added 3 commits May 21, 2026 07:52
Worker crashed inside rep movsb at line 84 of
ScreenshotFeature.cpp when memcpy ran past the driver-mapped region
of a staging texture (crash 2026-05-19 01:20:48, fault at source
+0x0FFF page boundary on a 2560-wide R8G8B8A8 capture).

The previous loop assumed mapped.RowPitch * destImage->height
covered the full subresource and that destImage->rowPitch never
exceeded mapped.RowPitch. Drivers can return a smaller mapped region
(alignment quirks, partial subresource exposure) or smaller row
pitch than DirectXTex computes for the destination.

Cap rows by mapped.DepthPitch / mapped.RowPitch (falling back to the
old assumption only when DepthPitch is zero), copy the smaller of
the two row pitches, and reject the call if Map succeeded with a
null pData or zero RowPitch.
Empty commit to trigger CodeQL on this PR. The default-setup CodeQL
workflow only fires on synchronize events, not reopened, so a push
is required to gate the PR on the new code scan.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two Copilot findings on PR #25:

- ScratchImage::Initialize2D leaves the pixel buffer uninitialized.
  When the driver mapped a short region (rowsToCopy < height or
  bytesPerRow < destImage->rowPitch) the unfilled bytes encoded as
  whatever was on the heap, producing nondeterministic/corrupted
  screenshots from SaveToWICFile. Zero-fill the pixel buffer up
  front so any uncopied bytes encode as deterministic black.
- File used std::min via transitive include — make the dependency
  explicit by adding <algorithm>.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@alandtse alandtse force-pushed the fix/screenshot-staging-memcpy branch from 7f357eb to 4ec9de2 Compare May 21, 2026 07:52
@alandtse alandtse merged commit ec2db80 into dev May 22, 2026
17 checks passed
@alandtse alandtse deleted the fix/screenshot-staging-memcpy branch May 22, 2026 06:11
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.

2 participants