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