1 : // Copyright 2016 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/agent/asan/reporters/crashpad_reporter.h"
16 :
17 : #include <algorithm>
18 :
19 : #include "base/environment.h"
20 : #include "base/strings/utf_string_conversions.h"
21 : #include "client/crashpad_client.h"
22 :
23 : namespace agent {
24 : namespace asan {
25 : namespace reporters {
26 :
27 : namespace {
28 :
29 : // The name of the environment variable that holds the crashpad pipe name.
30 : const char kCrashpadPipeNameVar[] = "CHROME_CRASHPAD_PIPE_NAME";
31 :
32 : // The crashpad client. This is used for communicating with the crashpad
33 : // process via IPC.
34 E : crashpad::CrashpadClient g_crashpad_client;
35 :
36 : // Used for establishing Crashpad IPC channels. This is racy, but the IPC
37 : // mechanism ensures everyone will get the same results and that it's
38 : // inherently safe. Barring people changing the environment variable between
39 : // calls. So, to be completely sure bring your own synchronization.
40 : // NOTE: This entire mechanism is... ugly. It relies on very specific knowledge
41 : // of how Chrome interacts with its instance of a Crashpad handler, and it
42 : // doesn't generalize to other clients. Moving forward we will be adding a
43 : // generic callback mechanism for instrumented clients to inform the RTL of the
44 : // crash handler to use.
45 E : bool EnsureCrashpadConnected() {
46 : static bool initialized = false;
47 : static bool crashpad_present = false;
48 :
49 : // Only initialize once.
50 E : if (initialized)
51 E : return crashpad_present;
52 E : initialized = true;
53 :
54 : // Get the name of the crashpad endpoint, failing if none exists.
55 E : std::unique_ptr<base::Environment> env(base::Environment::Create());
56 E : std::string pipe_name;
57 E : if (!env->GetVar(kCrashpadPipeNameVar, &pipe_name))
58 E : return false;
59 i : std::wstring pipe_name_w = base::UTF8ToWide(pipe_name);
60 :
61 : // Initialize the crashpad client.
62 i : if (!g_crashpad_client.SetHandlerIPCPipe(pipe_name_w))
63 i : return false;
64 i : if (!g_crashpad_client.UseHandler())
65 i : return false;
66 :
67 i : crashpad_present = true;
68 i : return true;
69 E : }
70 :
71 : } // namespace
72 :
73 : const char CrashpadReporter::kName[] = "CrashpadReporter";
74 :
75 : // static
76 E : std::unique_ptr<CrashpadReporter> CrashpadReporter::Create() {
77 : // Create a crashpad reporter only if a crashpad instance is running for this
78 : // process.
79 E : if (!EnsureCrashpadConnected())
80 E : return nullptr;
81 :
82 i : auto crashpad_info = crashpad::CrashpadInfo::GetCrashpadInfo();
83 i : return std::unique_ptr<CrashpadReporter>(new CrashpadReporter(crashpad_info));
84 E : }
85 :
86 E : const char* CrashpadReporter::GetName() const {
87 E : return kName;
88 E : }
89 :
90 E : uint32_t CrashpadReporter::GetFeatures() const {
91 E : return FEATURE_CRASH_KEYS | FEATURE_EARLY_CRASH_KEYS |
92 : FEATURE_MEMORY_RANGES | FEATURE_CUSTOM_STREAMS |
93 : FEATURE_DUMP_WITHOUT_CRASH;
94 E : }
95 :
96 : bool CrashpadReporter::SetCrashKey(base::StringPiece key,
97 E : base::StringPiece value) {
98 E : DCHECK_NE(reinterpret_cast<crashpad::SimpleStringDictionary*>(nullptr),
99 E : crash_keys_.get());
100 :
101 : // StringPiece's aren't necessarily null terminated, so convert to
102 : // std::string first.
103 E : std::string k = key.as_string();
104 :
105 : // SetKeyValue fails silently when the dictionary is full. If we're out of
106 : // entries fail if this is a new key.
107 E : if (crash_keys_->GetCount() == crash_keys_->num_entries &&
108 : crash_keys_->GetValueForKey(k.c_str()) == nullptr) {
109 E : return false;
110 : }
111 :
112 : // Set the key if there's room.
113 E : std::string v = value.as_string();
114 E : crash_keys_->SetKeyValue(k.c_str(), v.c_str());
115 E : return true;
116 E : }
117 :
118 E : bool CrashpadReporter::SetMemoryRanges(const MemoryRanges& memory_ranges) {
119 E : auto crashpad_info = crashpad::CrashpadInfo::GetCrashpadInfo();
120 E : if (!crashpad_info)
121 i : return false;
122 :
123 : // Create a local bag of address ranges and populate it.
124 E : std::unique_ptr<crashpad::SimpleAddressRangeBag> ranges(
125 : new crashpad::SimpleAddressRangeBag());
126 :
127 : // Copy over as many ranges as will fit in the constrained
128 : // SimpleAddressRangeBag.
129 E : size_t count = std::min(memory_ranges.size(), ranges->num_entries);
130 E : for (size_t i = 0; i < count; ++i) {
131 E : const auto& range = memory_ranges[i];
132 E : ranges->Insert(crashpad::CheckedRange<uint64_t>(
133 : reinterpret_cast<uint32_t>(range.first), range.second));
134 E : }
135 :
136 : // Swap out the old bag for the new.
137 E : crash_ranges_.reset(ranges.release());
138 E : crashpad_info->set_extra_memory_ranges(crash_ranges_.get());
139 :
140 : // Return success only if all of the ranges were set.
141 E : return count == memory_ranges.size();
142 E : }
143 :
144 : bool CrashpadReporter::SetCustomStream(uint32_t stream_type,
145 : const uint8_t* stream_data,
146 i : size_t stream_length) {
147 i : auto crashpad_info = crashpad::CrashpadInfo::GetCrashpadInfo();
148 i : if (!crashpad_info)
149 i : return false;
150 i : crashpad_info->AddUserDataMinidumpStream(
151 : stream_type, stream_data, stream_length);
152 i : return true;
153 i : }
154 :
155 : // Crashes the running process and sends a crash report.
156 i : void CrashpadReporter::DumpAndCrash(EXCEPTION_POINTERS* exception_pointers) {
157 i : g_crashpad_client.DumpAndCrash(exception_pointers);
158 :
159 : // The crash function shouldn't return, but putting a NOTREACHED here makes
160 : // this function difficult to test.
161 i : }
162 :
163 i : bool CrashpadReporter::DumpWithoutCrash(const CONTEXT& context) {
164 i : g_crashpad_client.DumpWithoutCrash(context);
165 i : return true;
166 i : }
167 :
168 : CrashpadReporter::CrashpadReporter(crashpad::CrashpadInfo* crashpad_info)
169 E : : crashpad_info_(crashpad_info) {
170 E : crash_keys_.reset(new crashpad::SimpleStringDictionary());
171 :
172 : // Initialize the crashpad info struct. Limit indirectly referenced memory to
173 : // a maximum of 1MB, so that crash reports come in at around 1.5-1.7MB. This
174 : // is similar to the size of SyzyAsan crash reports generated by MS tools.
175 E : crashpad_info->set_crashpad_handler_behavior(
176 : crashpad::TriState::kEnabled);
177 E : crashpad_info->set_system_crash_reporter_forwarding(
178 : crashpad::TriState::kDisabled);
179 E : crashpad_info->set_gather_indirectly_referenced_memory(
180 : crashpad::TriState::kEnabled, 1 * 1024 * 1024);
181 E : crashpad_info->set_simple_annotations(crash_keys_.get());
182 E : }
183 :
184 : } // namespace reporters
185 : } // namespace asan
186 : } // namespace agent
|