Rework ReadCStringUnmapped[SizeLimited](Self|Forked) to not use fork()

Avoids using pointers shared between parent/child. Explicitly builds the
test strings in the child process, and then passes both the address and
the expected value of the string to the parent process for expectation
checking. This is necessary to have the test work on Fuchsia.

Also renames ...Forked to ...Child.

Bug: crashpad:196, crashpad:215
Change-Id: I7f22c134301a2806eb39549e371414e7ec9bf225
Reviewed-on: https://chromium-review.googlesource.com/896228
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Scott Graham <scottmg@chromium.org>
This commit is contained in:
Scott Graham 2018-01-31 14:34:38 -08:00 committed by Commit Bot
parent 75ae5bae78
commit 709a92981d

View File

@ -423,51 +423,46 @@ TEST(ProcessMemory, ReadUnmappedChild) {
test.RunAgainstChild();
}
// TODO(scottmg): Need to be ported to MultiprocessExec and not rely on fork().
#if !defined(OS_FUCHSIA)
constexpr size_t kChildProcessStringLength = 10;
class TargetProcessTest : public Multiprocess {
class StringDataInChildProcess {
public:
TargetProcessTest() : Multiprocess() {}
~TargetProcessTest() {}
// This constructor only makes sense in the child process.
explicit StringDataInChildProcess(const char* cstring)
: address_(FromPointerCast<VMAddress>(cstring)) {
memcpy(expected_value_, cstring, kChildProcessStringLength + 1);
}
void RunAgainstSelf() { DoTest(getpid()); }
void Write(FileHandle out) {
CheckedWriteFile(out, &address_, sizeof(address_));
CheckedWriteFile(out, &expected_value_, sizeof(expected_value_));
}
void RunAgainstForked() { Run(); }
static StringDataInChildProcess Read(FileHandle in) {
StringDataInChildProcess str;
EXPECT_TRUE(ReadFileExactly(in, &str.address_, sizeof(str.address_)));
EXPECT_TRUE(
ReadFileExactly(in, &str.expected_value_, sizeof(str.expected_value_)));
return str;
}
VMAddress address() const { return address_; }
std::string expected_value() const { return expected_value_; }
private:
void MultiprocessParent() override { DoTest(ChildPID()); }
StringDataInChildProcess() : address_(0), expected_value_() {}
void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); }
virtual void DoTest(pid_t pid) = 0;
DISALLOW_COPY_AND_ASSIGN(TargetProcessTest);
VMAddress address_;
char expected_value_[kChildProcessStringLength + 1];
};
bool ReadCString(const ProcessMemory& memory,
const char* pointer,
std::string* result) {
return memory.ReadCString(FromPointerCast<VMAddress>(pointer), result);
}
bool ReadCStringSizeLimited(const ProcessMemory& memory,
const char* pointer,
size_t size,
std::string* result) {
return memory.ReadCStringSizeLimited(
FromPointerCast<VMAddress>(pointer), size, result);
}
class ReadCStringUnmappedTest : public TargetProcessTest {
public:
ReadCStringUnmappedTest(bool limit_size)
: TargetProcessTest(),
page_size_(getpagesize()),
region_size_(2 * page_size_),
limit_size_(limit_size) {
if (!pages_.ResetMmap(nullptr,
region_size_,
void DoCStringUnmappedTestSetup(
ScopedMmap* pages,
std::vector<StringDataInChildProcess>* strings) {
const size_t page_size = getpagesize();
const size_t region_size = 2 * page_size;
if (!pages->ResetMmap(nullptr,
region_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS,
-1,
@ -476,67 +471,104 @@ class ReadCStringUnmappedTest : public TargetProcessTest {
return;
}
char* region = pages_.addr_as<char*>();
for (size_t index = 0; index < region_size_; ++index) {
char* region = pages->addr_as<char*>();
for (size_t index = 0; index < region_size; ++index) {
region[index] = 1 + index % 255;
}
// A string at the start of the mapped region
string1_ = region;
string1_[expected_length_] = '\0';
char* string1 = region;
string1[kChildProcessStringLength] = '\0';
// A string near the end of the mapped region
string2_ = region + page_size_ - expected_length_ * 2;
string2_[expected_length_] = '\0';
char* string2 = region + page_size - kChildProcessStringLength * 2;
string2[kChildProcessStringLength] = '\0';
// A string that crosses from the mapped into the unmapped region
string3_ = region + page_size_ - expected_length_ + 1;
string3_[expected_length_] = '\0';
char* string3 = region + page_size - kChildProcessStringLength + 1;
string3[kChildProcessStringLength] = '\0';
// A string entirely in the unmapped region
string4_ = region + page_size_ + 10;
string4_[expected_length_] = '\0';
char* string4 = region + page_size + 10;
string4[kChildProcessStringLength] = '\0';
result_.reserve(expected_length_ + 1);
strings->push_back(StringDataInChildProcess(string1));
strings->push_back(StringDataInChildProcess(string2));
strings->push_back(StringDataInChildProcess(string3));
strings->push_back(StringDataInChildProcess(string4));
EXPECT_TRUE(pages_.ResetAddrLen(region, page_size_));
EXPECT_TRUE(pages->ResetAddrLen(region, page_size));
}
CRASHPAD_CHILD_TEST_MAIN(ReadCStringUnmappedChildMain) {
ScopedMmap pages;
std::vector<StringDataInChildProcess> strings;
DoCStringUnmappedTestSetup(&pages, &strings);
FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);
strings[0].Write(out);
strings[1].Write(out);
strings[2].Write(out);
strings[3].Write(out);
CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
return 0;
}
class ReadCStringUnmappedTest : public MultiprocessExec {
public:
ReadCStringUnmappedTest(bool limit_size)
: MultiprocessExec(), limit_size_(limit_size) {
SetChildTestMainFunction("ReadCStringUnmappedChildMain");
}
void RunAgainstSelf() {
ScopedMmap pages;
std::vector<StringDataInChildProcess> strings;
DoCStringUnmappedTestSetup(&pages, &strings);
DoTest(GetSelfProcess(), strings);
}
void RunAgainstChild() { Run(); }
private:
void DoTest(pid_t pid) override {
void MultiprocessParent() override {
std::vector<StringDataInChildProcess> strings;
strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));
strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));
strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));
strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));
ASSERT_NO_FATAL_FAILURE();
DoTest(ChildProcess(), strings);
}
void DoTest(ProcessType process,
const std::vector<StringDataInChildProcess>& strings) {
ProcessMemoryNative memory;
ASSERT_TRUE(memory.Initialize(pid));
ASSERT_TRUE(memory.Initialize(process));
std::string result;
result.reserve(kChildProcessStringLength + 1);
if (limit_size_) {
ASSERT_TRUE(ReadCStringSizeLimited(
memory, string1_, expected_length_ + 1, &result_));
EXPECT_EQ(result_, string1_);
ASSERT_TRUE(ReadCStringSizeLimited(
memory, string2_, expected_length_ + 1, &result_));
EXPECT_EQ(result_, string2_);
EXPECT_FALSE(ReadCStringSizeLimited(
memory, string3_, expected_length_ + 1, &result_));
EXPECT_FALSE(ReadCStringSizeLimited(
memory, string4_, expected_length_ + 1, &result_));
ASSERT_TRUE(memory.ReadCStringSizeLimited(
strings[0].address(), kChildProcessStringLength + 1, &result));
EXPECT_EQ(result, strings[0].expected_value());
ASSERT_TRUE(memory.ReadCStringSizeLimited(
strings[1].address(), kChildProcessStringLength + 1, &result));
EXPECT_EQ(result, strings[1].expected_value());
EXPECT_FALSE(memory.ReadCStringSizeLimited(
strings[2].address(), kChildProcessStringLength + 1, &result));
EXPECT_FALSE(memory.ReadCStringSizeLimited(
strings[3].address(), kChildProcessStringLength + 1, &result));
} else {
ASSERT_TRUE(ReadCString(memory, string1_, &result_));
EXPECT_EQ(result_, string1_);
ASSERT_TRUE(ReadCString(memory, string2_, &result_));
EXPECT_EQ(result_, string2_);
EXPECT_FALSE(ReadCString(memory, string3_, &result_));
EXPECT_FALSE(ReadCString(memory, string4_, &result_));
ASSERT_TRUE(memory.ReadCString(strings[0].address(), &result));
EXPECT_EQ(result, strings[0].expected_value());
ASSERT_TRUE(memory.ReadCString(strings[1].address(), &result));
EXPECT_EQ(result, strings[1].expected_value());
EXPECT_FALSE(memory.ReadCString(strings[2].address(), &result));
EXPECT_FALSE(memory.ReadCString(strings[3].address(), &result));
}
}
std::string result_;
ScopedMmap pages_;
const size_t page_size_;
const size_t region_size_;
static const size_t expected_length_ = 10;
char* string1_;
char* string2_;
char* string3_;
char* string4_;
const bool limit_size_;
DISALLOW_COPY_AND_ASSIGN(ReadCStringUnmappedTest);
@ -548,10 +580,10 @@ TEST(ProcessMemory, ReadCStringUnmappedSelf) {
test.RunAgainstSelf();
}
TEST(ProcessMemory, ReadCStringUnmappedForked) {
TEST(ProcessMemory, ReadCStringUnmappedChild) {
ReadCStringUnmappedTest test(/* limit_size= */ false);
ASSERT_FALSE(testing::Test::HasFailure());
test.RunAgainstForked();
test.RunAgainstChild();
}
TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedSelf) {
@ -560,14 +592,12 @@ TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedSelf) {
test.RunAgainstSelf();
}
TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedForked) {
TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedChild) {
ReadCStringUnmappedTest test(/* limit_size= */ true);
ASSERT_FALSE(testing::Test::HasFailure());
test.RunAgainstForked();
test.RunAgainstChild();
}
#endif // !defined(OS_FUCHSIA)
} // namespace
} // namespace test
} // namespace crashpad