Ejemplo n.º 1
0
void TestDEPCheckFailurePath(void) {
  size_t size = NACL_PAGESIZE;
  void *page;
  CHECK(NaCl_page_alloc(&page, size) == 0);

  CHECK(NaCl_mprotect(page, size, PROT_READ | PROT_WRITE | PROT_EXEC) == 0);
  CHECK(!NaClAttemptToExecuteDataAtAddr(page, size));

  /* DEP is not guaranteed to work on x86-32. */
  if (!(NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32)) {
    CHECK(NaCl_mprotect(page, size, PROT_READ | PROT_WRITE) == 0);
    CHECK(NaClAttemptToExecuteDataAtAddr(page, size));
  }

  NaCl_page_free(page, size);
}
/* bool */
int NaClFindAddressSpaceRandomized(uintptr_t *addr, size_t memory_size,
                                   int max_tries) {
  void *map_addr;
  int tries_remaining;
  /*
   * Mask for keeping the low order NUM_USER_ADDRESS_BITS of a randomly
   * generated address.
   */
  uintptr_t addr_mask;
  uintptr_t suggested_addr;
  int mmap_type;

  size_t map_failed_count_left = NACL_VALGRIND_MAP_FAILED_COUNT;

  CHECK(max_tries >= 0);
  NaClLog(4,
          "NaClFindAddressSpaceRandomized: looking for %"NACL_PRIxS" bytes\n",
          memory_size);
  NaClLog(4, "NaClFindAddressSpaceRandomized: max %d tries\n", max_tries);

  if (NACL_ARCH(NACL_BUILD_ARCH) != NACL_mips) {
    /* 4kB pages */
    addr_mask = ~(((uintptr_t) 1 << 12) - 1);
    mmap_type = MAP_PRIVATE;
  } else {
    /*
     * 8kB pages in userspace memory region.
     * Apart from system page size, MIPS shared memory mask in kernel can depend
     * on dcache attributes. Ideally, we could use kernel constant SHMLBA, but
     * it is too large. Common case is that 8kB is sufficient.
     * As shared memory mask alignment is more rigid on MIPS, we need to pass
     * MAP_SHARED type to mmap, so it can return value applicable both for
     * private and shared mapping.
     */
    addr_mask = ~(((uintptr_t) 1 << 13) - 1);
    mmap_type = MAP_SHARED;
  }
  /*
   * We cannot just do
   *
   *   if (NUM_USER_ADDRESS_BITS < sizeof(uintptr_t) * 8) {
   *     addr_mask &= (((uintptr_t) 1 << NUM_USER_ADDRESS_BITS) - 1);
   *   }
   *
   * since when NUM_USER_ADDRESS_BITS is defined to be 32, for
   * example, and sizeof(uintptr_t) is 4, then even though the
   * constant expression NUM_USER_ADDRESS_BITS < sizeof(uintptr_t) * 8
   * is false, the compiler still parses the block of code controlled
   * by the conditional.  And the warning for shifting by too many
   * bits would be produced because we'd venture into
   * undefined-behavior territory:
   *
   *   6.5.7.3: "The integer promotions are performed on each of the
   *   operands. The type of the result is that of the promoted left
   *   operand. If the value of the right operand is negative or is
   *   greater than or equal to the width of the promoted left
   *   operand, the behavior is undefined."
   *
   * Since we compile with -Wall and -Werror, this would lead to a
   * build failure.
   */
#if NACL_POINTER_SIZE > NUM_USER_ADDRESS_BITS
  addr_mask &= (((uintptr_t) 1 << NUM_USER_ADDRESS_BITS) - 1);
#endif

  tries_remaining = max_tries;
  for (;;) {
#if NACL_POINTER_SIZE > 32
    suggested_addr = (((uintptr_t) NaClGlobalSecureRngUint32() << 32) |
                      (uintptr_t) NaClGlobalSecureRngUint32());
#else
    suggested_addr = ((uintptr_t) NaClGlobalSecureRngUint32());
#endif
    suggested_addr &= addr_mask;

    NaClLog(4,
            ("NaClFindAddressSpaceRandomized: non-MAP_FAILED tries"
             " remaining %d, hint addr %"NACL_PRIxPTR"\n"),
            tries_remaining,
            suggested_addr);
    map_addr = mmap((void *) suggested_addr, memory_size,
                    PROT_NONE,
                    MAP_ANONYMOUS | MAP_NORESERVE | mmap_type,
                    -1,
                    0);
    /*
     * On most POSIX systems, the mmap syscall, without MAP_FIXED,
     * will use the first parameter (actual argument suggested_addr)
     * as a hint for where to find a hole in the address space for the
     * new memory -- often as a starting point for a search.
     *
     * However, when Valgrind is used (3.7.0 to 3.8.1 at least), the
     * mmap syscall is replaced with wrappers which do not behave
     * correctly: the Valgrind-provided mmap replacement will return
     * MAP_FAILED instead of ignoring the hint, until the "Warning:
     * set address range perms: large range [0x...., 0x....]
     * (noaccess)" message to warn about large allocations shows up.
     * So in order for this code to not fail when run under Valgrind,
     * we have to ignore MAP_FAILED and not count these attempts
     * against randomization failures.
     */
    if (MAP_FAILED == map_addr) {
      if (--map_failed_count_left != 0) {
        NaClLog(LOG_INFO, "NaClFindAddressSpaceRandomized: MAP_FAILED\n");
      } else {
        NaClLog(LOG_ERROR,
                "NaClFindAddressSpaceRandomized: too many MAP_FAILED\n");
        return 0;
      }
    } else {
      /*
       * mmap will use a system-dependent algorithm to find a starting
       * address if the hint location cannot work, e.g., if there is
       * already memory mapped there.  We do not trust that algorithm
       * to provide any randomness in the high-order bits, so we only
       * accept allocations that match the requested high-order bits
       * exactly.  If the algorithm is to scan the address space
       * starting near the hint address, then it would be acceptable;
       * if it is to use a default algorithm that is independent of
       * the supplied hint, then it would not be.
       */
      if ((addr_mask & ((uintptr_t) map_addr)) == suggested_addr) {
        NaClLog(5,
                "NaClFindAddressSpaceRandomized: high order bits matched.\n");
        /* success */
        break;
      }

      if (0 == tries_remaining--) {
        /* give up on retrying, and just use what was returned */
        NaClLog(5, "NaClFindAddressSpaceRandomized: last try, taking as is.\n");
        break;
      }
      /*
       * Remove undesirable mapping location before trying again.
       */
      if (-1 == munmap(map_addr, memory_size)) {
        NaClLog(LOG_FATAL,
                "NaClFindAddressSpaceRandomized: could not unmap non-random"
                " memory\n");
      }
    }
  }
  NaClLog(4,
          "NaClFindAddressSpaceRandomized: got addr %"NACL_PRIxPTR"\n",
          (uintptr_t) map_addr);
  *addr = (uintptr_t) map_addr;
  return 1;
}
Ejemplo n.º 3
0
/* Test comparison for a single instruction. */
static void TryOneInstruction(uint8_t *itext, size_t nbytes) {
  NaClEnumerator pinst; /* for prod validator */
  NaClEnumerator dinst; /* for dfa validator */
  Bool prod_okay, rdfa_okay;

  IncrTried();
  do {
    if (gVerbose) {
      printf("================");
      PrintBytes(stdout, itext, nbytes);
      printf("\n");
    }

    /* Try to parse the sequence of test bytes. */
    InitInst(&pinst, itext, nbytes);
    InitInst(&dinst, itext, nbytes);
    vProd->_parse_inst_fn(&pinst, kTextAddress);
    vDFA->_parse_inst_fn(&dinst, kTextAddress);
    prod_okay = vProd->_maybe_inst_validates_fn(&pinst);
    rdfa_okay = vDFA->_maybe_inst_validates_fn(&dinst);

    if (prod_okay && rdfa_okay) {
      if (vProd->_inst_length_fn(&pinst) ==
          vDFA->_inst_length_fn(&dinst)) {
        /* Both validators see a legal instruction, */
        /* and they agree on critical details.      */
        IncrValid();
      } else {
        DecoderError("LENGTH MISMATCH", &pinst, &dinst, "");
        IncrErrors();
      }
    } else if (prod_okay && !rdfa_okay) {
      /*
       * 32bit production validator by design is unable to distingush a lot of
       * instructions (the ones which work only with memory or only with
       * registers).  To avoid commiting multimegabyte golden file don't count
       * these differences as substantial.  It's not a security problem if we
       * reject some valid x86 instructions and if we'll lose something
       * important hopefully developers will remind us.
       */
      if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 &&
          NACL_TARGET_SUBARCH == 32) {
        IncrIgnored();
      } else {
        /* Validators disagree on instruction legality */
        DecoderError("VALIDATORS DISAGREE (prod accepts, RDFA rejects)",
                     &pinst,
                     &dinst,
                     "");
        IncrErrors();
      }
    } else if (!prod_okay && rdfa_okay) {
      /* Validators disagree on instruction legality */
      DecoderError("VALIDATORS DISAGREE (prod rejects, RDFA accepts)",
                   &pinst,
                   &dinst,
                   "");
      IncrErrors();
    } else {
      /* Both validators see an illegal instruction */
      IncrInvalid();
    }

    if (gVerbose) {
      PrintDisassembledInstructionVariants(&pinst, &dinst);
    }
  } while (0);
}
Ejemplo n.º 4
0
/*
 * On x86-64 Windows, we expect the backtrace to contain the
 * following, with no gaps:
 *
 *   NaClSyscallCSegHook
 *   NaClSwitchSavingStackPtr
 *   NaClStartThreadInApp
 *
 * We could check for those names, but symbols are not always
 * available.  Instead we check for bogus stack frames below, which
 * the stack unwinder lets through.
 */
