Skip to content

Qualcomm: bounds-check the delegate - #22237

Merged
psiddh merged 6 commits into
pytorch:mainfrom
psiddh:qnn-execute-bounds-check
Sep 4, 2026
Merged

psiddh merged 6 commits into
pytorch:mainfrom
psiddh:qnn-execute-bounds-check

Conversation

@psiddh

@psiddh psiddh commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

Qualcomm: bounds-check the delegate argument walk instead of running 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 mismatch in either direction is fatal. A
shortfall is the memory-safety case, since the walk reads past the end of args;
a surplus does not read out of bounds, but it still means the binary and the
program disagree on the signature, which is a defect either way. This started
out warning on a surplus and was changed to fail at review request.

Verified against 14 lowerings -- single I/O, multi-input, multi-output,
partially-consumed multi-output, topk with both outputs used, a mutable buffer
and a conv, each under an fp16 and a quantized spec -- with no false positives.
Mutable buffers are excluded from the count by the same rule the binding loops
use, so they do not create a surplus.

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.

❌ 3 New Failures

As of commit 25d20f1 with merge base fe5d8d6 (image):

NEW FAILURES - The following jobs have failed:

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
@harshs-qti

Copy link
Copy Markdown

" 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 "

Can we add a testcase to ensure this scenario is not broken again

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

Thanks for the PR.
Please have a look at the comments.

Comment thread backends/qualcomm/runtime/QnnExecuTorchBackend.cpp Outdated
Review follow-up. A surplus is not a memory-safety problem, which is why it
warned, but if the check exists to keep QNN graph I/O and the delegate signature
aligned then both directions are misalignment.

Verified against 14 lowerings -- single I/O, multi-input, multi-output,
partially-consumed multi-output, topk with both outputs used, a mutable buffer
and a conv, each under an fp16 and a quantized spec -- with no false positives.
Mutable buffers are excluded from the count by the same rule the binding loops
use, so they do not create a surplus.
Copilot AI review requested due to automatic review settings September 2, 2026 01:19

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.

🟡 Changes recommended

The newly added argument-count check currently makes surplus args fatal, which contradicts the PR’s stated behavior (surplus should warn) and is stricter than required for the memory-safety fix.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread backends/qualcomm/runtime/QnnExecuTorchBackend.cpp
@psiddh

psiddh commented Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

" 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 "

Can we add a testcase to ensure this scenario is not broken again

Good point. Can you point me to right C++ test target for this particular test ? Can't seem to find the right one... Worth noting the scenario isn't reachable from AOT any more, #22011 removed the bug that produced it, so a stale binary is the only remaining path.

@psiddh psiddh changed the title Qualcomm: bounds-check the delegate argument walk instead of running … Qualcomm: bounds-check the delegate Sep 2, 2026
Copilot AI review requested due to automatic review settings September 2, 2026 22:41

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.

🟢 Approval recommended

The added preflight check closes a clear memory-safety hole with minimal, localized impact (only a minor error-message wording nit remains).

Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread backends/qualcomm/runtime/QnnExecuTorchBackend.cpp Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 3, 2026 20:53

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.

🟡 Changes recommended

There are a couple of small but concrete correctness/robustness improvements (notably args_index type safety) that should be addressed alongside this memory-safety fix.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment thread backends/qualcomm/runtime/QnnExecuTorchBackend.cpp Outdated
Comment thread backends/qualcomm/runtime/QnnExecuTorchBackend.cpp
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 3, 2026 21:03
@psiddh

psiddh commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

@winskuo-quic Can you take a look at it again ? Thanks

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.

🟢 Approval recommended

The change is a narrowly scoped, internally consistent bounds check that prevents a confirmed OOB dereference without altering the binding rules used by the existing loops.

Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@psiddh
psiddh merged commit 372aa3b into pytorch:main Sep 4, 2026
48 checks passed
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.

5 participants