Backed out 15 changesets (bug 1479960, bug 1426526, bug 1534780, bug 1536697) for toolchain bustages on UniquePtrExtensions.h . CLOSED TREE

Backed out changeset a8518ea4b594 (bug 1479960)
Backed out changeset 7172762c4b87 (bug 1536697)
Backed out changeset 2ea5ccb8f3a1 (bug 1426526)
Backed out changeset d892a888fe9c (bug 1426526)
Backed out changeset 2c4d12bdfec3 (bug 1479960)
Backed out changeset 8a322064cf6d (bug 1479960)
Backed out changeset 47d387b6cd4a (bug 1479960)
Backed out changeset 8332565a6943 (bug 1479960)
Backed out changeset 9d7f1835f96f (bug 1479960)
Backed out changeset 0aa8af4965c5 (bug 1479960)
Backed out changeset 036809330a51 (bug 1479960)
Backed out changeset 39e18373e3d3 (bug 1479960)
Backed out changeset 6c2b995a9d30 (bug 1479960)
Backed out changeset 3c2b31744645 (bug 1534780)
Backed out changeset 26bb00a94d5d (bug 1534780)
This commit is contained in:
Narcis Beleuzu 2019-06-28 22:42:00 +03:00
parent 60ba27adf8
commit a89b67e772
35 changed files with 1523 additions and 679 deletions

View File

