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 <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 // Add some valid code to the end of the first page and graceful failure rescue at the beginning of
52 // the second page.
53 constexpr uint32_t kPageCrossingCode[] = {
54 //
55 // First page
56 //
57
58 // addi sp, sp, -16
59 0xff010113,
60 // sd ra, 8(sp) (push ra)
61 //
62 // We may need ra for graceful return if SIGSEGV doesn't happen.
63 0x00113423,
64 // jalr a0
65 //
66 // The only way to check that this was executed (i.e. SIGSEGV didn't happen too early) is to
67 // print something to stderr. Call FirstPageExecutionHelper for this.
68 0x000500e7,
69 // nop
70 //
71 // Make sure we cross pages without jumps (i.e. we don't return from jalr directly to the second
72 // page).
73 0x00000013,
74
75 //
76 // Second page
77 //
78
79 // ld ra, 8(sp) (pop ra)
80 //
81 // If SIGSEGV doesn't happen, make sure we return cleanly.
82 0x00813083,
83 // addi sp, sp, 16
84 0x01010113,
85 // ret
86 0x00008067,
87 };
88
89 constexpr size_t kFirstPageInsnNum = 4;
90
FirstPageExecutionHelper()91 void FirstPageExecutionHelper() {
92 fprintf(stderr, "First page has executed");
93 }
94
TEST(HandleNotExecutable,ExecutableToNotExecutablePageCrossing)95 TEST(HandleNotExecutable, ExecutableToNotExecutablePageCrossing) {
96 const long kPageSize = sysconf(_SC_PAGESIZE);
97 // Allocate two executable pages.
98 uint32_t* first_page = reinterpret_cast<uint32_t*>(mmap(
99 0, kPageSize * 2, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
100
101 uint32_t* second_page = first_page + (kPageSize / sizeof(uint32_t));
102 // Make second page nonexecutable.
103 mprotect(second_page, kPageSize, PROT_READ | PROT_WRITE);
104
105 uint32_t* start_addr = second_page - kFirstPageInsnNum;
106 memcpy(start_addr, kPageCrossingCode, sizeof(kPageCrossingCode));
107
108 using Func = void (*)(void (*)());
109 ASSERT_EXIT((reinterpret_cast<Func>(start_addr))(&FirstPageExecutionHelper),
110 testing::KilledBySignal(SIGSEGV),
111 "First page has executed");
112
113 munmap(first_page, kPageSize * 2);
114 }
115
116 } // namespace
117