Backed out changeset 2d6c5cbcc213 (bug 902587) for failures during linking on a CLOSED TREE

--HG--
rename : xpcom/build/PoisonIOInterposer.h => xpcom/build/mozPoisonWrite.h
rename : xpcom/build/PoisonIOInterposerBase.cpp => xpcom/build/mozPoisonWriteBase.cpp
rename : xpcom/build/PoisonIOInterposerMac.cpp => xpcom/build/mozPoisonWriteMac.cpp
rename : xpcom/build/PoisonIOInterposerWin.cpp => xpcom/build/mozPoisonWriteWin.cpp
This commit is contained in:
Ed Morley 2013-10-25 14:24:34 +01:00
Родитель 4de5a040d1
Коммит 019abea10a
25 изменённых файлов: 796 добавлений и 1154 удалений

Просмотреть файл

@ -150,7 +150,7 @@ XRE_SetupDllBlocklistType XRE_SetupDllBlocklist;
XRE_TelemetryAccumulateType XRE_TelemetryAccumulate;
XRE_StartupTimelineRecordType XRE_StartupTimelineRecord;
XRE_mainType XRE_main;
XRE_StopLateWriteChecksType XRE_StopLateWriteChecks;
XRE_DisableWritePoisoningType XRE_DisableWritePoisoning;
static const nsDynamicFunctionLoad kXULFuncs[] = {
{ "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath },
@ -162,7 +162,7 @@ static const nsDynamicFunctionLoad kXULFuncs[] = {
{ "XRE_TelemetryAccumulate", (NSFuncPtr*) &XRE_TelemetryAccumulate },
{ "XRE_StartupTimelineRecord", (NSFuncPtr*) &XRE_StartupTimelineRecord },
{ "XRE_main", (NSFuncPtr*) &XRE_main },
{ "XRE_StopLateWriteChecks", (NSFuncPtr*) &XRE_StopLateWriteChecks },
{ "XRE_DisableWritePoisoning", (NSFuncPtr*) &XRE_DisableWritePoisoning },
{ nullptr, nullptr }
};
@ -642,7 +642,7 @@ int main(int argc, char* argv[])
// at least one such write that we don't control (see bug 826029). For
// now we enable writes again and early exits will have to use exit instead
// of _exit.
XRE_StopLateWriteChecks();
XRE_DisableWritePoisoning();
#endif
return result;

Просмотреть файл

@ -29,6 +29,7 @@
#include "nsPISocketTransportService.h"
#include "nsServiceManagerUtils.h"
#include "TestHarness.h"
#include "mozilla/mozPoisonWrite.h"
class MtransportTestUtils {
public:

Просмотреть файл

@ -22,7 +22,7 @@
#include "nsIPropertyBag2.h"
#include "mozilla/Services.h"
#include "mozilla/Preferences.h"
#include "mozilla/LateWriteChecks.h"
#include "mozilla/mozPoisonWrite.h"
#include "mozIStorageCompletionCallback.h"
#include "sqlite3.h"

Просмотреть файл

@ -37,6 +37,7 @@
#include "jsapi.h"
#include "prenv.h"
#include "nsAppDirectoryServiceDefs.h"
#include "mozilla/mozPoisonWrite.h"
#if defined(XP_WIN)
// Prevent collisions with nsAppStartup::GetStartupInfo()

Просмотреть файл

@ -49,7 +49,7 @@
#include "mozilla/Mutex.h"
#include "mozilla/FileUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/PoisonIOInterposer.h"
#include "mozilla/mozPoisonWrite.h"
#if defined(MOZ_ENABLE_PROFILER_SPS)
#include "shared-libraries.h"
#endif

Просмотреть файл

@ -82,7 +82,7 @@ using mozilla::InjectCrashRunnable;
#include <vector>
#include "mozilla/mozalloc_oom.h"
#include "mozilla/LateWriteChecks.h"
#include "mozilla/mozPoisonWrite.h"
#if defined(XP_MACOSX)
CFStringRef reporterClientAppID = CFSTR("org.mozilla.crashreporter");
@ -792,7 +792,7 @@ static bool ShouldReport()
namespace {
bool Filter(void* context) {
mozilla::StopLateWriteChecks();
mozilla::DisableWritePoisoning();
return true;
}
}
@ -2234,7 +2234,7 @@ OOPInitialized()
#ifdef XP_MACOSX
static bool ChildFilter(void *context) {
mozilla::StopLateWriteChecks();
mozilla::DisableWritePoisoning();
return true;
}
#endif

Просмотреть файл

@ -129,7 +129,7 @@
#include "nsINIParser.h"
#include "mozilla/Omnijar.h"
#include "mozilla/StartupTimeline.h"
#include "mozilla/LateWriteChecks.h"
#include "mozilla/mozPoisonWrite.h"
#include <stdlib.h>
@ -3961,10 +3961,10 @@ XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
// corresponds to nsIAppStartup.quit(eRestart)
if (rv == NS_SUCCESS_RESTART_APP) {
appInitiatedRestart = true;
// We have an application restart don't do any shutdown checks here
// In particular we don't want to poison IO for checking late-writes.
gShutdownChecks = SCM_NOTHING;
} else {
// We will have a real shutdown, let ShutdownXPCOM poison writes to
// find any late ones.
mozilla::EnableWritePoisoning();
}
if (!mShuttingDown) {
@ -4144,8 +4144,8 @@ void SetWindowsEnvironment(WindowsEnvironmentType aEnvID);
#endif // MOZ_METRO || !defined(XP_WIN)
void
XRE_StopLateWriteChecks(void) {
mozilla::StopLateWriteChecks();
XRE_DisableWritePoisoning(void) {
mozilla::DisableWritePoisoning();
}
int

Просмотреть файл

@ -35,7 +35,7 @@
#include "nsStackWalk.h"
#include "nsTraceMallocCallbacks.h"
#include "nsTypeInfo.h"
#include "mozilla/PoisonIOInterposer.h"
#include "mozilla/mozPoisonWrite.h"
#if defined(XP_MACOSX)

Просмотреть файл

@ -123,7 +123,7 @@
#include <stdio.h>
#include "mozilla/Likely.h"
#include "mozilla/PoisonIOInterposer.h"
#include "mozilla/mozPoisonWrite.h"
#include "mozilla/Telemetry.h"
#include "mozilla/ThreadLocal.h"

Просмотреть файл

@ -31,7 +31,7 @@
#endif
#include "mozilla/BlockingResourceBase.h"
#include "mozilla/PoisonIOInterposer.h"
#include "mozilla/mozPoisonWrite.h"
#ifdef HAVE_DLOPEN
#include <dlfcn.h>

Просмотреть файл

@ -1,248 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 sts=4 ci et: */
/* 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 <algorithm>
#include "mozilla/IOInterposer.h"
#include "mozilla/PoisonIOInterposer.h"
#include "mozilla/ProcessedStack.h"
#include "mozilla/SHA1.h"
#include "mozilla/Scoped.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Telemetry.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsPrintfCString.h"
#include "nsStackWalk.h"
#include "plstr.h"
#ifdef XP_WIN
#define NS_T(str) L ## str
#define NS_SLASH "\\"
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <windows.h>
#else
#define NS_SLASH "/"
#endif
#include "LateWriteChecks.h"
using namespace mozilla;
/*************************** Auxiliary Declarations ***************************/
// This a wrapper over a file descriptor that provides a Printf method and
// computes the sha1 of the data that passes through it.
class SHA1Stream
{
public:
explicit SHA1Stream(FILE *stream)
: mFile(stream)
{
MozillaRegisterDebugFILE(mFile);
}
void Printf(const char *aFormat, ...)
{
MOZ_ASSERT(mFile);
va_list list;
va_start(list, aFormat);
nsAutoCString str;
str.AppendPrintf(aFormat, list);
va_end(list);
mSHA1.update(str.get(), str.Length());
fwrite(str.get(), 1, str.Length(), mFile);
}
void Finish(SHA1Sum::Hash &aHash)
{
int fd = fileno(mFile);
fflush(mFile);
MozillaUnRegisterDebugFD(fd);
fclose(mFile);
mSHA1.finish(aHash);
mFile = NULL;
}
private:
FILE *mFile;
SHA1Sum mSHA1;
};
static void RecordStackWalker(void *aPC, void *aSP, void *aClosure)
{
std::vector<uintptr_t> *stack =
static_cast<std::vector<uintptr_t>*>(aClosure);
stack->push_back(reinterpret_cast<uintptr_t>(aPC));
}
/**************************** Late-Write Observer ****************************/
/**
* An implementation of IOInterposeObserver to be registered with IOInterposer.
* This observer logs all writes as late writes.
*/
class LateWriteObserver MOZ_FINAL : public IOInterposeObserver
{
public:
LateWriteObserver(const char* aProfileDirectory)
: mProfileDirectory(PL_strdup(aProfileDirectory))
{
}
~LateWriteObserver() {
PL_strfree(mProfileDirectory);
mProfileDirectory = nullptr;
}
void Observe(IOInterposeObserver::Observation& aObservation);
private:
char* mProfileDirectory;
};
void LateWriteObserver::Observe(IOInterposeObserver::Observation& aOb)
{
// Crash if that is the shutdown check mode
if (gShutdownChecks == SCM_CRASH) {
MOZ_CRASH();
}
// If we have shutdown mode SCM_NOTHING or we can't record then abort
if (gShutdownChecks == SCM_NOTHING || !Telemetry::CanRecord()) {
return;
}
// Write the stack and loaded libraries to a file. We can get here
// concurrently from many writes, so we use multiple temporary files.
std::vector<uintptr_t> rawStack;
NS_StackWalk(RecordStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
reinterpret_cast<void*>(&rawStack), 0, nullptr);
Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack);
nsPrintfCString nameAux("%s%s%s", mProfileDirectory,
NS_SLASH, "Telemetry.LateWriteTmpXXXXXX");
char *name;
nameAux.GetMutableData(&name);
// We want the sha1 of the entire file, so please don't write to fd
// directly; use sha1Stream.
FILE *stream;
#ifdef XP_WIN
HANDLE hFile;
do {
// mkstemp isn't supported so keep trying until we get a file
int result = _mktemp_s(name, strlen(name) + 1);
hFile = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL, NULL);
} while (GetLastError() == ERROR_FILE_EXISTS);
if (hFile == INVALID_HANDLE_VALUE) {
NS_RUNTIMEABORT("Um, how did we get here?");
}
// http://support.microsoft.com/kb/139640
int fd = _open_osfhandle((intptr_t)hFile, _O_APPEND);
if (fd == -1) {
NS_RUNTIMEABORT("Um, how did we get here?");
}
stream = _fdopen(fd, "w");
#else
int fd = mkstemp(name);
stream = fdopen(fd, "w");
#endif
SHA1Stream sha1Stream(stream);
size_t numModules = stack.GetNumModules();
sha1Stream.Printf("%u\n", (unsigned)numModules);
for (size_t i = 0; i < numModules; ++i) {
Telemetry::ProcessedStack::Module module = stack.GetModule(i);
sha1Stream.Printf("%s %s\n", module.mBreakpadId.c_str(),
module.mName.c_str());
}
size_t numFrames = stack.GetStackSize();
sha1Stream.Printf("%u\n", (unsigned)numFrames);
for (size_t i = 0; i < numFrames; ++i) {
const Telemetry::ProcessedStack::Frame &frame =
stack.GetFrame(i);
// NOTE: We write the offsets, while the atos tool expects a value with
// the virtual address added. For example, running otool -l on the the firefox
// binary shows
// cmd LC_SEGMENT_64
// cmdsize 632
// segname __TEXT
// vmaddr 0x0000000100000000
// so to print the line matching the offset 123 one has to run
// atos -o firefox 0x100000123.
sha1Stream.Printf("%d %x\n", frame.mModIndex, (unsigned)frame.mOffset);
}
SHA1Sum::Hash sha1;
sha1Stream.Finish(sha1);
// Note: These files should be deleted by telemetry once it reads them. If
// there were no telemetry runs by the time we shut down, we just add files
// to the existing ones instead of replacing them. Given that each of these
// files is a bug to be fixed, that is probably the right thing to do.
// We append the sha1 of the contents to the file name. This provides a simple
// client side deduplication.
nsPrintfCString finalName("%s%s", mProfileDirectory,
"/Telemetry.LateWriteFinal-");
for (int i = 0; i < 20; ++i) {
finalName.AppendPrintf("%02x", sha1[i]);
}
PR_Delete(finalName.get());
PR_Rename(name, finalName.get());
}
/******************************* Setup/Teardown *******************************/
static StaticAutoPtr<LateWriteObserver> sLateWriteObserver;
namespace mozilla{
void InitLateWriteChecks()
{
nsCOMPtr<nsIFile> mozFile;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mozFile));
if (mozFile) {
nsAutoCString nativePath;
nsresult rv = mozFile->GetNativePath(nativePath);
if (NS_SUCCEEDED(rv) && nativePath.get()) {
sLateWriteObserver = new LateWriteObserver(nativePath.get());
}
}
}
void BeginLateWriteChecks()
{
if (sLateWriteObserver) {
IOInterposer::Register(
IOInterposeObserver::OpWriteFSync,
sLateWriteObserver
);
}
}
void StopLateWriteChecks()
{
if (sLateWriteObserver) {
IOInterposer::Unregister(
IOInterposeObserver::OpAll,
sLateWriteObserver
);
// Deallocation would not be thread-safe, and StopLateWriteChecks() is
// called at shutdown and only in special cases.
// sLateWriteObserver = nullptr;
}
}
} // namespace mozilla

Просмотреть файл

@ -1,59 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 sts=4 ci et: */
/* 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/. */
#ifndef mozilla_LateWriteChecks_h
#define mozilla_LateWriteChecks_h
// This file, along with LateWriteChecks.cpp, serves to check for and report
// late writes. The idea is discover writes to the file system that happens
// during shutdown such that these maybe be moved forward and the process may be
// killed without waiting for static destructors.
namespace mozilla {
/** Different shutdown check modes */
enum ShutdownChecksMode {
SCM_CRASH, /** Crash on shutdown check failure */
SCM_RECORD, /** Record shutdown check violations */
SCM_NOTHING /** Don't attempt any shutdown checks */
};
/**
* Current shutdown check mode.
* This variable is defined and initialized in nsAppRunner.cpp
*/
extern ShutdownChecksMode gShutdownChecks;
/**
* Allocate structures and acquire information from XPCOM necessary to do late
* write checks. This function must be invoked before BeginLateWriteChecks()
* and before XPCOM has stopped working.
*/
void InitLateWriteChecks();
/**
* Begin recording all writes as late-writes. This function should be called
* when all legitimate writes have occurred. This function does not rely on
* XPCOM as it is designed to be invoked during XPCOM shutdown.
*
* For late-write checks to work you must initialize one or more backends that
* reports IO through the IOInterposer API. PoisonIOInterposer would probably
* be the backend of choice in this case.
*
* Note: BeginLateWriteChecks() must have been invoked before this function.
*/
void BeginLateWriteChecks();
/**
* Stop recording all writes as late-writes, call this function when you want
* late-write checks to stop. I.e. exception handling, or the special case on
* Mac described in bug 826029.
*/
void StopLateWriteChecks();
} // mozilla
#endif // mozilla_LateWriteChecks_h

Просмотреть файл

@ -1,88 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 sts=4 ci et: */
/* 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/. */
#ifndef mozilla_PoisonIOInterposer_h
#define mozilla_PoisonIOInterposer_h
#include "mozilla/Types.h"
#include <stdio.h>
#if defined(MOZ_ENABLE_PROFILER_SPS) && (defined(XP_WIN) || defined(XP_MACOSX))
MOZ_BEGIN_EXTERN_C
/** Register file descriptor to be ignored by poisoning IO interposer */
void MozillaRegisterDebugFD(int fd);
/** Register file to be ignored by poisoning IO interposer */
void MozillaRegisterDebugFILE(FILE *f);
/** Unregister file descriptor from being ignored by poisoning IO interposer */
void MozillaUnRegisterDebugFD(int fd);
/** Unregister file from being ignored by poisoning IO interposer */
void MozillaUnRegisterDebugFILE(FILE *f);
MOZ_END_EXTERN_C
#ifdef __cplusplus
namespace mozilla {
/**
* Check if a file is registered as a debug file.
*/
bool IsDebugFile(intptr_t aFileID);
/**
* Initialize IO poisoning, this is only safe to do on the main-thread when no
* other threads are running.
*
* Please, note that this probably has performance implications as all
*/
void InitPoisonIOInterposer();
#ifdef XP_MACOSX
/**
* Check that writes are dirty before reporting I/O (Mac OS X only)
* This is necessary for late-write checks on Mac OS X, but reading the buffer
* from file to see if we're writing dirty bits is expensive, so we don't want
* to do this for everything else that uses
*/
void OnlyReportDirtyWrites();
#endif /* XP_MACOSX */
/**
* Clear IO poisoning, this is only safe to do on the main-thread when no other
* threads are running.
*/
void ClearPoisonIOInterposer();
} // namespace mozilla
#endif /* __cplusplus */
#else /* MOZ_ENABLE_PROFILER_SPS && (XP_WIN || XP_MACOSX) */
MOZ_BEGIN_EXTERN_C
inline void MozillaRegisterDebugFD(int fd){}
inline void MozillaRegisterDebugFILE(FILE *f){}
inline void MozillaUnRegisterDebugFD(int fd){}
inline void MozillaUnRegisterDebugFILE(FILE *f){}
MOZ_END_EXTERN_C
#ifdef __cplusplus
namespace mozilla {
inline bool IsDebugFile(intptr_t aFileID){ return true; }
inline void InitPoisonIOInterposer(){}
inline void ClearPoisonIOInterposer(){}
#ifdef XP_MACOSX
inline void OnlyReportDirtyWrites(){}
#endif /* XP_MACOSX */
} // namespace mozilla
#endif /* __cplusplus */
#endif /* MOZ_ENABLE_PROFILER_SPS && (XP_WIN || XP_MACOSX) */
#endif // mozilla_PoisonIOInterposer_h

Просмотреть файл

@ -1,150 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 ci et: */
/* 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 "mozilla/Mutex.h"
#include "mozilla/Scoped.h"
#include <algorithm>
#include <vector>
#include "PoisonIOInterposer.h"
// Auxiliary method to convert file descriptors to ids
#if defined(XP_WIN32)
#include <io.h>
inline intptr_t FileDescriptorToID(int aFd) {
return _get_osfhandle(aFd);
}
#else
inline intptr_t FileDescriptorToID(int aFd) {
return aFd;
}
#endif /* if not XP_WIN32 */
using namespace mozilla;
namespace {
struct DebugFilesAutoLockTraits {
typedef PRLock *type;
const static type empty() {
return nullptr;
}
const static void release(type aL) {
PR_Unlock(aL);
}
};
class DebugFilesAutoLock : public Scoped<DebugFilesAutoLockTraits> {
static PRLock *Lock;
public:
static void Clear();
static PRLock *getDebugFileIDsLock() {
// On windows this static is not thread safe, but we know that the first
// call is from
// * An early registration of a debug FD or
// * The call to InitWritePoisoning.
// Since the early debug FDs are logs created early in the main thread
// and no writes are trapped before InitWritePoisoning, we are safe.
if (!Lock) {
Lock = PR_NewLock();
}
// We have to use something lower level than a mutex. If we don't, we
// can get recursive in here when called from logging a call to free.
return Lock;
}
DebugFilesAutoLock() :
Scoped<DebugFilesAutoLockTraits>(getDebugFileIDsLock()) {
PR_Lock(get());
}
};
PRLock *DebugFilesAutoLock::Lock;
void DebugFilesAutoLock::Clear() {
MOZ_ASSERT(Lock != nullptr);
Lock = nullptr;
}
// Return a vector used to hold the IDs of the current debug files. On unix
// an ID is a file descriptor. On Windows it is a file HANDLE.
std::vector<intptr_t>* getDebugFileIDs() {
PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(DebugFilesAutoLock::getDebugFileIDsLock());
// We have to use new as some write happen during static destructors
// so an static std::vector might be destroyed while we still need it.
static std::vector<intptr_t> *DebugFileIDs = new std::vector<intptr_t>();
return DebugFileIDs;
}
} // anonymous namespace
namespace mozilla{
// Auxiliary Method to test if a file descriptor is registered to be ignored
// by the poisoning IO interposer
bool IsDebugFile(intptr_t aFileID) {
DebugFilesAutoLock lockedScope;
std::vector<intptr_t> &Vec = *getDebugFileIDs();
return std::find(Vec.begin(), Vec.end(), aFileID) != Vec.end();
}
// Clean-up for the registered debug files.
// We should probably make sure all debug files are unregistered instead.
// But as the poison IO interposer is used for late-write checks we're not
// disabling it at any point yet. So Really no need for this.
//
// void ClearDebugFileRegister() {
// PRLock *Lock;
// {
// DebugFilesAutoLock lockedScope;
// delete getDebugFileIDs();
// Lock = DebugFilesAutoLock::getDebugFileIDsLock();
// DebugFilesAutoLock::Clear();
// }
// PR_DestroyLock(Lock);
// }
} // namespace mozilla
extern "C" {
void MozillaRegisterDebugFD(int fd) {
intptr_t fileId = FileDescriptorToID(fd);
DebugFilesAutoLock lockedScope;
std::vector<intptr_t> &Vec = *getDebugFileIDs();
MOZ_ASSERT(std::find(Vec.begin(), Vec.end(), fileId) == Vec.end());
Vec.push_back(fileId);
}
void MozillaRegisterDebugFILE(FILE *f) {
int fd = fileno(f);
if (fd == 1 || fd == 2) {
return;
}
MozillaRegisterDebugFD(fd);
}
void MozillaUnRegisterDebugFD(int fd) {
DebugFilesAutoLock lockedScope;
intptr_t fileId = FileDescriptorToID(fd);
std::vector<intptr_t> &Vec = *getDebugFileIDs();
std::vector<intptr_t>::iterator i =
std::find(Vec.begin(), Vec.end(), fileId);
MOZ_ASSERT(i != Vec.end());
Vec.erase(i);
}
void MozillaUnRegisterDebugFILE(FILE *f) {
int fd = fileno(f);
if (fd == 1 || fd == 2) {
return;
}
fflush(f);
MozillaUnRegisterDebugFD(fd);
}
}

Просмотреть файл

@ -1,340 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 sts=4 ci et: */
/* 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 "PoisonIOInterposer.h"
#include "mach_override.h"
#include "mozilla/Assertions.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/IOInterposer.h"
#include "mozilla/Mutex.h"
#include "mozilla/ProcessedStack.h"
#include "mozilla/Scoped.h"
#include "mozilla/Telemetry.h"
#include "mozilla/Util.h"
#include "nsPrintfCString.h"
#include "nsStackWalk.h"
#include "nsTraceRefcntImpl.h"
#include "plstr.h"
#include "prio.h"
#include <vector>
#include <algorithm>
#include <string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <aio.h>
#include <dlfcn.h>
namespace {
using namespace mozilla;
// Bit tracking if poisoned writes are enabled
static bool sIsEnabled = false;
// Check if writes are dirty before reporting IO
static bool sOnlyReportDirtyWrites = false;
// Routines for write validation
bool IsValidWrite(int fd, const void *wbuf, size_t count);
bool IsIPCWrite(int fd, const struct stat &buf);
/******************************** IO AutoTimer ********************************/
/**
* RAII class for timing the duration of an I/O call and reporting the result
* to the IOInterposeObserver API.
*/
class MacIOAutoObservation : public IOInterposeObserver::Observation
{
public:
MacIOAutoObservation(IOInterposeObserver::Operation aOp,
const char* aReference, int aFd)
: mFd(aFd),
mShouldObserve(sIsEnabled && IOInterposer::IsObservedOperation(aOp) &&
!IsDebugFile(aFd))
{
if (mShouldObserve) {
mOperation = aOp;
mReference = aReference;
mStart = TimeStamp::Now();
}
}
MacIOAutoObservation(IOInterposeObserver::Operation aOp,
const char* aReference, int aFd, const void *aBuf,
size_t aCount)
: mFd(aFd),
mShouldObserve(sIsEnabled && IOInterposer::IsObservedOperation(aOp) &&
!IsDebugFile(aFd))
{
if (mShouldObserve) {
mShouldObserve = IsValidWrite(aFd, aBuf, aCount);
if (mShouldObserve) {
mOperation = aOp;
mReference = aReference;
mStart = TimeStamp::Now();
}
}
}
~MacIOAutoObservation()
{
if (mShouldObserve) {
mEnd = TimeStamp::Now();
// Report this observation
IOInterposer::Report(*this);
}
}
private:
int mFd;
bool mShouldObserve;
};
/****************************** Write Validation ******************************/
// We want to detect "actual" writes, not IPC. Some IPC mechanisms are
// implemented with file descriptors, so filter them out.
bool IsIPCWrite(int fd, const struct stat &buf) {
if ((buf.st_mode & S_IFMT) == S_IFIFO) {
return true;
}
if ((buf.st_mode & S_IFMT) != S_IFSOCK) {
return false;
}
sockaddr_storage address;
socklen_t len = sizeof(address);
if (getsockname(fd, (sockaddr*) &address, &len) != 0) {
return true; // Ignore the fd if we can't find what it is.
}
return address.ss_family == AF_UNIX;
}
// We want to report actual disk IO not things that don't move bits on the disk
bool IsValidWrite(int fd, const void *wbuf, size_t count)
{
// Ignore writes of zero bytes, Firefox does some during shutdown.
if (count == 0) {
return false;
}
{
struct stat buf;
int rv = fstat(fd, &buf);
if (rv != 0) {
return true;
}
if (IsIPCWrite(fd, buf)) {
return false;
}
}
// For writev we pass NULL in wbuf. We should only get here from
// dbm, and it uses write, so assert that we have wbuf.
if (!wbuf) {
return true;
}
// Break, here if we're allowed to report non-dirty writes
if(!sOnlyReportDirtyWrites) {
return true;
}
// As a really bad hack, accept writes that don't change the on disk
// content. This is needed because dbm doesn't keep track of dirty bits
// and can end up writing the same data to disk twice. Once when the
// user (nss) asks it to sync and once when closing the database.
ScopedFreePtr<void> wbuf2(malloc(count));
if (!wbuf2) {
return true;
}
off_t pos = lseek(fd, 0, SEEK_CUR);
if (pos == -1) {
return true;
}
ssize_t r = read(fd, wbuf2, count);
if (r < 0 || (size_t)r != count) {
return true;
}
int cmp = memcmp(wbuf, wbuf2, count);
if (cmp != 0) {
return true;
}
off_t pos2 = lseek(fd, pos, SEEK_SET);
if (pos2 != pos) {
return true;
}
// Otherwise this is not a valid write
return false;
}
/*************************** Function Interception ***************************/
/** Structure for declaration of function override */
struct FuncData {
const char *Name; // Name of the function for the ones we use dlsym
const void *Wrapper; // The function that we will replace 'Function' with
void *Function; // The function that will be replaced with 'Wrapper'
void *Buffer; // Will point to the jump buffer that lets us call
// 'Function' after it has been replaced.
};
// Wrap aio_write. We have not seen it before, so just assert/report it.
typedef ssize_t (*aio_write_t)(struct aiocb *aiocbp);
ssize_t wrap_aio_write(struct aiocb *aiocbp);
FuncData aio_write_data = { 0, (void*) wrap_aio_write, (void*) aio_write };
ssize_t wrap_aio_write(struct aiocb *aiocbp) {
const char* ref = "aio_write";
MacIOAutoObservation timer(IOInterposeObserver::OpWrite, ref,
aiocbp->aio_fildes);
aio_write_t old_write = (aio_write_t) aio_write_data.Buffer;
return old_write(aiocbp);
}
// Wrap pwrite-like functions.
// We have not seen them before, so just assert/report it.
typedef ssize_t (*pwrite_t)(int fd, const void *buf, size_t nbyte, off_t offset);
template<FuncData &foo>
ssize_t wrap_pwrite_temp(int fd, const void *buf, size_t nbyte, off_t offset) {
const char* ref = "pwrite_*";
MacIOAutoObservation timer(IOInterposeObserver::OpWrite, ref, fd);
pwrite_t old_write = (pwrite_t) foo.Buffer;
return old_write(fd, buf, nbyte, offset);
}
// Define a FuncData for a pwrite-like functions.
#define DEFINE_PWRITE_DATA(X, NAME) \
FuncData X ## _data = { NAME, (void*) wrap_pwrite_temp<X ## _data> }; \
// This exists everywhere.
DEFINE_PWRITE_DATA(pwrite, "pwrite")
// These exist on 32 bit OS X
DEFINE_PWRITE_DATA(pwrite_NOCANCEL_UNIX2003, "pwrite$NOCANCEL$UNIX2003");
DEFINE_PWRITE_DATA(pwrite_UNIX2003, "pwrite$UNIX2003");
// This exists on 64 bit OS X
DEFINE_PWRITE_DATA(pwrite_NOCANCEL, "pwrite$NOCANCEL");
typedef ssize_t (*writev_t)(int fd, const struct iovec *iov, int iovcnt);
template<FuncData &foo>
ssize_t wrap_writev_temp(int fd, const struct iovec *iov, int iovcnt) {
const char* ref = "pwrite_*";
MacIOAutoObservation timer(IOInterposeObserver::OpWrite, ref, fd, nullptr,
iovcnt);
writev_t old_write = (writev_t) foo.Buffer;
return old_write(fd, iov, iovcnt);
}
// Define a FuncData for a writev-like functions.
#define DEFINE_WRITEV_DATA(X, NAME) \
FuncData X ## _data = { NAME, (void*) wrap_writev_temp<X ## _data> }; \
// This exists everywhere.
DEFINE_WRITEV_DATA(writev, "writev");
// These exist on 32 bit OS X
DEFINE_WRITEV_DATA(writev_NOCANCEL_UNIX2003, "writev$NOCANCEL$UNIX2003");
DEFINE_WRITEV_DATA(writev_UNIX2003, "writev$UNIX2003");
// This exists on 64 bit OS X
DEFINE_WRITEV_DATA(writev_NOCANCEL, "writev$NOCANCEL");
typedef ssize_t (*write_t)(int fd, const void *buf, size_t count);
template<FuncData &foo>
ssize_t wrap_write_temp(int fd, const void *buf, size_t count) {
const char* ref = "pwrite_*";
MacIOAutoObservation timer(IOInterposeObserver::OpWrite, ref, fd, buf,
count);
write_t old_write = (write_t) foo.Buffer;
return old_write(fd, buf, count);
}
// Define a FuncData for a write-like functions.
#define DEFINE_WRITE_DATA(X, NAME) \
FuncData X ## _data = { NAME, (void*) wrap_write_temp<X ## _data> }; \
// This exists everywhere.
DEFINE_WRITE_DATA(write, "write");
// These exist on 32 bit OS X
DEFINE_WRITE_DATA(write_NOCANCEL_UNIX2003, "write$NOCANCEL$UNIX2003");
DEFINE_WRITE_DATA(write_UNIX2003, "write$UNIX2003");
// This exists on 64 bit OS X
DEFINE_WRITE_DATA(write_NOCANCEL, "write$NOCANCEL");
FuncData *Functions[] = { &aio_write_data,
&pwrite_data,
&pwrite_NOCANCEL_UNIX2003_data,
&pwrite_UNIX2003_data,
&pwrite_NOCANCEL_data,
&write_data,
&write_NOCANCEL_UNIX2003_data,
&write_UNIX2003_data,
&write_NOCANCEL_data,
&writev_data,
&writev_NOCANCEL_UNIX2003_data,
&writev_UNIX2003_data,
&writev_NOCANCEL_data};
const int NumFunctions = ArrayLength(Functions);
} // anonymous namespace
/******************************** IO Poisoning ********************************/
namespace mozilla {
void InitPoisonIOInterposer() {
// Enable reporting from poisoned write methods
sIsEnabled = true;
// Make sure we only poison writes once!
static bool WritesArePoisoned = false;
if (WritesArePoisoned) {
return;
}
WritesArePoisoned = true;
// stdout and stderr are OK.
MozillaRegisterDebugFD(1);
MozillaRegisterDebugFD(2);
for (int i = 0; i < NumFunctions; ++i) {
FuncData *d = Functions[i];
if (!d->Function) {
d->Function = dlsym(RTLD_DEFAULT, d->Name);
}
if (!d->Function) {
continue;
}
DebugOnly<mach_error_t> t = mach_override_ptr(d->Function, d->Wrapper,
&d->Buffer);
MOZ_ASSERT(t == err_none);
}
}
void OnlyReportDirtyWrites() {
sOnlyReportDirtyWrites = true;
}
void ClearPoisonIOInterposer() {
// Not sure how or if we can unpoison the functions. Would be nice, but no
// worries we won't need to do this anyway.
sIsEnabled = false;
}
} // namespace mozilla