static void Backtrace(CONTEXT *initial_context) {
  int machine_type;
  CONTEXT context_for_frame = *initial_context;
  STACKFRAME64 frame = { 0 };
  int frame_number = 0;
  int failed = 0;

  fprintf(stderr, "Stack backtrace:\n");
#if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64
  machine_type = IMAGE_FILE_MACHINE_AMD64;
  frame.AddrPC.Offset = initial_context->Rip;
  frame.AddrFrame.Offset = initial_context->Rbp;
  frame.AddrStack.Offset = initial_context->Rsp;
#elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
  machine_type = IMAGE_FILE_MACHINE_I386;
  frame.AddrPC.Offset = initial_context->Eip;
  frame.AddrFrame.Offset = initial_context->Ebp;
  frame.AddrStack.Offset = initial_context->Esp;
#else
# error Unknown architecture
#endif
  frame.AddrPC.Mode = AddrModeFlat;
  frame.AddrFrame.Mode = AddrModeFlat;
  frame.AddrStack.Mode = AddrModeFlat;
  frame.Virtual = 0;

  while (1) {
    STACKFRAME64 previous_frame = frame;
    if (!StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(),
                     &frame, &context_for_frame,
                     NULL, /* use ReadMemory() */
                     SymFunctionTableAccess64, SymGetModuleBase64, NULL)) {
      break;
    }
    fprintf(stderr, "#%i: ip=%p stack=%llx frame=%llx ",
            frame_number,
            frame.AddrPC.Offset,
            frame.AddrStack.Offset,
            frame.AddrFrame.Offset);
    PrintSymbolForAddress(frame.AddrPC.Offset);
    /*
     * Perform some sanity checks.  Windows' x86-64 stack unwinder
     * applies a fallback rule when it sees return addresses without
     * unwind info, but the fallback rule is for leaf functions.  This
     * causes the stack unwinder to report stack frames that cannot
     * possibly be valid in the Windows x86-64 ABI.  We check for such
     * frames here.  An error here would suggest that the unwind info
     * for NaClSwitchSavingStackPtr is wrong.
     *
     * The frame for a non-leaf function Foo() looks like this:
     *     32 bytes   shadow space (scratch space for Foo())
     *   ---- Foo()'s caller's AddrStack points here
     *      8 bytes   return address (points into Foo()'s caller)
     *      8 bytes   scratch space for Foo()
     *   ---- Foo()'s AddrFrame points here (unless the hardware exception
     *        occurred inside Foo(), in which case this is less well-defined)
     *   16*n bytes   scratch space for Foo() (for some n >= 0)
     *     32 bytes   shadow space (scratch space for Foo()'s callees)
     *   ---- Foo()'s rsp and AddrStack point here
     *
     * The frame for a leaf function Bar() that never adjusts rsp
     * looks like this:
     *     32 bytes   shadow space (scratch space for Bar())
     *      8 bytes   return address (points into Bar()'s caller)
     *   ---- Bar()'s rsp and AddrStack points here
     *      8 bytes   not usable by Bar() at all!
     *   ---- Bar()'s AddrFrame points here
     */
    if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64) {
      /* frame_size must be signed for the check to be useful. */
      long long frame_size = frame.AddrFrame.Offset - frame.AddrStack.Offset;
      if (frame_number > 0 && frame_size < 32) {
        fprintf(stderr, "Error: frame_size=%i, which is too small\n",
                frame_size);
        failed = 1;
      }
      if (frame_number > 1 &&
          frame.AddrStack.Offset != previous_frame.AddrFrame.Offset + 16) {
        fprintf(stderr, "Error: stack does not fit with previous frame\n");
        failed = 1;
      }
    }
    frame_number++;
  }
  if (failed) {
    _exit(1);
  }
}