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/kasko_reporter.h"
16 :
17 : #include "base/file_version_info.h"
18 : #include "base/path_service.h"
19 : #include "base/version.h"
20 : #include "base/files/file_path.h"
21 : #include "base/strings/string_util.h"
22 : #include "base/strings/utf_string_conversions.h"
23 : #include "syzygy/kasko/api/client.h"
24 :
25 : namespace agent {
26 : namespace asan {
27 :
28 : static_assert(kasko::api::kProtobufStreamType ==
29 : ReporterInterface::kCrashdataProtobufStreamType,
30 : "protobuf stream type id mismatch");
31 :
32 : // Define required export names.
33 : const char* reporters::KaskoReporter::ReportCrashWithProtobuf::name_ =
34 : "ReportCrashWithProtobuf";
35 : const char* reporters::KaskoReporter::
36 : ReportCrashWithProtobufAndMemoryRanges::name_ =
37 : "ReportCrashWithProtobufAndMemoryRanges";
38 : const char* reporters::KaskoReporter:: SetCrashKeyValueImpl::name_ =
39 : "SetCrashKeyValueImpl";
40 :
41 : namespace reporters {
42 :
43 : // static
44 E : std::unique_ptr<KaskoReporter> KaskoReporter::Create() {
45 : // Initialize the required reporter functions
46 E : KaskoFunctions kasko_functions;
47 E : kasko_functions.set_crash_key_value_impl.Lookup();
48 E : kasko_functions.report_crash_with_protobuf.Lookup();
49 E : kasko_functions.report_crash_with_protobuf_and_memory_ranges.Lookup();
50 E : if (!AreValid(kasko_functions))
51 E : return nullptr;
52 :
53 E : return std::unique_ptr<KaskoReporter>(new KaskoReporter(kasko_functions));
54 E : }
55 :
56 : // static
57 E : bool KaskoReporter::AreValid(const KaskoFunctions& kasko_functions) {
58 : // The crash key function and at least one reporting function must be
59 : // present.
60 E : if (!kasko_functions.set_crash_key_value_impl.IsValid())
61 E : return false;
62 E : return (kasko_functions.report_crash_with_protobuf.IsValid() ||
63 : kasko_functions.report_crash_with_protobuf_and_memory_ranges.IsValid());
64 E : }
65 :
66 E : const char* KaskoReporter::GetName() const {
67 E : return "KaskoReporter";
68 E : }
69 :
70 E : uint32_t KaskoReporter::GetFeatures() const {
71 E : uint32_t features = FEATURE_CRASH_KEYS | FEATURE_CUSTOM_STREAMS;
72 E : if (kasko_functions_.report_crash_with_protobuf_and_memory_ranges.IsValid())
73 E : features |= FEATURE_MEMORY_RANGES;
74 E : if (SupportsEarlyCrashKeys())
75 i : features |= FEATURE_EARLY_CRASH_KEYS;
76 E : return features;
77 E : }
78 :
79 : bool KaskoReporter::SetCrashKey(base::StringPiece key,
80 E : base::StringPiece value) {
81 E : DCHECK(kasko_functions_.set_crash_key_value_impl.IsValid());
82 :
83 E : std::wstring wkey = base::UTF8ToWide(key);
84 E : std::wstring wvalue = base::UTF8ToWide(value);
85 E : kasko_functions_.set_crash_key_value_impl.Run(wkey.c_str(), wvalue.c_str());
86 E : return true;
87 E : }
88 :
89 E : bool KaskoReporter::SetMemoryRanges(const MemoryRanges& memory_ranges) {
90 : // This is only supported if the appropriate reporting function was found.
91 E : if (!kasko_functions_.report_crash_with_protobuf_and_memory_ranges.IsValid())
92 E : return false;
93 :
94 : // Convert the memory ranges to the null terminated format Kasko expects.
95 E : range_bases_.resize(memory_ranges.size() + 1);
96 E : range_lengths_.resize(memory_ranges.size() + 1);
97 E : for (size_t i = 0; i < memory_ranges.size(); ++i) {
98 E : range_bases_[i] = memory_ranges[i].first;
99 E : range_lengths_[i] = memory_ranges[i].second;
100 E : }
101 E : range_bases_.back() = nullptr;
102 E : range_lengths_.back() = 0;
103 E : return true;
104 E : }
105 :
106 : bool KaskoReporter::SetCustomStream(uint32_t stream_type,
107 : const uint8_t* stream_data,
108 E : size_t stream_length) {
109 : // Only support setting the Kasko stream type.
110 E : if (stream_type != kCrashdataProtobufStreamType)
111 E : return false;
112 E : protobuf_.assign(reinterpret_cast<const char*>(stream_data), stream_length);
113 E : return true;
114 E : }
115 :
116 : // Crashes the running process and sends a crash report.
117 E : void KaskoReporter::DumpAndCrash(EXCEPTION_POINTERS* exception_pointers) {
118 : // Prefer to use the memory ranges version.
119 E : if (kasko_functions_.report_crash_with_protobuf_and_memory_ranges.IsValid()) {
120 E : kasko_functions_.report_crash_with_protobuf_and_memory_ranges.Run(
121 : exception_pointers, protobuf_.c_str(), protobuf_.size(),
122 : range_bases_.data(), range_lengths_.data());
123 E : } else {
124 E : DCHECK(kasko_functions_.report_crash_with_protobuf.IsValid());
125 E : kasko_functions_.report_crash_with_protobuf.Run(
126 : exception_pointers, protobuf_.c_str(), protobuf_.size());
127 : }
128 :
129 : // The crash function shouldn't return, but putting a NOTREACHED here makes
130 : // this function difficult to test.
131 E : }
132 :
133 E : bool KaskoReporter::DumpWithoutCrash(const CONTEXT& context) {
134 : // This functionality is not supported in Kasko.
135 E : return false;
136 E : }
137 :
138 : // static
139 E : bool KaskoReporter::SupportsEarlyCrashKeys() {
140 : // Whether or not this is safe to do is really dependent on the crash key
141 : // system as implemented in a given binary. Kasko doesn't provide its own,
142 : // but rather relies on that provided by the instrumented binary itself.
143 : // Binaries need to be evaluated individually and added to this whitelist
144 : // explicitly if early crash key support is required.
145 : //
146 : // This whole thing becomes a moot point when using Crashpad, as it provides
147 : // a uniform and safe early crash key mechanism. Moving forward, all Chromium
148 : // projects will be using it.
149 :
150 : // The process needs to be an instance of "chrome.exe".
151 E : base::FilePath path;
152 E : if (!PathService::Get(base::FILE_EXE, &path))
153 i : return false;
154 E : if (!base::EqualsCaseInsensitiveASCII(path.BaseName().value(),
155 : L"chrome.exe")) {
156 E : return false;
157 : }
158 :
159 i : std::unique_ptr<FileVersionInfo> version_info(
160 : FileVersionInfo::CreateFileVersionInfo(path));
161 i : if (!version_info.get())
162 i : return false;
163 :
164 : // The version string may have the format "0.1.2.3 (baadf00d)". The
165 : // revision hash must be stripped in order to use base::Version.
166 i : std::string v = base::WideToUTF8(version_info->product_version());
167 i : size_t offset = v.find_first_not_of("0123456789.");
168 i : if (offset != v.npos)
169 i : v.resize(offset);
170 :
171 : // Ensure the version is sufficiently new. Prior to M36 the crashkey
172 : // implementation used a structure that wasn't ready or safe to use before
173 : // all initializers had run. Afterwards it uses a global static structure so
174 : // crash key writing early on is safe.
175 i : base::Version version(v);
176 i : if (!version.IsValid())
177 i : return false;
178 i : if (version < base::Version("36.0.0.0"))
179 i : return false;
180 :
181 i : return true;
182 E : }
183 :
184 : } // namespace reporters
185 : } // namespace asan
186 : } // namespace agent
|