1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // A set of common helper functions used by crazy_linker tests.
6 // IMPORTANT: ALL FUNCTIONS HERE ARE INLINED. This avoids adding a
7 // dependency on another source file for all tests that include this
8 // header.
9
10 #ifndef TEST_UTIL_H
11 #define TEST_UTIL_H
12
13 #include <crazy_linker.h>
14
15 #include <dirent.h>
16 #include <errno.h>
17 #include <limits.h>
18 #include <stdarg.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <sys/mman.h>
22 #include <sys/socket.h>
23 #include <sys/stat.h>
24 #include <sys/uio.h>
25 #include <unistd.h>
26 #ifndef __STDC_FORMAT_MACROS
27 #define __STDC_FORMAT_MACROS // to get PRI and SCN in 32-bit inttypes.h
28 #endif
29 #include <inttypes.h>
30
31 namespace {
32
33 // Print an error message and exit the process.
34 // Message must be terminated by a newline.
Panic(const char * fmt,...)35 inline void Panic(const char* fmt, ...) {
36 va_list args;
37 fprintf(stderr, "PANIC: ");
38 va_start(args, fmt);
39 vfprintf(stderr, fmt, args);
40 va_end(args);
41 exit(1);
42 }
43
44 // Print an error message, the errno message, then exit the process.
45 // Message must not be terminated by a newline.
PanicErrno(const char * fmt,...)46 inline void PanicErrno(const char* fmt, ...) {
47 int old_errno = errno;
48 va_list args;
49 fprintf(stderr, "PANIC: ");
50 va_start(args, fmt);
51 vfprintf(stderr, fmt, args);
52 va_end(args);
53 fprintf(stderr, ": %s\n", strerror(old_errno));
54 exit(1);
55 }
56
57 // Simple string class.
58 class String {
59 public:
String()60 String() : str_(NULL), len_(0) {}
61
String(const String & other)62 String(const String& other) { String(other.str_, other.len_); }
63
String(const char * str)64 String(const char* str) { String(str, strlen(str)); }
65
String(const char * str,size_t len)66 String(const char* str, size_t len) : str_(NULL), len_(0) {
67 Append(str, len);
68 }
69
~String()70 ~String() {
71 if (str_) {
72 free(str_);
73 str_ = NULL;
74 }
75 }
76
77 String& operator+=(const char* str) {
78 Append(str, strlen(str));
79 return *this;
80 }
81
82 String& operator+=(const String& other) {
83 Append(other.str_, other.len_);
84 return *this;
85 }
86
87 String& operator+=(char ch) {
88 Append(&ch, 1);
89 return *this;
90 }
91
c_str()92 const char* c_str() const { return len_ ? str_ : ""; }
ptr()93 char* ptr() { return str_; }
size()94 size_t size() const { return len_; }
95
Append(const char * str,size_t len)96 void Append(const char* str, size_t len) {
97 size_t old_len = len_;
98 Resize(len_ + len);
99 memcpy(str_ + old_len, str, len);
100 }
101
Resize(size_t len)102 void Resize(size_t len) {
103 str_ = reinterpret_cast<char*>(realloc(str_, len + 1));
104 if (len > len_)
105 memset(str_ + len_, '\0', len - len_);
106 str_[len] = '\0';
107 len_ = len;
108 }
109
Format(const char * fmt,...)110 void Format(const char* fmt, ...) {
111 va_list args;
112 va_start(args, fmt);
113 Resize(128);
114 for (;;) {
115 va_list args2;
116 va_copy(args2, args);
117 int ret = vsnprintf(str_, len_ + 1, fmt, args2);
118 va_end(args2);
119 if (ret < static_cast<int>(len_ + 1))
120 break;
121
122 Resize(len_ * 2);
123 }
124 }
125
126 private:
127 char* str_;
128 size_t len_;
129 };
130
131 // Helper class to create a temporary directory that gets deleted on scope exit,
132 // as well as all regular files it contains.
133 class TempDirectory {
134 public:
TempDirectory()135 TempDirectory() {
136 snprintf(path_, sizeof path_, "/data/local/tmp/temp-XXXXXX");
137 if (!mktemp(path_))
138 Panic("Could not create temporary directory name: %s\n", strerror(errno));
139 if (mkdir(path_, 0700) < 0)
140 Panic("Could not create temporary directory %s: %s\n", strerror(errno));
141 }
142
~TempDirectory()143 ~TempDirectory() {
144 // Remove any file in this directory.
145 DIR* d = opendir(path_);
146 if (!d)
147 Panic("Could not open directory %s: %s\n", strerror(errno));
148
149 struct dirent* entry;
150 while ((entry = readdir(d)) != NULL) {
151 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
152 continue;
153 String file_path;
154 file_path.Format("%s/%s", path_, entry->d_name);
155 if (unlink(file_path.c_str()) < 0)
156 Panic("Could not remove %s: %s\n", file_path.c_str(), strerror(errno));
157 }
158 closedir(d);
159
160 if (rmdir(path_) < 0)
161 Panic("Could not remove dir %s: %s\n", path_, strerror(errno));
162 }
163
path()164 const char* path() const { return path_; }
165
166 private:
167 char path_[PATH_MAX];
168 };
169
170 // Scoped FILE* class. Always closed on destruction.
171 class ScopedFILE {
172 public:
ScopedFILE()173 ScopedFILE() : file_(NULL) {}
174
~ScopedFILE()175 ~ScopedFILE() {
176 if (file_) {
177 fclose(file_);
178 file_ = NULL;
179 }
180 }
181
Open(const char * path,const char * mode)182 void Open(const char* path, const char* mode) {
183 file_ = fopen(path, mode);
184 if (!file_)
185 Panic("Could not open file for reading: %s: %s\n", path, strerror(errno));
186 }
187
file()188 FILE* file() const { return file_; }
189
190 private:
191 FILE* file_;
192 };
193
194 // Retrieve current executable path as a String.
GetCurrentExecutable()195 inline String GetCurrentExecutable() {
196 String path;
197 path.Resize(PATH_MAX);
198 ssize_t ret =
199 TEMP_FAILURE_RETRY(readlink("/proc/self/exe", path.ptr(), path.size()));
200 if (ret < 0)
201 Panic("Could not read /proc/self/exe: %s\n", strerror(errno));
202
203 return path;
204 }
205
206 // Retrieve current executable directory as a String.
GetCurrentExecutableDirectory()207 inline String GetCurrentExecutableDirectory() {
208 String path = GetCurrentExecutable();
209 // Find basename.
210 const char* p = reinterpret_cast<const char*>(strrchr(path.c_str(), '/'));
211 if (p == NULL)
212 Panic("Current executable does not have directory root?: %s\n",
213 path.c_str());
214
215 path.Resize(p - path.c_str());
216 return path;
217 }
218
219 // Copy a file named |src_file_name| in directory |src_file_dir| into
220 // a file named |dst_file_name| in directory |dst_file_dir|. Panics on error.
CopyFile(const char * src_file_name,const char * src_file_dir,const char * dst_file_name,const char * dst_file_dir)221 inline void CopyFile(const char* src_file_name,
222 const char* src_file_dir,
223 const char* dst_file_name,
224 const char* dst_file_dir) {
225 String src_path;
226 src_path.Format("%s/%s", src_file_dir, src_file_name);
227
228 ScopedFILE src_file;
229 src_file.Open(src_path.c_str(), "rb");
230
231 String dst_path;
232 dst_path.Format("%s/%s", dst_file_dir, dst_file_name);
233 ScopedFILE dst_file;
234 dst_file.Open(dst_path.c_str(), "wb");
235
236 char buffer[8192];
237 for (;;) {
238 size_t read = fread(buffer, 1, sizeof buffer, src_file.file());
239 if (read > 0) {
240 size_t written = fwrite(buffer, 1, read, dst_file.file());
241 if (written != read)
242 Panic("Wrote %d bytes instead of %d into %s\n",
243 written,
244 read,
245 dst_path.c_str());
246 }
247 if (read < sizeof buffer)
248 break;
249 }
250 }
251
252 // Send a file descriptor |fd| through |socket|.
253 // Return 0 on success, -1/errno on failure.
SendFd(int socket,int fd)254 inline int SendFd(int socket, int fd) {
255 struct iovec iov;
256
257 char buffer[1];
258 buffer[0] = 0;
259
260 iov.iov_base = buffer;
261 iov.iov_len = 1;
262
263 struct msghdr msg;
264 struct cmsghdr* cmsg;
265 char cms[CMSG_SPACE(sizeof(int))];
266
267 ::memset(&msg, 0, sizeof(msg));
268 msg.msg_iov = &iov;
269 msg.msg_iovlen = 1;
270 msg.msg_control = reinterpret_cast<caddr_t>(cms);
271 msg.msg_controllen = CMSG_LEN(sizeof(int));
272
273 cmsg = CMSG_FIRSTHDR(&msg);
274 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
275 cmsg->cmsg_level = SOL_SOCKET;
276 cmsg->cmsg_type = SCM_RIGHTS;
277 ::memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
278
279 int ret = sendmsg(socket, &msg, 0);
280 if (ret < 0)
281 return -1;
282
283 if (ret != iov.iov_len) {
284 errno = EIO;
285 return -1;
286 }
287
288 return 0;
289 }
290
ReceiveFd(int socket,int * fd)291 inline int ReceiveFd(int socket, int* fd) {
292 char buffer[1];
293 struct iovec iov;
294
295 iov.iov_base = buffer;
296 iov.iov_len = 1;
297
298 struct msghdr msg;
299 struct cmsghdr* cmsg;
300 char cms[CMSG_SPACE(sizeof(int))];
301
302 ::memset(&msg, 0, sizeof msg);
303 msg.msg_name = 0;
304 msg.msg_namelen = 0;
305 msg.msg_iov = &iov;
306 msg.msg_iovlen = 1;
307
308 msg.msg_control = reinterpret_cast<caddr_t>(cms);
309 msg.msg_controllen = sizeof(cms);
310
311 int ret = recvmsg(socket, &msg, 0);
312 if (ret < 0)
313 return -1;
314 if (ret == 0) {
315 errno = EIO;
316 return -1;
317 }
318
319 cmsg = CMSG_FIRSTHDR(&msg);
320 ::memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
321 return 0;
322 }
323
324 // Check that there are exactly |expected_count| memory mappings in
325 // /proc/self/maps that point to a RELRO ashmem region.
CheckRelroMaps(int expected_count)326 inline void CheckRelroMaps(int expected_count) {
327 printf("Checking for %d RELROs in /proc/self/maps\n", expected_count);
328
329 FILE* file = fopen("/proc/self/maps", "rb");
330 if (!file)
331 Panic("Could not open /proc/self/maps (pid %d): %s\n",
332 getpid(),
333 strerror(errno));
334
335 char line[512];
336 int count_relros = 0;
337 printf("proc/%d/maps:\n", getpid());
338 while (fgets(line, sizeof line, file)) {
339 if (strstr(line, "with_relro")) {
340 // The supported library names are "lib<name>_with_relro.so".
341 printf("%s", line);
342 if (strstr(line, "/dev/ashmem/RELRO:")) {
343 count_relros++;
344 // Check that they are read-only mappings.
345 if (!strstr(line, " r--"))
346 Panic("Shared RELRO mapping is not readonly!\n");
347 // Check that they can't be remapped read-write.
348 uint64_t vma_start, vma_end;
349 if (sscanf(line, "%" SCNx64 "-%" SCNx64, &vma_start, &vma_end) != 2)
350 Panic("Could not parse VM address range!\n");
351 int ret = ::mprotect(
352 (void*)vma_start, vma_end - vma_start, PROT_READ | PROT_WRITE);
353 if (ret == 0)
354 Panic("Could remap shared RELRO as writable, should not happen!\n");
355
356 if (errno != EACCES)
357 Panic("remapping shared RELRO to writable failed with: %s\n",
358 strerror(errno));
359 }
360 }
361 }
362 fclose(file);
363
364 if (count_relros != expected_count)
365 Panic(
366 "Invalid shared RELRO sections in /proc/self/maps: %d"
367 " (expected %d)\n",
368 count_relros,
369 expected_count);
370
371 printf("RELRO count check ok!\n");
372 }
373
374 struct RelroInfo {
375 size_t start;
376 size_t size;
377 int fd;
378 };
379
380 struct RelroLibrary {
381 const char* name;
382 crazy_library_t* library;
383 RelroInfo relro;
384
InitRelroLibrary385 void Init(const char* name, crazy_context_t* context) {
386 printf("Loading %s\n", name);
387 this->name = name;
388 if (!crazy_library_open(&this->library, name, context)) {
389 Panic("Could not open %s: %s\n", name, crazy_context_get_error(context));
390 }
391 }
392
CloseRelroLibrary393 void Close() { crazy_library_close(this->library); }
394
CreateSharedRelroRelroLibrary395 void CreateSharedRelro(crazy_context_t* context, size_t load_address) {
396 if (!crazy_library_create_shared_relro(this->library,
397 context,
398 load_address,
399 &this->relro.start,
400 &this->relro.size,
401 &this->relro.fd)) {
402 Panic("Could not create shared RELRO for %s: %s",
403 this->name,
404 crazy_context_get_error(context));
405 }
406
407 printf("Parent %s relro info relro_start=%p relro_size=%p relro_fd=%d\n",
408 this->name,
409 (void*)this->relro.start,
410 (void*)this->relro.size,
411 this->relro.fd);
412 }
413
EnableSharedRelroRelroLibrary414 void EnableSharedRelro(crazy_context_t* context) {
415 CreateSharedRelro(context, 0);
416 UseSharedRelro(context);
417 }
418
SendRelroInfoRelroLibrary419 void SendRelroInfo(int fd) {
420 if (SendFd(fd, this->relro.fd) < 0) {
421 Panic("Could not send %s RELRO fd: %s", this->name, strerror(errno));
422 }
423
424 int ret =
425 TEMP_FAILURE_RETRY(::write(fd, &this->relro, sizeof(this->relro)));
426 if (ret != static_cast<int>(sizeof(this->relro))) {
427 Panic("Parent could not send %s RELRO info: %s",
428 this->name,
429 strerror(errno));
430 }
431 }
432
ReceiveRelroInfoRelroLibrary433 void ReceiveRelroInfo(int fd) {
434 // Receive relro information from parent.
435 int relro_fd = -1;
436 if (ReceiveFd(fd, &relro_fd) < 0) {
437 Panic("Could not receive %s relro descriptor from parent", this->name);
438 }
439
440 printf("Child received %s relro fd %d\n", this->name, relro_fd);
441
442 int ret = TEMP_FAILURE_RETRY(::read(fd, &this->relro, sizeof(this->relro)));
443 if (ret != static_cast<int>(sizeof(this->relro))) {
444 Panic("Could not receive %s relro information from parent", this->name);
445 }
446
447 this->relro.fd = relro_fd;
448 printf("Child received %s relro start=%p size=%p\n",
449 this->name,
450 (void*)this->relro.start,
451 (void*)this->relro.size);
452 }
453
UseSharedRelroRelroLibrary454 void UseSharedRelro(crazy_context_t* context) {
455 if (!crazy_library_use_shared_relro(this->library,
456 context,
457 this->relro.start,
458 this->relro.size,
459 this->relro.fd)) {
460 Panic("Could not use %s shared RELRO: %s\n",
461 this->name,
462 crazy_context_get_error(context));
463 }
464 }
465 };
466
467 } // namespace
468
469 #endif // TEST_UTIL_H
470