blob: af2081d3c92a9765eef96dda69584330d3d06e7d [file] [log] [blame]
[email protected]44106182012-04-06 03:53:021// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]0716cba2009-12-17 12:37:582// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
[email protected]c2a18492011-10-05 13:22:504//
5// This file contains intentional memory errors, some of which may lead to
6// crashes if the test is ran without special memory testing tools. We use these
7// errors to verify the sanity of the tools.
[email protected]0716cba2009-12-17 12:37:588
avi9b6f42932015-12-26 22:15:149#include <stddef.h>
10
[email protected]f49ecd8f2011-05-10 18:03:3411#include "base/atomicops.h"
Vlad Tsyrkleviche0408ff2017-10-13 19:35:2412#include "base/cfi_flags.h"
[email protected]b4b34792014-06-14 08:29:3713#include "base/debug/asan_invalid_access.h"
14#include "base/debug/profiler.h"
[email protected]495cad92013-07-18 08:12:4015#include "base/message_loop/message_loop.h"
[email protected]ee857512010-05-14 08:24:4216#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
[email protected]34b99632011-01-01 01:01:0617#include "base/threading/thread.h"
avi9b6f42932015-12-26 22:15:1418#include "build/build_config.h"
[email protected]0716cba2009-12-17 12:37:5819#include "testing/gtest/include/gtest/gtest.h"
20
[email protected]ce072a72010-12-31 20:02:1621namespace base {
22
[email protected]0716cba2009-12-17 12:37:5823namespace {
24
[email protected]f49ecd8f2011-05-10 18:03:3425const base::subtle::Atomic32 kMagicValue = 42;
[email protected]0716cba2009-12-17 12:37:5826
[email protected]c2a18492011-10-05 13:22:5027// Helper for memory accesses that can potentially corrupt memory or cause a
28// crash during a native run.
[email protected]aee2f332014-03-27 15:08:0429#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
[email protected]9a4f6182012-10-24 14:11:0230#if defined(OS_IOS)
31// EXPECT_DEATH is not supported on IOS.
32#define HARMFUL_ACCESS(action,error_regexp) do { action; } while (0)
[email protected]b4b34792014-06-14 08:29:3733#elif defined(SYZYASAN)
34// We won't get a meaningful error message because we're not running under the
35// SyzyASan logger, but we can at least make sure that the error has been
36// generated in the SyzyASan runtime.
37#define HARMFUL_ACCESS(action,unused) \
38if (debug::IsBinaryInstrumented()) { EXPECT_DEATH(action, \
39 "AsanRuntime::OnError"); }
[email protected]9a4f6182012-10-24 14:11:0240#else
[email protected]c2a18492011-10-05 13:22:5041#define HARMFUL_ACCESS(action,error_regexp) EXPECT_DEATH(action,error_regexp)
[email protected]b4b34792014-06-14 08:29:3742#endif // !OS_IOS && !SYZYASAN
[email protected]c2a18492011-10-05 13:22:5043#else
44#define HARMFUL_ACCESS(action,error_regexp) \
45do { if (RunningOnValgrind()) { action; } } while (0)
46#endif
47
[email protected]1e51a962014-01-16 05:31:4148void DoReadUninitializedValue(char *ptr) {
[email protected]43dea192012-05-16 08:42:0849 // Comparison with 64 is to prevent clang from optimizing away the
[email protected]83a13b22011-09-18 16:39:1250 // jump -- valgrind only catches jumps and conditional moves, but clang uses
51 // the borrow flag if the condition is just `*ptr == '\0'`.
[email protected]43dea192012-05-16 08:42:0852 if (*ptr == 64) {
[email protected]1e51a962014-01-16 05:31:4153 VLOG(1) << "Uninit condition is true";
[email protected]adf7d802010-09-23 09:12:3754 } else {
[email protected]1e51a962014-01-16 05:31:4155 VLOG(1) << "Uninit condition is false";
[email protected]adf7d802010-09-23 09:12:3756 }
57}
58
[email protected]1e51a962014-01-16 05:31:4159void ReadUninitializedValue(char *ptr) {
60#if defined(MEMORY_SANITIZER)
61 EXPECT_DEATH(DoReadUninitializedValue(ptr),
62 "use-of-uninitialized-value");
63#else
64 DoReadUninitializedValue(ptr);
65#endif
66}
67
[email protected]9ded9912010-03-26 12:54:4468void ReadValueOutOfArrayBoundsLeft(char *ptr) {
[email protected]45b9eba2010-10-18 23:57:4969 char c = ptr[-2];
70 VLOG(1) << "Reading a byte out of bounds: " << c;
[email protected]9ded9912010-03-26 12:54:4471}
72
73void ReadValueOutOfArrayBoundsRight(char *ptr, size_t size) {
[email protected]45b9eba2010-10-18 23:57:4974 char c = ptr[size + 1];
75 VLOG(1) << "Reading a byte out of bounds: " << c;
[email protected]9ded9912010-03-26 12:54:4476}
77
78// This is harmless if you run it under Valgrind thanks to redzones.
79void WriteValueOutOfArrayBoundsLeft(char *ptr) {
[email protected]f49ecd8f2011-05-10 18:03:3480 ptr[-1] = kMagicValue;
[email protected]9ded9912010-03-26 12:54:4481}
82
83// This is harmless if you run it under Valgrind thanks to redzones.
84void WriteValueOutOfArrayBoundsRight(char *ptr, size_t size) {
[email protected]f49ecd8f2011-05-10 18:03:3485 ptr[size] = kMagicValue;
[email protected]9ded9912010-03-26 12:54:4486}
87
88void MakeSomeErrors(char *ptr, size_t size) {
[email protected]adf7d802010-09-23 09:12:3789 ReadUninitializedValue(ptr);
[email protected]e260ebc2013-12-18 05:31:3390
[email protected]c2a18492011-10-05 13:22:5091 HARMFUL_ACCESS(ReadValueOutOfArrayBoundsLeft(ptr),
[email protected]e260ebc2013-12-18 05:31:3392 "2 bytes to the left");
[email protected]c2a18492011-10-05 13:22:5093 HARMFUL_ACCESS(ReadValueOutOfArrayBoundsRight(ptr, size),
[email protected]e260ebc2013-12-18 05:31:3394 "1 bytes to the right");
[email protected]c2a18492011-10-05 13:22:5095 HARMFUL_ACCESS(WriteValueOutOfArrayBoundsLeft(ptr),
[email protected]e260ebc2013-12-18 05:31:3396 "1 bytes to the left");
[email protected]c2a18492011-10-05 13:22:5097 HARMFUL_ACCESS(WriteValueOutOfArrayBoundsRight(ptr, size),
[email protected]e260ebc2013-12-18 05:31:3398 "0 bytes to the right");
[email protected]9ded9912010-03-26 12:54:4499}
100
[email protected]66d051bb2010-10-14 08:25:54101} // namespace
102
103// A memory leak detector should report an error in this test.
104TEST(ToolsSanityTest, MemoryLeak) {
[email protected]6bfad0e2011-11-01 11:08:09105 // Without the |volatile|, clang optimizes away the next two lines.
106 int* volatile leak = new int[256]; // Leak some memory intentionally.
[email protected]66d051bb2010-10-14 08:25:54107 leak[4] = 1; // Make sure the allocated memory is used.
108}
109
[email protected]3da8c162014-03-28 17:35:10110#if (defined(ADDRESS_SANITIZER) && defined(OS_IOS)) || defined(SYZYASAN)
[email protected]9a4f6182012-10-24 14:11:02111// Because iOS doesn't support death tests, each of the following tests will
[email protected]3da8c162014-03-28 17:35:10112// crash the whole program under Asan. On Windows Asan is based on SyzyAsan; the
113// error report mechanism is different than with Asan so these tests will fail.
[email protected]9a4f6182012-10-24 14:11:02114#define MAYBE_AccessesToNewMemory DISABLED_AccessesToNewMemory
115#define MAYBE_AccessesToMallocMemory DISABLED_AccessesToMallocMemory
[email protected]2d4e0132014-05-14 11:50:37116#else
117#define MAYBE_AccessesToNewMemory AccessesToNewMemory
118#define MAYBE_AccessesToMallocMemory AccessesToMallocMemory
[email protected]de0c7ed52014-07-18 02:40:40119#endif // (defined(ADDRESS_SANITIZER) && defined(OS_IOS)) || defined(SYZYASAN)
[email protected]f5162802014-05-14 16:04:30120
121// The following tests pass with Clang r170392, but not r172454, which
122// makes AddressSanitizer detect errors in them. We disable these tests under
123// AddressSanitizer until we fully switch to Clang r172454. After that the
124// tests should be put back under the (defined(OS_IOS) || defined(OS_WIN))
125// clause above.
126// See also http://crbug.com/172614.
127#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
[email protected]2d4e0132014-05-14 11:50:37128#define MAYBE_SingleElementDeletedWithBraces \
129 DISABLED_SingleElementDeletedWithBraces
[email protected]f5162802014-05-14 16:04:30130#define MAYBE_ArrayDeletedWithoutBraces DISABLED_ArrayDeletedWithoutBraces
[email protected]de0c7ed52014-07-18 02:40:40131#else
132#define MAYBE_ArrayDeletedWithoutBraces ArrayDeletedWithoutBraces
133#define MAYBE_SingleElementDeletedWithBraces SingleElementDeletedWithBraces
134#endif // defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
135
[email protected]9a4f6182012-10-24 14:11:02136TEST(ToolsSanityTest, MAYBE_AccessesToNewMemory) {
[email protected]9ded9912010-03-26 12:54:44137 char *foo = new char[10];
138 MakeSomeErrors(foo, 10);
139 delete [] foo;
[email protected]c2a18492011-10-05 13:22:50140 // Use after delete.
141 HARMFUL_ACCESS(foo[5] = 0, "heap-use-after-free");
[email protected]9ded9912010-03-26 12:54:44142}
143
[email protected]9a4f6182012-10-24 14:11:02144TEST(ToolsSanityTest, MAYBE_AccessesToMallocMemory) {
[email protected]9ded9912010-03-26 12:54:44145 char *foo = reinterpret_cast<char*>(malloc(10));
146 MakeSomeErrors(foo, 10);
147 free(foo);
[email protected]c2a18492011-10-05 13:22:50148 // Use after free.
149 HARMFUL_ACCESS(foo[5] = 0, "heap-use-after-free");
[email protected]9ded9912010-03-26 12:54:44150}
151
hanscd4cce32015-05-19 17:03:14152static int* allocateArray() {
153 // Clang warns about the mismatched new[]/delete if they occur in the same
154 // function.
155 return new int[10];
156}
157
[email protected]9a4f6182012-10-24 14:11:02158TEST(ToolsSanityTest, MAYBE_ArrayDeletedWithoutBraces) {
[email protected]aee2f332014-03-27 15:08:04159#if !defined(ADDRESS_SANITIZER) && !defined(SYZYASAN)
[email protected]c2a18492011-10-05 13:22:50160 // This test may corrupt memory if not run under Valgrind or compiled with
161 // AddressSanitizer.
[email protected]9ded9912010-03-26 12:54:44162 if (!RunningOnValgrind())
163 return;
[email protected]c2a18492011-10-05 13:22:50164#endif
[email protected]9ded9912010-03-26 12:54:44165
[email protected]014b0b062011-09-18 16:30:12166 // Without the |volatile|, clang optimizes away the next two lines.
hanscd4cce32015-05-19 17:03:14167 int* volatile foo = allocateArray();
[email protected]f5162802014-05-14 16:04:30168 delete foo;
[email protected]9ded9912010-03-26 12:54:44169}
170
hanscd4cce32015-05-19 17:03:14171static int* allocateScalar() {
172 // Clang warns about the mismatched new/delete[] if they occur in the same
173 // function.
174 return new int;
175}
176
[email protected]9a4f6182012-10-24 14:11:02177TEST(ToolsSanityTest, MAYBE_SingleElementDeletedWithBraces) {
178#if !defined(ADDRESS_SANITIZER)
[email protected]c2a18492011-10-05 13:22:50179 // This test may corrupt memory if not run under Valgrind or compiled with
180 // AddressSanitizer.
[email protected]9ded9912010-03-26 12:54:44181 if (!RunningOnValgrind())
182 return;
[email protected]c2a18492011-10-05 13:22:50183#endif
[email protected]9ded9912010-03-26 12:54:44184
[email protected]014b0b062011-09-18 16:30:12185 // Without the |volatile|, clang optimizes away the next two lines.
hanscd4cce32015-05-19 17:03:14186 int* volatile foo = allocateScalar();
[email protected]928ca1d2011-09-30 18:52:04187 (void) foo;
[email protected]f5162802014-05-14 16:04:30188 delete [] foo;
[email protected]9ded9912010-03-26 12:54:44189}
190
[email protected]aee2f332014-03-27 15:08:04191#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
[email protected]b4b34792014-06-14 08:29:37192
[email protected]9b147ae2011-11-08 09:28:49193TEST(ToolsSanityTest, DISABLED_AddressSanitizerNullDerefCrashTest) {
[email protected]6fcbc62f2011-10-12 17:18:24194 // Intentionally crash to make sure AddressSanitizer is running.
195 // This test should not be ran on bots.
196 int* volatile zero = NULL;
197 *zero = 0;
198}
[email protected]9b147ae2011-11-08 09:28:49199
200TEST(ToolsSanityTest, DISABLED_AddressSanitizerLocalOOBCrashTest) {
201 // Intentionally crash to make sure AddressSanitizer is instrumenting
202 // the local variables.
203 // This test should not be ran on bots.
204 int array[5];
205 // Work around the OOB warning reported by Clang.
206 int* volatile access = &array[5];
207 *access = 43;
208}
209
210namespace {
211int g_asan_test_global_array[10];
212} // namespace
213
214TEST(ToolsSanityTest, DISABLED_AddressSanitizerGlobalOOBCrashTest) {
215 // Intentionally crash to make sure AddressSanitizer is instrumenting
216 // the global variables.
217 // This test should not be ran on bots.
218
219 // Work around the OOB warning reported by Clang.
220 int* volatile access = g_asan_test_global_array - 1;
221 *access = 43;
222}
223
[email protected]b4b34792014-06-14 08:29:37224TEST(ToolsSanityTest, AsanHeapOverflow) {
225 HARMFUL_ACCESS(debug::AsanHeapOverflow() ,"to the right");
226}
227
228TEST(ToolsSanityTest, AsanHeapUnderflow) {
229 HARMFUL_ACCESS(debug::AsanHeapUnderflow(), "to the left");
230}
231
232TEST(ToolsSanityTest, AsanHeapUseAfterFree) {
233 HARMFUL_ACCESS(debug::AsanHeapUseAfterFree(), "heap-use-after-free");
234}
235
236#if defined(SYZYASAN)
237TEST(ToolsSanityTest, AsanCorruptHeapBlock) {
238 HARMFUL_ACCESS(debug::AsanCorruptHeapBlock(), "");
239}
240
241TEST(ToolsSanityTest, AsanCorruptHeap) {
242 // This test will kill the process by raising an exception, there's no
243 // particular string to look for in the stack trace.
244 EXPECT_DEATH(debug::AsanCorruptHeap(), "");
245}
246#endif // SYZYASAN
247
248#endif // ADDRESS_SANITIZER || SYZYASAN
[email protected]f49ecd8f2011-05-10 18:03:34249
250namespace {
251
252// We use caps here just to ensure that the method name doesn't interfere with
253// the wildcarded suppressions.
254class TOOLS_SANITY_TEST_CONCURRENT_THREAD : public PlatformThread::Delegate {
255 public:
256 explicit TOOLS_SANITY_TEST_CONCURRENT_THREAD(bool *value) : value_(value) {}
dcheng56488182014-10-21 10:54:51257 ~TOOLS_SANITY_TEST_CONCURRENT_THREAD() override {}
258 void ThreadMain() override {
[email protected]f49ecd8f2011-05-10 18:03:34259 *value_ = true;
260
261 // Sleep for a few milliseconds so the two threads are more likely to live
262 // simultaneously. Otherwise we may miss the report due to mutex
263 // lock/unlock's inside thread creation code in pure-happens-before mode...
[email protected]a1b75b942011-12-31 22:53:51264 PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
[email protected]f49ecd8f2011-05-10 18:03:34265 }
266 private:
267 bool *value_;
268};
269
270class ReleaseStoreThread : public PlatformThread::Delegate {
271 public:
272 explicit ReleaseStoreThread(base::subtle::Atomic32 *value) : value_(value) {}
dcheng56488182014-10-21 10:54:51273 ~ReleaseStoreThread() override {}
274 void ThreadMain() override {
[email protected]f49ecd8f2011-05-10 18:03:34275 base::subtle::Release_Store(value_, kMagicValue);
276
277 // Sleep for a few milliseconds so the two threads are more likely to live
278 // simultaneously. Otherwise we may miss the report due to mutex
279 // lock/unlock's inside thread creation code in pure-happens-before mode...
[email protected]a1b75b942011-12-31 22:53:51280 PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
[email protected]f49ecd8f2011-05-10 18:03:34281 }
282 private:
283 base::subtle::Atomic32 *value_;
284};
285
286class AcquireLoadThread : public PlatformThread::Delegate {
287 public:
288 explicit AcquireLoadThread(base::subtle::Atomic32 *value) : value_(value) {}
dcheng56488182014-10-21 10:54:51289 ~AcquireLoadThread() override {}
290 void ThreadMain() override {
[email protected]f49ecd8f2011-05-10 18:03:34291 // Wait for the other thread to make Release_Store
[email protected]a1b75b942011-12-31 22:53:51292 PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
[email protected]f49ecd8f2011-05-10 18:03:34293 base::subtle::Acquire_Load(value_);
294 }
295 private:
296 base::subtle::Atomic32 *value_;
297};
298
299void RunInParallel(PlatformThread::Delegate *d1, PlatformThread::Delegate *d2) {
300 PlatformThreadHandle a;
301 PlatformThreadHandle b;
302 PlatformThread::Create(0, d1, &a);
303 PlatformThread::Create(0, d2, &b);
304 PlatformThread::Join(a);
305 PlatformThread::Join(b);
306}
307
[email protected]b11d38d02014-06-26 13:10:50308#if defined(THREAD_SANITIZER)
309void DataRace() {
[email protected]a4057982013-03-23 11:28:17310 bool *shared = new bool(false);
311 TOOLS_SANITY_TEST_CONCURRENT_THREAD thread1(shared), thread2(shared);
[email protected]f49ecd8f2011-05-10 18:03:34312 RunInParallel(&thread1, &thread2);
[email protected]a4057982013-03-23 11:28:17313 EXPECT_TRUE(*shared);
314 delete shared;
[email protected]b11d38d02014-06-26 13:10:50315 // We're in a death test - crash.
316 CHECK(0);
[email protected]f49ecd8f2011-05-10 18:03:34317}
[email protected]b11d38d02014-06-26 13:10:50318#endif
319
320} // namespace
321
322#if defined(THREAD_SANITIZER)
323// A data race detector should report an error in this test.
324TEST(ToolsSanityTest, DataRace) {
325 // The suppression regexp must match that in base/debug/tsan_suppressions.cc.
326 EXPECT_DEATH(DataRace(), "1 race:base/tools_sanity_unittest.cc");
327}
328#endif
[email protected]f49ecd8f2011-05-10 18:03:34329
330TEST(ToolsSanityTest, AnnotateBenignRace) {
331 bool shared = false;
332 ANNOTATE_BENIGN_RACE(&shared, "Intentional race - make sure doesn't show up");
333 TOOLS_SANITY_TEST_CONCURRENT_THREAD thread1(&shared), thread2(&shared);
334 RunInParallel(&thread1, &thread2);
335 EXPECT_TRUE(shared);
336}
337
338TEST(ToolsSanityTest, AtomicsAreIgnored) {
339 base::subtle::Atomic32 shared = 0;
340 ReleaseStoreThread thread1(&shared);
341 AcquireLoadThread thread2(&shared);
342 RunInParallel(&thread1, &thread2);
343 EXPECT_EQ(kMagicValue, shared);
[email protected]0716cba2009-12-17 12:37:58344}
[email protected]ce072a72010-12-31 20:02:16345
Vlad Tsyrkleviche0408ff2017-10-13 19:35:24346#if BUILDFLAG(CFI_ENFORCEMENT_TRAP)
Peter Collingbourne355546f2017-10-12 19:51:27347#if defined(OS_WIN)
348#define CFI_ERROR_MSG "EXCEPTION_ILLEGAL_INSTRUCTION"
349#elif defined(OS_ANDROID)
350// TODO(pcc): Produce proper stack dumps on Android and test for the correct
351// si_code here.
352#define CFI_ERROR_MSG "^$"
353#else
Vlad Tsyrklevich37f1e352017-08-15 23:45:10354#define CFI_ERROR_MSG "ILL_ILLOPN"
Peter Collingbourne355546f2017-10-12 19:51:27355#endif
Vlad Tsyrkleviche0408ff2017-10-13 19:35:24356#elif BUILDFLAG(CFI_ENFORCEMENT_DIAGNOSTIC)
Vlad Tsyrklevich37f1e352017-08-15 23:45:10357#define CFI_ERROR_MSG "runtime error: control flow integrity check"
Vlad Tsyrkleviche0408ff2017-10-13 19:35:24358#endif // BUILDFLAG(CFI_ENFORCEMENT_TRAP || CFI_ENFORCEMENT_DIAGNOSTIC)
pcc07364912015-07-31 00:19:06359
Vlad Tsyrklevich37f1e352017-08-15 23:45:10360#if defined(CFI_ERROR_MSG)
krasin59d37182016-07-13 03:24:34361class A {
362 public:
363 A(): n_(0) {}
364 virtual void f() { n_++; }
365 protected:
366 int n_;
367};
368
369class B: public A {
370 public:
371 void f() override { n_--; }
372};
373
Vlad Tsyrklevich37f1e352017-08-15 23:45:10374class C: public B {
375 public:
376 void f() override { n_ += 2; }
377};
378
krasin59d37182016-07-13 03:24:34379NOINLINE void KillVptrAndCall(A *obj) {
380 *reinterpret_cast<void **>(obj) = 0;
381 obj->f();
382}
383
Vlad Tsyrklevich37f1e352017-08-15 23:45:10384TEST(ToolsSanityTest, BadVirtualCallNull) {
krasin59d37182016-07-13 03:24:34385 A a;
386 B b;
Vlad Tsyrklevich37f1e352017-08-15 23:45:10387 EXPECT_DEATH({ KillVptrAndCall(&a); KillVptrAndCall(&b); }, CFI_ERROR_MSG);
krasin59d37182016-07-13 03:24:34388}
389
Vlad Tsyrklevich37f1e352017-08-15 23:45:10390NOINLINE void OverwriteVptrAndCall(B *obj, A *vptr) {
391 *reinterpret_cast<void **>(obj) = *reinterpret_cast<void **>(vptr);
392 obj->f();
393}
394
395TEST(ToolsSanityTest, BadVirtualCallWrongType) {
396 A a;
397 B b;
398 C c;
399 EXPECT_DEATH({ OverwriteVptrAndCall(&b, &a); OverwriteVptrAndCall(&b, &c); },
400 CFI_ERROR_MSG);
401}
402
403// TODO(pcc): remove CFI_CAST_CHECK, see https://crbug.com/626794.
Vlad Tsyrkleviche0408ff2017-10-13 19:35:24404#if BUILDFLAG(CFI_CAST_CHECK)
Vlad Tsyrklevich37f1e352017-08-15 23:45:10405TEST(ToolsSanityTest, BadDerivedCast) {
406 A a;
407 EXPECT_DEATH((void)(B*)&a, CFI_ERROR_MSG);
408}
409
410TEST(ToolsSanityTest, BadUnrelatedCast) {
411 class A {
412 virtual void f() {}
413 };
414
415 class B {
416 virtual void f() {}
417 };
418
419 A a;
420 EXPECT_DEATH((void)(B*)&a, CFI_ERROR_MSG);
421}
Vlad Tsyrkleviche0408ff2017-10-13 19:35:24422#endif // BUILDFLAG(CFI_CAST_CHECK)
Vlad Tsyrklevich37f1e352017-08-15 23:45:10423
Vlad Tsyrkleviche0408ff2017-10-13 19:35:24424#endif // CFI_ERROR_MSG
pcc07364912015-07-31 00:19:06425
[email protected]ce072a72010-12-31 20:02:16426} // namespace base