Skip to content

Commit 2dc2261

Browse files
authored
[ASan] Honor allocator_may_return_null when set through user-function and fix large alloc edge case (#117929)
**Related:** #117925 **About this PR:** This PR performs 3 small but related fixes for ASan users on Windows: 1. It ensures that the `allocator_may_return_null` flag is honored when set through the user function `__asan_default_options`. For more details, please see: #117925 2. It adds a missing `AllocatorMayReturnNull()` check inside `InternalAlloc` that's needed to avoid error'ing out when the allocator _correctly_ returns `null` when `allocator_may_return_null` is set. 3. In `sanitizer_win`'s `ReturnNullptrOnOOMOrDie`, it allows returning `null` when the last error is set to `ERROR_INVALID_PARAMETER` which may be set by `VirtualAlloc` on WIndows when attempting to allocate exceedingly large memory. I've added test cases that should cover these new behaviors. Happy to take on any feedback as well. Thank you :-) --------- Co-authored-by: David Justo <[email protected]>
1 parent b0763a4 commit 2dc2261

File tree

3 files changed

+59
-1
lines changed

3 files changed

+59
-1
lines changed

compiler-rt/lib/asan/asan_flags.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,13 @@ void InitializeFlags() {
240240

241241
DisplayHelpMessages(&asan_parser);
242242
ProcessFlags();
243+
244+
// TODO: Update other globals and data structures that may need to change
245+
// after initialization due to new flags potentially being set changing after
246+
// `__asan_default_options` is registered.
247+
// See GH issue 'https://github.com/llvm/llvm-project/issues/117925' for
248+
// details.
249+
SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
243250
});
244251

245252
# if CAN_SANITIZE_UB

compiler-rt/lib/sanitizer_common/sanitizer_win.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,24 @@ void UnmapOrDie(void *addr, uptr size, bool raw_report) {
164164
static void *ReturnNullptrOnOOMOrDie(uptr size, const char *mem_type,
165165
const char *mmap_type) {
166166
error_t last_error = GetLastError();
167-
if (last_error == ERROR_NOT_ENOUGH_MEMORY)
167+
168+
// Assumption: VirtualAlloc is the last system call that was invoked before
169+
// this method.
170+
// VirtualAlloc emits one of 2 error codes when running out of memory
171+
// 1. ERROR_NOT_ENOUGH_MEMORY:
172+
// There's not enough memory to execute the command
173+
// 2. ERROR_INVALID_PARAMETER:
174+
// VirtualAlloc will return this if the request would allocate memory at an
175+
// address exceeding or being very close to the maximum application address
176+
// (the `lpMaximumApplicationAddress` field within the `SystemInfo` struct).
177+
// This does not seem to be officially documented, but is corroborated here:
178+
// https://stackoverflow.com/questions/45833674/why-does-virtualalloc-fail-for-lpaddress-greater-than-0x6ffffffffff
179+
180+
// Note - It's possible that 'ERROR_COMMITMENT_LIMIT' needs to be handled here
181+
// as well. It is currently not handled due to the lack of a reproducer that
182+
// induces the error code.
183+
if (last_error == ERROR_NOT_ENOUGH_MEMORY ||
184+
last_error == ERROR_INVALID_PARAMETER)
168185
return nullptr;
169186
ReportMmapFailureAndDie(size, mem_type, mmap_type, last_error);
170187
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %clangxx_asan -O0 %s -o %t
2+
// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ABORT
3+
// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-RETURN-NULL
4+
5+
// RUN: %clangxx_asan -O0 %s -o %t -DUSER_FUNCTION
6+
// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-RETURN-NULL
7+
8+
#if USER_FUNCTION
9+
// On Windows, flags configured through the user-defined function `__asan_default_options`
10+
// are suspected to not always be honored according to GitHub bug:
11+
// https://github.com/llvm/llvm-project/issues/117925
12+
// This test ensures we do not regress on `allocator_may_return_null` specifically.
13+
extern "C" __declspec(dllexport) extern const char *__asan_default_options() {
14+
return "allocator_may_return_null=1";
15+
}
16+
#endif
17+
18+
#include <cstdint>
19+
#include <cstdio>
20+
#include <cstdlib>
21+
#include <limits>
22+
23+
int main() {
24+
// Attempt to allocate an excessive amount of memory, which should
25+
// terminate the program unless `allocator_may_return_null` is set.
26+
size_t max = std::numeric_limits<size_t>::max();
27+
28+
// CHECK-ABORT: exceeds maximum supported size
29+
// CHECK-ABORT: ABORT
30+
free(malloc(max));
31+
32+
printf("Success"); // CHECK-RETURN-NULL: Success
33+
return 0;
34+
}

0 commit comments

Comments
 (0)