-
Notifications
You must be signed in to change notification settings - Fork 16
fix(picklescan): safely parse bounded PyTorch tensor batches #1783
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 4 commits
c49fdac
6ab180a
117dc52
662ee8b
165da93
b6e5500
8c1de87
5accbc4
6acec3c
a2f6598
fc0cb23
d394b41
a9b5322
1ae8668
5f68a46
37ac497
48d2778
119697c
96ed814
e9cb6a8
d6f6771
fd602ee
2bdd951
fcd0e42
7081570
9c2735c
3e5b248
46a8fb7
d9f9103
9226edd
5c56a10
21bc219
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1874,6 +1874,10 @@ def _pytorch_storage_keys_from_pickle_bytes( | |
| ) | ||
|
|
||
| marker = object() | ||
| canonical_tensor = object() | ||
| canonical_batch_placeholder = object() | ||
| canonical_batch_entries: list[tuple[str, object]] = [] | ||
| canonical_batch_target: object | None = None | ||
| memo: dict[int, Any] = {} | ||
| stack: list[Any] = [] | ||
| referenced_keys: set[str] = set() | ||
|
|
@@ -1932,21 +1936,72 @@ def apply_setitems_to_target(items: tuple[tuple[Any, Any], ...]) -> None: | |
| else: | ||
| poison_stack_top() | ||
|
|
||
| def pop_marked_tuple() -> tuple[Any, ...] | None: | ||
| def pop_marked_tuple(*, max_width: int = _PYTORCH_STORAGE_TRUST_MAX_TUPLE_WIDTH) -> tuple[Any, ...] | None: | ||
| items: list[Any] = [] | ||
| while stack: | ||
| item = stack.pop() | ||
| if item is marker: | ||
| return tuple(reversed(items)) | ||
| items.append(item) | ||
| if len(items) > _PYTORCH_STORAGE_TRUST_MAX_TUPLE_WIDTH: | ||
| raise ValueError("PyTorch storage persistent ID tuple exceeded trust parser width") | ||
| if len(items) > max_width: | ||
| raise ValueError("PyTorch storage trust parser marked collection exceeded its width limit") | ||
| return None | ||
|
|
||
| def compact_canonical_setitems_stack() -> bool: | ||
| nonlocal canonical_batch_target | ||
|
|
||
| if len(canonical_batch_entries) >= _PYTORCH_STORAGE_TRUST_MAX_STACK_DEPTH: | ||
| return False | ||
| for marker_index, item in enumerate(stack): | ||
| if item is not marker or marker_index == 0: | ||
| continue | ||
| target = stack[marker_index - 1] | ||
| if not isinstance(target, dict | _PytorchOrderedDictState): | ||
| continue | ||
| if isinstance(target, _PytorchOrderedDictState) and target.used_as_hooks: | ||
| continue | ||
| if canonical_batch_target is not None and target is not canonical_batch_target: | ||
| continue | ||
|
mldangelo-oai marked this conversation as resolved.
|
||
| pair_index = marker_index + 1 | ||
| if pair_index < len(stack) and stack[pair_index] is canonical_batch_placeholder: | ||
| pair_index += 1 | ||
| if pair_index + 1 >= len(stack): | ||
| continue | ||
| key = stack[pair_index] | ||
| value = stack[pair_index + 1] | ||
| if not isinstance(key, str): | ||
| continue | ||
| if value is not canonical_tensor and not isinstance(value, (str, int, float, bytes, type(None))): | ||
| continue | ||
| if value is not canonical_tensor and not ( | ||
| any(item is canonical_tensor for item in stack[pair_index + 2 :]) | ||
| or any(entry_value is canonical_tensor for _entry_key, entry_value in canonical_batch_entries) | ||
| ): | ||
|
mldangelo-oai marked this conversation as resolved.
Outdated
|
||
| continue | ||
|
mldangelo-oai marked this conversation as resolved.
|
||
| if canonical_batch_target is None: | ||
| canonical_batch_target = target | ||
| stack.insert(marker_index + 1, canonical_batch_placeholder) | ||
| pair_index += 1 | ||
| canonical_batch_entries.append((key, value)) | ||
| del stack[pair_index : pair_index + 2] | ||
| return True | ||
| return False | ||
|
|
||
| def within_limits() -> bool: | ||
| while len(stack) > _PYTORCH_STORAGE_TRUST_MAX_STACK_DEPTH: | ||
| if not compact_canonical_setitems_stack(): | ||
| return False | ||
| if canonical_batch_entries and not any( | ||
| item is marker | ||
| and marker_index > 0 | ||
| and stack[marker_index - 1] is canonical_batch_target | ||
| and marker_index + 1 < len(stack) | ||
|
mldangelo-oai marked this conversation as resolved.
|
||
| and stack[marker_index + 1] is canonical_batch_placeholder | ||
| for marker_index, item in enumerate(stack) | ||
| ): | ||
| return False | ||
| return ( | ||
| len(stack) <= _PYTORCH_STORAGE_TRUST_MAX_STACK_DEPTH | ||
| and len(memo) <= _PYTORCH_STORAGE_TRUST_MAX_MEMO_ENTRIES | ||
| len(memo) <= _PYTORCH_STORAGE_TRUST_MAX_MEMO_ENTRIES | ||
| and len(referenced_keys) <= _PYTORCH_STORAGE_TRUST_MAX_REFERENCED_KEYS | ||
| ) | ||
|
|
||
|
|
@@ -2041,6 +2096,7 @@ def reduce_result(function: Any, args: Any, reduce_position: int) -> Any: | |
| tensor_rebuild_uses.add((function.position, reduce_position)) | ||
| if rebuild_tensor_v2_args_are_canonical(args): | ||
| canonical_tensor_rebuild_invocations.add((function.position, reduce_position)) | ||
| return canonical_tensor | ||
| else: | ||
| invalidate_tensor_rebuild_proof() | ||
| return None | ||
|
|
@@ -2192,13 +2248,35 @@ def reduce_result(function: Any, args: Any, reduce_position: int) -> Any: | |
| key = stack.pop() | ||
| apply_setitems_to_target(((key, value),)) | ||
| elif opcode_name == "SETITEMS": | ||
| setitem_items = pop_marked_tuple() | ||
| setitem_items = pop_marked_tuple(max_width=_PYTORCH_STORAGE_TRUST_MAX_STACK_DEPTH) | ||
|
mldangelo-oai marked this conversation as resolved.
|
||
| if setitem_items is None or len(setitem_items) % 2 != 0 or not stack: | ||
| clear_stack_after_malformed_provenance() | ||
| continue | ||
| apply_setitems_to_target( | ||
| tuple((setitem_items[index], setitem_items[index + 1]) for index in range(0, len(setitem_items), 2)) | ||
| ) | ||
| if ( | ||
| setitem_items is None | ||
| or not setitem_items | ||
| or setitem_items[0] is not canonical_batch_placeholder | ||
| or not stack | ||
| or stack[-1] is not canonical_batch_target | ||
| or (len(setitem_items) - 1) % 2 != 0 | ||
| ): | ||
| clear_stack_after_malformed_provenance() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When AGENTS.md reference: AGENTS.md:L115-L117 Useful? React with 👍 / 👎. |
||
| continue | ||
| remaining_items = setitem_items[1:] | ||
| if ( | ||
| len(canonical_batch_entries) + len(remaining_items) // 2 | ||
| > _PYTORCH_STORAGE_TRUST_MAX_STACK_DEPTH | ||
| ): | ||
| return _PytorchStorageReferenceParse(set(), {}, set(), set(), False, False) | ||
| setitem_pairs = tuple(canonical_batch_entries) + tuple( | ||
| (remaining_items[index], remaining_items[index + 1]) | ||
| for index in range(0, len(remaining_items), 2) | ||
| ) | ||
| canonical_batch_entries.clear() | ||
| canonical_batch_target = None | ||
| else: | ||
| setitem_pairs = tuple( | ||
| (setitem_items[index], setitem_items[index + 1]) for index in range(0, len(setitem_items), 2) | ||
| ) | ||
| apply_setitems_to_target(setitem_pairs) | ||
| elif opcode_name == "BINPERSID": | ||
| pid = stack.pop() if stack else None | ||
| storage_ref = storage_ref_from_pid(pid) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.