• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "gtest/gtest.h"
18 
19 #include <setjmp.h>
20 #include <sys/mman.h>
21 #include <unistd.h>  // sysconf(_SC_PAGESIZE)
22 
23 #include <cstdint>
24 #include <cstdio>
25 #include <cstring>
26 
27 #include "berberis/ndk_program_tests/scoped_sigaction.h"
28 
29 // Make sure compiler doesn't recognize undefined behavior and doesn't optimize out call to nullptr.
30 volatile void* g_null_addr = nullptr;
31 
32 namespace {
33 
TEST(HandleNotExecutable,NotExecutable)34 TEST(HandleNotExecutable, NotExecutable) {
35   uint32_t* code = reinterpret_cast<uint32_t*>(mmap(0,
36                                                     sysconf(_SC_PAGESIZE),
37                                                     PROT_READ | PROT_WRITE,  // No PROT_EXEC!
38                                                     MAP_PRIVATE | MAP_ANONYMOUS,
39                                                     -1,
40                                                     0));
41   using Func = void (*)();
42   ASSERT_EXIT((reinterpret_cast<Func>(code))(), testing::KilledBySignal(SIGSEGV), "");
43   munmap(code, sysconf(_SC_PAGESIZE));
44 }
45 
TEST(HandleNotExecutable,PcLessThan4096)46 TEST(HandleNotExecutable, PcLessThan4096) {
47   using Func = void (*)();
48   ASSERT_EXIT((reinterpret_cast<Func>(const_cast<void*>(g_null_addr)))(),
49               testing::KilledBySignal(SIGSEGV),
50               "");
51   ASSERT_EXIT((reinterpret_cast<Func>(4095))(), testing::KilledBySignal(SIGSEGV), "");
52 }
53 
54 // Add some valid code to the end of the first page and graceful failure rescue at the beginning of
55 // the second page.
56 constexpr uint32_t kPageCrossingCode[] = {
57     // First page
58     // nop
59     0x00000013,
60     // Second page
61     // If SIGSEGV doesn't happen, make sure we return cleanly.
62     // ret
63     0x00008067,
64 };
65 
66 constexpr size_t kFirstPageCodeSize = 4;
67 sigjmp_buf g_jmpbuf;
68 uint8_t* g_noexec_page_addr = nullptr;
69 
SigsegvHandler(int,siginfo_t *,void * ctx)70 void SigsegvHandler(int /* sig */, siginfo_t* /* info */, void* ctx) {
71   fprintf(stderr, "SIGSEGV caught\n");
72   // Warning: do not use ASSERT, so that we recover with longjump unconditionally.
73   // Otherwise we'll be calling the handler in infinite loop.
74   EXPECT_EQ(static_cast<ucontext*>(ctx)->uc_mcontext.__gregs[REG_PC],
75             reinterpret_cast<uintptr_t>(g_noexec_page_addr));
76   longjmp(g_jmpbuf, 1);
77 }
78 
TEST(HandleNotExecutable,ExecutableToNotExecutablePageCrossing)79 TEST(HandleNotExecutable, ExecutableToNotExecutablePageCrossing) {
80   const long kPageSize = sysconf(_SC_PAGESIZE);
81   // Allocate two pages.
82   uint8_t* first_page = static_cast<uint8_t*>(
83       mmap(0, kPageSize * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
84   // Make first page executable.
85   mprotect(first_page, kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC);
86 
87   g_noexec_page_addr = first_page + kPageSize;
88   uint8_t* start_addr = g_noexec_page_addr - kFirstPageCodeSize;
89   memcpy(start_addr, kPageCrossingCode, sizeof(kPageCrossingCode));
90 
91   struct sigaction sa;
92   sa.sa_flags = SA_SIGINFO;
93   sigemptyset(&sa.sa_mask);
94   sa.sa_sigaction = SigsegvHandler;
95   ScopedSigaction scoped_sa(SIGSEGV, &sa);
96 
97   if (setjmp(g_jmpbuf) == 0) {
98     fprintf(stderr, "Jumping to executable page before non-executable page\n");
99     reinterpret_cast<void (*)()>(start_addr)();
100     ADD_FAILURE() << "Function call should not have returned";
101   } else {
102     fprintf(stderr, "Successful recovery\n");
103   }
104 
105   munmap(first_page, kPageSize * 2);
106 }
107 
108 }  // namespace
109