2023-01-28 04:54:20 +00:00
|
|
|
// Copyright 2017 The Crashpad Authors
|
2022-04-02 01:21:55 +00:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
#include <windows.h>
|
|
|
|
#include <dbghelp.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <map>
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "base/files/file_path.h"
|
|
|
|
#include "client/crash_report_database.h"
|
|
|
|
#include "client/crashpad_client.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "minidump/test/minidump_file_writer_test_util.h"
|
|
|
|
#include "test/test_paths.h"
|
|
|
|
#include "test/win/win_multiprocess_with_temp_dir.h"
|
|
|
|
#include "util/file/file_reader.h"
|
|
|
|
#include "util/misc/capture_context.h"
|
|
|
|
|
|
|
|
namespace crashpad {
|
|
|
|
namespace test {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
constexpr DWORD kExpectedExitCode = 0x1CEB00DA;
|
|
|
|
|
|
|
|
void StartAndCrashWithExtendedHandler(const base::FilePath& temp_dir) {
|
|
|
|
base::FilePath handler_path = TestPaths::BuildArtifact(
|
|
|
|
L"handler", L"extended_handler", TestPaths::FileType::kExecutable);
|
|
|
|
|
|
|
|
CrashpadClient client;
|
|
|
|
ASSERT_TRUE(client.StartHandler(handler_path,
|
|
|
|
temp_dir,
|
|
|
|
base::FilePath(),
|
|
|
|
"",
|
|
|
|
std::map<std::string, std::string>(),
|
|
|
|
std::vector<std::string>(),
|
|
|
|
false,
|
|
|
|
false));
|
|
|
|
|
|
|
|
// It appears that the Google Test fixture will catch and handle exceptions
|
|
|
|
// from here. Hence the fabricated crash in favor of raising an exception.
|
|
|
|
EXCEPTION_RECORD exception_record = {kExpectedExitCode,
|
|
|
|
EXCEPTION_NONCONTINUABLE};
|
|
|
|
CONTEXT context;
|
|
|
|
CaptureContext(&context);
|
|
|
|
EXCEPTION_POINTERS exception_pointers = {&exception_record, &context};
|
|
|
|
CrashpadClient::DumpAndCrash(&exception_pointers);
|
|
|
|
}
|
|
|
|
|
|
|
|
class CrashWithExtendedHandler final : public WinMultiprocessWithTempDir {
|
|
|
|
public:
|
|
|
|
CrashWithExtendedHandler() : WinMultiprocessWithTempDir() {}
|
|
|
|
~CrashWithExtendedHandler() {}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void ValidateGeneratedDump();
|
|
|
|
|
|
|
|
void WinMultiprocessParent() override {
|
|
|
|
SetExpectedChildExitCode(kExpectedExitCode);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WinMultiprocessChild() override {
|
|
|
|
StartAndCrashWithExtendedHandler(GetTempDirPath());
|
|
|
|
}
|
|
|
|
|
|
|
|
void WinMultiprocessParentAfterChild(HANDLE child) override {
|
|
|
|
// At this point the child has exited, which means the crash report should
|
|
|
|
// have been written.
|
|
|
|
ValidateGeneratedDump();
|
|
|
|
|
|
|
|
// Delegate the cleanup to the superclass.
|
|
|
|
WinMultiprocessWithTempDir::WinMultiprocessParentAfterChild(child);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void CrashWithExtendedHandler::ValidateGeneratedDump() {
|
|
|
|
// Open the database and find the sole dump that should have been created.
|
|
|
|
std::unique_ptr<CrashReportDatabase> database(
|
|
|
|
CrashReportDatabase::Initialize(GetTempDirPath()));
|
|
|
|
ASSERT_TRUE(database);
|
|
|
|
|
|
|
|
std::vector<CrashReportDatabase::Report> reports;
|
|
|
|
ASSERT_EQ(database->GetPendingReports(&reports),
|
|
|
|
CrashReportDatabase::kNoError);
|
|
|
|
ASSERT_EQ(reports.size(), 1u);
|
|
|
|
|
|
|
|
// Open the dump and validate that it has the extension stream with the
|
|
|
|
// expected contents.
|
|
|
|
FileReader reader;
|
|
|
|
ASSERT_TRUE(reader.Open(reports[0].file_path));
|
|
|
|
|
|
|
|
// Read the header.
|
|
|
|
MINIDUMP_HEADER header = {};
|
|
|
|
ASSERT_TRUE(reader.ReadExactly(&header, sizeof(header)));
|
|
|
|
|
|
|
|
// Read the directory.
|
|
|
|
std::vector<MINIDUMP_DIRECTORY> directory(header.NumberOfStreams);
|
|
|
|
ASSERT_TRUE(reader.SeekSet(header.StreamDirectoryRva));
|
|
|
|
ASSERT_TRUE(reader.ReadExactly(directory.data(),
|
|
|
|
directory.size() * sizeof(directory[0])));
|
|
|
|
|
|
|
|
// Search for the extension stream.
|
|
|
|
size_t found_extension_streams = 0;
|
|
|
|
for (const auto& entry : directory) {
|
|
|
|
if (entry.StreamType == 0xCAFEBABE) {
|
|
|
|
++found_extension_streams;
|
|
|
|
|
|
|
|
ASSERT_TRUE(reader.SeekSet(entry.Location.Rva));
|
|
|
|
|
|
|
|
std::vector<char> data;
|
|
|
|
data.resize(entry.Location.DataSize);
|
|
|
|
|
|
|
|
ASSERT_TRUE(reader.ReadExactly(data.data(), data.size()));
|
|
|
|
|
|
|
|
static constexpr char kExpectedData[] = "Injected extension stream!";
|
|
|
|
EXPECT_EQ(memcmp(kExpectedData, data.data(), sizeof(kExpectedData)), 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPECT_EQ(found_extension_streams, 1u);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(ADDRESS_SANITIZER)
|
|
|
|
// https://crbug.com/845011
|
|
|
|
#define MAYBE_ExtensibilityCalloutsWork DISABLED_ExtensibilityCalloutsWork
|
|
|
|
#else
|
|
|
|
#define MAYBE_ExtensibilityCalloutsWork ExtensibilityCalloutsWork
|
|
|
|
#endif
|
|
|
|
TEST(CrashpadHandler, MAYBE_ExtensibilityCalloutsWork) {
|
|
|
|
WinMultiprocessWithTempDir::Run<CrashWithExtendedHandler>();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
} // namespace test
|
|
|
|
} // namespace crashpad
|