Skip to content

Commit a81f97d

Browse files
authored
Merge pull request RustPython#3280 from DimitrisJim/bad_indexing
Fix deadlocks when slicing mutable sequences.
2 parents e732669 + 0539d33 commit a81f97d

File tree

8 files changed

+207
-161
lines changed

8 files changed

+207
-161
lines changed

Lib/test/test_xml_etree.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2515,7 +2515,6 @@ def __index__(self):
25152515
e.append(ET.Element('child'))
25162516
e[0:10:X()] # shouldn't crash
25172517

2518-
@unittest.skip("TODO: RUSTPYTHON, hangs")
25192518
def test_ass_subscr(self):
25202519
# Issue #27863
25212520
class X:

extra_tests/snippets/bad_indexing.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
""" Test that indexing ops don't hang when an object with a mutating
2+
__index__ is used."""
3+
from testutils import assert_raises
4+
from array import array
5+
6+
7+
class BadIndex:
8+
def __index__(self):
9+
# assign ourselves, makes it easy to re-use with
10+
# all mutable collections.
11+
e[:] = e
12+
return 1
13+
14+
15+
def run_setslice():
16+
with assert_raises(IndexError):
17+
e[BadIndex()] = 42
18+
e[BadIndex():0:-1] = e
19+
e[0:BadIndex():1] = e
20+
e[0:10:BadIndex()] = e
21+
22+
23+
def run_delslice():
24+
del e[BadIndex():0:-1]
25+
del e[0:BadIndex():1]
26+
del e[0:10:BadIndex()]
27+
28+
# Check types
29+
instances = [list(), bytearray(), array('b')]
30+
for e in instances:
31+
run_setslice()
32+
run_delslice()

