mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
csv: reject reentrant reader advancement (#8324)
* csv: reject reentrant reader advancement Snapshot the reader generation before calling the input iterator. If a nested call completes a record and advances the same reader, reject the outer call with csv.Error instead of continuing with stale parser state. Increment the generation on each successful record path, matching CPython's handling of the existing reentrant-reader regression. Enable that test by removing its expectedFailure marker. Assisted-by: Codex:gpt-5.6-sol * csv: invalidate outer reads after reentry Advance the reader generation immediately after the reentrancy check, before validating or parsing the returned input item. This invalidates the outer call even when a nested read later fails validation or parsing. Remove the per-return generation updates now that the common path advances it exactly once. Assisted-by: Codex:gpt-5.6-sol * csv: generation type conversion(usize to u64) On 32-bit targets (like WebAssembly, which RustPython supports), usize is 32-bit. Csv with over 32-bit rows can raise panic on these platform. type conversion from usize to u64.
This commit is contained in:
1 parent
c7d265e8dd
commit
570258f6cb
2 files changed
+15
-4
No files matched your search
Vendored
-1
@@ -568,7 +568,6 @@ class Test_Csv(unittest.TestCase):
|
||||
self.assertEqual(row, rows[i])
|
||||
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: Error not raised
|
||||
def test_reader_reentrant_iterator(self):
|
||||
# gh-145105: re-entering the reader from the iterator must not crash.
|
||||
class ReentrantIter:
|
||||
|
||||
@@ -411,6 +411,7 @@ mod _csv {
|
||||
skipinitialspace: options.get_skipinitialspace(),
|
||||
delimiter: options.get_delimiter(),
|
||||
line_num: 0,
|
||||
generation: 0,
|
||||
}),
|
||||
dialect: options.result(vm)?,
|
||||
})
|
||||
@@ -961,6 +962,7 @@ mod _csv {
|
||||
skipinitialspace: bool,
|
||||
delimiter: u8,
|
||||
line_num: u64,
|
||||
generation: u64,
|
||||
}
|
||||
|
||||
#[pyclass(no_attr, module = "_csv", name = "reader", traverse)]
|
||||
@@ -1057,8 +1059,18 @@ mod _csv {
|
||||
|
||||
impl IterNext for Reader {
|
||||
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
|
||||
let string = raise_if_stop!(zelf.iter.next(vm)?);
|
||||
let string = string.downcast::<PyStr>().map_err(|obj| {
|
||||
let generation = zelf.state.lock().generation;
|
||||
let string_obj = raise_if_stop!(zelf.iter.next(vm)?);
|
||||
let mut state = zelf.state.lock();
|
||||
if state.generation != generation {
|
||||
return Err(new_csv_error(
|
||||
vm,
|
||||
"iterator has already advanced the reader",
|
||||
));
|
||||
}
|
||||
state.generation += 1;
|
||||
|
||||
let string = string_obj.downcast::<PyStr>().map_err(|obj| {
|
||||
new_csv_error(
|
||||
vm,
|
||||
format!(
|
||||
@@ -1071,7 +1083,6 @@ mod _csv {
|
||||
if input.is_empty() || input.starts_with(b"\n") {
|
||||
return Ok(PyIterReturn::Return(vm.ctx.new_list(vec![]).into()));
|
||||
}
|
||||
let mut state = zelf.state.lock();
|
||||
let ReadState {
|
||||
buffer,
|
||||
output_ends,
|
||||
@@ -1079,6 +1090,7 @@ mod _csv {
|
||||
skipinitialspace,
|
||||
delimiter,
|
||||
line_num,
|
||||
generation: _,
|
||||
} = &mut *state;
|
||||
|
||||
let mut input_offset = 0;
|
||||
|
||||
Reference in new issue
Block a user