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 <sys/mman.h>
20 #include <unistd.h> // sysconf(_SC_PAGESIZE)
21
22 #include <cstdint>
23 #include <cstdio>
24 #include <cstring>
25
26 // Make sure compiler doesn't recognize undefined behavior and doesn't optimize out call to nullptr.
27 volatile void* g_null_addr = nullptr;
28
29 namespace {
30
TEST(HandleNotExecutable,NotExecutable)31 TEST(HandleNotExecutable, NotExecutable) {
32 uint32_t* code = reinterpret_cast<uint32_t*>(mmap(0,
33 sysconf(_SC_PAGESIZE),
34 PROT_READ | PROT_WRITE, // No PROT_EXEC!
35 MAP_PRIVATE | MAP_ANONYMOUS,
36 -1,
37 0));
38 using Func = void (*)();
39 ASSERT_EXIT((reinterpret_cast<Func>(code))(), testing::KilledBySignal(SIGSEGV), "");
40 munmap(code, sysconf(_SC_PAGESIZE));
41 }
42
TEST(HandleNotExecutable,PcLessThan4096)43 TEST(HandleNotExecutable, PcLessThan4096) {
44 using Func = void (*)();
45 ASSERT_EXIT((reinterpret_cast<Func>(const_cast<void*>(g_null_addr)))(),
46 testing::KilledBySignal(SIGSEGV),
47 "");
48 ASSERT_EXIT((reinterpret_cast<Func>(4095))(), testing::KilledBySignal(SIGSEGV), "");
49 }
50
51 constexpr uint32_t kPageCrossingCode[] = {
52 //
53 // First page
54 //
55
56 // str lr, [sp, #-8]! (push lr)
57 //
58 // We may need lr for graceful return if SIGSEGV doesn't happen.
59 0xf81f8ffe,
60 // blr x0
61 //
62 // The only way to check that this was executed (i.e. SIGSEGV didn't happen too early) is to
63 // print something to stderr. Call FirstPageExecutionHelper for this.
64 0xd63f0000,
65 // mov x0, x0
66 //
67 // Make sure we cross pages without jumps (i.e. we don't return from blx directly to the second
68 // page).
69 0xaa0003e0,
70
71 //
72 // Second page
73 //
74
75 // ldr lr, [sp], #8 (pop lr)
76 //
77 // If SIGSEGV doesn't happen, make sure we return cleanly.
78 0xf84087fe,
79 // ret
80 0xd65f03c0,
81 };
82
83 constexpr size_t kFirstPageInsnNum = 3;
84
FirstPageExecutionHelper()85 void FirstPageExecutionHelper() {
86 fprintf(stderr, "First page has executed");
87 }
88
TEST(HandleNotExecutable,ExecutableToNotExecutablePageCrossing)89 TEST(HandleNotExecutable, ExecutableToNotExecutablePageCrossing) {
90 const long kPageSize = sysconf(_SC_PAGESIZE);
91 // Allocate two executable pages.
92 uint32_t* first_page = reinterpret_cast<uint32_t*>(mmap(
93 0, kPageSize * 2, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
94
95 uint32_t* second_page = first_page + (kPageSize / sizeof(uint32_t));
96 // Make second page nonexecutable.
97 mprotect(second_page, kPageSize, PROT_READ | PROT_WRITE);
98
99 uint32_t* start_addr = second_page - kFirstPageInsnNum;
100 memcpy(start_addr, kPageCrossingCode, sizeof(kPageCrossingCode));
101
102 using Func = void (*)(void (*)());
103 ASSERT_EXIT((reinterpret_cast<Func>(start_addr))(&FirstPageExecutionHelper),
104 testing::KilledBySignal(SIGSEGV),
105 "First page has executed");
106
107 munmap(first_page, kPageSize * 2);
108 }
109
110 } // namespace
111