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 "berberis/kernel_api/sys_mman_emulation.h"
18
19 #include <sys/mman.h>
20
21 #include <cerrno>
22
23 #include "berberis/base/mmap.h"
24 #include "berberis/base/prctl_helpers.h"
25 #include "berberis/base/tracing.h"
26 #include "berberis/guest_os_primitives/guest_map_shadow.h"
27 #include "berberis/guest_state/guest_addr.h"
28
29 namespace berberis {
30
31 namespace {
32
ToHostProt(int guest_prot)33 int ToHostProt(int guest_prot) {
34 if (guest_prot & PROT_EXEC) {
35 // Guest EXEC should _not_ be host EXEC but should be host READ!
36 return (guest_prot & ~PROT_EXEC) | PROT_READ;
37 }
38 return guest_prot;
39 }
40
41 // Clobbers errno.
UpdateGuestProt(int guest_prot,void * addr,size_t length)42 void UpdateGuestProt(int guest_prot, void* addr, size_t length) {
43 GuestAddr guest_addr = ToGuestAddr(addr);
44 GuestMapShadow* shadow = GuestMapShadow::GetInstance();
45 if (guest_prot & PROT_EXEC) {
46 // Since we strip guest executable bit from host mappings kernel may merge r-- and r-x guest
47 // mappings together, which is difficult to split back when emulating /proc/self/maps. Setting
48 // region name helps to prevent regions merging. It helps even if it's a file backed mapping,
49 // even though filename isn't visibly changed in /proc/self/maps in this case.
50 // Note that this name can be overridden by the app, which is fine as long as it's
51 // unique for this mapping. We do not remove this name if executable bit is
52 // removed which also should be fine since it's just a hint.
53 int res = SetVmaAnonName(addr, AlignUpPageSize(length), "[guest exec mapping hint]");
54 if (res == -1) {
55 TRACE("PR_SET_VMA_ANON_NAME failed with errno=%s", std::strerror(errno));
56 }
57
58 shadow->SetExecutable(guest_addr, length);
59 } else {
60 shadow->ClearExecutable(guest_addr, length);
61 }
62 }
63
64 } // namespace
65
66 // ATTENTION: the order of mmap/mprotect/munmap and SetExecutable/ClearExecutable is essential!
67 //
68 // The issue here is that threads might be executing the code being munmap'ed or mprotect'ed.
69 // SetExecutable/ClearExecutable should flush code cache and notify threads to restart.
70 // If other thread starts translation after actual mmap/mprotect/munmap but before xbit update,
71 // it might pick up an already obsolete code.
72
MmapForGuest(void * addr,size_t length,int prot,int flags,int fd,off64_t offset)73 void* MmapForGuest(void* addr, size_t length, int prot, int flags, int fd, off64_t offset) {
74 void* result = mmap64(addr, length, ToHostProt(prot), flags, fd, offset);
75 if (result != MAP_FAILED) {
76 UpdateGuestProt(prot, result, length);
77 }
78 return result;
79 }
80
MunmapForGuest(void * addr,size_t length)81 int MunmapForGuest(void* addr, size_t length) {
82 GuestMapShadow::GetInstance()->ClearExecutable(ToGuestAddr(addr), length);
83 return munmap(addr, length);
84 }
85
MprotectForGuest(void * addr,size_t length,int prot)86 int MprotectForGuest(void* addr, size_t length, int prot) {
87 // In b/218772975 the app is scanning "/proc/self/maps" and tries to mprotect
88 // mappings for some libraries found there (for unknown reason) effectively removing
89 // execution permission. GuestMapShadow is pre-populated with such mappings, so we
90 // suppress guest mprotect for them.
91 if (GuestMapShadow::GetInstance()->IntersectsWithProtectedMapping(
92 addr, static_cast<char*>(addr) + length)) {
93 TRACE("Suppressing guest mprotect(%p, %zu) on a mapping protected from guest", addr, length);
94 errno = EACCES;
95 return -1;
96 }
97
98 UpdateGuestProt(prot, addr, length);
99 return mprotect(addr, length, ToHostProt(prot));
100 }
101
MremapForGuest(void * old_addr,size_t old_size,size_t new_size,int flags,void * new_addr)102 void* MremapForGuest(void* old_addr, size_t old_size, size_t new_size, int flags, void* new_addr) {
103 // As we drop xbit for host mmap calls, host mappings might differ from guest
104 // mappings, and host mremap might work when guest mremap should not. Check in
105 // advance to avoid that. Rules for checks:
106 // 1. Shrink without MREMAP_FIXED - always Ok.
107 // 2. Shrink with MREMAP_FIXED - needs consistent permissions within new_size.
108 // 3. Grow - needs consistent permissions within old_size.
109 GuestMapShadow* shadow = GuestMapShadow::GetInstance();
110 if (new_size <= old_size) {
111 if ((flags & MREMAP_FIXED) &&
112 shadow->GetExecutable(ToGuestAddr(old_addr), new_size) == kBitMixed) {
113 errno = EFAULT;
114 return MAP_FAILED;
115 }
116 } else {
117 if (shadow->GetExecutable(ToGuestAddr(old_addr), old_size) == kBitMixed) {
118 errno = EFAULT;
119 return MAP_FAILED;
120 }
121 }
122
123 void* result = mremap(old_addr, old_size, new_size, flags, new_addr);
124
125 if (result != MAP_FAILED) {
126 shadow->RemapExecutable(ToGuestAddr(old_addr), old_size, ToGuestAddr(result), new_size);
127 }
128 return result;
129 }
130
131 } // namespace berberis
132