xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/sandbox2/testcases/limits.cc (revision ec63e07ab9515d95e79c211197c445ef84cefa6a)
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