Skip to content

Commit

Permalink
Add an integration test for the cmsg(3) functions.
Browse files Browse the repository at this point in the history
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.

Skip the CMSG_NXTHDR test on sparc64 linux because it hits a Bus Error.

Issue #1239

Skip the entire cmsg test program on s390x because it dumps core
seemingly before the kernel finishes booting.

Issue #1240
  • Loading branch information
asomers committed Feb 5, 2019
1 parent eddc8d3 commit 38cf5b1
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 10 deletions.
3 changes: 2 additions & 1 deletion ci/docker/x86_64-rumprun-netbsd/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ fn find_ok(input: &mut Read, tx: mpsc::Sender<()>) {
for line in BufReader::new(input).lines() {
let line = line.unwrap();
println!("{}", line);
if line.starts_with("PASSED ") && line.contains(" tests") {
if (line.starts_with("PASSED ") && line.contains(" tests")) ||
line.starts_with("test result: ok"){
tx.send(()).unwrap();
}
}
Expand Down
7 changes: 5 additions & 2 deletions ci/ios/deploy_and_run_on_ios_simulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,11 @@ fn run_app_on_simulator() {

let stdout = String::from_utf8_lossy(&output.stdout);
let passed = stdout.lines()
.find(|l| l.contains("PASSED"))
.map(|l| l.contains("tests"))
.find(|l|
(l.contains("PASSED") &&
l.contains("tests")) ||
l.contains("test result: ok")
)
.unwrap_or(false);

println!("Shutting down simulator");
Expand Down
2 changes: 1 addition & 1 deletion ci/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ if [ "$QEMU" != "" ]; then
-net user \
-nographic \
-vga none 2>&1 | tee "${CARGO_TARGET_DIR}/out.log"
exec grep "^PASSED .* tests" "${CARGO_TARGET_DIR}/out.log"
exec egrep "^(PASSED)|(test result: ok)" "${CARGO_TARGET_DIR}/out.log"
fi

# FIXME: x86_64-unknown-linux-gnux32 fail to compile without --release
Expand Down
8 changes: 5 additions & 3 deletions ci/runtest-android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ fn main() {
String::from_utf8_lossy(&output.stderr));

let stdout = String::from_utf8_lossy(&output.stdout);
let mut lines = stdout.lines().filter(|l| l.starts_with("PASSED "));
if !lines.any(|l| l.contains(" tests")) {
let passed = stdout.lines().find(|l|
(l.starts_with("PASSED ") && l.contains(" tests")) ||
l.starts_with("test result: ok")
).unwrap_or_else(|| {
panic!("failed to find successful test run");
}
});
}
15 changes: 13 additions & 2 deletions ci/test-runner-linux
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,18 @@ set -e
arch=$1
prog=$2

# Skip cmsg test on linux-s390x
# https://github.com/rust-lang/libc/issues/1240
if [ "$arch" = "s390x" ]; then
progbasename=`basename $prog`
if [ "${progbasename%%-*}" = "cmsg" ]; then
exit 0
fi
fi

cd /qemu/init
echo "#!/bin/sh\n/prog --color=never" > run_prog.sh
chmod +x run_prog.sh
cp -f $2 prog
find . | cpio --create --format='newc' --quiet | gzip > ../initrd.gz
cd ..
Expand All @@ -15,9 +26,9 @@ timeout 30s qemu-system-$arch \
-nographic \
-kernel kernel \
-initrd initrd.gz \
-append init=/prog > output || true
-append init=/run_prog.sh > output || true

# remove kernel messages
tr -d '\r' < output | egrep -v '^\['

grep PASSED output > /dev/null
egrep "(PASSED)|(test result: ok)" output > /dev/null
5 changes: 5 additions & 0 deletions libc-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ path = ".."
default-features = false

[build-dependencies]
cc = "1.0"
ctest = "0.2.8"

[features]
Expand All @@ -27,3 +28,7 @@ name = "linux-fcntl"
path = "test/linux_fcntl.rs"
harness = false

[[test]]
name = "cmsg"
path = "test/cmsg.rs"
harness = true
18 changes: 17 additions & 1 deletion libc-test/build.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
#![deny(warnings)]

extern crate cc;
extern crate ctest;

use std::env;

fn main() {
#[cfg(unix)]
fn do_cc() {
cc::Build::new()
.file("src/cmsg.c")
.compile("cmsg");
}
#[cfg(not(unix))]
fn do_cc() {
}

fn do_ctest() {
let target = env::var("TARGET").unwrap();
let aarch64 = target.contains("aarch64");
let i686 = target.contains("i686");
Expand Down Expand Up @@ -975,3 +986,8 @@ fn main() {
}
cfg.generate("../src/lib.rs", "linux_fcntl.rs");
}

fn main() {
do_cc();
do_ctest();
}
28 changes: 28 additions & 0 deletions libc-test/src/cmsg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include <sys/param.h>
#include <sys/socket.h>

// Since the cmsg(3) macros are macros instead of functions, they aren't
// available to FFI. libc must reimplement them, which is error-prone. This
// file provides FFI access to the actual macros so they can be tested against
// the Rust reimplementations.

struct cmsghdr *cmsg_firsthdr(struct msghdr *msgh) {
return CMSG_FIRSTHDR(msgh);
}

struct cmsghdr *cmsg_nxthdr(struct msghdr *msgh, struct cmsghdr *cmsg) {
return CMSG_NXTHDR(msgh, cmsg);
}

size_t cmsg_space(size_t length) {
return CMSG_SPACE(length);
}

size_t cmsg_len(size_t length) {
return CMSG_LEN(length);
}

unsigned char *cmsg_data(struct cmsghdr *cmsg) {
return CMSG_DATA(cmsg);
}

99 changes: 99 additions & 0 deletions libc-test/test/cmsg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//! Compare libc's CMSG(3) family of functions against the actual C macros, for
//! various inputs.

extern crate libc;

#[cfg(unix)]
mod t {

use libc::{self, c_uchar, c_uint, c_void, cmsghdr, msghdr};
use std::mem;

extern {
pub fn cmsg_firsthdr(msgh: *const msghdr) -> *mut cmsghdr;
pub fn cmsg_nxthdr(mhdr: *const msghdr,
cmsg: *const cmsghdr) -> *mut cmsghdr;
pub fn cmsg_space(length: c_uint) -> usize;
pub fn cmsg_len(length: c_uint) -> usize;
pub fn cmsg_data(cmsg: *const cmsghdr) -> *mut c_uchar;
}

#[test]
fn test_cmsg_data() {
for l in 0..128 {
let pcmsghdr = l as *const cmsghdr;
unsafe {
assert_eq!(libc::CMSG_DATA(pcmsghdr), cmsg_data(pcmsghdr));
}
}
}

#[test]
fn test_cmsg_firsthdr() {
let mut mhdr: msghdr = unsafe{mem::zeroed()};
mhdr.msg_control = 0xdeadbeef as *mut c_void;
let pmhdr = &mhdr as *const msghdr;
for l in 0..128 {
mhdr.msg_controllen = l;
unsafe {
assert_eq!(libc::CMSG_FIRSTHDR(pmhdr), cmsg_firsthdr(pmhdr));
}
}
}

#[test]
fn test_cmsg_len() {
for l in 0..128 {
unsafe {
assert_eq!(libc::CMSG_LEN(l) as usize, cmsg_len(l));
}
}
}

// Skip on sparc64
// https://github.com/rust-lang/libc/issues/1239
#[cfg(not(target_arch = "sparc64"))]
#[test]
fn test_cmsg_nxthdr() {
use std::ptr;

let mut buffer = [0u8; 256];
let mut mhdr: msghdr = unsafe{mem::zeroed()};
let pmhdr = &mhdr as *const msghdr;
for start_ofs in 0..64 {
let pcmsghdr = &mut buffer[start_ofs] as *mut u8 as *mut cmsghdr;
mhdr.msg_control = pcmsghdr as *mut c_void;
mhdr.msg_controllen = (160 - start_ofs) as _;
for cmsg_len in 0..64 {
for next_cmsg_len in 0..32 {
for i in buffer[start_ofs..].iter_mut() {
*i = 0;
}
unsafe {
(*pcmsghdr).cmsg_len = cmsg_len;
let libc_next = libc::CMSG_NXTHDR(pmhdr, pcmsghdr);
let next = cmsg_nxthdr(pmhdr, pcmsghdr);
assert_eq!(libc_next, next);

if libc_next != ptr::null_mut() {
(*libc_next).cmsg_len = next_cmsg_len;
let libc_next = libc::CMSG_NXTHDR(pmhdr, pcmsghdr);
let next = cmsg_nxthdr(pmhdr, pcmsghdr);
assert_eq!(libc_next, next);
}
}
}
}
}
}

#[test]
fn test_cmsg_space() {
unsafe {
for l in 0..128 {
assert_eq!(libc::CMSG_SPACE(l) as usize, cmsg_space(l));
}
}
}

}

0 comments on commit 38cf5b1

Please sign in to comment.