stdlib/src/array.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mod array {
1313
use crate::vm::{
1414
builtins::{
1515
PyByteArray, PyBytes, PyBytesRef, PyDictRef, PyFloat, PyInt, PyIntRef, PyList,
16-
PyListRef, PySliceRef, PyStr, PyStrRef, PyTupleRef, PyTypeRef,
16+
PyListRef, PyStr, PyStrRef, PyTupleRef, PyTypeRef,
1717
},
1818
class_or_notimplemented,
1919
function::{
@@ -23,7 +23,7 @@ mod array {
2323
BufferInternal, BufferOptions, BufferResizeGuard, PyBuffer, PyIterReturn,
2424
PyMappingMethods,
2525
},
26-
sliceable::{saturate_index, PySliceableSequence, PySliceableSequenceMut, SequenceIndex},
26+
sliceable::{PySliceableSequence, PySliceableSequenceMut, SaturatedSlice, SequenceIndex},
2727
slots::{
2828
AsBuffer, AsMapping, Comparable, Iterable, IteratorIterable, PyComparisonOp,
2929
SlotConstructor, SlotIterator,
@@ -122,14 +122,14 @@ mod array {
122122

123123
fn insert(
124124
&mut self,
125-
i: usize,
125+
i: isize,
126126
obj: PyObjectRef,
127127
vm: &VirtualMachine
128128
) -> PyResult<()> {
129129
match self {
130130
$(ArrayContentType::$n(v) => {
131131
let val = <$t>::try_into_from_object(vm, obj)?;
132-
v.insert(i, val);
132+
v.insert(v.saturate_index(i), val);
133133
})*
134134
}
135135
Ok(())
@@ -276,7 +276,10 @@ mod array {
276276
v.get(pos_index).unwrap().into_pyresult(vm)
277277
}
278278
SequenceIndex::Slice(slice) => {
279-
let elements = v.get_slice_items(vm, &slice)?;
279+
// TODO: Use interface similar to set/del item. This can
280+
// still hang.
281+
let slice = slice.to_saturated(vm)?;
282+
let elements = v.get_slice_items(vm, slice)?;
280283
let array: PyArray = ArrayContentType::$n(elements).into();
281284
Ok(array.into_object(vm))
282285
}
@@ -287,13 +290,13 @@ mod array {
287290

288291
fn setitem_by_slice(
289292
&mut self,
290-
slice: PySliceRef,
293+
slice: SaturatedSlice,
291294
items: &ArrayContentType,
292295
vm: &VirtualMachine
293296
) -> PyResult<()> {
294297
match self {
295298
$(Self::$n(elements) => if let ArrayContentType::$n(items) = items {
296-
elements.set_slice_items(vm, &slice, items)
299+
elements.set_slice_items(vm, slice, items)
297300
} else {
298301
Err(vm.new_type_error(
299302
"bad argument type for built-in operation".to_owned()
@@ -304,13 +307,13 @@ mod array {
304307

305308
fn setitem_by_slice_no_resize(
306309
&mut self,
307-
slice: PySliceRef,
310+
slice: SaturatedSlice,
308311
items: &ArrayContentType,
309312
vm: &VirtualMachine
310313
) -> PyResult<()> {
311314
match self {
312315
$(Self::$n(elements) => if let ArrayContentType::$n(items) = items {
313-
elements.set_slice_items_no_resize(vm, &slice, items)
316+
elements.set_slice_items_no_resize(vm, slice, items)
314317
} else {
315318
Err(vm.new_type_error(
316319
"bad argument type for built-in operation".to_owned()
@@ -350,12 +353,12 @@ mod array {
350353

351354
fn delitem_by_slice(
352355
&mut self,
353-
slice: PySliceRef,
356+
slice: SaturatedSlice,
354357
vm: &VirtualMachine
355358
) -> PyResult<()> {
356359
match self {
357360
$(ArrayContentType::$n(elements) => {
358-
elements.delete_slice(vm, &slice)
361+
elements.delete_slice(vm, slice)
359362
})*
360363
}
361364
}
@@ -895,7 +898,6 @@ mod array {
895898
vm: &VirtualMachine,
896899
) -> PyResult<()> {
897900
let mut w = zelf.try_resizable(vm)?;
898-
let i = saturate_index(i, w.len());
899901
w.insert(i, x, vm)
900902
}
901903

@@ -983,6 +985,7 @@ mod array {
983985
match SequenceIndex::try_from_object_for(vm, needle, "array")? {
984986
SequenceIndex::Int(i) => zelf.write().setitem_by_idx(i, obj, vm),
985987
SequenceIndex::Slice(slice) => {
988+
let slice = slice.to_saturated(vm)?;
986989
let cloned;
987990
let guard;
988991
let items = if zelf.is(&obj) {
@@ -1015,7 +1018,10 @@ mod array {
10151018
fn delitem(zelf: PyRef<Self>, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
10161019
match SequenceIndex::try_from_object_for(vm, needle, "array")? {
10171020
SequenceIndex::Int(i) => zelf.try_resizable(vm)?.delitem_by_idx(i, vm),
1018-
SequenceIndex::Slice(slice) => zelf.try_resizable(vm)?.delitem_by_slice(slice, vm),
1021+
SequenceIndex::Slice(slice) => {
1022+
let slice = slice.to_saturated(vm)?;
1023+
zelf.try_resizable(vm)?.delitem_by_slice(slice, vm)
1024+
}
10191025
}
10201026
}
10211027

vm/src/builtins/bytearray.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,16 +182,17 @@ impl PyByteArray {
182182
}
183183
}
184184
SequenceIndex::Slice(slice) => {
185+
let slice = slice.to_saturated(vm)?;
185186
let items = if zelf.is(&value) {
186187
zelf.borrow_buf().to_vec()
187188
} else {
188189
bytes_from_object(vm, &value)?
189190
};
190191
if let Ok(mut w) = zelf.try_resizable(vm) {
191-
w.elements.set_slice_items(vm, &slice, items.as_slice())
192+
w.elements.set_slice_items(vm, slice, items.as_slice())
192193
} else {
193194
zelf.borrow_buf_mut()
194-
.set_slice_items_no_resize(vm, &slice, items.as_slice())
195+
.set_slice_items_no_resize(vm, slice, items.as_slice())
195196
}
196197
}
197198
}
@@ -212,17 +213,21 @@ impl PyByteArray {
212213

213214
#[pymethod(magic)]
214215
pub fn delitem(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
215-
let elements = &mut self.try_resizable(vm)?.elements;
216216
match SequenceIndex::try_from_object_for(vm, needle, Self::NAME)? {
217217
SequenceIndex::Int(int) => {
218+
let elements = &mut self.try_resizable(vm)?.elements;
218219
if let Some(idx) = elements.wrap_index(int) {
219220
elements.remove(idx);
220221
Ok(())
221222
} else {
222223
Err(vm.new_index_error("index out of range".to_owned()))
223224
}
224225
}
225-
SequenceIndex::Slice(slice) => elements.delete_slice(vm, &slice),
226+
SequenceIndex::Slice(slice) => {
227+
let slice = slice.to_saturated(vm)?;
228+
let elements = &mut self.try_resizable(vm)?.elements;
229+
elements.delete_slice(vm, slice)
230+
}
226231
}
227232
}
228233

vm/src/builtins/list.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,9 @@ impl PyList {
213213
fn setslice(&self, slice: PySliceRef, sec: ArgIterable, vm: &VirtualMachine) -> PyResult<()> {
214214
let items: Result<Vec<PyObjectRef>, _> = sec.iter(vm)?.collect();
215215
let items = items?;
216+
let slice = slice.to_saturated(vm)?;
216217
let mut elements = self.borrow_vec_mut();
217-
elements.set_slice_items(vm, &slice, items.as_slice())
218+
elements.set_slice_items(vm, slice, items.as_slice())
218219
}
219220

220221
#[pymethod(magic)]
@@ -373,7 +374,8 @@ impl PyList {
373374
}
374375

375376
fn delslice(&self, slice: PySliceRef, vm: &VirtualMachine) -> PyResult<()> {
376-
self.borrow_vec_mut().delete_slice(vm, &slice)
377+
let slice = slice.to_saturated(vm)?;
378+
self.borrow_vec_mut().delete_slice(vm, slice)
377379
}
378380

379381
#[pymethod]

vm/src/builtins/memory.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
bytesinner::bytes_to_hex,
1010
function::{FuncArgs, IntoPyObject, OptionalArg},
1111
protocol::{BufferInternal, BufferOptions, PyBuffer, PyMappingMethods},
12-
sliceable::{convert_slice, wrap_index, SequenceIndex},
12+
sliceable::{wrap_index, SaturatedSlice, SequenceIndex},
1313
slots::{AsBuffer, AsMapping, Comparable, Hashable, PyComparisonOp, SlotConstructor},
1414
stdlib::pystruct::FormatSpec,
1515
utils::Either,
@@ -253,7 +253,8 @@ impl PyMemoryView {
253253
fn getitem_by_slice(zelf: PyRef<Self>, slice: PySliceRef, vm: &VirtualMachine) -> PyResult {
254254
// slicing a memoryview return a new memoryview
255255
let len = zelf.buffer.options.len;
256-
let (range, step, is_negative_step) = convert_slice(&slice, len, vm)?;
256+
let (range, step, is_negative_step) =
257+
SaturatedSlice::with_slice(&slice, vm)?.adjust_indices(len);
257258
let abs_step = step.unwrap();
258259
let step = if is_negative_step {
259260
-(abs_step as isize)
@@ -393,7 +394,8 @@ impl PyMemoryView {
393394
return diff_err();
394395
}
395396

396-
let (range, step, is_negative_step) = convert_slice(&slice, zelf.buffer.options.len, vm)?;
397+
let (range, step, is_negative_step) =
398+
SaturatedSlice::with_slice(&slice, vm)?.adjust_indices(zelf.buffer.options.len);
397399

398400
let bytes = items.to_contiguous();
399401
assert_eq!(bytes.len(), len * itemsize);

0 commit comments

Comments
 (0)