From 442a7bbedc37c589be490cfe21a74be5ea9de65b Mon Sep 17 00:00:00 2001
From: jD91mZM2 <me@krake.one>
Date: Sat, 4 Aug 2018 07:14:07 +0200
Subject: [PATCH] Fix getcwd with a NULL argument

---
 src/platform/src/redox/mod.rs |  8 ++++++--
 src/unistd/src/lib.rs         | 31 ++++++++++++++++++++++++++++---
 tests/Makefile                |  1 +
 tests/unistd/getcwd.c         | 22 ++++++++++++++++++++++
 4 files changed, 57 insertions(+), 5 deletions(-)
 create mode 100644 tests/unistd/getcwd.c

diff --git a/src/platform/src/redox/mod.rs b/src/platform/src/redox/mod.rs
index 48585668..05afc235 100644
--- a/src/platform/src/redox/mod.rs
+++ b/src/platform/src/redox/mod.rs
@@ -346,10 +346,14 @@ pub fn utimens(path: *const c_char, times: *const timespec) -> c_int {
 }
 
 pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char {
-    let buf_slice = unsafe { slice::from_raw_parts_mut(buf as *mut u8, size as usize) };
-    if e(syscall::getcwd(buf_slice)) == !0 {
+    let buf_slice = unsafe { slice::from_raw_parts_mut(buf as *mut u8, size as usize - 1) };
+    let read = e(syscall::getcwd(buf_slice));
+    if read == !0 {
         ptr::null_mut()
     } else {
+        unsafe {
+            *buf.offset(read as isize + 1) = 0;
+        }
         buf
     }
 }
diff --git a/src/unistd/src/lib.rs b/src/unistd/src/lib.rs
index 95c3eac4..944d9726 100644
--- a/src/unistd/src/lib.rs
+++ b/src/unistd/src/lib.rs
@@ -38,6 +38,8 @@ pub const STDIN_FILENO: c_int = 0;
 pub const STDOUT_FILENO: c_int = 1;
 pub const STDERR_FILENO: c_int = 2;
 
+const PATH_MAX: usize = 4096;
+
 #[no_mangle]
 pub extern "C" fn _exit(status: c_int) {
     platform::exit(status)
@@ -184,8 +186,31 @@ pub extern "C" fn ftruncate(fildes: c_int, length: off_t) -> c_int {
 }
 
 #[no_mangle]
-pub extern "C" fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char {
-    platform::getcwd(buf, size)
+pub extern "C" fn getcwd(mut buf: *mut c_char, mut size: size_t) -> *mut c_char {
+    let alloc = buf.is_null();
+    let mut stack_buf = [0; PATH_MAX];
+    if alloc {
+        buf = stack_buf.as_mut_ptr();
+        size = stack_buf.len();
+    }
+
+    let ret = platform::getcwd(buf, size);
+    if ret == ptr::null_mut() {
+        return ptr::null_mut();
+    }
+
+    if alloc {
+        let mut len = stack_buf.iter().position(|b| *b == 0).expect("no nul-byte in getcwd string") + 1;
+        let mut heap_buf = unsafe { platform::alloc(len) as *mut c_char };
+        for i in 0..len {
+            unsafe {
+                *heap_buf.offset(i as isize) = stack_buf[i];
+            }
+        }
+        heap_buf
+    } else {
+        ret
+    }
 }
 
 // #[no_mangle]
@@ -275,7 +300,7 @@ pub extern "C" fn getuid() -> uid_t {
 
 #[no_mangle]
 pub extern "C" fn getwd(path_name: *mut c_char) -> *mut c_char {
-    getcwd(path_name, 4096 /* PATH_MAX */)
+    getcwd(path_name, PATH_MAX)
 }
 
 #[no_mangle]
diff --git a/tests/Makefile b/tests/Makefile
index cad834e3..3ca98006 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -77,6 +77,7 @@ BINS=\
 	time/gettimeofday \
 	time/times \
 	unistd/chdir \
+	unistd/getcwd \
 	unistd/gethostname \
 	unistd/getid \
 	unistd/link \
diff --git a/tests/unistd/getcwd.c b/tests/unistd/getcwd.c
new file mode 100644
index 00000000..0d8e5f10
--- /dev/null
+++ b/tests/unistd/getcwd.c
@@ -0,0 +1,22 @@
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int main() {
+    char first[PATH_MAX];
+    getcwd(first, PATH_MAX);
+    puts(first);
+
+    char* second = getcwd(NULL, 0);
+    puts(second);
+
+    if (strcmp(first, second)) {
+        puts("Not matching");
+        free(second);
+        return 1;
+    }
+
+    free(second);
+}
-- 
GitLab