<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#define XOPEN_SOURCE 500
#include <algorithm>
#include <elf.h>
#include <errno.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/unistd.h>
#include <set>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "allocator.h"
#include "debug.h"
#include "library.h"
#include "sandbox_impl.h"
#include "syscall.h"
#include "syscall_table.h"
#include "x86_decode.h"

#if defined(__x86_64__)
typedef Elf64_Phdr    Elf_Phdr;
typedef Elf64_Rela    Elf_Rel;

typedef Elf64_Half    Elf_Half;
typedef Elf64_Word    Elf_Word;
typedef Elf64_Sword   Elf_Sword;
typedef Elf64_Xword   Elf_Xword;
typedef Elf64_Sxword  Elf_Sxword;
typedef Elf64_Off     Elf_Off;
typedef Elf64_Section Elf_Section;
typedef Elf64_Versym  Elf_Versym;

#define ELF_ST_BIND   ELF64_ST_BIND
#define ELF_ST_TYPE   ELF64_ST_TYPE
#define ELF_ST_INFO   ELF64_ST_INFO
#define ELF_R_SYM     ELF64_R_SYM
#define ELF_R_TYPE    ELF64_R_TYPE
#define ELF_R_INFO    ELF64_R_INFO

#define ELF_REL_PLT   ".rela.plt"
#define ELF_JUMP_SLOT R_X86_64_JUMP_SLOT
#elif defined(__i386__)
typedef Elf32_Phdr    Elf_Phdr;
typedef Elf32_Rel     Elf_Rel;

typedef Elf32_Half    Elf_Half;
typedef Elf32_Word    Elf_Word;
typedef Elf32_Sword   Elf_Sword;
typedef Elf32_Xword   Elf_Xword;
typedef Elf32_Sxword  Elf_Sxword;
typedef Elf32_Off     Elf_Off;
typedef Elf32_Section Elf_Section;
typedef Elf32_Versym  Elf_Versym;

#define ELF_ST_BIND   ELF32_ST_BIND
#define ELF_ST_TYPE   ELF32_ST_TYPE
#define ELF_ST_INFO   ELF32_ST_INFO
#define ELF_R_SYM     ELF32_R_SYM
#define ELF_R_TYPE    ELF32_R_TYPE
#define ELF_R_INFO    ELF32_R_INFO

#define ELF_REL_PLT   ".rel.plt"
#define ELF_JUMP_SLOT R_386_JMP_SLOT
#else
#error Unsupported target platform
#endif

