Commit a22a9a9
* Fix cyclic AST recursion DoS (issue #196)
Cyclic pickle structures (e.g., `L = []; L.append(L)`) caused
RecursionError when fickling analyzed them. This fix detects cycles
at mutation points (Append, Appends, SetItem, SetItems, AddItems)
and replaces self-references with `<cyclic-ref>` placeholder AST nodes.
Changes:
- Add `_has_cycle` flag to Interpreter to track cycle detection
- Modify mutation operations to check if value being added `is` the container
- Add `has_cycles` property to Pickled class for downstream awareness
- Update tests to verify the fix works correctly
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Restore cycle detection in ASTProperties visitor
This was erroneously added and later removed in PR #210. The visitor
override tracks visited nodes by id to prevent infinite recursion when
traversing cyclic AST structures created via MEMOIZE + GET opcodes.
Co-Authored-By: Dan Guido <dan@trailofbits.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
* Merge and uniformize tests
* Add tests for all cycle detection corner cases
Cover dict value cycles, dict key cycles, and set cycles
to ensure the cycle detection feature handles all supported
mutation opcodes (SetItem, AddItems).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix AddItems to peek instead of pop the set
Match Python's pickle implementation which uses stack[-1] to peek
at the set without removing it from the stack.
Ref: https://github.com/python/cpython/blob/6ea3f8cd7ff4ff9769ae276dabc0753e09dca998/Lib/pickle.py#L1840
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use Python-style repr for cyclic references
Match Python's repr output for cyclic structures:
- List cycles: [[...]] instead of [<cyclic-ref>]
- Dict cycles: {'key': {...}} instead of {'key': <cyclic-ref>}
- Set cycles: {{...}} instead of {<cyclic-ref>}
This makes fickling's AST output directly comparable to what Python
displays when printing cyclic data structures.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Raise InterpretationError for impossible cyclic structures
Match Python's behavior for unhashable cyclic references:
- Dict key cycles: raise InterpretationError (Python raises TypeError)
- Set cycles: raise InterpretationError (Python raises TypeError)
- Dict value cycles: still allowed with {...} placeholder
- List cycles: still allowed with [...] placeholder
This distinguishes between valid cyclic structures that Python can
represent (lists containing themselves, dicts with self-referencing
values) and impossible structures that would fail at runtime.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Thomas Chauchefoin <thomas.chauchefoin@trailofbits.com>
1 parent 19e828d commit a22a9a9
2 files changed
+141
-4
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
528 | 528 | | |
529 | 529 | | |
530 | 530 | | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
531 | 544 | | |
532 | 545 | | |
533 | 546 | | |
| |||
572 | 585 | | |
573 | 586 | | |
574 | 587 | | |
| 588 | + | |
| 589 | + | |
575 | 590 | | |
576 | 591 | | |
577 | 592 | | |
| |||
891 | 906 | | |
892 | 907 | | |
893 | 908 | | |
| 909 | + | |
894 | 910 | | |
895 | 911 | | |
896 | 912 | | |
| |||
1036 | 1052 | | |
1037 | 1053 | | |
1038 | 1054 | | |
1039 | | - | |
| 1055 | + | |
| 1056 | + | |
| 1057 | + | |
1040 | 1058 | | |
1041 | 1059 | | |
1042 | 1060 | | |
| |||
1046 | 1064 | | |
1047 | 1065 | | |
1048 | 1066 | | |
| 1067 | + | |
| 1068 | + | |
| 1069 | + | |
| 1070 | + | |
| 1071 | + | |
| 1072 | + | |
1049 | 1073 | | |
1050 | 1074 | | |
1051 | 1075 | | |
| |||
1130 | 1154 | | |
1131 | 1155 | | |
1132 | 1156 | | |
| 1157 | + | |
1133 | 1158 | | |
1134 | 1159 | | |
1135 | 1160 | | |
| |||
1477 | 1502 | | |
1478 | 1503 | | |
1479 | 1504 | | |
1480 | | - | |
| 1505 | + | |
1481 | 1506 | | |
1482 | 1507 | | |
1483 | 1508 | | |
1484 | 1509 | | |
| 1510 | + | |
| 1511 | + | |
| 1512 | + | |
| 1513 | + | |
1485 | 1514 | | |
1486 | 1515 | | |
1487 | 1516 | | |
| |||
1760 | 1789 | | |
1761 | 1790 | | |
1762 | 1791 | | |
1763 | | - | |
| 1792 | + | |
1764 | 1793 | | |
1765 | 1794 | | |
1766 | 1795 | | |
1767 | 1796 | | |
| 1797 | + | |
| 1798 | + | |
| 1799 | + | |
| 1800 | + | |
| 1801 | + | |
| 1802 | + | |
1768 | 1803 | | |
1769 | 1804 | | |
1770 | 1805 | | |
| |||
1792 | 1827 | | |
1793 | 1828 | | |
1794 | 1829 | | |
| 1830 | + | |
| 1831 | + | |
| 1832 | + | |
| 1833 | + | |
| 1834 | + | |
| 1835 | + | |
1795 | 1836 | | |
1796 | 1837 | | |
1797 | 1838 | | |
| |||
1871 | 1912 | | |
1872 | 1913 | | |
1873 | 1914 | | |
| 1915 | + | |
| 1916 | + | |
| 1917 | + | |
1874 | 1918 | | |
1875 | 1919 | | |
1876 | 1920 | | |
| |||
1879 | 1923 | | |
1880 | 1924 | | |
1881 | 1925 | | |
1882 | | - | |
| 1926 | + | |
1883 | 1927 | | |
1884 | 1928 | | |
| 1929 | + | |
| 1930 | + | |
| 1931 | + | |
| 1932 | + | |
1885 | 1933 | | |
1886 | 1934 | | |
1887 | 1935 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
| 14 | + | |
13 | 15 | | |
14 | 16 | | |
15 | 17 | | |
16 | 18 | | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
17 | 23 | | |
18 | 24 | | |
19 | 25 | | |
20 | 26 | | |
21 | 27 | | |
22 | 28 | | |
23 | 29 | | |
| 30 | + | |
24 | 31 | | |
25 | 32 | | |
26 | 33 | | |
| |||
164 | 171 | | |
165 | 172 | | |
166 | 173 | | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
0 commit comments