Skip to content

fix(picklescan): safely parse bounded PyTorch tensor batches - #1783

Open
mldangelo-oai wants to merge 30 commits into
mainfrom
fix/hf-pytorch-batched-setitems
Open

fix(picklescan): safely parse bounded PyTorch tensor batches#1783
mldangelo-oai wants to merge 30 commits into
mainfrom
fix/hf-pytorch-batched-setitems

Conversation

@mldangelo-oai

@mldangelo-oai mldangelo-oai commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Stream proven canonical tensor entries and inert checkpoint metadata without exceeding the existing 1,024-item simulated-stack bound.
  • Cover standard 600/1,000-entry benign batches, mixed metadata, oversized batches, and malicious near-matches.
  • Keep this change scoped to the independently released picklescan package; release-please will publish its next patch before any root dependency-floor change.

Verification

  • Immutable timm/mobilenetv3_small_100.lamb_in1k checkpoint 1824797e7887cbec1990e4adbd6675960a36c589 reproduced and scans clean without executing model contents.
  • Dependency-free standalone regressions pass, including the malicious mixed-metadata control.
  • Ruff, standalone mypy, both frozen lock checks, and the 187-test Rust suite pass.

Copilot AI review requested due to automatic review settings July 27, 2026 04:13

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.

Pull request overview

This PR fixes a false-positive condition in the PyTorch ZIP “trusted storage reference” parsing path when state dictionaries are serialized using large SETITEMS batches (common in batched state dicts). It keeps the existing overall stack-depth bound while decoupling it from the smaller tuple-width limit, and adds regression coverage across both the standalone picklescan package and the root scanner integration.

Changes:

  • Adjust the PyTorch storage trust parser to apply SETITEMS batching limits using the stack-depth bound rather than the tuple-width bound.
  • Add deterministic unit/regression tests for large batched state dicts (benign and malicious near-match) and explicit stack/tuple bound behaviors.
  • Add changelog entries for both modelaudit and modelaudit-picklescan.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/scanners/test_pytorch_zip_scanner.py Adds an integration regression ensuring large batched torch.save state dicts remain clean (no _rebuild_tensor_v2 false positives).
packages/modelaudit-picklescan/tests/test_api.py Adds deterministic ZIP fixtures + regressions for large batched SETITEMS, plus stack/tuple bound unit tests.
packages/modelaudit-picklescan/src/modelaudit_picklescan/api.py Updates marked-tuple parsing to accept a configurable width and uses stack-depth for SETITEMS batches.
packages/modelaudit-picklescan/CHANGELOG.md Notes the bug fix in the picklescan package changelog.
CHANGELOG.md Notes the bug fix in the root package changelog.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/modelaudit-picklescan/tests/test_api.py Outdated
Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/api.py Outdated
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Workflow run and artifacts

Performance Benchmarks

Compared 13 shared benchmarks with a regression threshold of 15%.
Status: 0 regressions, 0 improved, 13 stable, 0 new, 0 missing.
Aggregate shared-benchmark median: 4.320s -> 4.347s (+0.6%).

Workload Benchmark Target Size Files Baseline Current Change Status
mixed-model-repository tests/benchmarks/test_scan_benchmarks.py::test_scan_release_candidate_repository release-candidate 547.3 KiB 32 635.19ms 659.28ms +3.8% stable
nested-payload-review tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_nested_payload_review[nested_hex] nested_hex 130 B 1 268.5us 261.5us -2.6% stable
clean-training-checkpoint tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_clean_training_checkpoint safe_large 278.2 KiB 1 117.51ms 114.57ms -2.5% stable
chunked-upload-stream tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_chunked_upload_stream chunked_stream 278.2 KiB 1 120.74ms 117.78ms -2.5% stable
nested-payload-review tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_nested_payload_review[nested_base64] nested_base64 98 B 1 260.4us 255.2us -2.0% stable
direct-malicious-upload tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_direct_malicious_upload malicious_reduce 52 B 1 187.9us 191.6us +2.0% stable
suspicious-pickle-intake tests/benchmarks/test_scan_benchmarks.py::test_scan_suspicious_pickle_intake suspicious-intake 183.8 KiB 4 150.47ms 147.80ms -1.8% stable
single-checkpoint-preflight tests/benchmarks/test_scan_benchmarks.py::test_scan_single_checkpoint_before_load single_checkpoint.pkl 183.0 KiB 1 109.43ms 107.52ms -1.7% stable
duplicate-heavy-registry tests/benchmarks/test_scan_benchmarks.py::test_scan_duplicate_registry_snapshot registry-snapshot 915.2 KiB 13 595.14ms 585.22ms -1.7% stable
padded-multi-stream-upload tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_padded_multi_stream_upload multi_stream_padded 4.1 KiB 1 290.4us 293.8us +1.2% stable
rejected-basic-auth-candidates tests/benchmarks/test_scan_benchmarks.py::test_rejected_basic_auth_candidates_scan_linearly - 371.1 KiB 1 2.451s 2.475s +1.0% stable
nested-payload-review tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_nested_payload_review[nested_raw] nested_raw 78 B 1 239.0us 239.6us +0.3% stable
warm-cache-rescan tests/benchmarks/test_scan_benchmarks.py::test_scan_warm_cached_repository_rescan release-candidate 547.3 KiB 32 138.85ms 138.95ms +0.1% stable

Copilot AI review requested due to automatic review settings July 27, 2026 04:22

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.

