From d45a28032b7b2816276521ce3ccdb8f9620f4733 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 18 Nov 2025 14:30:50 +0900 Subject: [PATCH 1/5] Split standard_b64encode_impl --- Modules/_base64/src/lib.rs | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/Modules/_base64/src/lib.rs b/Modules/_base64/src/lib.rs index f308a61daf301e..8fbb1c2fde0ce8 100644 --- a/Modules/_base64/src/lib.rs +++ b/Modules/_base64/src/lib.rs @@ -83,7 +83,7 @@ struct BorrowedBuffer { } impl BorrowedBuffer { - unsafe fn from_object(obj: *mut PyObject) -> Result { + fn from_object(obj: &mut PyObject) -> Result { let mut view = MaybeUninit::::uninit(); if unsafe { PyObject_GetBuffer(obj, view.as_mut_ptr(), PYBUF_SIMPLE) } != 0 { return Err(()); @@ -110,6 +110,9 @@ impl Drop for BorrowedBuffer { } } +/// # Safety +/// `module` must be a valid pointer of PyObject representing the module. +/// `args` must be a valid pointer to an array of valid PyObject pointers with length `nargs`. #[unsafe(no_mangle)] pub unsafe extern "C" fn standard_b64encode( _module: *mut PyObject, @@ -123,13 +126,21 @@ pub unsafe extern "C" fn standard_b64encode( c"standard_b64encode() takes exactly one argument".as_ptr(), ); } - return ptr::null_mut(); } - let source = unsafe { *args }; - let buffer = match unsafe { BorrowedBuffer::from_object(source) } { + let source = unsafe { &mut **args }; + + // Safe cast by Safety + match standard_b64encode_impl(source) { + Ok(result) => result, + Err(_) => ptr::null_mut(), + } +} + +fn standard_b64encode_impl(source: &mut PyObject) -> Result<*mut PyObject, ()> { + let buffer = match BorrowedBuffer::from_object(source) { Ok(buf) => buf, - Err(_) => return ptr::null_mut(), + Err(_) => return Err(()), }; let view_len = buffer.len(); @@ -140,8 +151,9 @@ pub unsafe extern "C" fn standard_b64encode( c"standard_b64encode() argument has negative length".as_ptr(), ); } - return ptr::null_mut(); + return Err(()); } + let input_len = view_len as usize; let input = unsafe { slice::from_raw_parts(buffer.as_ptr(), input_len) }; @@ -149,21 +161,19 @@ pub unsafe extern "C" fn standard_b64encode( unsafe { PyErr_NoMemory(); } - return ptr::null_mut(); + return Err(()); }; if output_len > isize::MAX as usize { unsafe { PyErr_NoMemory(); } - return ptr::null_mut(); + return Err(()); } - let result = unsafe { - PyBytes_FromStringAndSize(ptr::null(), output_len as Py_ssize_t) - }; + let result = unsafe { PyBytes_FromStringAndSize(ptr::null(), output_len as Py_ssize_t) }; if result.is_null() { - return ptr::null_mut(); + return Err(()); } let dest_ptr = unsafe { PyBytes_AsString(result) }; @@ -171,13 +181,13 @@ pub unsafe extern "C" fn standard_b64encode( unsafe { Py_DecRef(result); } - return ptr::null_mut(); + return Err(()); } let dest = unsafe { slice::from_raw_parts_mut(dest_ptr.cast::(), output_len) }; let written = encode_into(input, dest); debug_assert_eq!(written, output_len); - result + Ok(result) } #[unsafe(no_mangle)] From 5d45c51b9b4bbc860b4521493e8b81e5f31709aa Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Wed, 19 Nov 2025 12:35:43 +0900 Subject: [PATCH 2/5] Wrap PyObject inner UnsafeCell --- Modules/_base64/src/lib.rs | 8 ++++---- Modules/cpython-sys/build.rs | 1 + Modules/cpython-sys/src/lib.rs | 12 ++++++++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Modules/_base64/src/lib.rs b/Modules/_base64/src/lib.rs index 8fbb1c2fde0ce8..a6f7fd94800fe0 100644 --- a/Modules/_base64/src/lib.rs +++ b/Modules/_base64/src/lib.rs @@ -83,9 +83,9 @@ struct BorrowedBuffer { } impl BorrowedBuffer { - fn from_object(obj: &mut PyObject) -> Result { + fn from_object(obj: &PyObject) -> Result { let mut view = MaybeUninit::::uninit(); - if unsafe { PyObject_GetBuffer(obj, view.as_mut_ptr(), PYBUF_SIMPLE) } != 0 { + if unsafe { PyObject_GetBuffer(obj as *const _ as *mut _, view.as_mut_ptr(), PYBUF_SIMPLE) } != 0 { return Err(()); } Ok(Self { @@ -128,7 +128,7 @@ pub unsafe extern "C" fn standard_b64encode( } } - let source = unsafe { &mut **args }; + let source = unsafe { &**args }; // Safe cast by Safety match standard_b64encode_impl(source) { @@ -137,7 +137,7 @@ pub unsafe extern "C" fn standard_b64encode( } } -fn standard_b64encode_impl(source: &mut PyObject) -> Result<*mut PyObject, ()> { +fn standard_b64encode_impl(source: &PyObject) -> Result<*mut PyObject, ()> { let buffer = match BorrowedBuffer::from_object(source) { Ok(buf) => buf, Err(_) => return Err(()), diff --git a/Modules/cpython-sys/build.rs b/Modules/cpython-sys/build.rs index 680066c4fd5e9d..8256e2fc93cd03 100644 --- a/Modules/cpython-sys/build.rs +++ b/Modules/cpython-sys/build.rs @@ -55,6 +55,7 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat .allowlist_type("_?Py.*") .allowlist_var("_?Py.*") .blocklist_type("^PyMethodDef$") + .blocklist_type("PyObject") .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .generate() .expect("Unable to generate bindings"); diff --git a/Modules/cpython-sys/src/lib.rs b/Modules/cpython-sys/src/lib.rs index ed1d68eedd600a..d6db5a819588f7 100644 --- a/Modules/cpython-sys/src/lib.rs +++ b/Modules/cpython-sys/src/lib.rs @@ -56,6 +56,10 @@ pub const _Py_STATIC_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = #[cfg(not(target_pointer_width = "64"))] pub const _Py_STATIC_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = 7u32 << 28; +#[repr(C)] +pub struct PyObject(std::cell::UnsafeCell<_object>); + + #[repr(C)] pub union PyMethodDefFuncPointer { pub PyCFunction: unsafe extern "C" fn(slf: *mut PyObject, args: *mut PyObject) -> *mut PyObject, @@ -113,18 +117,18 @@ unsafe impl Send for PyMethodDef {} #[cfg(py_gil_disabled)] pub const PyObject_HEAD_INIT: PyObject = { - let mut obj: PyObject = unsafe { std::mem::MaybeUninit::zeroed().assume_init() }; + let mut obj: _object = unsafe { std::mem::MaybeUninit::zeroed().assume_init() }; obj.ob_flags = _Py_STATICALLY_ALLOCATED_FLAG as _; - obj + PyObject(std::cell::UnsafeCell::new(obj)) }; #[cfg(not(py_gil_disabled))] -pub const PyObject_HEAD_INIT: PyObject = PyObject { +pub const PyObject_HEAD_INIT: PyObject = PyObject(std::cell::UnsafeCell::new(_object { __bindgen_anon_1: _object__bindgen_ty_1 { ob_refcnt_full: _Py_STATIC_IMMORTAL_INITIAL_REFCNT as i64, }, ob_type: std::ptr::null_mut(), -}; +})); pub const PyModuleDef_HEAD_INIT: PyModuleDef_Base = PyModuleDef_Base { ob_base: PyObject_HEAD_INIT, From 3862bae7353ed8e2babe43a074859635ec2948a8 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Wed, 19 Nov 2025 16:42:04 +0900 Subject: [PATCH 3/5] PyObject::as_raw --- Modules/_base64/src/lib.rs | 2 +- Modules/cpython-sys/src/lib.rs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Modules/_base64/src/lib.rs b/Modules/_base64/src/lib.rs index a6f7fd94800fe0..22286684c50f3a 100644 --- a/Modules/_base64/src/lib.rs +++ b/Modules/_base64/src/lib.rs @@ -85,7 +85,7 @@ struct BorrowedBuffer { impl BorrowedBuffer { fn from_object(obj: &PyObject) -> Result { let mut view = MaybeUninit::::uninit(); - if unsafe { PyObject_GetBuffer(obj as *const _ as *mut _, view.as_mut_ptr(), PYBUF_SIMPLE) } != 0 { + if unsafe { PyObject_GetBuffer(obj.as_raw(), view.as_mut_ptr(), PYBUF_SIMPLE) } != 0 { return Err(()); } Ok(Self { diff --git a/Modules/cpython-sys/src/lib.rs b/Modules/cpython-sys/src/lib.rs index d6db5a819588f7..fdd17d2dc1c326 100644 --- a/Modules/cpython-sys/src/lib.rs +++ b/Modules/cpython-sys/src/lib.rs @@ -59,6 +59,13 @@ pub const _Py_STATIC_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = 7u32 << 28; #[repr(C)] pub struct PyObject(std::cell::UnsafeCell<_object>); +impl PyObject { + #[inline] + pub fn as_raw(&self) -> *mut Self { + self.0.get() as *mut Self + } +} + #[repr(C)] pub union PyMethodDefFuncPointer { From eab7de0e51e75c60bf36575b7cb68f97f1cd1d6c Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Wed, 19 Nov 2025 16:44:00 +0900 Subject: [PATCH 4/5] add missing null --- Modules/_base64/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_base64/src/lib.rs b/Modules/_base64/src/lib.rs index 22286684c50f3a..49fd7930045c0b 100644 --- a/Modules/_base64/src/lib.rs +++ b/Modules/_base64/src/lib.rs @@ -126,6 +126,7 @@ pub unsafe extern "C" fn standard_b64encode( c"standard_b64encode() takes exactly one argument".as_ptr(), ); } + return ptr::null_mut(); } let source = unsafe { &**args }; From b591b22cda44f8d87526e8a55fb02236fd678114 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Wed, 19 Nov 2025 16:57:33 +0900 Subject: [PATCH 5/5] transparent --- Modules/cpython-sys/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/cpython-sys/src/lib.rs b/Modules/cpython-sys/src/lib.rs index fdd17d2dc1c326..9a3c46b34d8c36 100644 --- a/Modules/cpython-sys/src/lib.rs +++ b/Modules/cpython-sys/src/lib.rs @@ -56,7 +56,7 @@ pub const _Py_STATIC_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = #[cfg(not(target_pointer_width = "64"))] pub const _Py_STATIC_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = 7u32 << 28; -#[repr(C)] +#[repr(transparent)] pub struct PyObject(std::cell::UnsafeCell<_object>); impl PyObject {