Skip to content

Commit 17277f1

Browse files
committed
[LSan] Make LSan allocator allocator_may_return_null compliant
Summary: LSan allocator used to always return nullptr on too big allocation requests (the definition of "too big" depends on platform and bitness), now it follows policy configured by allocator_may_return_null flag. Reviewers: eugenis Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D34786 llvm-svn: 306624
1 parent 34a1872 commit 17277f1

File tree

3 files changed

+127
-3
lines changed

3 files changed

+127
-3
lines changed

compiler-rt/lib/lsan/lsan_allocator.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ void *Allocate(const StackTrace &stack, uptr size, uptr alignment,
7474
size = 1;
7575
if (size > kMaxAllowedMallocSize) {
7676
Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", size);
77-
return nullptr;
77+
return Allocator::FailureHandler::OnBadRequest();
7878
}
7979
void *p = allocator.Allocate(GetAllocatorCache(), size, alignment);
8080
// Do not rely on the allocator to clear the memory (it's slow).
@@ -99,7 +99,7 @@ void *Reallocate(const StackTrace &stack, void *p, uptr new_size,
9999
if (new_size > kMaxAllowedMallocSize) {
100100
Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", new_size);
101101
allocator.Deallocate(GetAllocatorCache(), p);
102-
return nullptr;
102+
return Allocator::FailureHandler::OnBadRequest();
103103
}
104104
p = allocator.Reallocate(GetAllocatorCache(), p, new_size, alignment);
105105
RegisterAllocation(stack, p, new_size);
@@ -134,6 +134,8 @@ void *lsan_realloc(void *p, uptr size, const StackTrace &stack) {
134134
}
135135

136136
void *lsan_calloc(uptr nmemb, uptr size, const StackTrace &stack) {
137+
if (CallocShouldReturnNullDueToOverflow(size, nmemb))
138+
return Allocator::FailureHandler::OnBadRequest();
137139
size *= nmemb;
138140
return Allocate(stack, size, 1, true);
139141
}

compiler-rt/lib/lsan/lsan_interceptors.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
7070
CHECK(allocated < kCallocPoolSize);
7171
return mem;
7272
}
73-
if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return nullptr;
7473
ENSURE_LSAN_INITED;
7574
GET_STACK_TRACE_MALLOC;
7675
return lsan_calloc(nmemb, size, stack);
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Test the behavior of malloc/calloc/realloc/new when the allocation size is
2+
// more than LSan allocator's max allowed one.
3+
// By default (allocator_may_return_null=0) the process should crash.
4+
// With allocator_may_return_null=1 the allocator should return 0, except the
5+
// operator new(), which should crash anyway (operator new(std::nothrow) should
6+
// return nullptr, indeed).
7+
//
8+
// RUN: %clangxx_lsan -O0 %s -o %t
9+
// RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH
10+
// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 \
11+
// RUN: | FileCheck %s --check-prefix=CHECK-mCRASH
12+
// RUN: %env_lsan_opts=allocator_may_return_null=1 %run %t malloc 2>&1 \
13+
// RUN: | FileCheck %s --check-prefix=CHECK-mNULL
14+
// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 \
15+
// RUN: | FileCheck %s --check-prefix=CHECK-cCRASH
16+
// RUN: %env_lsan_opts=allocator_may_return_null=1 %run %t calloc 2>&1 \
17+
// RUN: | FileCheck %s --check-prefix=CHECK-cNULL
18+
// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 \
19+
// RUN: | FileCheck %s --check-prefix=CHECK-coCRASH
20+
// RUN: %env_lsan_opts=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 \
21+
// RUN: | FileCheck %s --check-prefix=CHECK-coNULL
22+
// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t realloc 2>&1 \
23+
// RUN: | FileCheck %s --check-prefix=CHECK-rCRASH
24+
// RUN: %env_lsan_opts=allocator_may_return_null=1 %run %t realloc 2>&1 \
25+
// RUN: | FileCheck %s --check-prefix=CHECK-rNULL
26+
// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 \
27+
// RUN: | FileCheck %s --check-prefix=CHECK-mrCRASH
28+
// RUN: %env_lsan_opts=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 \
29+
// RUN: | FileCheck %s --check-prefix=CHECK-mrNULL
30+
// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t new 2>&1 \
31+
// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH
32+
// RUN: %env_lsan_opts=allocator_may_return_null=1 not %run %t new 2>&1 \
33+
// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH
34+
// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 \
35+
// RUN: | FileCheck %s --check-prefix=CHECK-nnCRASH
36+
// RUN: %env_lsan_opts=allocator_may_return_null=1 %run %t new-nothrow 2>&1 \
37+
// RUN: | FileCheck %s --check-prefix=CHECK-nnNULL
38+
39+
#include <assert.h>
40+
#include <string.h>
41+
#include <stdio.h>
42+
#include <stdlib.h>
43+
#include <limits>
44+
#include <new>
45+
46+
int main(int argc, char **argv) {
47+
// Disable stderr buffering. Needed on Windows.
48+
setvbuf(stderr, NULL, _IONBF, 0);
49+
50+
assert(argc == 2);
51+
const char *action = argv[1];
52+
fprintf(stderr, "%s:\n", action);
53+
54+
// Use max of ASan and LSan allocator limits to cover both "lsan" and
55+
// "lsan + asan" configs.
56+
static const size_t kMaxAllowedMallocSizePlusOne =
57+
#if __LP64__ || defined(_WIN64)
58+
(1ULL << 40) + 1;
59+
#else
60+
(3UL << 30) + 1;
61+
#endif
62+
63+
void *x = 0;
64+
if (!strcmp(action, "malloc")) {
65+
x = malloc(kMaxAllowedMallocSizePlusOne);
66+
} else if (!strcmp(action, "calloc")) {
67+
x = calloc((kMaxAllowedMallocSizePlusOne / 4) + 1, 4);
68+
} else if (!strcmp(action, "calloc-overflow")) {
69+
volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max();
70+
size_t kArraySize = 4096;
71+
volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10;
72+
x = calloc(kArraySize, kArraySize2);
73+
} else if (!strcmp(action, "realloc")) {
74+
x = realloc(0, kMaxAllowedMallocSizePlusOne);
75+
} else if (!strcmp(action, "realloc-after-malloc")) {
76+
char *t = (char*)malloc(100);
77+
*t = 42;
78+
x = realloc(t, kMaxAllowedMallocSizePlusOne);
79+
assert(*t == 42);
80+
free(t);
81+
} else if (!strcmp(action, "new")) {
82+
x = operator new(kMaxAllowedMallocSizePlusOne);
83+
} else if (!strcmp(action, "new-nothrow")) {
84+
x = operator new(kMaxAllowedMallocSizePlusOne, std::nothrow);
85+
} else {
86+
assert(0);
87+
}
88+
89+
// The NULL pointer is printed differently on different systems, while (long)0
90+
// is always the same.
91+
fprintf(stderr, "x: %zu\n", (size_t)x);
92+
free(x);
93+
94+
return x != 0;
95+
}
96+
97+
// CHECK-mCRASH: malloc:
98+
// CHECK-mCRASH: Sanitizer's allocator is terminating the process
99+
// CHECK-cCRASH: calloc:
100+
// CHECK-cCRASH: Sanitizer's allocator is terminating the process
101+
// CHECK-coCRASH: calloc-overflow:
102+
// CHECK-coCRASH: Sanitizer's allocator is terminating the process
103+
// CHECK-rCRASH: realloc:
104+
// CHECK-rCRASH: Sanitizer's allocator is terminating the process
105+
// CHECK-mrCRASH: realloc-after-malloc:
106+
// CHECK-mrCRASH: Sanitizer's allocator is terminating the process
107+
// CHECK-nCRASH: new:
108+
// CHECK-nCRASH: Sanitizer's allocator is terminating the process
109+
// CHECK-nnCRASH: new-nothrow:
110+
// CHECK-nnCRASH: Sanitizer's allocator is terminating the process
111+
112+
// CHECK-mNULL: malloc:
113+
// CHECK-mNULL: x: 0
114+
// CHECK-cNULL: calloc:
115+
// CHECK-cNULL: x: 0
116+
// CHECK-coNULL: calloc-overflow:
117+
// CHECK-coNULL: x: 0
118+
// CHECK-rNULL: realloc:
119+
// CHECK-rNULL: x: 0
120+
// CHECK-mrNULL: realloc-after-malloc:
121+
// CHECK-mrNULL: x: 0
122+
// CHECK-nnNULL: new-nothrow:
123+
// CHECK-nnNULL: x: 0

0 commit comments

Comments
 (0)