Просмотреть файл

@ -1,222 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 ci et: */
/* 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 "PoisonIOInterposer.h"
#include <algorithm>
#include <stdio.h>
#include <vector>
#include <io.h>
#include <windows.h>
#include <winternl.h>
#include "mozilla/Assertions.h"
#include "mozilla/IOInterposer.h"
#include "mozilla/Mutex.h"
#include "mozilla/TimeStamp.h"
#include "nsTArray.h"
#include "nsWindowsDllInterceptor.h"
#include "plstr.h"
using namespace mozilla;
namespace {
// Keep track of poisoned state. Notice that there is no reason to lock access
// to this variable as it's only changed in InitPoisonIOInterposer and
// ClearPoisonIOInterposer which may only be called on the main-thread when no
// other threads are running.
static bool sIOPoisoned = false;
/************************ Internal NT API Declarations ************************/
/**
* Function pointer declaration for internal NT routine to write data to file.
* For documentation on the NtWriteFile routine, see ZwWriteFile on MSDN.
*/
typedef NTSTATUS (WINAPI *NtWriteFileFn)(
IN HANDLE aFileHandle,
IN HANDLE aEvent,
IN PIO_APC_ROUTINE aApc,
IN PVOID aApcCtx,
OUT PIO_STATUS_BLOCK aIoStatus,
IN PVOID aBuffer,
IN ULONG aLength,
IN PLARGE_INTEGER aOffset,
IN PULONG aKey
);
/**
* Function pointer declaration for internal NT routine to write data to file.
* No documentation exists, see wine sources for details.
*/
typedef NTSTATUS (WINAPI *NtWriteFileGatherFn)(
IN HANDLE aFileHandle,
IN HANDLE aEvent,
IN PIO_APC_ROUTINE aApc,
IN PVOID aApcCtx,
OUT PIO_STATUS_BLOCK aIoStatus,
IN FILE_SEGMENT_ELEMENT* aSegments,
IN ULONG aLength,
IN PLARGE_INTEGER aOffset,
IN PULONG aKey
);
/*************************** Auxiliary Declarations ***************************/
/**
* RAII class for timing the duration of an I/O call and reporting the result
* to the IOInterposeObserver API.
*/
class WinIOAutoObservation : public IOInterposeObserver::Observation
{
public:
WinIOAutoObservation(IOInterposeObserver::Operation aOp,
const char* aReference, HANDLE aFileHandle)
: mFileHandle(aFileHandle),
mShouldObserve(IOInterposer::IsObservedOperation(aOp) &&
!IsDebugFile(reinterpret_cast<intptr_t>(aFileHandle)))
{
if (mShouldObserve) {
mOperation = aOp;
mReference = aReference;
mStart = TimeStamp::Now();
}
}
~WinIOAutoObservation()
{
if (mShouldObserve) {
mEnd = TimeStamp::Now();
// Report this observation
IOInterposer::Report(*this);
}
}
private:
HANDLE mFileHandle;
bool mShouldObserve;
};
/*************************** IO Interposing Methods ***************************/
// Function pointers to original functions
static NtWriteFileFn gOriginalNtWriteFile;
static NtWriteFileGatherFn gOriginalNtWriteFileGather;
// Interposed NtWriteFile function
static NTSTATUS WINAPI InterposedNtWriteFile(
HANDLE aFileHandle,
HANDLE aEvent,
PIO_APC_ROUTINE aApc,
PVOID aApcCtx,
PIO_STATUS_BLOCK aIoStatus,
PVOID aBuffer,
ULONG aLength,
PLARGE_INTEGER aOffset,
PULONG aKey)
{
// Report IO
const char* ref = "NtWriteFile";
WinIOAutoObservation timer(IOInterposeObserver::OpWrite, ref, aFileHandle);
// Something is badly wrong if this function is undefined
MOZ_ASSERT(gOriginalNtWriteFile);
// Execute original function
return gOriginalNtWriteFile(
aFileHandle,
aEvent,
aApc,
aApcCtx,
aIoStatus,
aBuffer,
aLength,
aOffset,
aKey
);
}
// Interposed NtWriteFileGather function
static NTSTATUS WINAPI InterposedNtWriteFileGather(
HANDLE aFileHandle,
HANDLE aEvent,
PIO_APC_ROUTINE aApc,
PVOID aApcCtx,
PIO_STATUS_BLOCK aIoStatus,
FILE_SEGMENT_ELEMENT* aSegments,
ULONG aLength,
PLARGE_INTEGER aOffset,
PULONG aKey)
{
// Report IO
const char* ref = "NtWriteFileGather";
WinIOAutoObservation timer(IOInterposeObserver::OpWrite, ref, aFileHandle);
// Something is badly wrong if this function is undefined
MOZ_ASSERT(gOriginalNtWriteFileGather);
// Execute original function
return gOriginalNtWriteFileGather(
aFileHandle,
aEvent,
aApc,
aApcCtx,
aIoStatus,
aSegments,
aLength,
aOffset,
aKey
);
}
} // anonymous namespace
/******************************** IO Poisoning ********************************/
// Windows DLL interceptor
static WindowsDllInterceptor sNtDllInterceptor;
namespace mozilla {
void InitPoisonIOInterposer() {
// Don't poison twice... as this function may only be invoked on the main
// thread when no other threads are running, it safe to allow multiple calls
// to InitPoisonIOInterposer() without complaining (ie. failing assertions).
if (sIOPoisoned) {
return;
}
sIOPoisoned = true;
// Stdout and Stderr are OK.
MozillaRegisterDebugFD(1);
MozillaRegisterDebugFD(2);
// Initialize dll interceptor and add hooks
sNtDllInterceptor.Init("ntdll.dll");
sNtDllInterceptor.AddHook(
"NtWriteFile",
reinterpret_cast<intptr_t>(InterposedNtWriteFile),
reinterpret_cast<void**>(&gOriginalNtWriteFile)
);
sNtDllInterceptor.AddHook(
"NtWriteFileGather",
reinterpret_cast<intptr_t>(InterposedNtWriteFileGather),
reinterpret_cast<void**>(&gOriginalNtWriteFileGather)
);
}
void ClearPoisonIOInterposer() {
MOZ_ASSERT(false);
if (sIOPoisoned) {
// Destroy the DLL interceptor
sIOPoisoned = false;
sNtDllInterceptor = WindowsDllInterceptor();
}
}
} // namespace mozilla