Pull request overview

Copilot reviewed 12 out of 15 changed files in this pull request and generated no new comments.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6ab180a928

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/api.py
Copilot AI review requested due to automatic review settings July 27, 2026 04:37

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.

Pull request overview

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

Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/api.py

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 117dc52da6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/modelaudit-picklescan/pyproject.toml Outdated
Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/api.py
Copilot AI review requested due to automatic review settings July 27, 2026 04:48
@mldangelo-oai mldangelo-oai changed the title fix: avoid false positives for batched PyTorch state dictionaries fix(picklescan): safely parse bounded PyTorch tensor batches Jul 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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings July 27, 2026 04:52

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 165da9330a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/api.py Outdated
Copilot AI review requested due to automatic review settings July 27, 2026 04:59

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2bdd951c24

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/api.py
Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/api.py Outdated
Copilot AI review requested due to automatic review settings July 27, 2026 06:38

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings July 27, 2026 06:43

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings July 27, 2026 06:49

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3e5b2486fa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/api.py Outdated
Copilot AI review requested due to automatic review settings July 27, 2026 07:03

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

packages/modelaudit-picklescan/src/modelaudit_picklescan/api.py:2098

  • isinstance() cannot take a PEP 604 union (dict | _PytorchOrderedDictState) as its second argument; this will raise TypeError: isinstance() argument 2 cannot be a union at runtime on Python 3.10+ when the compaction path executes. Use a tuple of types instead.
            if not isinstance(target, dict | _PytorchOrderedDictState):

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 46a8fb7a2d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/api.py
Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/api.py Outdated
Copilot AI review requested due to automatic review settings July 27, 2026 07:16

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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

packages/modelaudit-picklescan/src/modelaudit_picklescan/api.py:2103

  • isinstance() cannot take a PEP 604 union (dict | _PytorchOrderedDictState) at runtime; this raises TypeError and will cause the trust parser to bail out early (caught by the broad except Exception, returning parse_complete=False). Use a tuple of types instead.
                continue
            target = stack[marker_index - 1]
            if not isinstance(target, dict | _PytorchOrderedDictState):
                continue
            if isinstance(target, _PytorchOrderedDictState) and target.used_as_hooks:

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d9f9103262

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (
len(setitem_pairs) * 2 > _PYTORCH_STORAGE_TRUST_MAX_TUPLE_WIDTH
and not batch_has_canonical_tensor
and not trusted_canonical_batch_seen

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Record canonical tensors inserted with SETITEM

When a valid state_dict contains one tensor and at least 33 module _metadata records, CPython serializes the tensor with SETITEM, not SETITEMS. Because only the SETITEMS branch sets trusted_canonical_batch_seen, this condition treats the later safe metadata batch as unproven and STOP returns parse_complete=False, restoring persistent-ID and _rebuild_tensor_v2 warnings. Fresh evidence beyond the earlier post-tensor report is that the current real-module regression has two tensor entries, while the one-entry OrderedDict regression strips the state dict's _metadata; have SETITEM recognize a canonical tensor and update the pending-batch state too.

AGENTS.md reference: AGENTS.md:L115-L117

Useful? React with 👍 / 👎.

or stack[-1] is not canonical_batch_target
or (len(setitem_items) - 1) % 2 != 0
):
clear_stack_after_malformed_provenance()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve popped SETITEMS provenance before clearing

When SETITEMS has odd arity or no target, pop_marked_tuple() has already removed its operands before this call clears the remaining stack, so a raw _PytorchStorageRef among those operands is never recorded as discarded. In a stream containing a newly accepted wide safe batch followed by this malformed operation and a later small canonical batch, the parser reports parse_complete=True with discarded_tracked_storage_references=False, and _trusted_pytorch_data_pkl_from_storage_member_sizes() consequently trusts the discarded storage key. A pickle hidden beyond the expanded 64 KiB member probe is then missed while its persistent-ID finding is suppressed; inspect setitem_items for tracked provenance before clearing it.

AGENTS.md reference: AGENTS.md:L115-L117

Useful? React with 👍 / 👎.

Copilot AI review requested due to automatic review settings July 27, 2026 07:31

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9226edde21

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1289 to +1292
except ValueError as error:
message = str(error).lower()
if "opcode" in message and "unknown" in message:
return False

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve generic detection for expanded storage probes

When a 33+-entry canonical state batch marks a storage member for an expanded probe, a member beginning \x80\x04NNNN\x00 (a binary header, harmless opcodes, then malformed padding) is rejected here before the 64 KiB sample is read. The same member is accepted by _zip_entry_looks_like_pickle() after its valid opcode prefix, allowing the Rust follow-on scanner to inspect a dangerous pickle placed later in the member; this early return instead prevents the member from ever being added to pickle_entries, so that payload is skipped. Keep this optimization at least as conservative as the generic probe.

AGENTS.md reference: AGENTS.md:L115-L115

Useful? React with 👍 / 👎.

message = str(error).lower()
if "opcode" in message and "unknown" in message:
return False
except Exception:

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.

Pull request overview

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

Comment on lines +1285 to 1296
if max_probe_bytes > _TRUSTED_STORAGE_PICKLE_PROBE_BYTES:
try:
for _opcode, _arg, _position in pickletools.genops(prefix):
pass
except ValueError as error:
message = str(error).lower()
if "opcode" in message and "unknown" in message:
return False
except Exception:
pass

sample = prefix
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