@ -262,7 +262,7 @@ add_task(async function() {
});
}
let tmpPath = expandWhitelistPath("TmpD:").toLowerCase();
let tmpPath = expandWhitelistPath(MAC ? "TmpD:" : "/dev/shm").toLowerCase();
let shouldPass = true;
for (let procName in processes) {
let whitelist = processes[procName];
@ -311,22 +311,15 @@ add_task(async function() {
continue;
}
if (!WIN && filename == "/dev/urandom") {
continue;
}
if (!WIN) {
if (filename == "/dev/urandom") {
continue;
}
// /dev/shm is always tmpfs (a memory filesystem); this isn't
// really I/O any more than mmap/munmap are.
if (LINUX && filename.startsWith("/dev/shm/")) {
continue;
}
// Shared memory uses temporary files on MacOS <= 10.11 to avoid
// a kernel security bug that will never be patched (see
// https://crbug.com/project-zero/1671 for details). This can
// be removed when we no longer support those OS versions.
if (MAC && filename.startsWith(tmpPath + "/org.mozilla.ipc.")) {
continue;
// Ignore I/O due to IPC. This doesn't really touch the disk.
if (filename.startsWith(tmpPath + "/org.chromium.")) {
continue;
}
}
let expected = false;

View File

@ -802,7 +802,7 @@ add_task(async function() {
});
}
let tmpPath = expandWhitelistPath("TmpD:").toLowerCase();
let tmpPath = expandWhitelistPath(MAC ? "TmpD:" : "/dev/shm").toLowerCase();
let shouldPass = true;
for (let phase in phases) {
let whitelist = startupPhases[phase];
@ -832,22 +832,15 @@ add_task(async function() {
continue;
}
if (!WIN && filename == "/dev/urandom") {
continue;
}
if (!WIN) {
if (filename == "/dev/urandom") {
continue;
}
// /dev/shm is always tmpfs (a memory filesystem); this isn't
// really I/O any more than mmap/munmap are.
if (LINUX && filename.startsWith("/dev/shm/")) {
continue;
}
// Shared memory uses temporary files on MacOS <= 10.11 to avoid
// a kernel security bug that will never be patched (see
// https://crbug.com/project-zero/1671 for details). This can
// be removed when we no longer support those OS versions.
if (MAC && filename.startsWith(tmpPath + "/org.mozilla.ipc.")) {
continue;
// Ignore I/O due to IPC. This doesn't really touch the disk.
if (filename.startsWith(tmpPath + "/org.chromium.")) {
continue;
}
}
let expected = false;

View File

@ -6,41 +6,109 @@
#include "MemMapSnapshot.h"
#include "mozilla/AutoMemMap.h"
#include "base/eintr_wrapper.h"
#include "base/file_util.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "nsIFile.h"
#ifdef XP_UNIX
# include <sys/stat.h>
#endif
namespace mozilla {
using loader::AutoMemMap;
namespace ipc {
Result<Ok, nsresult> MemMapSnapshot::Init(size_t aSize) {
MOZ_ASSERT(!mInitialized);
if (NS_WARN_IF(!mMem.CreateFreezeable(aSize))) {
return Err(NS_ERROR_FAILURE);
}
if (NS_WARN_IF(!mMem.Map(aSize))) {
return Err(NS_ERROR_FAILURE);
}
MOZ_TRY(Create(aSize));
mInitialized = true;
return Ok();
}
Result<Ok, nsresult> MemMapSnapshot::Finalize(loader::AutoMemMap& aMem) {
Result<Ok, nsresult> MemMapSnapshot::Finalize(AutoMemMap& aMem) {
MOZ_ASSERT(mInitialized);
if (NS_WARN_IF(!mMem.Freeze())) {
return Err(NS_ERROR_FAILURE);
}
// TakeHandle resets mMem, so call max_size first.
size_t size = mMem.max_size();
FileDescriptor memHandle(mMem.TakeHandle());
MOZ_TRY(aMem.initWithHandle(memHandle, size));
MOZ_TRY(Freeze(aMem));
mInitialized = false;
return Ok();
}
#if defined(XP_WIN)
Result<Ok, nsresult> MemMapSnapshot::Create(size_t aSize) {
HANDLE handle = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr,
PAGE_READWRITE, 0, DWORD(aSize), nullptr);
if (!handle) {
return Err(NS_ERROR_FAILURE);
}
mFile.emplace(handle);
return mMem.initWithHandle(mFile.ref(), aSize, PR_PROT_READWRITE);
}
Result<Ok, nsresult> MemMapSnapshot::Freeze(AutoMemMap& aMem) {
auto orig = mFile.ref().ClonePlatformHandle();
mFile.reset();
HANDLE handle;
if (!::DuplicateHandle(
GetCurrentProcess(), orig.release(), GetCurrentProcess(), &handle,
GENERIC_READ | FILE_MAP_READ, false, DUPLICATE_CLOSE_SOURCE)) {
return Err(NS_ERROR_FAILURE);
}
return aMem.initWithHandle(FileDescriptor(handle), mMem.size());
}
#elif defined(XP_UNIX)
Result<Ok, nsresult> MemMapSnapshot::Create(size_t aSize) {
FilePath path;
ScopedCloseFile fd(file_util::CreateAndOpenTemporaryShmemFile(&path));
if (!fd) {
return Err(NS_ERROR_FAILURE);
}
if (HANDLE_EINTR(ftruncate(fileno(fd), aSize)) != 0) {
return Err(NS_ERROR_FAILURE);
}
MOZ_TRY(mMem.init(FILEToFileDescriptor(fd), PR_PROT_READWRITE));
mPath.Assign(path.value().data(), path.value().length());
return Ok();
}
Result<Ok, nsresult> MemMapSnapshot::Freeze(AutoMemMap& aMem) {
// Delete the shm file after we're done here, whether we succeed or not. The
// open file descriptor will keep it alive until all remaining references
// are closed, at which point it will be automatically freed.
auto cleanup = MakeScopeExit([&]() { PR_Delete(mPath.get()); });
// Make the shm file readonly. This doesn't make a difference in practice,
// since we open and share a read-only file descriptor, and then delete the
// file. But it doesn't hurt, either.
chmod(mPath.get(), 0400);
nsCOMPtr<nsIFile> file;
MOZ_TRY(NS_NewNativeLocalFile(mPath, /* followLinks = */ false,
getter_AddRefs(file)));
return aMem.init(file);
}
#else
# error "Unsupported build configuration"
#endif
} // namespace ipc
} // namespace mozilla

View File

@ -7,17 +7,16 @@
#ifndef dom_ipc_MemMapSnapshot_h
#define dom_ipc_MemMapSnapshot_h
#include "mozilla/AutoMemMap.h"
#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"
#include "mozilla/RangedPtr.h"
#include "mozilla/Result.h"
#include "base/shared_memory.h"
#ifdef XP_WIN
# include "mozilla/ipc/FileDescriptor.h"
#endif
namespace mozilla {
namespace loader {
class AutoMemMap;
}
namespace ipc {
/**
@ -36,15 +35,25 @@ class MOZ_RAII MemMapSnapshot {
Result<Ok, nsresult> Init(size_t aSize);
Result<Ok, nsresult> Finalize(loader::AutoMemMap& aMap);
template <typename T>
template <typename T = void>
RangedPtr<T> Get() {
MOZ_ASSERT(mInitialized);
return {static_cast<T*>(mMem.memory()), mMem.max_size() / sizeof(T)};
return mMem.get<T>();
}
private:
base::SharedMemory mMem;
Result<Ok, nsresult> Create(size_t aSize);
Result<Ok, nsresult> Freeze(loader::AutoMemMap& aMem);
loader::AutoMemMap mMem;
bool mInitialized = false;
#ifdef XP_WIN
Maybe<FileDescriptor> mFile;
#else
nsCString mPath;
#endif
};
} // namespace ipc

View File

@ -21,8 +21,6 @@ using namespace ipc;
namespace dom {
namespace ipc {
static constexpr uint32_t kSharedStringMapMagic = 0x9e3779b9;
static inline size_t GetAlignmentOffset(size_t aOffset, size_t aAlign) {
auto mod = aOffset % aAlign;
return mod ? aAlign - mod : 0;
@ -32,7 +30,6 @@ SharedStringMap::SharedStringMap(const FileDescriptor& aMapFile,
size_t aMapSize) {
auto result = mMap.initWithHandle(aMapFile, aMapSize);
MOZ_RELEASE_ASSERT(result.isOk());
MOZ_RELEASE_ASSERT(GetHeader().mMagic == kSharedStringMapMagic);
// We return literal nsStrings and nsCStrings pointing to the mapped data,
// which means that we may still have references to the mapped data even
// after this instance is destroyed. That means that we need to keep the
@ -43,7 +40,6 @@ SharedStringMap::SharedStringMap(const FileDescriptor& aMapFile,
SharedStringMap::SharedStringMap(SharedStringMapBuilder&& aBuilder) {
auto result = aBuilder.Finalize(mMap);
MOZ_RELEASE_ASSERT(result.isOk());
MOZ_RELEASE_ASSERT(GetHeader().mMagic == kSharedStringMapMagic);
mMap.setPersistent();
}
@ -96,7 +92,7 @@ Result<Ok, nsresult> SharedStringMapBuilder::Finalize(
}
keys.Sort();
Header header = {kSharedStringMapMagic, uint32_t(keys.Length())};
Header header = {uint32_t(keys.Length())};
size_t offset = sizeof(header);
offset += GetAlignmentOffset(offset, alignof(Header));

View File

@ -59,7 +59,6 @@ class SharedStringMap {
* encoded as character (*not* byte) offsets into this region.
*/
struct Header {
uint32_t mMagic;
// The number of entries in this map.
uint32_t mEntryCount;

View File

@ -22,7 +22,11 @@ nsresult SharedDIB::Create(uint32_t aSize) {
return NS_OK;
}
bool SharedDIB::IsValid() { return mShMem && mShMem->IsValid(); }
bool SharedDIB::IsValid() {
if (!mShMem) return false;
return base::SharedMemory::IsHandleValid(mShMem->handle());
}
nsresult SharedDIB::Close() {
delete mShMem;

View File

@ -100,9 +100,8 @@ nsresult SharedDIBWin::SetupSurface(HDC aHdc, BITMAPV4HEADER* aHdr) {
if (!mSharedHdc) return NS_ERROR_FAILURE;
mSharedBmp =
::CreateDIBSection(mSharedHdc, (BITMAPINFO*)aHdr, DIB_RGB_COLORS,
&mBitmapBits, mShMem->GetHandle(), kHeaderBytes);
mSharedBmp = ::CreateDIBSection(mSharedHdc, (BITMAPINFO*)aHdr, DIB_RGB_COLORS,
&mBitmapBits, mShMem->handle(), kHeaderBytes);
if (!mSharedBmp) return NS_ERROR_FAILURE;
mOldObj = SelectObject(mSharedHdc, mSharedBmp);

View File

@ -39,6 +39,7 @@
#include "mozilla/Unused.h"
#include "base/eintr_wrapper.h"
#include "base/file_util.h"
#include <locale.h>

View File

@ -11,6 +11,7 @@ UNIFIED_SOURCES += [
'src/base/at_exit.cc',
'src/base/command_line.cc',
'src/base/file_path.cc',
'src/base/file_util.cc',
'src/base/histogram.cc',
'src/base/logging.cc',
'src/base/message_loop.cc',
@ -34,6 +35,7 @@ UNIFIED_SOURCES += [
if os_win:
SOURCES += [
'src/base/condition_variable_win.cc',
'src/base/file_util_win.cc',
'src/base/lock_impl_win.cc',
'src/base/message_pump_win.cc',
'src/base/object_watcher.cc',
@ -55,6 +57,7 @@ elif not CONFIG['MOZ_SYSTEM_LIBEVENT']:
if os_posix:
UNIFIED_SOURCES += [
'src/base/condition_variable_posix.cc',
'src/base/file_util_posix.cc',
'src/base/lock_impl_posix.cc',
'src/base/message_pump_libevent.cc',
'src/base/platform_thread_posix.cc',
@ -71,6 +74,7 @@ if os_posix:
if os_macosx:
UNIFIED_SOURCES += [
'src/base/chrome_application_mac.mm',
'src/base/file_util_mac.mm',
'src/base/mac_util.mm',
'src/base/message_pump_mac.mm',
'src/base/process_util_mac.mm',

View File

@ -0,0 +1,234 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2009 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.
#include "base/file_util.h"
#if defined(OS_WIN)
# include <io.h>
#endif
#include <stdio.h>
#if defined(ANDROID) || defined(OS_POSIX)
# include <unistd.h>
#endif
#include <fstream>
#include "base/file_path.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "base/string_piece.h"
#include "base/sys_string_conversions.h"
namespace {
const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
} // namespace
namespace file_util {
bool EndsWithSeparator(const FilePath& path) {
FilePath::StringType value = path.value();
if (value.empty()) return false;
return FilePath::IsSeparator(value[value.size() - 1]);
}
void TrimTrailingSeparator(std::wstring* dir) {
while (dir->length() > 1 && EndsWithSeparator(dir))
dir->resize(dir->length() - 1);
}
FilePath::StringType GetFileExtensionFromPath(const FilePath& path) {
FilePath::StringType file_name = path.BaseName().value();
const FilePath::StringType::size_type last_dot =
file_name.rfind(kExtensionSeparator);
return FilePath::StringType(last_dot == FilePath::StringType::npos
? FILE_PATH_LITERAL("")
: file_name,
last_dot + 1);
}
void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
FilePath::StringType& value =
const_cast<FilePath::StringType&>(path->value());
const FilePath::StringType::size_type last_dot =
value.rfind(kExtensionSeparator);
const FilePath::StringType::size_type last_separator =
value.find_last_of(FilePath::StringType(FilePath::kSeparators));
if (last_dot == FilePath::StringType::npos ||
(last_separator != std::wstring::npos && last_dot < last_separator)) {
// The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
// We should just append the suffix to the entire path.
value.append(suffix);
return;
}
value.insert(last_dot, suffix);
}
void ReplaceExtension(FilePath* path, const FilePath::StringType& extension) {
FilePath::StringType clean_extension;
// If the new extension is "" or ".", then we will just remove the current
// extension.
if (!extension.empty() &&
extension != FilePath::StringType(&kExtensionSeparator, 1)) {
if (extension[0] != kExtensionSeparator)
clean_extension.append(&kExtensionSeparator, 1);
clean_extension.append(extension);
}
FilePath::StringType& value =
const_cast<FilePath::StringType&>(path->value());
const FilePath::StringType::size_type last_dot =
value.rfind(kExtensionSeparator);
const FilePath::StringType::size_type last_separator =
value.find_last_of(FilePath::StringType(FilePath::kSeparators));
// Erase the current extension, if any.
if ((last_dot > last_separator ||
last_separator == FilePath::StringType::npos) &&
last_dot != FilePath::StringType::npos)
value.erase(last_dot);
value.append(clean_extension);
}
FILE* CreateAndOpenTemporaryFile(FilePath* path) {
FilePath directory;
if (!GetTempDir(&directory)) return NULL;
return CreateAndOpenTemporaryFileInDir(directory, path);
}
bool GetFileSize(const FilePath& file_path, int64_t* file_size) {
FileInfo info;
if (!GetFileInfo(file_path, &info)) return false;
*file_size = info.size;
return true;
}
bool CloseFile(FILE* file) {
if (file == NULL) return true;
return fclose(file) == 0;
}
// Deprecated functions ----------------------------------------------------
bool AbsolutePath(std::wstring* path_str) {
FilePath path(FilePath::FromWStringHack(*path_str));
if (!AbsolutePath(&path)) return false;
*path_str = path.ToWStringHack();
return true;
}
void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
if (!path) {
NOTREACHED();
return; // Don't crash in this function in release builds.
}
if (!EndsWithSeparator(path)) path->push_back(FilePath::kSeparators[0]);
path->append(new_ending);
}
bool CopyFile(const std::wstring& from_path, const std::wstring& to_path) {
return CopyFile(FilePath::FromWStringHack(from_path),
FilePath::FromWStringHack(to_path));
}
bool CreateDirectory(const std::wstring& full_path) {
return CreateDirectory(FilePath::FromWStringHack(full_path));
}
bool CreateNewTempDirectory(const std::wstring& prefix,
std::wstring* new_temp_path) {
#if defined(OS_WIN)
FilePath::StringType dir_prefix(prefix);
#elif defined(OS_POSIX)
FilePath::StringType dir_prefix = WideToUTF8(prefix);
#endif
FilePath temp_path;
if (!CreateNewTempDirectory(dir_prefix, &temp_path)) return false;
*new_temp_path = temp_path.ToWStringHack();
return true;
}
bool CreateTemporaryFileName(std::wstring* temp_file) {
FilePath temp_file_path;
if (!CreateTemporaryFileName(&temp_file_path)) return false;
*temp_file = temp_file_path.ToWStringHack();
return true;
}
bool Delete(const std::wstring& path) {
return Delete(FilePath::FromWStringHack(path));
}
bool DirectoryExists(const std::wstring& path) {
return DirectoryExists(FilePath::FromWStringHack(path));
}
bool EndsWithSeparator(std::wstring* path) {
return EndsWithSeparator(FilePath::FromWStringHack(*path));
}
bool EndsWithSeparator(const std::wstring& path) {
return EndsWithSeparator(FilePath::FromWStringHack(path));
}
bool GetCurrentDirectory(std::wstring* path_str) {
FilePath path;
if (!GetCurrentDirectory(&path)) return false;
*path_str = path.ToWStringHack();
return true;
}
std::wstring GetFileExtensionFromPath(const std::wstring& path) {
FilePath::StringType extension =
GetFileExtensionFromPath(FilePath::FromWStringHack(path));
#if defined(OS_WIN)
return extension;
#elif defined(OS_POSIX)
return UTF8ToWide(extension);
#endif
}
bool GetFileInfo(const std::wstring& file_path, FileInfo* results) {
return GetFileInfo(FilePath::FromWStringHack(file_path), results);
}
std::wstring GetFilenameFromPath(const std::wstring& path) {
if (path.empty() || EndsWithSeparator(path)) return std::wstring();
return FilePath::FromWStringHack(path).BaseName().ToWStringHack();
}
bool GetFileSize(const std::wstring& file_path, int64_t* file_size) {
return GetFileSize(FilePath::FromWStringHack(file_path), file_size);
}
bool GetTempDir(std::wstring* path_str) {
FilePath path;
if (!GetTempDir(&path)) return false;
*path_str = path.ToWStringHack();
return true;
}
FILE* OpenFile(const std::wstring& filename, const char* mode) {
return OpenFile(FilePath::FromWStringHack(filename), mode);
}
bool PathExists(const std::wstring& path) {
return PathExists(FilePath::FromWStringHack(path));
}
bool PathIsWritable(const std::wstring& path) {
return PathIsWritable(FilePath::FromWStringHack(path));
}
int ReadFile(const std::wstring& filename, char* data, int size) {
return ReadFile(FilePath::FromWStringHack(filename), data, size);
}
bool SetCurrentDirectory(const std::wstring& directory) {
return SetCurrentDirectory(FilePath::FromWStringHack(directory));
}
void UpOneDirectory(std::wstring* dir) {
FilePath path = FilePath::FromWStringHack(*dir);
FilePath directory = path.DirName();
// If there is no separator, we will get back kCurrentDirectory.
// In this case don't change |dir|.
if (directory.value() != FilePath::kCurrentDirectory)
*dir = directory.ToWStringHack();
}
int WriteFile(const std::wstring& filename, const char* data, int size) {
return WriteFile(FilePath::FromWStringHack(filename), data, size);
}
} // namespace file_util

View File

@ -0,0 +1,229 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 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.
// This file contains utility functions for dealing with the local
// filesystem.
#ifndef BASE_FILE_UTIL_H_
#define BASE_FILE_UTIL_H_
#include "build/build_config.h"
#if defined(OS_WIN)
# include <windows.h>
#elif defined(ANDROID)
# include <sys/stat.h>
#elif defined(OS_POSIX)
# include <sys/types.h>
# include <sys/stat.h>
#endif
#include <stdio.h>
#include <stack>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/file_path.h"
namespace file_util {
//-----------------------------------------------------------------------------
// Functions that operate purely on a path string w/o touching the filesystem:
// Returns true if the given path ends with a path separator character.
bool EndsWithSeparator(const FilePath& path);
// These two versions are both deprecated. TODO(estade): remove them.
bool EndsWithSeparator(std::wstring* path);
bool EndsWithSeparator(const std::wstring& path);
// Modifies a string by trimming all trailing separators from the end.
// Deprecated. FilePath does this automatically, and if it's constructed from a
// path with a trailing separator, StripTrailingSeparators() may be used.
void TrimTrailingSeparator(std::wstring* dir);
// Strips the topmost directory from the end of 'dir'. Assumes 'dir' does not
// refer to a file.
// If 'dir' is a root directory, return without change.
// Deprecated. Use FilePath::DirName instead.
void UpOneDirectory(std::wstring* dir);
// Returns the filename portion of 'path', without any leading \'s or /'s.
// Deprecated. Use FilePath::BaseName instead.
std::wstring GetFilenameFromPath(const std::wstring& path);
// Deprecated compatibility function. Use FilePath::Extension.
FilePath::StringType GetFileExtensionFromPath(const FilePath& path);
// Deprecated temporary compatibility function.
std::wstring GetFileExtensionFromPath(const std::wstring& path);
// Appends new_ending to path, adding a separator between the two if necessary.
void AppendToPath(std::wstring* path, const std::wstring& new_ending);
// Convert provided relative path into an absolute path. Returns false on
// error. On POSIX, this function fails if the path does not exist.
bool AbsolutePath(FilePath* path);
// Deprecated temporary compatibility function.
bool AbsolutePath(std::wstring* path);
// Deprecated compatibility function. Use FilePath::InsertBeforeExtension.
void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix);
// Deprecated compatibility function. Use FilePath::ReplaceExtension.
void ReplaceExtension(FilePath* file_name,
const FilePath::StringType& extension);
#if defined(OS_WIN)
// Deprecated temporary compatibility functions.
void InsertBeforeExtension(std::wstring* path, const std::wstring& suffix);
void ReplaceExtension(std::wstring* file_name, const std::wstring& extension);
#endif
//-----------------------------------------------------------------------------
// Functions that involve filesystem access or modification:
// Deletes the given path, whether it's a file or a directory.
// If it's a directory, it's perfectly happy to delete all of the
// directory's contents.
// Returns true if successful, false otherwise.
bool Delete(const FilePath& path);
// Deprecated temporary compatibility function.
bool Delete(const std::wstring& path);
// Copies a single file. Use CopyDirectory to copy directories.
bool CopyFile(const FilePath& from_path, const FilePath& to_path);
// Deprecated temporary compatibility function.
bool CopyFile(const std::wstring& from_path, const std::wstring& to_path);
// Returns true if the given path exists on the local filesystem,
// false otherwise.
bool PathExists(const FilePath& path);
// Deprecated temporary compatibility function.
bool PathExists(const std::wstring& path);
// Returns true if the given path is writable by the user, false otherwise.
bool PathIsWritable(const FilePath& path);
// Deprecated temporary compatibility function.
bool PathIsWritable(const std::wstring& path);
// Returns true if the given path exists and is a directory, false otherwise.
bool DirectoryExists(const FilePath& path);
// Deprecated temporary compatibility function.
bool DirectoryExists(const std::wstring& path);
#if defined(OS_POSIX)
// Read exactly |bytes| bytes from file descriptor |fd|, storing the result
// in |buffer|. This function is protected against EINTR and partial reads.
// Returns true iff |bytes| bytes have been successfuly read from |fd|.
bool ReadFromFD(int fd, char* buffer, size_t bytes);
#endif // defined(OS_POSIX)
// Get the temporary directory provided by the system.
bool GetTempDir(FilePath* path);
// Deprecated temporary compatibility function.
bool GetTempDir(std::wstring* path);
// Get a temporary directory for shared memory files.
// Only useful on POSIX; redirects to GetTempDir() on Windows.
bool GetShmemTempDir(FilePath* path);
// Creates a temporary file. The full path is placed in |path|, and the
// function returns true if was successful in creating the file. The file will
// be empty and all handles closed after this function returns.
// TODO(erikkay): rename this function and track down all of the callers.
// (Clarification of erik's comment: the intent is to rename the BlahFileName()
// calls into BlahFile(), since they create temp files (not temp filenames).)
bool CreateTemporaryFileName(FilePath* path);
// Deprecated temporary compatibility function.
bool CreateTemporaryFileName(std::wstring* temp_file);
// Create and open a temporary file. File is opened for read/write.
// The full path is placed in |path|, and the function returns true if
// was successful in creating and opening the file.
FILE* CreateAndOpenTemporaryFile(FilePath* path);
// Like above but for shmem files. Only useful for POSIX.
FILE* CreateAndOpenTemporaryShmemFile(FilePath* path);
// Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|.
FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path);
// Same as CreateTemporaryFileName but the file is created in |dir|.
bool CreateTemporaryFileNameInDir(const std::wstring& dir,
std::wstring* temp_file);
// Create a new directory under TempPath. If prefix is provided, the new
// directory name is in the format of prefixyyyy.
// NOTE: prefix is ignored in the POSIX implementation.
// TODO(erikkay): is this OK?
// If success, return true and output the full path of the directory created.
bool CreateNewTempDirectory(const FilePath::StringType& prefix,
FilePath* new_temp_path);
// Deprecated temporary compatibility function.
bool CreateNewTempDirectory(const std::wstring& prefix,
std::wstring* new_temp_path);
// Creates a directory, as well as creating any parent directories, if they
// don't exist. Returns 'true' on successful creation, or if the directory
// already exists.
bool CreateDirectory(const FilePath& full_path);
// Deprecated temporary compatibility function.
bool CreateDirectory(const std::wstring& full_path);
// Returns the file size. Returns true on success.
bool GetFileSize(const FilePath& file_path, int64_t* file_size);
// Deprecated temporary compatibility function.
bool GetFileSize(const std::wstring& file_path, int64_t* file_size);
// Used to hold information about a given file path. See GetFileInfo below.
struct FileInfo {
// The size of the file in bytes. Undefined when is_directory is true.
int64_t size;
// True if the file corresponds to a directory.
bool is_directory;
// Add additional fields here as needed.
};
// Returns information about the given file path.
bool GetFileInfo(const FilePath& file_path, FileInfo* info);
// Deprecated temporary compatibility function.
bool GetFileInfo(const std::wstring& file_path, FileInfo* info);
// Wrapper for fopen-like calls. Returns non-NULL FILE* on success.
FILE* OpenFile(const FilePath& filename, const char* mode);
// Deprecated temporary compatibility functions.
FILE* OpenFile(const std::string& filename, const char* mode);
FILE* OpenFile(const std::wstring& filename, const char* mode);
// Closes file opened by OpenFile. Returns true on success.
bool CloseFile(FILE* file);
// Reads the given number of bytes from the file into the buffer. Returns
// the number of read bytes, or -1 on error.
int ReadFile(const FilePath& filename, char* data, int size);
// Deprecated temporary compatibility function.
int ReadFile(const std::wstring& filename, char* data, int size);
// Writes the given buffer into the file, overwriting any data that was
// previously there. Returns the number of bytes written, or -1 on error.
int WriteFile(const FilePath& filename, const char* data, int size);
// Deprecated temporary compatibility function.
int WriteFile(const std::wstring& filename, const char* data, int size);
// Gets the current working directory for the process.
bool GetCurrentDirectory(FilePath* path);
// Deprecated temporary compatibility function.
bool GetCurrentDirectory(std::wstring* path);
// Sets the current working directory for the process.
bool SetCurrentDirectory(const FilePath& path);
// Deprecated temporary compatibility function.
bool SetCurrentDirectory(const std::wstring& current_directory);
} // namespace file_util
#endif // BASE_FILE_UTIL_H_

View File

@ -0,0 +1,31 @@
// Copyright (c) 2006-2008 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.
#include "base/file_util.h"
#import <Cocoa/Cocoa.h>
#include <copyfile.h>
#include "base/file_path.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "base/scoped_nsautorelease_pool.h"
namespace file_util {
bool GetTempDir(FilePath* path) {
base::ScopedNSAutoreleasePool autorelease_pool;
NSString* tmp = NSTemporaryDirectory();
if (tmp == nil) return false;
*path = FilePath([tmp fileSystemRepresentation]);
return true;
}
bool GetShmemTempDir(FilePath* path) { return GetTempDir(path); }
bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
return (copyfile(from_path.value().c_str(), to_path.value().c_str(), NULL, COPYFILE_ALL) == 0);
}
} // namespace

View File

@ -0,0 +1,316 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 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.
#include "base/file_util.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <libgen.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#define _DARWIN_USE_64_BIT_INODE // Use 64-bit inode data structures
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <fstream>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/eintr_wrapper.h"
#include "base/file_path.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "base/time.h"
namespace file_util {
#if defined(GOOGLE_CHROME_BUILD)
static const char* kTempFileName = "com.google.chrome.XXXXXX";
#else
static const char* kTempFileName = "org.chromium.XXXXXX";
#endif
bool AbsolutePath(FilePath* path) {
char full_path[PATH_MAX];
if (realpath(path->value().c_str(), full_path) == NULL) return false;
*path = FilePath(full_path);
return true;
}
// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
// which works both with and without the recursive flag. I'm not sure we need
// that functionality. If not, remove from file_util_win.cc, otherwise add it
// here.
bool Delete(const FilePath& path) {
const char* path_str = path.value().c_str();
struct stat file_info;
int test = stat(path_str, &file_info);
if (test != 0) {
// The Windows version defines this condition as success.
bool ret = (errno == ENOENT || errno == ENOTDIR);
return ret;
}
if (!S_ISDIR(file_info.st_mode)) return (unlink(path_str) == 0);
return (rmdir(path_str) == 0);
}
bool PathExists(const FilePath& path) {
struct stat file_info;
return (stat(path.value().c_str(), &file_info) == 0);
}
bool PathIsWritable(const FilePath& path) {
FilePath test_path(path);
struct stat file_info;
if (stat(test_path.value().c_str(), &file_info) != 0) {
// If the path doesn't exist, test the parent dir.
test_path = test_path.DirName();
// If the parent dir doesn't exist, then return false (the path is not
// directly writable).
if (stat(test_path.value().c_str(), &file_info) != 0) return false;
}
if (S_IWOTH & file_info.st_mode) return true;
if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode))
return true;
if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode))
return true;
return false;
}
bool DirectoryExists(const FilePath& path) {
struct stat file_info;
if (stat(path.value().c_str(), &file_info) == 0)
return S_ISDIR(file_info.st_mode);
return false;
}
bool ReadFromFD(int fd, char* buffer, size_t bytes) {
size_t total_read = 0;
while (total_read < bytes) {
ssize_t bytes_read =
HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
if (bytes_read <= 0) break;
total_read += bytes_read;
}
return total_read == bytes;
}
// Creates and opens a temporary file in |directory|, returning the
// file descriptor. |path| is set to the temporary file path.
// Note TODO(erikkay) comment in header for BlahFileName() calls; the
// intent is to rename these files BlahFile() (since they create
// files, not filenames). This function does NOT unlink() the file.
int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
*path = directory.Append(kTempFileName);
const std::string& tmpdir_string = path->value();
// this should be OK since mkstemp just replaces characters in place
char* buffer = const_cast<char*>(tmpdir_string.c_str());
return mkstemp(buffer);
}
bool CreateTemporaryFileName(FilePath* path) {
FilePath directory;
if (!GetTempDir(&directory)) return false;
int fd = CreateAndOpenFdForTemporaryFile(directory, path);
if (fd < 0) return false;
close(fd);
return true;
}
FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
FilePath directory;
if (!GetShmemTempDir(&directory)) return NULL;
return CreateAndOpenTemporaryFileInDir(directory, path);
}
FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
int fd = CreateAndOpenFdForTemporaryFile(dir, path);
if (fd < 0) return NULL;
return fdopen(fd, "a+");
}
bool CreateTemporaryFileNameInDir(const std::wstring& dir,
std::wstring* temp_file) {
// Not implemented yet.
NOTREACHED();
return false;
}
bool CreateNewTempDirectory(const FilePath::StringType& prefix,
FilePath* new_temp_path) {
FilePath tmpdir;
if (!GetTempDir(&tmpdir)) return false;
tmpdir = tmpdir.Append(kTempFileName);
std::string tmpdir_string = tmpdir.value();
#ifdef ANDROID
char* dtemp = NULL;
#else
// this should be OK since mkdtemp just replaces characters in place
char* buffer = const_cast<char*>(tmpdir_string.c_str());
char* dtemp = mkdtemp(buffer);
#endif
if (!dtemp) return false;
*new_temp_path = FilePath(dtemp);
return true;
}
bool CreateDirectory(const FilePath& full_path) {
std::vector<FilePath> subpaths;
// Collect a list of all parent directories.
FilePath last_path = full_path;
subpaths.push_back(full_path);
for (FilePath path = full_path.DirName(); path.value() != last_path.value();
path = path.DirName()) {
subpaths.push_back(path);
last_path = path;
}
// Iterate through the parents and create the missing ones.
for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
i != subpaths.rend(); ++i) {
if (!DirectoryExists(*i)) {
if (mkdir(i->value().c_str(), 0777) != 0) return false;
}
}
return true;
}
bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
struct stat file_info;
if (stat(file_path.value().c_str(), &file_info) != 0) return false;
results->is_directory = S_ISDIR(file_info.st_mode);
results->size = file_info.st_size;
return true;
}
FILE* OpenFile(const std::string& filename, const char* mode) {
return OpenFile(FilePath(filename), mode);
}
FILE* OpenFile(const FilePath& filename, const char* mode) {
return fopen(filename.value().c_str(), mode);
}
int ReadFile(const FilePath& filename, char* data, int size) {
int fd = open(filename.value().c_str(), O_RDONLY);
if (fd < 0) return -1;
int ret_value = HANDLE_EINTR(read(fd, data, size));
IGNORE_EINTR(close(fd));
return ret_value;
}
int WriteFile(const FilePath& filename, const char* data, int size) {
int fd = creat(filename.value().c_str(), 0666);
if (fd < 0) return -1;
// Allow for partial writes
ssize_t bytes_written_total = 0;
do {
ssize_t bytes_written_partial = HANDLE_EINTR(
write(fd, data + bytes_written_total, size - bytes_written_total));
if (bytes_written_partial < 0) {
IGNORE_EINTR(close(fd));
return -1;
}
bytes_written_total += bytes_written_partial;
} while (bytes_written_total < size);
IGNORE_EINTR(close(fd));
return bytes_written_total;
}
// Gets the current working directory for the process.
bool GetCurrentDirectory(FilePath* dir) {
char system_buffer[PATH_MAX] = "";
if (!getcwd(system_buffer, sizeof(system_buffer))) {
NOTREACHED();
return false;
}
*dir = FilePath(system_buffer);
return true;
}
// Sets the current working directory for the process.
bool SetCurrentDirectory(const FilePath& path) {
int ret = chdir(path.value().c_str());
return !ret;
}
#if !defined(OS_MACOSX)
bool GetTempDir(FilePath* path) {
const char* tmp = getenv("TMPDIR");
if (tmp)
*path = FilePath(tmp);
else
*path = FilePath("/tmp");
return true;
}
bool GetShmemTempDir(FilePath* path) {
# if defined(OS_LINUX) && !defined(ANDROID)
*path = FilePath("/dev/shm");
return true;
# else
return GetTempDir(path);
# endif
}
bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
int infile = open(from_path.value().c_str(), O_RDONLY);
if (infile < 0) return false;
int outfile = creat(to_path.value().c_str(), 0666);
if (outfile < 0) {
close(infile);
return false;
}
const size_t kBufferSize = 32768;
std::vector<char> buffer(kBufferSize);
bool result = true;
while (result) {
ssize_t bytes_read = HANDLE_EINTR(read(infile, &buffer[0], buffer.size()));
if (bytes_read < 0) {
result = false;
break;
}
if (bytes_read == 0) break;
// Allow for partial writes
ssize_t bytes_written_per_read = 0;
do {
ssize_t bytes_written_partial =
HANDLE_EINTR(write(outfile, &buffer[bytes_written_per_read],
bytes_read - bytes_written_per_read));
if (bytes_written_partial < 0) {
result = false;
break;
}
bytes_written_per_read += bytes_written_partial;
} while (bytes_written_per_read < bytes_read);
}
if (IGNORE_EINTR(close(infile)) < 0) result = false;
if (IGNORE_EINTR(close(outfile)) < 0) result = false;
return result;
}
#endif // !defined(OS_MACOSX)
} // namespace file_util

