Add and use CheckedReadFD(), CheckedWriteFD(), and CheckedReadFDAtEOF().

TEST=util_test
R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/577333002
This commit is contained in:
Mark Mentovai 2014-09-18 15:03:49 -04:00
parent 12b67422f9
commit eeaf460f82
7 changed files with 124 additions and 156 deletions

View File

@ -16,6 +16,8 @@
#include <unistd.h>
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/posix/eintr_wrapper.h"
namespace {
@ -72,4 +74,34 @@ ssize_t WriteFD(int fd, const void* buffer, size_t size) {
return ReadOrWrite<WriteTraits>(fd, buffer, size);
}
void CheckedReadFD(int fd, void* buffer, size_t size) {
ssize_t expect = base::checked_cast<ssize_t>(size);
ssize_t rv = ReadFD(fd, buffer, size);
if (rv < 0) {
PCHECK(rv == expect) << "read";
} else {
CHECK_EQ(rv, expect) << "read";
}
}
void CheckedWriteFD(int fd, const void* buffer, size_t size) {
ssize_t expect = base::checked_cast<ssize_t>(size);
ssize_t rv = WriteFD(fd, buffer, size);
if (rv < 0) {
PCHECK(rv == expect) << "write";
} else {
CHECK_EQ(rv, expect) << "write";
}
}
void CheckedReadFDAtEOF(int fd) {
char c;
ssize_t rv = ReadFD(fd, &c, 1);
if (rv < 0) {
PCHECK(rv == 0) << "read";
} else {
CHECK_EQ(rv, 0) << "read";
}
}
} // namespace crashpad

View File

@ -30,6 +30,8 @@ namespace crashpad {
//! have been read into \a buffer.
//!
//! \sa WriteFD
//! \sa CheckedReadFD
//! \sa CheckedReadFDAtEOF
ssize_t ReadFD(int fd, void* buffer, size_t size);
//! \brief Wraps `write()`, retrying when interrupted or following a short
@ -43,8 +45,40 @@ ssize_t ReadFD(int fd, void* buffer, size_t size);
//! been written to \a fd.
//!
//! \sa ReadFD
//! \sa CheckedWriteFD
ssize_t WriteFD(int fd, const void* buffer, size_t size);
//! \brief Wraps ReadFD(), ensuring that exactly \a size bytes are read.
//!
//! If \a size is out of the range of possible `read()` return values, if the
//! underlying ReadFD() fails, or if other than \a size bytes were read, this
//! function causes execution to terminate without returning.
//!
//! \sa CheckedWriteFD
//! \sa ReadFD
//! \sa CheckedReadFDAtEOF
void CheckedReadFD(int fd, void* buffer, size_t size);
//! \brief Wraps WriteFD(), ensuring that exactly \a size bytes are written.
//!
//! If \a size is out of the range of possible `write()` return values, if the
//! underlying WriteFD() fails, or if other than \a size bytes were written,
//! this function causes execution to terminate without returning.
//!
//! \sa CheckedReadFD
//! \sa WriteFD
void CheckedWriteFD(int fd, const void* buffer, size_t size);
//! \brief Wraps ReadFD(), ensuring that it indicates end-of-file.
//!
//! Attempts to read a single byte from \a fd, expecting no data to be read. If
//! the underlying ReadFD() fails, or if a byte actually is read, this function
//! causes execution to terminate without returning.
//!
//! \sa CheckedReadFD
//! \sa ReadFD
void CheckedReadFDAtEOF(int fd);
} // namespace crashpad
#endif // CRASHPAD_UTIL_FILE_FD_IO_H_

View File

@ -91,20 +91,11 @@ class ProcessReaderChild final : public MachMultiprocess {
int read_fd = ReadPipeFD();
mach_vm_address_t address;
ssize_t rv = ReadFD(read_fd, &address, sizeof(address));
ASSERT_EQ(static_cast<ssize_t>(sizeof(address)), rv)
<< ErrnoMessage("read");
CheckedReadFD(read_fd, &address, sizeof(address));
std::string read_string;
ASSERT_TRUE(process_reader.Memory()->ReadCString(address, &read_string));
EXPECT_EQ(kTestMemory, read_string);
// Tell the child that its OK to exit. The child needed to be kept alive
// until the parent finished working with it.
int write_fd = WritePipeFD();
char c = '\0';
rv = WriteFD(write_fd, &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("write");
}
void MachMultiprocessChild() override {
@ -112,15 +103,11 @@ class ProcessReaderChild final : public MachMultiprocess {
mach_vm_address_t address =
reinterpret_cast<mach_vm_address_t>(kTestMemory);
ssize_t rv = WriteFD(write_fd, &address, sizeof(address));
ASSERT_EQ(static_cast<ssize_t>(sizeof(address)), rv)
<< ErrnoMessage("write");
CheckedWriteFD(write_fd, &address, sizeof(address));
// Wait for the parent to say that its OK to exit.
int read_fd = ReadPipeFD();
char c;
rv = ReadFD(read_fd, &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("read");
// Wait for the parent to signal that its OK to exit by closing its end of
// the pipe.
CheckedReadFDAtEOF(ReadPipeFD());
}
DISALLOW_COPY_AND_ASSIGN(ProcessReaderChild);
@ -455,22 +442,15 @@ class ProcessReaderThreadedChild final : public MachMultiprocess {
thread_index < thread_count_ + 1;
++thread_index) {
uint64_t thread_id;
ssize_t rv = ReadFD(read_fd, &thread_id, sizeof(thread_id));
ASSERT_EQ(static_cast<ssize_t>(sizeof(thread_id)), rv)
<< ErrnoMessage("read");
CheckedReadFD(read_fd, &thread_id, sizeof(thread_id));
TestThreadPool::ThreadExpectation expectation;
rv = ReadFD(read_fd,
&expectation.stack_address,
sizeof(expectation.stack_address));
ASSERT_EQ(static_cast<ssize_t>(sizeof(expectation.stack_address)), rv)
<< ErrnoMessage("read");
rv = ReadFD(read_fd,
&expectation.suspend_count,
sizeof(expectation.suspend_count));
ASSERT_EQ(static_cast<ssize_t>(sizeof(expectation.suspend_count)), rv)
<< ErrnoMessage("read");
CheckedReadFD(read_fd,
&expectation.stack_address,
sizeof(expectation.stack_address));
CheckedReadFD(read_fd,
&expectation.suspend_count,
sizeof(expectation.suspend_count));
// There cant be any duplicate thread IDs.
EXPECT_EQ(0u, thread_map.count(thread_id));
@ -483,13 +463,6 @@ class ProcessReaderThreadedChild final : public MachMultiprocess {
// The child shouldnt have any threads other than its main thread and the
// ones it created in its pool, so pass false for |tolerate_extra_threads|.
ExpectSeveralThreads(&thread_map, threads, false);
// Tell the child that its OK to exit. The child needed to be kept alive
// until the parent finished working with it.
int write_fd = WritePipeFD();
char c = '\0';
ssize_t rv = WriteFD(write_fd, &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("write");
}
void MachMultiprocessChild() override {
@ -505,25 +478,18 @@ class ProcessReaderThreadedChild final : public MachMultiprocess {
// to inspect it. Write an entry for it.
uint64_t thread_id = PthreadToThreadID(pthread_self());
ssize_t rv = WriteFD(write_fd, &thread_id, sizeof(thread_id));
ASSERT_EQ(static_cast<ssize_t>(sizeof(thread_id)), rv)
<< ErrnoMessage("write");
CheckedWriteFD(write_fd, &thread_id, sizeof(thread_id));
TestThreadPool::ThreadExpectation expectation;
expectation.stack_address = reinterpret_cast<mach_vm_address_t>(&thread_id);
expectation.suspend_count = 0;
rv = WriteFD(write_fd,
&expectation.stack_address,
sizeof(expectation.stack_address));
ASSERT_EQ(static_cast<ssize_t>(sizeof(expectation.stack_address)), rv)
<< ErrnoMessage("write");
rv = WriteFD(write_fd,
&expectation.suspend_count,
sizeof(expectation.suspend_count));
ASSERT_EQ(static_cast<ssize_t>(sizeof(expectation.suspend_count)), rv)
<< ErrnoMessage("write");
CheckedWriteFD(write_fd,
&expectation.stack_address,
sizeof(expectation.stack_address));
CheckedWriteFD(write_fd,
&expectation.suspend_count,
sizeof(expectation.suspend_count));
// Write an entry for everything in the thread pool.
for (size_t thread_index = 0;
@ -532,28 +498,18 @@ class ProcessReaderThreadedChild final : public MachMultiprocess {
uint64_t thread_id =
thread_pool.GetThreadInfo(thread_index, &expectation);
rv = WriteFD(write_fd, &thread_id, sizeof(thread_id));
ASSERT_EQ(static_cast<ssize_t>(sizeof(thread_id)), rv)
<< ErrnoMessage("write");
rv = WriteFD(write_fd,
&expectation.stack_address,
sizeof(expectation.stack_address));
ASSERT_EQ(static_cast<ssize_t>(sizeof(expectation.stack_address)), rv)
<< ErrnoMessage("write");
rv = WriteFD(write_fd,
&expectation.suspend_count,
sizeof(expectation.suspend_count));
ASSERT_EQ(static_cast<ssize_t>(sizeof(expectation.suspend_count)), rv)
<< ErrnoMessage("write");
CheckedWriteFD(write_fd, &thread_id, sizeof(thread_id));
CheckedWriteFD(write_fd,
&expectation.stack_address,
sizeof(expectation.stack_address));
CheckedWriteFD(write_fd,
&expectation.suspend_count,
sizeof(expectation.suspend_count));
}
// Wait for the parent to say that its OK to exit.
int read_fd = ReadPipeFD();
char c;
rv = ReadFD(read_fd, &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("read");
// Wait for the parent to signal that its OK to exit by closing its end of
// the pipe.
CheckedReadFDAtEOF(ReadPipeFD());
}
size_t thread_count_;
@ -650,9 +606,7 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
int read_fd = ReadPipeFD();
uint32_t expect_modules;
ssize_t rv = ReadFD(read_fd, &expect_modules, sizeof(expect_modules));
ASSERT_EQ(static_cast<ssize_t>(sizeof(expect_modules)), rv)
<< ErrnoMessage("read");
CheckedReadFD(read_fd, &expect_modules, sizeof(expect_modules));
ASSERT_EQ(expect_modules, modules.size());
@ -661,24 +615,16 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
"index %zu, name %s", index, modules[index].name.c_str()));
uint32_t expect_name_length;
rv = ReadFD(
CheckedReadFD(
read_fd, &expect_name_length, sizeof(expect_name_length));
ASSERT_EQ(static_cast<ssize_t>(sizeof(expect_name_length)), rv)
<< ErrnoMessage("read");
// The NUL terminator is not read.
std::string expect_name(expect_name_length, '\0');
rv = ReadFD(read_fd, &expect_name[0], expect_name_length);
ASSERT_EQ(static_cast<ssize_t>(expect_name_length), rv)
<< ErrnoMessage("read");
CheckedReadFD(read_fd, &expect_name[0], expect_name_length);
EXPECT_EQ(expect_name, modules[index].name);
mach_vm_address_t expect_address;
rv = ReadFD(read_fd, &expect_address, sizeof(expect_address));
ASSERT_EQ(static_cast<ssize_t>(sizeof(expect_address)), rv)
<< ErrnoMessage("read");
CheckedReadFD(read_fd, &expect_address, sizeof(expect_address));
EXPECT_EQ(expect_address, modules[index].address);
if (index == 0 || index == modules.size() - 1) {
@ -695,13 +641,6 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
}
}
}
// Tell the child that its OK to exit. The child needed to be kept alive
// until the parent finished working with it.
int write_fd = WritePipeFD();
char c = '\0';
rv = WriteFD(write_fd, &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("write");
}
void MachMultiprocessChild() override {
@ -718,10 +657,7 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
++write_image_count;
}
ssize_t rv = WriteFD(
write_fd, &write_image_count, sizeof(write_image_count));
ASSERT_EQ(static_cast<ssize_t>(sizeof(write_image_count)), rv)
<< ErrnoMessage("write");
CheckedWriteFD(write_fd, &write_image_count, sizeof(write_image_count));
for (size_t index = 0; index < write_image_count; ++index) {
const char* dyld_image_name;
@ -738,26 +674,18 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
}
uint32_t dyld_image_name_length = strlen(dyld_image_name);
rv = WriteFD(
CheckedWriteFD(
write_fd, &dyld_image_name_length, sizeof(dyld_image_name_length));
ASSERT_EQ(static_cast<ssize_t>(sizeof(dyld_image_name_length)), rv)
<< ErrnoMessage("write");
// The NUL terminator is not written.
rv = WriteFD(write_fd, dyld_image_name, dyld_image_name_length);
ASSERT_EQ(static_cast<ssize_t>(dyld_image_name_length), rv)
<< ErrnoMessage("write");
CheckedWriteFD(write_fd, dyld_image_name, dyld_image_name_length);
rv = WriteFD(write_fd, &dyld_image_address, sizeof(dyld_image_address));
ASSERT_EQ(static_cast<ssize_t>(sizeof(dyld_image_address)), rv)
<< ErrnoMessage("write");
CheckedWriteFD(write_fd, &dyld_image_address, sizeof(dyld_image_address));
}
// Wait for the parent to say that its OK to exit.
int read_fd = ReadPipeFD();
char c;
rv = ReadFD(read_fd, &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("read");
// Wait for the parent to signal that its OK to exit by closing its end of
// the pipe.
CheckedReadFDAtEOF(ReadPipeFD());
}
DISALLOW_COPY_AND_ASSIGN(ProcessReaderModulesChild);

View File

@ -30,7 +30,6 @@
#include "util/mach/exc_server_variants.h"
#include "util/mach/mach_extensions.h"
#include "util/misc/scoped_forbid_return.h"
#include "util/test/errors.h"
#include "util/test/mac/mach_errors.h"
#include "util/test/mac/mach_multiprocess.h"
@ -246,12 +245,10 @@ class TestExceptionPorts : public UniversalMachExcServer,
// Tell the parent process that everything is set up.
char c = '\0';
ssize_t rv_ssize = WriteFD(test_exception_ports_->WritePipeFD(), &c, 1);
ASSERT_EQ(1, rv_ssize) << ErrnoMessage("write");
CheckedWriteFD(test_exception_ports_->WritePipeFD(), &c, 1);
// Wait for the parent process to say that its end is set up.
rv_ssize = ReadFD(test_exception_ports_->ReadPipeFD(), &c, 1);
ASSERT_EQ(1, rv_ssize) << ErrnoMessage("read");
CheckedReadFD(test_exception_ports_->ReadPipeFD(), &c, 1);
EXPECT_EQ('\0', c);
// Regardless of where ExceptionPorts::SetExceptionPort() ran,
@ -367,8 +364,7 @@ class TestExceptionPorts : public UniversalMachExcServer,
// Wait for the child process to be ready. It needs to have all of its
// threads set up before proceeding if in kSetOutOfProcess mode.
char c;
ssize_t rv = ReadFD(ReadPipeFD(), &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("read");
CheckedReadFD(ReadPipeFD(), &c, 1);
EXPECT_EQ('\0', c);
mach_port_t local_port = LocalPort();
@ -451,8 +447,7 @@ class TestExceptionPorts : public UniversalMachExcServer,
// Let the child process know that everything in the parent process is set
// up.
c = '\0';
rv = WriteFD(WritePipeFD(), &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("write");
CheckedWriteFD(WritePipeFD(), &c, 1);
if (who_crashes_ != kNobodyCrashes) {
kern_return_t kr = MachMessageServer::Run(this,
@ -470,8 +465,7 @@ class TestExceptionPorts : public UniversalMachExcServer,
// Wait for the child process to exit or terminate, as indicated by it
// closing its pipe. This keeps LocalPort() alive in the child as
// RemotePort(), for the childs use in its TestGetExceptionPorts().
rv = ReadFD(ReadPipeFD(), &c, 1);
ASSERT_EQ(0, rv);
CheckedReadFDAtEOF(ReadPipeFD());
}
virtual void MachMultiprocessChild() override {

View File

@ -22,7 +22,6 @@
#include "gtest/gtest.h"
#include "util/file/fd_io.h"
#include "util/mach/mach_extensions.h"
#include "util/test/errors.h"
#include "util/test/mac/mach_errors.h"
#include "util/test/mac/mach_multiprocess.h"
@ -301,8 +300,7 @@ class TestMachMessageServer : public MachMessageServer::Interface,
if (options_.parent_wait_for_child_pipe) {
// Wait until the child is done sending what its going to send.
char c;
ssize_t rv = ReadFD(ReadPipeFD(), &c, 1);
EXPECT_EQ(1, rv) << ErrnoMessage("read");
CheckedReadFD(ReadPipeFD(), &c, 1);
EXPECT_EQ('\0', c);
}
@ -348,8 +346,7 @@ class TestMachMessageServer : public MachMessageServer::Interface,
if (options_.child_wait_for_parent_pipe) {
// Let the child know its safe to exit.
char c = '\0';
ssize_t rv = WriteFD(WritePipeFD(), &c, 1);
EXPECT_EQ(1, rv) << ErrnoMessage("write");
CheckedWriteFD(WritePipeFD(), &c, 1);
}
}
@ -393,8 +390,7 @@ class TestMachMessageServer : public MachMessageServer::Interface,
if (options_.child_wait_for_parent_pipe) {
char c;
ssize_t rv = ReadFD(ReadPipeFD(), &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("read");
CheckedReadFD(ReadPipeFD(), &c, 1);
ASSERT_EQ('\0', c);
}
}
@ -522,8 +518,7 @@ class TestMachMessageServer : public MachMessageServer::Interface,
// running MachMessageServer() once its received.
void ChildNotifyParentViaPipe() {
char c = '\0';
ssize_t rv = WriteFD(WritePipeFD(), &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("write");
CheckedWriteFD(WritePipeFD(), &c, 1);
}
// In the child process, sends a request message to the server and then

View File

@ -19,7 +19,6 @@
#include "base/basictypes.h"
#include "gtest/gtest.h"
#include "util/file/fd_io.h"
#include "util/test/errors.h"
#include "util/test/executable_path.h"
namespace {
@ -35,14 +34,10 @@ class TestMultiprocessExec final : public MultiprocessExec {
private:
virtual void MultiprocessParent() override {
int write_fd = WritePipeFD();
char c = 'z';
ssize_t rv = WriteFD(write_fd, &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("write");
CheckedWriteFD(WritePipeFD(), &c, 1);
int read_fd = ReadPipeFD();
rv = ReadFD(read_fd, &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("read");
CheckedReadFD(ReadPipeFD(), &c, 1);
EXPECT_EQ('Z', c);
}

View File

@ -21,7 +21,6 @@
#include "base/basictypes.h"
#include "gtest/gtest.h"
#include "util/file/fd_io.h"
#include "util/test/errors.h"
namespace {
@ -38,40 +37,31 @@ class TestMultiprocess final : public Multiprocess {
virtual void MultiprocessParent() override {
int read_fd = ReadPipeFD();
char c;
ssize_t rv = ReadFD(read_fd, &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("read");
CheckedReadFD(read_fd, &c, 1);
EXPECT_EQ('M', c);
pid_t pid;
rv = ReadFD(read_fd, &pid, sizeof(pid));
ASSERT_EQ(static_cast<ssize_t>(sizeof(pid)), rv) << ErrnoMessage("read");
CheckedReadFD(read_fd, &pid, sizeof(pid));
EXPECT_EQ(pid, ChildPID());
int write_fd = WritePipeFD();
c = 'm';
rv = WriteFD(write_fd, &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("write");
CheckedWriteFD(WritePipeFD(), &c, 1);
// The child will close its end of the pipe and exit. Make sure that the
// parent sees EOF.
rv = ReadFD(read_fd, &c, 1);
ASSERT_EQ(0, rv) << ErrnoMessage("read");
CheckedReadFDAtEOF(read_fd);
}
virtual void MultiprocessChild() override {
int write_fd = WritePipeFD();
char c = 'M';
ssize_t rv = WriteFD(write_fd, &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("write");
CheckedWriteFD(write_fd, &c, 1);
pid_t pid = getpid();
rv = WriteFD(write_fd, &pid, sizeof(pid));
ASSERT_EQ(static_cast<ssize_t>(sizeof(pid)), rv) << ErrnoMessage("write");
CheckedWriteFD(write_fd, &pid, sizeof(pid));
int read_fd = ReadPipeFD();
rv = ReadFD(read_fd, &c, 1);
ASSERT_EQ(1, rv) << ErrnoMessage("read");
CheckedReadFD(ReadPipeFD(), &c, 1);
EXPECT_EQ('m', c);
}