From 73df81fcec8484e6b1fa538b771a97434e5e5a30 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Thu, 31 Jan 2019 09:29:00 -0700 Subject: [PATCH 1/4] Fix CMSG_NXTHDR for OSX. This was an oversight from PR #1212. It's been revealed by the new cmsg test. --- src/unix/bsd/apple/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/unix/bsd/apple/mod.rs b/src/unix/bsd/apple/mod.rs index 59394c6bcaf1b..85332180e015d 100644 --- a/src/unix/bsd/apple/mod.rs +++ b/src/unix/bsd/apple/mod.rs @@ -2777,11 +2777,10 @@ f! { return ::CMSG_FIRSTHDR(mhdr); }; let cmsg_len = (*cmsg).cmsg_len as usize; - let next = cmsg as usize + __DARWIN_ALIGN32(cmsg_len as usize) - + __DARWIN_ALIGN32(mem::size_of::<::cmsghdr>()); + let next = cmsg as usize + __DARWIN_ALIGN32(cmsg_len as usize); let max = (*mhdr).msg_control as usize + (*mhdr).msg_controllen as usize; - if next > max { + if next + __DARWIN_ALIGN32(mem::size_of::<::cmsghdr>()) > max { 0 as *mut ::cmsghdr } else { next as *mut ::cmsghdr @@ -2800,7 +2799,7 @@ f! { } pub fn CMSG_LEN(length: ::c_uint) -> ::c_uint { - __DARWIN_ALIGN32(mem::size_of::<::cmsghdr>() + length as usize) + (__DARWIN_ALIGN32(mem::size_of::<::cmsghdr>()) + length as usize) as ::c_uint } From 4300666bf3d51aa644b7ded0a59a16b661b00c71 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Thu, 31 Jan 2019 11:52:14 -0700 Subject: [PATCH 2/4] Fix Linux's CMSG_NXTHDR and CMSG_SPACE definitions. This is an error from PR #1098. The wrong definitions coincidentally work on Linux/glibc, but fail on Linux/musl. --- src/unix/notbsd/mod.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/unix/notbsd/mod.rs b/src/unix/notbsd/mod.rs index 51414e688d73d..bbb76ac2cab78 100644 --- a/src/unix/notbsd/mod.rs +++ b/src/unix/notbsd/mod.rs @@ -1114,6 +1114,10 @@ pub const ARPHRD_IEEE802154: u16 = 804; pub const ARPHRD_VOID: u16 = 0xFFFF; pub const ARPHRD_NONE: u16 = 0xFFFE; +fn CMSG_ALIGN(len: usize) -> usize { + len + mem::size_of::() - 1 & !(mem::size_of::() - 1) +} + f! { pub fn CMSG_FIRSTHDR(mhdr: *const msghdr) -> *mut cmsghdr { if (*mhdr).msg_controllen as usize >= mem::size_of::() { @@ -1125,17 +1129,19 @@ f! { pub fn CMSG_NXTHDR(mhdr: *const msghdr, cmsg: *const cmsghdr) -> *mut cmsghdr { - if cmsg.is_null() { - return CMSG_FIRSTHDR(mhdr); + if ((*cmsg).cmsg_len as usize) < mem::size_of::() { + return 0 as *mut cmsghdr; }; - let pad = mem::align_of::() - 1; - let next = cmsg as usize + (*cmsg).cmsg_len as usize + pad & !pad; + let next = (cmsg as usize + CMSG_ALIGN((*cmsg).cmsg_len as usize)) + as *mut cmsghdr; let max = (*mhdr).msg_control as usize + (*mhdr).msg_controllen as usize; - if next < max { - next as *mut cmsghdr - } else { + if (next.offset(1)) as usize > max + || next as usize + CMSG_ALIGN((*next).cmsg_len as usize) > max + { 0 as *mut cmsghdr + } else { + next as *mut cmsghdr } } @@ -1144,12 +1150,12 @@ f! { } pub fn CMSG_SPACE(length: ::c_uint) -> ::c_uint { - let pad = mem::align_of::() as ::c_uint - 1; - mem::size_of::() as ::c_uint + ((length + pad) & !pad) + (CMSG_ALIGN(length as usize) + CMSG_ALIGN(mem::size_of::())) + as ::c_uint } pub fn CMSG_LEN(length: ::c_uint) -> ::c_uint { - mem::size_of::() as ::c_uint + length + CMSG_ALIGN(mem::size_of::()) as ::c_uint + length } pub fn FD_CLR(fd: ::c_int, set: *mut fd_set) -> () { From eddc8d342b6371b55cb8910fc8365ce0a3880f50 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Tue, 5 Feb 2019 08:00:26 -0700 Subject: [PATCH 3/4] Specialize CMSG_NXTHDR for Android and Emscripten --- src/unix/notbsd/android/mod.rs | 14 ++++++++++++++ src/unix/notbsd/emscripten.rs | 17 +++++++++++++++++ src/unix/notbsd/linux/mod.rs | 19 +++++++++++++++++++ src/unix/notbsd/mod.rs | 18 ------------------ 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/unix/notbsd/android/mod.rs b/src/unix/notbsd/android/mod.rs index 0e2eebf056e39..f768ce1283e0e 100644 --- a/src/unix/notbsd/android/mod.rs +++ b/src/unix/notbsd/android/mod.rs @@ -1688,6 +1688,20 @@ pub const MODULE_INIT_IGNORE_VERMAGIC: ::c_uint = 0x0002; pub const ENOATTR: ::c_int = ::ENODATA; f! { + pub fn CMSG_NXTHDR(mhdr: *const msghdr, + cmsg: *const cmsghdr) -> *mut cmsghdr { + let next = (cmsg as usize + + super::CMSG_ALIGN((*cmsg).cmsg_len as usize)) + as *mut cmsghdr; + let max = (*mhdr).msg_control as usize + + (*mhdr).msg_controllen as usize; + if (next.offset(1)) as usize > max { + 0 as *mut cmsghdr + } else { + next as *mut cmsghdr + } + } + pub fn CPU_ZERO(cpuset: &mut cpu_set_t) -> () { for slot in cpuset.__bits.iter_mut() { *slot = 0; diff --git a/src/unix/notbsd/emscripten.rs b/src/unix/notbsd/emscripten.rs index 9bf2026b22e4d..2685e769fc1d6 100644 --- a/src/unix/notbsd/emscripten.rs +++ b/src/unix/notbsd/emscripten.rs @@ -1515,6 +1515,23 @@ pub const ARPD_FLUSH: ::c_ushort = 0x03; pub const ATF_MAGIC: ::c_int = 0x80; f! { + pub fn CMSG_NXTHDR(mhdr: *const msghdr, + cmsg: *const cmsghdr) -> *mut cmsghdr { + if ((*cmsg).cmsg_len as usize) < mem::size_of::() { + return 0 as *mut cmsghdr; + }; + let next = (cmsg as usize + + super::CMSG_ALIGN((*cmsg).cmsg_len as usize)) + as *mut cmsghdr; + let max = (*mhdr).msg_control as usize + + (*mhdr).msg_controllen as usize; + if (next.offset(1)) as usize > max { + 0 as *mut cmsghdr + } else { + next as *mut cmsghdr + } + } + pub fn CPU_ZERO(cpuset: &mut cpu_set_t) -> () { for slot in cpuset.bits.iter_mut() { *slot = 0; diff --git a/src/unix/notbsd/linux/mod.rs b/src/unix/notbsd/linux/mod.rs index e1a24dcd3c0b8..034db9bc85482 100644 --- a/src/unix/notbsd/linux/mod.rs +++ b/src/unix/notbsd/linux/mod.rs @@ -1897,6 +1897,25 @@ pub const SOF_TIMESTAMPING_SYS_HARDWARE: ::c_uint = 1 << 5; pub const SOF_TIMESTAMPING_RAW_HARDWARE: ::c_uint = 1 << 6; f! { + pub fn CMSG_NXTHDR(mhdr: *const msghdr, + cmsg: *const cmsghdr) -> *mut cmsghdr { + if ((*cmsg).cmsg_len as usize) < mem::size_of::() { + return 0 as *mut cmsghdr; + }; + let next = (cmsg as usize + + super::CMSG_ALIGN((*cmsg).cmsg_len as usize)) + as *mut cmsghdr; + let max = (*mhdr).msg_control as usize + + (*mhdr).msg_controllen as usize; + if (next.offset(1)) as usize > max || + next as usize + super::CMSG_ALIGN((*next).cmsg_len as usize) > max + { + 0 as *mut cmsghdr + } else { + next as *mut cmsghdr + } + } + pub fn CPU_ZERO(cpuset: &mut cpu_set_t) -> () { for slot in cpuset.bits.iter_mut() { *slot = 0; diff --git a/src/unix/notbsd/mod.rs b/src/unix/notbsd/mod.rs index bbb76ac2cab78..3698590ad4525 100644 --- a/src/unix/notbsd/mod.rs +++ b/src/unix/notbsd/mod.rs @@ -1127,24 +1127,6 @@ f! { } } - pub fn CMSG_NXTHDR(mhdr: *const msghdr, - cmsg: *const cmsghdr) -> *mut cmsghdr { - if ((*cmsg).cmsg_len as usize) < mem::size_of::() { - return 0 as *mut cmsghdr; - }; - let next = (cmsg as usize + CMSG_ALIGN((*cmsg).cmsg_len as usize)) - as *mut cmsghdr; - let max = (*mhdr).msg_control as usize - + (*mhdr).msg_controllen as usize; - if (next.offset(1)) as usize > max - || next as usize + CMSG_ALIGN((*next).cmsg_len as usize) > max - { - 0 as *mut cmsghdr - } else { - next as *mut cmsghdr - } - } - pub fn CMSG_DATA(cmsg: *const cmsghdr) -> *mut ::c_uchar { cmsg.offset(1) as *mut ::c_uchar } From 38cf5b15c61b6c4f76b30da34b7c70b8ee5ff6de Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Thu, 31 Jan 2019 22:34:17 -0700 Subject: [PATCH 4/4] 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)); + } + } +} + +}