diff --git a/src/libstd/env.rs b/src/libstd/env.rs
index 7c2cd615303..b65e065fe91 100644
--- a/src/libstd/env.rs
+++ b/src/libstd/env.rs
@@ -36,7 +36,6 @@ use sys::os as os_imp;
 ///
 /// * Current directory does not exist.
 /// * There are insufficient permissions to access the current directory.
-/// * The internal buffer is not large enough to hold the path.
 ///
 /// # Examples
 ///
diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs
index 6fd20b940bb..90b7d5f4719 100644
--- a/src/libstd/sys/unix/mod.rs
+++ b/src/libstd/sys/unix/mod.rs
@@ -75,6 +75,41 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
     }
 }
 
+// Some system functions expect the user to pass a appropiately-sized buffer
+// without specifying its size. They will only report back whether the buffer
+// was large enough or not.
+//
+// The callback is yielded a (pointer, len) pair which can be
+// passed to a syscall. The `ptr` is valid for `len` items (i8 in this case).
+// The closure is expected to return `None` if the space was insufficient and
+// `Some(r)` if the syscall did not fail due to insufficient space.
+fn fill_bytes_buf<F, T>(mut f: F) -> io::Result<T>
+    where F: FnMut(*mut i8, libc::size_t) -> Option<io::Result<T>>,
+{
+    // Start off with a stack buf but then spill over to the heap if we end up
+    // needing more space.
+    let mut stack_buf = [0i8; os::BUF_BYTES];
+    let mut heap_buf = Vec::new();
+    unsafe {
+        let mut n = stack_buf.len();
+        loop {
+            let buf = if n <= stack_buf.len() {
+                &mut stack_buf[..]
+            } else {
+                heap_buf.set_len(0);
+                heap_buf.reserve(n);
+                heap_buf.set_len(n);
+                &mut heap_buf[..]
+            };
+
+            match f(buf.as_mut_ptr(), n as libc::size_t) {
+                None => n *= 2,
+                Some(r) => return r,
+            }
+        }
+    }
+}
+
 pub fn cvt<T: One + PartialEq + Neg<Output=T>>(t: T) -> io::Result<T> {
     let one: T = T::one();
     if t == -one {
diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs
index 334dd6b5f18..6cf07ea077d 100644
--- a/src/libstd/sys/unix/os.rs
+++ b/src/libstd/sys/unix/os.rs
@@ -22,15 +22,15 @@ use io;
 use iter;
 use libc::{self, c_int, c_char, c_void};
 use mem;
-use ptr;
 use path::{self, PathBuf};
+use ptr;
 use slice;
 use str;
 use sys::c;
 use sys::fd;
 use vec;
 
-const BUF_BYTES: usize = 2048;
+pub const BUF_BYTES: usize = 2048;
 const TMPBUF_SZ: usize = 128;
 
 fn bytes2path(b: &[u8]) -> PathBuf {
@@ -102,14 +102,19 @@ pub fn error_string(errno: i32) -> String {
 }
 
 pub fn getcwd() -> io::Result<PathBuf> {
-    let mut buf = [0 as c_char; BUF_BYTES];
-    unsafe {
-        if libc::getcwd(buf.as_mut_ptr(), buf.len() as libc::size_t).is_null() {
-            Err(io::Error::last_os_error())
-        } else {
-            Ok(bytes2path(CStr::from_ptr(buf.as_ptr()).to_bytes()))
+    super::fill_bytes_buf(|buf, len| {
+        unsafe {
+            Some(if !libc::getcwd(buf, len).is_null() {
+                Ok(bytes2path(CStr::from_ptr(buf).to_bytes()))
+            } else {
+                let error = io::Error::last_os_error();
+                if error.raw_os_error().unwrap() == libc::ERANGE {
+                    return None;
+                }
+                Err(error)
+            })
         }
-    }
+    })
 }
 
 pub fn chdir(p: &path::Path) -> io::Result<()> {