• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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/open_emulation.h"
18 
19 #include <fcntl.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 
23 #include <cstdint>
24 #include <cstdio>
25 #include <cstring>
26 #include <mutex>
27 #include <utility>
28 
29 #include "berberis/base/arena_alloc.h"
30 #include "berberis/base/arena_map.h"
31 #include "berberis/base/arena_string.h"
32 #include "berberis/base/arena_vector.h"
33 #include "berberis/base/checks.h"
34 #include "berberis/base/fd.h"
35 #include "berberis/base/forever_alloc.h"
36 #include "berberis/base/tracing.h"
37 #include "berberis/guest_os_primitives/guest_map_shadow.h"
38 #include "berberis/guest_state/guest_addr.h"
39 #include "berberis/kernel_api/main_executable_real_path_emulation.h"
40 
41 namespace berberis {
42 
43 namespace {
44 
45 class EmulatedFileDescriptors {
46  public:
EmulatedFileDescriptors()47   explicit EmulatedFileDescriptors() : fds_(&arena_) {}
48 
GetInstance()49   static EmulatedFileDescriptors* GetInstance() {
50     static auto* g_emulated_proc_self_maps_fds = NewForever<EmulatedFileDescriptors>();
51     return g_emulated_proc_self_maps_fds;
52   }
53 
54   // Not copyable or movable.
55   EmulatedFileDescriptors(const EmulatedFileDescriptors&) = delete;
56   EmulatedFileDescriptors& operator=(const EmulatedFileDescriptors&) = delete;
57 
Add(int fd)58   void Add(int fd) {
59     std::lock_guard lock(mutex_);
60     auto [unused_it, inserted] = fds_.insert(std::make_pair(fd, 0));
61     if (!inserted) {
62       // We expect every fd to be added at most once. But if it breaks let's consider it non-fatal.
63       TRACE("Detected duplicated fd in EmulatedFileDescriptors");
64     }
65   }
66 
Contains(int fd)67   bool Contains(int fd) {
68     std::lock_guard lock(mutex_);
69     return fds_.find(fd) != fds_.end();
70   }
71 
Remove(int fd)72   void Remove(int fd) {
73     std::lock_guard lock(mutex_);
74     auto it = fds_.find(fd);
75     if (it != fds_.end()) {
76       fds_.erase(it);
77     }
78   }
79 
80  private:
81   std::mutex mutex_;
82   Arena arena_;
83   // We use it as a set because we don't have ArenaSet, so client data isn't really used.
84   ArenaMap<int, int> fds_;
85 };
86 
87 // It's macro since we use it as string literal below.
88 #define PROC_SELF_MAPS "/proc/self/maps"
89 
90 // Reader that works with custom allocator strings. Based on android::base::ReadFileToString.
91 template <typename String>
ReadProcSelfMapsToString(String & content)92 bool ReadProcSelfMapsToString(String& content) {
93   int fd = open(PROC_SELF_MAPS, O_RDONLY);
94   if (fd == -1) {
95     return false;
96   }
97   char buf[4096] __attribute__((__uninitialized__));
98   ssize_t n;
99   while ((n = read(fd, &buf[0], sizeof(buf))) > 0) {
100     content.append(buf, n);
101   }
102   close(fd);
103   return n == 0;
104 }
105 
106 // String split that works with custom allocator strings. Based on android::base::Split.
107 template <typename String>
SplitLines(Arena * arena,const String & content)108 ArenaVector<String> SplitLines(Arena* arena, const String& content) {
109   ArenaVector<String> lines(arena);
110   size_t base = 0;
111   size_t found;
112   while (true) {
113     found = content.find_first_of('\n', base);
114     lines.emplace_back(content, base, found - base, content.get_allocator());
115     if (found == content.npos) break;
116     base = found + 1;
117   }
118   return lines;
119 }
120 
121 // Note that dirfd, flags and mode are only used to fallback to
122 // host's openat in case of failure.
123 // Avoid mallocs since bionic tests use it under malloc_disable (b/338211718).
OpenatProcSelfMapsForGuest(int dirfd,int flags,mode_t mode)124 int OpenatProcSelfMapsForGuest(int dirfd, int flags, mode_t mode) {
125   TRACE("Openat for " PROC_SELF_MAPS);
126 
127   Arena arena;
128   ArenaString file_data(&arena);
129   bool success = ReadProcSelfMapsToString(file_data);
130   if (!success) {
131     TRACE("Cannot read " PROC_SELF_MAPS ", falling back to host's openat");
132     return openat(dirfd, PROC_SELF_MAPS, flags, mode);
133   }
134 
135   int mem_fd = CreateMemfdOrDie("[guest " PROC_SELF_MAPS "]");
136 
137   auto* maps_shadow = GuestMapShadow::GetInstance();
138 
139   auto lines = SplitLines(&arena, file_data);
140   ArenaString guest_maps(&arena);
141   for (size_t i = 0; i < lines.size(); i++) {
142     uintptr_t start;
143     uintptr_t end;
144     int prot_offset;
145     auto& cur_line = lines.at(i);
146     if (sscanf(cur_line.c_str(), "%" SCNxPTR "-%" SCNxPTR " %n", &start, &end, &prot_offset) != 2) {
147       if (!cur_line.empty()) {
148         TRACE("Cannot parse " PROC_SELF_MAPS " line : %s", cur_line.c_str());
149       }
150       guest_maps.append(cur_line + "\n");
151       continue;
152     }
153     // Split the line into guest exec / no-exec chunks.
154     uintptr_t original_start = start;
155     while (start < end) {
156       auto [is_exec, region_size] =
157           maps_shadow->GetExecutableRegionSize(GuestAddr(start), end - start);
158       // prot_offset points to "rwxp", so offset of "x" is 2 symbols away.
159       cur_line.at(prot_offset + 2) = is_exec ? 'x' : '-';
160       if ((start == original_start) && ((start + region_size) >= end)) {
161         // Most often we should be able to just take the whole host line.
162         guest_maps.append(cur_line);
163         guest_maps.append("\n");
164         break;
165       }
166       // We cannot print into cur_line in place since we don't want the terminating null. Also the
167       // new range can theoretically be longer than the old one. E.g. if "a000-ba000" (len=10) is
168       // split into "a000-aa000" (len=10) and "aa000-ba000" (len=11).
169       // At max, for 64-bit pointers, we need 16(ptr)+1(-)+16(ptr)+1(\0)=34 symbols buffer,
170       // so 64-bytes should be more than enough.
171       char addr_range_buf[64];
172       int chars_num = snprintf(addr_range_buf,
173                                sizeof(addr_range_buf),
174                                "%" PRIxPTR "-%" PRIxPTR,
175                                start,
176                                start + region_size);
177       CHECK_LT(static_cast<size_t>(chars_num), sizeof(addr_range_buf));
178       guest_maps.append(addr_range_buf);
179       // Append the rest of the line starting from protections and including the front space.
180       guest_maps.append(cur_line.data() + prot_offset - 1);
181       guest_maps.append("\n");
182       start += region_size;
183     }
184   }
185 
186   // Normally /proc/self/maps doesn't have newline at the end.
187   // It's simpler to remove it than to not add it in the loop.
188   CHECK_EQ(guest_maps.back(), '\n');
189   guest_maps.pop_back();
190 
191   TRACE("--------\n%s\n--------", guest_maps.c_str());
192 
193   WriteFullyOrDie(mem_fd, guest_maps.c_str(), guest_maps.size());
194 
195   lseek(mem_fd, 0, 0);
196 
197   EmulatedFileDescriptors::GetInstance()->Add(mem_fd);
198 
199   return mem_fd;
200 }
201 
IsProcSelfMaps(const char * path,int flags)202 bool IsProcSelfMaps(const char* path, int flags) {
203   struct stat cur_stat;
204   struct stat proc_stat;
205   // This check works for /proc/self/maps itself as well as symlinks (unless AT_SYMLINK_NOFOLLOW is
206   // requested). As an added benefit it gracefully handles invalid pointers in path.
207   return stat(path, &cur_stat) == 0 && stat(PROC_SELF_MAPS, &proc_stat) == 0 &&
208          !(S_ISLNK(cur_stat.st_mode) && (flags & AT_SYMLINK_NOFOLLOW)) &&
209          cur_stat.st_ino == proc_stat.st_ino && cur_stat.st_dev == proc_stat.st_dev;
210 }
211 
212 // In zygote this is not needed because native bridge is mounting
213 // /proc/cpuinfo to point to the emulated version. But for executables
214 // this does not happen and they end up reading host cpuinfo.
215 //
216 // Note that current selinux policies prevent us from mounting /proc/cpuinfo
217 // (replicating what zygote process does) for executables hence we need to
218 // emulate it here.
TryTranslateProcCpuinfoPath(const char * path,int flags)219 const char* TryTranslateProcCpuinfoPath(const char* path, int flags) {
220 #if defined(__ANDROID__)
221   struct stat cur_stat;
222   struct stat cpuinfo_stat;
223 
224   if (stat(path, &cur_stat) == -1 || stat("/proc/cpuinfo", &cpuinfo_stat) == -1) {
225     return nullptr;
226   }
227 
228   if (S_ISLNK(cur_stat.st_mode) && (flags & AT_SYMLINK_NOFOLLOW)) {
229     return nullptr;
230   }
231 
232   if ((cur_stat.st_ino == cpuinfo_stat.st_ino) && (cur_stat.st_dev == cpuinfo_stat.st_dev)) {
233     TRACE("openat: Translating %s to %s", path, kGuestCpuinfoPath);
234     return kGuestCpuinfoPath;
235   }
236 #else
237   UNUSED(path, flags);
238 #endif
239   return nullptr;
240 }
241 
242 }  // namespace
243 
IsFileDescriptorEmulatedProcSelfMaps(int fd)244 bool IsFileDescriptorEmulatedProcSelfMaps(int fd) {
245   return EmulatedFileDescriptors::GetInstance()->Contains(fd);
246 }
247 
CloseEmulatedProcSelfMapsFileDescriptor(int fd)248 void CloseEmulatedProcSelfMapsFileDescriptor(int fd) {
249   EmulatedFileDescriptors::GetInstance()->Remove(fd);
250 }
251 
OpenatForGuest(int dirfd,const char * path,int guest_flags,mode_t mode)252 int OpenatForGuest(int dirfd, const char* path, int guest_flags, mode_t mode) {
253   int host_flags = ToHostOpenFlags(guest_flags);
254 
255   if (IsProcSelfMaps(path, host_flags)) {
256     return OpenatProcSelfMapsForGuest(dirfd, host_flags, mode);
257   }
258 
259   const char* real_path = nullptr;
260   if ((host_flags & AT_SYMLINK_NOFOLLOW) == 0) {
261     real_path = TryReadLinkToMainExecutableRealPath(path);
262   }
263 
264   if (real_path == nullptr) {
265     real_path = TryTranslateProcCpuinfoPath(path, host_flags);
266   }
267 
268   return openat(dirfd, real_path != nullptr ? real_path : path, host_flags, mode);
269 }
270 
OpenForGuest(const char * path,int flags,mode_t mode)271 int OpenForGuest(const char* path, int flags, mode_t mode) {
272   return OpenatForGuest(AT_FDCWD, path, flags, mode);
273 }
274 
275 }  // namespace berberis
276