Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,60 @@
// Copyright 2018 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/posix/can_lower_nice_to.h"
#include <limits.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <unistd.h>
#include "build/build_config.h"
// Not defined on AIX by default.
#if defined(OS_AIX)
#if defined(RLIMIT_NICE)
#error Assumption about OS_AIX is incorrect
#endif
#define RLIMIT_NICE 20
#endif
namespace base {
namespace internal {
bool CanLowerNiceTo(int nice_value) {
// On a POSIX system, the nice value of a thread can be lowered 1. by the root
// user, 2. by a user with the CAP_SYS_NICE permission or 3. by any user if
// the target value is within the range allowed by RLIMIT_NICE.
// 1. Check for root user.
if (geteuid() == 0)
return true;
// 2. Skip checking the CAP_SYS_NICE permission because it would require
// libcap.so.
// 3. Check whether the target value is within the range allowed by
// RLIMIT_NICE.
//
// NZERO should be defined in <limits.h> per POSIX, and should be at least 20.
// (NZERO-1) is the highest possible niceness value (i.e. lowest priority).
// Most platforms use NZERO=20.
//
// RLIMIT_NICE tells us how much we can reduce niceness (increase priority) if
// we start at NZERO. For example, if NZERO is 20 and the rlimit is 30, we can
// lower niceness anywhere within the [-10, 19] range (20 - 30 = -10).
//
// So, we are allowed to reduce niceness to a minimum of NZERO - rlimit:
struct rlimit rlim;
if (getrlimit(RLIMIT_NICE, &rlim) != 0)
return false;
const int lowest_nice_allowed = NZERO - static_cast<int>(rlim.rlim_cur);
// And lowering niceness to |nice_value| is allowed if it is greater than or
// equal to the limit:
return nice_value >= lowest_nice_allowed;
}
} // namespace internal
} // namespace base

View file

@ -0,0 +1,19 @@
// Copyright 2018 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.
#ifndef BASE_POSIX_CAN_LOWER_NICE_TO_H_
#define BASE_POSIX_CAN_LOWER_NICE_TO_H_
namespace base {
namespace internal {
// Returns true if lowering the nice value of a process or thread to
// |nice_value| using setpriority() or nice() should succeed. Note: A lower nice
// value means a higher priority.
bool CanLowerNiceTo(int nice_value);
} // namespace internal
} // namespace base
#endif // BASE_POSIX_CAN_LOWER_NICE_TO_H_

View file

@ -0,0 +1,68 @@
// Copyright (c) 2012 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 provides a wrapper around system calls which may be interrupted by a
// signal and return EINTR. See man 7 signal.
// To prevent long-lasting loops (which would likely be a bug, such as a signal
// that should be masked) to go unnoticed, there is a limit after which the
// caller will nonetheless see an EINTR in Debug builds.
//
// On Windows and Fuchsia, this wrapper macro does nothing because there are no
// signals.
//
// Don't wrap close calls in HANDLE_EINTR. Use IGNORE_EINTR if the return
// value of close is significant. See http://crbug.com/269623.
#ifndef BASE_POSIX_EINTR_WRAPPER_H_
#define BASE_POSIX_EINTR_WRAPPER_H_
#include "build/build_config.h"
#if defined(OS_POSIX)
#include <errno.h>
#if defined(NDEBUG)
#define HANDLE_EINTR(x) ({ \
decltype(x) eintr_wrapper_result; \
do { \
eintr_wrapper_result = (x); \
} while (eintr_wrapper_result == -1 && errno == EINTR); \
eintr_wrapper_result; \
})
#else
#define HANDLE_EINTR(x) ({ \
int eintr_wrapper_counter = 0; \
decltype(x) eintr_wrapper_result; \
do { \
eintr_wrapper_result = (x); \
} while (eintr_wrapper_result == -1 && errno == EINTR && \
eintr_wrapper_counter++ < 100); \
eintr_wrapper_result; \
})
#endif // NDEBUG
#define IGNORE_EINTR(x) ({ \
decltype(x) eintr_wrapper_result; \
do { \
eintr_wrapper_result = (x); \
if (eintr_wrapper_result == -1 && errno == EINTR) { \
eintr_wrapper_result = 0; \
} \
} while (0); \
eintr_wrapper_result; \
})
#else // !OS_POSIX
#define HANDLE_EINTR(x) (x)
#define IGNORE_EINTR(x) (x)
#endif // !OS_POSIX
#endif // BASE_POSIX_EINTR_WRAPPER_H_

View file

@ -0,0 +1,100 @@
// Copyright (c) 2011 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/posix/file_descriptor_shuffle.h"
#include <unistd.h>
#include <stddef.h>
#include <ostream>
#include "base/posix/eintr_wrapper.h"
#include "base/logging.h"
namespace base {
bool PerformInjectiveMultimapDestructive(
InjectiveMultimap* m, InjectionDelegate* delegate) {
static const size_t kMaxExtraFDs = 16;
int extra_fds[kMaxExtraFDs];
unsigned next_extra_fd = 0;
// DANGER: this function must not allocate or lock.
// Cannot use STL iterators here, since debug iterators use locks.
for (size_t i_index = 0; i_index < m->size(); ++i_index) {
InjectiveMultimap::value_type* i = &(*m)[i_index];
int temp_fd = -1;
// We DCHECK the injectiveness of the mapping.
for (size_t j_index = i_index + 1; j_index < m->size(); ++j_index) {
InjectiveMultimap::value_type* j = &(*m)[j_index];
DCHECK(i->dest != j->dest) << "Both fd " << i->source
<< " and " << j->source << " map to " << i->dest;
}
const bool is_identity = i->source == i->dest;
for (size_t j_index = i_index + 1; j_index < m->size(); ++j_index) {
InjectiveMultimap::value_type* j = &(*m)[j_index];
if (!is_identity && i->dest == j->source) {
if (temp_fd == -1) {
if (!delegate->Duplicate(&temp_fd, i->dest))
return false;
if (next_extra_fd < kMaxExtraFDs) {
extra_fds[next_extra_fd++] = temp_fd;
} else {
RAW_LOG(ERROR, "PerformInjectiveMultimapDestructive overflowed "
"extra_fds. Leaking file descriptors!");
}
}
j->source = temp_fd;
j->close = false;
}
if (i->close && i->source == j->dest)
i->close = false;
if (i->close && i->source == j->source) {
i->close = false;
j->close = true;
}
}
if (!is_identity) {
if (!delegate->Move(i->source, i->dest))
return false;
}
if (!is_identity && i->close)
delegate->Close(i->source);
}
for (unsigned i = 0; i < next_extra_fd; i++)
delegate->Close(extra_fds[i]);
return true;
}
bool PerformInjectiveMultimap(const InjectiveMultimap& m_in,
InjectionDelegate* delegate) {
InjectiveMultimap m(m_in);
return PerformInjectiveMultimapDestructive(&m, delegate);
}
bool FileDescriptorTableInjection::Duplicate(int* result, int fd) {
*result = HANDLE_EINTR(dup(fd));
return *result >= 0;
}
bool FileDescriptorTableInjection::Move(int src, int dest) {
return HANDLE_EINTR(dup2(src, dest)) != -1;
}
void FileDescriptorTableInjection::Close(int fd) {
int ret = IGNORE_EINTR(close(fd));
DPCHECK(ret == 0);
}
} // namespace base

View file

@ -0,0 +1,87 @@
// Copyright (c) 2011 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.
#ifndef BASE_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_
#define BASE_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_
// This code exists to shuffle file descriptors, which is commonly needed when
// forking subprocesses. The naive approach (just call dup2 to set up the
// desired descriptors) is very simple, but wrong: it won't handle edge cases
// (like mapping 0 -> 1, 1 -> 0) correctly.
//
// In order to unittest this code, it's broken into the abstract action (an
// injective multimap) and the concrete code for dealing with file descriptors.
// Users should use the code like this:
// base::InjectiveMultimap file_descriptor_map;
// file_descriptor_map.push_back(base::InjectionArc(devnull, 0, true));
// file_descriptor_map.push_back(base::InjectionArc(devnull, 2, true));
// file_descriptor_map.push_back(base::InjectionArc(pipe[1], 1, true));
// base::ShuffleFileDescriptors(file_descriptor_map);
//
// and trust the the Right Thing will get done.
#include <vector>
#include "base/base_export.h"
#include "base/compiler_specific.h"
namespace base {
// A Delegate which performs the actions required to perform an injective
// multimapping in place.
class InjectionDelegate {
public:
// Duplicate |fd|, an element of the domain, and write a fresh element of the
// domain into |result|. Returns true iff successful.
virtual bool Duplicate(int* result, int fd) = 0;
// Destructively move |src| to |dest|, overwriting |dest|. Returns true iff
// successful.
virtual bool Move(int src, int dest) = 0;
// Delete an element of the domain.
virtual void Close(int fd) = 0;
protected:
virtual ~InjectionDelegate() = default;
};
// An implementation of the InjectionDelegate interface using the file
// descriptor table of the current process as the domain.
class BASE_EXPORT FileDescriptorTableInjection : public InjectionDelegate {
bool Duplicate(int* result, int fd) override;
bool Move(int src, int dest) override;
void Close(int fd) override;
};
// A single arc of the directed graph which describes an injective multimapping.
struct InjectionArc {
InjectionArc(int in_source, int in_dest, bool in_close)
: source(in_source),
dest(in_dest),
close(in_close) {
}
int source;
int dest;
bool close; // if true, delete the source element after performing the
// mapping.
};
typedef std::vector<InjectionArc> InjectiveMultimap;
BASE_EXPORT bool PerformInjectiveMultimap(const InjectiveMultimap& map,
InjectionDelegate* delegate);
BASE_EXPORT bool PerformInjectiveMultimapDestructive(
InjectiveMultimap* map,
InjectionDelegate* delegate);
// This function will not call malloc but will mutate |map|
static inline bool ShuffleFileDescriptors(InjectiveMultimap* map) {
FileDescriptorTableInjection delegate;
return PerformInjectiveMultimapDestructive(map, &delegate);
}
} // namespace base
#endif // BASE_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_

View file

@ -0,0 +1,99 @@
// Copyright (c) 2012 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/posix/global_descriptors.h"
#include <vector>
#include <utility>
#include "base/logging.h"
namespace base {
GlobalDescriptors::Descriptor::Descriptor(Key key, int fd)
: key(key), fd(fd), region(base::MemoryMappedFile::Region::kWholeFile) {
}
GlobalDescriptors::Descriptor::Descriptor(Key key,
int fd,
base::MemoryMappedFile::Region region)
: key(key), fd(fd), region(region) {
}
// static
GlobalDescriptors* GlobalDescriptors::GetInstance() {
typedef Singleton<base::GlobalDescriptors,
LeakySingletonTraits<base::GlobalDescriptors> >
GlobalDescriptorsSingleton;
return GlobalDescriptorsSingleton::get();
}
int GlobalDescriptors::Get(Key key) const {
const int ret = MaybeGet(key);
if (ret == -1)
DLOG(DCHECK) << "Unknown global descriptor: " << key;
return ret;
}
int GlobalDescriptors::MaybeGet(Key key) const {
for (const auto& i : descriptors_) {
if (i.key == key)
return i.fd;
}
return -1;
}
base::ScopedFD GlobalDescriptors::TakeFD(
Key key,
base::MemoryMappedFile::Region* region) {
base::ScopedFD fd;
for (auto i = descriptors_.begin(); i != descriptors_.end(); ++i) {
if (i->key == key) {
*region = i->region;
fd.reset(i->fd);
descriptors_.erase(i);
break;
}
}
return fd;
}
void GlobalDescriptors::Set(Key key, int fd) {
Set(key, fd, base::MemoryMappedFile::Region::kWholeFile);
}
void GlobalDescriptors::Set(Key key,
int fd,
base::MemoryMappedFile::Region region) {
for (auto& i : descriptors_) {
if (i.key == key) {
i.fd = fd;
i.region = region;
return;
}
}
descriptors_.push_back(Descriptor(key, fd, region));
}
base::MemoryMappedFile::Region GlobalDescriptors::GetRegion(Key key) const {
for (const auto& i : descriptors_) {
if (i.key == key)
return i.region;
}
DLOG(DCHECK) << "Unknown global descriptor: " << key;
return base::MemoryMappedFile::Region::kWholeFile;
}
void GlobalDescriptors::Reset(const Mapping& mapping) {
descriptors_ = mapping;
}
GlobalDescriptors::GlobalDescriptors() = default;
GlobalDescriptors::~GlobalDescriptors() = default;
} // namespace base

View file

@ -0,0 +1,98 @@
// Copyright (c) 2012 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.
#ifndef BASE_POSIX_GLOBAL_DESCRIPTORS_H_
#define BASE_POSIX_GLOBAL_DESCRIPTORS_H_
#include "build/build_config.h"
#include <vector>
#include <utility>
#include <stdint.h>
#include "base/files/memory_mapped_file.h"
#include "base/files/scoped_file.h"
#include "base/memory/singleton.h"
namespace base {
// It's common practice to install file descriptors into well known slot
// numbers before execing a child; stdin, stdout and stderr are ubiqutous
// examples.
//
// However, when using a zygote model, this becomes troublesome. Since the
// descriptors which need to be in these slots generally aren't known, any code
// could open a resource and take one of the reserved descriptors. Simply
// overwriting the slot isn't a viable solution.
//
// We could try to fill the reserved slots as soon as possible, but this is a
// fragile solution since global constructors etc are able to open files.
//
// Instead, we retreat from the idea of installing descriptors in specific
// slots and add a layer of indirection in the form of this singleton object.
// It maps from an abstract key to a descriptor. If independent modules each
// need to define keys, then values should be chosen randomly so as not to
// collide.
//
// Note that this class is deprecated and passing file descriptor should ideally
// be done through the command line and using FileDescriptorStore.
// See https://crbugs.com/detail?id=692619
class BASE_EXPORT GlobalDescriptors {
public:
typedef uint32_t Key;
struct Descriptor {
Descriptor(Key key, int fd);
Descriptor(Key key, int fd, base::MemoryMappedFile::Region region);
// Globally unique key.
Key key;
// Actual FD.
int fd;
// Optional region, defaults to kWholeFile.
base::MemoryMappedFile::Region region;
};
typedef std::vector<Descriptor> Mapping;
// Often we want a canonical descriptor for a given Key. In this case, we add
// the following constant to the key value:
static const int kBaseDescriptor = 3; // 0, 1, 2 are already taken.
// Return the singleton instance of GlobalDescriptors.
static GlobalDescriptors* GetInstance();
// Get a descriptor given a key. It is a fatal error if the key is not known.
int Get(Key key) const;
// Get a descriptor given a key. Returns -1 on error.
int MaybeGet(Key key) const;
// Returns a descriptor given a key and removes it from this class mappings.
// Also populates |region|.
// It is a fatal error if the key is not known.
base::ScopedFD TakeFD(Key key, base::MemoryMappedFile::Region* region);
// Get a region given a key. It is a fatal error if the key is not known.
base::MemoryMappedFile::Region GetRegion(Key key) const;
// Set the descriptor for the given |key|. This sets the region associated
// with |key| to kWholeFile.
void Set(Key key, int fd);
// Set the descriptor and |region| for the given |key|.
void Set(Key key, int fd, base::MemoryMappedFile::Region region);
void Reset(const Mapping& mapping);
private:
friend struct DefaultSingletonTraits<GlobalDescriptors>;
GlobalDescriptors();
~GlobalDescriptors();
Mapping descriptors_;
};
} // namespace base
#endif // BASE_POSIX_GLOBAL_DESCRIPTORS_H_

View file

@ -0,0 +1,128 @@
// 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.
#if defined(__ANDROID__)
// Post-L versions of bionic define the GNU-specific strerror_r if _GNU_SOURCE
// is defined, but the symbol is renamed to __gnu_strerror_r which only exists
// on those later versions. To preserve ABI compatibility with older versions,
// undefine _GNU_SOURCE and use the POSIX version.
#undef _GNU_SOURCE
#endif
#include "base/posix/safe_strerror.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "build/build_config.h"
namespace base {
#if defined(__GLIBC__) || defined(OS_NACL)
#define USE_HISTORICAL_STRERRO_R 1
#else
#define USE_HISTORICAL_STRERRO_R 0
#endif
#if USE_HISTORICAL_STRERRO_R && defined(__GNUC__)
// GCC will complain about the unused second wrap function unless we tell it
// that we meant for them to be potentially unused, which is exactly what this
// attribute is for.
#define POSSIBLY_UNUSED __attribute__((unused))
#else
#define POSSIBLY_UNUSED
#endif
#if USE_HISTORICAL_STRERRO_R
// glibc has two strerror_r functions: a historical GNU-specific one that
// returns type char *, and a POSIX.1-2001 compliant one available since 2.3.4
// that returns int. This wraps the GNU-specific one.
static void POSSIBLY_UNUSED wrap_posix_strerror_r(
char *(*strerror_r_ptr)(int, char *, size_t),
int err,
char *buf,
size_t len) {
// GNU version.
char *rc = (*strerror_r_ptr)(err, buf, len);
if (rc != buf) {
// glibc did not use buf and returned a static string instead. Copy it
// into buf.
buf[0] = '\0';
strncat(buf, rc, len - 1);
}
// The GNU version never fails. Unknown errors get an "unknown error" message.
// The result is always null terminated.
}
#endif // USE_HISTORICAL_STRERRO_R
// Wrapper for strerror_r functions that implement the POSIX interface. POSIX
// does not define the behaviour for some of the edge cases, so we wrap it to
// guarantee that they are handled. This is compiled on all POSIX platforms, but
// it will only be used on Linux if the POSIX strerror_r implementation is
// being used (see below).
static void POSSIBLY_UNUSED wrap_posix_strerror_r(
int (*strerror_r_ptr)(int, char *, size_t),
int err,
char *buf,
size_t len) {
int old_errno = errno;
// Have to cast since otherwise we get an error if this is the GNU version
// (but in such a scenario this function is never called). Sadly we can't use
// C++-style casts because the appropriate one is reinterpret_cast but it's
// considered illegal to reinterpret_cast a type to itself, so we get an
// error in the opposite case.
int result = (*strerror_r_ptr)(err, buf, len);
if (result == 0) {
// POSIX is vague about whether the string will be terminated, although
// it indirectly implies that typically ERANGE will be returned, instead
// of truncating the string. We play it safe by always terminating the
// string explicitly.
buf[len - 1] = '\0';
} else {
// Error. POSIX is vague about whether the return value is itself a system
// error code or something else. On Linux currently it is -1 and errno is
// set. On BSD-derived systems it is a system error and errno is unchanged.
// We try and detect which case it is so as to put as much useful info as
// we can into our message.
int strerror_error; // The error encountered in strerror
int new_errno = errno;
if (new_errno != old_errno) {
// errno was changed, so probably the return value is just -1 or something
// else that doesn't provide any info, and errno is the error.
strerror_error = new_errno;
} else {
// Either the error from strerror_r was the same as the previous value, or
// errno wasn't used. Assume the latter.
strerror_error = result;
}
// snprintf truncates and always null-terminates.
snprintf(buf,
len,
"Error %d while retrieving error %d",
strerror_error,
err);
}
errno = old_errno;
}
void safe_strerror_r(int err, char *buf, size_t len) {
if (buf == nullptr || len <= 0) {
return;
}
// If using glibc (i.e., Linux), the compiler will automatically select the
// appropriate overloaded function based on the function type of strerror_r.
// The other one will be elided from the translation unit since both are
// static.
wrap_posix_strerror_r(&strerror_r, err, buf, len);
}
std::string safe_strerror(int err) {
const int buffer_size = 256;
char buf[buffer_size];
safe_strerror_r(err, buf, sizeof(buf));
return std::string(buf);
}
} // namespace base

View file

@ -0,0 +1,44 @@
// Copyright (c) 2011 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.
#ifndef BASE_POSIX_SAFE_STRERROR_H_
#define BASE_POSIX_SAFE_STRERROR_H_
#include <stddef.h>
#include <string>
#include "base/base_export.h"
namespace base {
// BEFORE using anything from this file, first look at PLOG and friends in
// logging.h and use them instead if applicable.
//
// This file declares safe, portable alternatives to the POSIX strerror()
// function. strerror() is inherently unsafe in multi-threaded apps and should
// never be used. Doing so can cause crashes. Additionally, the thread-safe
// alternative strerror_r varies in semantics across platforms. Use these
// functions instead.
// Thread-safe strerror function with dependable semantics that never fails.
// It will write the string form of error "err" to buffer buf of length len.
// If there is an error calling the OS's strerror_r() function then a message to
// that effect will be printed into buf, truncating if necessary. The final
// result is always null-terminated. The value of errno is never changed.
//
// Use this instead of strerror_r().
BASE_EXPORT void safe_strerror_r(int err, char *buf, size_t len);
// Calls safe_strerror_r with a buffer of suitable size and returns the result
// in a C++ string.
//
// Use this instead of strerror(). Note though that safe_strerror_r will be
// more robust in the case of heap corruption errors, since it doesn't need to
// allocate a string.
BASE_EXPORT std::string safe_strerror(int err);
} // namespace base
#endif // BASE_POSIX_SAFE_STRERROR_H_

View file

@ -0,0 +1,288 @@
// Copyright (c) 2011 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/posix/unix_domain_socket.h"
#include <errno.h>
#include <sys/socket.h>
#if !defined(OS_NACL_NONSFI)
#include <sys/un.h>
#endif
#include <unistd.h>
#include <vector>
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/pickle.h"
#include "base/posix/eintr_wrapper.h"
#include "base/stl_util.h"
#include "build/build_config.h"
#if !defined(OS_NACL_NONSFI)
#include <sys/uio.h>
#endif
namespace base {
const size_t UnixDomainSocket::kMaxFileDescriptors = 16;
#if !defined(OS_NACL_NONSFI)
bool CreateSocketPair(ScopedFD* one, ScopedFD* two) {
int raw_socks[2];
#if defined(OS_MACOSX)
// macOS does not support SEQPACKET.
const int flags = SOCK_STREAM;
#else
const int flags = SOCK_SEQPACKET;
#endif
if (socketpair(AF_UNIX, flags, 0, raw_socks) == -1)
return false;
#if defined(OS_MACOSX)
// On macOS, preventing SIGPIPE is done with socket option.
const int no_sigpipe = 1;
if (setsockopt(raw_socks[0], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
sizeof(no_sigpipe)) != 0)
return false;
if (setsockopt(raw_socks[1], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
sizeof(no_sigpipe)) != 0)
return false;
#endif
one->reset(raw_socks[0]);
two->reset(raw_socks[1]);
return true;
}
// static
bool UnixDomainSocket::EnableReceiveProcessId(int fd) {
#if !defined(OS_MACOSX)
const int enable = 1;
return setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable)) == 0;
#else
// SO_PASSCRED is not supported on macOS.
return true;
#endif // OS_MACOSX
}
#endif // !defined(OS_NACL_NONSFI)
// static
bool UnixDomainSocket::SendMsg(int fd,
const void* buf,
size_t length,
const std::vector<int>& fds) {
struct msghdr msg = {};
struct iovec iov = {const_cast<void*>(buf), length};
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
char* control_buffer = nullptr;
if (fds.size()) {
const unsigned control_len = CMSG_SPACE(sizeof(int) * fds.size());
control_buffer = new char[control_len];
struct cmsghdr* cmsg;
msg.msg_control = control_buffer;
msg.msg_controllen = control_len;
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
memcpy(CMSG_DATA(cmsg), &fds[0], sizeof(int) * fds.size());
msg.msg_controllen = cmsg->cmsg_len;
}
// Avoid a SIGPIPE if the other end breaks the connection.
// Due to a bug in the Linux kernel (net/unix/af_unix.c) MSG_NOSIGNAL isn't
// regarded for SOCK_SEQPACKET in the AF_UNIX domain, but it is mandated by
// POSIX. On Mac MSG_NOSIGNAL is not supported, so we need to ensure that
// SO_NOSIGPIPE is set during socket creation.
#if defined(OS_MACOSX)
const int flags = 0;
int no_sigpipe = 0;
socklen_t no_sigpipe_len = sizeof(no_sigpipe);
DPCHECK(getsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
&no_sigpipe_len) == 0)
<< "Failed ot get socket option.";
DCHECK(no_sigpipe) << "SO_NOSIGPIPE not set on the socket.";
#else
const int flags = MSG_NOSIGNAL;
#endif // OS_MACOSX
const ssize_t r = HANDLE_EINTR(sendmsg(fd, &msg, flags));
const bool ret = static_cast<ssize_t>(length) == r;
delete[] control_buffer;
return ret;
}
// static
ssize_t UnixDomainSocket::RecvMsg(int fd,
void* buf,
size_t length,
std::vector<ScopedFD>* fds) {
return UnixDomainSocket::RecvMsgWithPid(fd, buf, length, fds, nullptr);
}
// static
ssize_t UnixDomainSocket::RecvMsgWithPid(int fd,
void* buf,
size_t length,
std::vector<ScopedFD>* fds,
ProcessId* pid) {
return UnixDomainSocket::RecvMsgWithFlags(fd, buf, length, 0, fds, pid);
}
// static
ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd,
void* buf,
size_t length,
int flags,
std::vector<ScopedFD>* fds,
ProcessId* out_pid) {
fds->clear();
struct msghdr msg = {};
struct iovec iov = {buf, length};
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
const size_t kControlBufferSize =
CMSG_SPACE(sizeof(int) * kMaxFileDescriptors)
#if !defined(OS_NACL_NONSFI) && !defined(OS_MACOSX)
// The PNaCl toolchain for Non-SFI binary build and macOS do not support
// ucred. macOS supports xucred, but this structure is insufficient.
+ CMSG_SPACE(sizeof(struct ucred))
#endif // OS_NACL_NONSFI or OS_MACOSX
;
char control_buffer[kControlBufferSize];
msg.msg_control = control_buffer;
msg.msg_controllen = sizeof(control_buffer);
const ssize_t r = HANDLE_EINTR(recvmsg(fd, &msg, flags));
if (r == -1)
return -1;
int* wire_fds = nullptr;
unsigned wire_fds_len = 0;
ProcessId pid = -1;
if (msg.msg_controllen > 0) {
struct cmsghdr* cmsg;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
DCHECK_EQ(payload_len % sizeof(int), 0u);
DCHECK_EQ(wire_fds, static_cast<void*>(nullptr));
wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
wire_fds_len = payload_len / sizeof(int);
}
#if !defined(OS_NACL_NONSFI) && !defined(OS_MACOSX)
// The PNaCl toolchain for Non-SFI binary build and macOS do not support
// SCM_CREDENTIALS.
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_CREDENTIALS) {
DCHECK_EQ(payload_len, sizeof(struct ucred));
DCHECK_EQ(pid, -1);
pid = reinterpret_cast<struct ucred*>(CMSG_DATA(cmsg))->pid;
}
#endif // !defined(OS_NACL_NONSFI) && !defined(OS_MACOSX)
}
}
if (msg.msg_flags & MSG_TRUNC || msg.msg_flags & MSG_CTRUNC) {
if (msg.msg_flags & MSG_CTRUNC) {
// Extraordinary case, not caller fixable. Log something.
LOG(ERROR) << "recvmsg returned MSG_CTRUNC flag, buffer len is "
<< msg.msg_controllen;
}
for (unsigned i = 0; i < wire_fds_len; ++i)
close(wire_fds[i]);
errno = EMSGSIZE;
return -1;
}
if (wire_fds) {
for (unsigned i = 0; i < wire_fds_len; ++i)
fds->push_back(ScopedFD(wire_fds[i])); // TODO(mdempsky): emplace_back
}
if (out_pid) {
#if defined(OS_MACOSX)
socklen_t pid_size = sizeof(pid);
if (getsockopt(fd, SOL_LOCAL, LOCAL_PEERPID, &pid, &pid_size) != 0)
pid = -1;
#else
// |pid| will legitimately be -1 if we read EOF, so only DCHECK if we
// actually received a message. Unfortunately, Linux allows sending zero
// length messages, which are indistinguishable from EOF, so this check
// has false negatives.
if (r > 0 || msg.msg_controllen > 0)
DCHECK_GE(pid, 0);
#endif
*out_pid = pid;
}
return r;
}
#if !defined(OS_NACL_NONSFI)
// static
ssize_t UnixDomainSocket::SendRecvMsg(int fd,
uint8_t* reply,
unsigned max_reply_len,
int* result_fd,
const Pickle& request) {
return UnixDomainSocket::SendRecvMsgWithFlags(fd, reply, max_reply_len,
0, /* recvmsg_flags */
result_fd, request);
}
// static
ssize_t UnixDomainSocket::SendRecvMsgWithFlags(int fd,
uint8_t* reply,
unsigned max_reply_len,
int recvmsg_flags,
int* result_fd,
const Pickle& request) {
// This socketpair is only used for the IPC and is cleaned up before
// returning.
ScopedFD recv_sock, send_sock;
if (!CreateSocketPair(&recv_sock, &send_sock))
return -1;
{
std::vector<int> send_fds;
send_fds.push_back(send_sock.get());
if (!SendMsg(fd, request.data(), request.size(), send_fds))
return -1;
}
// Close the sending end of the socket right away so that if our peer closes
// it before sending a response (e.g., from exiting), RecvMsgWithFlags() will
// return EOF instead of hanging.
send_sock.reset();
std::vector<ScopedFD> recv_fds;
// When porting to OSX keep in mind it doesn't support MSG_NOSIGNAL, so the
// sender might get a SIGPIPE.
const ssize_t reply_len = RecvMsgWithFlags(
recv_sock.get(), reply, max_reply_len, recvmsg_flags, &recv_fds, nullptr);
recv_sock.reset();
if (reply_len == -1)
return -1;
// If we received more file descriptors than caller expected, then we treat
// that as an error.
if (recv_fds.size() > (result_fd != nullptr ? 1 : 0)) {
NOTREACHED();
return -1;
}
if (result_fd)
*result_fd = recv_fds.empty() ? -1 : recv_fds[0].release();
return reply_len;
}
#endif // !defined(OS_NACL_NONSFI)
} // namespace base

View file

@ -0,0 +1,111 @@
// Copyright (c) 2011 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.
#ifndef BASE_POSIX_UNIX_DOMAIN_SOCKET_H_
#define BASE_POSIX_UNIX_DOMAIN_SOCKET_H_
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <vector>
#include "base/base_export.h"
#include "base/files/scoped_file.h"
#include "base/process/process_handle.h"
#include "build/build_config.h"
namespace base {
class Pickle;
#if !defined(OS_NACL_NONSFI)
// Creates a connected pair of UNIX-domain SOCK_SEQPACKET sockets, and passes
// ownership of the newly allocated file descriptors to |one| and |two|.
// Returns true on success.
bool BASE_EXPORT CreateSocketPair(ScopedFD* one, ScopedFD* two);
#endif
class BASE_EXPORT UnixDomainSocket {
public:
// Maximum number of file descriptors that can be read by RecvMsg().
static const size_t kMaxFileDescriptors;
#if !defined(OS_NACL_NONSFI)
// Use to enable receiving process IDs in RecvMsgWithPid. Should be called on
// the receiving socket (i.e., the socket passed to RecvMsgWithPid). Returns
// true if successful.
static bool EnableReceiveProcessId(int fd);
#endif // !defined(OS_NACL_NONSFI)
// Use sendmsg to write the given msg and include a vector of file
// descriptors. Returns true if successful.
static bool SendMsg(int fd,
const void* msg,
size_t length,
const std::vector<int>& fds);
// Use recvmsg to read a message and an array of file descriptors. Returns
// -1 on failure. Note: will read, at most, |kMaxFileDescriptors| descriptors.
static ssize_t RecvMsg(int fd,
void* msg,
size_t length,
std::vector<ScopedFD>* fds);
// Same as RecvMsg above, but also returns the sender's process ID (as seen
// from the caller's namespace). However, before using this function to
// receive process IDs, EnableReceiveProcessId() should be called on the
// receiving socket.
static ssize_t RecvMsgWithPid(int fd,
void* msg,
size_t length,
std::vector<ScopedFD>* fds,
ProcessId* pid);
#if !defined(OS_NACL_NONSFI)
// Perform a sendmsg/recvmsg pair.
// 1. This process creates a UNIX SEQPACKET socketpair. Using
// connection-oriented sockets (SEQPACKET or STREAM) is critical here,
// because if one of the ends closes the other one must be notified.
// 2. This process writes a request to |fd| with an SCM_RIGHTS control
// message containing on end of the fresh socket pair.
// 3. This process blocks reading from the other end of the fresh
// socketpair.
// 4. The target process receives the request, processes it and writes the
// reply to the end of the socketpair contained in the request.
// 5. This process wakes up and continues.
//
// fd: descriptor to send the request on
// reply: buffer for the reply
// reply_len: size of |reply|
// result_fd: (may be NULL) the file descriptor returned in the reply
// (if any)
// request: the bytes to send in the request
static ssize_t SendRecvMsg(int fd,
uint8_t* reply,
unsigned reply_len,
int* result_fd,
const Pickle& request);
// Similar to SendRecvMsg(), but |recvmsg_flags| allows to control the flags
// of the recvmsg(2) call.
static ssize_t SendRecvMsgWithFlags(int fd,
uint8_t* reply,
unsigned reply_len,
int recvmsg_flags,
int* result_fd,
const Pickle& request);
#endif // !defined(OS_NACL_NONSFI)
private:
// Similar to RecvMsg, but allows to specify |flags| for recvmsg(2).
static ssize_t RecvMsgWithFlags(int fd,
void* msg,
size_t length,
int flags,
std::vector<ScopedFD>* fds,
ProcessId* pid);
};
} // namespace base
#endif // BASE_POSIX_UNIX_DOMAIN_SOCKET_H_