Skip to content

Commit 2caaa96

Browse files
authored
Add safe malloc/calloc/realloc/free/strdup (#234)
1 parent 6b0264a commit 2caaa96

10 files changed

Lines changed: 408 additions & 21 deletions

File tree

libcc2rs/src/alloc.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,40 @@
11
// Copyright (c) 2022-present INESC-ID.
22
// Distributed under the MIT license that can be found in the LICENSE file.
33

4+
use crate::rc::{AnyPtr, Ptr};
5+
6+
pub fn malloc_refcount(a0: usize) -> AnyPtr {
7+
Ptr::alloc_array(vec![0u8; a0].into_boxed_slice()).to_any()
8+
}
9+
10+
pub fn free_refcount(a0: AnyPtr) {
11+
if a0.is_null() {
12+
return;
13+
}
14+
a0.reinterpret_cast::<u8>().delete_array();
15+
}
16+
17+
pub fn realloc_refcount(a0: AnyPtr, a1: usize) -> AnyPtr {
18+
if a0.is_null() {
19+
return malloc_refcount(a1);
20+
}
21+
let __new = Ptr::alloc_array(vec![0u8; a1].into_boxed_slice()).to_any();
22+
let __n = a1.min(a0.reinterpret_cast::<u8>().len());
23+
__new.memcpy(&a0, __n);
24+
a0.reinterpret_cast::<u8>().delete_array();
25+
__new
26+
}
27+
28+
pub fn calloc_refcount(a0: usize, a1: usize) -> AnyPtr {
29+
Ptr::alloc_array(vec![0u8; a0.wrapping_mul(a1)].into_boxed_slice()).to_any()
30+
}
31+
32+
pub fn strdup_refcount(a0: Ptr<u8>) -> Ptr<u8> {
33+
let mut bytes: Vec<u8> = a0.to_c_string_iterator().collect();
34+
bytes.push(0);
35+
Ptr::alloc_array(bytes.into_boxed_slice())
36+
}
37+
438
/// # Safety
539
///
640
/// Same contract as C's `malloc`.

libcc2rs/src/rc.rs

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -210,32 +210,38 @@ impl<T> Ptr<T> {
210210

211211
#[inline]
212212
pub fn delete(&self) {
213-
assert_eq!(self.offset, 0, "ub: invalid delete");
214-
let weak = match self.kind {
215-
PtrKind::HeapSingle(ref weak) => weak,
213+
match &self.kind {
214+
PtrKind::HeapSingle(weak) => {
215+
assert_eq!(self.offset, 0, "ub: invalid delete");
216+
assert_eq!(Weak::strong_count(weak), 1, "ub: invalid delete");
217+
unsafe {
218+
let strong = weak.upgrade().expect("ub: dangling pointer");
219+
Rc::from_raw(Rc::as_ptr(&strong));
220+
}
221+
assert_eq!(Weak::strong_count(weak), 0, "ub: double free");
222+
}
223+
PtrKind::Reinterpreted(data) => data.alloc.delete(),
224+
PtrKind::Null => {}
216225
_ => panic!("ub: invalid delete"),
217-
};
218-
assert_eq!(Weak::strong_count(weak), 1, "ub: invalid delete");
219-
unsafe {
220-
let strong = weak.upgrade().expect("ub: dangling pointer");
221-
Rc::from_raw(Rc::as_ptr(&strong));
222226
}
223-
assert_eq!(Weak::strong_count(weak), 0, "ub: strong count is not zero");
224227
}
225228

226229
#[inline]
227230
pub fn delete_array(&self) {
228-
assert_eq!(self.offset, 0, "ub: invalid delete");
229-
let weak = match self.kind {
230-
PtrKind::HeapArray(ref weak) => weak,
231+
match &self.kind {
232+
PtrKind::HeapArray(weak) => {
233+
assert_eq!(self.offset, 0, "ub: invalid delete");
234+
assert_eq!(Weak::strong_count(weak), 1, "ub: invalid delete");
235+
unsafe {
236+
let strong = weak.upgrade().expect("ub: dangling pointer");
237+
Rc::from_raw(Rc::as_ptr(&strong));
238+
}
239+
assert_eq!(Weak::strong_count(weak), 0, "ub: double free");
240+
}
241+
PtrKind::Reinterpreted(data) => data.alloc.delete(),
242+
PtrKind::Null => {}
231243
_ => panic!("ub: invalid delete"),
232-
};
233-
assert_eq!(Weak::strong_count(weak), 1, "ub: invalid delete");
234-
unsafe {
235-
let strong = weak.upgrade().expect("ub: dangling pointer");
236-
Rc::from_raw(Rc::as_ptr(&strong));
237244
}
238-
assert_eq!(Weak::strong_count(weak), 0, "ub: strong count is not zero");
239245
}
240246

241247
#[inline]
@@ -1109,6 +1115,10 @@ impl AnyPtr {
11091115
}
11101116
self.ptr.as_bytes().reinterpret_cast::<T>()
11111117
}
1118+
1119+
pub fn is_null(&self) -> bool {
1120+
self.ptr.is_null()
1121+
}
11121122
}
11131123

11141124
impl PartialEq for AnyPtr {

libcc2rs/src/reinterpret.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// Copyright (c) 2022-present INESC-ID.
22
// Distributed under the MIT license that can be found in the LICENSE file.
33

4-
use std::{cell::RefCell, rc::Weak};
4+
use std::{
5+
cell::RefCell,
6+
rc::{Rc, Weak},
7+
};
58

69
pub trait ByteRepr: 'static {
710
fn byte_size() -> usize
@@ -100,6 +103,7 @@ pub trait OriginalAlloc {
100103
fn total_byte_len(&self) -> usize;
101104
// Stable address used for pointer equality across PtrKind variants.
102105
fn address(&self) -> usize;
106+
fn delete(&self);
103107
}
104108

105109
// Read bytes starting at `byte_offset` from a slice of S elements into `buf`.
@@ -165,6 +169,15 @@ impl<T: ByteRepr> OriginalAlloc for SingleOriginalAlloc<T> {
165169
fn address(&self) -> usize {
166170
self.weak.as_ptr() as usize
167171
}
172+
173+
fn delete(&self) {
174+
assert_eq!(Weak::strong_count(&self.weak), 1, "ub: invalid delete");
175+
unsafe {
176+
let strong = self.weak.upgrade().expect("ub: dangling pointer");
177+
Rc::from_raw(Rc::as_ptr(&strong));
178+
}
179+
assert_eq!(Weak::strong_count(&self.weak), 0, "ub: double free");
180+
}
168181
}
169182

170183
pub(crate) trait AsSlice {
@@ -219,4 +232,13 @@ impl<T: AsSlice + 'static> OriginalAlloc for SliceOriginalAlloc<T> {
219232
fn address(&self) -> usize {
220233
self.weak.as_ptr() as usize
221234
}
235+
236+
fn delete(&self) {
237+
assert_eq!(Weak::strong_count(&self.weak), 1, "ub: invalid delete");
238+
unsafe {
239+
let strong = self.weak.upgrade().expect("ub: dangling pointer");
240+
Rc::from_raw(Rc::as_ptr(&strong));
241+
}
242+
assert_eq!(Weak::strong_count(&self.weak), 0, "ub: double free");
243+
}
222244
}

rules/cstdlib/tgt_refcount.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) 2022-present INESC-ID.
2+
// Distributed under the MIT license that can be found in the LICENSE file.
3+
4+
use libcc2rs::*;
5+
6+
fn f2(a0: AnyPtr) {
7+
libcc2rs::free_refcount(a0)
8+
}
9+
10+
fn f3(a0: usize) -> AnyPtr {
11+
libcc2rs::malloc_refcount(a0)
12+
}
13+
14+
fn f4(a0: AnyPtr, a1: usize) -> AnyPtr {
15+
libcc2rs::realloc_refcount(a0, a1)
16+
}
17+
18+
fn f5(a0: usize, a1: usize) -> AnyPtr {
19+
libcc2rs::calloc_refcount(a0, a1)
20+
}

rules/cstring/tgt_refcount.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@ fn f4(a0: AnyPtr, a1: AnyPtr, a2: usize) -> AnyPtr {
2525
unsafe fn f7(a0: Ptr<u8>) -> usize {
2626
a0.to_string_iterator().count()
2727
}
28+
29+
fn f15(a0: Ptr<u8>) -> Ptr<u8> {
30+
libcc2rs::strdup_refcount(a0)
31+
}

rules/src/modules.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ pub mod carray_tgt_refcount;
2626
pub mod carray_tgt_unsafe;
2727
#[path = r#"../cmath/tgt_unsafe.rs"#]
2828
pub mod cmath_tgt_unsafe;
29+
#[path = r#"../cstdlib/tgt_refcount.rs"#]
30+
pub mod cstdlib_tgt_refcount;
2931
#[path = r#"../cstdlib/tgt_unsafe.rs"#]
3032
pub mod cstdlib_tgt_unsafe;
3133
#[path = r#"../cstring/tgt_refcount.rs"#]

tests/unit/malloc_realloc_free.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// no-compile: refcount
21
#include <assert.h>
32
#include <stdlib.h>
43

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
extern crate libcc2rs;
2+
use libcc2rs::*;
3+
use std::cell::RefCell;
4+
use std::collections::BTreeMap;
5+
use std::io::prelude::*;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::AsFd;
8+
use std::rc::{Rc, Weak};
9+
pub fn main() {
10+
std::process::exit(main_0());
11+
}
12+
fn main_0() -> i32 {
13+
let mut __do_while = true;
14+
'loop_: while __do_while || (0 != 0) {
15+
__do_while = false;
16+
let p: Value<Ptr<i32>> = Rc::new(RefCell::new(
17+
libcc2rs::malloc_refcount(::std::mem::size_of::<i32>()).reinterpret_cast::<i32>(),
18+
));
19+
(*p.borrow()).write(42);
20+
assert!((((((*p.borrow()).read()) == 42) as i32) != 0));
21+
libcc2rs::free_refcount(((*p.borrow()).clone() as Ptr<i32>).to_any());
22+
let arr: Value<Ptr<i32>> = Rc::new(RefCell::new(
23+
libcc2rs::malloc_refcount(
24+
(4_usize).wrapping_mul((::std::mem::size_of::<i32>() as usize)),
25+
)
26+
.reinterpret_cast::<i32>(),
27+
));
28+
let i: Value<i32> = Rc::new(RefCell::new(0));
29+
'loop_: while ((((*i.borrow()) < 4) as i32) != 0) {
30+
let __rhs = ((*i.borrow()) * 10);
31+
(*arr.borrow()).offset((*i.borrow()) as isize).write(__rhs);
32+
(*i.borrow_mut()).postfix_inc();
33+
}
34+
assert!((((((*arr.borrow()).offset((0) as isize).read()) == 0) as i32) != 0));
35+
assert!((((((*arr.borrow()).offset((3) as isize).read()) == 30) as i32) != 0));
36+
libcc2rs::free_refcount(((*arr.borrow()).clone() as Ptr<i32>).to_any());
37+
let grow: Value<Ptr<i32>> = Rc::new(RefCell::new(
38+
libcc2rs::malloc_refcount(
39+
(2_usize).wrapping_mul((::std::mem::size_of::<i32>() as usize)),
40+
)
41+
.reinterpret_cast::<i32>(),
42+
));
43+
(*grow.borrow()).offset((0) as isize).write(1);
44+
(*grow.borrow()).offset((1) as isize).write(2);
45+
let __rhs = libcc2rs::realloc_refcount(
46+
((*grow.borrow()).clone() as Ptr<i32>).to_any(),
47+
(4_usize).wrapping_mul((::std::mem::size_of::<i32>() as usize)),
48+
)
49+
.reinterpret_cast::<i32>();
50+
(*grow.borrow_mut()) = __rhs;
51+
(*grow.borrow()).offset((2) as isize).write(3);
52+
(*grow.borrow()).offset((3) as isize).write(4);
53+
assert!((((((*grow.borrow()).offset((0) as isize).read()) == 1) as i32) != 0));
54+
assert!((((((*grow.borrow()).offset((1) as isize).read()) == 2) as i32) != 0));
55+
assert!((((((*grow.borrow()).offset((2) as isize).read()) == 3) as i32) != 0));
56+
assert!((((((*grow.borrow()).offset((3) as isize).read()) == 4) as i32) != 0));
57+
libcc2rs::free_refcount(((*grow.borrow()).clone() as Ptr<i32>).to_any());
58+
let zeros: Value<Ptr<i32>> = Rc::new(RefCell::new(
59+
libcc2rs::calloc_refcount(4_usize, ::std::mem::size_of::<i32>())
60+
.reinterpret_cast::<i32>(),
61+
));
62+
let i: Value<i32> = Rc::new(RefCell::new(0));
63+
'loop_: while ((((*i.borrow()) < 4) as i32) != 0) {
64+
assert!(
65+
(((((*zeros.borrow()).offset((*i.borrow()) as isize).read()) == 0) as i32) != 0)
66+
);
67+
(*i.borrow_mut()).postfix_inc();
68+
}
69+
libcc2rs::free_refcount(((*zeros.borrow()).clone() as Ptr<i32>).to_any());
70+
}
71+
let pmalloc: Value<FnPtr<fn(usize) -> AnyPtr>> =
72+
Rc::new(RefCell::new(FnPtr::<fn(usize) -> AnyPtr>::new(
73+
libcc2rs::malloc_refcount,
74+
)));
75+
let pfree: Value<FnPtr<fn(AnyPtr)>> = Rc::new(RefCell::new(FnPtr::<fn(AnyPtr)>::new(
76+
libcc2rs::free_refcount,
77+
)));
78+
let prealloc: Value<FnPtr<fn(AnyPtr, usize) -> AnyPtr>> =
79+
Rc::new(RefCell::new(FnPtr::<fn(AnyPtr, usize) -> AnyPtr>::new(
80+
libcc2rs::realloc_refcount,
81+
)));
82+
let pcalloc: Value<FnPtr<fn(usize, usize) -> AnyPtr>> =
83+
Rc::new(RefCell::new(FnPtr::<fn(usize, usize) -> AnyPtr>::new(
84+
libcc2rs::calloc_refcount,
85+
)));
86+
let mut __do_while = true;
87+
'loop_: while __do_while || (0 != 0) {
88+
__do_while = false;
89+
let p: Value<Ptr<i32>> = Rc::new(RefCell::new(
90+
({ (*(*pmalloc.borrow()))(::std::mem::size_of::<i32>()) }).reinterpret_cast::<i32>(),
91+
));
92+
(*p.borrow()).write(42);
93+
assert!((((((*p.borrow()).read()) == 42) as i32) != 0));
94+
({ (*(*pfree.borrow()))(((*p.borrow()).clone() as Ptr<i32>).to_any()) });
95+
let arr: Value<Ptr<i32>> = Rc::new(RefCell::new(
96+
({
97+
(*(*pmalloc.borrow()))(
98+
(4_usize).wrapping_mul((::std::mem::size_of::<i32>() as usize)),
99+
)
100+
})
101+
.reinterpret_cast::<i32>(),
102+
));
103+
let i: Value<i32> = Rc::new(RefCell::new(0));
104+
'loop_: while ((((*i.borrow()) < 4) as i32) != 0) {
105+
let __rhs = ((*i.borrow()) * 10);
106+
(*arr.borrow()).offset((*i.borrow()) as isize).write(__rhs);
107+
(*i.borrow_mut()).postfix_inc();
108+
}
109+
assert!((((((*arr.borrow()).offset((0) as isize).read()) == 0) as i32) != 0));
110+
assert!((((((*arr.borrow()).offset((3) as isize).read()) == 30) as i32) != 0));
111+
({ (*(*pfree.borrow()))(((*arr.borrow()).clone() as Ptr<i32>).to_any()) });
112+
let grow: Value<Ptr<i32>> = Rc::new(RefCell::new(
113+
({
114+
(*(*pmalloc.borrow()))(
115+
(2_usize).wrapping_mul((::std::mem::size_of::<i32>() as usize)),
116+
)
117+
})
118+
.reinterpret_cast::<i32>(),
119+
));
120+
(*grow.borrow()).offset((0) as isize).write(1);
121+
(*grow.borrow()).offset((1) as isize).write(2);
122+
let __rhs = ({
123+
(*(*prealloc.borrow()))(
124+
((*grow.borrow()).clone() as Ptr<i32>).to_any(),
125+
(4_usize).wrapping_mul((::std::mem::size_of::<i32>() as usize)),
126+
)
127+
})
128+
.reinterpret_cast::<i32>();
129+
(*grow.borrow_mut()) = __rhs;
130+
(*grow.borrow()).offset((2) as isize).write(3);
131+
(*grow.borrow()).offset((3) as isize).write(4);
132+
assert!((((((*grow.borrow()).offset((0) as isize).read()) == 1) as i32) != 0));
133+
assert!((((((*grow.borrow()).offset((1) as isize).read()) == 2) as i32) != 0));
134+
assert!((((((*grow.borrow()).offset((2) as isize).read()) == 3) as i32) != 0));
135+
assert!((((((*grow.borrow()).offset((3) as isize).read()) == 4) as i32) != 0));
136+
({ (*(*pfree.borrow()))(((*grow.borrow()).clone() as Ptr<i32>).to_any()) });
137+
let zeros: Value<Ptr<i32>> = Rc::new(RefCell::new(
138+
({ (*(*pcalloc.borrow()))(4_usize, ::std::mem::size_of::<i32>()) })
139+
.reinterpret_cast::<i32>(),
140+
));
141+
let i: Value<i32> = Rc::new(RefCell::new(0));
142+
'loop_: while ((((*i.borrow()) < 4) as i32) != 0) {
143+
assert!(
144+
(((((*zeros.borrow()).offset((*i.borrow()) as isize).read()) == 0) as i32) != 0)
145+
);
146+
(*i.borrow_mut()).postfix_inc();
147+
}
148+
({ (*(*pfree.borrow()))(((*zeros.borrow()).clone() as Ptr<i32>).to_any()) });
149+
}
150+
return 0;
151+
}

0 commit comments

Comments
 (0)