DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (409f3966645a)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "DirtyMemoryHandler.h"

#include "ipc/ChildInternal.h"
#include "mozilla/Sprintf.h"
#include "MemorySnapshot.h"
#include "Thread.h"

#include <mach/exc.h>
#include <mach/mach.h>
#include <mach/mach_vm.h>
#include <sys/time.h>

namespace mozilla {
namespace recordreplay {

static mach_port_t gDirtyMemoryExceptionPort;

// See AsmJSSignalHandlers.cpp.
static const mach_msg_id_t sExceptionId = 2405;

// This definition was generated by mig (the Mach Interface Generator) for the
// routine 'exception_raise' (exc.defs). See js/src/wasm/WasmSignalHandlers.cpp.
#pragma pack(4)
typedef struct {
  mach_msg_header_t Head;
  /* start of the kernel processed data */
  mach_msg_body_t msgh_body;
  mach_msg_port_descriptor_t thread;
  mach_msg_port_descriptor_t task;
  /* end of the kernel processed data */
  NDR_record_t NDR;
  exception_type_t exception;
  mach_msg_type_number_t codeCnt;
  int64_t code[2];
} Request__mach_exception_raise_t;
#pragma pack()

typedef struct {
  Request__mach_exception_raise_t body;
  mach_msg_trailer_t trailer;
} ExceptionRequest;

static void
DirtyMemoryExceptionHandlerThread(void*)
{
  kern_return_t kret;

  while (true) {
    ExceptionRequest request;
    kret = mach_msg(&request.body.Head, MACH_RCV_MSG, 0, sizeof(request),
                    gDirtyMemoryExceptionPort, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    kern_return_t replyCode = KERN_FAILURE;
    if (kret == KERN_SUCCESS &&
        request.body.Head.msgh_id == sExceptionId &&
        request.body.exception == EXC_BAD_ACCESS &&
        request.body.codeCnt == 2)
    {
      uint8_t* faultingAddress = (uint8_t*) request.body.code[1];
      if (HandleDirtyMemoryFault(faultingAddress)) {
        replyCode = KERN_SUCCESS;
      } else {
        child::MinidumpInfo info(request.body.exception,
                                 request.body.code[0], request.body.code[1],
                                 request.body.thread.name);
        child::ReportFatalError(Some(info), "HandleDirtyMemoryFault failed %p %s",
                                faultingAddress, gMozCrashReason ? gMozCrashReason : "");
      }
    } else {
      child::ReportFatalError(Nothing(),
                              "DirtyMemoryExceptionHandlerThread mach_msg returned unexpected data");
    }

    __Reply__exception_raise_t reply;
    reply.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request.body.Head.msgh_bits), 0);
    reply.Head.msgh_size = sizeof(reply);
    reply.Head.msgh_remote_port = request.body.Head.msgh_remote_port;
    reply.Head.msgh_local_port = MACH_PORT_NULL;
    reply.Head.msgh_id = request.body.Head.msgh_id + 100;
    reply.NDR = NDR_record;
    reply.RetCode = replyCode;
    mach_msg(&reply.Head, MACH_SEND_MSG, sizeof(reply), 0, MACH_PORT_NULL,
             MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
  }
}

void
SetupDirtyMemoryHandler()
{
  // Allow repeated calls.
  static bool hasDirtyMemoryHandler = false;
  if (hasDirtyMemoryHandler) {
    return;
  }
  hasDirtyMemoryHandler = true;

  MOZ_RELEASE_ASSERT(AreThreadEventsPassedThrough());
  kern_return_t kret;

  // Get a port which can send and receive data.
  kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &gDirtyMemoryExceptionPort);
  MOZ_RELEASE_ASSERT(kret == KERN_SUCCESS);

  kret = mach_port_insert_right(mach_task_self(),
                                gDirtyMemoryExceptionPort, gDirtyMemoryExceptionPort,
                                MACH_MSG_TYPE_MAKE_SEND);
  MOZ_RELEASE_ASSERT(kret == KERN_SUCCESS);

  // Create a thread to block on reading the port.
  Thread::SpawnNonRecordedThread(DirtyMemoryExceptionHandlerThread, nullptr);

  // Set exception ports on the entire task. Unfortunately, this clobbers any
  // other exception ports for the task, and forwarding to those other ports
  // is not easy to get right.
  kret = task_set_exception_ports(mach_task_self(),
                                  EXC_MASK_BAD_ACCESS,
                                  gDirtyMemoryExceptionPort,
                                  EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
                                  THREAD_STATE_NONE);
  MOZ_RELEASE_ASSERT(kret == KERN_SUCCESS);
}

} // namespace recordreplay
} // namespace mozilla