diff --git a/ci/docker/x86_64-rumprun-netbsd/runtest.rs b/ci/docker/x86_64-rumprun-netbsd/runtest.rs
index 94b5946080b691e37dccdfb11e6fdbf41203930d..7e96fbfab442d2bd704e95f38644acbacef75ec5 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 95df52d76d593e29358525dc324642746197f099..2075be6d62007c726dfb4a48d9dc83bca64155e3 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 853b7c10537ff1e68329c252caec446a3a4c1103..1fb5e127a254e1da968c38557f10a3a084234e5f 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 a68b854cf68a0eb792c3a825d9c0e3accdaf4da6..18d39bfdfe89c06d5033f5bd9f46c3189d890326 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 5f1fb237c28eadce3b8b16791da228484a83e0c5..569fa0077006f6da15dd76763cb6aa42fb346b1a 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 f8bd11b5edc908877d89a2ee9e429a1961245add..7eecc4994cabe7427545704b487a1c66472cf1cc 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 d81a54d71622495aef3e5c65b21772bbf1bb2881..eab5b762f0db2323f8481f1d78fbf5ec5e6eef4b 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 0000000000000000000000000000000000000000..a8b1c371736c84f55e46210e0644225b2b909f66
--- /dev/null
+++ b/libc-test/src/cmsg.c
@@ -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);
+}
+
diff --git a/libc-test/test/cmsg.rs b/libc-test/test/cmsg.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c9eecb628d97599976889884cf924c2c981ddb97
--- /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));
+        }
+    }
+}
+
+}