about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBenjamin Herr <ben@0x539.de>2014-10-02 20:55:26 +0200
committerBenjamin Herr <ben@0x539.de>2014-10-02 21:16:37 +0200
commitaf633ce157082bf2b0fd0577765e9b49c34c3e90 (patch)
tree2f20450b85cbc3b335a321aefba65989f8ca0677
parentdd7f00de801e4ca24c9c1235227ace4f998d4b1c (diff)
downloadrust-af633ce157082bf2b0fd0577765e9b49c34c3e90.tar.gz
rust-af633ce157082bf2b0fd0577765e9b49c34c3e90.zip
native: fix passing errno to parent after fork
The bitshifts were wrong in that they invoked undefined behavior and
only passed the lower byte of the presumed-to-be-32bit errno value.
Apparently all actually possible values for errno happen to be easily
under 256, so this didn't cause any actual problems.

This commit fixes the bitshifts, but doesn't generalize to errno types
that aren't 32bit.
-rw-r--r--src/libnative/io/process.rs17
-rw-r--r--src/test/run-pass/unix-process-spawn-errno.rs94
2 files changed, 103 insertions, 8 deletions
diff --git a/src/libnative/io/process.rs b/src/libnative/io/process.rs
index 3a6ae42f946..7a0c1c35d65 100644
--- a/src/libnative/io/process.rs
+++ b/src/libnative/io/process.rs
@@ -583,10 +583,11 @@ fn spawn_process_os(cfg: ProcessConfig,
                 let mut bytes = [0, ..4];
                 return match input.inner_read(bytes) {
                     Ok(4) => {
-                        let errno = (bytes[0] << 24) as i32 |
-                                    (bytes[1] << 16) as i32 |
-                                    (bytes[2] <<  8) as i32 |
-                                    (bytes[3] <<  0) as i32;
+                        let errno = (bytes[0] as i32 << 24) |
+                                    (bytes[1] as i32 << 16) |
+                                    (bytes[2] as i32 <<  8) |
+                                    (bytes[3] as i32 <<  0);
+
                         Err(IoError {
                             code: errno as uint,
                             detail: None,
@@ -637,10 +638,10 @@ fn spawn_process_os(cfg: ProcessConfig,
             fn fail(output: &mut file::FileDesc) -> ! {
                 let errno = os::errno();
                 let bytes = [
-                    (errno << 24) as u8,
-                    (errno << 16) as u8,
-                    (errno <<  8) as u8,
-                    (errno <<  0) as u8,
+                    (errno >> 24) as u8,
+                    (errno >> 16) as u8,
+                    (errno >>  8) as u8,
+                    (errno >>  0) as u8,
                 ];
                 assert!(output.inner_write(bytes).is_ok());
                 unsafe { libc::_exit(1) }
diff --git a/src/test/run-pass/unix-process-spawn-errno.rs b/src/test/run-pass/unix-process-spawn-errno.rs
new file mode 100644
index 00000000000..555bf2cd4c0
--- /dev/null
+++ b/src/test/run-pass/unix-process-spawn-errno.rs
@@ -0,0 +1,94 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-windows
+
+#![feature(macro_rules)]
+
+extern crate native;
+extern crate rustrt;
+extern crate libc;
+use libc::{c_char, c_int};
+use native::io::process;
+use rustrt::rtio;
+use rustrt::c_str;
+
+macro_rules! c_string {
+    ($s:expr) => { {
+        let ptr = concat!($s, "\0").as_ptr() as *const i8;
+        unsafe { &c_str::CString::new(ptr, false) }
+    } }
+}
+
+static EXPECTED_ERRNO: c_int = 0x778899aa;
+
+#[no_mangle]
+pub unsafe extern "C" fn chdir(_: *const c_char) -> c_int {
+    // copied from std::os::errno()
+    #[cfg(any(target_os = "macos",
+              target_os = "ios",
+              target_os = "freebsd"))]
+    fn errno_location() -> *mut c_int {
+        extern {
+            fn __error() -> *mut c_int;
+        }
+        unsafe {
+            __error()
+        }
+    }
+
+    #[cfg(target_os = "dragonfly")]
+    fn errno_location() -> *mut c_int {
+        extern {
+            fn __dfly_error() -> *mut c_int;
+        }
+        unsafe {
+            __dfly_error()
+        }
+    }
+
+    #[cfg(any(target_os = "linux", target_os = "android"))]
+    fn errno_location() -> *mut c_int {
+        extern {
+            fn __errno_location() -> *mut c_int;
+        }
+        unsafe {
+            __errno_location()
+        }
+    }
+
+    *errno_location() = EXPECTED_ERRNO;
+    return -1;
+}
+
+fn main() {
+    let program = c_string!("true");
+    let cwd = c_string!("whatever");
+    let cfg = rtio::ProcessConfig {
+        program: program,
+        args: &[],
+        env: None,
+        cwd: Some(cwd),
+        stdin: rtio::Ignored,
+        stdout: rtio::Ignored,
+        stderr: rtio::Ignored,
+        extra_io: &[],
+        uid: None,
+        gid: None,
+        detach: false
+    };
+
+    match process::Process::spawn(cfg) {
+        Ok(_) => { fail!("spawn() should have failled"); }
+        Err(rtio::IoError { code: err, ..}) => {
+            assert_eq!(err as c_int, EXPECTED_ERRNO);
+        }
+    };
+}