Skip to content

Commit db331b4

Browse files
authored
[mypyc] Fix reference leak in mypyc bytes concatenation (#21469)
Fixes [Mypyc #1192](mypyc/mypyc#1192) Fix a reference leak in mypyc-generated code for `bytes + bytes`. The `bytes + bytes` primitive for `CPyBytes_Concat` was marked as stealing the left operand with `steals=[True, False]`. However, `CPyBytes_Concat` does not steal or decref either argument; it allocates and returns a new `bytes` object. Because of this mismatch, the refcount pass skipped emitting a `dec_ref` for owned left operands. For code such as: ```python return (1).to_bytes(4, "big") + (2).to_bytes(4, "big") ```` the generated IR decrefed only the right operand after `CPyBytes_Concat`, leaving the owned left operand alive. Chained concatenations amplified the leak because intermediate concat results could also become leaked left operands. This PR removes the incorrect `steals=[True, False]` annotation from the `bytes + bytes` primitive, so both operands are treated as non-stolen and owned operands are decrefed normally after the call. The tests verify that the generated IR decrefs both owned operands after `CPyBytes_Concat`.
1 parent d62a508 commit db331b4

2 files changed

Lines changed: 28 additions & 1 deletion

File tree

mypyc/primitives/bytes_ops.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@
6464
return_type=bytes_rprimitive,
6565
c_function_name="CPyBytes_Concat",
6666
error_kind=ERR_MAGIC,
67-
steals=[True, False],
6867
)
6968

7069
# bytes * int

mypyc/test-data/refcount.test

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2125,3 +2125,31 @@ L3:
21252125
r8 = box(None, 1)
21262126
inc_ref r8
21272127
return r8
2128+
2129+
[case testBytesConcatRefcount]
2130+
def f(a: bytes, b: bytes) -> bytes:
2131+
return b"1" + a + b
2132+
[out]
2133+
def f(a, b):
2134+
a, b, r0, r1, r2 :: bytes
2135+
L0:
2136+
r0 = b'1'
2137+
r1 = CPyBytes_Concat(r0, a)
2138+
r2 = CPyBytes_Concat(r1, b)
2139+
dec_ref r1
2140+
return r2
2141+
2142+
[case testChainedBytesConcatRefcount]
2143+
def f(a: bytes, b: bytes, c: bytes) -> bytes:
2144+
return b"1" + a + b + c
2145+
[out]
2146+
def f(a, b, c):
2147+
a, b, c, r0, r1, r2, r3 :: bytes
2148+
L0:
2149+
r0 = b'1'
2150+
r1 = CPyBytes_Concat(r0, a)
2151+
r2 = CPyBytes_Concat(r1, b)
2152+
dec_ref r1
2153+
r3 = CPyBytes_Concat(r2, c)
2154+
dec_ref r2
2155+
return r3

0 commit comments

Comments
 (0)