Skip to content

fix: terminate call-graph alias fixpoint on oscillating rebinds (#1247)#1259

Merged
mldangelo-oai merged 26 commits into
mainfrom
fix/call-graph-alias-cycle-hang
May 23, 2026
Merged

fix: terminate call-graph alias fixpoint on oscillating rebinds (#1247)#1259
mldangelo-oai merged 26 commits into
mainfrom
fix/call-graph-alias-cycle-hang

Conversation

@mldangelo
Copy link
Copy Markdown
Member

Summary

Fixes #1247 — scanning a pickle that references imaplib, http.server, or nntplib caused modelaudit to hang forever, blocking every other file in a directory scan.

Huge thanks to @HalaAli198 for the exceptionally well-researched report. 🙏 The hex dump, the minimal 42-byte proof-of-concept, the public Hugging Face repros, the workaround (--exclude-scanner pickle), and the cross-reference to promptfoo/promptfoo#6266 made this fast to reproduce and diagnose. That is exactly the kind of issue maintainers dream of receiving.

Root cause

The report attributed the hang to the compiled Rust scanner (_rust.abi3.so), which was a very reasonable inference given --timeout had no effect. A faulthandler traceback showed the hang is actually in pure-Python call-graph analysis, before the slow Rust path is even reached:

call_graph.py:1673  _collect_assignment_aliases   ← infinite loop
call_graph.py:1245  _analyze_module
...
api.py:1014         _with_call_graph_findings

When call-graph analysis imports such a module's source, it flattens every module-level statement — including both branches of the if __name__ == '__main__' block. imaplib.py binds the same name twice:

M = IMAP4_stream(stream_command)   # imaplib.py:1619
M = IMAP4(host)                    # imaplib.py:1621

The alias fixpoint loop in _collect_assignment_aliases flips M between imaplib.IMAP4 and imaplib.IMAP4_stream on every pass, always marking changed = True. The loop's only guard checks the alias dict size, which never grows past 1 — so it spins forever. http.server and nntplib hit the identical __main__-block rebind pattern.

Fix

Track every (target, resolved) pair already applied. Re-applying a previously seen pair no longer counts as progress, so an oscillating bind converges once both values have been observed (~3 passes) instead of looping forever. The alias is still applied so later nodes resolve correctly, and detection severity is unchanged — all affected files still report CRITICAL.

Verification

  • All three reported modules and the issue's hex-dump PoC now scan in <0.15s and still report CRITICAL.
  • New regression test test_call_graph_assignment_alias_cycle.py: a deterministic unit test for the oscillating-bind fixpoint plus an end-to-end imaplib PoC scan, both guarded by thread watchdogs that fail rather than hang on regression. Registered in the reduced-CI allowlist in tests/conftest.py.
  • Full picklescan suite (1338 passed), main test_pickle_scanner.py (131 passed), ruff format/check, and mypy all clean.

Note on --timeout

The report also observed that --timeout does not interrupt the hang. That is a separate architectural limitation: the timeout is checked cooperatively between scanners, so any CPU-bound single-threaded stretch (Rust or Python) cannot be preempted mid-call. Fixing the actual infinite loop resolves the reported denial-of-service; making long-running scans pre-emptible would be a larger, separate change worth tracking on its own.

Thanks again, @HalaAli198 — this was a genuinely valuable find, and the care you put into the report is much appreciated. Wishing you well, and happy (safe) scanning! 🛡️

🤖 Generated with Claude Code

Scanning a pickle that references certain stdlib modules (imaplib,
http.server, nntplib) caused modelaudit to hang indefinitely. A
faulthandler trace showed the hang was not in the Rust scanner, as
originally reported, but in pure-Python call-graph analysis:
_collect_assignment_aliases.

When call-graph analysis imports such a module's source, it flattens
all module-level statements -- including both branches of the
`if __name__ == '__main__'` block. imaplib binds the same name twice:

    M = IMAP4_stream(stream_command)
    M = IMAP4(host)

The alias fixpoint loop flips `M` between `imaplib.IMAP4` and
`imaplib.IMAP4_stream` on every pass, always marking `changed = True`.
The only loop guard checks the alias dict size, which never grows past
1, so the loop spins forever. http.server and nntplib hit the same
`__main__`-block rebind pattern.

Track every (target, resolved) pair already applied. Re-applying a
previously seen pair no longer counts as progress, so an oscillating
bind converges once both values have been observed instead of looping
forever. The alias is still applied so later nodes resolve correctly;
detection severity is unchanged (affected files still report CRITICAL).

Adds test_call_graph_assignment_alias_cycle.py: a deterministic unit
test for the oscillating-bind fixpoint plus an end-to-end imaplib
proof-of-concept scan, both guarded by thread watchdogs that fail
rather than hang on regression. Registers the file in the reduced-CI
allowlist in tests/conftest.py.

Fixes #1247

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

Workflow run and artifacts

Performance Benchmarks

Compared 12 shared benchmarks with a regression threshold of 15%.
Status: 0 regressions, 0 improved, 12 stable, 0 new, 0 missing.
Aggregate shared-benchmark median: 622.62ms -> 624.23ms (+0.3%).

Workload Benchmark Target Size Files Baseline Current Change Status
chunked-upload-stream tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_chunked_upload_stream chunked_stream 278.2 KiB 1 17.96ms 19.00ms +5.8% stable
clean-training-checkpoint tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_clean_training_checkpoint safe_large 278.2 KiB 1 14.86ms 15.40ms +3.6% stable
padded-multi-stream-upload tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_padded_multi_stream_upload multi_stream_padded 4.1 KiB 1 479.8us 466.1us -2.9% stable
nested-payload-review tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_nested_payload_review[nested_base64] nested_base64 98 B 1 137.5us 136.3us -0.9% stable
single-checkpoint-preflight tests/benchmarks/test_scan_benchmarks.py::test_scan_single_checkpoint_before_load single_checkpoint.pkl 183.0 KiB 1 36.11ms 35.80ms -0.9% stable
nested-payload-review tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_nested_payload_review[nested_raw] nested_raw 78 B 1 131.7us 132.5us +0.6% stable
nested-payload-review tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_nested_payload_review[nested_hex] nested_hex 130 B 1 140.9us 140.3us -0.4% stable
direct-malicious-upload tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_direct_malicious_upload malicious_reduce 52 B 1 410.9us 410.0us -0.2% stable
mixed-model-repository tests/benchmarks/test_scan_benchmarks.py::test_scan_release_candidate_repository release-candidate 547.3 KiB 32 253.81ms 254.30ms +0.2% stable
warm-cache-rescan tests/benchmarks/test_scan_benchmarks.py::test_scan_warm_cached_repository_rescan release-candidate 547.3 KiB 32 37.08ms 37.15ms +0.2% stable
duplicate-heavy-registry tests/benchmarks/test_scan_benchmarks.py::test_scan_duplicate_registry_snapshot registry-snapshot 915.2 KiB 13 187.55ms 187.25ms -0.2% stable
suspicious-pickle-intake tests/benchmarks/test_scan_benchmarks.py::test_scan_suspicious_pickle_intake suspicious-intake 183.8 KiB 4 73.94ms 74.05ms +0.2% stable

Copy link
Copy Markdown

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

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: 9feb7bc97d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/call_graph.py Outdated
Copy link
Copy Markdown

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

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: d78c997c7a

ℹ️ 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/call_graph.py Outdated
Copy link
Copy Markdown

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

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: 26655add3a

ℹ️ 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/call_graph.py Outdated
Copy link
Copy Markdown

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

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: 21fcf1a7ef

ℹ️ 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".

Copy link
Copy Markdown

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

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: c617e8ea85

ℹ️ 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/call_graph.py Outdated
Copy link
Copy Markdown

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

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: 42e7702a61

ℹ️ 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".

Copy link
Copy Markdown

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

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: 3acc6a8ae8

ℹ️ 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/call_graph.py Outdated
Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/call_graph.py Outdated
Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/call_graph.py Outdated
Copy link
Copy Markdown

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

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: f670f86681

ℹ️ 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/call_graph.py Outdated
Copy link
Copy Markdown

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

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: dbcdffbf84

ℹ️ 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".

Copy link
Copy Markdown

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

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: 6cd7de3e79

ℹ️ 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/call_graph.py Outdated
Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/call_graph.py Outdated
Copy link
Copy Markdown

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

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: 78eb56db50

ℹ️ 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/call_graph.py Outdated
Copy link
Copy Markdown

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

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: 8f2c0b4fd0

ℹ️ 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/call_graph.py Outdated
Copy link
Copy Markdown

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

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: 183869a10f

ℹ️ 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/call_graph.py Outdated
Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/call_graph.py Outdated
Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/call_graph.py Outdated
Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/call_graph.py Outdated
Copy link
Copy Markdown

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

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: 95642a4ac9

ℹ️ 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".

Copy link
Copy Markdown

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

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: 2d00183f36

ℹ️ 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".

Copy link
Copy Markdown

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

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: 8ca3b53efe

ℹ️ 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/call_graph.py Outdated
Copy link
Copy Markdown

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

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: 71925b19c1

ℹ️ 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/call_graph.py Outdated
Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/call_graph.py Outdated
Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/call_graph.py Outdated
Copy link
Copy Markdown

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

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: 06b7a3caab

ℹ️ 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/call_graph.py Outdated
Comment thread packages/modelaudit-picklescan/src/modelaudit_picklescan/call_graph.py Outdated
@mldangelo-oai mldangelo-oai merged commit 89895a4 into main May 23, 2026
32 checks passed
@mldangelo-oai mldangelo-oai deleted the fix/call-graph-alias-cycle-hang branch May 23, 2026 15:50
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.

Rust pickle scanner hangs indefinitely on pickle files referencing http.server, nntplib, and imaplib

2 participants