View File

@ -0,0 +1,330 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 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.
#include "base/file_util.h"
#include <windows.h>
#include <shellapi.h>
#include <shlobj.h>
#include <time.h>
#include <string>
#include "base/file_path.h"
#include "base/logging.h"
#include "base/scoped_handle.h"
#include "base/string_util.h"
#include "base/time.h"
#include "base/win_util.h"
namespace file_util {
bool AbsolutePath(FilePath* path) {
wchar_t file_path_buf[MAX_PATH];
if (!_wfullpath(file_path_buf, path->value().c_str(), MAX_PATH)) return false;
*path = FilePath(file_path_buf);
return true;
}
bool Delete(const FilePath& path) {
if (path.value().length() >= MAX_PATH) return false;
// Use DeleteFile; it should be faster. DeleteFile
// fails if passed a directory though, which is why we fall through on
// failure to the SHFileOperation.
if (DeleteFile(path.value().c_str()) != 0) return true;
// SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
// so we have to use wcscpy because wcscpy_s writes non-NULLs
// into the rest of the buffer.
wchar_t double_terminated_path[MAX_PATH + 1] = {0};
#pragma warning(suppress : 4996) // don't complain about wcscpy deprecation
wcscpy(double_terminated_path, path.value().c_str());
SHFILEOPSTRUCT file_operation = {0};
file_operation.wFunc = FO_DELETE;
file_operation.pFrom = double_terminated_path;
file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION;
file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
int err = SHFileOperation(&file_operation);
// Some versions of Windows return ERROR_FILE_NOT_FOUND when
// deleting an empty directory.
return (err == 0 || err == ERROR_FILE_NOT_FOUND);
}
bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
// NOTE: I suspect we could support longer paths, but that would involve
// analyzing all our usage of files.
if (from_path.value().length() >= MAX_PATH ||
to_path.value().length() >= MAX_PATH) {
return false;
}
return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(),
false) != 0);
}
bool ShellCopy(const FilePath& from_path, const FilePath& to_path,
bool recursive) {
// NOTE: I suspect we could support longer paths, but that would involve
// analyzing all our usage of files.
if (from_path.value().length() >= MAX_PATH ||
to_path.value().length() >= MAX_PATH) {
return false;
}
// SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
// so we have to use wcscpy because wcscpy_s writes non-NULLs
// into the rest of the buffer.
wchar_t double_terminated_path_from[MAX_PATH + 1] = {0};
wchar_t double_terminated_path_to[MAX_PATH + 1] = {0};
#pragma warning(suppress : 4996) // don't complain about wcscpy deprecation
wcscpy(double_terminated_path_from, from_path.value().c_str());
#pragma warning(suppress : 4996) // don't complain about wcscpy deprecation
wcscpy(double_terminated_path_to, to_path.value().c_str());
SHFILEOPSTRUCT file_operation = {0};
file_operation.wFunc = FO_COPY;
file_operation.pFrom = double_terminated_path_from;
file_operation.pTo = double_terminated_path_to;
file_operation.fFlags =
FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR;
if (!recursive) file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
return (SHFileOperation(&file_operation) == 0);
}
bool PathExists(const FilePath& path) {
return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES);
}
bool PathIsWritable(const FilePath& path) {
HANDLE dir =
CreateFile(path.value().c_str(), FILE_ADD_FILE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (dir == INVALID_HANDLE_VALUE) return false;
CloseHandle(dir);
return true;
}
bool DirectoryExists(const FilePath& path) {
DWORD fileattr = GetFileAttributes(path.value().c_str());
if (fileattr != INVALID_FILE_ATTRIBUTES)
return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
return false;
}
bool GetTempDir(FilePath* path) {
wchar_t temp_path[MAX_PATH + 1];
DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
if (path_len >= MAX_PATH || path_len <= 0) return false;
// TODO(evanm): the old behavior of this function was to always strip the
// trailing slash. We duplicate this here, but it shouldn't be necessary
// when everyone is using the appropriate FilePath APIs.
std::wstring path_str(temp_path);
TrimTrailingSeparator(&path_str);
*path = FilePath(path_str);
return true;
}
bool GetShmemTempDir(FilePath* path) { return GetTempDir(path); }
bool CreateTemporaryFileName(FilePath* path) {
std::wstring temp_path, temp_file;
if (!GetTempDir(&temp_path)) return false;
if (CreateTemporaryFileNameInDir(temp_path, &temp_file)) {
*path = FilePath(temp_file);
return true;
}
return false;
}
FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
return CreateAndOpenTemporaryFile(path);
}
// On POSIX we have semantics to create and open a temporary file
// atomically.
// TODO(jrg): is there equivalent call to use on Windows instead of
// going 2-step?
FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
std::wstring wstring_path;
if (!CreateTemporaryFileNameInDir(dir.value(), &wstring_path)) {
return NULL;
}
*path = FilePath(wstring_path);
// Open file in binary mode, to avoid problems with fwrite. On Windows
// it replaces \n's with \r\n's, which may surprise you.
// Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx
return OpenFile(*path, "wb+");
}
bool CreateTemporaryFileNameInDir(const std::wstring& dir,
std::wstring* temp_file) {
wchar_t temp_name[MAX_PATH + 1];
if (!GetTempFileName(dir.c_str(), L"", 0, temp_name)) return false; // fail!
DWORD path_len = GetLongPathName(temp_name, temp_name, MAX_PATH);
if (path_len > MAX_PATH + 1 || path_len == 0) return false; // fail!
temp_file->assign(temp_name, path_len);
return true;
}
bool CreateNewTempDirectory(const FilePath::StringType& prefix,
FilePath* new_temp_path) {
FilePath system_temp_dir;
if (!GetTempDir(&system_temp_dir)) return false;
FilePath path_to_create;
srand(static_cast<uint32_t>(time(NULL)));
int count = 0;
while (count < 50) {
// Try create a new temporary directory with random generated name. If
// the one exists, keep trying another path name until we reach some limit.
path_to_create = system_temp_dir;
std::wstring new_dir_name;
new_dir_name.assign(prefix);
new_dir_name.append(IntToWString(rand() % kint16max));
path_to_create = path_to_create.Append(new_dir_name);
if (::CreateDirectory(path_to_create.value().c_str(), NULL)) break;
count++;
}
if (count == 50) {
return false;
}
*new_temp_path = path_to_create;
return true;
}
bool CreateDirectory(const FilePath& full_path) {
if (DirectoryExists(full_path)) return true;
int err = SHCreateDirectoryEx(NULL, full_path.value().c_str(), NULL);
return err == ERROR_SUCCESS;
}
bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
WIN32_FILE_ATTRIBUTE_DATA attr;
if (!GetFileAttributesEx(file_path.ToWStringHack().c_str(),
GetFileExInfoStandard, &attr)) {
return false;
}
ULARGE_INTEGER size;
size.HighPart = attr.nFileSizeHigh;
size.LowPart = attr.nFileSizeLow;
results->size = size.QuadPart;
results->is_directory =
(attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
return true;
}
FILE* OpenFile(const FilePath& filename, const char* mode) {
std::wstring w_mode = ASCIIToWide(std::string(mode));
FILE* file;
if (_wfopen_s(&file, filename.value().c_str(), w_mode.c_str()) != 0) {
return NULL;
}
return file;
}
FILE* OpenFile(const std::string& filename, const char* mode) {
FILE* file;
if (fopen_s(&file, filename.c_str(), mode) != 0) {
return NULL;
}
return file;
}
int ReadFile(const FilePath& filename, char* data, int size) {
ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL));
if (file == INVALID_HANDLE_VALUE) return -1;
int ret_value;
DWORD read;
if (::ReadFile(file, data, size, &read, NULL) && read == size) {
ret_value = static_cast<int>(read);
} else {
ret_value = -1;
}
return ret_value;
}
int WriteFile(const FilePath& filename, const char* data, int size) {
ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, 0, NULL));
if (file == INVALID_HANDLE_VALUE) {
CHROMIUM_LOG(WARNING) << "CreateFile failed for path " << filename.value()
<< " error code=" << GetLastError()
<< " error text=" << win_util::FormatLastWin32Error();
return -1;
}
DWORD written;
BOOL result = ::WriteFile(file, data, size, &written, NULL);
if (result && written == size) return static_cast<int>(written);
if (!result) {
// WriteFile failed.
CHROMIUM_LOG(WARNING) << "writing file " << filename.value()
<< " failed, error code=" << GetLastError()
<< " description="
<< win_util::FormatLastWin32Error();
} else {
// Didn't write all the bytes.
CHROMIUM_LOG(WARNING) << "wrote" << written << " bytes to "
<< filename.value() << " expected " << size;
}
return -1;
}
// Gets the current working directory for the process.
bool GetCurrentDirectory(FilePath* dir) {
wchar_t system_buffer[MAX_PATH];
system_buffer[0] = 0;
DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer);
if (len == 0 || len > MAX_PATH) return false;
// TODO(evanm): the old behavior of this function was to always strip the
// trailing slash. We duplicate this here, but it shouldn't be necessary
// when everyone is using the appropriate FilePath APIs.
std::wstring dir_str(system_buffer);
file_util::TrimTrailingSeparator(&dir_str);
*dir = FilePath(dir_str);
return true;
}
// Sets the current working directory for the process.
bool SetCurrentDirectory(const FilePath& directory) {
BOOL ret = ::SetCurrentDirectory(directory.value().c_str());
return ret != 0;
}
// Deprecated functions ----------------------------------------------------
void InsertBeforeExtension(std::wstring* path_str, const std::wstring& suffix) {
FilePath path(*path_str);
InsertBeforeExtension(&path, suffix);
path_str->assign(path.value());
}
void ReplaceExtension(std::wstring* file_name, const std::wstring& extension) {
FilePath path(*file_name);
ReplaceExtension(&path, extension);
file_name->assign(path.value());
}
} // namespace file_util