namespace playground {

Maps::Maps* Library::maps_;
char* Library::__kernel_vsyscall;
char* Library::__kernel_sigreturn;
char* Library::__kernel_rt_sigreturn;

Library::~Library() {
  if (image_size_) {
    // We no longer need access to a full mapping of the underlying library
    // file. Move the temporarily extended mapping back to where we originally
    // found. Make sure to preserve any changes that we might have made since.
    Sandbox::SysCalls sys;
    sys.mprotect(image_, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
    if (memcmp(image_, memory_ranges_.rbegin()->second.start, 4096)) {
      // Only copy data, if we made any changes in this data. Otherwise there
      // is no need to create another modified COW mapping.
      memcpy(image_, memory_ranges_.rbegin()->second.start, 4096);
    }
    sys.mprotect(image_, 4096, PROT_READ | PROT_EXEC);
    sys.mremap(image_, image_size_, 4096, MREMAP_MAYMOVE | MREMAP_FIXED,
               memory_ranges_.rbegin()->second.start);
  }
}

char* Library::getBytes(char* dst, const char* src, ssize_t len) {
  // Some kernels don't allow accessing the VDSO from write()
  if (isVDSO_ &&
      src >= memory_ranges_.begin()->second.start &&
      src <= memory_ranges_.begin()->second.stop) {
    ssize_t max =
      reinterpret_cast<char *>(memory_ranges_.begin()->second.stop) - src;
    if (len > max) {
      len = max;
    }
    memcpy(dst, src, len);
    return dst;
  }

  // Read up to "len" bytes from "src" and copy them to "dst". Short
  // copies are possible, if we are at the end of a mapping. Returns
  // NULL, if the operation failed completely.
  static int helper_socket[2];
  Sandbox::SysCalls sys;
  if (!helper_socket[0] && !helper_socket[1]) {
    // Copy data through a socketpair, as this allows us to access it
    // without incurring a segmentation fault.
    sys.socketpair(AF_UNIX, SOCK_STREAM, 0, helper_socket);
  }
  char* ptr = dst;
  int   inc = 4096;
  while (len > 0) {
    ssize_t l = inc == 1 ? inc : 4096 - (reinterpret_cast<long>(src) & 0xFFF);
    if (l > len) {
      l = len;
    }
    l = NOINTR_SYS(sys.write(helper_socket[0], src, l));
    if (l == -1) {
      if (sys.my_errno == EFAULT) {
        if (inc == 1) {
          if (ptr == dst) {
            return NULL;
          }
          break;
        }
        inc = 1;
        continue;
      } else {
        return NULL;
      }
    }
    l = sys.read(helper_socket[1], ptr, l);
    if (l <= 0) {
      return NULL;
    }
    ptr += l;
    src += l;
    len -= l;
  }
  return dst;
}

char *Library::get(Elf_Addr offset, char *buf, size_t len) {
  if (!valid_) {
    memset(buf, 0, len);
    return NULL;
  }
  RangeMap::const_iterator iter = memory_ranges_.lower_bound(offset);
  if (iter == memory_ranges_.end()) {
    memset(buf, 0, len);
    return NULL;
  }
  offset -= iter->first;
  long size = reinterpret_cast<char *>(iter->second.stop) -
              reinterpret_cast<char *>(iter->second.start);
  if (offset > size - len) {
    memset(buf, 0, len);
    return NULL;
  }
  char *src = reinterpret_cast<char *>(iter->second.start) + offset;
  memset(buf, 0, len);
  if (!getBytes(buf, src, len)) {
    return NULL;
  }
  return buf;
}

Library::string Library::get(Elf_Addr offset) {
  if (!valid_) {
    return "";
  }
  RangeMap::const_iterator iter = memory_ranges_.lower_bound(offset);
  if (iter == memory_ranges_.end()) {
    return "";
  }
  offset -= iter->first;
  const char *start = reinterpret_cast<char *>(iter->second.start) + offset;
  const char *stop  = reinterpret_cast<char *>(iter->second.stop) + offset;
  char buf[4096]    = { 0 };
  getBytes(buf, start, stop - start >= (int)sizeof(buf) ?
           sizeof(buf) - 1 : stop - start);
  start             = buf;
  stop              = buf;
  while (*stop) {
    ++stop;
  }
  string s = stop > start ? string(start, stop - start) : "";
  return s;
}

char *Library::getOriginal(Elf_Addr offset, char *buf, size_t len) {
  if (!valid_) {
    memset(buf, 0, len);
    return NULL;
  }
  Sandbox::SysCalls sys;
  if (!image_ && !isVDSO_ && !memory_ranges_.empty() &&
      memory_ranges_.rbegin()->first == 0) {
    // Extend the mapping of the very first page of the underlying library
    // file. This way, we can read the original file contents of the entire
    // library.
    // We have to be careful, because doing so temporarily removes the first
    // 4096 bytes of the library from memory. And we don't want to accidentally
    // unmap code that we are executing. So, only use functions that can be
    // inlined.
    void* start = memory_ranges_.rbegin()->second.start;
    image_size_ = memory_ranges_.begin()->first +
      (reinterpret_cast<char *>(memory_ranges_.begin()->second.stop) -
       reinterpret_cast<char *>(memory_ranges_.begin()->second.start));
    if (image_size_ < 8192) {
      // It is possible to create a library that is only a single page in
      // size. In that case, we have to make sure that we artificially map
      // one extra page past the end of it, as our code relies on mremap()
      // actually moving the mapping.
      image_size_ = 8192;
    }
    image_ = reinterpret_cast<char *>(sys.mremap(start, 4096, image_size_,
                                                 MREMAP_MAYMOVE));
    if (image_size_ == 8192 && image_ == start) {
      // We really mean it, when we say we want the memory to be moved.
      image_ = reinterpret_cast<char *>(sys.mremap(start, 4096, image_size_,
                                                   MREMAP_MAYMOVE));
      sys.munmap(reinterpret_cast<char *>(start) + 4096, 4096);
    }
    if (image_ == MAP_FAILED) {
      image_ = NULL;
    } else {
      sys.MMAP(start, 4096, PROT_READ | PROT_WRITE | PROT_EXEC,
               MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
      for (int i = 4096 / sizeof(long); --i;
           reinterpret_cast<long *>(start)[i] =
             reinterpret_cast<long *>(image_)[i]);
    }
  }

  if (image_) {
    if (offset + len > image_size_) {
      // It is quite likely that we initially did not map the entire file as
      // we did not know how large it is. So, if necessary, try to extend the
      // mapping.
      size_t new_size = (offset + len + 4095) & ~4095;
      char* tmp =
        reinterpret_cast<char *>(sys.mremap(image_, image_size_, new_size,
                                            MREMAP_MAYMOVE));
      if (tmp != MAP_FAILED) {
        image_      = tmp;
        image_size_ = new_size;
      }
    }
    if (buf && offset + len <= image_size_) {
      return reinterpret_cast<char *>(memcpy(buf, image_ + offset, len));
    }
    return NULL;
  }
  return buf ? get(offset, buf, len) : NULL;
}

Library::string Library::getOriginal(Elf_Addr offset) {
  if (!valid_) {
    return "";
  }
  // Make sure we actually have a mapping that we can access. If the string
  // is located at the end of the image, we might not yet have extended the
  // mapping sufficiently.
  if (!image_ || image_size_ <= offset) {
    getOriginal(offset, NULL, 1);
  }

  if (image_) {
    if (offset < image_size_) {
      char* start = image_ + offset;
      char* stop  = start;
      while (stop < image_ + image_size_ && *stop) {
        ++stop;
        if (stop >= image_ + image_size_) {
          getOriginal(stop - image_, NULL, 1);
        }
      }
      return string(start, stop - start);
    }
    return "";
  }
  return get(offset);
}

const Elf_Ehdr* Library::getEhdr() {
  if (!valid_) {
    return NULL;
  }
  return &ehdr_;
}

const Elf_Shdr* Library::getSection(const string& section) {
  if (!valid_) {
    return NULL;
  }
  SectionTable::const_iterator iter = section_table_.find(section);
  if (iter == section_table_.end()) {
    return NULL;
  }
  return &iter->second.second;
}

int Library::getSectionIndex(const string& section) {
  if (!valid_) {
    return -1;
  }
  SectionTable::const_iterator iter = section_table_.find(section);
  if (iter == section_table_.end()) {
    return -1;
  }
  return iter->second.first;
}

void Library::makeWritable(bool state) const {
  for (RangeMap::const_iterator iter = memory_ranges_.begin();
       iter != memory_ranges_.end(); ++iter) {
    const Range& range = iter->second;
    long length = reinterpret_cast<char *>(range.stop) -
                  reinterpret_cast<char *>(range.start);
    Sandbox::SysCalls sys;
    sys.mprotect(range.start, length,
                 range.prot | (state ? PROT_WRITE : 0));
  }
}

bool Library::isSafeInsn(unsigned short insn) {
  // Check if the instruction has no unexpected side-effects. If so, it can
  // be safely relocated from the function that we are patching into the
  // out-of-line scratch space that we are setting up. This is often necessary
  // to make room for the JMP into the scratch space.
  return ((insn & 0x7) < 0x6 && (insn & 0xF0) < 0x40
          /* ADD, OR, ADC, SBB, AND, SUB, XOR, CMP */) ||
         #if defined(__x86_64__)
         insn == 0x63 /* MOVSXD */ ||
         #endif
         (insn >= 0x80 && insn <= 0x8E /* ADD, OR, ADC,
         SBB, AND, SUB, XOR, CMP, TEST, XCHG, MOV, LEA */) ||
         (insn == 0x90) || /* NOP */
         (insn >= 0xA0 && insn <= 0xA9) /* MOV, TEST */ ||
         (insn >= 0xB0 && insn <= 0xBF /* MOV */) ||
         (insn >= 0xC0 && insn <= 0xC1) || /* Bit Shift */
         (insn >= 0xD0 && insn <= 0xD3) || /* Bit Shift */
         (insn >= 0xC6 && insn <= 0xC7 /* MOV */) ||
         (insn == 0xF7) /* TEST, NOT, NEG, MUL, IMUL, DIV, IDIV */;
}

char* Library::getScratchSpace(const Maps* maps, char* near, int needed,
                               char** extraSpace, int* extraLength) {
  if (needed > *extraLength ||
      labs(*extraSpace - reinterpret_cast<char *>(near)) > (1536 << 20)) {
    if (*extraSpace) {
      // Start a new scratch page and mark any previous page as write-protected
      Sandbox::SysCalls sys;
      sys.mprotect(*extraSpace, 4096, PROT_READ|PROT_EXEC);
    }<commit_msg>Enable support for injecting code into "long NOPs". The gas assembler generates these instructions when asked to align the .text segment.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#define XOPEN_SOURCE 500
#include <algorithm>
#include <elf.h>
#include <errno.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/unistd.h>
#include <set>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ptrace.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "allocator.h"
#include "debug.h"
#include "library.h"
#include "sandbox_impl.h"
#include "syscall.h"
#include "syscall_table.h"
#include "x86_decode.h"

#if defined(__x86_64__)
typedef Elf64_Phdr    Elf_Phdr;
typedef Elf64_Rela    Elf_Rel;

typedef Elf64_Half    Elf_Half;
typedef Elf64_Word    Elf_Word;
typedef Elf64_Sword   Elf_Sword;
typedef Elf64_Xword   Elf_Xword;
typedef Elf64_Sxword  Elf_Sxword;
typedef Elf64_Off     Elf_Off;
typedef Elf64_Section Elf_Section;
typedef Elf64_Versym  Elf_Versym;

#define ELF_ST_BIND   ELF64_ST_BIND
#define ELF_ST_TYPE   ELF64_ST_TYPE
#define ELF_ST_INFO   ELF64_ST_INFO
#define ELF_R_SYM     ELF64_R_SYM
#define ELF_R_TYPE    ELF64_R_TYPE
#define ELF_R_INFO    ELF64_R_INFO

#define ELF_REL_PLT   ".rela.plt"
#define ELF_JUMP_SLOT R_X86_64_JUMP_SLOT
#elif defined(__i386__)
typedef Elf32_Phdr    Elf_Phdr;
typedef Elf32_Rel     Elf_Rel;

typedef Elf32_Half    Elf_Half;
typedef Elf32_Word    Elf_Word;
typedef Elf32_Sword   Elf_Sword;
typedef Elf32_Xword   Elf_Xword;
typedef Elf32_Sxword  Elf_Sxword;
typedef Elf32_Off     Elf_Off;
typedef Elf32_Section Elf_Section;
typedef Elf32_Versym  Elf_Versym;

#define ELF_ST_BIND   ELF32_ST_BIND
#define ELF_ST_TYPE   ELF32_ST_TYPE
#define ELF_ST_INFO   ELF32_ST_INFO
#define ELF_R_SYM     ELF32_R_SYM
#define ELF_R_TYPE    ELF32_R_TYPE
#define ELF_R_INFO    ELF32_R_INFO

#define ELF_REL_PLT   ".rel.plt"
#define ELF_JUMP_SLOT R_386_JMP_SLOT
#else
#error Unsupported target platform
#endif

namespace playground {

Maps::Maps* Library::maps_;
char* Library::__kernel_vsyscall;
char* Library::__kernel_sigreturn;
char* Library::__kernel_rt_sigreturn;

Library::~Library() {
  if (image_size_) {
    // We no longer need access to a full mapping of the underlying library
    // file. Move the temporarily extended mapping back to where we originally
    // found. Make sure to preserve any changes that we might have made since.
    Sandbox::SysCalls sys;
    sys.mprotect(image_, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
    if (memcmp(image_, memory_ranges_.rbegin()->second.start, 4096)) {
      // Only copy data, if we made any changes in this data. Otherwise there
      // is no need to create another modified COW mapping.
      memcpy(image_, memory_ranges_.rbegin()->second.start, 4096);
    }
    sys.mprotect(image_, 4096, PROT_READ | PROT_EXEC);
    sys.mremap(image_, image_size_, 4096, MREMAP_MAYMOVE | MREMAP_FIXED,
               memory_ranges_.rbegin()->second.start);
  }
}

char* Library::getBytes(char* dst, const char* src, ssize_t len) {
  // Some kernels don't allow accessing the VDSO from write()
  if (isVDSO_ &&
      src >= memory_ranges_.begin()->second.start &&
      src <= memory_ranges_.begin()->second.stop) {
    ssize_t max =
      reinterpret_cast<char *>(memory_ranges_.begin()->second.stop) - src;
    if (len > max) {
      len = max;
    }
    memcpy(dst, src, len);
    return dst;
  }

  // Read up to "len" bytes from "src" and copy them to "dst". Short
  // copies are possible, if we are at the end of a mapping. Returns
  // NULL, if the operation failed completely.
  static int helper_socket[2];
  Sandbox::SysCalls sys;
  if (!helper_socket[0] && !helper_socket[1]) {
    // Copy data through a socketpair, as this allows us to access it
    // without incurring a segmentation fault.
    sys.socketpair(AF_UNIX, SOCK_STREAM, 0, helper_socket);
  }
  char* ptr = dst;
  int   inc = 4096;
  while (len > 0) {
    ssize_t l = inc == 1 ? inc : 4096 - (reinterpret_cast<long>(src) & 0xFFF);
    if (l > len) {
      l = len;
    }
    l = NOINTR_SYS(sys.write(helper_socket[0], src, l));
    if (l == -1) {
      if (sys.my_errno == EFAULT) {
        if (inc == 1) {
          if (ptr == dst) {
            return NULL;
          }
          break;
        }
        inc = 1;
        continue;
      } else {
        return NULL;
      }
    }
    l = sys.read(helper_socket[1], ptr, l);
    if (l <= 0) {
      return NULL;
    }
    ptr += l;
    src += l;
    len -= l;
  }
  return dst;
}

char *Library::get(Elf_Addr offset, char *buf, size_t len) {
  if (!valid_) {
    memset(buf, 0, len);
    return NULL;
  }
  RangeMap::const_iterator iter = memory_ranges_.lower_bound(offset);
  if (iter == memory_ranges_.end()) {
    memset(buf, 0, len);
    return NULL;
  }
  offset -= iter->first;
  long size = reinterpret_cast<char *>(iter->second.stop) -
              reinterpret_cast<char *>(iter->second.start);
  if (offset > size - len) {
    memset(buf, 0, len);
    return NULL;
  }
  char *src = reinterpret_cast<char *>(iter->second.start) + offset;
  memset(buf, 0, len);
  if (!getBytes(buf, src, len)) {
    return NULL;
  }
  return buf;
}

Library::string Library::get(Elf_Addr offset) {
  if (!valid_) {
    return "";
  }
  RangeMap::const_iterator iter = memory_ranges_.lower_bound(offset);
  if (iter == memory_ranges_.end()) {
    return "";
  }
  offset -= iter->first;
  const char *start = reinterpret_cast<char *>(iter->second.start) + offset;
  const char *stop  = reinterpret_cast<char *>(iter->second.stop) + offset;
  char buf[4096]    = { 0 };
  getBytes(buf, start, stop - start >= (int)sizeof(buf) ?
           sizeof(buf) - 1 : stop - start);
  start             = buf;
  stop              = buf;
  while (*stop) {
    ++stop;
  }
  string s = stop > start ? string(start, stop - start) : "";
  return s;
}

char *Library::getOriginal(Elf_Addr offset, char *buf, size_t len) {
  if (!valid_) {
    memset(buf, 0, len);
    return NULL;
  }
  Sandbox::SysCalls sys;
  if (!image_ && !isVDSO_ && !memory_ranges_.empty() &&
      memory_ranges_.rbegin()->first == 0) {
    // Extend the mapping of the very first page of the underlying library
    // file. This way, we can read the original file contents of the entire
    // library.
    // We have to be careful, because doing so temporarily removes the first
    // 4096 bytes of the library from memory. And we don't want to accidentally
    // unmap code that we are executing. So, only use functions that can be
    // inlined.
    void* start = memory_ranges_.rbegin()->second.start;
    image_size_ = memory_ranges_.begin()->first +
      (reinterpret_cast<char *>(memory_ranges_.begin()->second.stop) -
       reinterpret_cast<char *>(memory_ranges_.begin()->second.start));
    if (image_size_ < 8192) {
      // It is possible to create a library that is only a single page in
      // size. In that case, we have to make sure that we artificially map
      // one extra page past the end of it, as our code relies on mremap()
      // actually moving the mapping.
      image_size_ = 8192;
    }
    image_ = reinterpret_cast<char *>(sys.mremap(start, 4096, image_size_,
                                                 MREMAP_MAYMOVE));
    if (image_size_ == 8192 && image_ == start) {
      // We really mean it, when we say we want the memory to be moved.
      image_ = reinterpret_cast<char *>(sys.mremap(start, 4096, image_size_,
                                                   MREMAP_MAYMOVE));
      sys.munmap(reinterpret_cast<char *>(start) + 4096, 4096);
    }
    if (image_ == MAP_FAILED) {
      image_ = NULL;
    } else {
      sys.MMAP(start, 4096, PROT_READ | PROT_WRITE | PROT_EXEC,
               MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
      for (int i = 4096 / sizeof(long); --i;
           reinterpret_cast<long *>(start)[i] =
             reinterpret_cast<long *>(image_)[i]);
    }
  }

  if (image_) {
    if (offset + len > image_size_) {
      // It is quite likely that we initially did not map the entire file as
      // we did not know how large it is. So, if necessary, try to extend the
      // mapping.
      size_t new_size = (offset + len + 4095) & ~4095;
      char* tmp =
        reinterpret_cast<char *>(sys.mremap(image_, image_size_, new_size,
                                            MREMAP_MAYMOVE));
      if (tmp != MAP_FAILED) {
        image_      = tmp;
        image_size_ = new_size;
      }
    }
    if (buf && offset + len <= image_size_) {
      return reinterpret_cast<char *>(memcpy(buf, image_ + offset, len));
    }
    return NULL;
  }
  return buf ? get(offset, buf, len) : NULL;
}

Library::string Library::getOriginal(Elf_Addr offset) {
  if (!valid_) {
    return "";
  }
  // Make sure we actually have a mapping that we can access. If the string
  // is located at the end of the image, we might not yet have extended the
  // mapping sufficiently.
  if (!image_ || image_size_ <= offset) {
    getOriginal(offset, NULL, 1);
  }

  if (image_) {
    if (offset < image_size_) {
      char* start = image_ + offset;
      char* stop  = start;
      while (stop < image_ + image_size_ && *stop) {
        ++stop;
        if (stop >= image_ + image_size_) {
          getOriginal(stop - image_, NULL, 1);
        }
      }
      return string(start, stop - start);
    }
    return "";
  }
  return get(offset);
}

const Elf_Ehdr* Library::getEhdr() {
  if (!valid_) {
    return NULL;
  }
  return &ehdr_;
}

const Elf_Shdr* Library::getSection(const string& section) {
  if (!valid_) {
    return NULL;
  }
  SectionTable::const_iterator iter = section_table_.find(section);
  if (iter == section_table_.end()) {
    return NULL;
  }
  return &iter->second.second;
}

int Library::getSectionIndex(const string& section) {
  if (!valid_) {
    return -1;
  }
  SectionTable::const_iterator iter = section_table_.find(section);
  if (iter == section_table_.end()) {
    return -1;
  }
  return iter->second.first;
}

void Library::makeWritable(bool state) const {
  for (RangeMap::const_iterator iter = memory_ranges_.begin();
       iter != memory_ranges_.end(); ++iter) {
    const Range& range = iter->second;
    long length = reinterpret_cast<char *>(range.stop) -
                  reinterpret_cast<char *>(range.start);
    Sandbox::SysCalls sys;
    sys.mprotect(range.start, length,
                 range.prot | (state ? PROT_WRITE : 0));
  }
}

bool Library::isSafeInsn(unsigned short insn) {
  // Check if the instruction has no unexpected side-effects. If so, it can
  // be safely relocated from the function that we are patching into the
  // out-of-line scratch space that we are setting up. This is often necessary
  // to make room for the JMP into the scratch space.
  return ((insn & 0x7) < 0x6 && (insn & 0xF0) < 0x40
          /* ADD, OR, ADC, SBB, AND, SUB, XOR, CMP */) ||
         #if defined(__x86_64__)
         insn == 0x63 /* MOVSXD */ ||
         #endif
         (insn >= 0x80 && insn <= 0x8E /* ADD, OR, ADC,
         SBB, AND, SUB, XOR, CMP, TEST, XCHG, MOV, LEA */) ||
         (insn == 0x90) || /* NOP */
         (insn >= 0xA0 && insn <= 0xA9) /* MOV, TEST */ ||
         (insn >= 0xB0 && insn <= 0xBF /* MOV */) ||
         (insn >= 0xC0 && insn <= 0xC1) || /* Bit Shift */
         (insn >= 0xD0 && insn <= 0xD3) || /* Bit Shift */
         (insn >= 0xC6 && insn <= 0xC7 /* MOV */) ||
         (insn == 0xF7) /* TEST, NOT, NEG, MUL, IMUL, DIV, IDIV */ ||
         (insn >= 0xF19 && insn <= 0xF1F) /* long NOP */;
}

char* Library::getScratchSpace(const Maps* maps, char* near, int needed,
                               char** extraSpace, int* extraLength) {
  if (needed > *extraLength ||
      labs(*extraSpace - reinterpret_cast<char *>(near)) > (1536 << 20)) {
    if (*extraSpace) {
      // Start a new scratch page and mark any previous page as write-protected
      Sandbox::SysCalls sys;
      sys.mprotect(*extraSpace, 4096, PROT_READ|PROT_EXEC);
    }
{'fraction_non_alphanumeric': 0.12386194338685648, 'fraction_numerical': 0.02089885780499917, 'mean_word_length': 2.732046332046332, 'pattern_counts': {'":': 0, '<': 106, '<?xml version=': 0, '>': 148, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1393165', 'n_tokens_mistral': 8799, 'n_tokens_neox': 8311, 'n_words': 2358}