Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(446)

Side by Side Diff: components/policy/core/common/preg_parser.cc

Issue 2791193005: Add fuzzer test for preg_parser (Closed)
Patch Set: Can't char string concat base::FilePath::value()s on Windows since file paths are UTF16 there. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/policy/core/common/preg_parser.h" 5 #include "components/policy/core/common/preg_parser.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <stdint.h> 8 #include <stdint.h>
9 9
10 #include <algorithm> 10 #include <algorithm>
11 #include <functional> 11 #include <functional>
12 #include <iterator> 12 #include <iterator>
13 #include <limits> 13 #include <limits>
14 #include <memory> 14 #include <memory>
15 #include <string> 15 #include <string>
16 #include <utility> 16 #include <utility>
17 #include <vector> 17 #include <vector>
18 18
19 #include "base/files/file_path.h" 19 #include "base/files/file_path.h"
20 #include "base/files/memory_mapped_file.h" 20 #include "base/files/memory_mapped_file.h"
21 #include "base/logging.h" 21 #include "base/logging.h"
22 #include "base/macros.h" 22 #include "base/macros.h"
23 #include "base/memory/ptr_util.h" 23 #include "base/memory/ptr_util.h"
24 #include "base/strings/string16.h" 24 #include "base/strings/string16.h"
25 #include "base/strings/string_split.h" 25 #include "base/strings/string_split.h"
26 #include "base/strings/string_util.h" 26 #include "base/strings/string_util.h"
27 #include "base/strings/stringprintf.h"
27 #include "base/strings/utf_string_conversions.h" 28 #include "base/strings/utf_string_conversions.h"
28 #include "base/sys_byteorder.h" 29 #include "base/sys_byteorder.h"
29 #include "base/values.h" 30 #include "base/values.h"
30 #include "components/policy/core/common/policy_load_status.h"
31 #include "components/policy/core/common/registry_dict.h" 31 #include "components/policy/core/common/registry_dict.h"
32 32
33 #if defined(OS_WIN) 33 #if defined(OS_WIN)
34 #include "windows.h" 34 #include "windows.h"
35 #else 35 #else
36 // Registry data type constants. 36 // Registry data type constants.
37 #define REG_NONE 0 37 #define REG_NONE 0
38 #define REG_SZ 1 38 #define REG_SZ 1
39 #define REG_EXPAND_SZ 2 39 #define REG_EXPAND_SZ 2
40 #define REG_BINARY 3 40 #define REG_BINARY 3
(...skipping 26 matching lines...) Expand all
67 67
68 // Magic strings for the PReg value field to trigger special actions. 68 // Magic strings for the PReg value field to trigger special actions.
69 const char kActionTriggerPrefix[] = "**"; 69 const char kActionTriggerPrefix[] = "**";
70 const char kActionTriggerDeleteValues[] = "deletevalues"; 70 const char kActionTriggerDeleteValues[] = "deletevalues";
71 const char kActionTriggerDel[] = "del."; 71 const char kActionTriggerDel[] = "del.";
72 const char kActionTriggerDelVals[] = "delvals"; 72 const char kActionTriggerDelVals[] = "delvals";
73 const char kActionTriggerDeleteKeys[] = "deletekeys"; 73 const char kActionTriggerDeleteKeys[] = "deletekeys";
74 const char kActionTriggerSecureKey[] = "securekey"; 74 const char kActionTriggerSecureKey[] = "securekey";
75 const char kActionTriggerSoft[] = "soft"; 75 const char kActionTriggerSoft[] = "soft";
76 76
77 // Same as base::EqualsCaseInsensitiveASCII, but with base::string16 args.
78 bool EqualsCaseInsensitiveASCII(const base::string16& a,
79 const base::string16& b) {
80 return base::EqualsCaseInsensitiveASCII(a, b);
81 }
82
77 // Returns the character at |cursor| and increments it, unless the end is here 83 // Returns the character at |cursor| and increments it, unless the end is here
78 // in which case -1 is returned. The calling code must guarantee that 84 // in which case -1 is returned. The calling code must guarantee that
79 // end - *cursor does not overflow ptrdiff_t. 85 // end - *cursor does not overflow ptrdiff_t.
80 int NextChar(const uint8_t** cursor, const uint8_t* end) { 86 int NextChar(const uint8_t** cursor, const uint8_t* end) {
81 // Only read the character if a full base::char16 is available. 87 // Only read the character if a full base::char16 is available.
82 // This comparison makes sure no overflow can happen. 88 // This comparison makes sure no overflow can happen.
83 if (*cursor >= end || 89 if (*cursor >= end ||
84 end - *cursor < static_cast<ptrdiff_t>(sizeof(base::char16))) 90 end - *cursor < static_cast<ptrdiff_t>(sizeof(base::char16)))
85 return -1; 91 return -1;
86 92
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
171 case REG_FULL_RESOURCE_DESCRIPTOR: 177 case REG_FULL_RESOURCE_DESCRIPTOR:
172 case REG_RESOURCE_REQUIREMENTS_LIST: 178 case REG_RESOURCE_REQUIREMENTS_LIST:
173 case REG_QWORD_LITTLE_ENDIAN: 179 case REG_QWORD_LITTLE_ENDIAN:
174 default: 180 default:
175 LOG(ERROR) << "Unsupported registry data type " << type; 181 LOG(ERROR) << "Unsupported registry data type " << type;
176 } 182 }
177 183
178 return false; 184 return false;
179 } 185 }
180 186
187 // Splits a registry key A\B\C into parts {A, B, C}.
188 std::vector<base::string16> SplitKey(const base::string16& key_name) {
189 return base::SplitString(key_name, kRegistryPathSeparator,
190 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
191 }
192
181 // Adds |value| and |data| to |dict| or an appropriate sub-dictionary indicated 193 // Adds |value| and |data| to |dict| or an appropriate sub-dictionary indicated
182 // by |key_name|. Creates sub-dictionaries if necessary. Also handles special 194 // by |key_parts|. Creates sub-dictionaries if necessary. Also handles special
183 // action triggers, see |kActionTrigger*|, that can, for instance, remove an 195 // action triggers, see |kActionTrigger*|, that can, for instance, remove an
184 // existing value. 196 // existing value.
185 void HandleRecord(const base::string16& key_name, 197 void HandleRecord(const std::vector<base::string16>& key_parts,
186 const base::string16& value, 198 const base::string16& value,
187 uint32_t type, 199 uint32_t type,
188 const std::vector<uint8_t>& data, 200 const std::vector<uint8_t>& data,
189 RegistryDict* dict) { 201 RegistryDict* dict) {
190 // Locate/create the dictionary to place the value in. 202 // Locate/create the dictionary to place the value in.
191 std::vector<base::string16> path; 203 std::vector<base::string16> path;
192 204
193 for (const base::string16& entry : 205 for (const base::string16& entry : key_parts) {
194 base::SplitString(key_name, kRegistryPathSeparator,
195 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
196 if (entry.empty()) 206 if (entry.empty())
197 continue; 207 continue;
198 const std::string name = base::UTF16ToUTF8(entry); 208 const std::string name = base::UTF16ToUTF8(entry);
199 RegistryDict* subdict = dict->GetKey(name); 209 RegistryDict* subdict = dict->GetKey(name);
200 if (!subdict) { 210 if (!subdict) {
201 subdict = new RegistryDict(); 211 subdict = new RegistryDict();
202 dict->SetKey(name, base::WrapUnique(subdict)); 212 dict->SetKey(name, base::WrapUnique(subdict));
203 } 213 }
204 dict = subdict; 214 dict = subdict;
205 } 215 }
206 216
207 if (value.empty()) 217 if (value.empty())
208 return; 218 return;
209 219
210 std::string value_name(base::UTF16ToUTF8(value)); 220 std::string value_name(base::UTF16ToUTF8(value));
211 if (!base::StartsWith(value_name, kActionTriggerPrefix, 221 if (!base::StartsWith(value_name, kActionTriggerPrefix,
212 base::CompareCase::SENSITIVE)) { 222 base::CompareCase::SENSITIVE)) {
213 std::unique_ptr<base::Value> value; 223 std::unique_ptr<base::Value> value;
214 if (DecodePRegValue(type, data, &value)) 224 if (DecodePRegValue(type, data, &value))
215 dict->SetValue(value_name, std::move(value)); 225 dict->SetValue(value_name, std::move(value));
216 return; 226 return;
217 } 227 }
218 228
219 std::string action_trigger(base::ToLowerASCII(value_name.substr( 229 std::string action_trigger(base::ToLowerASCII(
220 arraysize(kActionTriggerPrefix) - 1))); 230 value_name.substr(arraysize(kActionTriggerPrefix) - 1)));
221 if (action_trigger == kActionTriggerDeleteValues) { 231 if (action_trigger == kActionTriggerDeleteValues) {
222 for (const std::string& value : 232 for (const std::string& value :
223 base::SplitString(DecodePRegStringValue(data), ";", 233 base::SplitString(DecodePRegStringValue(data), ";",
224 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) 234 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY))
225 dict->RemoveValue(value); 235 dict->RemoveValue(value);
226 } else if (base::StartsWith(action_trigger, kActionTriggerDeleteKeys, 236 } else if (base::StartsWith(action_trigger, kActionTriggerDeleteKeys,
227 base::CompareCase::SENSITIVE)) { 237 base::CompareCase::SENSITIVE)) {
228 for (const std::string& key : 238 for (const std::string& key :
229 base::SplitString(DecodePRegStringValue(data), ";", 239 base::SplitString(DecodePRegStringValue(data), ";",
230 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) 240 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY))
231 dict->RemoveKey(key); 241 dict->RemoveKey(key);
232 } else if (base::StartsWith(action_trigger, kActionTriggerDel, 242 } else if (base::StartsWith(action_trigger, kActionTriggerDel,
233 base::CompareCase::SENSITIVE)) { 243 base::CompareCase::SENSITIVE)) {
234 dict->RemoveValue( 244 dict->RemoveValue(value_name.substr(arraysize(kActionTriggerPrefix) - 1 +
235 value_name.substr(arraysize(kActionTriggerPrefix) - 1 + 245 arraysize(kActionTriggerDel) - 1));
236 arraysize(kActionTriggerDel) - 1));
237 } else if (base::StartsWith(action_trigger, kActionTriggerDelVals, 246 } else if (base::StartsWith(action_trigger, kActionTriggerDelVals,
238 base::CompareCase::SENSITIVE)) { 247 base::CompareCase::SENSITIVE)) {
239 // Delete all values. 248 // Delete all values.
240 dict->ClearValues(); 249 dict->ClearValues();
241 } else if (base::StartsWith(action_trigger, kActionTriggerSecureKey, 250 } else if (base::StartsWith(action_trigger, kActionTriggerSecureKey,
242 base::CompareCase::SENSITIVE) || 251 base::CompareCase::SENSITIVE) ||
243 base::StartsWith(action_trigger, kActionTriggerSoft, 252 base::StartsWith(action_trigger, kActionTriggerSoft,
244 base::CompareCase::SENSITIVE)) { 253 base::CompareCase::SENSITIVE)) {
245 // Doesn't affect values. 254 // Doesn't affect values.
246 } else { 255 } else {
247 LOG(ERROR) << "Bad action trigger " << value_name; 256 LOG(ERROR) << "Bad action trigger " << value_name;
248 } 257 }
249 } 258 }
250 259
251 } // namespace 260 } // namespace
252 261
253 namespace policy { 262 namespace policy {
254 namespace preg_parser { 263 namespace preg_parser {
255 264
256 const char kPRegFileHeader[8] = 265 const char kPRegFileHeader[8] = {'P', 'R', 'e', 'g',
257 { 'P', 'R', 'e', 'g', '\x01', '\x00', '\x00', '\x00' }; 266 '\x01', '\x00', '\x00', '\x00'};
258 267
259 bool ReadFile(const base::FilePath& file_path, 268 bool ReadFile(const base::FilePath& file_path,
260 const base::string16& root, 269 const base::string16& root,
261 RegistryDict* dict, 270 RegistryDict* dict,
262 PolicyLoadStatusSample* status) { 271 PolicyLoadStatusSample* status_sample) {
263 base::MemoryMappedFile mapped_file; 272 base::MemoryMappedFile mapped_file;
264 if (!mapped_file.Initialize(file_path) || !mapped_file.IsValid()) { 273 if (!mapped_file.Initialize(file_path) || !mapped_file.IsValid()) {
265 PLOG(ERROR) << "Failed to map " << file_path.value(); 274 PLOG(ERROR) << "Failed to map " << file_path.value();
266 status->Add(POLICY_LOAD_STATUS_READ_ERROR); 275 status_sample->Add(POLICY_LOAD_STATUS_READ_ERROR);
267 return false; 276 return false;
268 } 277 }
269 278
270 if (mapped_file.length() > kMaxPRegFileSize) { 279 PolicyLoadStatus status = POLICY_LOAD_STATUS_SIZE;
271 LOG(ERROR) << "PReg file " << file_path.value() << " too large: " 280 bool res =
272 << mapped_file.length(); 281 ReadData(mapped_file.data(), mapped_file.length(), root, dict, &status,
273 status->Add(POLICY_LOAD_STATUS_TOO_BIG); 282 base::StringPrintf("file '%s'", file_path.value().c_str()));
283 if (!res) {
284 DCHECK(status != POLICY_LOAD_STATUS_SIZE);
285 status_sample->Add(status);
286 }
287 return res;
288 }
289
290 POLICY_EXPORT bool ReadData(const uint8_t* data,
291 size_t data_size,
292 const base::string16& root,
293 RegistryDict* dict,
294 PolicyLoadStatus* status,
295 const std::string& debug_name) {
296 DCHECK(status);
297
298 // Check data size.
299 if (data_size > kMaxPRegFileSize) {
300 LOG(ERROR) << "PReg " << debug_name << " too large: " << data_size;
301 *status = POLICY_LOAD_STATUS_TOO_BIG;
274 return false; 302 return false;
275 } 303 }
276 304
277 // Check the header. 305 // Check the header.
278 const int kHeaderSize = arraysize(kPRegFileHeader); 306 const int kHeaderSize = arraysize(kPRegFileHeader);
279 if (mapped_file.length() < kHeaderSize || 307 if (!data || data_size < kHeaderSize ||
280 memcmp(kPRegFileHeader, mapped_file.data(), kHeaderSize) != 0) { 308 memcmp(kPRegFileHeader, data, kHeaderSize) != 0) {
281 LOG(ERROR) << "Bad policy file " << file_path.value(); 309 LOG(ERROR) << "Bad PReg " << debug_name;
282 status->Add(POLICY_LOAD_STATUS_PARSE_ERROR); 310 *status = POLICY_LOAD_STATUS_PARSE_ERROR;
283 return false; 311 return false;
284 } 312 }
285 313
286 // Parse file contents, which is UCS-2 and little-endian. The latter I 314 // Split |root| into parts. It is important that we don't do a simple
315 // StartsWith check for |root| in the |key_name| (see below) since then root =
316 // "someroot" would trigger in key_name = "somerooting\data".
Mattias Nissler (ping if slow) 2017/04/07 08:13:26 Alternatively you could enforce that the prefix we
ljusten (tachyonic) 2017/04/10 10:22:51 I thought about that, I was a bit scared by the nu
317 const std::vector<base::string16> root_parts = SplitKey(root);
318
319 // Parse data, which is expected to be UCS-2 and little-endian. The latter I
287 // couldn't find documentation on, but the example I saw were all 320 // couldn't find documentation on, but the example I saw were all
288 // little-endian. It'd be interesting to check on big-endian hardware. 321 // little-endian. It'd be interesting to check on big-endian hardware.
289 const uint8_t* cursor = mapped_file.data() + kHeaderSize; 322 const uint8_t* cursor = data + kHeaderSize;
290 const uint8_t* end = mapped_file.data() + mapped_file.length(); 323 const uint8_t* end = data + data_size;
291 while (true) { 324 while (true) {
292 if (cursor == end) 325 if (cursor == end)
293 return true; 326 return true;
294 327
295 if (NextChar(&cursor, end) != kDelimBracketOpen) 328 if (NextChar(&cursor, end) != kDelimBracketOpen)
296 break; 329 break;
297 330
298 // Read the record fields. 331 // Read the record fields.
299 base::string16 key_name; 332 base::string16 key_name;
300 base::string16 value; 333 base::string16 value;
(...skipping 29 matching lines...) Expand all
330 data.resize(size); 363 data.resize(size);
331 if (!ReadFieldBinary(&cursor, end, size, data.data())) 364 if (!ReadFieldBinary(&cursor, end, size, data.data()))
332 break; 365 break;
333 current = NextChar(&cursor, end); 366 current = NextChar(&cursor, end);
334 } 367 }
335 368
336 if (current != kDelimBracketClose) 369 if (current != kDelimBracketClose)
337 break; 370 break;
338 371
339 // Process the record if it is within the |root| subtree. 372 // Process the record if it is within the |root| subtree.
340 if (base::StartsWith(key_name, root, base::CompareCase::INSENSITIVE_ASCII)) 373 std::vector<base::string16> key_parts = SplitKey(key_name);
341 HandleRecord(key_name.substr(root.size()), value, type, data, dict); 374 if (key_parts.size() >= root_parts.size() &&
375 std::equal(root_parts.begin(), root_parts.end(), key_parts.begin(),
376 &EqualsCaseInsensitiveASCII)) {
377 key_parts.erase(key_parts.begin(), key_parts.begin() + root_parts.size());
378 HandleRecord(key_parts, value, type, data, dict);
379 }
342 } 380 }
343 381
344 LOG(ERROR) << "Error parsing " << file_path.value() << " at offset " 382 LOG(ERROR) << "Error parsing PReg " << debug_name << " at offset "
345 << reinterpret_cast<const uint8_t*>(cursor - 1) - 383 << (reinterpret_cast<const uint8_t*>(cursor - 1) - data);
346 mapped_file.data(); 384 *status = POLICY_LOAD_STATUS_PARSE_ERROR;
347 status->Add(POLICY_LOAD_STATUS_PARSE_ERROR);
348 return false; 385 return false;
349 } 386 }
350 387
351 } // namespace preg_parser 388 } // namespace preg_parser
352 } // namespace policy 389 } // namespace policy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698