From 693ff6d550c6b2191992a44e653de9c91f9c2229 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 30 Jan 2018 10:16:57 -0800 Subject: [PATCH] Rework ReadCString[SizeLimited](Self|Forked) to not use fork() Instead of using pointers shared between the parent/child due to fork, explicitly builds and passes them between processes. This is unfortunately a bit more verbose, but seems like it tests functionality a little better, and is required to have the test work on Fuchsia. Also renames the ...Forked to ...Child to be correct after the change from Multiprocess to MultiprocessExec. Bug: crashpad:196, crashpad:215 Change-Id: I610a7f1e35b6513805c27d9e610f7a9b9820cabc Reviewed-on: https://chromium-review.googlesource.com/892286 Commit-Queue: Scott Graham Reviewed-by: Mark Mentovai Reviewed-by: Joshua Peraza --- util/process/process_memory_test.cc | 271 ++++++++++++++++++---------- 1 file changed, 175 insertions(+), 96 deletions(-) diff --git a/util/process/process_memory_test.cc b/util/process/process_memory_test.cc index ce628e73..28d62d02 100644 --- a/util/process/process_memory_test.cc +++ b/util/process/process_memory_test.cc @@ -147,6 +147,181 @@ TEST(ProcessMemory, ReadChild) { test.RunAgainstChild(); } +constexpr char kConstCharEmpty[] = ""; +constexpr char kConstCharShort[] = "A short const char[]"; + +#define SHORT_LOCAL_STRING "A short local variable char[]" + +std::string MakeLongString() { + std::string long_string; + const size_t kStringLongSize = 4 * getpagesize(); + for (size_t index = 0; index < kStringLongSize; ++index) { + long_string.push_back((index % 255) + 1); + } + EXPECT_EQ(long_string.size(), kStringLongSize); + return long_string; +} + +void DoChildCStringReadTestSetup(const char** const_empty, + const char** const_short, + const char** local_empty, + const char** local_short, + std::string* long_string) { + *const_empty = kConstCharEmpty; + *const_short = kConstCharShort; + *local_empty = ""; + *local_short = SHORT_LOCAL_STRING; + *long_string = MakeLongString(); +} + +CRASHPAD_CHILD_TEST_MAIN(ReadCStringTestChild) { + const char* const_empty; + const char* const_short; + const char* local_empty; + const char* local_short; + std::string long_string; + DoChildCStringReadTestSetup( + &const_empty, &const_short, &local_empty, &local_short, &long_string); + const auto write_address = [](const char* p) { + VMAddress address = FromPointerCast(p); + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), + &address, + sizeof(address)); + }; + write_address(const_empty); + write_address(const_short); + write_address(local_empty); + write_address(local_short); + write_address(long_string.c_str()); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ReadCStringTest : public MultiprocessExec { + public: + ReadCStringTest(bool limit_size) + : MultiprocessExec(), limit_size_(limit_size) { + SetChildTestMainFunction("ReadCStringTestChild"); + } + + void RunAgainstSelf() { + const char* const_empty; + const char* const_short; + const char* local_empty; + const char* local_short; + std::string long_string; + DoChildCStringReadTestSetup( + &const_empty, &const_short, &local_empty, &local_short, &long_string); + DoTest(GetSelfProcess(), + FromPointerCast(const_empty), + FromPointerCast(const_short), + FromPointerCast(local_empty), + FromPointerCast(local_short), + FromPointerCast(long_string.c_str())); + } + void RunAgainstChild() { Run(); } + + private: + void MultiprocessParent() override { +#define DECLARE_AND_READ_ADDRESS(name) \ + VMAddress name; \ + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &name, sizeof(name))); + DECLARE_AND_READ_ADDRESS(const_empty_address); + DECLARE_AND_READ_ADDRESS(const_short_address); + DECLARE_AND_READ_ADDRESS(local_empty_address); + DECLARE_AND_READ_ADDRESS(local_short_address); + DECLARE_AND_READ_ADDRESS(long_string_address); +#undef DECLARE_AND_READ_ADDRESS + + DoTest(ChildProcess(), + const_empty_address, + const_short_address, + local_empty_address, + local_short_address, + long_string_address); + } + + void DoTest(ProcessType process, + VMAddress const_empty_address, + VMAddress const_short_address, + VMAddress local_empty_address, + VMAddress local_short_address, + VMAddress long_string_address) { + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); + + std::string result; + + if (limit_size_) { + ASSERT_TRUE(memory.ReadCStringSizeLimited( + const_empty_address, arraysize(kConstCharEmpty), &result)); + EXPECT_EQ(result, kConstCharEmpty); + + ASSERT_TRUE(memory.ReadCStringSizeLimited( + const_short_address, arraysize(kConstCharShort), &result)); + EXPECT_EQ(result, kConstCharShort); + EXPECT_FALSE(memory.ReadCStringSizeLimited( + const_short_address, arraysize(kConstCharShort) - 1, &result)); + + ASSERT_TRUE( + memory.ReadCStringSizeLimited(local_empty_address, 1, &result)); + EXPECT_EQ(result, ""); + + ASSERT_TRUE(memory.ReadCStringSizeLimited( + local_short_address, strlen(SHORT_LOCAL_STRING) + 1, &result)); + EXPECT_EQ(result, SHORT_LOCAL_STRING); + EXPECT_FALSE(memory.ReadCStringSizeLimited( + local_short_address, strlen(SHORT_LOCAL_STRING), &result)); + + std::string long_string_for_comparison = MakeLongString(); + ASSERT_TRUE(memory.ReadCStringSizeLimited( + long_string_address, long_string_for_comparison.size() + 1, &result)); + EXPECT_EQ(result, long_string_for_comparison); + EXPECT_FALSE(memory.ReadCStringSizeLimited( + long_string_address, long_string_for_comparison.size(), &result)); + } else { + ASSERT_TRUE(memory.ReadCString(const_empty_address, &result)); + EXPECT_EQ(result, kConstCharEmpty); + + ASSERT_TRUE(memory.ReadCString(const_short_address, &result)); + EXPECT_EQ(result, kConstCharShort); + + ASSERT_TRUE(memory.ReadCString(local_empty_address, &result)); + EXPECT_EQ(result, ""); + + ASSERT_TRUE(memory.ReadCString(local_short_address, &result)); + EXPECT_EQ(result, SHORT_LOCAL_STRING); + + ASSERT_TRUE(memory.ReadCString(long_string_address, &result)); + EXPECT_EQ(result, MakeLongString()); + } + } + + const bool limit_size_; + + DISALLOW_COPY_AND_ASSIGN(ReadCStringTest); +}; + +TEST(ProcessMemory, ReadCStringSelf) { + ReadCStringTest test(/* limit_size= */ false); + test.RunAgainstSelf(); +} + +TEST(ProcessMemory, ReadCStringChild) { + ReadCStringTest test(/* limit_size= */ false); + test.RunAgainstChild(); +} + +TEST(ProcessMemory, ReadCStringSizeLimitedSelf) { + ReadCStringTest test(/* limit_size= */ true); + test.RunAgainstSelf(); +} + +TEST(ProcessMemory, ReadCStringSizeLimitedChild) { + ReadCStringTest test(/* limit_size= */ true); + test.RunAgainstChild(); +} + // TODO(scottmg): Need to be ported to MultiprocessExec and not rely on fork(). #if !defined(OS_FUCHSIA) @@ -183,102 +358,6 @@ bool ReadCStringSizeLimited(const ProcessMemory& memory, FromPointerCast(pointer), size, result); } -constexpr char kConstCharEmpty[] = ""; -constexpr char kConstCharShort[] = "A short const char[]"; - -class ReadCStringTest : public TargetProcessTest { - public: - ReadCStringTest(bool limit_size) - : TargetProcessTest(), - member_char_empty_(""), - member_char_short_("A short member char[]"), - limit_size_(limit_size) { - const size_t kStringLongSize = 4 * getpagesize(); - for (size_t index = 0; index < kStringLongSize; ++index) { - string_long_.push_back((index % 255) + 1); - } - EXPECT_EQ(string_long_.size(), kStringLongSize); - } - - private: - void DoTest(pid_t pid) override { - ProcessMemoryNative memory; - ASSERT_TRUE(memory.Initialize(pid)); - - std::string result; - - if (limit_size_) { - ASSERT_TRUE(ReadCStringSizeLimited( - memory, kConstCharEmpty, arraysize(kConstCharEmpty), &result)); - EXPECT_EQ(result, kConstCharEmpty); - - ASSERT_TRUE(ReadCStringSizeLimited( - memory, kConstCharShort, arraysize(kConstCharShort), &result)); - EXPECT_EQ(result, kConstCharShort); - EXPECT_FALSE(ReadCStringSizeLimited( - memory, kConstCharShort, arraysize(kConstCharShort) - 1, &result)); - - ASSERT_TRUE(ReadCStringSizeLimited( - memory, member_char_empty_, strlen(member_char_empty_) + 1, &result)); - EXPECT_EQ(result, member_char_empty_); - - ASSERT_TRUE(ReadCStringSizeLimited( - memory, member_char_short_, strlen(member_char_short_) + 1, &result)); - EXPECT_EQ(result, member_char_short_); - EXPECT_FALSE(ReadCStringSizeLimited( - memory, member_char_short_, strlen(member_char_short_), &result)); - - ASSERT_TRUE(ReadCStringSizeLimited( - memory, string_long_.c_str(), string_long_.size() + 1, &result)); - EXPECT_EQ(result, string_long_); - EXPECT_FALSE(ReadCStringSizeLimited( - memory, string_long_.c_str(), string_long_.size(), &result)); - } else { - ASSERT_TRUE(ReadCString(memory, kConstCharEmpty, &result)); - EXPECT_EQ(result, kConstCharEmpty); - - ASSERT_TRUE(ReadCString(memory, kConstCharShort, &result)); - EXPECT_EQ(result, kConstCharShort); - - ASSERT_TRUE(ReadCString(memory, member_char_empty_, &result)); - EXPECT_EQ(result, member_char_empty_); - - ASSERT_TRUE(ReadCString(memory, member_char_short_, &result)); - EXPECT_EQ(result, member_char_short_); - - ASSERT_TRUE(ReadCString(memory, string_long_.c_str(), &result)); - EXPECT_EQ(result, string_long_); - } - } - - std::string string_long_; - const char* member_char_empty_; - const char* member_char_short_; - const bool limit_size_; - - DISALLOW_COPY_AND_ASSIGN(ReadCStringTest); -}; - -TEST(ProcessMemory, ReadCStringSelf) { - ReadCStringTest test(/* limit_size= */ false); - test.RunAgainstSelf(); -} - -TEST(ProcessMemory, ReadCStringForked) { - ReadCStringTest test(/* limit_size= */ false); - test.RunAgainstForked(); -} - -TEST(ProcessMemory, ReadCStringSizeLimitedSelf) { - ReadCStringTest test(/* limit_size= */ true); - test.RunAgainstSelf(); -} - -TEST(ProcessMemory, ReadCStringSizeLimitedForked) { - ReadCStringTest test(/* limit_size= */ true); - test.RunAgainstForked(); -} - class ReadUnmappedTest : public TargetProcessTest { public: ReadUnmappedTest()