1// Copyright 2014 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
31namespace {
32
33// Print an error message and exit the process.
34// Message must be terminated by a newline.
35inline 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.
46inline 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.
58class String {
59 public:
60  String() : str_(NULL), len_(0) {}
61
62  String(const String& other) { String(other.str_, other.len_); }
63
64  String(const char* str) { String(str, strlen(str)); }
65
66  String(const char* str, size_t len) : str_(NULL), len_(0) {
67    Append(str, len);
68  }
69
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
92  const char* c_str() const { return len_ ? str_ : ""; }
93  char* ptr() { return str_; }
94  size_t size() const { return len_; }
95
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
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
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.
133class TempDirectory {
134 public:
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
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
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.
171class ScopedFILE {
172 public:
173  ScopedFILE() : file_(NULL) {}
174
175  ~ScopedFILE() {
176    if (file_) {
177      fclose(file_);
178      file_ = NULL;
179    }
180  }
181
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
188  FILE* file() const { return file_; }
189
190 private:
191  FILE* file_;
192};
193
194// Retrieve current executable path as a String.
195inline 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.
207inline 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.
221inline 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.
254inline 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
291inline 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.
326inline 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
374struct RelroInfo {
375  size_t start;
376  size_t size;
377  int fd;
378};
379
380struct RelroLibrary {
381  const char* name;
382  crazy_library_t* library;
383  RelroInfo relro;
384
385  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
393  void Close() { crazy_library_close(this->library); }
394
395  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
414  void EnableSharedRelro(crazy_context_t* context) {
415    CreateSharedRelro(context, 0);
416    UseSharedRelro(context);
417  }
418
419  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
433  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
454  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