Skip to content

Panic in hash wrapping when user hash returns oversized int #6550

@jackfromeast

Description

@jackfromeast

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::VirtualMachine::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)

Metadata

Metadata

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions