1 /*
2 * Copyright (C) 2016 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 // mov x0, x0
59 0xaa0003e0,
60 // Second page
61 // If SIGSEGV doesn't happen, make sure we return cleanly.
62 // ret
63 0xd65f03c0,
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.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