-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Open
Description
What happened?
Calling hash() on an object whose __hash__ returns an oversized integer (e.g., 1 << 63) causes a panic in hash_wrapper. The wrapper attempts to convert the BigInt to i64, then falls back to % u64::MAX, but that remainder can still be outside i64 and leads to unwrap() on None instead of raising a Python exception or normalizing safely.
Proof of Concept:
class Evil:
def __hash__(self):
return 1 << 63
hash(Evil())Affected Versions
| RustPython Version | Status | Exit Code |
|---|---|---|
Python 3.13.0alpha (heads/main-dirty:21300f689, Dec 13 2025, 22:16:49) [RustPython 0.4.0 with rustc 1.90.0-nightly (11ad40bb8 2025-06-28)] |
Panic | 1 |
Vulnerable Code
fn hash_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
let hash_obj = vm.call_special_method(zelf, identifier!(vm, __hash__), ())?;
let py_int = hash_obj
.downcast_ref::<PyInt>()
.ok_or_else(|| vm.new_type_error("__hash__ method should return an integer"))?;
let big_int = py_int.as_bigint();
let hash: PyHash = big_int
.to_i64()
.unwrap_or_else(|| (big_int % BigInt::from(u64::MAX)).to_i64().unwrap()); // Bug: u64::MAX should be i64::Max
Ok(hash)
}Rust Output
thread 'main' panicked at crates/vm/src/types/slot.rs:369:72:
called `Option::unwrap()` on a `None` value
stack backtrace:
0: __rustc::rust_begin_unwind
at /rustc/11ad40bb839ca16f74784b4ab72596ad85587298/library/std/src/panicking.rs:697:5
1: core::panicking::panic_fmt
at /rustc/11ad40bb839ca16f74784b4ab72596ad85587298/library/core/src/panicking.rs:75:14
2: core::panicking::panic
at /rustc/11ad40bb839ca16f74784b4ab72596ad85587298/library/core/src/panicking.rs:145:5
3: core::option::unwrap_failed
at /rustc/11ad40bb839ca16f74784b4ab72596ad85587298/library/core/src/option.rs:2072:5
4: core::option::Option<T>::unwrap
at /home/jackfromeast/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:1005:21
5: rustpython_vm::types::slot::hash_wrapper::{{closure}}
at ./crates/vm/src/types/slot.rs:369:72
6: core::option::Option<T>::unwrap_or_else
at /home/jackfromeast/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:1050:21
7: rustpython_vm::types::slot::hash_wrapper
at ./crates/vm/src/types/slot.rs:369:10
8: rustpython_vm::protocol::object::<impl rustpython_vm::object::core::PyObject>::hash
at ./crates/vm/src/protocol/object.rs:663:20
9: rustpython_vm::stdlib::builtins::builtins::hash
at ./crates/vm/src/stdlib/builtins.rs:440:13
10: core::ops::function::Fn::call
at /home/jackfromeast/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:79:5
11: rustpython_vm::function::builtin::<impl rustpython_vm::function::builtin::sealed::PyNativeFnInternal<(rustpython_vm::function::builtin::OwnedParam<T1>,),R,rustpython_vm::vm::VirtualMachine> for F>::call_
at ./crates/vm/src/function/builtin.rs:126:17
12: <F as rustpython_vm::function::builtin::IntoPyNativeFn<(T,R,VM)>>::call
at ./crates/vm/src/function/builtin.rs:92:14
13: rustpython_vm::function::builtin::into_func::{{closure}}
at ./crates/vm/src/function/builtin.rs:50:40
14: <rustpython_vm::builtins::builtin_func::PyNativeFunction as rustpython_vm::types::slot::Callable>::call
at ./crates/vm/src/builtins/builtin_func.rs:73:9
15: rustpython_vm::types::slot::Callable::slot_call
at ./crates/vm/src/types/slot.rs:1028:9
16: rustpython_vm::protocol::callable::PyCallable::invoke
at ./crates/vm/src/protocol/callable.rs:52:22
17: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call_with_args
at ./crates/vm/src/protocol/callable.rs:33:18
18: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call
at ./crates/vm/src/protocol/callable.rs:22:14
19: rustpython_vm::frame::ExecutingFrame::execute_call
at ./crates/vm/src/frame.rs:1880:30
20: rustpython_vm::frame::ExecutingFrame::execute_instruction
at ./crates/vm/src/frame.rs:667:22
21: rustpython_vm::frame::ExecutingFrame::run
at ./crates/vm/src/frame.rs:372:31
22: rustpython_vm::frame::<impl rustpython_vm::object::core::Py<rustpython_vm::frame::Frame>>::run::{{closure}}
at ./crates/vm/src/frame.rs:247:40
23: rustpython_vm::frame::<impl rustpython_vm::object::core::Py<rustpython_vm::frame::Frame>>::with_exec
at ./crates/vm/src/frame.rs:242:9
24: rustpython_vm::frame::<impl rustpython_vm::object::core::Py<rustpython_vm::frame::Frame>>::run
at ./crates/vm/src/frame.rs:247:14
25: rustpython_vm::vm::VirtualMachine::run_frame::{{closure}}
at ./crates/vm/src/vm/mod.rs:467:44
26: rustpython_vm::vm::VirtualMachine::with_frame::{{closure}}
at ./crates/vm/src/vm/mod.rs:495:26
27: rustpython_vm::vm::VirtualMachine::with_recursion
at ./crates/vm/src/vm/mod.rs:483:22
28: rustpython_vm::vm::VirtualMachine::with_frame
at ./crates/vm/src/vm/mod.rs:493:14
29: rustpython_vm::vm::VirtualMachine::run_frame
at ./crates/vm/src/vm/mod.rs:467:20
30: rustpython_vm::vm::VirtualMac
6308
hine::run_code_obj
at ./crates/vm/src/vm/mod.rs:442:14
31: rustpython_vm::vm::compile::<impl rustpython_vm::vm::VirtualMachine>::run_simple_file
at ./crates/vm/src/vm/compile.rs:93:26
32: rustpython_vm::vm::compile::<impl rustpython_vm::vm::VirtualMachine>::run_any_file
at ./crates/vm/src/vm/compile.rs:57:14
33: rustpython_vm::vm::compile::<impl rustpython_vm::vm::VirtualMachine>::run_script
at ./crates/vm/src/vm/compile.rs:50:14
34: rustpython::run_rustpython
at ./src/lib.rs:231:16
35: rustpython::run::{{closure}}
at ./src/lib.rs:112:41
36: rustpython_vm::vm::interpreter::Interpreter::run::{{closure}}
at ./crates/vm/src/vm/interpreter.rs:103:35
37: rustpython_vm::vm::interpreter::Interpreter::enter::{{closure}}
at ./crates/vm/src/vm/interpreter.rs:72:39
38: scoped_tls::ScopedKey<T>::set
at /home/jackfromeast/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/scoped-tls-1.0.1/src/lib.rs:137:9
39: rustpython_vm::vm::thread::enter_vm::{{closure}}
at ./crates/vm/src/vm/thread.rs:30:20
40: std::thread::local::LocalKey<T>::try_with
at /home/jackfromeast/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:315:12
41: std::thread::local::LocalKey<T>::with
at /home/jackfromeast/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:279:20
42: rustpython_vm::vm::thread::enter_vm
at ./crates/vm/src/vm/thread.rs:27:14
43: rustpython_vm::vm::interpreter::Interpreter::enter
at ./crates/vm/src/vm/interpreter.rs:72:9
44: rustpython_vm::vm::interpreter::Interpreter::run
at ./crates/vm/src/vm/interpreter.rs:103:24
45: rustpython::run
at ./src/lib.rs:112:27
46: rustpython::main
at ./src/main.rs:2:5
47: core::ops::function::FnOnce::call_once
at /home/jackfromeast/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
CPython Output
(No output)
coderabbitai
Metadata
Metadata
Assignees
Labels
No labels