Skip to content

Commit 0d0f6fb

Browse files
committed
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
1 parent 25732c1 commit 0d0f6fb

1 file changed

Lines changed: 12 additions & 13 deletions

File tree

crates/stdlib/src/csv.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,8 +1060,17 @@ mod _csv {
10601060
impl IterNext for Reader {
10611061
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
10621062
let generation = zelf.state.lock().generation;
1063-
let string = raise_if_stop!(zelf.iter.next(vm)?);
1064-
let string = string.downcast::<PyStr>().map_err(|obj| {
1063+
let string_obj = raise_if_stop!(zelf.iter.next(vm)?);
1064+
let mut state = zelf.state.lock();
1065+
if state.generation != generation {
1066+
return Err(new_csv_error(
1067+
vm,
1068+
"iterator has already advanced the reader",
1069+
));
1070+
}
1071+
state.generation += 1;
1072+
1073+
let string = string_obj.downcast::<PyStr>().map_err(|obj| {
10651074
new_csv_error(
10661075
vm,
10671076
format!(
@@ -1071,15 +1080,7 @@ mod _csv {
10711080
)
10721081
})?;
10731082
let input = string.as_bytes();
1074-
let mut state = zelf.state.lock();
1075-
if state.generation != generation {
1076-
return Err(new_csv_error(
1077-
vm,
1078-
"iterator has already advanced the reader",
1079-
));
1080-
}
10811083
if input.is_empty() || input.starts_with(b"\n") {
1082-
state.generation += 1;
10831084
return Ok(PyIterReturn::Return(vm.ctx.new_list(vec![]).into()));
10841085
}
10851086
let ReadState {
@@ -1089,7 +1090,7 @@ mod _csv {
10891090
skipinitialspace,
10901091
delimiter,
10911092
line_num,
1092-
generation,
1093+
generation: _,
10931094
} = &mut *state;
10941095

10951096
let mut input_offset = 0;
@@ -1100,7 +1101,6 @@ mod _csv {
11001101
if zelf.dialect.quoting == QuoteStyle::None && zelf.dialect.escapechar.is_some() {
11011102
let out = read_quote_none_record(input, zelf.dialect, field_limit, vm)?;
11021103
*line_num += 1;
1103-
*generation += 1;
11041104
return Ok(PyIterReturn::Return(vm.ctx.new_list(out).into()));
11051105
}
11061106

@@ -1188,7 +1188,6 @@ mod _csv {
11881188
// out.pop();
11891189
// }
11901190
*line_num += 1;
1191-
*generation += 1;
11921191
Ok(PyIterReturn::Return(vm.ctx.new_list(out).into()))
11931192
}
11941193
}

0 commit comments

Comments
 (0)