From bc44ab54df5a9b8e2762d734141be6311f37276a Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 6 Jul 2026 16:55:52 +0100 Subject: [PATCH 1/3] Add todo implementations for address-taken libcc2rs functions --- libcc2rs/src/alloc.rs | 22 +++ rules/cstdlib/tgt_refcount.rs | 20 +++ rules/cstring/tgt_refcount.rs | 4 + rules/src/modules.rs | 2 + tests/unit/malloc_realloc_free.c | 2 +- .../unit/out/refcount/malloc_realloc_free.rs | 151 ++++++++++++++++++ .../out/refcount/union_flex_array_member.rs | 145 +++++++++++++++++ tests/unit/union_flex_array_member.c | 2 +- 8 files changed, 346 insertions(+), 2 deletions(-) create mode 100644 rules/cstdlib/tgt_refcount.rs create mode 100644 tests/unit/out/refcount/malloc_realloc_free.rs create mode 100644 tests/unit/out/refcount/union_flex_array_member.rs diff --git a/libcc2rs/src/alloc.rs b/libcc2rs/src/alloc.rs index 4c643188..52b6d6c5 100644 --- a/libcc2rs/src/alloc.rs +++ b/libcc2rs/src/alloc.rs @@ -1,6 +1,28 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. +use crate::rc::{AnyPtr, Ptr}; + +pub fn malloc_refcount(_a0: usize) -> AnyPtr { + todo!("malloc_refcount") +} + +pub fn free_refcount(_a0: AnyPtr) { + todo!("free_refcount") +} + +pub fn realloc_refcount(_a0: AnyPtr, _a1: usize) -> AnyPtr { + todo!("realloc_refcount") +} + +pub fn calloc_refcount(_a0: usize, _a1: usize) -> AnyPtr { + todo!("calloc_refcount") +} + +pub fn strdup_refcount(_a0: Ptr) -> Ptr { + todo!("strdup_refcount") +} + /// # Safety /// /// Same contract as C's `malloc`. diff --git a/rules/cstdlib/tgt_refcount.rs b/rules/cstdlib/tgt_refcount.rs new file mode 100644 index 00000000..060dd534 --- /dev/null +++ b/rules/cstdlib/tgt_refcount.rs @@ -0,0 +1,20 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +use libcc2rs::*; + +fn f2(a0: AnyPtr) { + libcc2rs::free_refcount(a0) +} + +fn f3(a0: usize) -> AnyPtr { + libcc2rs::malloc_refcount(a0) +} + +fn f4(a0: AnyPtr, a1: usize) -> AnyPtr { + libcc2rs::realloc_refcount(a0, a1) +} + +fn f5(a0: usize, a1: usize) -> AnyPtr { + libcc2rs::calloc_refcount(a0, a1) +} diff --git a/rules/cstring/tgt_refcount.rs b/rules/cstring/tgt_refcount.rs index e2f34fcc..5d2eb6c4 100644 --- a/rules/cstring/tgt_refcount.rs +++ b/rules/cstring/tgt_refcount.rs @@ -25,3 +25,7 @@ fn f4(a0: AnyPtr, a1: AnyPtr, a2: usize) -> AnyPtr { unsafe fn f7(a0: Ptr) -> usize { a0.to_string_iterator().count() } + +fn f15(a0: Ptr) -> Ptr { + libcc2rs::strdup_refcount(a0) +} diff --git a/rules/src/modules.rs b/rules/src/modules.rs index b05d690c..53112eed 100644 --- a/rules/src/modules.rs +++ b/rules/src/modules.rs @@ -26,6 +26,8 @@ pub mod carray_tgt_refcount; pub mod carray_tgt_unsafe; #[path = r#"../cmath/tgt_unsafe.rs"#] pub mod cmath_tgt_unsafe; +#[path = r#"../cstdlib/tgt_refcount.rs"#] +pub mod cstdlib_tgt_refcount; #[path = r#"../cstdlib/tgt_unsafe.rs"#] pub mod cstdlib_tgt_unsafe; #[path = r#"../cstring/tgt_refcount.rs"#] diff --git a/tests/unit/malloc_realloc_free.c b/tests/unit/malloc_realloc_free.c index 643f8d95..9edb3097 100644 --- a/tests/unit/malloc_realloc_free.c +++ b/tests/unit/malloc_realloc_free.c @@ -1,4 +1,4 @@ -// no-compile: refcount +// panic: refcount #include #include diff --git a/tests/unit/out/refcount/malloc_realloc_free.rs b/tests/unit/out/refcount/malloc_realloc_free.rs new file mode 100644 index 00000000..d07cebe5 --- /dev/null +++ b/tests/unit/out/refcount/malloc_realloc_free.rs @@ -0,0 +1,151 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let mut __do_while = true; + 'loop_: while __do_while || (0 != 0) { + __do_while = false; + let p: Value> = Rc::new(RefCell::new( + libcc2rs::malloc_refcount(::std::mem::size_of::()).reinterpret_cast::(), + )); + (*p.borrow()).write(42); + assert!((((((*p.borrow()).read()) == 42) as i32) != 0)); + libcc2rs::free_refcount(((*p.borrow()).clone() as Ptr).to_any()); + let arr: Value> = Rc::new(RefCell::new( + libcc2rs::malloc_refcount( + (4_usize).wrapping_mul((::std::mem::size_of::() as usize)), + ) + .reinterpret_cast::(), + )); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((((*i.borrow()) < 4) as i32) != 0) { + let __rhs = ((*i.borrow()) * 10); + (*arr.borrow()).offset((*i.borrow()) as isize).write(__rhs); + (*i.borrow_mut()).postfix_inc(); + } + assert!((((((*arr.borrow()).offset((0) as isize).read()) == 0) as i32) != 0)); + assert!((((((*arr.borrow()).offset((3) as isize).read()) == 30) as i32) != 0)); + libcc2rs::free_refcount(((*arr.borrow()).clone() as Ptr).to_any()); + let grow: Value> = Rc::new(RefCell::new( + libcc2rs::malloc_refcount( + (2_usize).wrapping_mul((::std::mem::size_of::() as usize)), + ) + .reinterpret_cast::(), + )); + (*grow.borrow()).offset((0) as isize).write(1); + (*grow.borrow()).offset((1) as isize).write(2); + let __rhs = libcc2rs::realloc_refcount( + ((*grow.borrow()).clone() as Ptr).to_any(), + (4_usize).wrapping_mul((::std::mem::size_of::() as usize)), + ) + .reinterpret_cast::(); + (*grow.borrow_mut()) = __rhs; + (*grow.borrow()).offset((2) as isize).write(3); + (*grow.borrow()).offset((3) as isize).write(4); + assert!((((((*grow.borrow()).offset((0) as isize).read()) == 1) as i32) != 0)); + assert!((((((*grow.borrow()).offset((1) as isize).read()) == 2) as i32) != 0)); + assert!((((((*grow.borrow()).offset((2) as isize).read()) == 3) as i32) != 0)); + assert!((((((*grow.borrow()).offset((3) as isize).read()) == 4) as i32) != 0)); + libcc2rs::free_refcount(((*grow.borrow()).clone() as Ptr).to_any()); + let zeros: Value> = Rc::new(RefCell::new( + libcc2rs::calloc_refcount(4_usize, ::std::mem::size_of::()) + .reinterpret_cast::(), + )); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((((*i.borrow()) < 4) as i32) != 0) { + assert!( + (((((*zeros.borrow()).offset((*i.borrow()) as isize).read()) == 0) as i32) != 0) + ); + (*i.borrow_mut()).postfix_inc(); + } + libcc2rs::free_refcount(((*zeros.borrow()).clone() as Ptr).to_any()); + } + let pmalloc: Value AnyPtr>> = + Rc::new(RefCell::new(FnPtr:: AnyPtr>::new( + libcc2rs::malloc_refcount, + ))); + let pfree: Value> = Rc::new(RefCell::new(FnPtr::::new( + libcc2rs::free_refcount, + ))); + let prealloc: Value AnyPtr>> = + Rc::new(RefCell::new(FnPtr:: AnyPtr>::new( + libcc2rs::realloc_refcount, + ))); + let pcalloc: Value AnyPtr>> = + Rc::new(RefCell::new(FnPtr:: AnyPtr>::new( + libcc2rs::calloc_refcount, + ))); + let mut __do_while = true; + 'loop_: while __do_while || (0 != 0) { + __do_while = false; + let p: Value> = Rc::new(RefCell::new( + ({ (*(*pmalloc.borrow()))(::std::mem::size_of::()) }).reinterpret_cast::(), + )); + (*p.borrow()).write(42); + assert!((((((*p.borrow()).read()) == 42) as i32) != 0)); + ({ (*(*pfree.borrow()))(((*p.borrow()).clone() as Ptr).to_any()) }); + let arr: Value> = Rc::new(RefCell::new( + ({ + (*(*pmalloc.borrow()))( + (4_usize).wrapping_mul((::std::mem::size_of::() as usize)), + ) + }) + .reinterpret_cast::(), + )); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((((*i.borrow()) < 4) as i32) != 0) { + let __rhs = ((*i.borrow()) * 10); + (*arr.borrow()).offset((*i.borrow()) as isize).write(__rhs); + (*i.borrow_mut()).postfix_inc(); + } + assert!((((((*arr.borrow()).offset((0) as isize).read()) == 0) as i32) != 0)); + assert!((((((*arr.borrow()).offset((3) as isize).read()) == 30) as i32) != 0)); + ({ (*(*pfree.borrow()))(((*arr.borrow()).clone() as Ptr).to_any()) }); + let grow: Value> = Rc::new(RefCell::new( + ({ + (*(*pmalloc.borrow()))( + (2_usize).wrapping_mul((::std::mem::size_of::() as usize)), + ) + }) + .reinterpret_cast::(), + )); + (*grow.borrow()).offset((0) as isize).write(1); + (*grow.borrow()).offset((1) as isize).write(2); + let __rhs = ({ + (*(*prealloc.borrow()))( + ((*grow.borrow()).clone() as Ptr).to_any(), + (4_usize).wrapping_mul((::std::mem::size_of::() as usize)), + ) + }) + .reinterpret_cast::(); + (*grow.borrow_mut()) = __rhs; + (*grow.borrow()).offset((2) as isize).write(3); + (*grow.borrow()).offset((3) as isize).write(4); + assert!((((((*grow.borrow()).offset((0) as isize).read()) == 1) as i32) != 0)); + assert!((((((*grow.borrow()).offset((1) as isize).read()) == 2) as i32) != 0)); + assert!((((((*grow.borrow()).offset((2) as isize).read()) == 3) as i32) != 0)); + assert!((((((*grow.borrow()).offset((3) as isize).read()) == 4) as i32) != 0)); + ({ (*(*pfree.borrow()))(((*grow.borrow()).clone() as Ptr).to_any()) }); + let zeros: Value> = Rc::new(RefCell::new( + ({ (*(*pcalloc.borrow()))(4_usize, ::std::mem::size_of::()) }) + .reinterpret_cast::(), + )); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((((*i.borrow()) < 4) as i32) != 0) { + assert!( + (((((*zeros.borrow()).offset((*i.borrow()) as isize).read()) == 0) as i32) != 0) + ); + (*i.borrow_mut()).postfix_inc(); + } + ({ (*(*pfree.borrow()))(((*zeros.borrow()).clone() as Ptr).to_any()) }); + } + return 0; +} diff --git a/tests/unit/out/refcount/union_flex_array_member.rs b/tests/unit/out/refcount/union_flex_array_member.rs new file mode 100644 index 00000000..6bf1107e --- /dev/null +++ b/tests/unit/out/refcount/union_flex_array_member.rs @@ -0,0 +1,145 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub struct anon_0 { + __bytes: Value>, +} +impl anon_0 { + pub fn bytes(&self) -> Ptr { + (self.__bytes.as_pointer() as Ptr).reinterpret_cast() + } + pub fn aligner(&self) -> Ptr { + (self.__bytes.as_pointer() as Ptr).reinterpret_cast() + } +} +impl Clone for anon_0 { + fn clone(&self) -> Self { + anon_0 { + __bytes: Rc::new(RefCell::new(self.__bytes.borrow().clone())), + } + } +} +impl Default for anon_0 { + fn default() -> Self { + anon_0 { + __bytes: Rc::new(RefCell::new(Box::from([0u8; 8]))), + } + } +} +impl ByteRepr for anon_0 { + fn byte_size() -> usize { + 8 + } + fn to_bytes(&self, buf: &mut [u8]) { + buf.copy_from_slice(&self.__bytes.borrow()); + } + fn from_bytes(buf: &[u8]) -> Self { + anon_0 { + __bytes: Rc::new(RefCell::new(Box::from(buf))), + } + } +} +#[derive(Default)] +pub struct node { + pub len: Value, + pub pos: Value, + pub x: Value, +} +impl Clone for node { + fn clone(&self) -> Self { + Self { + len: Rc::new(RefCell::new((*self.len.borrow()).clone())), + pos: Rc::new(RefCell::new((*self.pos.borrow()).clone())), + x: Rc::new(RefCell::new((*self.x.borrow()).clone())), + } + } +} +impl ByteRepr for node { + fn byte_size() -> usize { + 24 + } + fn to_bytes(&self, buf: &mut [u8]) { + (*self.len.borrow()).to_bytes(&mut buf[0..8]); + (*self.pos.borrow()).to_bytes(&mut buf[8..16]); + (*self.x.borrow()).to_bytes(&mut buf[16..24]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + len: Rc::new(RefCell::new(::from_bytes(&buf[0..8]))), + pos: Rc::new(RefCell::new(::from_bytes(&buf[8..16]))), + x: Rc::new(RefCell::new(::from_bytes(&buf[16..24]))), + } + } +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let tail_size: Value = Rc::new(RefCell::new(32_usize)); + let n: Value> = Rc::new(RefCell::new( + libcc2rs::malloc_refcount( + ((24usize as u64).wrapping_add(((*tail_size.borrow()) as u64)) as usize), + ) + .reinterpret_cast::(), + )); + (*(*(*n.borrow()).upgrade().deref()).len.borrow_mut()) = (*tail_size.borrow()); + let i: Value = Rc::new(RefCell::new(0_usize)); + 'loop_: while ((((*i.borrow()) < (*tail_size.borrow())) as i32) != 0) { + let __rhs = (((*i.borrow()) & 255_usize) as u8); + ((*(*(*n.borrow()).upgrade().deref()).x.borrow()) + .bytes() + .reinterpret_cast::() as Ptr) + .offset((*i.borrow()) as isize) + .write(__rhs); + (*i.borrow_mut()).postfix_inc(); + } + let i: Value = Rc::new(RefCell::new(0_usize)); + 'loop_: while ((((*i.borrow()) < (*tail_size.borrow())) as i32) != 0) { + assert!( + ((({ + let _lhs = ((((*(*(*n.borrow()).upgrade().deref()).x.borrow()) + .bytes() + .reinterpret_cast::() as Ptr) + .offset((*i.borrow()) as isize) + .read()) as i32); + _lhs == ((((*i.borrow()) & 255_usize) as u8) as i32) + }) as i32) + != 0) + ); + (*i.borrow_mut()).postfix_inc(); + } + let p: Value> = Rc::new(RefCell::new( + (((*(*(*n.borrow()).upgrade().deref()).x.borrow()) + .bytes() + .reinterpret_cast::() as Ptr) + .offset((10) as isize)), + )); + assert!(((((((*p.borrow()).read()) as i32) == 10) as i32) != 0)); + (*p.borrow()).write(170_u8); + assert!( + (((((((*(*(*n.borrow()).upgrade().deref()).x.borrow()) + .bytes() + .reinterpret_cast::() as Ptr::) + .offset((10) as isize) + .read()) as i32) + == 170) as i32) + != 0) + ); + (*(*(*n.borrow()).upgrade().deref()).pos.borrow_mut()) = 20_usize; + let q: Value> = Rc::new(RefCell::new( + (((*(*(*n.borrow()).upgrade().deref()).x.borrow()) + .bytes() + .reinterpret_cast::() as Ptr) + .offset((*(*(*n.borrow()).upgrade().deref()).pos.borrow()) as isize)), + )); + assert!(((((((*q.borrow()).read()) as i32) == 20) as i32) != 0)); + (*q.borrow()).write(187_u8); + assert!(((((((*q.borrow()).read()) as i32) == 187) as i32) != 0)); + libcc2rs::free_refcount(((*n.borrow()).clone() as Ptr).to_any()); + return 0; +} diff --git a/tests/unit/union_flex_array_member.c b/tests/unit/union_flex_array_member.c index 7a41d456..68247d11 100644 --- a/tests/unit/union_flex_array_member.c +++ b/tests/unit/union_flex_array_member.c @@ -1,4 +1,4 @@ -// no-compile: refcount +// panic: refcount #include #include #include From 7374e08bd1012d1c9d87fa04bf83e37d2835b888 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Jul 2026 09:47:25 +0100 Subject: [PATCH 2/3] Add malloc/calloc/free implementations --- libcc2rs/src/alloc.rs | 32 +++++++++++++++++++--------- libcc2rs/src/rc.rs | 42 +++++++++++++++++++++---------------- libcc2rs/src/reinterpret.rs | 24 ++++++++++++++++++++- 3 files changed, 69 insertions(+), 29 deletions(-) diff --git a/libcc2rs/src/alloc.rs b/libcc2rs/src/alloc.rs index 52b6d6c5..9c90a63e 100644 --- a/libcc2rs/src/alloc.rs +++ b/libcc2rs/src/alloc.rs @@ -3,24 +3,36 @@ use crate::rc::{AnyPtr, Ptr}; -pub fn malloc_refcount(_a0: usize) -> AnyPtr { - todo!("malloc_refcount") +pub fn malloc_refcount(a0: usize) -> AnyPtr { + Ptr::alloc_array(vec![0u8; a0].into_boxed_slice()).to_any() } -pub fn free_refcount(_a0: AnyPtr) { - todo!("free_refcount") +pub fn free_refcount(a0: AnyPtr) { + if a0.is_null() { + return; + } + a0.reinterpret_cast::().delete_array(); } -pub fn realloc_refcount(_a0: AnyPtr, _a1: usize) -> AnyPtr { - todo!("realloc_refcount") +pub fn realloc_refcount(a0: AnyPtr, a1: usize) -> AnyPtr { + if a0.is_null() { + return malloc_refcount(a1); + } + let __new = Ptr::alloc_array(vec![0u8; a1].into_boxed_slice()).to_any(); + let __n = a1.min(a0.reinterpret_cast::().len()); + __new.memcpy(&a0, __n); + a0.reinterpret_cast::().delete_array(); + __new } -pub fn calloc_refcount(_a0: usize, _a1: usize) -> AnyPtr { - todo!("calloc_refcount") +pub fn calloc_refcount(a0: usize, a1: usize) -> AnyPtr { + Ptr::alloc_array(vec![0u8; a0.wrapping_mul(a1)].into_boxed_slice()).to_any() } -pub fn strdup_refcount(_a0: Ptr) -> Ptr { - todo!("strdup_refcount") +pub fn strdup_refcount(a0: Ptr) -> Ptr { + let mut bytes: Vec = a0.to_c_string_iterator().collect(); + bytes.push(0); + Ptr::alloc_array(bytes.into_boxed_slice()) } /// # Safety diff --git a/libcc2rs/src/rc.rs b/libcc2rs/src/rc.rs index f13be101..a1320a38 100644 --- a/libcc2rs/src/rc.rs +++ b/libcc2rs/src/rc.rs @@ -210,32 +210,38 @@ impl Ptr { #[inline] pub fn delete(&self) { - assert_eq!(self.offset, 0, "ub: invalid delete"); - let weak = match self.kind { - PtrKind::HeapSingle(ref weak) => weak, + match &self.kind { + PtrKind::HeapSingle(weak) => { + assert_eq!(self.offset, 0, "ub: invalid delete"); + assert_eq!(Weak::strong_count(weak), 1, "ub: invalid delete"); + unsafe { + let strong = weak.upgrade().expect("ub: dangling pointer"); + Rc::from_raw(Rc::as_ptr(&strong)); + } + assert_eq!(Weak::strong_count(weak), 0, "ub: double free"); + } + PtrKind::Reinterpreted(data) => data.alloc.delete(), + PtrKind::Null => {} _ => panic!("ub: invalid delete"), - }; - assert_eq!(Weak::strong_count(weak), 1, "ub: invalid delete"); - unsafe { - let strong = weak.upgrade().expect("ub: dangling pointer"); - Rc::from_raw(Rc::as_ptr(&strong)); } - assert_eq!(Weak::strong_count(weak), 0, "ub: strong count is not zero"); } #[inline] pub fn delete_array(&self) { - assert_eq!(self.offset, 0, "ub: invalid delete"); - let weak = match self.kind { - PtrKind::HeapArray(ref weak) => weak, + match &self.kind { + PtrKind::HeapArray(weak) => { + assert_eq!(self.offset, 0, "ub: invalid delete"); + assert_eq!(Weak::strong_count(weak), 1, "ub: invalid delete"); + unsafe { + let strong = weak.upgrade().expect("ub: dangling pointer"); + Rc::from_raw(Rc::as_ptr(&strong)); + } + assert_eq!(Weak::strong_count(weak), 0, "ub: double free"); + } + PtrKind::Reinterpreted(data) => data.alloc.delete(), + PtrKind::Null => {} _ => panic!("ub: invalid delete"), - }; - assert_eq!(Weak::strong_count(weak), 1, "ub: invalid delete"); - unsafe { - let strong = weak.upgrade().expect("ub: dangling pointer"); - Rc::from_raw(Rc::as_ptr(&strong)); } - assert_eq!(Weak::strong_count(weak), 0, "ub: strong count is not zero"); } #[inline] diff --git a/libcc2rs/src/reinterpret.rs b/libcc2rs/src/reinterpret.rs index b496f8b2..a9240160 100644 --- a/libcc2rs/src/reinterpret.rs +++ b/libcc2rs/src/reinterpret.rs @@ -1,7 +1,10 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. -use std::{cell::RefCell, rc::Weak}; +use std::{ + cell::RefCell, + rc::{Rc, Weak}, +}; pub trait ByteRepr: 'static { fn byte_size() -> usize @@ -100,6 +103,7 @@ pub trait OriginalAlloc { fn total_byte_len(&self) -> usize; // Stable address used for pointer equality across PtrKind variants. fn address(&self) -> usize; + fn delete(&self); } // Read bytes starting at `byte_offset` from a slice of S elements into `buf`. @@ -165,6 +169,15 @@ impl OriginalAlloc for SingleOriginalAlloc { fn address(&self) -> usize { self.weak.as_ptr() as usize } + + fn delete(&self) { + assert_eq!(Weak::strong_count(&self.weak), 1, "ub: invalid delete"); + unsafe { + let strong = self.weak.upgrade().expect("ub: dangling pointer"); + Rc::from_raw(Rc::as_ptr(&strong)); + } + assert_eq!(Weak::strong_count(&self.weak), 0, "ub: double free"); + } } pub(crate) trait AsSlice { @@ -219,4 +232,13 @@ impl OriginalAlloc for SliceOriginalAlloc { fn address(&self) -> usize { self.weak.as_ptr() as usize } + + fn delete(&self) { + assert_eq!(Weak::strong_count(&self.weak), 1, "ub: invalid delete"); + unsafe { + let strong = self.weak.upgrade().expect("ub: dangling pointer"); + Rc::from_raw(Rc::as_ptr(&strong)); + } + assert_eq!(Weak::strong_count(&self.weak), 0, "ub: double free"); + } } From ebe318fd4c3a03e1c57627eacb5c7c41a24300fc Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Jul 2026 11:40:06 +0100 Subject: [PATCH 3/3] Add AnyPtr::is_null --- libcc2rs/src/rc.rs | 4 ++++ tests/unit/malloc_realloc_free.c | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libcc2rs/src/rc.rs b/libcc2rs/src/rc.rs index a1320a38..5c490eb4 100644 --- a/libcc2rs/src/rc.rs +++ b/libcc2rs/src/rc.rs @@ -1115,6 +1115,10 @@ impl AnyPtr { } self.ptr.as_bytes().reinterpret_cast::() } + + pub fn is_null(&self) -> bool { + self.ptr.is_null() + } } impl PartialEq for AnyPtr { diff --git a/tests/unit/malloc_realloc_free.c b/tests/unit/malloc_realloc_free.c index 9edb3097..e69cc8b9 100644 --- a/tests/unit/malloc_realloc_free.c +++ b/tests/unit/malloc_realloc_free.c @@ -1,4 +1,3 @@ -// panic: refcount #include #include