Skip to content

Commit bdb622a

Browse files
committed
Add an integration test for the cmsg(3) functions.
Since these are defined in C as macros, they must be reimplemented in libc as Rust functions. They're hard to get exactly right, and they vary from platform to platform. The test builds custom C code that uses the real macros, and compares its output to the Rust versions' output for various inputs.
1 parent 4a70c93 commit bdb622a

File tree

5 files changed

+134
-1
lines changed

5 files changed

+134
-1
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libc-test/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ path = ".."
99
default-features = false
1010

1111
[build-dependencies]
12+
cc = "1.0"
1213
ctest = "0.2.8"
1314

1415
[features]
@@ -26,3 +27,7 @@ name = "linux-fcntl"
2627
path = "test/linux_fcntl.rs"
2728
harness = false
2829

30+
[[test]]
31+
name = "cmsg"
32+
path = "test/cmsg.rs"
33+
harness = true

libc-test/build.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
#![deny(warnings)]
22

3+
extern crate cc;
34
extern crate ctest;
45

56
use std::env;
67

7-
fn main() {
8+
fn do_cc() {
9+
cc::Build::new()
10+
.file("src/cmsg.c")
11+
.compile("cmsg");
12+
}
13+
14+
fn do_ctest() {
815
let target = env::var("TARGET").unwrap();
916
let aarch64 = target.contains("aarch64");
1017
let i686 = target.contains("i686");
@@ -973,3 +980,8 @@ fn main() {
973980
}
974981
cfg.generate("../src/lib.rs", "linux_fcntl.rs");
975982
}
983+
984+
fn main() {
985+
do_cc();
986+
do_ctest();
987+
}

libc-test/src/cmsg.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include <sys/socket.h>
2+
3+
// Since the cmsg(3) macros are macros instead of functions, they aren't
4+
// available to FFI. libc must reimplement them, which is error-prone. This
5+
// file provides FFI access to the actual macros so they can be tested against
6+
// the Rust reimplementations.
7+
8+
struct cmsghdr *cmsg_firsthdr(struct msghdr *msgh) {
9+
return CMSG_FIRSTHDR(msgh);
10+
}
11+
12+
struct cmsghdr *cmsg_nxthdr(struct msghdr *msgh, struct cmsghdr *cmsg) {
13+
return CMSG_NXTHDR(msgh, cmsg);
14+
}
15+
16+
size_t cmsg_space(size_t length) {
17+
return CMSG_SPACE(length);
18+
}
19+
20+
size_t cmsg_len(size_t length) {
21+
return CMSG_LEN(length);
22+
}
23+
24+
unsigned char *cmsg_data(struct cmsghdr *cmsg) {
25+
return CMSG_DATA(cmsg);
26+
}
27+

libc-test/test/cmsg.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//! Compare libc's CMSG(3) family of functions against the actual C macros, for
2+
//! various inputs.
3+
4+
extern crate libc;
5+
use libc::{c_uchar, c_uint, c_void, cmsghdr, msghdr};
6+
use std::{mem, ptr};
7+
8+
extern {
9+
pub fn cmsg_firsthdr(msgh: *const msghdr) -> *mut cmsghdr;
10+
pub fn cmsg_nxthdr(mhdr: *const msghdr,
11+
cmsg: *const cmsghdr) -> *mut cmsghdr;
12+
pub fn cmsg_space(length: c_uint) -> usize;
13+
pub fn cmsg_len(length: c_uint) -> usize;
14+
pub fn cmsg_data(cmsg: *const cmsghdr) -> *mut c_uchar;
15+
}
16+
17+
#[test]
18+
fn test_cmsg_data() {
19+
for l in 0..128 {
20+
let pcmsghdr = l as *const cmsghdr;
21+
unsafe {
22+
assert_eq!(libc::CMSG_DATA(pcmsghdr), cmsg_data(pcmsghdr));
23+
}
24+
}
25+
}
26+
27+
#[test]
28+
fn test_cmsg_firsthdr() {
29+
let mut mhdr: msghdr = unsafe{mem::zeroed()};
30+
mhdr.msg_control = 0xdeadbeef as *mut c_void;
31+
let pmhdr = &mhdr as *const msghdr;
32+
for l in 0..128 {
33+
mhdr.msg_controllen = l;
34+
unsafe {
35+
assert_eq!(libc::CMSG_FIRSTHDR(pmhdr), cmsg_firsthdr(pmhdr));
36+
}
37+
}
38+
}
39+
40+
#[test]
41+
fn test_cmsg_len() {
42+
for l in 0..128 {
43+
unsafe {
44+
assert_eq!(libc::CMSG_LEN(l) as usize, cmsg_len(l));
45+
}
46+
}
47+
}
48+
49+
#[test]
50+
fn test_cmsg_nxthdr() {
51+
let mut buffer = [0u8; 256];
52+
let mut mhdr: msghdr = unsafe{mem::zeroed()};
53+
let pmhdr = &mhdr as *const msghdr;
54+
for start_ofs in 0..64 {
55+
let pcmsghdr = &mut buffer[start_ofs] as *mut u8 as *mut cmsghdr;
56+
mhdr.msg_control = pcmsghdr as *mut c_void;
57+
mhdr.msg_controllen = (160 - start_ofs) as _;
58+
for cmsg_len in 0..64 {
59+
for next_cmsg_len in 0..32 {
60+
for i in buffer[start_ofs..].iter_mut() {
61+
*i = 0;
62+
}
63+
unsafe {
64+
(*pcmsghdr).cmsg_len = cmsg_len;
65+
let libc_next = libc::CMSG_NXTHDR(pmhdr, pcmsghdr);
66+
let next = cmsg_nxthdr(pmhdr, pcmsghdr);
67+
assert_eq!(libc_next, next);
68+
69+
if libc_next != ptr::null_mut() {
70+
(*libc_next).cmsg_len = next_cmsg_len;
71+
let libc_next = libc::CMSG_NXTHDR(pmhdr, pcmsghdr);
72+
let next = cmsg_nxthdr(pmhdr, pcmsghdr);
73+
assert_eq!(libc_next, next);
74+
}
75+
}
76+
}
77+
}
78+
}
79+
}
80+
81+
#[test]
82+
fn test_cmsg_space() {
83+
unsafe {
84+
for l in 0..128 {
85+
assert_eq!(libc::CMSG_SPACE(l) as usize, cmsg_space(l));
86+
}
87+
}
88+
}

0 commit comments

Comments
 (0)