Skip to content

Commit d902a3b

Browse files
authored
Fix void pointer comparisons (#424)
`AnyPtr` comparisons rely on the equals method of the `ErasedPtr` trait. For `Ptr`, this method is as follows: ```rust fn equals(&self, other: &dyn ErasedPtr) -> bool { other.as_any().downcast_ref::<Ptr<T>>() == Some(self) } ``` This implementation causes `AnyPtr` equality checks to succeed only when the underlying `ErasedPtr`s have the same pointee type, since `.as_any().downcast_ref::<Ptr<T>>()` returns `None` otherwise. I do not believe this behavior is correct. Two pointers cast to `void *` compare equal if they point to the same address, regardless of their original types. This patch adds two unit tests covering this bug and addresses it by comparing the two `ErasedPtrs` through their byte representations.
1 parent 7eb17e8 commit d902a3b

7 files changed

Lines changed: 169 additions & 1 deletion

File tree

‎libcc2rs/src/void.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ where
3333
}
3434

3535
fn equals(&self, other: &dyn ErasedPtr) -> bool {
36-
other.as_any().downcast_ref::<Ptr<T>>() == Some(self)
36+
other.as_bytes() == self.as_bytes()
3737
}
3838

3939
fn is_null(&self) -> bool {

‎tests/unit/bsearch_ptr_cmp.cpp‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <cassert>
2+
#include <cstdlib>
3+
4+
int int_cmp(const void *v1, const void *v2) {
5+
return *(const int *)v1 - *(const int *)v2;
6+
}
7+
8+
int main() {
9+
int a1[] = {1, 2, 3};
10+
void *vptr1 = std::bsearch(&a1[0], a1, 1, sizeof(int), int_cmp);
11+
assert(vptr1 == &a1[0]);
12+
return 0;
13+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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 int_cmp_0(v1: AnyPtr, v2: AnyPtr) -> i32 {
10+
let v1: Value<AnyPtr> = Rc::new(RefCell::new(v1));
11+
let v2: Value<AnyPtr> = Rc::new(RefCell::new(v2));
12+
return {
13+
let _lhs = ((*v1.borrow()).reinterpret_cast::<i32>().read());
14+
_lhs - ((*v2.borrow()).reinterpret_cast::<i32>().read())
15+
};
16+
}
17+
pub fn main() {
18+
__cpp2rust_init_globals();
19+
std::process::exit(main_0());
20+
}
21+
fn main_0() -> i32 {
22+
let a1: Value<Box<[i32]>> = Rc::new(RefCell::new(Box::new([1, 2, 3])));
23+
let vptr1: Value<AnyPtr> = Rc::new(RefCell::new({
24+
let __base = ((a1.as_pointer() as Ptr<i32>) as Ptr<i32>)
25+
.to_any()
26+
.reinterpret_cast::<u8>();
27+
let mut __lo: isize = 0;
28+
let mut __hi: isize = 1_usize as isize - 1;
29+
let mut __found = AnyPtr::default();
30+
while __lo <= __hi && __found.is_null() {
31+
let __mid = __lo + (__hi - __lo) / 2;
32+
let __elem = __base.offset(__mid as usize * ::std::mem::size_of::<i32>());
33+
let __r = int_cmp_0(
34+
(((a1.as_pointer() as Ptr<i32>).offset(0)) as Ptr<i32>)
35+
.to_any()
36+
.clone(),
37+
__elem.to_any(),
38+
);
39+
if __r == 0 {
40+
__found = __elem.to_any();
41+
} else if __r < 0 {
42+
__hi = __mid - 1;
43+
} else {
44+
__lo = __mid + 1;
45+
}
46+
}
47+
__found
48+
}));
49+
assert!({
50+
let _lhs = (*vptr1.borrow()).clone();
51+
_lhs == (((a1.as_pointer() as Ptr<i32>).offset(0)) as Ptr<i32>).to_any()
52+
});
53+
return 0;
54+
}
55+
pub fn __cpp2rust_init_globals() {}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
__cpp2rust_init_globals();
11+
std::process::exit(main_0());
12+
}
13+
fn main_0() -> i32 {
14+
let i1: Value<i32> = Rc::new(RefCell::new(42));
15+
let ptr1: Value<Ptr<i32>> = Rc::new(RefCell::new((i1.as_pointer())));
16+
let ptr2: Value<Ptr<u8>> = Rc::new(RefCell::new((i1.as_pointer()).reinterpret_cast::<u8>()));
17+
let vptr1: Value<AnyPtr> = Rc::new(RefCell::new(
18+
((*ptr1.borrow()).clone() as Ptr<i32>).to_any(),
19+
));
20+
let vptr2: Value<AnyPtr> =
21+
Rc::new(RefCell::new(((*ptr2.borrow()).clone() as Ptr<u8>).to_any()));
22+
assert!({
23+
let _lhs = (*vptr1.borrow()).clone();
24+
_lhs == (*vptr2.borrow()).clone()
25+
});
26+
return 0;
27+
}
28+
pub fn __cpp2rust_init_globals() {}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
extern crate libc;
2+
use libc::*;
3+
extern crate libcc2rs;
4+
use libcc2rs::*;
5+
use std::collections::BTreeMap;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::{AsFd, FromRawFd, IntoRawFd};
8+
use std::rc::Rc;
9+
pub unsafe fn int_cmp_0(mut v1: *const ::libc::c_void, mut v2: *const ::libc::c_void) -> i32 {
10+
return ((*(v1 as *const i32)) - (*(v2 as *const i32)));
11+
}
12+
pub fn main() {
13+
unsafe {
14+
__cpp2rust_init_globals();
15+
std::process::exit(main_0() as i32);
16+
}
17+
}
18+
unsafe fn main_0() -> i32 {
19+
let mut a1: [i32; 3] = [1, 2, 3];
20+
let mut vptr1: *mut ::libc::c_void = libc::bsearch(
21+
((&mut a1[(0) as usize] as *mut i32) as *const i32 as *const ::libc::c_void),
22+
(a1.as_mut_ptr() as *const i32 as *const ::libc::c_void),
23+
1_usize,
24+
::std::mem::size_of::<i32>(),
25+
Some(std::mem::transmute::<
26+
*const (),
27+
unsafe extern "C" fn(*const ::libc::c_void, *const ::libc::c_void) -> i32,
28+
>(
29+
(int_cmp_0 as unsafe fn(*const ::libc::c_void, *const ::libc::c_void) -> i32)
30+
as *const (),
31+
)),
32+
);
33+
assert!(((vptr1) == ((&mut a1[(0) as usize] as *mut i32) as *mut i32 as *mut ::libc::c_void)));
34+
return 0;
35+
}
36+
pub unsafe fn __cpp2rust_init_globals() {}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
extern crate libc;
2+
use libc::*;
3+
extern crate libcc2rs;
4+
use libcc2rs::*;
5+
use std::collections::BTreeMap;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::{AsFd, FromRawFd, IntoRawFd};
8+
use std::rc::Rc;
9+
pub fn main() {
10+
unsafe {
11+
__cpp2rust_init_globals();
12+
std::process::exit(main_0() as i32);
13+
}
14+
}
15+
unsafe fn main_0() -> i32 {
16+
let mut i1: i32 = 42;
17+
let mut ptr1: *mut i32 = (&mut i1 as *mut i32);
18+
let mut ptr2: *mut libc::c_char = ((&mut i1 as *mut i32) as *mut libc::c_char);
19+
let mut vptr1: *mut ::libc::c_void = (ptr1 as *mut i32 as *mut ::libc::c_void);
20+
let mut vptr2: *mut ::libc::c_void = (ptr2 as *mut libc::c_char as *mut ::libc::c_void);
21+
assert!(((vptr1) == (vptr2)));
22+
return 0;
23+
}
24+
pub unsafe fn __cpp2rust_init_globals() {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <cassert>
2+
3+
int main() {
4+
int i1 = 42;
5+
int *ptr1 = &i1;
6+
char *ptr2 = reinterpret_cast<char *>(&i1);
7+
8+
void *vptr1 = ptr1;
9+
void *vptr2 = ptr2;
10+
assert(vptr1 == vptr2);
11+
return 0;
12+
}

0 commit comments

Comments
 (0)