|
| 1 | +# Bug Log - CPython Container Test Import |
| 2 | + |
| 3 | +## Bugs Found & Fixed |
| 4 | + |
| 5 | +### 1. Negative-step slicelength missing guard (list.asm, tuple.asm) |
| 6 | +**Symptom**: `a[3:3:-2]` causes OOM (allocates huge array) |
| 7 | +**Root cause**: Negative step slicelength formula `(start-stop-1)/abs(step)+1` used unsigned div without checking `start <= stop` first. When start==stop, `start-stop-1 = -1 = 0xFFFFFFFFFFFFFFFF` unsigned. |
| 8 | +**Fix**: Add `jle .empty` guard after `sub rax, r14` in both list and tuple getslice. |
| 9 | + |
| 10 | +### 2. slice_indices start/stop clamping for negative step (slice.asm) |
| 11 | +**Symptom**: `a[100:-100:-1]` segfaults (out-of-bounds read) |
| 12 | +**Root cause**: `start >= length` clamped to `length` for all steps, but negative step needs `length-1`. `stop < 0` after adding length clamped to 0 for all steps, but negative step needs `-1`. |
| 13 | +**Fix**: Conditional clamping based on step sign, matching CPython's PySlice_AdjustIndices. |
| 14 | + |
| 15 | +### 3. slice_indices NONE_SENTINEL collision with sys.maxsize (slice.asm) |
| 16 | +**Symptom**: `a[1::sys.maxsize]` treats step as None (defaults to 1) |
| 17 | +**Root cause**: NONE_SENTINEL = 0x7FFFFFFFFFFFFFFF == sys.maxsize. When step = sys.maxsize, the comparison `cmp rax, NONE_SENTINEL` matched and step was treated as None. |
| 18 | +**Fix**: Check actual payload against `none_singleton` pointer AND tag for `TAG_NONE` or `TAG_PTR` with None, instead of comparing integer values. |
| 19 | + |
| 20 | +### 4. `del a[i]` not decrementing ob_size (list.asm) |
| 21 | +**Symptom**: `del a[1]` on `[0, 1]` gives `[0, ]` (size still 2, second slot cleared) |
| 22 | +**Root cause**: `list_ass_subscript` with value=NULL always called `list_setitem` which replaces the slot with NULL but doesn't remove it or update size. |
| 23 | +**Fix**: Add `.las_int_delete` path that DECREFs old, shifts elements via memmove, decrements `ob_size`. |
| 24 | + |
| 25 | +### 5. `list[string_key]` segfault (list.asm, tuple.asm) |
| 26 | +**Symptom**: `a['x']` segfaults instead of raising TypeError |
| 27 | +**Root cause**: `list_subscript` assumed non-SmallInt, non-slice keys were heap ints and called `int_to_i64` on them, crashing on strings. |
| 28 | +**Fix**: Check `ob_type == int_type` before calling `int_to_i64`; raise TypeError otherwise. |
| 29 | + |
| 30 | +### 6. list.index() ignoring start/stop parameters (methods.asm) |
| 31 | +**Symptom**: `[-2,-1,0,0,1,2].index(0, 3)` returns 2 instead of 3 |
| 32 | +**Root cause**: `list_method_index` always searched from index 0 regardless of nargs. |
| 33 | +**Fix**: Check nargs >= 3/4 and extract start/stop from args[2]/args[3] with negative index handling. |
| 34 | + |
| 35 | +### 7. tuple*int dispatch missing (opcodes_misc.asm) |
| 36 | +**Symptom**: `(1,2)*2` raises TypeError |
| 37 | +**Root cause**: BINARY_OP only checked right operand for sq_repeat when left was SmallInt. When left was a sequence (tuple/list) and right was SmallInt, it fell through to tp_as_number which is NULL for tuples. |
| 38 | +**Fix**: Add `.binop_try_left_seq` check in `.binop_not_smallint_left` path. |
| 39 | + |
| 40 | +### 8. tuple+tuple dispatch missing (opcodes_misc.asm) |
| 41 | +**Symptom**: `(1,2) + (3,4)` raises TypeError |
| 42 | +**Root cause**: BINARY_OP for NB_ADD only checked tp_as_number, not tp_as_sequence.sq_concat. |
| 43 | +**Fix**: Add `.binop_try_seq_fallback` that checks sq_concat for ADD and sq_repeat for MULTIPLY when tp_as_number is NULL. |
| 44 | + |
| 45 | +### 9. list_inplace_repeat tag realloc using wrong size (list.asm) |
| 46 | +**Symptom**: `s *= 10` corrupts memory / changes list identity |
| 47 | +**Root cause**: After payload `ap_realloc` (which returns new ptr in rax), the tag realloc used `rax` (the pointer!) as the size instead of the actual new_size from the stack. |
| 48 | +**Fix**: `mov rsi, [rsp]` to load new_size from stack instead of `mov rsi, rax`. |
| 49 | + |
| 50 | +### 10. list_inplace_multiply dispatched to sq_repeat (opcodes_misc.asm) |
| 51 | +**Symptom**: `s *= 10` changed list identity (`id(s)` differed after) |
| 52 | +**Root cause**: NB_INPLACE_MULTIPLY was dispatched to sq_repeat (creates new object) instead of nb_imul/sq_inplace_repeat (mutates in place). |
| 53 | +**Fix**: Remove NB_INPLACE_MULTIPLY from the left-seq sq_repeat dispatch; let it fall through to nb_imul. |
| 54 | + |
| 55 | +### 11. List extended slice self-assignment corruption (list.asm) |
| 56 | +**Symptom**: `a[::-1] = a` gives `[0, 1, 1, 0]` instead of `[3, 2, 1, 0]` |
| 57 | +**Root cause**: Extended slice loop reads from source and writes to target simultaneously; when source == target, writes corrupt subsequent reads. |
| 58 | +**Fix**: Detect self-assignment (`r12 == rbx`), create shallow copy via `list_copy()`, use copy as source. Store copy in LAS_TEMP for cleanup. |
| 59 | + |
| 60 | +### 12. List step-1 slice self-assignment corruption (list.asm) |
| 61 | +**Symptom**: `a[1:] = a` gives `[1, 1, 1, 1, 1, 1]` instead of `[1, 1, 2, 3, 4, 5]` |
| 62 | +**Root cause**: Same as #11 but for the step-1 path. Also: `call list_copy` clobbered `rcx` (old_len). |
| 63 | +**Fix**: Self-assignment detection + `list_copy` + save/restore `rcx` around the call. |
| 64 | + |
| 65 | +### 13. Exhausted list iterator not marked (iter.asm, opcodes_build.asm) |
| 66 | +**Symptom**: `list(exhausted_iter)` picks up newly-appended elements |
| 67 | +**Root cause**: `FOR_ITER_LIST` specialized opcode didn't mark iterators as exhausted (set `it_seq = NULL`) when done. Only the generic `list_iter_next` path did. |
| 68 | +**Fix**: Add `it_seq = NULL` + DECREF in `FOR_ITER_LIST`'s `.fil_exhausted` path. Guard dealloc against NULL `it_seq`. |
| 69 | + |
| 70 | +### 14. tuple.index() ignoring start/stop parameters (methods.asm) |
| 71 | +**Symptom**: Same as #6 but for tuples |
| 72 | +**Fix**: Same pattern as list.index - check nargs, extract start/stop. |
| 73 | + |
| 74 | +## New Infrastructure Added |
| 75 | +- `list_copy()` - standalone shallow copy function |
| 76 | +- `ALWAYS_EQ`, `NEVER_EQ`, `C_RECURSION_LIMIT` in `lib/test/support/__init__.py` |
| 77 | +- `lib/test/seq_tests.py` - adapted base test class |
| 78 | +- `lib/test/list_tests.py` - adapted list test class |
0 commit comments