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));
+        }
+    }
+}
+
+}
diff --git a/src/unix/bsd/apple/mod.rs b/src/unix/bsd/apple/mod.rs
index 59394c6bcaf1b1403593f0b044456bf1b9a5e6ab..85332180e015d377803d611ae5fea785ad3e3ce2 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
     }
 
diff --git a/src/unix/notbsd/android/mod.rs b/src/unix/notbsd/android/mod.rs
index 0e2eebf056e39c2e004cdd2f33e1b010cf450f67..f768ce1283e0e8930792968c720ea8892a0a5e49 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 9bf2026b22e4d510a04d43758389e37ee89e1593..2685e769fc1d6adb59d9fe149e24f170a26d38e3 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::<cmsghdr>() {
+            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 e1a24dcd3c0b8b756b63e1432ba7c7e739ab05e4..034db9bc854824657230a3bf339a3ef5beb8f2e9 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::<cmsghdr>() {
+            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 51414e688d73dd59fb2269168bf74fc2625aa5fc..3698590ad452584281e0317b68fdcaea64eeede6 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::<usize>() - 1 & !(mem::size_of::<usize>() - 1)
+}
+
 f! {
     pub fn CMSG_FIRSTHDR(mhdr: *const msghdr) -> *mut cmsghdr {
         if (*mhdr).msg_controllen as usize >= mem::size_of::<cmsghdr>() {
@@ -1123,33 +1127,17 @@ f! {
         }
     }
 
-    pub fn CMSG_NXTHDR(mhdr: *const msghdr,
-                       cmsg: *const cmsghdr) -> *mut cmsghdr {
-        if cmsg.is_null() {
-            return CMSG_FIRSTHDR(mhdr);
-        };
-        let pad = mem::align_of::<cmsghdr>() - 1;
-        let next = cmsg as usize + (*cmsg).cmsg_len as usize + pad & !pad;
-        let max = (*mhdr).msg_control as usize
-            + (*mhdr).msg_controllen as usize;
-        if next < max {
-            next as *mut cmsghdr
-        } else {
-            0 as *mut cmsghdr
-        }
-    }
-
     pub fn CMSG_DATA(cmsg: *const cmsghdr) -> *mut ::c_uchar {
         cmsg.offset(1) as *mut ::c_uchar
     }
 
     pub fn CMSG_SPACE(length: ::c_uint) -> ::c_uint {
-        let pad = mem::align_of::<cmsghdr>() as ::c_uint - 1;
-        mem::size_of::<cmsghdr>() as ::c_uint + ((length + pad) & !pad)
+        (CMSG_ALIGN(length as usize) + CMSG_ALIGN(mem::size_of::<cmsghdr>()))
+            as ::c_uint
     }
 
     pub fn CMSG_LEN(length: ::c_uint) -> ::c_uint {
-        mem::size_of::<cmsghdr>() as ::c_uint + length
+        CMSG_ALIGN(mem::size_of::<cmsghdr>()) as ::c_uint + length
     }
 
     pub fn FD_CLR(fd: ::c_int, set: *mut fd_set) -> () {