View File

@ -18,8 +18,6 @@
#include "base/basictypes.h"
#include "base/process.h"
#include "mozilla/Attributes.h"
#include "mozilla/UniquePtrExtensions.h"
namespace base {
@ -59,21 +57,12 @@ class SharedMemory {
// invalid value; NULL for a HANDLE and -1 for a file descriptor)
static bool IsHandleValid(const SharedMemoryHandle& handle);
// IsHandleValid applied to this object's handle.
bool IsValid() const;
// Return invalid handle (see comment above for exact definition).
static SharedMemoryHandle NULLHandle();
// Creates a shared memory segment.
// Returns true on success, false on failure.
bool Create(size_t size) { return CreateInternal(size, false); }
// Creates a shared memory segment that supports the Freeze()
// method; see below. (Warning: creating freezeable shared memory
// within a sandboxed process isn't possible on some platforms.)
// Returns true on success, false on failure.
bool CreateFreezeable(size_t size) { return CreateInternal(size, true); }
bool Create(size_t size);
// Maps the shared memory into the caller's address space.
// Returns true on success, false otherwise. The memory address
@ -100,29 +89,10 @@ class SharedMemory {
// Mapped via Map(). Returns NULL if it is not mapped.
void* memory() const { return memory_; }
// Extracts the underlying file handle; similar to
// GiveToProcess(GetCurrentProcId(), ...) but returns a RAII type.
// Like GiveToProcess, this unmaps the memory as a side-effect.
mozilla::UniqueFileHandle TakeHandle();
#ifdef OS_WIN
// Used only in gfx/ipc/SharedDIBWin.cpp; should be removable once
// NPAPI goes away.
HANDLE GetHandle() {
freezeable_ = false;
return mapped_file_;
}
#endif
// Make the shared memory object read-only, such that it cannot be
// written even if it's sent to an untrusted process. If it was
// mapped in this process, it will be unmapped. The object must
// have been created with CreateFreezeable(), and must not have
// already been shared to another process.
//
// (See bug 1479960 comment #0 for OS-specific implementation
// details.)
MOZ_MUST_USE bool Freeze();
// Get access to the underlying OS handle for this segment.
// Use of this handle for anything other than an opaque
// identifier is not portable.
SharedMemoryHandle handle() const;
// Closes the open shared memory segment.
// It is safe to call Close repeatedly.
@ -170,8 +140,6 @@ class SharedMemory {
bool ShareToProcessCommon(ProcessId target_pid,
SharedMemoryHandle* new_handle, bool close_self);
bool CreateInternal(size_t size, bool freezeable);
#if defined(OS_WIN)
// If true indicates this came from an external source so needs extra checks
// before being mapped.
@ -179,12 +147,9 @@ class SharedMemory {
HANDLE mapped_file_;
#elif defined(OS_POSIX)
int mapped_file_;
int frozen_file_;
size_t mapped_size_;
#endif
void* memory_;
bool read_only_;
bool freezeable_;
size_t max_size_;
DISALLOW_EVIL_CONSTRUCTORS(SharedMemory);

View File

@ -20,19 +20,12 @@
#include "base/logging.h"
#include "base/string_util.h"
#include "mozilla/Atomics.h"
#include "mozilla/UniquePtrExtensions.h"
#include "prenv.h"
namespace base {
SharedMemory::SharedMemory()
: mapped_file_(-1),
frozen_file_(-1),
mapped_size_(0),
memory_(nullptr),
read_only_(false),
freezeable_(false),
max_size_(0) {}
: mapped_file_(-1), memory_(NULL), read_only_(false), max_size_(0) {}
SharedMemory::SharedMemory(SharedMemory&& other) {
if (this == &other) {
@ -40,16 +33,11 @@ SharedMemory::SharedMemory(SharedMemory&& other) {
}
mapped_file_ = other.mapped_file_;
mapped_size_ = other.mapped_size_;
frozen_file_ = other.frozen_file_;
memory_ = other.memory_;
read_only_ = other.read_only_;
freezeable_ = other.freezeable_;
max_size_ = other.max_size_;
other.mapped_file_ = -1;
other.mapped_size_ = 0;
other.frozen_file_ = -1;
other.memory_ = nullptr;
}
@ -57,9 +45,7 @@ SharedMemory::~SharedMemory() { Close(); }
bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) {
DCHECK(mapped_file_ == -1);
DCHECK(frozen_file_ == -1);
freezeable_ = false;
mapped_file_ = handle.fd;
read_only_ = read_only;
return true;
@ -70,97 +56,12 @@ bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
return handle.fd >= 0;
}
bool SharedMemory::IsValid() const { return mapped_file_ >= 0; }
// static
SharedMemoryHandle SharedMemory::NULLHandle() { return SharedMemoryHandle(); }
// Workaround for CVE-2018-4435 (https://crbug.com/project-zero/1671);
// can be removed when minimum OS version is at least 10.12.
#ifdef OS_MACOSX
static const char* GetTmpDir() {
static const char* const kTmpDir = [] {
const char* tmpdir = PR_GetEnv("TMPDIR");
if (tmpdir) {
return tmpdir;
}
return "/tmp";
}();
return kTmpDir;
}
static int FakeShmOpen(const char* name, int oflag, int mode) {
CHECK(name[0] == '/');
std::string path(GetTmpDir());
path += name;
return open(path.c_str(), oflag | O_CLOEXEC | O_NOCTTY, mode);
}
static int FakeShmUnlink(const char* name) {
CHECK(name[0] == '/');
std::string path(GetTmpDir());
path += name;
return unlink(path.c_str());
}
static bool IsShmOpenSecure() {
static const bool kIsSecure = [] {
mozilla::UniqueFileHandle rwfd, rofd;
std::string name;
CHECK(SharedMemory::AppendPosixShmPrefix(&name, getpid()));
name += "sectest";
// The prefix includes the pid and this will be called at most
// once per process, so no need for a counter.
rwfd.reset(
HANDLE_EINTR(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600)));
// An adversary could steal the name. Handle this semi-gracefully.
DCHECK(rwfd);
if (!rwfd) {
return false;
}
rofd.reset(shm_open(name.c_str(), O_RDONLY, 0400));
CHECK(rofd);
CHECK(shm_unlink(name.c_str()) == 0);
CHECK(ftruncate(rwfd.get(), 1) == 0);
rwfd = nullptr;
void* map = mmap(nullptr, 1, PROT_READ, MAP_SHARED, rofd.get(), 0);
CHECK(map != MAP_FAILED);
bool ok = mprotect(map, 1, PROT_READ | PROT_WRITE) != 0;
munmap(map, 1);
return ok;
}();
return kIsSecure;
}
static int SafeShmOpen(bool freezeable, const char* name, int oflag, int mode) {
if (!freezeable || IsShmOpenSecure()) {
return shm_open(name, oflag, mode);
} else {
return FakeShmOpen(name, oflag, mode);
}
}
static int SafeShmUnlink(bool freezeable, const char* name) {
if (!freezeable || IsShmOpenSecure()) {
return shm_unlink(name);
} else {
return FakeShmUnlink(name);
}
}
#elif !defined(ANDROID)
static int SafeShmOpen(bool freezeable, const char* name, int oflag, int mode) {
return shm_open(name, oflag, mode);
}
static int SafeShmUnlink(bool freezeable, const char* name) {
return shm_unlink(name);
}
#endif
// static
bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) {
#if defined(ANDROID)
#if defined(ANDROID) || defined(SHM_ANON)
return false;
#else
*str += '/';
@ -186,32 +87,35 @@ bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) {
// enough for this.
StringAppendF(str, "org.mozilla.ipc.%d.", static_cast<int>(pid));
return true;
#endif // !ANDROID
#endif // !ANDROID && !SHM_ANON
}
bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
bool SharedMemory::Create(size_t size) {
read_only_ = false;
DCHECK(size > 0);
DCHECK(mapped_file_ == -1);
DCHECK(frozen_file_ == -1);
mozilla::UniqueFileHandle fd;
mozilla::UniqueFileHandle frozen_fd;
int fd;
bool needs_truncate = true;
#ifdef ANDROID
// Android has its own shared memory facility:
fd.reset(open("/" ASHMEM_NAME_DEF, O_RDWR, 0600));
if (!fd) {
fd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600);
if (fd < 0) {
CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
return false;
}
if (ioctl(fd.get(), ASHMEM_SET_SIZE, size) != 0) {
if (ioctl(fd, ASHMEM_SET_SIZE, size) != 0) {
CHROMIUM_LOG(WARNING) << "failed to set shm size: " << strerror(errno);
close(fd);
return false;
}
needs_truncate = false;
#elif defined(SHM_ANON)
// FreeBSD (or any other Unix that might decide to implement this
// nice, simple API):
fd = shm_open(SHM_ANON, O_RDWR, 0600);
#else
// Generic Unix: shm_open + shm_unlink
do {
@ -222,104 +126,69 @@ bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
CHECK(AppendPosixShmPrefix(&name, getpid()));
StringAppendF(&name, "%zu", sNameCounter++);
// O_EXCL means the names being predictable shouldn't be a problem.
fd.reset(HANDLE_EINTR(SafeShmOpen(freezeable, name.c_str(),
O_RDWR | O_CREAT | O_EXCL, 0600)));
if (fd) {
if (freezeable) {
frozen_fd.reset(HANDLE_EINTR(
SafeShmOpen(freezeable, name.c_str(), O_RDONLY, 0400)));
if (!frozen_fd) {
int open_err = errno;
SafeShmUnlink(freezeable, name.c_str());
DLOG(FATAL) << "failed to re-open freezeable shm: "
<< strerror(open_err);
return false;
}
}
if (SafeShmUnlink(freezeable, name.c_str()) != 0) {
fd = HANDLE_EINTR(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600));
if (fd >= 0) {
if (shm_unlink(name.c_str()) != 0) {
// This shouldn't happen, but if it does: assume the file is
// in fact leaked, and bail out now while it's still 0-length.
DLOG(FATAL) << "failed to unlink shm: " << strerror(errno);
return false;
}
}
} while (!fd && errno == EEXIST);
} while (fd < 0 && errno == EEXIST);
#endif
if (!fd) {
if (fd < 0) {
CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
return false;
}
if (needs_truncate) {
if (HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(size))) != 0) {
if (HANDLE_EINTR(ftruncate(fd, static_cast<off_t>(size))) != 0) {
CHROMIUM_LOG(WARNING) << "failed to set shm size: " << strerror(errno);
close(fd);
return false;
}
}
mapped_file_ = fd.release();
frozen_file_ = frozen_fd.release();
mapped_file_ = fd;
max_size_ = size;
freezeable_ = freezeable;
return true;
}
bool SharedMemory::Freeze() {
DCHECK(!read_only_);
CHECK(freezeable_);
Unmap();
#ifdef ANDROID
if (ioctl(mapped_file_, ASHMEM_SET_PROT_MASK, PROT_READ) != 0) {
CHROMIUM_LOG(WARNING) << "failed to freeze shm: " << strerror(errno);
return false;
}
#else
DCHECK(frozen_file_ >= 0);
DCHECK(mapped_file_ >= 0);
close(mapped_file_);
mapped_file_ = frozen_file_;
frozen_file_ = -1;
#endif
read_only_ = true;
freezeable_ = false;
return true;
}
bool SharedMemory::Map(size_t bytes, void* fixed_address) {
if (mapped_file_ == -1) return false;
DCHECK(!memory_);
// Don't use MAP_FIXED when a fixed_address was specified, since that can
// replace pages that are alread mapped at that address.
void* mem =
memory_ =
mmap(fixed_address, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
MAP_SHARED, mapped_file_, 0);
if (mem == MAP_FAILED) {
CHROMIUM_LOG(WARNING) << "Call to mmap failed: " << strerror(errno);
return false;
bool mmap_succeeded = memory_ != MAP_FAILED;
DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno;
if (mmap_succeeded) {
if (fixed_address && memory_ != fixed_address) {
bool munmap_succeeded = munmap(memory_, bytes) == 0;
DCHECK(munmap_succeeded) << "Call to munmap failed, errno=" << errno;
memory_ = NULL;
return false;
}
max_size_ = bytes;
}
if (fixed_address && mem != fixed_address) {
bool munmap_succeeded = munmap(mem, bytes) == 0;
DCHECK(munmap_succeeded) << "Call to munmap failed, errno=" << errno;
return false;
}
memory_ = mem;
mapped_size_ = bytes;
return true;
return mmap_succeeded;
}
bool SharedMemory::Unmap() {
if (memory_ == NULL) return false;
munmap(memory_, mapped_size_);
munmap(memory_, max_size_);
memory_ = NULL;
mapped_size_ = 0;
max_size_ = 0;
return true;
}
@ -333,7 +202,6 @@ void* SharedMemory::FindFreeAddressSpace(size_t size) {
bool SharedMemory::ShareToProcessCommon(ProcessId processId,
SharedMemoryHandle* new_handle,
bool close_self) {
freezeable_ = false;
const int new_fd = dup(mapped_file_);
DCHECK(new_fd >= -1);
new_handle->fd = new_fd;
@ -353,20 +221,10 @@ void SharedMemory::Close(bool unmap_view) {
close(mapped_file_);
mapped_file_ = -1;
}
if (frozen_file_ >= 0) {
CHROMIUM_LOG(WARNING) << "freezeable shared memory was never frozen";
close(frozen_file_);
frozen_file_ = -1;
}
}
mozilla::UniqueFileHandle SharedMemory::TakeHandle() {
mozilla::UniqueFileHandle fh(mapped_file_);
mapped_file_ = -1;
// Now that the main fd is removed, reset everything else: close the
// frozen fd if present and unmap the memory if mapped.
Close();
return fh;
SharedMemoryHandle SharedMemory::handle() const {
return FileDescriptor(mapped_file_, false);
}
} // namespace base

View File

@ -59,7 +59,6 @@ SharedMemory::SharedMemory()
mapped_file_(NULL),
memory_(NULL),
read_only_(false),
freezeable_(false),
max_size_(0) {}
SharedMemory::SharedMemory(SharedMemory&& other) {
@ -71,7 +70,6 @@ SharedMemory::SharedMemory(SharedMemory&& other) {
memory_ = other.memory_;
read_only_ = other.read_only_;
max_size_ = other.max_size_;
freezeable_ = other.freezeable_;
external_section_ = other.external_section_;
other.mapped_file_ = nullptr;
@ -87,7 +85,6 @@ bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) {
DCHECK(mapped_file_ == NULL);
external_section_ = true;
freezeable_ = false; // just in case
mapped_file_ = handle;
read_only_ = read_only;
return true;
@ -98,12 +95,10 @@ bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
return handle != NULL;
}
bool SharedMemory::IsValid() const { return mapped_file_ != NULL; }
// static
SharedMemoryHandle SharedMemory::NULLHandle() { return NULL; }
bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
bool SharedMemory::Create(size_t size) {
DCHECK(mapped_file_ == NULL);
read_only_ = false;
mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
@ -111,23 +106,6 @@ bool SharedMemory::CreateInternal(size_t size, bool freezeable) {
if (!mapped_file_) return false;
max_size_ = size;
freezeable_ = freezeable;
return true;
}
bool SharedMemory::Freeze() {
DCHECK(!read_only_);
CHECK(freezeable_);
Unmap();
if (!::DuplicateHandle(GetCurrentProcess(), mapped_file_, GetCurrentProcess(),
&mapped_file_, GENERIC_READ | FILE_MAP_READ, false,
DUPLICATE_CLOSE_SOURCE)) {
return false;
}
read_only_ = true;
freezeable_ = false;
return true;
}
@ -168,7 +146,6 @@ void* SharedMemory::FindFreeAddressSpace(size_t size) {
bool SharedMemory::ShareToProcessCommon(ProcessId processId,
SharedMemoryHandle* new_handle,
bool close_self) {
freezeable_ = false;
*new_handle = 0;
DWORD access = FILE_MAP_READ | SECTION_QUERY;
DWORD options = 0;
@ -207,11 +184,6 @@ void SharedMemory::Close(bool unmap_view) {
}
}
mozilla::UniqueFileHandle SharedMemory::TakeHandle() {
mozilla::UniqueFileHandle fh(mapped_file_);
mapped_file_ = NULL;
Unmap();
return fh;
}
SharedMemoryHandle SharedMemory::handle() const { return mapped_file_; }
} // namespace base

View File

@ -7,74 +7,104 @@
#include "FileDescriptor.h"
#include "mozilla/Assertions.h"
#include "mozilla/TypeTraits.h"
#include "mozilla/Move.h"
#include "nsDebug.h"
#ifdef XP_WIN
# include <windows.h>
# include "ProtocolUtils.h"
# define INVALID_HANDLE INVALID_HANDLE_VALUE
#else // XP_WIN
# include <unistd.h>
# ifndef OS_POSIX
# define OS_POSIX
# endif
# include "base/eintr_wrapper.h"
# define INVALID_HANDLE -1
#endif // XP_WIN
namespace mozilla {
namespace ipc {
FileDescriptor::FileDescriptor() = default;
FileDescriptor::FileDescriptor() : mHandle(INVALID_HANDLE) {}
FileDescriptor::FileDescriptor(const FileDescriptor& aOther)
: mHandle(Clone(aOther.mHandle.get())) {}
: mHandle(INVALID_HANDLE) {
Assign(aOther);
}
FileDescriptor::FileDescriptor(FileDescriptor&& aOther)
: mHandle(std::move(aOther.mHandle)) {}
: mHandle(INVALID_HANDLE) {
*this = std::move(aOther);
}
FileDescriptor::FileDescriptor(PlatformHandleType aHandle)
: mHandle(Clone(aHandle)) {}
: mHandle(INVALID_HANDLE) {
mHandle = Clone(aHandle);
}
FileDescriptor::FileDescriptor(UniquePlatformHandle&& aHandle)
: mHandle(std::move(aHandle)) {}
FileDescriptor::FileDescriptor(const IPDLPrivate&, const PickleType& aPickle) {
FileDescriptor::FileDescriptor(const IPDLPrivate&, const PickleType& aPickle)
: mHandle(INVALID_HANDLE) {
#ifdef XP_WIN
mHandle.reset(aPickle);
mHandle = aPickle;
#else
mHandle.reset(aPickle.fd);
mHandle = aPickle.fd;
#endif
}
FileDescriptor::~FileDescriptor() = default;
FileDescriptor::~FileDescriptor() { Close(); }
FileDescriptor& FileDescriptor::operator=(const FileDescriptor& aOther) {
if (this != &aOther) {
mHandle = Clone(aOther.mHandle.get());
Assign(aOther);
}
return *this;
}
FileDescriptor& FileDescriptor::operator=(FileDescriptor&& aOther) {
if (this != &aOther) {
mHandle = std::move(aOther.mHandle);
Close();
mHandle = aOther.mHandle;
aOther.mHandle = INVALID_HANDLE;
}
return *this;
}
bool FileDescriptor::IsValid() const { return IsValid(mHandle); }
void FileDescriptor::Assign(const FileDescriptor& aOther) {
Close();
mHandle = Clone(aOther.mHandle);
}
void FileDescriptor::Close() {
Close(mHandle);
mHandle = INVALID_HANDLE;
}
FileDescriptor::PickleType FileDescriptor::ShareTo(
const FileDescriptor::IPDLPrivate&,
FileDescriptor::ProcessId aTargetPid) const {
PlatformHandleType newHandle;
#ifdef XP_WIN
if (IsValid()) {
if (mozilla::ipc::DuplicateHandle(mHandle.get(), aTargetPid, &newHandle, 0,
if (mozilla::ipc::DuplicateHandle(mHandle, aTargetPid, &newHandle, 0,
DUPLICATE_SAME_ACCESS)) {
return newHandle;
}
NS_WARNING("Failed to duplicate file handle for other process!");
}
return INVALID_HANDLE_VALUE;
return INVALID_HANDLE;
#else // XP_WIN
if (IsValid()) {
newHandle = dup(mHandle.get());
if (newHandle >= 0) {
newHandle = dup(mHandle);
if (IsValid(newHandle)) {
return base::FileDescriptor(newHandle, /* auto_close */ true);
}
NS_WARNING("Failed to duplicate file handle for other process!");
@ -85,15 +115,9 @@ FileDescriptor::PickleType FileDescriptor::ShareTo(
MOZ_CRASH("Must not get here!");
}
bool FileDescriptor::IsValid() const { return mHandle != nullptr; }
FileDescriptor::UniquePlatformHandle FileDescriptor::ClonePlatformHandle()
const {
return Clone(mHandle.get());
}
FileDescriptor::UniquePlatformHandle FileDescriptor::TakePlatformHandle() {
return UniquePlatformHandle(mHandle.release());
return UniquePlatformHandle(Clone(mHandle));
}
bool FileDescriptor::operator==(const FileDescriptor& aOther) const {
@ -101,29 +125,67 @@ bool FileDescriptor::operator==(const FileDescriptor& aOther) const {
}
// static
FileDescriptor::UniquePlatformHandle FileDescriptor::Clone(
PlatformHandleType aHandle) {
FileDescriptor::PlatformHandleType newHandle;
bool FileDescriptor::IsValid(PlatformHandleType aHandle) {
return aHandle != INVALID_HANDLE;
}
#ifdef XP_WIN
if (aHandle == INVALID_HANDLE_VALUE) {
return UniqueFileHandle();
// static
FileDescriptor::PlatformHandleType FileDescriptor::Clone(
PlatformHandleType aHandle) {
if (!IsValid(aHandle)) {
return INVALID_HANDLE;
}
FileDescriptor::PlatformHandleType newHandle;
#ifdef XP_WIN
if (::DuplicateHandle(GetCurrentProcess(), aHandle, GetCurrentProcess(),
&newHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
return UniqueFileHandle(newHandle);
}
#else // XP_WIN
if (aHandle < 0) {
return UniqueFileHandle();
}
newHandle = dup(aHandle);
if (newHandle >= 0) {
return UniqueFileHandle(newHandle);
}
if ((newHandle = dup(aHandle)) != INVALID_HANDLE) {
#endif
return newHandle;
}
NS_WARNING("Failed to duplicate file handle for current process!");
return UniqueFileHandle();
return INVALID_HANDLE;
}
// static
void FileDescriptor::Close(PlatformHandleType aHandle) {
if (IsValid(aHandle)) {
#ifdef XP_WIN
if (!CloseHandle(aHandle)) {
NS_WARNING("Failed to close file handle for current process!");
}
#else // XP_WIN
IGNORE_EINTR(close(aHandle));
#endif
}
}
FileDescriptor::PlatformHandleHelper::PlatformHandleHelper(
FileDescriptor::PlatformHandleType aHandle)
: mHandle(aHandle) {}
FileDescriptor::PlatformHandleHelper::PlatformHandleHelper(std::nullptr_t)
: mHandle(INVALID_HANDLE) {}
bool FileDescriptor::PlatformHandleHelper::operator!=(std::nullptr_t) const {
return mHandle != INVALID_HANDLE;
}
FileDescriptor::PlatformHandleHelper::
operator FileDescriptor::PlatformHandleType() const {
return mHandle;
}
#ifdef XP_WIN
FileDescriptor::PlatformHandleHelper::operator std::intptr_t() const {
return reinterpret_cast<std::intptr_t>(mHandle);
}
#endif
void FileDescriptor::PlatformHandleDeleter::operator()(
FileDescriptor::PlatformHandleHelper aHelper) {
FileDescriptor::Close(aHelper);
}
void IPDLParamTraits<FileDescriptor>::Write(IPC::Message* aMsg,

View File

@ -9,11 +9,15 @@
#include "base/basictypes.h"
#include "base/process.h"
#include "mozilla/UniquePtr.h"
#include "ipc/IPCMessageUtils.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/ipc/IPDLParamTraits.h"
#ifdef XP_UNIX
#ifdef XP_WIN
// Need the HANDLE typedef.
# include <winnt.h>
# include <cstdint>
#else
# include "base/file_descriptor_posix.h"
#endif
@ -36,15 +40,32 @@ class FileDescriptor {
public:
typedef base::ProcessId ProcessId;
using UniquePlatformHandle = mozilla::UniqueFileHandle;
using PlatformHandleType = UniquePlatformHandle::ElementType;
#ifdef XP_WIN
typedef PlatformHandleType PickleType;
typedef HANDLE PlatformHandleType;
typedef HANDLE PickleType;
#else
typedef int PlatformHandleType;
typedef base::FileDescriptor PickleType;
#endif
struct PlatformHandleHelper {
MOZ_IMPLICIT PlatformHandleHelper(PlatformHandleType aHandle);
MOZ_IMPLICIT PlatformHandleHelper(std::nullptr_t);
bool operator!=(std::nullptr_t) const;
operator PlatformHandleType() const;
#ifdef XP_WIN
operator std::intptr_t() const;
#endif
private:
PlatformHandleType mHandle;
};
struct PlatformHandleDeleter {
typedef PlatformHandleHelper pointer;
void operator()(PlatformHandleHelper aHelper);
};
typedef UniquePtr<PlatformHandleType, PlatformHandleDeleter>
UniquePlatformHandle;
// This should only ever be created by IPDL.
struct IPDLPrivate {};
@ -60,8 +81,6 @@ class FileDescriptor {
// The caller still have to close aHandle.
explicit FileDescriptor(PlatformHandleType aHandle);
explicit FileDescriptor(UniquePlatformHandle&& aHandle);
// This constructor WILL NOT duplicate the handle.
// FileDescriptor takes the ownership from IPC message.
FileDescriptor(const IPDLPrivate&, const PickleType& aPickle);
@ -85,17 +104,23 @@ class FileDescriptor {
// handle.
UniquePlatformHandle ClonePlatformHandle() const;
// Extracts the underlying handle and makes this object an invalid handle.
// (Compare UniquePtr::release.)
UniquePlatformHandle TakePlatformHandle();
// Only used in nsTArray.
bool operator==(const FileDescriptor& aOther) const;
private:
static UniqueFileHandle Clone(PlatformHandleType aHandle);
friend struct PlatformHandleTrait;
UniquePlatformHandle mHandle;
void Assign(const FileDescriptor& aOther);
void Close();
static bool IsValid(PlatformHandleType aHandle);
static PlatformHandleType Clone(PlatformHandleType aHandle);
static void Close(PlatformHandleType aHandle);
PlatformHandleType mHandle;
};
template <>

View File

@ -27,12 +27,17 @@ class SharedPreferenceSerializer final {
bool SerializeToSharedMemory();
base::SharedMemoryHandle GetSharedMemoryHandle() const {
return mShm.handle();
}
const FileDescriptor::UniquePlatformHandle& GetPrefMapHandle() const {
return mPrefMapHandle;
}
nsACString::size_type GetPrefLength() const { return mPrefs.Length(); }
size_t GetPrefMapSize() const { return mPrefMapSize; }
size_t GetPrefsLength() const { return mPrefsLength; }
const UniqueFileHandle& GetPrefsHandle() const { return mPrefsHandle; }
const UniqueFileHandle& GetPrefMapHandle() const { return mPrefMapHandle; }
void AddSharedPrefCmdLineArgs(GeckoChildProcessHost& procHost,
std::vector<std::string>& aExtraOpts) const;
@ -40,9 +45,9 @@ class SharedPreferenceSerializer final {
private:
DISALLOW_COPY_AND_ASSIGN(SharedPreferenceSerializer);
size_t mPrefMapSize;
size_t mPrefsLength;
UniqueFileHandle mPrefMapHandle;
UniqueFileHandle mPrefsHandle;
FileDescriptor::UniquePlatformHandle mPrefMapHandle;
base::SharedMemory mShm;
nsAutoCStringN<1024> mPrefs;
};
class SharedPreferenceDeserializer final {

View File

@ -8,7 +8,6 @@
#include "mozilla/Preferences.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/UniquePtrExtensions.h"
namespace mozilla {
namespace ipc {
@ -24,36 +23,32 @@ SharedPreferenceSerializer::~SharedPreferenceSerializer() {
SharedPreferenceSerializer::SharedPreferenceSerializer(
SharedPreferenceSerializer&& aOther)
: mPrefMapSize(aOther.mPrefMapSize),
mPrefsLength(aOther.mPrefsLength),
mPrefMapHandle(std::move(aOther.mPrefMapHandle)),
mPrefsHandle(std::move(aOther.mPrefsHandle)) {
mShm(std::move(aOther.mShm)),
mPrefs(std::move(aOther.mPrefs)) {
MOZ_COUNT_CTOR(SharedPreferenceSerializer);
}
bool SharedPreferenceSerializer::SerializeToSharedMemory() {
mPrefMapHandle =
Preferences::EnsureSnapshot(&mPrefMapSize).TakePlatformHandle();
Preferences::EnsureSnapshot(&mPrefMapSize).ClonePlatformHandle();
// Serialize the early prefs.
nsAutoCStringN<1024> prefs;
Preferences::SerializePreferences(prefs);
mPrefsLength = prefs.Length();
Preferences::SerializePreferences(mPrefs);
base::SharedMemory shm;
// Set up the shared memory.
if (!shm.Create(prefs.Length())) {
if (!mShm.Create(mPrefs.Length())) {
NS_ERROR("failed to create shared memory in the parent");
return false;
}
if (!shm.Map(prefs.Length())) {
if (!mShm.Map(mPrefs.Length())) {
NS_ERROR("failed to map shared memory in the parent");
return false;
}
// Copy the serialized prefs into the shared memory.
memcpy(static_cast<char*>(shm.memory()), prefs.get(), mPrefsLength);
memcpy(static_cast<char*>(mShm.memory()), mPrefs.get(), mPrefs.Length());
mPrefsHandle = shm.TakeHandle();
return true;
}
@ -69,10 +64,11 @@ void SharedPreferenceSerializer::AddSharedPrefCmdLineArgs(
#if defined(XP_WIN)
// Record the handle as to-be-shared, and pass it via a command flag. This
// works because Windows handles are system-wide.
procHost.AddHandleToShare(GetPrefsHandle().get());
HANDLE prefsHandle = GetSharedMemoryHandle();
procHost.AddHandleToShare(prefsHandle);
procHost.AddHandleToShare(GetPrefMapHandle().get());
aExtraOpts.push_back("-prefsHandle");
aExtraOpts.push_back(formatPtrArg(GetPrefsHandle().get()).get());
aExtraOpts.push_back(formatPtrArg(prefsHandle).get());
aExtraOpts.push_back("-prefMapHandle");
aExtraOpts.push_back(formatPtrArg(GetPrefMapHandle().get()).get());
#else
@ -83,13 +79,13 @@ void SharedPreferenceSerializer::AddSharedPrefCmdLineArgs(
// Note: on Android, AddFdToRemap() sets up the fd to be passed via a Parcel,
// and the fixed fd isn't used. However, we still need to mark it for
// remapping so it doesn't get closed in the child.
procHost.AddFdToRemap(GetPrefsHandle().get(), kPrefsFileDescriptor);
procHost.AddFdToRemap(GetSharedMemoryHandle().fd, kPrefsFileDescriptor);
procHost.AddFdToRemap(GetPrefMapHandle().get(), kPrefMapFileDescriptor);
#endif
// Pass the lengths via command line flags.
aExtraOpts.push_back("-prefsLen");
aExtraOpts.push_back(formatPtrArg(GetPrefsLength()).get());
aExtraOpts.push_back(formatPtrArg(GetPrefLength()).get());
aExtraOpts.push_back("-prefMapSize");
aExtraOpts.push_back(formatPtrArg(GetPrefMapSize()).get());
}
@ -137,13 +133,16 @@ bool SharedPreferenceDeserializer::DeserializeFromSharedMemory(
return false;
}
// The FileDescriptor constructor will clone this handle when constructed,
// so store it in a UniquePlatformHandle to make sure the original gets
// closed.
FileDescriptor::UniquePlatformHandle handle(
parseHandleArg(aPrefMapHandleStr));
if (!aPrefMapHandleStr || aPrefMapHandleStr[0] != '\0') {
return false;
}
mPrefMapHandle.emplace(std::move(handle));
mPrefMapHandle.emplace(handle.get());
#endif
mPrefsLen = Some(parseUIntPtrArg(aPrefsLenStr));
@ -161,12 +160,17 @@ bool SharedPreferenceDeserializer::DeserializeFromSharedMemory(
MOZ_RELEASE_ASSERT(gPrefsFd != -1);
mPrefsHandle = Some(base::FileDescriptor(gPrefsFd, /* auto_close */ true));
mPrefMapHandle.emplace(UniqueFileHandle(gPrefMapFd));
FileDescriptor::UniquePlatformHandle handle(gPrefMapFd);
mPrefMapHandle.emplace(handle.get());
#elif XP_UNIX
mPrefsHandle = Some(base::FileDescriptor(kPrefsFileDescriptor,
/* auto_close */ true));
mPrefMapHandle.emplace(UniqueFileHandle(kPrefMapFileDescriptor));
// The FileDescriptor constructor will clone this handle when constructed,
// so store it in a UniquePlatformHandle to make sure the original gets
// closed.
FileDescriptor::UniquePlatformHandle handle(kPrefMapFileDescriptor);
mPrefMapHandle.emplace(handle.get());
#endif
if (mPrefsHandle.isNothing() || mPrefsLen.isNothing() ||

View File

@ -83,8 +83,6 @@ class SharedMemory {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedMemory)
static void SystemProtect(char* aAddr, size_t aSize, int aRights);
static MOZ_MUST_USE bool SystemProtectFallible(char* aAddr, size_t aSize,
int aRights);
static size_t SystemPageSize();
static size_t PageAlignedSize(size_t aSize);

View File

@ -13,19 +13,12 @@ namespace mozilla {
namespace ipc {
void SharedMemory::SystemProtect(char* aAddr, size_t aSize, int aRights) {
if (!SystemProtectFallible(aAddr, aSize, aRights)) {
MOZ_CRASH("can't mprotect()");
}
}
bool SharedMemory::SystemProtectFallible(char* aAddr, size_t aSize,
int aRights) {
int flags = 0;
if (aRights & RightsRead) flags |= PROT_READ;
if (aRights & RightsWrite) flags |= PROT_WRITE;
if (RightsNone == aRights) flags = PROT_NONE;
return 0 == mprotect(aAddr, aSize, flags);
if (0 < mprotect(aAddr, aSize, flags)) MOZ_CRASH("can't mprotect()");
}
size_t SharedMemory::SystemPageSize() { return sysconf(_SC_PAGESIZE); }

View File

@ -12,13 +12,6 @@ namespace mozilla {
namespace ipc {
void SharedMemory::SystemProtect(char* aAddr, size_t aSize, int aRights) {
if (!SystemProtectFallible(aAddr, aSize, aRights)) {
MOZ_CRASH("can't VirtualProtect()");
}
}
bool SharedMemory::SystemProtectFallible(char* aAddr, size_t aSize,
int aRights) {
DWORD flags;
if ((aRights & RightsRead) && (aRights & RightsWrite))
flags = PAGE_READWRITE;
@ -28,7 +21,8 @@ bool SharedMemory::SystemProtectFallible(char* aAddr, size_t aSize,
flags = PAGE_NOACCESS;
DWORD oldflags;
return VirtualProtect(aAddr, aSize, flags, &oldflags);
if (!VirtualProtect(aAddr, aSize, flags, &oldflags))
MOZ_CRASH("can't VirtualProtect()");
}
size_t SharedMemory::SystemPageSize() {

View File

@ -1,109 +0,0 @@
/* -*- 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 "gtest/gtest.h"
#include "base/shared_memory.h"
#include "base/process_util.h"
#include "mozilla/ipc/SharedMemory.h"
namespace mozilla {
// Try to map a frozen shm for writing. Threat model: the process is
// compromised and then receives a frozen handle.
TEST(IPCSharedMemory, FreezeAndMapRW)
{
base::SharedMemory shm;
// Create and initialize
ASSERT_TRUE(shm.CreateFreezeable(1));
ASSERT_TRUE(shm.Map(1));
auto mem = reinterpret_cast<char*>(shm.memory());
ASSERT_TRUE(mem);
*mem = 'A';
// Freeze
ASSERT_TRUE(shm.Freeze());
ASSERT_FALSE(shm.memory());
// Re-create as writeable
auto handle = base::SharedMemory::NULLHandle();
ASSERT_TRUE(shm.GiveToProcess(base::GetCurrentProcId(), &handle));
ASSERT_TRUE(shm.IsHandleValid(handle));
ASSERT_FALSE(shm.IsValid());
ASSERT_TRUE(shm.SetHandle(handle, /* read-only */ false));
ASSERT_TRUE(shm.IsValid());
// This should fail
EXPECT_FALSE(shm.Map(1));
}
// Try to restore write permissions to a frozen mapping. Threat
// model: the process has mapped frozen shm normally and then is
// compromised, or as for FreezeAndMapRW (see also the
// proof-of-concept at https://crbug.com/project-zero/1671 ).
TEST(IPCSharedMemory, FreezeAndReprotect)
{
base::SharedMemory shm;
// Create and initialize
ASSERT_TRUE(shm.CreateFreezeable(1));
ASSERT_TRUE(shm.Map(1));
auto mem = reinterpret_cast<char*>(shm.memory());
ASSERT_TRUE(mem);
*mem = 'A';
// Freeze
ASSERT_TRUE(shm.Freeze());
ASSERT_FALSE(shm.memory());
// Re-map
ASSERT_TRUE(shm.Map(1));
mem = reinterpret_cast<char*>(shm.memory());
ASSERT_EQ(*mem, 'A');
// Try to alter protection; should fail
EXPECT_FALSE(ipc::SharedMemory::SystemProtectFallible(
mem, 1, ipc::SharedMemory::RightsReadWrite));
}
#ifndef XP_WIN
// This essentially tests whether FreezeAndReprotect would have failed
// without the freeze. It doesn't work on Windows: VirtualProtect
// can't exceed the permissions set in MapViewOfFile regardless of the
// security status of the original handle.
TEST(IPCSharedMemory, Reprotect)
{
base::SharedMemory shm;
// Create and initialize
ASSERT_TRUE(shm.CreateFreezeable(1));
ASSERT_TRUE(shm.Map(1));
auto mem = reinterpret_cast<char*>(shm.memory());
ASSERT_TRUE(mem);
*mem = 'A';
// Re-create as read-only
auto handle = base::SharedMemory::NULLHandle();
ASSERT_TRUE(shm.GiveToProcess(base::GetCurrentProcId(), &handle));
ASSERT_TRUE(shm.IsHandleValid(handle));
ASSERT_FALSE(shm.IsValid());
ASSERT_TRUE(shm.SetHandle(handle, /* read-only */ true));
ASSERT_TRUE(shm.IsValid());
// Re-map
ASSERT_TRUE(shm.Map(1));
mem = reinterpret_cast<char*>(shm.memory());
ASSERT_EQ(*mem, 'A');
// Try to alter protection; should succeed, because not frozen
EXPECT_TRUE(ipc::SharedMemory::SystemProtectFallible(
mem, 1, ipc::SharedMemory::RightsReadWrite));
}
#endif
} // namespace mozilla

View File

@ -1,15 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
Library('ipctest')
SOURCES += [
'TestSharedMemory.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul-gtest'

View File

@ -5,7 +5,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += [
'app',
'chromium',
'glue',
'ipdl',
@ -18,9 +17,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
if CONFIG['OS_ARCH'] == 'WINNT':
DIRS += ['mscom']
TEST_DIRS += [
'gtest',
]
DIRS += ['app']
with Files("**"):
BUG_COMPONENT = ("Core", "IPC")

View File

@ -39,7 +39,8 @@ Result<Ok, nsresult> AutoMemMap::init(nsIFile* file, int flags, int mode,
}
Result<Ok, nsresult> AutoMemMap::init(const FileDescriptor& file,
PRFileMapProtect prot, size_t maybeSize) {
PRFileMapProtect prot,
size_t expectedSize) {
MOZ_ASSERT(!fd);
if (!file.IsValid()) {
return Err(NS_ERROR_INVALID_ARG);
@ -53,29 +54,19 @@ Result<Ok, nsresult> AutoMemMap::init(const FileDescriptor& file,
}
Unused << handle.release();
return initInternal(prot, maybeSize);
return initInternal(prot, expectedSize);
}
Result<Ok, nsresult> AutoMemMap::initInternal(PRFileMapProtect prot,
size_t maybeSize) {
size_t expectedSize) {
MOZ_ASSERT(!fileMap);
MOZ_ASSERT(!addr);
if (maybeSize > 0) {
// Some OSes' shared memory objects can't be stat()ed, either at
// all (Android) or without loosening the sandbox (Mac) so just
// use the size.
size_ = maybeSize;
} else {
// But if we don't have the size, assume it's a regular file and
// ask for it.
PRFileInfo64 fileInfo;
MOZ_TRY(PR_GetOpenFileInfo64(fd.get(), &fileInfo));
PRFileInfo64 fileInfo;
MOZ_TRY(PR_GetOpenFileInfo64(fd.get(), &fileInfo));
if (fileInfo.size > UINT32_MAX) {
return Err(NS_ERROR_INVALID_ARG);
}
size_ = fileInfo.size;
if (fileInfo.size > UINT32_MAX) {
return Err(NS_ERROR_INVALID_ARG);
}
fileMap = PR_CreateFileMap(fd, 0, prot);
@ -83,6 +74,13 @@ Result<Ok, nsresult> AutoMemMap::initInternal(PRFileMapProtect prot,
return Err(NS_ERROR_FAILURE);
}
size_ = fileInfo.size;
// The memory region size passed in certain IPC messages isn't necessary on
// Unix-like systems, since we can always stat the file descriptor to
// determine it accurately. But since we have it, anyway, sanity check that
// it matches the size returned by the stat.
MOZ_ASSERT_IF(expectedSize > 0, size_ == expectedSize);
addr = PR_MemMap(fileMap, 0, size_);
if (!addr) {
return Err(NS_ERROR_FAILURE);
@ -124,8 +122,7 @@ FileDescriptor AutoMemMap::cloneHandle() const {
Result<Ok, nsresult> AutoMemMap::initWithHandle(const FileDescriptor& file,
size_t size,
PRFileMapProtect prot) {
MOZ_DIAGNOSTIC_ASSERT(size > 0);
return init(file, prot, size);
return init(file, prot);
}
FileDescriptor AutoMemMap::cloneHandle() const { return cloneFileDescriptor(); }

View File

@ -36,7 +36,7 @@ class AutoMemMap {
Result<Ok, nsresult> init(const FileDescriptor& file,
PRFileMapProtect prot = PR_PROT_READONLY,
size_t maybeSize = 0);
size_t expectedSize = 0);
// Initializes the mapped memory with a shared memory handle. On
// Unix-like systems, this is identical to the above init() method. On
@ -73,8 +73,8 @@ class AutoMemMap {
void setPersistent() { persistent_ = true; }
private:
Result<Ok, nsresult> initInternal(PRFileMapProtect prot,
size_t maybeSize = 0);
Result<Ok, nsresult> initInternal(PRFileMapProtect prot = PR_PROT_READONLY,
size_t expectedSize = 0);
AutoFDClose fd;
PRFileMap* fileMap = nullptr;

View File

@ -1,35 +0,0 @@
/* -*- 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 "UniquePtrExtensions.h"
#include "mozilla/Assertions.h"
#include "mozilla/DebugOnly.h"
#ifdef XP_WIN
# include <windows.h>
#else
# include <errno.h>
# include <unistd.h>
#endif
namespace mozilla {
namespace detail {
void FileHandleDeleter::operator()(FileHandleHelper aHelper) {
if (aHelper != nullptr) {
DebugOnly<bool> ok;
#ifdef XP_WIN
ok = CloseHandle(aHelper);
#else
ok = close(aHelper) == 0 || errno == EINTR;
#endif
MOZ_ASSERT(ok, "failed to close file handle");
}
}
} // namespace detail
} // namespace mozilla

View File

@ -12,10 +12,6 @@
#include "mozilla/fallible.h"
#include "mozilla/UniquePtr.h"
#ifdef XP_WIN
# include <cstdint>
#endif
namespace mozilla {
/**
@ -46,81 +42,11 @@ struct FreePolicy {
void operator()(const void* ptr) { free(const_cast<void*>(ptr)); }
};
#if defined(XP_WIN)
// Can't include <windows.h> to get the actual definition of HANDLE
// because of namespace pollution.
typedef void* FileHandleType;
#elif defined(XP_UNIX)
typedef int FileHandleType;
#else
# error "Unsupported OS?"
#endif
struct FileHandleHelper {
MOZ_IMPLICIT FileHandleHelper(FileHandleType aHandle) : mHandle(aHandle) {}
MOZ_IMPLICIT constexpr FileHandleHelper(std::nullptr_t)
: mHandle(kInvalidHandle) {}
bool operator!=(std::nullptr_t) const {
#ifdef XP_WIN
// Windows uses both nullptr and INVALID_HANDLE_VALUE (-1 cast to
// HANDLE) in different situations, but nullptr is more reliably
// null while -1 is also valid input to some calls that take
// handles. So class considers both to be null (since neither
// should be closed) but default-constructs as nullptr.
if (mHandle == (void*)-1) {
return false;
}
#endif
return mHandle != kInvalidHandle;
}
operator FileHandleType() const { return mHandle; }
#ifdef XP_WIN
// NSPR uses an integer type for PROsfd, so this conversion is
// provided for working with it without needing reinterpret casts
// everywhere.
operator std::intptr_t() const {
return reinterpret_cast<std::intptr_t>(mHandle);
}
#endif
// When there's only one user-defined conversion operator, the
// compiler will use that to derive equality, but that doesn't work
// when the conversion is ambiguoug (the XP_WIN case above).
bool operator==(const FileHandleHelper& aOther) const {
return mHandle == aOther.mHandle;
}
private:
FileHandleType mHandle;
#ifdef XP_WIN
// See above for why this is nullptr. (Also, INVALID_HANDLE_VALUE
// can't be expressed as a constexpr.)
static constexpr FileHandleType kInvalidHandle = nullptr;
#else
static constexpr FileHandleType kInvalidHandle = -1;
#endif
};
struct FileHandleDeleter {
typedef FileHandleHelper pointer;
MFBT_API void operator()(FileHandleHelper aHelper);
};
} // namespace detail
template <typename T>
using UniqueFreePtr = UniquePtr<T, detail::FreePolicy<T>>;
// A RAII class for the OS construct used for open files and similar
// objects: a file descriptor on Unix or a handle on Windows.
using UniqueFileHandle =
UniquePtr<detail::FileHandleType, detail::FileHandleDeleter>;
} // namespace mozilla
#endif // mozilla_UniquePtrExtensions_h

View File

@ -150,7 +150,6 @@ UNIFIED_SOURCES += [
'RandomNum.cpp',
'SHA1.cpp',
'TaggedAnonymousMemory.cpp',
'UniquePtrExtensions.cpp',
'Unused.cpp',
'Utf8.cpp',
]

View File

@ -87,7 +87,7 @@ class SandboxBrokerTest : public ::testing::Test {
mServer = SandboxBroker::Create(GetPolicy(), getpid(), fd);
ASSERT_NE(mServer, nullptr);
ASSERT_TRUE(fd.IsValid());
auto rawFD = fd.TakePlatformHandle();
auto rawFD = fd.ClonePlatformHandle();
mClient.reset(new SandboxBrokerClient(rawFD.release()));
}

View File

@ -73,6 +73,7 @@ static const char kMappedFileUnsafePrefix[] = "/dev/";
static const char kDeletedSuffix[] = " (deleted)";
static const char kReservedFlags[] = " ---p";
static const char kMozillaIpcPrefix[] = "org.mozilla.ipc.";
static const char kChromiumPrefix[] = "org.chromium.";
inline static bool IsMappedFileOpenUnsafe(
const google_breakpad::MappingInfo& mapping) {
@ -574,7 +575,8 @@ bool LinuxDumper::ReadAuxv() {
}
bool LinuxDumper::IsIPCSharedMemorySegment(const char* name) {
if (my_strstr(name, kMozillaIpcPrefix) &&
if ((my_strstr(name, kMozillaIpcPrefix) ||
my_strstr(name, kChromiumPrefix)) &&
my_strstr(name, kDeletedSuffix)) {
return true;
}