Skip to content

Commit cf3b639

Browse files
authored
Fix panic in select.select() when too many FDs specified (RustPython#7948)
Also: * Add regression test into existing `extra_tests/snippets/stdlib_select.py` * Stop calculating nfds on Windows as it is ignored there Panic: thread 'main' (189598) panicked at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/libc-0.2.186/src/unix/linux_like/mod.rs:1777:9: index out of bounds: the len is 16 but the index is 16 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace thread 'main' (189598) panicked at library/core/src/panicking.rs:225:5: panic in a function that cannot unwind stack backtrace: 0: 0xaaab763a0b88 - <<std[1934960bf7f41d0a]::sys::backtrace::BacktraceLock>::print::DisplayBacktrace as core[f1abae5f1257fe69]::fmt::Display>::fmt 1: 0xaaab75590ff0 - core[f1abae5f1257fe69]::fmt::write 2: 0xaaab763a94fc - <std[1934960bf7f41d0a]::sys::stdio::unix::Stderr as std[1934960bf7f41d0a]::io::Write>::write_fmt 3: 0xaaab7638b714 - std[1934960bf7f41d0a]::panicking::default_hook::{closure#0} 4: 0xaaab7639b288 - std[1934960bf7f41d0a]::panicking::default_hook 5: 0xaaab7639b478 - std[1934960bf7f41d0a]::panicking::panic_with_hook 6: 0xaaab7638b7ec - std[1934960bf7f41d0a]::panicking::panic_handler::{closure#0} 7: 0xaaab76382654 - std[1934960bf7f41d0a]::sys::backtrace::__rust_end_short_backtrace::<std[1934960bf7f41d0a]::panicking::panic_handler::{closure#0}, !> 8: 0xaaab7638c504 - __rustc[b7425922bef61dcf]::rust_begin_unwind 9: 0xaaab754f778c - core[f1abae5f1257fe69]::panicking::panic_nounwind_fmt 10: 0xaaab754f7714 - core[f1abae5f1257fe69]::panicking::panic_nounwind 11: 0xaaab754f786c - core[f1abae5f1257fe69]::panicking::panic_cannot_unwind 12: 0xaaab75a6283c - rustpython_vm::function::builtin::<impl rustpython_vm::function::builtin::sealed::PyNativeFnInternal<(rustpython_vm::function::builtin::OwnedParam<T1>,rustpython_vm::function::builtin::OwnedParam<T2>,rustpython_vm::function::builtin::OwnedParam<T3>,rustpython_vm::function::builtin::OwnedParam<T4>),R,rustpython_vm::vm::VirtualMachine> for F>::call_::h2471c8e242c9b51d 13: 0xaaab75db1e68 - rustpython_vm::types::slot::Callable::slot_call::hd1c1ad0ad14f306b 14: 0xaaab762c0a50 - rustpython_vm::protocol::callable::PyCallable::invoke::h9f6d571fca351ca6 15: 0xaaab75c550e8 - rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call_with_args::hed1f4a61aba2dced 16: 0xaaab762e7c24 - rustpython_vm::frame::ExecutingFrame::execute_call::h0ad3490dd74ed1e3 17: 0xaaab762fed40 - rustpython_vm::frame::ExecutingFrame::run::hcf90f0950fc26812 18: 0xaaab761e6768 - rustpython_vm::vm::VirtualMachine::with_frame::hd49ba6fcdf2422e2 19: 0xaaab75c45398 - rustpython_vm::builtins::function::<impl rustpython_vm::object::core::Py<rustpython_vm::builtins::function::PyFunction>>::invoke_with_locals::h42de3d2316941ce2 20: 0xaaab76132a80 - rustpython_vm::builtins::function::vectorcall_function::h7331cb67b334e867 21: 0xaaab763369d8 - rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::vectorcall::h9019c5d16685c89a 22: 0xaaab762f4b54 - rustpython_vm::frame::ExecutingFrame::execute_call_vectorcall::h120134e11a58c946 23: 0xaaab76302a7c - rustpython_vm::frame::ExecutingFrame::run::hcf90f0950fc26812 24: 0xaaab761e6768 - rustpython_vm::vm::VirtualMachine::with_frame::hd49ba6fcdf2422e2 25: 0xaaab761e7f24 - rustpython_vm::vm::VirtualMachine::run_code_obj::h354618be6e5cc553 26: 0xaaab761e2d18 - rustpython_vm::vm::python_run::file_run::<impl rustpython_vm::vm::VirtualMachine>::run_any_file::h783d3127fbc0b523 27: 0xaaab757d700c - rustpython::run_rustpython::h354efb8d817cefbf 28: 0xaaab757c79e0 - std::thread::local::LocalKey<T>::with::hc9728e249843a926 29: 0xaaab757db860 - rustpython_vm::vm::interpreter::Interpreter::run::h42ac1fe9ed2287a2 30: 0xaaab757d7b30 - rustpython::run::hf14a209db5b4289c 31: 0xaaab757e2eb4 - rustpython::main::h1b59d8e13276ac48 32: 0xaaab757e2eec - std::sys::backtrace::__rust_begin_short_backtrace::h47e4b1f073f2155c 33: 0xaaab757e2ed4 - std::rt::lang_start::{{closure}}::h663a6c3dc7d80101 34: 0xaaab76399fd4 - std[1934960bf7f41d0a]::rt::lang_start_internal 35: 0xaaab757e2f44 - main 36: 0xfffed057655c - __libc_start_call_main 37: 0xfffed057663c - __libc_start_main@@GLIBC_2.34 38: 0xaaab755526f0 - _start 39: 0x0 - <unknown> thread caused non-unwinding panic. aborting. Aborted (core dumped) cargo run --release -- extra_tests/snippets/stdlib_select.py
1 parent 2a16360 commit cf3b639

2 files changed

Lines changed: 49 additions & 6 deletions

File tree

crates/stdlib/src/select.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pub(crate) use decl::module_def;
55
use crate::vm::{
66
PyObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine, builtins::PyListRef,
77
};
8-
use rustpython_host_env::select::{self as host_select, FdSet, RawFd};
8+
use rustpython_host_env::select::{self as host_select, FdSet, RawFd, platform::FD_SETSIZE};
99
use std::io;
1010

1111
#[derive(Traverse)]
@@ -81,8 +81,22 @@ mod decl {
8181

8282
let seq2set = |list: &PyObject| -> PyResult<(Vec<Selectable>, FdSet)> {
8383
let v: Vec<Selectable> = list.try_to_value(vm)?;
84+
85+
let too_many_fds = cfg_select! {
86+
windows => v.len() > FD_SETSIZE as usize,
87+
_ => v.len() > FD_SETSIZE,
88+
};
89+
if too_many_fds {
90+
return Err(vm.new_value_error("too many file descriptors in select()"));
91+
}
92+
8493
let mut fds = FdSet::new();
8594
for fd in &v {
95+
#[cfg(unix)]
96+
if fd.fno as usize >= FD_SETSIZE {
97+
return Err(vm.new_value_error("file descriptor out of range in select()"));
98+
}
99+
86100
fds.insert(fd.fno);
87101
}
88102
Ok((v, fds))
@@ -97,11 +111,15 @@ mod decl {
97111
return Ok((empty.clone(), empty.clone(), empty));
98112
}
99113

100-
let nfds: i32 = [&mut r, &mut w, &mut x]
101-
.iter_mut()
102-
.filter_map(|set| set.highest())
103-
.max()
104-
.map_or(0, |n| n + 1) as _;
114+
let nfds = cfg_select! {
115+
windows => 0, // value is ignored on windows
116+
117+
_ => [&mut r, &mut w, &mut x]
118+
.iter_mut()
119+
.filter_map(|set| set.highest())
120+
.max()
121+
.map_or(0, |n| n + 1) as _,
122+
};
105123

106124
loop {
107125
let mut tv = timeout.map(host_select::sec_to_timeval);

extra_tests/snippets/stdlib_select.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
from testutils import assert_raises
66

7+
TOO_MANY_SELECT_FDS = 4096
8+
79

810
class Nope:
911
pass
@@ -42,3 +44,26 @@ def fileno(self):
4244
assert recvr in rres
4345

4446
assert sendr in wres
47+
48+
# Too many descriptors for select.select()
49+
if sys.platform != "win32":
50+
import resource
51+
52+
soft_max_fds, hard_max_fds = resource.getrlimit(resource.RLIMIT_NOFILE)
53+
if soft_max_fds != resource.RLIM_INFINITY:
54+
# 100 additional fds should be enough for interpreter needs
55+
need_fds = TOO_MANY_SELECT_FDS + 100
56+
57+
soft_max_fds = max(soft_max_fds, need_fds)
58+
if hard_max_fds != resource.RLIM_INFINITY:
59+
assert hard_max_fds >= soft_max_fds, (
60+
"Not enough file descriptors for this test"
61+
)
62+
resource.setrlimit(resource.RLIMIT_NOFILE, (soft_max_fds, hard_max_fds))
63+
sockets = [s for _ in range(TOO_MANY_SELECT_FDS // 2) for s in socket.socketpair()]
64+
assert_raises(ValueError, select.select, sockets, [], [], 0)
65+
del sockets
66+
a, b = socket.socketpair()
67+
# CPython disallows this on *nix systems too.
68+
assert_raises(ValueError, select.select, [a] * TOO_MANY_SELECT_FDS, [], [], 0)
69+
del a, b

0 commit comments

Comments
 (0)