{"record":{"id":"566442613bf80068","repo":"PyO3/pyo3","slug":"overflow-adding-py-vectorcall-arguments-offset","errorCode":null,"errorMessage":"overflow adding PY_VECTORCALL_ARGUMENTS_OFFSET","messagePattern":"overflow adding PY_VECTORCALL_ARGUMENTS_OFFSET","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/types/tuple.rs","lineNumber":981,"sourceCode":"    }\n\n    // SAFETY: array is layout compatible with *const *mut crate::PyObject\n    // and does not steal the bound reference.\n    #[cfg(RustPython)]\n    unsafe {\n        ffi::PyTuple_FromArray(array.as_ptr().cast(), N.try_into().expect(\"0 < N <= 12\"))\n            .assume_owned(py)\n            .cast_into_unchecked()\n    }\n}\n\n/// Add `PY_VECTORCALL_ARGUMENTS_OFFSET` to the given number, checking for overflow at compile time.\n///\n/// Guarantees that we don't accidentally overflow a `size_t` should this get changed in the future.\n#[cfg(all(not(any(PyPy, GraalPy)), any(not(Py_LIMITED_API), Py_3_12)))]\nconst fn with_vectorcall_arguments_offset(n: size_t) -> size_t {\n    n.checked_add(ffi::PY_VECTORCALL_ARGUMENTS_OFFSET)\n        .expect(\"overflow adding PY_VECTORCALL_ARGUMENTS_OFFSET\")\n}\n\ntuple_conversion!(1, (0, T0));\ntuple_conversion!(2, (0, T0), (1, T1));\ntuple_conversion!(3, (0, T0), (1, T1), (2, T2));\ntuple_conversion!(4, (0, T0), (1, T1), (2, T2), (3, T3));\ntuple_conversion!(5, (0, T0), (1, T1), (2, T2), (3, T3), (4, T4));\ntuple_conversion!(6, (0, T0), (1, T1), (2, T2), (3, T3), (4, T4), (5, T5));\ntuple_conversion!(\n    7,\n    (0, T0),\n    (1, T1),\n    (2, T2),\n    (3, T3),\n    (4, T4),\n    (5, T5),\n    (6, T6)\n);","sourceCodeStart":963,"sourceCodeEnd":999,"githubUrl":"https://github.com/PyO3/pyo3/blob/ac9b6899d348be4d54614d060dea53a645a12e36/src/types/tuple.rs#L963-L999","documentation":"This panic comes from a compile-time-checked `checked_add` in pyo3's `with_vectorcall_arguments_offset`, which adds CPython's `PY_VECTORCALL_ARGUMENTS_OFFSET` flag to the argument count before calling the vectorcall protocol. The `.expect` fires only if `n + PY_VECTORCALL_ARGUMENTS_OFFSET` overflows `size_t`, i.e. the caller supplied an astronomically large argument count. It is a defensive guard against future `size_t` narrowing, not a condition normal code reaches.","triggerScenarios":"Calling into pyo3's tuple conversion macro (`tuple_conversion!`) or vectorcall machinery with an argument count `n` so large that adding `PY_VECTORCALL_ARGUMENTS_OFFSET` (a huge sentinel bit value like `1 << 63` on 64-bit) overflows `size_t` — effectively only with counts near `usize::MAX`.","commonSituations":"Essentially never hit in real applications; would require a bug elsewhere (uninitialized/garbage length, integer underflow producing a near-`usize::MAX` count) passed into pyo3's C-call wrappers, or a future change of the offset constant tripping the compile-time guard.","solutions":["Audit the code computing the number of arguments for underflow or garbage values before it reaches pyo3's call machinery","Reduce the number of arguments being passed (chunk large argument lists)","If seen in a pyo3 version update, report it — it indicates the PY_VECTORCALL_ARGUMENTS_OFFSET constant changed incompatibly with the guard"],"exampleFix":"// before\nlet n = args.len() as size_t; // possibly garbage\npyo3::ffi::call with with_vectorcall_arguments_offset(n)\n// after\nassert!(args.len() < isize::MAX as usize, \"implausible argument count\");\nlet n = args.len() as size_t;","handlingStrategy":"validation","validationCode":"fn safe_vectorcall_count(n: usize) -> Option<usize> {\n    n.checked_add(1usize << 63) // PY_VECTORCALL_ARGUMENTS_OFFSET on 64-bit\n}\n// call site: assert!(safe_vectorcall_count(args.len()).is_some(), \"argument count overflows\");","typeGuard":"fn plausible_arg_count(n: usize) -> bool {\n    n < (1usize << 62)\n}","tryCatchPattern":null,"preventionTips":["Never compute argument counts from unchecked arithmetic (subtraction can underflow to near usize::MAX)","Clamp or assert argument list lengths before crossing into pyo3's ffi call layer","Run debug builds: checked_add panics surface immediately in tests","If upgrading pyo3/CPython, re-run tests covering very large call signatures"],"tags":["rust","pyo3","overflow","ffi"],"backgroundTag":"integer-overflow-panic","analyzedSha":"ac9b6899d348be4d54614d060dea53a645a12e36","analyzedAt":"2026-09-05T09:20:35.319Z","contentChangedAt":"2026-09-05T09:20:35.319Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}