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