Home | History | Annotate | Download | only in tests
      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 
     31 namespace {
     32 
     33 // Print an error message and exit the process.
     34 // Message must be terminated by a newline.
     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.
     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:
     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.
    133 class 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.
    171 class 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.
    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.
    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.
    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.
    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 
    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.
    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 
    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