1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // A binary to test sandbox2 limits.
16 // Per setrlimit(2): exceeding RLIMIT_AS with mmap, brk or mremap do not
17 // kill but fail with ENOMEM. However if we trigger automatic stack
18 // expansion, for instance with a large stack allocation with alloca(3),
19 // and we have no alternate stack, then we are killed with SIGSEGV.
20
21 #include <alloca.h>
22 #include <sys/mman.h>
23
24 #include <cerrno>
25 #include <cstdio>
26 #include <cstdlib>
27
TestMmapUnderLimit(void)28 int TestMmapUnderLimit(void) {
29 // mmap should work
30 void* ptr = mmap(0, 1ULL << 20 /* 1 MiB */, PROT_READ | PROT_WRITE,
31 MAP_ANONYMOUS | MAP_SHARED, -1, 0);
32 if (ptr == MAP_FAILED) {
33 return EXIT_FAILURE;
34 }
35 return EXIT_SUCCESS;
36 }
37
TestMmapAboveLimit(void)38 int TestMmapAboveLimit(void) {
39 // mmap should fail with ENOMEM
40 void* ptr = mmap(0, 100ULL << 20 /* 100 MiB */, PROT_READ | PROT_WRITE,
41 MAP_ANONYMOUS | MAP_SHARED, -1, 0);
42 if (ptr != MAP_FAILED || errno != ENOMEM) {
43 return EXIT_FAILURE;
44 }
45 return EXIT_SUCCESS;
46 }
47
48 // Tests using alloca are marked noinline because clang in optimized mode tries
49 // to inline the test function, and then "optimizes" it by moving the alloca
50 // stack allocation to the beginning of main() and merging it with main()'s
51 // local variable allocation. This is specially inconvenient for TestAllocaBig*
52 // functions below, because they make an allocation big enough to kill the
53 // process, and with inlining they get to kill the process every time.
54 //
55 // This workaround makes sure the stack allocation is only done when the test
56 // function is actually called.
57
TestAllocaSmallUnderLimit()58 __attribute__((noinline)) int TestAllocaSmallUnderLimit() {
59 void* ptr = alloca(1ULL << 20 /* 1 MiB */);
60 printf("alloca worked (ptr=%p)\n", ptr);
61 return EXIT_SUCCESS;
62 }
63
TestAllocaBigUnderLimit()64 __attribute__((noinline)) int TestAllocaBigUnderLimit() {
65 void* ptr = alloca(8ULL << 20 /* 8 MiB */);
66 printf("We should have been killed by now (ptr=%p)\n", ptr);
67 return EXIT_FAILURE;
68 }
69
TestAllocaBigAboveLimit()70 __attribute__((noinline)) int TestAllocaBigAboveLimit() {
71 void* ptr = alloca(100ULL << 20 /* 100 MiB */);
72 printf("We should have been killed by now (ptr=%p)\n", ptr);
73 return EXIT_FAILURE;
74 }
75
main(int argc,char * argv[])76 int main(int argc, char* argv[]) {
77 // Disable buffering.
78 setbuf(stdin, nullptr);
79 setbuf(stdout, nullptr);
80 setbuf(stderr, nullptr);
81
82 if (argc < 2) {
83 printf("argc < 2\n");
84 return EXIT_FAILURE;
85 }
86
87 int testno = atoi(argv[1]); // NOLINT
88 switch (testno) {
89 case 1:
90 return TestMmapUnderLimit();
91 case 2:
92 return TestMmapAboveLimit();
93 case 3:
94 return TestAllocaSmallUnderLimit();
95 case 4:
96 return TestAllocaBigUnderLimit();
97 case 5:
98 return TestAllocaBigAboveLimit();
99 default:
100 printf("Unknown test: %d\n", testno);
101 return EXIT_FAILURE;
102 }
103
104 return EXIT_SUCCESS;
105 }
106