Skip to content

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

Closed

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)

Activity

  1. coderabbitai commented on Dec 27, 2025

    @coderabbitai
    Contributor

    📝 CodeRabbit Plan Mode

    Generate an implementation plan and prompts that you can use with your favorite coding agent.

    • Create Plan
    Examples

    🔗 Similar Issues

    Related Issues

    🔗 Related PRs

    #5826 - Remove unnecessary string conversions in error message construction [merged]
    #6102 - lookup slot in hash() [merged]
    #6480 - hash to slot_wrapper [merged]

    👤 Suggested Assignees

    🧪 Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  2. youknowone commented on Dec 28, 2025

    @youknowone
    Member

    Thank you for the report. fixed by #6561

  3. jackfromeast commented on Dec 28, 2025

    @jackfromeast
    Author

    Thank you for the prompt fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions