Skip to content

Fix two memory-safety defects in file-backed weights cache#20562

Open
JakeStevens wants to merge 1 commit into
pytorch:mainfrom
JakeStevens:export-D109886666
Open

Fix two memory-safety defects in file-backed weights cache#20562
JakeStevens wants to merge 1 commit into
pytorch:mainfrom
JakeStevens:export-D109886666

Conversation

@JakeStevens

Copy link
Copy Markdown
Contributor

Summary:
Fixes two defects in the XNNPACK file-backed packed-weight cache (XNNWeightsCache)

  1. Use-after-munmap via offset_to_addr (SIGBUS). offset_to_addr returns the raw pointer stored in packed_data_ptrs_[offset], which on the file-backed path is an address inside a MAP_SHARED region. reset_for_fresh_write() munmaps every region but left the offset->pointer slots populated, so a subsequent offset_to_addr handed XNNPACK a pointer into unmapped memory — dereferencing it faults (SIGBUS/SIGSEGV, or an ASAN report). Fix: null each file-backed slot before erasing its metadata entry, so offset_to_addr returns nullptr (the miss contract look_up_or_insert already expects) instead of a dangling pointer.

  2. Mid-trailer-truncated cache file wrongly accepted. In load_packed_cache, the parse loop guard i < entry_count && cursor + 4 <= end terminated silently when fewer than 4 bytes remained, with no post-loop check that all entry_count entries were consumed. A file whose footer claimed entry_count=N but whose index region held only N-1 complete entries (plus <4 trailing bytes) was accepted as a partial cache — the existing truncated-entry-header check only fires when >=4 bytes remain. The next save_packed_index would then rewrite a trailer covering only the partial subset, permanently dropping the missing entries (the exact failure the code's own comment warns about). Fix: after the loop, require that exactly entry_count entries were read and that the cursor consumed the whole index region; otherwise roll back (munmap, clear state) and return false, matching the in-loop corruption branches.

A minimal test seam (friend class XNNWeightsCacheTestPeer) is added to the runtime header so the unit test can drive the private fresh-write path directly. Changes are mirrored across the fbcode/ and xplat/ copies.

Differential Revision: D109886666

Summary:
Fixes two defects in the XNNPACK file-backed packed-weight cache (`XNNWeightsCache`)

1. Use-after-munmap via `offset_to_addr` (SIGBUS). `offset_to_addr` returns the raw pointer stored in `packed_data_ptrs_[offset]`, which on the file-backed path is an address inside a `MAP_SHARED` region. `reset_for_fresh_write()` munmaps every region but left the offset->pointer slots populated, so a subsequent `offset_to_addr` handed XNNPACK a pointer into unmapped memory — dereferencing it faults (SIGBUS/SIGSEGV, or an ASAN report). Fix: null each file-backed slot before erasing its metadata entry, so `offset_to_addr` returns `nullptr` (the miss contract `look_up_or_insert` already expects) instead of a dangling pointer.

2. Mid-trailer-truncated cache file wrongly accepted. In `load_packed_cache`, the parse loop guard `i < entry_count && cursor + 4 <= end` terminated silently when fewer than 4 bytes remained, with no post-loop check that all `entry_count` entries were consumed. A file whose footer claimed `entry_count=N` but whose index region held only `N-1` complete entries (plus <4 trailing bytes) was accepted as a partial cache — the existing truncated-entry-header check only fires when >=4 bytes remain. The next `save_packed_index` would then rewrite a trailer covering only the partial subset, permanently dropping the missing entries (the exact failure the code's own comment warns about). Fix: after the loop, require that exactly `entry_count` entries were read and that the cursor consumed the whole index region; otherwise roll back (munmap, clear state) and return false, matching the in-loop corruption branches.

A minimal test seam (`friend class XNNWeightsCacheTestPeer`) is added to the runtime header so the unit test can drive the private fresh-write path directly. Changes are mirrored across the `fbcode/` and `xplat/` copies.

Differential Revision: D109886666
@JakeStevens JakeStevens requested a review from digantdesai as a code owner June 27, 2026 02:20
@pytorch-bot

pytorch-bot Bot commented Jun 27, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20562

Note: Links to docs will display an error until the docs builds have been completed.

✅ No Failures

As of commit fa23731 with merge base 825bd30 (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 27, 2026
@meta-codesync

meta-codesync Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

@JakeStevens has exported this pull request. If you are a Meta employee, you can view the originating Diff in D109886666.

@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. meta-exported

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant