Skip to content

Qualcomm: bounds-check the delegate argument walk instead of running … - #22237

Open
psiddh wants to merge 1 commit into
pytorch:mainfrom
psiddh:qnn-execute-bounds-check
Open

Qualcomm: bounds-check the delegate argument walk instead of running …#22237
psiddh wants to merge 1 commit into
pytorch:mainfrom
psiddh:qnn-execute-bounds-check

Conversation

@psiddh

@psiddh psiddh commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

…off the end

execute() binds delegate arguments positionally. It walks the input and output tensor lists recovered from the context binary and, for every tensor the name prefixes mark as bindable, consumes one entry from args with a running counter. Nothing relates that counter to args.size().

So when the binary and the program disagree on the delegate signature -- a stale binary, or an AOT bug that publishes extra graph I/O -- the walk indexes past the end of the Span and dereferences whatever is there. In the case that prompted this, a context binary declaring 54 graph inputs and 56 graph outputs met a program passing 4 tensors, and the result was a null dereference at 0x8 with the two counts sitting in registers. Reading that back to a cause took days.

Count the bindable tensors with the same prefix rules the loops use, then check once before either loop runs. A shortfall is the memory-safety case and is fatal; a surplus is not unsafe, so it warns rather than failing, since a trailing unused argument is not obviously wrong.

Deliberately not included: a matching "input_" prefix filter on the input loop, for symmetry with the output loop. Inputs of a model built by from_context_binary carry names straight from the QNN converter with no such prefix, and the runtime only renames outputs (QnnManager.cpp SetName("output_" + tensor_name)). Filtering on it would skip every input of those models and leave the counter at zero when the output loop starts, writing outputs into input buffers. The count check gives the same protection without that risk.

cc @cbilgin

…off the end

execute() binds delegate arguments positionally. It walks the input and output
tensor lists recovered from the context binary and, for every tensor the name
prefixes mark as bindable, consumes one entry from args with a running counter.
Nothing relates that counter to args.size().

So when the binary and the program disagree on the delegate signature -- a stale
binary, or an AOT bug that publishes extra graph I/O -- the walk indexes past the
end of the Span and dereferences whatever is there. In the case that prompted
this, a context binary declaring 54 graph inputs and 56 graph outputs met a
program passing 4 tensors, and the result was a null dereference at 0x8 with the
two counts sitting in registers. Reading that back to a cause took days.

Count the bindable tensors with the same prefix rules the loops use, then check
once before either loop runs. A shortfall is the memory-safety case and is
fatal; a surplus is not unsafe, so it warns rather than failing, since a
trailing unused argument is not obviously wrong.

Deliberately not included: a matching "input_" prefix filter on the input loop,
for symmetry with the output loop. Inputs of a model built by from_context_binary
carry names straight from the QNN converter with no such prefix, and the runtime
only renames outputs (QnnManager.cpp SetName("output_" + tensor_name)). Filtering
on it would skip every input of those models and leave the counter at zero when
the output loop starts, writing outputs into input buffers. The count check gives
the same protection without that risk.

Authored with assistance from Claude Code.
Copilot AI lite review requested due to automatic review settings August 27, 2026 21:09
@pytorch-bot

pytorch-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown

🔗 Helpful Links

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

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

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 Aug 27, 2026

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@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.

@psiddh psiddh added the module: qnn Issues related to Qualcomm's QNN delegate and code under backends/qualcomm/ label Aug 27, 2026
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. module: qnn Issues related to Qualcomm's QNN delegate and code under backends/qualcomm/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants