diff options
author | Daniel Erat <derat@google.com> | 2015-07-06 13:18:13 -0600 |
---|---|---|
committer | Daniel Erat <derat@google.com> | 2015-07-07 08:49:02 -0600 |
commit | b8cf94937c52feb53b55c39e3f82094d27de464c (patch) | |
tree | 20cd8f9867dfe9c0c2510899346c744605425b75 /base/posix | |
parent | 5bb0a2e04cf1346d5a2819f277381c0415549600 (diff) | |
download | libchrome-b8cf94937c52feb53b55c39e3f82094d27de464c.tar.gz |
Add upstream code as of Chromium r334380.
Copy the unchanged source from
https://chromium.googlesource.com/chromium/src/base/ as of
r334380 (really r334285 a.k.a. 23911a0c in the base/
subtree).
Also add MODULE_LICENSE_BSD and copy Chromium's current
LICENSE file to NOTICE.
Bug: 22317122
Change-Id: I89863bfeca67b3a1ff05e6078f2f9ee4e31c5c99
Diffstat (limited to 'base/posix')
-rw-r--r-- | base/posix/eintr_wrapper.h | 67 | ||||
-rw-r--r-- | base/posix/file_descriptor_shuffle.cc | 100 | ||||
-rw-r--r-- | base/posix/file_descriptor_shuffle.h | 87 | ||||
-rw-r--r-- | base/posix/file_descriptor_shuffle_unittest.cc | 281 | ||||
-rw-r--r-- | base/posix/global_descriptors.cc | 85 | ||||
-rw-r--r-- | base/posix/global_descriptors.h | 92 | ||||
-rw-r--r-- | base/posix/safe_strerror.cc | 124 | ||||
-rw-r--r-- | base/posix/safe_strerror.h | 42 | ||||
-rw-r--r-- | base/posix/unix_domain_socket_linux.cc | 245 | ||||
-rw-r--r-- | base/posix/unix_domain_socket_linux.h | 103 | ||||
-rw-r--r-- | base/posix/unix_domain_socket_linux_unittest.cc | 161 |
11 files changed, 1387 insertions, 0 deletions
diff --git a/base/posix/eintr_wrapper.h b/base/posix/eintr_wrapper.h new file mode 100644 index 0000000000..5a5dc758a9 --- /dev/null +++ b/base/posix/eintr_wrapper.h @@ -0,0 +1,67 @@ +// 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, this wrapper macro does nothing. +// +// 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 + +#define HANDLE_EINTR(x) (x) +#define IGNORE_EINTR(x) (x) + +#endif // OS_POSIX + +#endif // BASE_POSIX_EINTR_WRAPPER_H_ diff --git a/base/posix/file_descriptor_shuffle.cc b/base/posix/file_descriptor_shuffle.cc new file mode 100644 index 0000000000..d2fd39a95a --- /dev/null +++ b/base/posix/file_descriptor_shuffle.cc @@ -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 diff --git a/base/posix/file_descriptor_shuffle.h b/base/posix/file_descriptor_shuffle.h new file mode 100644 index 0000000000..78e3a7d493 --- /dev/null +++ b/base/posix/file_descriptor_shuffle.h @@ -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() {} +}; + +// 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_ diff --git a/base/posix/file_descriptor_shuffle_unittest.cc b/base/posix/file_descriptor_shuffle_unittest.cc new file mode 100644 index 0000000000..3dfbf7e246 --- /dev/null +++ b/base/posix/file_descriptor_shuffle_unittest.cc @@ -0,0 +1,281 @@ +// 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/file_descriptor_shuffle.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// 'Duplicated' file descriptors start at this number +const int kDuplicateBase = 1000; + +} // namespace + +namespace base { + +struct Action { + enum Type { + CLOSE, + MOVE, + DUPLICATE, + }; + + Action(Type in_type, int in_fd1, int in_fd2 = -1) + : type(in_type), + fd1(in_fd1), + fd2(in_fd2) { + } + + bool operator==(const Action& other) const { + return other.type == type && + other.fd1 == fd1 && + other.fd2 == fd2; + } + + Type type; + int fd1; + int fd2; +}; + +class InjectionTracer : public InjectionDelegate { + public: + InjectionTracer() + : next_duplicate_(kDuplicateBase) { + } + + bool Duplicate(int* result, int fd) override { + *result = next_duplicate_++; + actions_.push_back(Action(Action::DUPLICATE, *result, fd)); + return true; + } + + bool Move(int src, int dest) override { + actions_.push_back(Action(Action::MOVE, src, dest)); + return true; + } + + void Close(int fd) override { actions_.push_back(Action(Action::CLOSE, fd)); } + + const std::vector<Action>& actions() const { return actions_; } + + private: + int next_duplicate_; + std::vector<Action> actions_; +}; + +TEST(FileDescriptorShuffleTest, Empty) { + InjectiveMultimap map; + InjectionTracer tracer; + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(0u, tracer.actions().size()); +} + +TEST(FileDescriptorShuffleTest, Noop) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 0, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(0u, tracer.actions().size()); +} + +TEST(FileDescriptorShuffleTest, NoopAndClose) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 0, true)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(0u, tracer.actions().size()); +} + +TEST(FileDescriptorShuffleTest, Simple1) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + ASSERT_EQ(1u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); +} + +TEST(FileDescriptorShuffleTest, Simple2) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, false)); + map.push_back(InjectionArc(2, 3, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + ASSERT_EQ(2u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 2, 3)); +} + +TEST(FileDescriptorShuffleTest, Simple3) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, true)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + ASSERT_EQ(2u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::CLOSE, 0)); +} + +TEST(FileDescriptorShuffleTest, Simple4) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(10, 0, true)); + map.push_back(InjectionArc(1, 1, true)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + ASSERT_EQ(2u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 10, 0)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::CLOSE, 10)); +} + +TEST(FileDescriptorShuffleTest, Cycle) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, false)); + map.push_back(InjectionArc(1, 0, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + ASSERT_EQ(4u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == + Action(Action::DUPLICATE, kDuplicateBase, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0)); + EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase)); +} + +TEST(FileDescriptorShuffleTest, CycleAndClose1) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, true)); + map.push_back(InjectionArc(1, 0, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + ASSERT_EQ(4u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == + Action(Action::DUPLICATE, kDuplicateBase, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0)); + EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase)); +} + +TEST(FileDescriptorShuffleTest, CycleAndClose2) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, false)); + map.push_back(InjectionArc(1, 0, true)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + ASSERT_EQ(4u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == + Action(Action::DUPLICATE, kDuplicateBase, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0)); + EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase)); +} + +TEST(FileDescriptorShuffleTest, CycleAndClose3) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, true)); + map.push_back(InjectionArc(1, 0, true)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + ASSERT_EQ(4u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == + Action(Action::DUPLICATE, kDuplicateBase, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0)); + EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase)); +} + +TEST(FileDescriptorShuffleTest, Fanout) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, false)); + map.push_back(InjectionArc(0, 2, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + ASSERT_EQ(2u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2)); +} + +TEST(FileDescriptorShuffleTest, FanoutAndClose1) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, true)); + map.push_back(InjectionArc(0, 2, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + ASSERT_EQ(3u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2)); + EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0)); +} + +TEST(FileDescriptorShuffleTest, FanoutAndClose2) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, false)); + map.push_back(InjectionArc(0, 2, true)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + ASSERT_EQ(3u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2)); + EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0)); +} + +TEST(FileDescriptorShuffleTest, FanoutAndClose3) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, true)); + map.push_back(InjectionArc(0, 2, true)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + ASSERT_EQ(3u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2)); + EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0)); +} + +class FailingDelegate : public InjectionDelegate { + public: + bool Duplicate(int* result, int fd) override { return false; } + + bool Move(int src, int dest) override { return false; } + + void Close(int fd) override {} +}; + +TEST(FileDescriptorShuffleTest, EmptyWithFailure) { + InjectiveMultimap map; + FailingDelegate failing; + + EXPECT_TRUE(PerformInjectiveMultimap(map, &failing)); +} + +TEST(FileDescriptorShuffleTest, NoopWithFailure) { + InjectiveMultimap map; + FailingDelegate failing; + map.push_back(InjectionArc(0, 0, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &failing)); +} + +TEST(FileDescriptorShuffleTest, Simple1WithFailure) { + InjectiveMultimap map; + FailingDelegate failing; + map.push_back(InjectionArc(0, 1, false)); + + EXPECT_FALSE(PerformInjectiveMultimap(map, &failing)); +} + +} // namespace base diff --git a/base/posix/global_descriptors.cc b/base/posix/global_descriptors.cc new file mode 100644 index 0000000000..6c187838ad --- /dev/null +++ b/base/posix/global_descriptors.cc @@ -0,0 +1,85 @@ +// 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(FATAL) << "Unknown global descriptor: " << key; + return ret; +} + +int GlobalDescriptors::MaybeGet(Key key) const { + for (Mapping::const_iterator + i = descriptors_.begin(); i != descriptors_.end(); ++i) { + if (i->key == key) + return i->fd; + } + + return -1; +} + +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(FATAL) << "Unknown global descriptor: " << key; + return base::MemoryMappedFile::Region::kWholeFile; +} + +void GlobalDescriptors::Reset(const Mapping& mapping) { + descriptors_ = mapping; +} + +GlobalDescriptors::GlobalDescriptors() {} + +GlobalDescriptors::~GlobalDescriptors() {} + +} // namespace base diff --git a/base/posix/global_descriptors.h b/base/posix/global_descriptors.h new file mode 100644 index 0000000000..c774634f59 --- /dev/null +++ b/base/posix/global_descriptors.h @@ -0,0 +1,92 @@ +// 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/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. +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: +#if !defined(OS_ANDROID) + static const int kBaseDescriptor = 3; // 0, 1, 2 are already taken. +#else + static const int kBaseDescriptor = 4; // 3 used by __android_log_write(). +#endif + + // 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; + + // 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_ diff --git a/base/posix/safe_strerror.cc b/base/posix/safe_strerror.cc new file mode 100644 index 0000000000..e80e8f8bd9 --- /dev/null +++ b/base/posix/safe_strerror.cc @@ -0,0 +1,124 @@ +// 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 { + +#define USE_HISTORICAL_STRERRO_R (defined(__GLIBC__) || defined(OS_NACL)) + +#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 == NULL || 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 diff --git a/base/posix/safe_strerror.h b/base/posix/safe_strerror.h new file mode 100644 index 0000000000..862a75066c --- /dev/null +++ b/base/posix/safe_strerror.h @@ -0,0 +1,42 @@ +// 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 <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_ diff --git a/base/posix/unix_domain_socket_linux.cc b/base/posix/unix_domain_socket_linux.cc new file mode 100644 index 0000000000..62c930fdee --- /dev/null +++ b/base/posix/unix_domain_socket_linux.cc @@ -0,0 +1,245 @@ +// 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_linux.h" + +#include <errno.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <vector> + +#include "base/files/scoped_file.h" +#include "base/logging.h" +#include "base/memory/scoped_vector.h" +#include "base/pickle.h" +#include "base/posix/eintr_wrapper.h" +#include "base/stl_util.h" + +#if !defined(OS_NACL_NONSFI) +#include <sys/uio.h> +#endif + +namespace base { + +const size_t UnixDomainSocket::kMaxFileDescriptors = 16; + +#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. +static bool CreateSocketPair(ScopedFD* one, ScopedFD* two) { + int raw_socks[2]; + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_socks) == -1) + return false; + one->reset(raw_socks[0]); + two->reset(raw_socks[1]); + return true; +} + +// static +bool UnixDomainSocket::EnableReceiveProcessId(int fd) { + const int enable = 1; + return setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable)) == 0; +} +#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 = NULL; + 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. + const int flags = MSG_NOSIGNAL; + 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, + ScopedVector<ScopedFD>* fds) { + return UnixDomainSocket::RecvMsgWithPid(fd, buf, length, fds, NULL); +} + +// static +ssize_t UnixDomainSocket::RecvMsgWithPid(int fd, + void* buf, + size_t length, + ScopedVector<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, + ScopedVector<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) + // The PNaCl toolchain for Non-SFI binary build does not support ucred. + + CMSG_SPACE(sizeof(struct ucred)) +#endif + ; + 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 = NULL; + 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) + // The PNaCl toolchain for Non-SFI binary build does 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 + } + } + + if (msg.msg_flags & MSG_TRUNC || msg.msg_flags & MSG_CTRUNC) { + 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(new ScopedFD(wire_fds[i])); + } + + if (out_pid) { + // |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); + + *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(); + + ScopedVector<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, NULL); + 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 != NULL ? 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 diff --git a/base/posix/unix_domain_socket_linux.h b/base/posix/unix_domain_socket_linux.h new file mode 100644 index 0000000000..94da4b4f91 --- /dev/null +++ b/base/posix/unix_domain_socket_linux.h @@ -0,0 +1,103 @@ +// 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_LINUX_H_ +#define BASE_POSIX_UNIX_DOMAIN_SOCKET_LINUX_H_ + +#include <stdint.h> +#include <sys/types.h> +#include <vector> + +#include "base/base_export.h" +#include "base/files/scoped_file.h" +#include "base/memory/scoped_vector.h" +#include "base/process/process_handle.h" + +namespace base { + +class Pickle; + +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, + ScopedVector<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, + ScopedVector<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, + ScopedVector<ScopedFD>* fds, + ProcessId* pid); +}; + +} // namespace base + +#endif // BASE_POSIX_UNIX_DOMAIN_SOCKET_LINUX_H_ diff --git a/base/posix/unix_domain_socket_linux_unittest.cc b/base/posix/unix_domain_socket_linux_unittest.cc new file mode 100644 index 0000000000..175ec52f08 --- /dev/null +++ b/base/posix/unix_domain_socket_linux_unittest.cc @@ -0,0 +1,161 @@ +// Copyright (c) 2013 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 <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/files/file_util.h" +#include "base/files/scoped_file.h" +#include "base/location.h" +#include "base/memory/scoped_vector.h" +#include "base/pickle.h" +#include "base/posix/unix_domain_socket_linux.h" +#include "base/single_thread_task_runner.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +namespace { + +TEST(UnixDomainSocketTest, SendRecvMsgAbortOnReplyFDClose) { + Thread message_thread("UnixDomainSocketTest"); + ASSERT_TRUE(message_thread.Start()); + + int fds[2]; + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); + ScopedFD scoped_fd0(fds[0]); + ScopedFD scoped_fd1(fds[1]); + + // Have the thread send a synchronous message via the socket. + Pickle request; + message_thread.task_runner()->PostTask( + FROM_HERE, + Bind(IgnoreResult(&UnixDomainSocket::SendRecvMsg), fds[1], + static_cast<uint8_t*>(NULL), 0U, static_cast<int*>(NULL), request)); + + // Receive the message. + ScopedVector<ScopedFD> message_fds; + uint8_t buffer[16]; + ASSERT_EQ(static_cast<int>(request.size()), + UnixDomainSocket::RecvMsg(fds[0], buffer, sizeof(buffer), + &message_fds)); + ASSERT_EQ(1U, message_fds.size()); + + // Close the reply FD. + message_fds.clear(); + + // Check that the thread didn't get blocked. + WaitableEvent event(false, false); + message_thread.task_runner()->PostTask( + FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&event))); + ASSERT_TRUE(event.TimedWait(TimeDelta::FromMilliseconds(5000))); +} + +TEST(UnixDomainSocketTest, SendRecvMsgAvoidsSIGPIPE) { + // Make sure SIGPIPE isn't being ignored. + struct sigaction act = {}, oldact; + act.sa_handler = SIG_DFL; + ASSERT_EQ(0, sigaction(SIGPIPE, &act, &oldact)); + int fds[2]; + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); + ScopedFD scoped_fd1(fds[1]); + ASSERT_EQ(0, IGNORE_EINTR(close(fds[0]))); + + // Have the thread send a synchronous message via the socket. Unless the + // message is sent with MSG_NOSIGNAL, this shall result in SIGPIPE. + Pickle request; + ASSERT_EQ(-1, + UnixDomainSocket::SendRecvMsg(fds[1], static_cast<uint8_t*>(NULL), + 0U, static_cast<int*>(NULL), request)); + ASSERT_EQ(EPIPE, errno); + // Restore the SIGPIPE handler. + ASSERT_EQ(0, sigaction(SIGPIPE, &oldact, NULL)); +} + +// Simple sanity check within a single process that receiving PIDs works. +TEST(UnixDomainSocketTest, RecvPid) { + int fds[2]; + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); + ScopedFD recv_sock(fds[0]); + ScopedFD send_sock(fds[1]); + + ASSERT_TRUE(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get())); + + static const char kHello[] = "hello"; + ASSERT_TRUE(UnixDomainSocket::SendMsg( + send_sock.get(), kHello, sizeof(kHello), std::vector<int>())); + + // Extra receiving buffer space to make sure we really received only + // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer. + char buf[sizeof(kHello) + 1]; + ProcessId sender_pid; + ScopedVector<ScopedFD> fd_vec; + const ssize_t nread = UnixDomainSocket::RecvMsgWithPid( + recv_sock.get(), buf, sizeof(buf), &fd_vec, &sender_pid); + ASSERT_EQ(sizeof(kHello), static_cast<size_t>(nread)); + ASSERT_EQ(0, memcmp(buf, kHello, sizeof(kHello))); + ASSERT_EQ(0U, fd_vec.size()); + + ASSERT_EQ(getpid(), sender_pid); +} + +// Same as above, but send the max number of file descriptors too. +TEST(UnixDomainSocketTest, RecvPidWithMaxDescriptors) { + int fds[2]; + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); + ScopedFD recv_sock(fds[0]); + ScopedFD send_sock(fds[1]); + + ASSERT_TRUE(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get())); + + static const char kHello[] = "hello"; + std::vector<int> send_fds(UnixDomainSocket::kMaxFileDescriptors, + send_sock.get()); + ASSERT_TRUE(UnixDomainSocket::SendMsg( + send_sock.get(), kHello, sizeof(kHello), send_fds)); + + // Extra receiving buffer space to make sure we really received only + // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer. + char buf[sizeof(kHello) + 1]; + ProcessId sender_pid; + ScopedVector<ScopedFD> recv_fds; + const ssize_t nread = UnixDomainSocket::RecvMsgWithPid( + recv_sock.get(), buf, sizeof(buf), &recv_fds, &sender_pid); + ASSERT_EQ(sizeof(kHello), static_cast<size_t>(nread)); + ASSERT_EQ(0, memcmp(buf, kHello, sizeof(kHello))); + ASSERT_EQ(UnixDomainSocket::kMaxFileDescriptors, recv_fds.size()); + + ASSERT_EQ(getpid(), sender_pid); +} + +// Check that RecvMsgWithPid doesn't DCHECK fail when reading EOF from a +// disconnected socket. +TEST(UnixDomianSocketTest, RecvPidDisconnectedSocket) { + int fds[2]; + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); + ScopedFD recv_sock(fds[0]); + ScopedFD send_sock(fds[1]); + + ASSERT_TRUE(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get())); + + send_sock.reset(); + + char ch; + ProcessId sender_pid; + ScopedVector<ScopedFD> recv_fds; + const ssize_t nread = UnixDomainSocket::RecvMsgWithPid( + recv_sock.get(), &ch, sizeof(ch), &recv_fds, &sender_pid); + ASSERT_EQ(0, nread); + ASSERT_EQ(-1, sender_pid); + ASSERT_EQ(0U, recv_fds.size()); +} + +} // namespace + +} // namespace base |