From 38cf5b15c61b6c4f76b30da34b7c70b8ee5ff6de Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Thu, 31 Jan 2019 22:34:17 -0700 Subject: [PATCH] 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. 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 --- ci/docker/x86_64-rumprun-netbsd/runtest.rs | 3 +- ci/ios/deploy_and_run_on_ios_simulator.rs | 7 +- ci/run.sh | 2 +- ci/runtest-android.rs | 8 +- ci/test-runner-linux | 15 +++- libc-test/Cargo.toml | 5 ++ libc-test/build.rs | 18 +++- libc-test/src/cmsg.c | 28 ++++++ libc-test/test/cmsg.rs | 99 ++++++++++++++++++++++ 9 files changed, 175 insertions(+), 10 deletions(-) create mode 100644 libc-test/src/cmsg.c create mode 100644 libc-test/test/cmsg.rs diff --git a/ci/docker/x86_64-rumprun-netbsd/runtest.rs b/ci/docker/x86_64-rumprun-netbsd/runtest.rs index 94b5946080b69..7e96fbfab442d 100644 --- a/ci/docker/x86_64-rumprun-netbsd/runtest.rs +++ b/ci/docker/x86_64-rumprun-netbsd/runtest.rs @@ -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(); } } diff --git a/ci/ios/deploy_and_run_on_ios_simulator.rs b/ci/ios/deploy_and_run_on_ios_simulator.rs index 95df52d76d593..2075be6d62007 100644 --- a/ci/ios/deploy_and_run_on_ios_simulator.rs +++ b/ci/ios/deploy_and_run_on_ios_simulator.rs @@ -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"); diff --git a/ci/run.sh b/ci/run.sh index 853b7c10537ff..1fb5e127a254e 100755 --- a/ci/run.sh +++ b/ci/run.sh @@ -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 diff --git a/ci/runtest-android.rs b/ci/runtest-android.rs index a68b854cf68a0..18d39bfdfe89c 100644 --- a/ci/runtest-android.rs +++ b/ci/runtest-android.rs @@ -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"); - } + }); } diff --git a/ci/test-runner-linux b/ci/test-runner-linux index 5f1fb237c28ea..569fa0077006f 100755 --- a/ci/test-runner-linux +++ b/ci/test-runner-linux @@ -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 .. @@ -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 diff --git a/libc-test/Cargo.toml b/libc-test/Cargo.toml index f8bd11b5edc90..7eecc4994cabe 100644 --- a/libc-test/Cargo.toml +++ b/libc-test/Cargo.toml @@ -9,6 +9,7 @@ path = ".." default-features = false [build-dependencies] +cc = "1.0" ctest = "0.2.8" [features] @@ -27,3 +28,7 @@ name = "linux-fcntl" path = "test/linux_fcntl.rs" harness = false +[[test]] +name = "cmsg" +path = "test/cmsg.rs" +harness = true diff --git a/libc-test/build.rs b/libc-test/build.rs index d81a54d716224..eab5b762f0db2 100644 --- a/libc-test/build.rs +++ b/libc-test/build.rs @@ -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"); @@ -975,3 +986,8 @@ fn main() { } cfg.generate("../src/lib.rs", "linux_fcntl.rs"); } + +fn main() { + do_cc(); + do_ctest(); +} diff --git a/libc-test/src/cmsg.c b/libc-test/src/cmsg.c new file mode 100644 index 0000000000000..a8b1c371736c8 --- /dev/null +++ b/libc-test/src/cmsg.c @@ -0,0 +1,28 @@ +#include +#include + +// 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); +} + diff --git a/libc-test/test/cmsg.rs b/libc-test/test/cmsg.rs new file mode 100644 index 0000000000000..c9eecb628d975 --- /dev/null +++ b/libc-test/test/cmsg.rs @@ -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)); + } + } +} + +}