Просмотреть файл

@ -17,9 +17,8 @@ EXPORTS += [
EXPORTS.mozilla += [
'FileLocation.h',
'LateWriteChecks.h',
'mozPoisonWrite.h',
'Omnijar.h',
'PoisonIOInterposer.h',
'ServiceList.h',
'Services.h',
'XPCOM.h',
@ -27,6 +26,20 @@ EXPORTS.mozilla += [
if CONFIG['OS_ARCH'] == 'WINNT':
EXPORTS.mozilla += ['perfprobe.h']
SOURCES += [
'mozPoisonWriteBase.cpp',
'mozPoisonWriteWin.cpp',
'perfprobe.cpp',
]
elif CONFIG['OS_ARCH'] == 'Darwin':
SOURCES += [
'mozPoisonWriteBase.cpp',
'mozPoisonWriteMac.cpp',
]
else:
SOURCES += [
'mozPoisonWriteStub.cpp',
]
include('../glue/objs.mozbuild')
@ -36,24 +49,12 @@ SOURCES += xpcom_glue_src_cppsrcs
SOURCES += [
'FileLocation.cpp',
'FrozenFunctions.cpp',
'LateWriteChecks.cpp',
'nsXPComInit.cpp',
'nsXPCOMStrings.cpp',
'Omnijar.cpp',
'Services.cpp',
]
if CONFIG['OS_TARGET'] == 'Darwin':
SOURCES += [
'PoisonIOInterposerBase.cpp',
'PoisonIOInterposerMac.cpp',
]
elif CONFIG['OS_TARGET'] == 'WINNT':
SOURCES += [
'PoisonIOInterposerBase.cpp',
'PoisonIOInterposerWin.cpp',
]
LIBXUL_LIBRARY = True
MSVC_ENABLE_PGO = True

Просмотреть файл

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 sts=4 ci et: */
/* 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/. */
#ifndef MOZPOISONWRITE_H
#define MOZPOISONWRITE_H
#include "mozilla/Types.h"
#include <stdio.h>
MOZ_BEGIN_EXTERN_C
void MozillaRegisterDebugFD(int fd);
void MozillaRegisterDebugFILE(FILE *f);
void MozillaUnRegisterDebugFD(int fd);
void MozillaUnRegisterDebugFILE(FILE *f);
MOZ_END_EXTERN_C
#ifdef __cplusplus
namespace mozilla {
enum ShutdownChecksMode {
SCM_CRASH,
SCM_RECORD,
SCM_NOTHING
};
extern ShutdownChecksMode gShutdownChecks;
void InitWritePoisoning();
void PoisonWrite();
void DisableWritePoisoning();
void EnableWritePoisoning();
}
#endif
#endif

Просмотреть файл

@ -0,0 +1,343 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 ci et: */
/* 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 "mozPoisonWrite.h"
#include "mozPoisonWriteBase.h"
#include "mozilla/ProcessedStack.h"
#include "mozilla/Scoped.h"
#include "mozilla/SHA1.h"
#include "mozilla/Telemetry.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsStackWalk.h"
#include "nsPrintfCString.h"
#include "plstr.h"
#include <algorithm>
#ifdef XP_WIN
#define NS_T(str) L ## str
#define NS_SLASH "\\"
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#else
#define NS_SLASH "/"
#endif
using namespace mozilla;
namespace {
struct DebugFilesAutoLockTraits {
typedef PRLock *type;
const static type empty() {
return nullptr;
}
const static void release(type aL) {
PR_Unlock(aL);
}
};
class DebugFilesAutoLock : public Scoped<DebugFilesAutoLockTraits> {
static PRLock *Lock;
public:
static void Clear();
static PRLock *getDebugFileIDsLock() {
// On windows this static is not thread safe, but we know that the first
// call is from
// * An early registration of a debug FD or
// * The call to InitWritePoisoning.
// Since the early debug FDs are logs created early in the main thread
// and no writes are trapped before InitWritePoisoning, we are safe.
static bool Initialized = false;
if (!Initialized) {
Lock = PR_NewLock();
Initialized = true;
}
// We have to use something lower level than a mutex. If we don't, we
// can get recursive in here when called from logging a call to free.
return Lock;
}
DebugFilesAutoLock() :
Scoped<DebugFilesAutoLockTraits>(getDebugFileIDsLock()) {
PR_Lock(get());
}
};
PRLock *DebugFilesAutoLock::Lock;
void DebugFilesAutoLock::Clear() {
MOZ_ASSERT(Lock != nullptr);
Lock = nullptr;
}
static char *sProfileDirectory = nullptr;
// Return a vector used to hold the IDs of the current debug files. On unix
// an ID is a file descriptor. On Windows it is a file HANDLE.
std::vector<intptr_t>* getDebugFileIDs() {
PRLock *lock = DebugFilesAutoLock::getDebugFileIDsLock();
PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(lock);
// We have to use new as some write happen during static destructors
// so an static std::vector might be destroyed while we still need it.
static std::vector<intptr_t> *DebugFileIDs = new std::vector<intptr_t>();
return DebugFileIDs;
}
// This a wrapper over a file descriptor that provides a Printf method and
// computes the sha1 of the data that passes through it.
class SHA1Stream
{
public:
explicit SHA1Stream(FILE *stream)
: mFile(stream)
{
MozillaRegisterDebugFILE(mFile);
}
void Printf(const char *aFormat, ...)
{
MOZ_ASSERT(mFile);
va_list list;
va_start(list, aFormat);
nsAutoCString str;
str.AppendPrintf(aFormat, list);
va_end(list);
mSHA1.update(str.get(), str.Length());
fwrite(str.get(), 1, str.Length(), mFile);
}
void Finish(SHA1Sum::Hash &aHash)
{
int fd = fileno(mFile);
fflush(mFile);
MozillaUnRegisterDebugFD(fd);
fclose(mFile);
mSHA1.finish(aHash);
mFile = nullptr;
}
private:
FILE *mFile;
SHA1Sum mSHA1;
};
static void RecordStackWalker(void *aPC, void *aSP, void *aClosure)
{
std::vector<uintptr_t> *stack =
static_cast<std::vector<uintptr_t>*>(aClosure);
stack->push_back(reinterpret_cast<uintptr_t>(aPC));
}
enum PoisonState {
POISON_UNINITIALIZED = 0,
POISON_ON,
POISON_OFF
};
// POISON_OFF has two consequences
// * It prevents PoisonWrite from patching the write functions.
// * If the patching has already been done, it prevents AbortOnBadWrite from
// asserting. Note that not all writes use AbortOnBadWrite at this point
// (aio_write for example), so disabling writes after patching doesn't
// completely undo it.
PoisonState sPoisoningState = POISON_UNINITIALIZED;
}
namespace mozilla {
void InitWritePoisoning()
{
// Stdout and Stderr are OK.
MozillaRegisterDebugFD(1);
MozillaRegisterDebugFD(2);
nsCOMPtr<nsIFile> mozFile;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mozFile));
if (mozFile) {
nsAutoCString nativePath;
nsresult rv = mozFile->GetNativePath(nativePath);
if (NS_SUCCEEDED(rv)) {
sProfileDirectory = PL_strdup(nativePath.get());
}
}
}
bool ValidWriteAssert(bool ok)
{
if (gShutdownChecks == SCM_CRASH && !ok) {
MOZ_CRASH();
}
// We normally don't poison writes if gShutdownChecks is SCM_NOTHING, but
// write poisoning can get more users in the future (profiling for example),
// so make sure we behave correctly.
if (gShutdownChecks == SCM_NOTHING || ok || !sProfileDirectory ||
!Telemetry::CanRecord()) {
return ok;
}
// Write the stack and loaded libraries to a file. We can get here
// concurrently from many writes, so we use multiple temporary files.
std::vector<uintptr_t> rawStack;
NS_StackWalk(RecordStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
reinterpret_cast<void*>(&rawStack), 0, nullptr);
Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack);
nsPrintfCString nameAux("%s%s%s", sProfileDirectory,
NS_SLASH, "Telemetry.LateWriteTmpXXXXXX");
char *name;
nameAux.GetMutableData(&name);
// We want the sha1 of the entire file, so please don't write to fd
// directly; use sha1Stream.
FILE *stream;
#ifdef XP_WIN
HANDLE hFile;
do {
// mkstemp isn't supported so keep trying until we get a file
int result = _mktemp_s(name, strlen(name) + 1);
hFile = CreateFileA(name, GENERIC_WRITE, 0, nullptr,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
} while (GetLastError() == ERROR_FILE_EXISTS);
if (hFile == INVALID_HANDLE_VALUE) {
NS_RUNTIMEABORT("Um, how did we get here?");
}
// http://support.microsoft.com/kb/139640
int fd = _open_osfhandle((intptr_t)hFile, _O_APPEND);
if (fd == -1) {
NS_RUNTIMEABORT("Um, how did we get here?");
}
stream = _fdopen(fd, "w");
#else
int fd = mkstemp(name);
stream = fdopen(fd, "w");
#endif
SHA1Stream sha1Stream(stream);
size_t numModules = stack.GetNumModules();
sha1Stream.Printf("%u\n", (unsigned)numModules);
for (size_t i = 0; i < numModules; ++i) {
Telemetry::ProcessedStack::Module module = stack.GetModule(i);
sha1Stream.Printf("%s %s\n", module.mBreakpadId.c_str(),
module.mName.c_str());
}
size_t numFrames = stack.GetStackSize();
sha1Stream.Printf("%u\n", (unsigned)numFrames);
for (size_t i = 0; i < numFrames; ++i) {
const Telemetry::ProcessedStack::Frame &frame =
stack.GetFrame(i);
// NOTE: We write the offsets, while the atos tool expects a value with
// the virtual address added. For example, running otool -l on the the firefox
// binary shows
// cmd LC_SEGMENT_64
// cmdsize 632
// segname __TEXT
// vmaddr 0x0000000100000000
// so to print the line matching the offset 123 one has to run
// atos -o firefox 0x100000123.
sha1Stream.Printf("%d %x\n", frame.mModIndex, (unsigned)frame.mOffset);
}
SHA1Sum::Hash sha1;
sha1Stream.Finish(sha1);
// Note: These files should be deleted by telemetry once it reads them. If
// there were no telemery runs by the time we shut down, we just add files
// to the existing ones instead of replacing them. Given that each of these
// files is a bug to be fixed, that is probably the right thing to do.
// We append the sha1 of the contents to the file name. This provides a simple
// client side deduplication.
nsPrintfCString finalName("%s%s", sProfileDirectory, "/Telemetry.LateWriteFinal-");
for (int i = 0; i < 20; ++i) {
finalName.AppendPrintf("%02x", sha1[i]);
}
PR_Delete(finalName.get());
PR_Rename(name, finalName.get());
return false;
}
void DisableWritePoisoning() {
if (sPoisoningState != POISON_ON)
return;
sPoisoningState = POISON_OFF;
PL_strfree(sProfileDirectory);
sProfileDirectory = nullptr;
PRLock *Lock;
{
DebugFilesAutoLock lockedScope;
delete getDebugFileIDs();
Lock = DebugFilesAutoLock::getDebugFileIDsLock();
DebugFilesAutoLock::Clear();
}
PR_DestroyLock(Lock);
}
void EnableWritePoisoning() {
sPoisoningState = POISON_ON;
}
bool PoisonWriteEnabled()
{
return sPoisoningState == POISON_ON;
}
bool IsDebugFile(intptr_t aFileID) {
DebugFilesAutoLock lockedScope;
std::vector<intptr_t> &Vec = *getDebugFileIDs();
return std::find(Vec.begin(), Vec.end(), aFileID) != Vec.end();
}
} // mozilla
extern "C" {
void MozillaRegisterDebugFD(int fd) {
if (sPoisoningState == POISON_OFF)
return;
DebugFilesAutoLock lockedScope;
intptr_t fileId = FileDescriptorToID(fd);
std::vector<intptr_t> &Vec = *getDebugFileIDs();
MOZ_ASSERT(std::find(Vec.begin(), Vec.end(), fileId) == Vec.end());
Vec.push_back(fileId);
}
void MozillaRegisterDebugFILE(FILE *f) {
if (sPoisoningState == POISON_OFF)
return;
int fd = fileno(f);
if (fd == 1 || fd == 2)
return;
MozillaRegisterDebugFD(fd);
}
void MozillaUnRegisterDebugFD(int fd) {
if (sPoisoningState == POISON_OFF)
return;
DebugFilesAutoLock lockedScope;
intptr_t fileId = FileDescriptorToID(fd);
std::vector<intptr_t> &Vec = *getDebugFileIDs();
std::vector<intptr_t>::iterator i =
std::find(Vec.begin(), Vec.end(), fileId);
MOZ_ASSERT(i != Vec.end());
Vec.erase(i);
}
void MozillaUnRegisterDebugFILE(FILE *f) {
if (sPoisoningState == POISON_OFF)
return;
int fd = fileno(f);
if (fd == 1 || fd == 2)
return;
fflush(f);
MozillaUnRegisterDebugFD(fd);
}
}

Просмотреть файл

@ -0,0 +1,26 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 ci et: */
/* 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/. */
// Private interface for code shared between the platforms of mozPoisonWriteXXX
#ifndef MOZPOISONWRITEBASE_H
#define MOZPOISONWRITEBASE_H
#include <stdio.h>
#include <vector>
#include "nspr.h"
#include "mozilla/NullPtr.h"
#include "mozilla/Util.h"
#include "mozilla/Scoped.h"
namespace mozilla {
bool PoisonWriteEnabled();
bool ValidWriteAssert(bool ok);
bool IsDebugFile(intptr_t aFileID);
intptr_t FileDescriptorToID(int aFd);
}
#endif

Просмотреть файл

@ -0,0 +1,234 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 sts=4 ci et: */
/* 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 "mozilla/mozPoisonWrite.h"
#include "mozPoisonWriteBase.h"
#include "mozilla/Util.h"
#include "nsTraceRefcntImpl.h"
#include "mozilla/Assertions.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Scoped.h"
#include "mozilla/Mutex.h"
#include "mozilla/Telemetry.h"
#include "mozilla/ProcessedStack.h"
#include "nsStackWalk.h"
#include "nsPrintfCString.h"
#include "mach_override.h"
#include "prio.h"
#include "plstr.h"
#include "nsCOMPtr.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "mozilla/SHA1.h"
#include <sys/stat.h>
#include <sys/socket.h>
#include <vector>
#include <algorithm>
#include <string.h>
#include <sys/uio.h>
#include <aio.h>
#include <dlfcn.h>
namespace {
using namespace mozilla;
struct FuncData {
const char *Name; // Name of the function for the ones we use dlsym
const void *Wrapper; // The function that we will replace 'Function' with
void *Function; // The function that will be replaced with 'Wrapper'
void *Buffer; // Will point to the jump buffer that lets us call
// 'Function' after it has been replaced.
};
// Wrap aio_write. We have not seen it before, so just assert/report it.
typedef ssize_t (*aio_write_t)(struct aiocb *aiocbp);
ssize_t wrap_aio_write(struct aiocb *aiocbp);
FuncData aio_write_data = { 0, (void*) wrap_aio_write, (void*) aio_write };
ssize_t wrap_aio_write(struct aiocb *aiocbp) {
ValidWriteAssert(0);
aio_write_t old_write = (aio_write_t) aio_write_data.Buffer;
return old_write(aiocbp);
}
// Wrap pwrite-like functions.
// We have not seen them before, so just assert/report it.
typedef ssize_t (*pwrite_t)(int fd, const void *buf, size_t nbyte, off_t offset);
template<FuncData &foo>
ssize_t wrap_pwrite_temp(int fd, const void *buf, size_t nbyte, off_t offset) {
ValidWriteAssert(0);
pwrite_t old_write = (pwrite_t) foo.Buffer;
return old_write(fd, buf, nbyte, offset);
}
// Define a FuncData for a pwrite-like functions.
#define DEFINE_PWRITE_DATA(X, NAME) \
FuncData X ## _data = { NAME, (void*) wrap_pwrite_temp<X ## _data> }; \
// This exists everywhere.
DEFINE_PWRITE_DATA(pwrite, "pwrite")
// These exist on 32 bit OS X
DEFINE_PWRITE_DATA(pwrite_NOCANCEL_UNIX2003, "pwrite$NOCANCEL$UNIX2003");
DEFINE_PWRITE_DATA(pwrite_UNIX2003, "pwrite$UNIX2003");
// This exists on 64 bit OS X
DEFINE_PWRITE_DATA(pwrite_NOCANCEL, "pwrite$NOCANCEL");
void AbortOnBadWrite(int fd, const void *wbuf, size_t count);
typedef ssize_t (*writev_t)(int fd, const struct iovec *iov, int iovcnt);
template<FuncData &foo>
ssize_t wrap_writev_temp(int fd, const struct iovec *iov, int iovcnt) {
AbortOnBadWrite(fd, 0, iovcnt);
writev_t old_write = (writev_t) foo.Buffer;
return old_write(fd, iov, iovcnt);
}
// Define a FuncData for a writev-like functions.
#define DEFINE_WRITEV_DATA(X, NAME) \
FuncData X ## _data = { NAME, (void*) wrap_writev_temp<X ## _data> }; \
// This exists everywhere.
DEFINE_WRITEV_DATA(writev, "writev");
// These exist on 32 bit OS X
DEFINE_WRITEV_DATA(writev_NOCANCEL_UNIX2003, "writev$NOCANCEL$UNIX2003");
DEFINE_WRITEV_DATA(writev_UNIX2003, "writev$UNIX2003");
// This exists on 64 bit OS X
DEFINE_WRITEV_DATA(writev_NOCANCEL, "writev$NOCANCEL");
typedef ssize_t (*write_t)(int fd, const void *buf, size_t count);
template<FuncData &foo>
ssize_t wrap_write_temp(int fd, const void *buf, size_t count) {
AbortOnBadWrite(fd, buf, count);
write_t old_write = (write_t) foo.Buffer;
return old_write(fd, buf, count);
}
// Define a FuncData for a write-like functions.
#define DEFINE_WRITE_DATA(X, NAME) \
FuncData X ## _data = { NAME, (void*) wrap_write_temp<X ## _data> }; \
// This exists everywhere.
DEFINE_WRITE_DATA(write, "write");
// These exist on 32 bit OS X
DEFINE_WRITE_DATA(write_NOCANCEL_UNIX2003, "write$NOCANCEL$UNIX2003");
DEFINE_WRITE_DATA(write_UNIX2003, "write$UNIX2003");
// This exists on 64 bit OS X
DEFINE_WRITE_DATA(write_NOCANCEL, "write$NOCANCEL");
FuncData *Functions[] = { &aio_write_data,
&pwrite_data,
&pwrite_NOCANCEL_UNIX2003_data,
&pwrite_UNIX2003_data,
&pwrite_NOCANCEL_data,
&write_data,
&write_NOCANCEL_UNIX2003_data,
&write_UNIX2003_data,
&write_NOCANCEL_data,
&writev_data,
&writev_NOCANCEL_UNIX2003_data,
&writev_UNIX2003_data,
&writev_NOCANCEL_data};
const int NumFunctions = ArrayLength(Functions);
// We want to detect "actual" writes, not IPC. Some IPC mechanisms are
// implemented with file descriptors, so filter them out.
bool IsIPCWrite(int fd, const struct stat &buf) {
if ((buf.st_mode & S_IFMT) == S_IFIFO) {
return true;
}
if ((buf.st_mode & S_IFMT) != S_IFSOCK) {
return false;
}
sockaddr_storage address;
socklen_t len = sizeof(address);
if (getsockname(fd, (sockaddr*) &address, &len) != 0) {
return true; // Ignore the fd if we can't find what it is.
}
return address.ss_family == AF_UNIX;
}
void AbortOnBadWrite(int fd, const void *wbuf, size_t count) {
if (!PoisonWriteEnabled())
return;
// Ignore writes of zero bytes, firefox does some during shutdown.
if (count == 0)
return;
struct stat buf;
int rv = fstat(fd, &buf);
if (!ValidWriteAssert(rv == 0))
return;
if (IsIPCWrite(fd, buf))
return;
// Debugging FDs are OK
if (IsDebugFile(fd))
return;
// For writev we pass nullptr in wbuf. We should only get here from
// dbm, and it uses write, so assert that we have wbuf.
if (!ValidWriteAssert(wbuf))
return;
// As a really bad hack, accept writes that don't change the on disk
// content. This is needed because dbm doesn't keep track of dirty bits
// and can end up writing the same data to disk twice. Once when the
// user (nss) asks it to sync and once when closing the database.
ScopedFreePtr<void> wbuf2(malloc(count));
if (!ValidWriteAssert(wbuf2))
return;
off_t pos = lseek(fd, 0, SEEK_CUR);
if (!ValidWriteAssert(pos != -1))
return;
ssize_t r = read(fd, wbuf2, count);
if (!ValidWriteAssert(r == static_cast<ssize_t>(count)))
return;
int cmp = memcmp(wbuf, wbuf2, count);
if (!ValidWriteAssert(cmp == 0))
return;
off_t pos2 = lseek(fd, pos, SEEK_SET);
if (!ValidWriteAssert(pos2 == pos))
return;
}
} // anonymous namespace
namespace mozilla {
intptr_t FileDescriptorToID(int aFd) {
return aFd;
}
void PoisonWrite() {
// Quick sanity check that we don't poison twice.
static bool WritesArePoisoned = false;
MOZ_ASSERT(!WritesArePoisoned);
if (WritesArePoisoned)
return;
WritesArePoisoned = true;
if (!PoisonWriteEnabled())
return;
for (int i = 0; i < NumFunctions; ++i) {
FuncData *d = Functions[i];
if (!d->Function)
d->Function = dlsym(RTLD_DEFAULT, d->Name);
if (!d->Function)
continue;
DebugOnly<mach_error_t> t = mach_override_ptr(d->Function, d->Wrapper,
&d->Buffer);
MOZ_ASSERT(t == err_none);
}
}
}

Просмотреть файл

@ -0,0 +1,27 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 sts=4 ci et: */
/* 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 <stdio.h>
namespace mozilla {
void PoisonWrite() {
}
void DisableWritePoisoning() {
}
void EnableWritePoisoning() {
}
void InitWritePoisoning() {
}
}
extern "C" {
void MozillaRegisterDebugFD(int fd) {
}
void MozillaRegisterDebugFILE(FILE *f) {
}
void MozillaUnRegisterDebugFD(int fd) {
}
void MozillaUnRegisterDebugFILE(FILE *f) {
}
}

Просмотреть файл

@ -0,0 +1,90 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 ci et: */
/* 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 <stdio.h>
#include <windows.h>
#include <winternl.h> // NTSTATUS
#include <io.h>
#include "nsWindowsDllInterceptor.h"
#include "mozilla/mozPoisonWrite.h"
#include "mozPoisonWriteBase.h"
#include "mozilla/Assertions.h"
namespace mozilla {
intptr_t FileDescriptorToID(int aFd) {
return _get_osfhandle(aFd);
}
static WindowsDllInterceptor sNtDllInterceptor;
void AbortOnBadWrite(HANDLE);
bool ValidWriteAssert(bool ok);
typedef NTSTATUS (WINAPI* WriteFile_fn)(HANDLE, HANDLE, PIO_APC_ROUTINE,
void*, PIO_STATUS_BLOCK,
const void*, ULONG, PLARGE_INTEGER,
PULONG);
WriteFile_fn gOriginalWriteFile;
static NTSTATUS WINAPI
patched_WriteFile(HANDLE aFile, HANDLE aEvent, PIO_APC_ROUTINE aApc,
void* aApcUser, PIO_STATUS_BLOCK aIoStatus,
const void* aBuffer, ULONG aLength,
PLARGE_INTEGER aOffset, PULONG aKey)
{
AbortOnBadWrite(aFile);
return gOriginalWriteFile(aFile, aEvent, aApc, aApcUser, aIoStatus,
aBuffer, aLength, aOffset, aKey);
}
typedef NTSTATUS (WINAPI* WriteFileGather_fn)(HANDLE, HANDLE, PIO_APC_ROUTINE,
void*, PIO_STATUS_BLOCK,
FILE_SEGMENT_ELEMENT*,
ULONG, PLARGE_INTEGER, PULONG);
WriteFileGather_fn gOriginalWriteFileGather;
static NTSTATUS WINAPI
patched_WriteFileGather(HANDLE aFile, HANDLE aEvent, PIO_APC_ROUTINE aApc,
void* aApcUser, PIO_STATUS_BLOCK aIoStatus,
FILE_SEGMENT_ELEMENT* aSegments, ULONG aLength,
PLARGE_INTEGER aOffset, PULONG aKey)
{
AbortOnBadWrite(aFile);
return gOriginalWriteFileGather(aFile, aEvent, aApc, aApcUser, aIoStatus,
aSegments, aLength, aOffset, aKey);
}
void AbortOnBadWrite(HANDLE aFile)
{
if (!PoisonWriteEnabled())
return;
// Debugging files are OK.
if (IsDebugFile(reinterpret_cast<intptr_t>(aFile)))
return;
ValidWriteAssert(false);
}
void PoisonWrite() {
// Quick sanity check that we don't poison twice.
static bool WritesArePoisoned = false;
MOZ_ASSERT(!WritesArePoisoned);
if (WritesArePoisoned)
return;
WritesArePoisoned = true;
if (!PoisonWriteEnabled())
return;
sNtDllInterceptor.Init("ntdll.dll");
sNtDllInterceptor.AddHook("NtWriteFile", reinterpret_cast<intptr_t>(patched_WriteFile), reinterpret_cast<void**>(&gOriginalWriteFile));
sNtDllInterceptor.AddHook("NtWriteFileGather", reinterpret_cast<intptr_t>(patched_WriteFileGather), reinterpret_cast<void**>(&gOriginalWriteFileGather));
}
}

Просмотреть файл

@ -109,9 +109,7 @@ extern nsresult nsStringInputStreamConstructor(nsISupports *, REFNSIID, void **)
#include "nsChromeRegistry.h"
#include "nsChromeProtocolHandler.h"
#include "mozilla/IOInterposer.h"
#include "mozilla/PoisonIOInterposer.h"
#include "mozilla/LateWriteChecks.h"
#include "mozilla/mozPoisonWrite.h"
#include "mozilla/scache/StartupCache.h"
@ -684,10 +682,10 @@ ShutdownXPCOM(nsIServiceManager* servMgr)
HangMonitor::NotifyActivity();
// Late-write checks needs to find the profile directory, so it has to
// Write poisoning needs to find the profile directory, so it has to
// be initialized before mozilla::services::Shutdown or (because of
// xpcshell tests replacing the service) modules being unloaded.
mozilla::InitLateWriteChecks();
InitWritePoisoning();
// We save the "xpcom-shutdown-loaders" observers to notify after
// the observerservice is gone.
@ -755,15 +753,7 @@ ShutdownXPCOM(nsIServiceManager* servMgr)
PROFILER_MARKER("Shutdown xpcom");
// If we are doing any shutdown checks, poison writes.
if (gShutdownChecks != SCM_NOTHING) {
// Calling InitIOInterposer or InitPoisonIOInterposer twice doesn't
// cause any problems, they'll safely abort the initialization on their
// own initiative.
mozilla::IOInterposer::Init();
mozilla::InitPoisonIOInterposer();
#ifdef XP_MACOSX
mozilla::OnlyReportDirtyWrites();
#endif /* XP_MACOSX */
mozilla::BeginLateWriteChecks();
mozilla::PoisonWrite();
}
// Shutdown nsLocalFile string conversion

Просмотреть файл

@ -453,7 +453,7 @@ XRE_API(void,
XRE_InitOmnijar, (nsIFile* greOmni,
nsIFile* appOmni))
XRE_API(void,
XRE_StopLateWriteChecks, (void))
XRE_DisableWritePoisoning, (void))
#ifdef XP_WIN
/**