51
51
#include < android-base/test_utils.h>
52
52
#include < android-base/unique_fd.h>
53
53
#include < cutils/sockets.h>
54
+ #include < gmock/gmock.h>
54
55
#include < gtest/gtest.h>
55
56
56
57
#include < libminijail.h>
@@ -65,6 +66,7 @@ using namespace std::chrono_literals;
65
66
66
67
using android::base::SendFileDescriptors;
67
68
using android::base::unique_fd;
69
+ using ::testing::HasSubstr;
68
70
69
71
#if defined(__LP64__)
70
72
#define ARCH_SUFFIX " 64"
@@ -307,6 +309,19 @@ static void ConsumeFd(unique_fd fd, std::string* output) {
307
309
*output = std::move (result);
308
310
}
309
311
312
+ class LogcatCollector {
313
+ public:
314
+ LogcatCollector () { system (" logcat -c" ); }
315
+
316
+ void Collect (std::string* output) {
317
+ FILE* cmd_stdout = popen (" logcat -d '*:S DEBUG'" , " r" );
318
+ ASSERT_NE (cmd_stdout, nullptr );
319
+ unique_fd tmp_fd (TEMP_FAILURE_RETRY (dup (fileno (cmd_stdout))));
320
+ ConsumeFd (std::move (tmp_fd), output);
321
+ pclose (cmd_stdout);
322
+ }
323
+ };
324
+
310
325
TEST_F (CrasherTest, smoke) {
311
326
int intercept_result;
312
327
unique_fd output_fd;
@@ -441,6 +456,7 @@ TEST_P(GwpAsanCrasherTest, gwp_asan_uaf) {
441
456
}
442
457
443
458
GwpAsanTestParameters params = GetParam ();
459
+ LogcatCollector logcat_collector;
444
460
445
461
int intercept_result;
446
462
unique_fd output_fd;
@@ -460,17 +476,18 @@ TEST_P(GwpAsanCrasherTest, gwp_asan_uaf) {
460
476
461
477
ASSERT_EQ (1 , intercept_result) << " tombstoned reported failure" ;
462
478
463
- std::string result;
464
- ConsumeFd (std::move (output_fd), &result);
479
+ std::vector<std::string> log_sources (2 );
480
+ ConsumeFd (std::move (output_fd), &log_sources[0 ]);
481
+ logcat_collector.Collect (&log_sources[1 ]);
465
482
466
- ASSERT_MATCH (result, R"( signal 11 \(SIGSEGV\), code 2 \(SEGV_ACCERR\))" );
467
- ASSERT_MATCH (result, R"( Cause: \[GWP-ASan\]: )" + params.cause_needle );
468
- if (params.free_before_access ) {
469
- ASSERT_MATCH (result, R"( deallocated by thread .*
470
- #00 pc)" );
483
+ for (const auto & result : log_sources) {
484
+ ASSERT_MATCH (result, R"( signal 11 \(SIGSEGV\), code 2 \(SEGV_ACCERR\))" );
485
+ ASSERT_MATCH (result, R"( Cause: \[GWP-ASan\]: )" + params.cause_needle );
486
+ if (params.free_before_access ) {
487
+ ASSERT_MATCH (result, R"( deallocated by thread .*\n.*#00 pc)" );
488
+ }
489
+ ASSERT_MATCH (result, R"( (^|\s)allocated by thread .*\n.*#00 pc)" );
471
490
}
472
- ASSERT_MATCH (result, R"( allocated by thread .*
473
- #00 pc)" );
474
491
}
475
492
476
493
struct SizeParamCrasherTest : CrasherTest, testing::WithParamInterface<size_t > {};
@@ -488,6 +505,8 @@ TEST_P(SizeParamCrasherTest, mte_uaf) {
488
505
return ;
489
506
}
490
507
508
+ LogcatCollector logcat_collector;
509
+
491
510
int intercept_result;
492
511
unique_fd output_fd;
493
512
StartProcess ([&]() {
@@ -504,16 +523,17 @@ TEST_P(SizeParamCrasherTest, mte_uaf) {
504
523
505
524
ASSERT_EQ (1 , intercept_result) << " tombstoned reported failure" ;
506
525
507
- std::string result;
508
- ConsumeFd (std::move (output_fd), &result);
526
+ std::vector<std::string> log_sources (2 );
527
+ ConsumeFd (std::move (output_fd), &log_sources[0 ]);
528
+ logcat_collector.Collect (&log_sources[1 ]);
509
529
510
- ASSERT_MATCH (result, R"( signal 11 \(SIGSEGV\) )" );
511
- ASSERT_MATCH (result, R"( Cause: \[MTE\]: Use After Free, 0 bytes into a )" +
512
- std::to_string ( GetParam ()) + R"( -byte allocation )" );
513
- ASSERT_MATCH (result, R"( deallocated by thread .*
514
- #00 pc)" );
515
- ASSERT_MATCH (result, R"( allocated by thread .*
516
- #00 pc )" );
530
+ for ( const auto & result : log_sources) {
531
+ ASSERT_MATCH (result, R"( signal 11 \(SIGSEGV\) )" );
532
+ ASSERT_MATCH (result, R"( Cause: \[MTE\]: Use After Free, 0 bytes into a )" +
533
+ std::to_string ( GetParam ()) + R"( -byte allocation )" );
534
+ ASSERT_MATCH (result, R"( deallocated by thread .*?\n.* #00 pc)" );
535
+ ASSERT_MATCH (result, R"( (^|\s) allocated by thread .*?\n.*#00 pc )" );
536
+ }
517
537
#else
518
538
GTEST_SKIP () << " Requires aarch64" ;
519
539
#endif
@@ -557,6 +577,7 @@ TEST_P(SizeParamCrasherTest, mte_overflow) {
557
577
GTEST_SKIP () << " Requires MTE" ;
558
578
}
559
579
580
+ LogcatCollector logcat_collector;
560
581
int intercept_result;
561
582
unique_fd output_fd;
562
583
StartProcess ([&]() {
@@ -572,14 +593,16 @@ TEST_P(SizeParamCrasherTest, mte_overflow) {
572
593
573
594
ASSERT_EQ (1 , intercept_result) << " tombstoned reported failure" ;
574
595
575
- std::string result;
576
- ConsumeFd (std::move (output_fd), &result);
596
+ std::vector<std::string> log_sources (2 );
597
+ ConsumeFd (std::move (output_fd), &log_sources[0 ]);
598
+ logcat_collector.Collect (&log_sources[1 ]);
577
599
578
- ASSERT_MATCH (result, R"( signal 11 \(SIGSEGV\))" );
579
- ASSERT_MATCH (result, R"( Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a )" +
580
- std::to_string (GetParam ()) + R"( -byte allocation)" );
581
- ASSERT_MATCH (result, R"( allocated by thread .*
582
- #00 pc)" );
600
+ for (const auto & result : log_sources) {
601
+ ASSERT_MATCH (result, R"( signal 11 \(SIGSEGV\))" );
602
+ ASSERT_MATCH (result, R"( Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a )" +
603
+ std::to_string (GetParam ()) + R"( -byte allocation)" );
604
+ ASSERT_MATCH (result, R"( (^|\s)allocated by thread .*?\n.*#00 pc)" );
605
+ }
583
606
#else
584
607
GTEST_SKIP () << " Requires aarch64" ;
585
608
#endif
@@ -612,7 +635,7 @@ TEST_P(SizeParamCrasherTest, mte_underflow) {
612
635
ASSERT_MATCH (result, R"( signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))" );
613
636
ASSERT_MATCH (result, R"( Cause: \[MTE\]: Buffer Underflow, 4 bytes left of a )" +
614
637
std::to_string (GetParam ()) + R"( -byte allocation)" );
615
- ASSERT_MATCH (result, R"( allocated by thread .*
638
+ ASSERT_MATCH (result, R"( (^|\s) allocated by thread .*
616
639
#00 pc)" );
617
640
#else
618
641
GTEST_SKIP () << " Requires aarch64" ;
@@ -625,6 +648,8 @@ TEST_F(CrasherTest, mte_multiple_causes) {
625
648
GTEST_SKIP () << " Requires MTE" ;
626
649
}
627
650
651
+ LogcatCollector logcat_collector;
652
+
628
653
int intercept_result;
629
654
unique_fd output_fd;
630
655
StartProcess ([]() {
@@ -657,17 +682,23 @@ TEST_F(CrasherTest, mte_multiple_causes) {
657
682
658
683
ASSERT_EQ (1 , intercept_result) << " tombstoned reported failure" ;
659
684
660
- std::string result;
661
- ConsumeFd (std::move (output_fd), &result);
662
-
663
- ASSERT_MATCH (result, R"( signal 11 \(SIGSEGV\))" );
664
- ASSERT_MATCH (
665
- result,
666
- R"( Note: multiple potential causes for this crash were detected, listing them in decreasing order of probability.)" );
667
-
668
- // Adjacent untracked allocations may cause us to see the wrong underflow here (or only
669
- // overflows), so we can't match explicitly for an underflow message.
670
- ASSERT_MATCH (result, R"( Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation)" );
685
+ std::vector<std::string> log_sources (2 );
686
+ ConsumeFd (std::move (output_fd), &log_sources[0 ]);
687
+ logcat_collector.Collect (&log_sources[1 ]);
688
+
689
+ for (const auto & result : log_sources) {
690
+ ASSERT_MATCH (result, R"( signal 11 \(SIGSEGV\))" );
691
+ ASSERT_THAT (result, HasSubstr (" Note: multiple potential causes for this crash were detected, "
692
+ " listing them in decreasing order of probability." ));
693
+ // Adjacent untracked allocations may cause us to see the wrong underflow here (or only
694
+ // overflows), so we can't match explicitly for an underflow message.
695
+ ASSERT_MATCH (result,
696
+ R"( Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation)" );
697
+ // Ensure there's at least two allocation traces (one for each cause).
698
+ ASSERT_MATCH (
699
+ result,
700
+ R"( (^|\s)allocated by thread .*?\n.*#00 pc(.|\n)*?(^|\s)allocated by thread .*?\n.*#00 pc)" );
701
+ }
671
702
#else
672
703
GTEST_SKIP () << " Requires aarch64" ;
673
704
#endif
0 commit comments