Vulnerability: Multiple integer overflows in the (1) chk_malloc, (2) leak_malloc, and (3) leak_memalign functions in libc/bionic/malloc_debug_leak.c in Bionic (libc) for Android, when libc.debug.malloc is set, make it easier for context-dependent attackers to perform memory-related attacks such as buffer overflows via a large size value, which causes less memory to be allocated than expected.
Vulnerability ID: CVE-2012-2674
CWE: CWE-189
Severity score: 2.9
Project: https://github.com/android/platform_bionic
Published: 2012-07-25
File: libc/bionic/malloc_debug_leak.c
Commit message: bionic: fix integer overflows in chk_malloc(), leak_malloc(), and leak_memalign()

The allocation size in chk_malloc(), leak_malloc(), and leak_memalign()
functions may be rounded up to a small value, leading to buffer overflows.
The code only runs in debugging mode.

This patch complements commit 6f04a0f4 (CVE-2009-0607).

Change-Id: Id899bcd2bcd2ea2205e5753c433390710032dc83
Signed-off-by: Xi Wang <xi.wang@gmail.com>
Commit author: Xi Wang

Vulnerable version (before fix):
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  * SUCH DAMAGE.  */ #include <errno.h> #include <pthread.h> #include <stdio.h> #include <arpa/inet.h> #include <sys/socket.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <stddef.h> #include <stdarg.h> #include <fcntl.h> #include <unwind.h> #include <dlfcn.h>  #include <sys/socket.h> #include <sys/un.h> #include <sys/select.h> #include <sys/types.h> #include <sys/system_properties.h>  #include "dlmalloc.h" #include "logd.h" static int chk_mem_check(void*       mem,  void* chk_malloc(size_t bytes) {     char* buffer = (char*)dlmalloc(bytes + CHK_OVERHEAD_SIZE);     if (buffer) {         memset(buffer, CHK_SENTINEL_VALUE, bytes + CHK_OVERHEAD_SIZE);         size_t offset = dlmalloc_usable_size(buffer) - sizeof(size_t); void* leak_malloc(size_t bytes)     // 1. allocate enough memory and include our header     // 2. set the base pointer to be right after our header      void* base = dlmalloc(bytes + sizeof(AllocationEntry));     if (base != NULL) {         pthread_mutex_lock(&gAllocationsMutex);  void* leak_memalign(size_t alignment, size_t bytes)     // we will align by at least MALLOC_ALIGNMENT bytes     // and at most alignment-MALLOC_ALIGNMENT bytes     size_t size = (alignment-MALLOC_ALIGNMENT) + bytes;     void* base = leak_malloc(size);     if (base != NULL) {         intptr_t ptr = (intptr_t)base;

Fixed version (after fix):
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  * SUCH DAMAGE.  */  #include <dlfcn.h> #include <errno.h> #include <fcntl.h> #include <pthread.h> #include <stdarg.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <unwind.h>  #include <arpa/inet.h> #include <sys/select.h> #include <sys/socket.h> #include <sys/system_properties.h> #include <sys/types.h> #include <sys/un.h>  #include "dlmalloc.h" #include "logd.h" static int chk_mem_check(void*       mem,  void* chk_malloc(size_t bytes) {     size_t size = bytes + CHK_OVERHEAD_SIZE;     if (size < bytes) { // Overflow.         return NULL;     }     uint8_t* buffer = (uint8_t*) dlmalloc(size);     if (buffer) {         memset(buffer, CHK_SENTINEL_VALUE, bytes + CHK_OVERHEAD_SIZE);         size_t offset = dlmalloc_usable_size(buffer) - sizeof(size_t); void* leak_malloc(size_t bytes)     // 1. allocate enough memory and include our header     // 2. set the base pointer to be right after our header      size_t size = bytes + sizeof(AllocationEntry);     if (size < bytes) { // Overflow.         return NULL;     }      void* base = dlmalloc(size);     if (base != NULL) {         pthread_mutex_lock(&gAllocationsMutex);  void* leak_memalign(size_t alignment, size_t bytes)     // we will align by at least MALLOC_ALIGNMENT bytes     // and at most alignment-MALLOC_ALIGNMENT bytes     size_t size = (alignment-MALLOC_ALIGNMENT) + bytes;     if (size < bytes) { // Overflow.         return NULL;     }      void* base = leak_malloc(size);     if (base != NULL) {         intptr_t ptr = (intptr_t)base;

Patch:
patch: @@ -25,26 +25,26 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
+#include <dlfcn.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <pthread.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdio.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <errno.h>
-#include <stddef.h>
-#include <stdarg.h>
-#include <fcntl.h>
 #include <unwind.h>
-#include <dlfcn.h>
 
-#include <sys/socket.h>
-#include <sys/un.h>
+#include <arpa/inet.h>
 #include <sys/select.h>
-#include <sys/types.h>
+#include <sys/socket.h>
 #include <sys/system_properties.h>
+#include <sys/types.h>
+#include <sys/un.h>
 
 #include "dlmalloc.h"
 #include "logd.h"
@@ -372,7 +372,11 @@ static int chk_mem_check(void*       mem,
 
 void* chk_malloc(size_t bytes)
 {
-    char* buffer = (char*)dlmalloc(bytes + CHK_OVERHEAD_SIZE);
+    size_t size = bytes + CHK_OVERHEAD_SIZE;
+    if (size < bytes) { // Overflow.
+        return NULL;
+    }
+    uint8_t* buffer = (uint8_t*) dlmalloc(size);
     if (buffer) {
         memset(buffer, CHK_SENTINEL_VALUE, bytes + CHK_OVERHEAD_SIZE);
         size_t offset = dlmalloc_usable_size(buffer) - sizeof(size_t);
@@ -505,7 +509,12 @@ void* leak_malloc(size_t bytes)
     // 1. allocate enough memory and include our header
     // 2. set the base pointer to be right after our header
 
-    void* base = dlmalloc(bytes + sizeof(AllocationEntry));
+    size_t size = bytes + sizeof(AllocationEntry);
+    if (size < bytes) { // Overflow.
+        return NULL;
+    }
+
+    void* base = dlmalloc(size);
     if (base != NULL) {
         pthread_mutex_lock(&gAllocationsMutex);
 
@@ -615,6 +624,10 @@ void* leak_memalign(size_t alignment, size_t bytes)
     // we will align by at least MALLOC_ALIGNMENT bytes
     // and at most alignment-MALLOC_ALIGNMENT bytes
     size_t size = (alignment-MALLOC_ALIGNMENT) + bytes;
+    if (size < bytes) { // Overflow.
+        return NULL;
+    }
+
     void* base = leak_malloc(size);
     if (base != NULL) {
         intptr_t ptr = (intptr_t)base;
path: libc/bionic/malloc_debug_leak.c