• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     shadow->SetExecutable(guest_addr, length);
47   } else {
48     shadow->ClearExecutable(guest_addr, length);
49   }
50 }
51 
52 }  // namespace
53 
54 // ATTENTION: the order of mmap/mprotect/munmap and SetExecutable/ClearExecutable is essential!
55 //
56 // The issue here is that threads might be executing the code being munmap'ed or mprotect'ed.
57 // SetExecutable/ClearExecutable should flush code cache and notify threads to restart.
58 // If other thread starts translation after actual mmap/mprotect/munmap but before xbit update,
59 // it might pick up an already obsolete code.
60 
MmapForGuest(void * addr,size_t length,int prot,int flags,int fd,off64_t offset)61 void* MmapForGuest(void* addr, size_t length, int prot, int flags, int fd, off64_t offset) {
62   void* result = mmap64(addr, length, ToHostProt(prot), flags, fd, offset);
63   if (result != MAP_FAILED) {
64     UpdateGuestProt(prot, result, length);
65   }
66   return result;
67 }
68 
MunmapForGuest(void * addr,size_t length)69 int MunmapForGuest(void* addr, size_t length) {
70   GuestMapShadow::GetInstance()->ClearExecutable(ToGuestAddr(addr), length);
71   return munmap(addr, length);
72 }
73 
MprotectForGuest(void * addr,size_t length,int prot)74 int MprotectForGuest(void* addr, size_t length, int prot) {
75   // In b/218772975 the app is scanning "/proc/self/maps" and tries to mprotect
76   // mappings for some libraries found there (for unknown reason) effectively removing
77   // execution permission. GuestMapShadow is pre-populated with such mappings, so we
78   // suppress guest mprotect for them.
79   if (GuestMapShadow::GetInstance()->IntersectsWithProtectedMapping(
80           addr, static_cast<char*>(addr) + length)) {
81     TRACE("Suppressing guest mprotect(%p, %zu) on a mapping protected from guest", addr, length);
82     errno = EACCES;
83     return -1;
84   }
85 
86   UpdateGuestProt(prot, addr, length);
87   return mprotect(addr, length, ToHostProt(prot));
88 }
89 
MremapForGuest(void * old_addr,size_t old_size,size_t new_size,int flags,void * new_addr)90 void* MremapForGuest(void* old_addr, size_t old_size, size_t new_size, int flags, void* new_addr) {
91   // As we drop xbit for host mmap calls, host mappings might differ from guest
92   // mappings, and host mremap might work when guest mremap should not. Check in
93   // advance to avoid that. Rules for checks:
94   // 1. Shrink without MREMAP_FIXED - always Ok.
95   // 2. Shrink with MREMAP_FIXED - needs consistent permissions within new_size.
96   // 3. Grow - needs consistent permissions within old_size.
97   GuestMapShadow* shadow = GuestMapShadow::GetInstance();
98   if (new_size <= old_size) {
99     if ((flags & MREMAP_FIXED) &&
100         shadow->GetExecutable(ToGuestAddr(old_addr), new_size) == kBitMixed) {
101       errno = EFAULT;
102       return MAP_FAILED;
103     }
104   } else {
105     if (shadow->GetExecutable(ToGuestAddr(old_addr), old_size) == kBitMixed) {
106       errno = EFAULT;
107       return MAP_FAILED;
108     }
109   }
110 
111   void* result = mremap(old_addr, old_size, new_size, flags, new_addr);
112 
113   if (result != MAP_FAILED) {
114     shadow->RemapExecutable(ToGuestAddr(old_addr), old_size, ToGuestAddr(result), new_size);
115   }
116   return result;
117 }
118 
119 }  // namespace berberis
120