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