1 | // Test the behavior of malloc/calloc/realloc/new when the allocation size |
2 | // exceeds the configured max_allocation_size_mb flag. |
3 | // By default (allocator_may_return_null=0) the process should crash. With |
4 | // allocator_may_return_null=1 the allocator should return nullptr and set errno |
5 | // to the appropriate error code. |
6 | // |
7 | // RUN: %clangxx -O0 %s -o %t |
8 | // RUN: %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-NOTNULL |
9 | // RUN: %env_tool_opts=max_allocation_size_mb=3 %run %t malloc 2>&1 \ |
10 | // RUN: | FileCheck %s --check-prefix=CHECK-NOTNULL |
11 | // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=0 \ |
12 | // RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH |
13 | // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=1 \ |
14 | // RUN: %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-NULL |
15 | // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=0 \ |
16 | // RUN: not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH |
17 | // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=1 \ |
18 | // RUN: %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-NULL |
19 | // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=0 \ |
20 | // RUN: not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH |
21 | // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=1 \ |
22 | // RUN: %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-NULL |
23 | // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=0 \ |
24 | // RUN: not %run %t realloc-after-malloc 2>&1 \ |
25 | // RUN: | FileCheck %s --check-prefix=CHECK-mrCRASH |
26 | // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=1 \ |
27 | // RUN: %run %t realloc-after-malloc 2>&1 \ |
28 | // RUN: | FileCheck %s --check-prefix=CHECK-NULL |
29 | // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=0 \ |
30 | // RUN: not %run %t new 2>&1 | FileCheck %s --check-prefix=CHECK-nCRASH |
31 | // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=1 \ |
32 | // RUN: not %run %t new 2>&1 | FileCheck %s --check-prefix=CHECK-nCRASH-OOM |
33 | // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=0 \ |
34 | // RUN: not %run %t new-nothrow 2>&1 \ |
35 | // RUN: | FileCheck %s --check-prefix=CHECK-nnCRASH |
36 | // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=1 \ |
37 | // RUN: %run %t new-nothrow 2>&1 | FileCheck %s --check-prefix=CHECK-NULL |
38 | // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=0:fast_unwind_on_malloc=0 \ |
39 | // RUN: not %run %t strndup 2>&1 | FileCheck %s --check-prefix=CHECK-sCRASH |
40 | // RUN: %env_tool_opts=max_allocation_size_mb=2:allocator_may_return_null=1 \ |
41 | // RUN: %run %t strndup 2>&1 | FileCheck %s --check-prefix=CHECK-NULL |
42 | |
43 | // win32 is disabled due to failing errno tests. |
44 | // UNSUPPORTED: ubsan, target={{.*windows-msvc.*}} |
45 | |
46 | // Symbolizer needs to allocated memory when reporting. |
47 | // UNSUPPORTED: internal_symbolizer |
48 | |
49 | #include <assert.h> |
50 | #include <errno.h> |
51 | #include <limits> |
52 | #include <new> |
53 | #include <stdio.h> |
54 | #include <stdlib.h> |
55 | #include <string.h> |
56 | |
57 | constexpr size_t MaxAllocationSize = size_t{2} << 20; |
58 | |
59 | static void *allocate(const char *Action, size_t Size) { |
60 | if (!strcmp(s1: Action, s2: "malloc" )) |
61 | return malloc(size: Size); |
62 | if (!strcmp(s1: Action, s2: "calloc" )) |
63 | return calloc(nmemb: (Size + 3) / 4, size: 4); |
64 | if (!strcmp(s1: Action, s2: "realloc" )) |
65 | return realloc(ptr: nullptr, size: Size); |
66 | if (!strcmp(s1: Action, s2: "realloc-after-malloc" )) { |
67 | void *P = malloc(size: 100); |
68 | if (void *Ret = realloc(ptr: P, size: Size)) |
69 | return Ret; |
70 | free(ptr: P); |
71 | return nullptr; |
72 | } |
73 | if (!strcmp(s1: Action, s2: "new" )) |
74 | return ::operator new(Size); |
75 | if (!strcmp(Action, "new-nothrow" )) |
76 | return ::operator new(Size, std::nothrow); |
77 | if (!strcmp(s1: Action, s2: "strndup" )) { |
78 | static char pstr[MaxAllocationSize + 1] = {'a'}; |
79 | for (size_t i = 0; i < MaxAllocationSize + 1; i++) |
80 | pstr[i] = 'a'; |
81 | if (Size == MaxAllocationSize) |
82 | pstr[MaxAllocationSize - 1] = '\0'; |
83 | return strndup(string: pstr, n: Size); |
84 | } |
85 | assert(0); |
86 | } |
87 | |
88 | static void deallocate(const char *Action, void *Ptr) { |
89 | if (!strcmp(s1: Action, s2: "malloc" ) || !strcmp(s1: Action, s2: "calloc" ) || |
90 | !strcmp(s1: Action, s2: "realloc" ) || !strcmp(s1: Action, s2: "realloc-after-malloc" ) || |
91 | !strcmp(s1: Action, s2: "strndup" )) |
92 | return free(ptr: Ptr); |
93 | if (!strcmp(s1: Action, s2: "new" )) |
94 | return ::operator delete(Ptr); |
95 | if (!strcmp(Action, "new-nothrow" )) |
96 | return ::operator delete(Ptr, std::nothrow); |
97 | assert(0); |
98 | } |
99 | |
100 | int main(int Argc, char **Argv) { |
101 | assert(Argc == 2); |
102 | const char *Action = Argv[1]; |
103 | fprintf(stderr, format: "%s:\n" , Action); |
104 | |
105 | // Should succeed when max_allocation_size_mb is set. |
106 | void *volatile P = allocate(Action, Size: MaxAllocationSize); |
107 | assert(P); |
108 | deallocate(Action, Ptr: P); |
109 | |
110 | // Should fail when max_allocation_size_mb is set. |
111 | P = allocate(Action, Size: MaxAllocationSize + 1); |
112 | // The NULL pointer is printed differently on different systems, while (long)0 |
113 | // is always the same. |
114 | fprintf(stderr, format: "errno: %d, P: %lx\n" , errno, (long)P); |
115 | deallocate(Action, Ptr: P); |
116 | |
117 | // Should succeed when max_allocation_size_mb is set. |
118 | P = allocate(Action, Size: MaxAllocationSize); |
119 | assert(P); |
120 | deallocate(Action, Ptr: P); |
121 | |
122 | return 0; |
123 | } |
124 | |
125 | // CHECK-mCRASH: malloc: |
126 | // CHECK-mCRASH: #{{[0-9]+.*}}max_allocation_size.cpp |
127 | // CHECK-mCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big.* in allocate}} |
128 | // CHECK-cCRASH: calloc: |
129 | // CHECK-cCRASH: #{{[0-9]+.*}}max_allocation_size.cpp |
130 | // CHECK-cCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big.* in allocate}} |
131 | // CHECK-rCRASH: realloc: |
132 | // CHECK-rCRASH: #{{[0-9]+.*}}max_allocation_size.cpp |
133 | // CHECK-rCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big.* in allocate}} |
134 | // CHECK-mrCRASH: realloc-after-malloc: |
135 | // CHECK-mrCRASH: #{{[0-9]+.*}}max_allocation_size.cpp |
136 | // CHECK-mrCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big.* in allocate}} |
137 | // CHECK-nCRASH: new: |
138 | // CHECK-nCRASH: #{{[0-9]+.*}}max_allocation_size.cpp |
139 | // CHECK-nCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big.* in allocate}} |
140 | // CHECK-nCRASH-OOM: new: |
141 | // CHECK-nCRASH-OOM: #{{[0-9]+.*}}max_allocation_size.cpp |
142 | // CHECK-nCRASH-OOM: {{SUMMARY: .*Sanitizer: out-of-memory.* in allocate}} |
143 | // CHECK-nnCRASH: new-nothrow: |
144 | // CHECK-nnCRASH: #{{[0-9]+.*}}max_allocation_size.cpp |
145 | // CHECK-nnCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big.* in allocate}} |
146 | // CHECK-sCRASH: strndup: |
147 | // CHECK-sCRASH: #{{[0-9]+.*}}max_allocation_size.cpp |
148 | // CHECK-sCRASH: {{SUMMARY: .*Sanitizer: allocation-size-too-big.*}} |
149 | |
150 | // CHECK-NULL: {{malloc|calloc|calloc-overflow|realloc|realloc-after-malloc|new-nothrow|strndup}} |
151 | // CHECK-NULL: errno: 12, P: 0 |
152 | // |
153 | // CHECK-NOTNULL-NOT: P: 0 |
154 | |