about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDaniel Micay <danielmicay@gmail.com>2014-01-17 20:45:48 -0500
committerDaniel Micay <danielmicay@gmail.com>2014-01-17 23:41:31 -0500
commitae2a5ecbf600495a80ae4d99853a2ed2c8f6b5e9 (patch)
tree699380bedd508918752bc0cfa7c8ebc91aff3ca0
parentaa67e13498936c42581f70daaf3b6d028426dde6 (diff)
downloadrust-ae2a5ecbf600495a80ae4d99853a2ed2c8f6b5e9.tar.gz
rust-ae2a5ecbf600495a80ae4d99853a2ed2c8f6b5e9.zip
handle zero-size allocations correctly
The `malloc` family of functions may return a null pointer for a
zero-size allocation, which should not be interpreted as an
out-of-memory error.

If the implementation does not return a null pointer, then handling
this will result in memory savings for zero-size types.

This also switches some code to `malloc_raw` in order to maintain a
centralized point for handling out-of-memory in `rt::global_heap`.

Closes #11634
-rw-r--r--src/libextra/c_vec.rs8
-rw-r--r--src/libnative/io/file.rs4
-rw-r--r--src/librustuv/net.rs4
-rw-r--r--src/librustuv/uvll.rs11
-rw-r--r--src/libstd/libc.rs2
-rw-r--r--src/libstd/rt/global_heap.rs37
-rw-r--r--src/libstd/unstable/mutex.rs13
7 files changed, 42 insertions, 37 deletions
diff --git a/src/libextra/c_vec.rs b/src/libextra/c_vec.rs
index 68499cf7032..fc2caa13584 100644
--- a/src/libextra/c_vec.rs
+++ b/src/libextra/c_vec.rs
@@ -160,21 +160,19 @@ impl <T> Container for CVec<T> {
 
 #[cfg(test)]
 mod tests {
-
     use super::*;
 
     use std::libc::*;
     use std::libc;
     use std::ptr;
+    use std::rt::global_heap::malloc_raw;
 
     fn malloc(n: uint) -> CVec<u8> {
         unsafe {
-            let mem = libc::malloc(n as size_t);
-
-            assert!(mem as int != 0);
+            let mem = malloc_raw(n);
 
             CVec::new_with_dtor(mem as *mut u8, n,
-                proc() { libc::free(mem); })
+                proc() { libc::free(mem as *c_void); })
         }
     }
 
diff --git a/src/libnative/io/file.rs b/src/libnative/io/file.rs
index 49290434785..9e1bc977082 100644
--- a/src/libnative/io/file.rs
+++ b/src/libnative/io/file.rs
@@ -13,7 +13,7 @@
 use std::c_str::CString;
 use std::io::IoError;
 use std::io;
-use std::libc::c_int;
+use std::libc::{c_int, c_void};
 use std::libc;
 use std::os;
 use std::rt::rtio;
@@ -548,7 +548,7 @@ pub fn readdir(p: &CString) -> IoResult<~[Path]> {
             let p = Path::new(p);
             let star = p.join("*");
             as_utf16_p(star.as_str().unwrap(), |path_ptr| {
-                let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint);
+                let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint) as *c_void;
                 let find_handle = FindFirstFileW(path_ptr, wfd_ptr as HANDLE);
                 if find_handle as libc::c_int != INVALID_HANDLE_VALUE {
                     let mut paths = ~[];
diff --git a/src/librustuv/net.rs b/src/librustuv/net.rs
index ee78585fcfd..a39f6ac286f 100644
--- a/src/librustuv/net.rs
+++ b/src/librustuv/net.rs
@@ -19,6 +19,7 @@ use std::rt::task::BlockedTask;
 use std::str;
 use std::unstable::finally::Finally;
 use std::vec;
+use std::rt::global_heap::malloc_raw;
 
 use homing::{HomingIO, HomeHandle};
 use stream::StreamWatcher;
@@ -122,8 +123,7 @@ fn socket_name(sk: SocketNameKind, handle: *c_void) -> Result<SocketAddr, IoErro
         // Allocate a sockaddr_storage
         // since we don't know if it's ipv4 or ipv6
         let size = uvll::rust_sockaddr_size();
-        let name = libc::malloc(size as size_t);
-        assert!(!name.is_null());
+        let name = malloc_raw(size as uint) as *c_void;
         let mut namelen = size;
 
         let ret = match getsockname(handle, name, &mut namelen) {
diff --git a/src/librustuv/uvll.rs b/src/librustuv/uvll.rs
index eefccf05a54..0dcc4c5ef52 100644
--- a/src/librustuv/uvll.rs
+++ b/src/librustuv/uvll.rs
@@ -31,8 +31,9 @@
 
 use std::libc::{size_t, c_int, c_uint, c_void, c_char, c_double};
 use std::libc::ssize_t;
-use std::libc::{malloc, free};
+use std::libc::free;
 use std::libc;
+use std::rt::global_heap::malloc_raw;
 
 #[cfg(test)]
 use std::libc::uintptr_t;
@@ -374,9 +375,7 @@ pub enum uv_membership {
 pub unsafe fn malloc_handle(handle: uv_handle_type) -> *c_void {
     assert!(handle != UV_UNKNOWN_HANDLE && handle != UV_HANDLE_TYPE_MAX);
     let size = uv_handle_size(handle);
-    let p = malloc(size);
-    assert!(p.is_not_null());
-    return p;
+    malloc_raw(size as uint) as *c_void
 }
 
 pub unsafe fn free_handle(v: *c_void) {
@@ -386,9 +385,7 @@ pub unsafe fn free_handle(v: *c_void) {
 pub unsafe fn malloc_req(req: uv_req_type) -> *c_void {
     assert!(req != UV_UNKNOWN_REQ && req != UV_REQ_TYPE_MAX);
     let size = uv_req_size(req);
-    let p = malloc(size);
-    assert!(p.is_not_null());
-    return p;
+    malloc_raw(size as uint) as *c_void
 }
 
 pub unsafe fn free_req(v: *c_void) {
diff --git a/src/libstd/libc.rs b/src/libstd/libc.rs
index 9cf94e5a1b8..77ac226a7f1 100644
--- a/src/libstd/libc.rs
+++ b/src/libstd/libc.rs
@@ -3223,7 +3223,7 @@ pub mod funcs {
                 pub fn strtoul(s: *c_char, endp: **c_char, base: c_int)
                                -> c_ulong;
                 pub fn calloc(nobj: size_t, size: size_t) -> *c_void;
-                pub fn malloc(size: size_t) -> *c_void;
+                pub fn malloc(size: size_t) -> *mut c_void;
                 pub fn realloc(p: *mut c_void, size: size_t) -> *mut c_void;
                 pub fn free(p: *c_void);
                 pub fn exit(status: c_int) -> !;
diff --git a/src/libstd/rt/global_heap.rs b/src/libstd/rt/global_heap.rs
index ce4072fb1ab..00195f726cb 100644
--- a/src/libstd/rt/global_heap.rs
+++ b/src/libstd/rt/global_heap.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use libc::{c_void, c_char, size_t, uintptr_t, free, malloc, realloc};
-use ptr::RawPtr;
+use ptr::{RawPtr, mut_null};
 use unstable::intrinsics::{TyDesc, abort};
 use unstable::raw;
 use mem::size_of;
@@ -31,24 +31,37 @@ fn align_to(size: uint, align: uint) -> uint {
 
 /// A wrapper around libc::malloc, aborting on out-of-memory
 #[inline]
-pub unsafe fn malloc_raw(size: uint) -> *c_void {
-    let p = malloc(size as size_t);
-    if p.is_null() {
-        // we need a non-allocating way to print an error here
-        abort();
+pub unsafe fn malloc_raw(size: uint) -> *mut c_void {
+    // `malloc(0)` may allocate, but it may also return a null pointer
+    // http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html
+    if size == 0 {
+        mut_null()
+    } else {
+        let p = malloc(size as size_t);
+        if p.is_null() {
+            // we need a non-allocating way to print an error here
+            abort();
+        }
+        p
     }
-    p
 }
 
 /// A wrapper around libc::realloc, aborting on out-of-memory
 #[inline]
 pub unsafe fn realloc_raw(ptr: *mut c_void, size: uint) -> *mut c_void {
-    let p = realloc(ptr, size as size_t);
-    if p.is_null() {
-        // we need a non-allocating way to print an error here
-        abort();
+    // `realloc(ptr, 0)` may allocate, but it may also return a null pointer
+    // http://pubs.opengroup.org/onlinepubs/9699919799/functions/realloc.html
+    if size == 0 {
+        free(ptr as *c_void);
+        mut_null()
+    } else {
+        let p = realloc(ptr, size as size_t);
+        if p.is_null() {
+            // we need a non-allocating way to print an error here
+            abort();
+        }
+        p
     }
-    p
 }
 
 /// The allocator for unique pointers without contained managed pointers.
diff --git a/src/libstd/unstable/mutex.rs b/src/libstd/unstable/mutex.rs
index 4d12435e01a..81317b7de79 100644
--- a/src/libstd/unstable/mutex.rs
+++ b/src/libstd/unstable/mutex.rs
@@ -167,7 +167,7 @@ mod imp {
     use libc::c_void;
     use libc;
     use ptr;
-    use ptr::RawPtr;
+    use rt::global_heap::malloc_raw;
 
     type pthread_mutex_t = libc::c_void;
     type pthread_mutexattr_t = libc::c_void;
@@ -175,16 +175,14 @@ mod imp {
     type pthread_condattr_t = libc::c_void;
 
     pub unsafe fn init_lock() -> uint {
-        let block = libc::malloc(rust_pthread_mutex_t_size() as libc::size_t);
-        assert!(!block.is_null());
+        let block = malloc_raw(rust_pthread_mutex_t_size() as uint) as *c_void;
         let n = pthread_mutex_init(block, ptr::null());
         assert_eq!(n, 0);
         return block as uint;
     }
 
     pub unsafe fn init_cond() -> uint {
-        let block = libc::malloc(rust_pthread_cond_t_size() as libc::size_t);
-        assert!(!block.is_null());
+        let block = malloc_raw(rust_pthread_cond_t_size() as uint) as *c_void;
         let n = pthread_cond_init(block, ptr::null());
         assert_eq!(n, 0);
         return block as uint;
@@ -249,14 +247,13 @@ mod imp {
     use libc;
     use libc::{HANDLE, BOOL, LPSECURITY_ATTRIBUTES, c_void, DWORD, LPCSTR};
     use ptr;
-    use ptr::RawPtr;
+    use rt::global_heap::malloc_raw;
 
     type LPCRITICAL_SECTION = *c_void;
     static SPIN_COUNT: DWORD = 4000;
 
     pub unsafe fn init_lock() -> uint {
-        let block = libc::malloc(rust_crit_section_size() as libc::size_t);
-        assert!(!block.is_null());
+        let block = malloc_raw(rust_crit_section_size() as uint) as *c_void;
         InitializeCriticalSectionAndSpinCount(block, SPIN_COUNT);
         return block as uint;
     }