Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d99e2c9

Browse files
authored
librt: Fix aliasing related segfault with vec append (#21303)
If there is another reference to the vec buffer that is grown during append, the other buffer could see NULL pointers and trigger a segfault. Now we only clear the buffer if there are no other references.
1 parent 67d35af commit d99e2c9

6 files changed

Lines changed: 66 additions & 4 deletions

File tree

mypyc/lib-rt/vecs/vec_nested.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,15 @@ VecNested VecNested_Append(VecNested vec, VecNestedBufItem x) {
265265
memcpy(new.buf->items, vec.buf->items, sizeof(VecNestedBufItem) * vec.len);
266266
// TODO: How to safely represent deleted items?
267267
memset(new.buf->items + vec.len, 0, sizeof(VecNestedBufItem) * (new_size - vec.len));
268-
// Clear the items in the old vec. We avoid reference count manipulation.
269-
memset(vec.buf->items, 0, sizeof(VecNestedBufItem) * vec.len);
268+
if (Py_REFCNT(vec.buf) > 1) {
269+
// Other references to old buffer exist; INCREF items in new buffer
270+
// so old buffer keeps valid references for aliases.
271+
for (Py_ssize_t i = 0; i < vec.len; i++)
272+
Py_XINCREF(new.buf->items[i].buf);
273+
} else {
274+
// No aliases; transfer ownership by clearing old buffer items.
275+
memset(vec.buf->items, 0, sizeof(VecNestedBufItem) * vec.len);
276+
}
270277
new.buf->items[vec.len] = x;
271278
new.len = vec.len + 1;
272279
VEC_DECREF(vec);

mypyc/lib-rt/vecs/vec_t.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,15 @@ VecT VecT_Append(VecT vec, PyObject *x, size_t item_type) {
279279
// Copy items to new vec.
280280
memcpy(new.buf->items, vec.buf->items, sizeof(PyObject *) * vec.len);
281281
memset(new.buf->items + vec.len, 0, sizeof(PyObject *) * (new_size - vec.len));
282-
// Clear the items in the old vec. We avoid reference count manipulation.
283-
memset(vec.buf->items, 0, sizeof(PyObject *) * vec.len);
282+
if (Py_REFCNT(vec.buf) > 1) {
283+
// Other references to old buffer exist; INCREF items in new buffer
284+
// so old buffer keeps valid references for aliases.
285+
for (Py_ssize_t i = 0; i < vec.len; i++)
286+
Py_XINCREF(new.buf->items[i]);
287+
} else {
288+
// No aliases; transfer ownership by clearing old buffer items.
289+
memset(vec.buf->items, 0, sizeof(PyObject *) * vec.len);
290+
}
284291
new.buf->items[vec.len] = x;
285292
new.len = vec.len + 1;
286293
VEC_DECREF(vec);

mypyc/test-data/run-vecs-nested-interp.test

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,3 +455,15 @@ def test_contains_deeply_nested() -> None:
455455
assert vec[vec[str]]([vec[str](['a'])]) in v
456456
assert vec[vec[str]]([vec[str](['b'])]) not in v
457457
assert vec[vec[str]]() not in v
458+
459+
def test_append_with_alias() -> None:
460+
v = vec[vec[i64]]()
461+
v = append(v, vec[i64]([1, 2, 3]))
462+
alias = v
463+
v = append(v, vec[i64]([4, 5]))
464+
v = append(v, vec[i64]([6]))
465+
v = append(v, vec[i64]([7]))
466+
assert alias[0] == vec[i64]([1, 2, 3])
467+
assert len(alias) == 1
468+
assert v[0] == vec[i64]([1, 2, 3])
469+
assert len(v) == 4

mypyc/test-data/run-vecs-nested.test

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,3 +506,15 @@ def test_union_of_nested_vecs() -> None:
506506
with assertRaises(TypeError):
507507
a5: Any = vec[vec[vec[i64]]]()
508508
len_union(a5)
509+
510+
def test_append_with_alias() -> None:
511+
v = vec[vec[i64]]()
512+
v = append(v, vec[i64]([1, 2, 3]))
513+
alias = v
514+
v = append(v, vec[i64]([4, 5]))
515+
v = append(v, vec[i64]([6]))
516+
v = append(v, vec[i64]([7]))
517+
assert alias[0] == vec[i64]([1, 2, 3])
518+
assert len(alias) == 1
519+
assert v[0] == vec[i64]([1, 2, 3])
520+
assert len(v) == 4

mypyc/test-data/run-vecs-t-interp.test

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,18 @@ def test_pop_index() -> None:
533533
assert item == '15'
534534
assert v == vec[str]()
535535

536+
def test_append_with_alias() -> None:
537+
v = vec[str]()
538+
v = append(v, 'a')
539+
alias = v
540+
v = append(v, 'b')
541+
v = append(v, 'c')
542+
v = append(v, 'd')
543+
assert alias[0] == 'a'
544+
assert len(alias) == 1
545+
assert v[0] == 'a'
546+
assert len(v) == 4
547+
536548
[file helpers.py]
537549
# Test helper classes
538550

mypyc/test-data/run-vecs-t.test

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,3 +436,15 @@ def test_union_of_vecs() -> None:
436436
with assertRaises(TypeError):
437437
a4: Any = vec[Foo]()
438438
len_union(a4)
439+
440+
def test_append_with_alias() -> None:
441+
v = vec[str]()
442+
v = append(v, 'a')
443+
alias = v
444+
v = append(v, 'b')
445+
v = append(v, 'c')
446+
v = append(v, 'd')
447+
assert alias[0] == 'a'
448+
assert len(alias) == 1
449+
assert v[0] == 'a'
450+
assert len(v) == 4

0 commit comments

Comments
 (0)