From 1d929c7915bcb563f39c0012b752572428958186 Mon Sep 17 00:00:00 2001 From: Mikhail Kashkarov Date: Thu, 15 Jun 2017 17:03:07 +0300 Subject: [PATCH 01/16] Implement AAPCS64 updates for alignment attribute. PR target/77728 gcc/ * config/aarch64/aarch64.c(aarch64_function_arg_alignmentq): Rewrite, looking one level down for records and arrays. Ignore TYPE_FIELDS chain decls other than FIELD_DECLs. (aarch64_layout_arg): Adjust aarch64_function_arg_alignment caller. (aarch64_function_arg_boundary): Likewise. Simplify using MIN/MAX. (aarch64_gimplify_va_arg_expr): Adjust aarch64_function_arg_alignment caller. testsuite/ * g++.dg/abi/pr77728-2.C: New test. Backported from trunk: be35fa06e8f976e1cb880988d5c82813106922fd 32cb614ad1f60267f914e38da26a73259299d720 26312b0ea5f3dfc6e3d8d8d18c76d464d6fa328e Change-Id: I4293167468353e9730dc918b87edda4484af8315 --- gcc/config/aarch64/aarch64.c | 54 ++++++------ gcc/testsuite/g++.dg/abi/pr77728-2.C | 165 +++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+), 26 deletions(-) create mode 100644 gcc/testsuite/g++.dg/abi/pr77728-2.C diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c index f0970f2..bd75ef2 100644 --- a/gcc/config/aarch64/aarch64.c +++ b/gcc/config/aarch64/aarch64.c @@ -2246,22 +2246,24 @@ aarch64_vfp_is_call_candidate (cumulative_args_t pcum_v, machine_mode mode, static unsigned int aarch64_function_arg_alignment (machine_mode mode, const_tree type) { - unsigned int alignment; + if (!type) + return GET_MODE_ALIGNMENT (mode); - if (type) - { - if (!integer_zerop (TYPE_SIZE (type))) - { - if (TYPE_MODE (type) == mode) - alignment = TYPE_ALIGN (type); - else - alignment = GET_MODE_ALIGNMENT (mode); - } - else - alignment = 0; - } - else - alignment = GET_MODE_ALIGNMENT (mode); + if (integer_zerop (TYPE_SIZE (type))) + return 0; + + gcc_assert (TYPE_MODE (type) == mode); + + if (!AGGREGATE_TYPE_P (type)) + return TYPE_ALIGN (TYPE_MAIN_VARIANT (type)); + + if (TREE_CODE (type) == ARRAY_TYPE) + return TYPE_ALIGN (TREE_TYPE (type)); + + unsigned int alignment = 0; + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL) + alignment = std::max (alignment, DECL_ALIGN (field)); return alignment; } @@ -2350,24 +2352,28 @@ aarch64_layout_arg (cumulative_args_t pcum_v, machine_mode mode, entirely general registers. */ if (allocate_ncrn && (ncrn + nregs <= NUM_ARG_REGS)) { - unsigned int alignment = aarch64_function_arg_alignment (mode, type); gcc_assert (nregs == 0 || nregs == 1 || nregs == 2); /* C.8 if the argument has an alignment of 16 then the NGRN is rounded up to the next even number. */ - if (nregs == 2 && alignment == 16 * BITS_PER_UNIT && ncrn % 2) + if (nregs == 2 + && ncrn % 2 + /* The == 16 * BITS_PER_UNIT instead of >= 16 * BITS_PER_UNIT + comparison is there because for > 16 * BITS_PER_UNIT + alignment nregs should be > 2 and therefore it should be + passed by reference rather than value. */ + && aarch64_function_arg_alignment (mode, type) == 16 * BITS_PER_UNIT) { ++ncrn; gcc_assert (ncrn + nregs <= NUM_ARG_REGS); } + /* NREGS can be 0 when e.g. an empty structure is to be passed. A reg is still generated for it, but the caller should be smart enough not to use it. */ if (nregs == 0 || nregs == 1 || GET_MODE_CLASS (mode) == MODE_INT) - { - pcum->aapcs_reg = gen_rtx_REG (mode, R0_REGNUM + ncrn); - } + pcum->aapcs_reg = gen_rtx_REG (mode, R0_REGNUM + ncrn); else { rtx par; @@ -2395,6 +2401,7 @@ aarch64_layout_arg (cumulative_args_t pcum_v, machine_mode mode, this argument and align the total size if necessary. */ on_stack: pcum->aapcs_stack_words = size / UNITS_PER_WORD; + if (aarch64_function_arg_alignment (mode, type) == 16 * BITS_PER_UNIT) pcum->aapcs_stack_size = ROUND_UP (pcum->aapcs_stack_size, 16 / UNITS_PER_WORD); @@ -2487,12 +2494,7 @@ static unsigned int aarch64_function_arg_boundary (machine_mode mode, const_tree type) { unsigned int alignment = aarch64_function_arg_alignment (mode, type); - - if (alignment < PARM_BOUNDARY) - alignment = PARM_BOUNDARY; - if (alignment > STACK_BOUNDARY) - alignment = STACK_BOUNDARY; - return alignment; + return MIN (MAX (alignment, PARM_BOUNDARY), STACK_BOUNDARY); } /* For use by FUNCTION_ARG_PADDING (MODE, TYPE). diff --git a/gcc/testsuite/g++.dg/abi/pr77728-2.C b/gcc/testsuite/g++.dg/abi/pr77728-2.C new file mode 100644 index 0000000..ffe6910 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/pr77728-2.C @@ -0,0 +1,165 @@ +// { dg-do compile { target { { aarch64-*-* } && c++11 } } } +// { dg-options "" } + +#include + +template +struct alignas (16) A { char p[16]; }; + +A<0> v; + +template +struct B +{ + typedef A T; + int i, j, k, l; +}; + +struct C : public B<0> {}; +struct D {}; +struct E : public D, C {}; +struct F : public B<1> {}; +struct G : public F { static int y alignas (16); }; +struct H : public G {}; +struct I : public D { int z alignas (16); }; +struct J : public D { static int z alignas (16); int i, j, k, l; }; + +template +struct K : public D { typedef A T; int i, j; }; + +struct L { static int h alignas (16); int i, j, k, l; }; + +int +fn1 (int a, B<0> b) +{ + return a + b.i; +} + +int +fn2 (int a, B<1> b) +{ + return a + b.i; +} + +int +fn3 (int a, L b) +{ + return a + b.i; +} + +int +fn4 (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, B<0> n, ...) +{ + va_list ap; + va_start (ap, n); + int x = va_arg (ap, int); + va_end (ap); + return x; +} + +int +fn5 (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, B<1> n, ...) +{ + va_list ap; + va_start (ap, n); + int x = va_arg (ap, int); + va_end (ap); + return x; +} + +int +fn6 (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, C n, ...) +{ + va_list ap; + va_start (ap, n); + int x = va_arg (ap, int); + va_end (ap); + return x; +} + +int +fn7 (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, E n, ...) +{ + va_list ap; + va_start (ap, n); + int x = va_arg (ap, int); + va_end (ap); + return x; +} + +int +fn8 (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, H n, ...) +{ + va_list ap; + va_start (ap, n); + int x = va_arg (ap, int); + va_end (ap); + return x; +} + +int +fn9 (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, I n, ...) +{ + va_list ap; + va_start (ap, n); + int x = va_arg (ap, int); + va_end (ap); + return x; +} + +int +fn10 (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, J n, ...) +{ + va_list ap; + va_start (ap, n); + int x = va_arg (ap, int); + va_end (ap); + return x; +} + +int +fn11 (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, K<0> n, ...) +{ + va_list ap; + va_start (ap, n); + int x = va_arg (ap, int); + va_end (ap); + return x; +} + +int +fn12 (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, K<2> n, ...) +{ + va_list ap; + va_start (ap, n); + int x = va_arg (ap, int); + va_end (ap); + return x; +} + +void +test () +{ + static B<0> b0; + static B<1> b1; + static L l; + static C c; + static E e; + static H h; + static I i; + static J j; + static K<0> k0; + static K<2> k2; + fn1 (1, b0); + fn2 (1, b1); + fn3 (1, l); + fn4 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, b0, 1, 2, 3, 4); + fn5 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, b1, 1, 2, 3, 4); + fn6 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, c, 1, 2, 3, 4); + fn7 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, e, 1, 2, 3, 4); + fn8 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, h, 1, 2, 3, 4); + fn9 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, i, 1, 2, 3, 4); + fn10 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, j, 1, 2, 3, 4); + fn11 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, k0, 1, 2, 3, 4); + fn12 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, k2, 1, 2, 3, 4); +} -- 2.7.4 From 1bee23a5afa1e59d348d3ebb1dc81cc209978a2b Mon Sep 17 00:00:00 2001 From: Mikhail Kashkarov Date: Thu, 15 Jun 2017 19:47:33 +0300 Subject: [PATCH 02/16] Tests of AAPCS64 updates for alignment attribute gcc/testsuite/ * gcc.target/aarch64/aapcs64/aapcs64.exp: Also execute rec_*.c * gcc.target/aarch64/aapcs64/rec_align-5.c: New. * gcc.target/aarch64/aapcs64/rec_align-6.c: New. * gcc.target/aarch64/aapcs64/rec_align-7.c: New. * gcc.target/aarch64/aapcs64/rec_align-8.c: New. * gcc.target/aarch64/aapcs64/rec_align-9.c: New. * gcc.target/aarch64/aapcs64/test_align-5.c: New. * gcc.target/aarch64/aapcs64/test_align-6.c: New. * gcc.target/aarch64/aapcs64/test_align-7.c: New. * gcc.target/aarch64/aapcs64/test_align-8.c: New. * gcc.target/aarch64/aapcs64/test_align-9.c: New. * gcc.target/aarch64/aapcs64/rec_vaarg-1.c: New. * gcc.target/aarch64/aapcs64/rec_vaarg-2.c: New. Backported from trunk: 84ee090575f413f92377812d277b705d023a4b0b Change-Id: Idf931ee8b3b5f556b7341734eb940f9a0926a03f --- .../gcc.target/aarch64/aapcs64/aapcs64.exp | 10 +++++ .../gcc.target/aarch64/aapcs64/rec_align-5.c | 44 ++++++++++++++++++++ .../gcc.target/aarch64/aapcs64/rec_align-6.c | 45 +++++++++++++++++++++ .../gcc.target/aarch64/aapcs64/rec_align-7.c | 47 ++++++++++++++++++++++ .../gcc.target/aarch64/aapcs64/rec_align-8.c | 37 +++++++++++++++++ .../gcc.target/aarch64/aapcs64/rec_align-9.c | 41 +++++++++++++++++++ .../gcc.target/aarch64/aapcs64/rec_align_vaarg-1.c | 38 +++++++++++++++++ .../gcc.target/aarch64/aapcs64/rec_align_vaarg-2.c | 28 +++++++++++++ .../gcc.target/aarch64/aapcs64/test_align-5.c | 35 ++++++++++++++++ .../gcc.target/aarch64/aapcs64/test_align-6.c | 36 +++++++++++++++++ .../gcc.target/aarch64/aapcs64/test_align-7.c | 38 +++++++++++++++++ .../gcc.target/aarch64/aapcs64/test_align-8.c | 33 +++++++++++++++ .../gcc.target/aarch64/aapcs64/test_align-9.c | 47 ++++++++++++++++++++++ 13 files changed, 479 insertions(+) create mode 100644 gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-5.c create mode 100644 gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-6.c create mode 100644 gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-7.c create mode 100644 gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-8.c create mode 100644 gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-9.c create mode 100644 gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align_vaarg-1.c create mode 100644 gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align_vaarg-2.c create mode 100644 gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-5.c create mode 100644 gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-6.c create mode 100644 gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-7.c create mode 100644 gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-8.c create mode 100644 gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-9.c diff --git a/gcc/testsuite/gcc.target/aarch64/aapcs64/aapcs64.exp b/gcc/testsuite/gcc.target/aarch64/aapcs64/aapcs64.exp index ad92994..9998d0b 100644 --- a/gcc/testsuite/gcc.target/aarch64/aapcs64/aapcs64.exp +++ b/gcc/testsuite/gcc.target/aarch64/aapcs64/aapcs64.exp @@ -38,6 +38,16 @@ foreach src [lsort [glob -nocomplain $srcdir/$subdir/test_*.c]] { } } +# Test parameter receiving. +set additional_flags_for_rec $additional_flags +append additional_flags_for_rec " -fno-inline" +foreach src [lsort [glob -nocomplain $srcdir/$subdir/rec_*.c]] { + if {[runtest_file_p $runtests $src]} { + c-torture-execute [list $src] \ + $additional_flags_for_rec + } +} + # Test unnamed argument retrieval via the va_arg macro. foreach src [lsort [glob -nocomplain $srcdir/$subdir/va_arg-*.c]] { if {[runtest_file_p $runtests $src]} { diff --git a/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-5.c b/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-5.c new file mode 100644 index 0000000..1b42c92 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-5.c @@ -0,0 +1,44 @@ +/* Test AAPCS layout (alignment) for callee. */ + +/* { dg-do run { target aarch64*-*-* } } */ + +extern void abort (void); + +typedef __attribute__ ((__aligned__ (8))) int alignedint; + +alignedint a = 11; +alignedint b = 13; +alignedint c = 17; +alignedint d = 19; +alignedint e = 23; +alignedint f = 29; +alignedint g = 31; +alignedint h = 37; +alignedint i = 41; +alignedint j = 43; + +void +test_passing_many_alignedint (alignedint x0, alignedint x1, alignedint x2, + alignedint x3, alignedint x4, alignedint x5, + alignedint x6, alignedint x7, alignedint stack, + alignedint stack8) +{ + if (x0 != a + || x1 != b + || x2 != c + || x3 != d + || x4 != e + || x5 != f + || x6 != g + || x7 != h + || stack != i + || stack8 !=j) + abort (); +} + +int +main (int argc, char **argv) +{ + test_passing_many_alignedint (a, b, c, d, e, f, g, h, i, j); + return 0; +} diff --git a/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-6.c b/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-6.c new file mode 100644 index 0000000..a8d8b1b --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-6.c @@ -0,0 +1,45 @@ +/* Test AAPCS layout (alignment) for callee. */ + +/* { dg-do run { target aarch64*-*-* } } */ + +extern int memcmp (const void *s1, const void *s2, __SIZE_TYPE__ n); +extern void abort (void); + +/* The underlying struct here has alignment 8. */ +typedef struct __attribute__ ((__aligned__ (16))) + { + long x; + long y; + } overaligned; + +overaligned a = { 2, 3 }; +overaligned b = { 5, 8 }; +overaligned c = { 13, 21 }; + +void +test_passing_overaligned_struct (int x0, overaligned x1, int x3, int x4, + overaligned x5, int x7, int stack, + overaligned stack8) +{ + if (x0 != 7 || x3 != 9 || x4 != 11 || x7 != 15 || stack != 10) + abort (); + if (memcmp ((void *) &x1, (void *)&a, sizeof (overaligned))) + abort (); + if (memcmp ((void *) &x5, (void *)&b, sizeof (overaligned))) + abort (); + if (memcmp ((void *)&stack8, (void *)&c, sizeof (overaligned))) + abort (); + long addr = ((long) &stack8) & 15; + if (addr != 0) + { + __builtin_printf ("Alignment was %d\n", addr); + abort (); + } +} + +int +main (int argc, char **argv) +{ + test_passing_overaligned_struct (7, a, 9, 11, b, 15, 10, c); + return 0; +} diff --git a/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-7.c b/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-7.c new file mode 100644 index 0000000..61e3c11 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-7.c @@ -0,0 +1,47 @@ +/* Test AAPCS layout (alignment) for callee. */ + +/* { dg-do run { target aarch64*-*-* } } */ + +extern int memcmp (const void *s1, const void *s2, __SIZE_TYPE__ n); +extern void abort (void); + +struct s + { + long x; + long y; + }; + +/* This still has size 16, so is still passed by value. */ +typedef __attribute__ ((__aligned__ (32))) struct s overaligned; + +/* A few structs, at 32-byte-aligned memory locations. */ +overaligned a = { 2, 3 }; +overaligned b = { 5, 8 }; +overaligned c = { 13, 21 }; + +void +test_pass_by_value (int x0, overaligned x1, int x3, int x4, overaligned x5, + int x7, int stack, overaligned stack8) +{ + if (x0 != 7 || x3 != 9 || x4 != 11 || x7 != 15 || stack != 10) + abort (); + if (memcmp ((void *) &x1, (void *)&a, sizeof (overaligned))) + abort (); + if (memcmp ((void *) &x5, (void *)&b, sizeof (overaligned))) + abort (); + if (memcmp ((void *)&stack8, (void *)&c, sizeof (overaligned))) + abort (); + long addr = ((long) &stack8) & 15; + if (addr != 0) + { + __builtin_printf ("Alignment was %d\n", addr); + abort (); + } +} + +int +main (int argc, char **argv) +{ + test_pass_by_value (7, a, 9, 11, b, 15, 10, c); + return 0; +} diff --git a/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-8.c b/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-8.c new file mode 100644 index 0000000..c935180 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-8.c @@ -0,0 +1,37 @@ +/* Test AAPCS layout (alignment) for callee. */ + +/* { dg-do run { target aarch64*-*-* } } */ + +extern int memcmp (const void *s1, const void *s2, __SIZE_TYPE__ n); +extern void abort (void); + +/* The alignment also gives this size 32, so will be passed by reference. */ +typedef struct __attribute__ ((__aligned__ (32))) + { + long x; + long y; + } overaligned; + +overaligned a = { 2, 3 }; + +void +test_pass_by_ref (int x0, overaligned x1, int x2) +{ + if (x0 != 7 || x2 != 9) + abort (); + if (memcmp ((void *) &x1, (void *)&a, sizeof (overaligned))) + abort (); + long addr = ((long) &x1) & 31; + if (addr != 0) + { + __builtin_printf ("Alignment was %d\n", addr); + abort (); + } +} + +int +main (int argc, char **argv) +{ + test_pass_by_ref (7, a, 9); + return 0; +} diff --git a/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-9.c b/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-9.c new file mode 100644 index 0000000..81139f5 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align-9.c @@ -0,0 +1,41 @@ +/* Test AAPCS layout (alignment) for callee. */ + +/* { dg-do run { target aarch64*-*-* } } */ + +extern int memcmp (const void *s1, const void *s2, __SIZE_TYPE__ n); +extern void abort (void); + +struct s + { + /* This forces the alignment and size of the struct to 16. */ + __attribute__ ((__aligned__ (16))) long x; + int y; + /* 4 bytes padding. */ + }; + +typedef struct s __attribute__ ((__aligned__ (8))) underaligned; + +underaligned a = { 1, 4 }; +underaligned b = { 9, 16 }; +underaligned c = { 25, 36 }; + +void +test_underaligned_struct (int x0, underaligned x2, int x4, underaligned x6, + int stack, underaligned stack16) +{ + if (x0 != 3 || x4 != 5 || stack != 7) + abort (); + if (memcmp ((void *) &x2, (void *)&a, sizeof (underaligned))) + abort (); + if (memcmp ((void *)&x6, (void *)&b, sizeof (underaligned))) + abort (); + if (memcmp ((void *)&stack16, (void *)&c, sizeof (underaligned))) + abort (); +} + +int +main (int argc, char **argv) +{ + test_underaligned_struct (3, a, 5, b, 7, c); + return 0; +} diff --git a/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align_vaarg-1.c b/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align_vaarg-1.c new file mode 100644 index 0000000..109ddc2 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align_vaarg-1.c @@ -0,0 +1,38 @@ +/* Test AAPCS layout (alignment of varargs) for callee. */ + +/* { dg-do run { target aarch64*-*-* } } */ + +#include + +extern void abort (void); + +typedef __attribute__ ((__aligned__ (16))) long alignedlong; + +void +test_pass_overaligned_long_vaargs (long l, ...) +{ + va_list va; + va_start (va, l); + /* Arguments should be passed in the same registers as if they were ints. */ + while (l-- > 0) + if (va_arg (va, long) != l) + abort (); + va_end (va); +} + +int +main (int argc, char **argv) +{ + alignedlong a = 9; + alignedlong b = 8; + alignedlong c = 7; + alignedlong d = 6; + alignedlong e = 5; + alignedlong f = 4; + alignedlong g = 3; + alignedlong h = 2; + alignedlong i = 1; + alignedlong j = 0; + test_pass_overaligned_long_vaargs (a, b, c, d, e, f, g, h, i, j); + return 0; +} diff --git a/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align_vaarg-2.c b/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align_vaarg-2.c new file mode 100644 index 0000000..dc4eb2f --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/aapcs64/rec_align_vaarg-2.c @@ -0,0 +1,28 @@ +/* Test AAPCS layout (alignment of varargs) for callee. */ + +/* { dg-do run { target aarch64*-*-* } } */ + +#include + +extern void abort (void); + +typedef __attribute__ ((__aligned__ (16))) int alignedint; + +void +test_pass_overaligned_int_vaargs (int i, ...) +{ + va_list va; + va_start (va, i); + /* alignedint should be pulled out of regs/stack just like an int. */ + while (i-- > 0) + if (va_arg (va, alignedint) != i) + abort (); + va_end (va); +} + +int +main (int argc, char **argv) +{ + test_pass_overaligned_int_vaargs (9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + return 0; +} diff --git a/gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-5.c b/gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-5.c new file mode 100644 index 0000000..ac5673e --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-5.c @@ -0,0 +1,35 @@ +/* Test AAPCS layout (alignment). */ + +/* { dg-do run { target aarch64*-*-* } } */ + +#ifndef IN_FRAMEWORK +#define TESTFILE "test_align-5.c" + +typedef __attribute__ ((__aligned__ (16))) long alignedint; + +alignedint a = 11; +alignedint b = 13; +alignedint c = 17; +alignedint d = 19; +alignedint e = 23; +alignedint f = 29; +alignedint g = 31; +alignedint h = 37; +alignedint i = 41; +alignedint j = 43; + +#include "abitest.h" +#else + ARG (alignedint, a, X0) + /* Attribute suggests R2, but we should use only natural alignment: */ + ARG (alignedint, b, X1) + ARG (alignedint, c, X2) + ARG (alignedint, d, X3) + ARG (alignedint, e, X4) + ARG (alignedint, f, X5) + ARG (alignedint, g, X6) + ARG (alignedint, h, X7) + ARG (alignedint, i, STACK) + /* Attribute would suggest STACK + 16 but should be ignored: */ + LAST_ARG (alignedint, j, STACK + 8) +#endif diff --git a/gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-6.c b/gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-6.c new file mode 100644 index 0000000..20cbd94 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-6.c @@ -0,0 +1,36 @@ +/* Test AAPCS layout (alignment). */ + +/* { dg-do run { target aarch64*-*-* } } */ + +#ifndef IN_FRAMEWORK +#define TESTFILE "test_align-6.c" + +/* The underlying struct here has alignment 8. */ +typedef struct __attribute__ ((__aligned__ (16))) + { + long x; + long y; + } overaligned; + +/* A couple of instances, at 16-byte-aligned memory locations. */ +overaligned a = { 2, 3 }; +overaligned b = { 5, 8 }; +overaligned c = { 13, 21 }; + +#include "abitest.h" +#else + ARG (int, 7, W0) + /* Natural alignment should be 8. */ + ARG (overaligned, a, X1) + ARG (int, 9, W3) + ARG (int, 11, W4) + ARG (overaligned, b, X5) + ARG (int, 15, W7) +#ifndef __AAPCS64_BIG_ENDIAN__ + ARG (int, 10, STACK) +#else + ARG (int, 10, STACK + 4) +#endif + /* Natural alignment should be 8. */ + LAST_ARG (overaligned, c, STACK + 8) +#endif diff --git a/gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-7.c b/gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-7.c new file mode 100644 index 0000000..6af422f --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-7.c @@ -0,0 +1,38 @@ +/* Test AAPCS layout (alignment). */ + +/* { dg-do run { target aarch64*-*-* } } */ + +#ifndef IN_FRAMEWORK +#define TESTFILE "test_align-7.c" + +struct s + { + long x; + long y; + }; + +/* This still has size 16, so is still passed by value. */ +typedef __attribute__ ((__aligned__ (32))) struct s overaligned; + +/* A few structs, at 32-byte-aligned memory locations. */ +overaligned a = { 2, 3 }; +overaligned b = { 5, 8 }; +overaligned c = { 13, 21 }; + +#include "abitest.h" +#else + ARG (int, 7, W0) + /* Alignment should be 8. */ + ARG (overaligned, a, X1) + ARG (int, 9, W3) + ARG (int, 11, W4) + ARG (overaligned, b, X5) + ARG (int, 15, W7) +#ifndef __AAPCS64_BIG_ENDIAN__ + ARG (int, 10, STACK) +#else + ARG (int, 10, STACK + 4) +#endif + /* Natural alignment should be 8. */ + LAST_ARG (overaligned, c, STACK + 8) +#endif diff --git a/gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-8.c b/gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-8.c new file mode 100644 index 0000000..ad4dfe4 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-8.c @@ -0,0 +1,33 @@ +/* Test AAPCS layout (alignment). */ + +/* { dg-do run { target aarch64*-*-* } } */ + +#ifndef IN_FRAMEWORK +#define TESTFILE "test_align-8.c" + +/* The alignment also gives this size 32, so will be passed by reference. */ +typedef struct __attribute__ ((__aligned__ (32))) + { + long x; + long y; + } overaligned; + +#define EXPECTED_STRUCT_SIZE 32 +extern void link_failure (void); +int +foo () +{ + /* Optimization gets rid of this before linking. */ + if (sizeof (overaligned) != EXPECTED_STRUCT_SIZE) + link_failure (); +} + +overaligned a = { 2, 3 }; + +#include "abitest.h" +#else + ARG (int, 7, W0) + /* Alignment should be 8. */ + PTR (overaligned, a, X1) + LAST_ARG (int, 9, W2) +#endif diff --git a/gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-9.c b/gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-9.c new file mode 100644 index 0000000..0f5fa35 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/aapcs64/test_align-9.c @@ -0,0 +1,47 @@ +/* Test AAPCS layout (alignment). */ + +/* { dg-do run { target aarch64*-*-* } } */ + +#ifndef IN_FRAMEWORK +#define TESTFILE "test_align-9.c" + +struct s + { + /* This forces the alignment and size of the struct to 16. */ + __attribute__ ((__aligned__ (16))) long x; + int y; + /* 4 bytes padding. */ + }; + +typedef struct s __attribute__ ((__aligned__ (8))) underaligned; + +#define EXPECTED_STRUCT_SIZE 16 +extern void link_failure (void); +int +foo () +{ + /* Optimization gets rid of this before linking. */ + if (sizeof (struct s) != EXPECTED_STRUCT_SIZE) + link_failure (); +} + +underaligned a = { 1, 4 }; +underaligned b = { 9, 16 }; +underaligned c = { 25, 36 }; + +#include "abitest.h" +#else + ARG (int, 3, W0) + /* Object alignment is 16, so skip X1. */ + ARG (underaligned, a, X2) + ARG (int, 5, W4) + /* Object alignment is 16, so skip X5. */ + ARG (underaligned, b, X6) +#ifndef __AAPCS64_BIG_ENDIAN__ + ARG (int, 7, STACK) +#else + ARG (int, 7, STACK + 4) +#endif + /* Natural alignment should be 16. */ + LAST_ARG (underaligned, c, STACK + 16) +#endif -- 2.7.4 From 8f0da1d02a4956801ccd1d0df529ca68d821d8e9 Mon Sep 17 00:00:00 2001 From: Sangmin Seo Date: Thu, 29 Jun 2017 16:56:39 +0900 Subject: [PATCH 03/16] Do not emit the -Wpsabi note for PR target/77728 Since PR target/77728 issue did not exist in Tizen/Linaro GCC 4.9.2 and has been resolved with the upstream patches, it does not make sense to print the -Wpsabi note, which is "parameter passing for argument of type ... changed in GCC 7.1." This patch removes the -Wpsabi note generated by the commit 6da83d4adc857ba1c4370b2b8f32825d5cb13ebe and dg-message comments from the test code. gcc/config/ * arm/arm.c (aapcs_layout_arg): Remove code emitting -Wpsabi note. (arm_function_arg): Likewise. (arm_function_arg_boundary): Likewise. (arm_setup_incoming_varargs): Likewise. gcc/testsuite/ * g++.dg/abi/pr77728-1.C: Remove dg-message comments. Change-Id: I5432536052bf9d534e21157ddfd2ac57d25da59f Signed-off-by: Sangmin Seo --- gcc/config/arm/arm.c | 23 +++-------------------- gcc/testsuite/g++.dg/abi/pr77728-1.C | 14 ++++---------- 2 files changed, 7 insertions(+), 30 deletions(-) diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index 5ab94d9..f48fe56 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -6208,15 +6208,7 @@ aapcs_layout_arg (CUMULATIVE_ARGS *pcum, machine_mode mode, if (ncrn & 1) { int res = arm_needs_doubleword_align (mode, type); - /* Only warn during RTL expansion of call stmts, otherwise we would - warn e.g. during gimplification even on functions that will be - always inlined, and we'd warn multiple times. Don't warn when - called in expand_function_start either, as we warn instead in - arm_function_arg_boundary in that case. */ - if (res < 0 && warn_psabi && currently_expanding_gimple_stmt) - inform (input_location, "parameter passing for argument of type " - "%qT changed in GCC 7.1", type); - else if (res > 0) + if (res > 0) ncrn++; } @@ -6416,10 +6408,7 @@ arm_function_arg (cumulative_args_t pcum_v, machine_mode mode, if ((pcum->nregs & 1) && ARM_DOUBLEWORD_ALIGN) { int res = arm_needs_doubleword_align (mode, type); - if (res < 0 && warn_psabi) - inform (input_location, "parameter passing for argument of type " - "%qT changed in GCC 7.1", type); - else if (res > 0) + if (res > 0) pcum->nregs++; } @@ -6444,9 +6433,6 @@ arm_function_arg_boundary (machine_mode mode, const_tree type) return PARM_BOUNDARY; int res = arm_needs_doubleword_align (mode, type); - if (res < 0 && warn_psabi) - inform (input_location, "parameter passing for argument of type %qT " - "changed in GCC 7.1", type); return res > 0 ? DOUBLEWORD_ALIGNMENT : PARM_BOUNDARY; } @@ -26342,10 +26328,7 @@ arm_setup_incoming_varargs (cumulative_args_t pcum_v, if (nregs & 1) { int res = arm_needs_doubleword_align (mode, type); - if (res < 0 && warn_psabi) - inform (input_location, "parameter passing for argument of " - "type %qT changed in GCC 7.1", type); - else if (res > 0) + if (res > 0) nregs++; } } diff --git a/gcc/testsuite/g++.dg/abi/pr77728-1.C b/gcc/testsuite/g++.dg/abi/pr77728-1.C index 05f08c9..15ccbfb 100644 --- a/gcc/testsuite/g++.dg/abi/pr77728-1.C +++ b/gcc/testsuite/g++.dg/abi/pr77728-1.C @@ -30,7 +30,7 @@ struct K : public D { typedef A T; int i, j; }; struct L { static double h; int i, j; }; int -fn1 (int a, B<0> b) // { dg-message "note: parameter passing for argument of type \[^\n\r]* changed in GCC 7\.1" } +fn1 (int a, B<0> b) { return a + b.i; } @@ -42,14 +42,13 @@ fn2 (int a, B<1> b) } int -fn3 (int a, L b) // { dg-message "note: parameter passing for argument of type \[^\n\r]* changed in GCC 7\.1" } +fn3 (int a, L b) { return a + b.i; } int fn4 (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, B<0> n, ...) -// { dg-message "note: parameter passing for argument of type \[^\n\r]* changed in GCC 7\.1" "" { target *-*-* } .-1 } { va_list ap; va_start (ap, n); @@ -110,7 +109,6 @@ fn9 (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k int fn10 (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, J n, ...) -// { dg-message "note: parameter passing for argument of type \[^\n\r]* changed in GCC 7\.1" "" { target *-*-* } .-1 } { va_list ap; va_start (ap, n); @@ -121,7 +119,6 @@ fn10 (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int int fn11 (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, K<0> n, ...) -// { dg-message "note: parameter passing for argument of type \[^\n\r]* changed in GCC 7\.1" "" { target *-*-* } .-1 } { va_list ap; va_start (ap, n); @@ -153,19 +150,16 @@ test () static J j; static K<0> k0; static K<2> k2; - fn1 (1, b0); // { dg-message "note: parameter passing for argument of type \[^\n\r]* changed in GCC 7\.1" } + fn1 (1, b0); fn2 (1, b1); - fn3 (1, l); // { dg-message "note: parameter passing for argument of type \[^\n\r]* changed in GCC 7\.1" } + fn3 (1, l); fn4 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, b0, 1, 2, 3, 4); - // { dg-message "note: parameter passing for argument of type \[^\n\r]* changed in GCC 7\.1" "" { target *-*-* } .-1 } fn5 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, b1, 1, 2, 3, 4); fn6 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, c, 1, 2, 3, 4); fn7 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, e, 1, 2, 3, 4); fn8 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, h, 1, 2, 3, 4); fn9 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, i, 1, 2, 3, 4); fn10 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, j, 1, 2, 3, 4); - // { dg-message "note: parameter passing for argument of type \[^\n\r]* changed in GCC 7\.1" "" { target *-*-* } .-1 } fn11 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, k0, 1, 2, 3, 4); - // { dg-message "note: parameter passing for argument of type \[^\n\r]* changed in GCC 7\.1" "" { target *-*-* } .-1 } fn12 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, k2, 1, 2, 3, 4); } -- 2.7.4 From 84bd4b431e26d7a03cb4318423fed860a9a9dfe2 Mon Sep 17 00:00:00 2001 From: Slava Barinov Date: Thu, 13 Jul 2017 13:35:02 +0300 Subject: [PATCH 04/16] packaging: Add %gcc_force_options and %gcc_unforce_options macros The macros can be used in certain .spec files to switch off sanitization of parts or modules without hard-coding sanitizer flags into packages. Change-Id: I1643a3bdd64a3d48855f02622feb064d482e3655 Signed-off-by: Slava Barinov Signed-off-by: Sangmin Seo --- packaging/gcc-aarch64.spec | 48 ++++++++++++++++++++++++++++++++++++---------- packaging/gcc-armv7l.spec | 48 ++++++++++++++++++++++++++++++++++++---------- packaging/linaro-gcc.spec | 48 ++++++++++++++++++++++++++++++++++++---------- 3 files changed, 114 insertions(+), 30 deletions(-) diff --git a/packaging/gcc-aarch64.spec b/packaging/gcc-aarch64.spec index 40d1ae6..89261f6 100644 --- a/packaging/gcc-aarch64.spec +++ b/packaging/gcc-aarch64.spec @@ -54,6 +54,14 @@ %define libdir %{!?cross:%{_libdir}}%{?cross:%{_prefix}/lib%{?aarch64:64}} %define libsubdir %{libdir}/gcc/%{target_arch}/%{version} +%define asan_force_options -fsanitize-recover=address -fsanitize=address -fno-omit-frame-pointer -Wp,-U_FORTIFY_SOURCE +%define ubsan_force_options -fsanitize=undefined,bounds-strict,float-divide-by-zero,float-cast-overflow +%define lsan_force_options -fsanitize=leak -fno-omit-frame-pointer -Wp,-U_FORTIFY_SOURCE + +%define asan_runtime_options halt_on_error=false:start_deactivated=true:print_cmdline=true:quarantine_size_mb=1:detect_leaks=0:log_path=/tmp/asan.log:log_exe_name=1 +%define ubsan_runtime_options print_cmdline=true:log_path=/tmp/ubsan.log +%define lsan_runtime_options print_cmdline=true:detect_leaks=1:log_path=/tmp/lsan.log:log_exe_name=1:fast_unwind_on_malloc=false:malloc_context_size=5:suppressions=/lsan.supp:print_suppressions=false + Name: gcc%{?cross:-%{cross}} # With generated files in src we could drop the following BuildRequires: bison @@ -649,14 +657,17 @@ Scripts for ASan instrumentation # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin [ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ -/usr/bin/gcc-force-options -fsanitize-recover=address -fsanitize=address -fno-common -fno-omit-frame-pointer -U_FORTIFY_SOURCE -# Add restore_fcommon macro +/usr/bin/gcc-force-options %asan_force_options -fno-common +# Add ASan-related macros cat >> /usr/lib/rpm/tizen_macros << EOF -%restore_fcommon \\ - /usr/bin/gcc-unforce-options \\ - /usr/bin/gcc-force-options -fsanitize-recover=address -fsanitize=address -fcommon -fno-omit-frame-pointer -U_FORTIFY_SOURCE - +%%asan_force_options %{asan_force_options} +%%gcc_unforce_options /usr/bin/gcc-unforce-options +%%gcc_force_options /usr/bin/gcc-force-options +%%gcc_force_default_options %%gcc_force_options %%asan_force_options -fno-common +%%restore_fcommon \\ + %%gcc_unforce_options \\ + %%gcc_force_options %%asan_force_options -fcommon EOF %preun -n asan-force-options @@ -678,7 +689,15 @@ Scripts for UBSan instrumentation # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin [ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ -/usr/bin/gcc-force-options -fsanitize=undefined,bounds-strict,float-divide-by-zero,float-cast-overflow +/usr/bin/gcc-force-options %ubsan_force_options +# Add UBSan-related macros +cat >> /usr/lib/rpm/tizen_macros << EOF + +%%ubsan_force_options %{ubsan_force_options} +%%gcc_unforce_options /usr/bin/gcc-unforce-options +%%gcc_force_options /usr/bin/gcc-force-options +%%gcc_force_default_options %%gcc_force_options %%ubsan_force_options +EOF %preun -n ubsan-force-options # Restore read-only mode @@ -711,7 +730,15 @@ Scripts for LSan instrumentation # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin [ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ -/usr/bin/gcc-force-options -fsanitize=leak -fno-omit-frame-pointer -U_FORTIFY_SOURCE +/usr/bin/gcc-force-options %lsan_force_options +# Add LSan-related macros +cat >> /usr/lib/rpm/tizen_macros << EOF + +%%lsan_force_options %{lsan_force_options} +%%gcc_unforce_options /usr/bin/gcc-unforce-options +%%gcc_force_options /usr/bin/gcc-force-options +%%gcc_force_default_options %%gcc_force_options %%lsan_force_options +EOF %preun -n lsan-force-options # Restore read-only mode @@ -754,6 +781,7 @@ LSan runtime environment echo "%{libdir}/liblsan.so" > /etc/ld.so.preload [ -f /etc/ld.so.preload.orig ] && cat /etc/ld.so.preload.orig >> /etc/ld.so.preload [ -f /etc/ld.so.preload.orig ] && rm -f /etc/ld.so.preload.orig +echo "%{lsan_runtime_options}" > /LSAN_OPTIONS chsmack -a "_" /etc/ld.so.preload /LSAN_OPTIONS /lsan.supp %preun -n lsan-runtime-env @@ -785,7 +813,7 @@ Asan runtime environment echo "%{libdir}/libasan.so" > /etc/ld.so.preload [ -f /etc/ld.so.preload.orig ] && cat /etc/ld.so.preload.orig >> /etc/ld.so.preload [ -f /etc/ld.so.preload.orig ] && rm -f /etc/ld.so.preload.orig -echo "halt_on_error=false:start_deactivated=true:print_cmdline=true:quarantine_size_mb=1:detect_leaks=0:log_path=/tmp/asan.log:log_exe_name=1" > /ASAN_OPTIONS +echo "%{asan_runtime_options}" > /ASAN_OPTIONS chsmack -a "_" /etc/ld.so.preload /ASAN_OPTIONS %preun -n asan-runtime-env @@ -801,7 +829,7 @@ Requires: libubsan UBSan runtime environment %post -n ubsan-runtime-env -echo "print_cmdline=true:log_path=/tmp/ubsan.log" > /UBSAN_OPTIONS +echo "%{ubsan_runtime_options}" > /UBSAN_OPTIONS %package plugin-devel Summary: GNU GCC Plugin development files diff --git a/packaging/gcc-armv7l.spec b/packaging/gcc-armv7l.spec index f3ad3bd..9c163e1 100644 --- a/packaging/gcc-armv7l.spec +++ b/packaging/gcc-armv7l.spec @@ -54,6 +54,14 @@ %define libdir %{!?cross:%{_libdir}}%{?cross:%{_prefix}/lib%{?aarch64:64}} %define libsubdir %{libdir}/gcc/%{target_arch}/%{version} +%define asan_force_options -fsanitize-recover=address -fsanitize=address -fno-omit-frame-pointer -Wp,-U_FORTIFY_SOURCE +%define ubsan_force_options -fsanitize=undefined,bounds-strict,float-divide-by-zero,float-cast-overflow +%define lsan_force_options -fsanitize=leak -fno-omit-frame-pointer -Wp,-U_FORTIFY_SOURCE + +%define asan_runtime_options halt_on_error=false:start_deactivated=true:print_cmdline=true:quarantine_size_mb=1:detect_leaks=0:log_path=/tmp/asan.log:log_exe_name=1 +%define ubsan_runtime_options print_cmdline=true:log_path=/tmp/ubsan.log +%define lsan_runtime_options print_cmdline=true:detect_leaks=1:log_path=/tmp/lsan.log:log_exe_name=1:fast_unwind_on_malloc=false:malloc_context_size=5:suppressions=/lsan.supp:print_suppressions=false + Name: gcc%{?cross:-%{cross}} # With generated files in src we could drop the following BuildRequires: bison @@ -649,14 +657,17 @@ Scripts for ASan instrumentation # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin [ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ -/usr/bin/gcc-force-options -fsanitize-recover=address -fsanitize=address -fno-common -fno-omit-frame-pointer -U_FORTIFY_SOURCE -# Add restore_fcommon macro +/usr/bin/gcc-force-options %asan_force_options -fno-common +# Add ASan-related macros cat >> /usr/lib/rpm/tizen_macros << EOF -%restore_fcommon \\ - /usr/bin/gcc-unforce-options \\ - /usr/bin/gcc-force-options -fsanitize-recover=address -fsanitize=address -fcommon -fno-omit-frame-pointer -U_FORTIFY_SOURCE - +%%asan_force_options %{asan_force_options} +%%gcc_unforce_options /usr/bin/gcc-unforce-options +%%gcc_force_options /usr/bin/gcc-force-options +%%gcc_force_default_options %%gcc_force_options %%asan_force_options -fno-common +%%restore_fcommon \\ + %%gcc_unforce_options \\ + %%gcc_force_options %%asan_force_options -fcommon EOF %preun -n asan-force-options @@ -678,7 +689,15 @@ Scripts for UBSan instrumentation # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin [ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ -/usr/bin/gcc-force-options -fsanitize=undefined,bounds-strict,float-divide-by-zero,float-cast-overflow +/usr/bin/gcc-force-options %ubsan_force_options +# Add UBSan-related macros +cat >> /usr/lib/rpm/tizen_macros << EOF + +%%ubsan_force_options %{ubsan_force_options} +%%gcc_unforce_options /usr/bin/gcc-unforce-options +%%gcc_force_options /usr/bin/gcc-force-options +%%gcc_force_default_options %%gcc_force_options %%ubsan_force_options +EOF %preun -n ubsan-force-options # Restore read-only mode @@ -711,7 +730,15 @@ Scripts for LSan instrumentation # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin [ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ -/usr/bin/gcc-force-options -fsanitize=leak -fno-omit-frame-pointer -U_FORTIFY_SOURCE +/usr/bin/gcc-force-options %lsan_force_options +# Add LSan-related macros +cat >> /usr/lib/rpm/tizen_macros << EOF + +%%lsan_force_options %{lsan_force_options} +%%gcc_unforce_options /usr/bin/gcc-unforce-options +%%gcc_force_options /usr/bin/gcc-force-options +%%gcc_force_default_options %%gcc_force_options %%lsan_force_options +EOF %preun -n lsan-force-options # Restore read-only mode @@ -754,6 +781,7 @@ LSan runtime environment echo "%{libdir}/liblsan.so" > /etc/ld.so.preload [ -f /etc/ld.so.preload.orig ] && cat /etc/ld.so.preload.orig >> /etc/ld.so.preload [ -f /etc/ld.so.preload.orig ] && rm -f /etc/ld.so.preload.orig +echo "%{lsan_runtime_options}" > /LSAN_OPTIONS chsmack -a "_" /etc/ld.so.preload /LSAN_OPTIONS /lsan.supp %preun -n lsan-runtime-env @@ -785,7 +813,7 @@ Asan runtime environment echo "%{libdir}/libasan.so" > /etc/ld.so.preload [ -f /etc/ld.so.preload.orig ] && cat /etc/ld.so.preload.orig >> /etc/ld.so.preload [ -f /etc/ld.so.preload.orig ] && rm -f /etc/ld.so.preload.orig -echo "halt_on_error=false:start_deactivated=true:print_cmdline=true:quarantine_size_mb=1:detect_leaks=0:log_path=/tmp/asan.log:log_exe_name=1" > /ASAN_OPTIONS +echo "%{asan_runtime_options}" > /ASAN_OPTIONS chsmack -a "_" /etc/ld.so.preload /ASAN_OPTIONS %preun -n asan-runtime-env @@ -801,7 +829,7 @@ Requires: libubsan UBSan runtime environment %post -n ubsan-runtime-env -echo "print_cmdline=true:log_path=/tmp/ubsan.log" > /UBSAN_OPTIONS +echo "%{ubsan_runtime_options}" > /UBSAN_OPTIONS %package plugin-devel Summary: GNU GCC Plugin development files diff --git a/packaging/linaro-gcc.spec b/packaging/linaro-gcc.spec index c6460cf..49d2499 100644 --- a/packaging/linaro-gcc.spec +++ b/packaging/linaro-gcc.spec @@ -51,6 +51,14 @@ %define libdir %{!?cross:%{_libdir}}%{?cross:%{_prefix}/lib%{?aarch64:64}} %define libsubdir %{libdir}/gcc/%{target_arch}/%{version} +%define asan_force_options -fsanitize-recover=address -fsanitize=address -fno-omit-frame-pointer -Wp,-U_FORTIFY_SOURCE +%define ubsan_force_options -fsanitize=undefined,bounds-strict,float-divide-by-zero,float-cast-overflow +%define lsan_force_options -fsanitize=leak -fno-omit-frame-pointer -Wp,-U_FORTIFY_SOURCE + +%define asan_runtime_options halt_on_error=false:start_deactivated=true:print_cmdline=true:quarantine_size_mb=1:detect_leaks=0:log_path=/tmp/asan.log:log_exe_name=1 +%define ubsan_runtime_options print_cmdline=true:log_path=/tmp/ubsan.log +%define lsan_runtime_options print_cmdline=true:detect_leaks=1:log_path=/tmp/lsan.log:log_exe_name=1:fast_unwind_on_malloc=false:malloc_context_size=5:suppressions=/lsan.supp:print_suppressions=false + Name: gcc%{?cross:-%{cross}} # With generated files in src we could drop the following BuildRequires: bison @@ -646,14 +654,17 @@ Scripts for ASan instrumentation # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin [ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ -/usr/bin/gcc-force-options -fsanitize-recover=address -fsanitize=address -fno-common -fno-omit-frame-pointer -U_FORTIFY_SOURCE -# Add restore_fcommon macro +/usr/bin/gcc-force-options %asan_force_options -fno-common +# Add ASan-related macros cat >> /usr/lib/rpm/tizen_macros << EOF -%restore_fcommon \\ - /usr/bin/gcc-unforce-options \\ - /usr/bin/gcc-force-options -fsanitize-recover=address -fsanitize=address -fcommon -fno-omit-frame-pointer -U_FORTIFY_SOURCE - +%%asan_force_options %{asan_force_options} +%%gcc_unforce_options /usr/bin/gcc-unforce-options +%%gcc_force_options /usr/bin/gcc-force-options +%%gcc_force_default_options %%gcc_force_options %%asan_force_options -fno-common +%%restore_fcommon \\ + %%gcc_unforce_options \\ + %%gcc_force_options %%asan_force_options -fcommon EOF %preun -n asan-force-options @@ -675,7 +686,15 @@ Scripts for UBSan instrumentation # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin [ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ -/usr/bin/gcc-force-options -fsanitize=undefined,bounds-strict,float-divide-by-zero,float-cast-overflow +/usr/bin/gcc-force-options %ubsan_force_options +# Add UBSan-related macros +cat >> /usr/lib/rpm/tizen_macros << EOF + +%%ubsan_force_options %{ubsan_force_options} +%%gcc_unforce_options /usr/bin/gcc-unforce-options +%%gcc_force_options /usr/bin/gcc-force-options +%%gcc_force_default_options %%gcc_force_options %%ubsan_force_options +EOF %preun -n ubsan-force-options # Restore read-only mode @@ -708,7 +727,15 @@ Scripts for LSan instrumentation # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin [ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ -/usr/bin/gcc-force-options -fsanitize=leak -fno-omit-frame-pointer -U_FORTIFY_SOURCE +/usr/bin/gcc-force-options %lsan_force_options +# Add LSan-related macros +cat >> /usr/lib/rpm/tizen_macros << EOF + +%%lsan_force_options %{lsan_force_options} +%%gcc_unforce_options /usr/bin/gcc-unforce-options +%%gcc_force_options /usr/bin/gcc-force-options +%%gcc_force_default_options %%gcc_force_options %%lsan_force_options +EOF %preun -n lsan-force-options # Restore read-only mode @@ -751,6 +778,7 @@ LSan runtime environment echo "%{libdir}/liblsan.so" > /etc/ld.so.preload [ -f /etc/ld.so.preload.orig ] && cat /etc/ld.so.preload.orig >> /etc/ld.so.preload [ -f /etc/ld.so.preload.orig ] && rm -f /etc/ld.so.preload.orig +echo "%{lsan_runtime_options}" > /LSAN_OPTIONS chsmack -a "_" /etc/ld.so.preload /LSAN_OPTIONS /lsan.supp %preun -n lsan-runtime-env @@ -782,7 +810,7 @@ Asan runtime environment echo "%{libdir}/libasan.so" > /etc/ld.so.preload [ -f /etc/ld.so.preload.orig ] && cat /etc/ld.so.preload.orig >> /etc/ld.so.preload [ -f /etc/ld.so.preload.orig ] && rm -f /etc/ld.so.preload.orig -echo "halt_on_error=false:start_deactivated=true:print_cmdline=true:quarantine_size_mb=1:detect_leaks=0:log_path=/tmp/asan.log:log_exe_name=1" > /ASAN_OPTIONS +echo "%{asan_runtime_options}" > /ASAN_OPTIONS chsmack -a "_" /etc/ld.so.preload /ASAN_OPTIONS %preun -n asan-runtime-env @@ -798,7 +826,7 @@ Requires: libubsan UBSan runtime environment %post -n ubsan-runtime-env -echo "print_cmdline=true:log_path=/tmp/ubsan.log" > /UBSAN_OPTIONS +echo "%{ubsan_runtime_options}" > /UBSAN_OPTIONS %package plugin-devel Summary: GNU GCC Plugin development files -- 2.7.4 From c51983b611f888ce51c6c137b50038a0daee45e9 Mon Sep 17 00:00:00 2001 From: Sangmin Seo Date: Tue, 18 Jul 2017 14:01:23 +0900 Subject: [PATCH 05/16] packaging: create a wrapper for collect2 in gcc-force-options gcc-force-options used to prepend/append all force options to the gcc/g++ wrappers. This approach worked well for most packages but caused some configure scripts to behave differently when linker flags are included in the force options. For example, if g++ is called with -ldl appended, it always goes to the linking phase. As a result, when configure has expanded AC_PROG_CXXCPP macro, variables related to preprocessor are not properly set and actual build ends up using incorrect compile and link flags. Since prepending or appending linker flags to the linker makes more sense than carrying them from the compilation phase, this patch basically divides the force options into compiler flags and linker ones, and inserts them to corresponding wrappers. Consequently, a wrapper for collect2 is created, as we did for gcc and g++, in order to pass the linker flags to the linker. Note that collect2 bridges the compiler and the actual linker, and thus creating a wrapper for collect2 is more portable and succinct than doing so for linkers. Change-Id: I8733bf14c5a2c04bfebe38a339c94e7a738cf17c Signed-off-by: Sangmin Seo --- packaging/gcc-aarch64.spec | 24 ++++++++++---- packaging/gcc-armv7l.spec | 24 ++++++++++---- packaging/gcc-force-options | 73 ++++++++++++++++++++++++++++++++++++++----- packaging/gcc-unforce-options | 2 +- packaging/linaro-gcc.spec | 24 ++++++++++---- 5 files changed, 121 insertions(+), 26 deletions(-) diff --git a/packaging/gcc-aarch64.spec b/packaging/gcc-aarch64.spec index 89261f6..ef0e568 100644 --- a/packaging/gcc-aarch64.spec +++ b/packaging/gcc-aarch64.spec @@ -656,7 +656,9 @@ Scripts for ASan instrumentation %post -n asan-force-options # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin -[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a+w %{libsubdir} +[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a+w /usr/bin/gcc-force-options %asan_force_options -fno-common # Add ASan-related macros cat >> /usr/lib/rpm/tizen_macros << EOF @@ -673,7 +675,9 @@ EOF %preun -n asan-force-options # Restore read-only mode chmod a-w /usr/bin -[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a-w %{libsubdir} +[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a-w /usr/bin/gcc-unforce-options %package -n ubsan-force-options @@ -688,7 +692,9 @@ Scripts for UBSan instrumentation %post -n ubsan-force-options # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin -[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a+w %{libsubdir} +[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a+w /usr/bin/gcc-force-options %ubsan_force_options # Add UBSan-related macros cat >> /usr/lib/rpm/tizen_macros << EOF @@ -702,7 +708,9 @@ EOF %preun -n ubsan-force-options # Restore read-only mode chmod a-w /usr/bin -[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a-w %{libsubdir} +[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a-w /usr/bin/gcc-unforce-options %package -n ubsan-build-env @@ -729,7 +737,9 @@ Scripts for LSan instrumentation %post -n lsan-force-options # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin -[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a+w %{libsubdir} +[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a+w /usr/bin/gcc-force-options %lsan_force_options # Add LSan-related macros cat >> /usr/lib/rpm/tizen_macros << EOF @@ -743,7 +753,9 @@ EOF %preun -n lsan-force-options # Restore read-only mode chmod a-w /usr/bin -[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a-w %{libsubdir} +[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a-w /usr/bin/gcc-unforce-options [ -d /emul/ ] && chmod a-w /emul/usr/bin/ diff --git a/packaging/gcc-armv7l.spec b/packaging/gcc-armv7l.spec index 9c163e1..878d527 100644 --- a/packaging/gcc-armv7l.spec +++ b/packaging/gcc-armv7l.spec @@ -656,7 +656,9 @@ Scripts for ASan instrumentation %post -n asan-force-options # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin -[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a+w %{libsubdir} +[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a+w /usr/bin/gcc-force-options %asan_force_options -fno-common # Add ASan-related macros cat >> /usr/lib/rpm/tizen_macros << EOF @@ -673,7 +675,9 @@ EOF %preun -n asan-force-options # Restore read-only mode chmod a-w /usr/bin -[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a-w %{libsubdir} +[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a-w /usr/bin/gcc-unforce-options %package -n ubsan-force-options @@ -688,7 +692,9 @@ Scripts for UBSan instrumentation %post -n ubsan-force-options # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin -[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a+w %{libsubdir} +[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a+w /usr/bin/gcc-force-options %ubsan_force_options # Add UBSan-related macros cat >> /usr/lib/rpm/tizen_macros << EOF @@ -702,7 +708,9 @@ EOF %preun -n ubsan-force-options # Restore read-only mode chmod a-w /usr/bin -[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a-w %{libsubdir} +[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a-w /usr/bin/gcc-unforce-options %package -n ubsan-build-env @@ -729,7 +737,9 @@ Scripts for LSan instrumentation %post -n lsan-force-options # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin -[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a+w %{libsubdir} +[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a+w /usr/bin/gcc-force-options %lsan_force_options # Add LSan-related macros cat >> /usr/lib/rpm/tizen_macros << EOF @@ -743,7 +753,9 @@ EOF %preun -n lsan-force-options # Restore read-only mode chmod a-w /usr/bin -[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a-w %{libsubdir} +[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a-w /usr/bin/gcc-unforce-options [ -d /emul/ ] && chmod a-w /emul/usr/bin/ diff --git a/packaging/gcc-force-options b/packaging/gcc-force-options index 34f4945..0dfdd9a 100644 --- a/packaging/gcc-force-options +++ b/packaging/gcc-force-options @@ -14,27 +14,64 @@ EOF exit 1 fi -FLAGS="$@" - if [ $(find $(dirname $0) -name \*-real | wc -l) -gt 0 ]; then echo >&2 "$(basename $0): directory was already processed, aborting" exit 1 fi +FLAGS="" +LD_FLAGS="" + +function divide_flags { + NEED_LIB_NAME="N" + for f in "$@"; do + case $f in + -l) + NEED_LIB_NAME="Y" + ;; + + -l*) + LDFLAGS="$LDFLAGS $f" + ;; + + -Wl,*) + LDFLAGS="$LDFLAGS ${f:4}" + ;; + + *) + if [ "$NEED_LIB_NAME" = "Y" ]; then + LDFLAGS="$LDFLAGS -l$f" + NEED_LIB_NAME="N" + else + FLAGS="$FLAGS $f" + fi + esac + done +} + case "$1" in prepend) shift - PREFLAGS="$@" + divide_flags $@ + PREFLAGS=$FLAGS POSTFLAGS= + LD_PREFLAGS=$LDFLAGS + LD_POSTFLAGS= ;; append) shift + divide_flags $@ PREFLAGS= - POSTFLAGS="$@" + POSTFLAGS=$FLAGS + LD_PREFLAGS= + LD_POSTFLAGS=$LDFLAGS ;; *) + divide_flags $@ PREFLAGS= - POSTFLAGS="$@" + POSTFLAGS=$FLAGS + LD_PREFLAGS= + LD_POSTFLAGS=$LDFLAGS ;; esac @@ -50,7 +87,9 @@ elif ! echo "\$@" | grep -q -e __KERNEL__ -e \-nostdlib; then # Use readlink in order to follow symlinks if any \$(readlink -f \$0)-real $PREFLAGS "\$@" $POSTFLAGS else - \$(readlink -f \$0)-real "\$@" + # -Wl,--tizen-no-force-options is used to tell collect2 to not add force + # options. It will be removed in collect2. + \$(readlink -f \$0)-real "\$@" -Wl,--tizen-no-force-options fi EOF chmod +x $TMP @@ -60,11 +99,31 @@ find -L $(dirname $0) -type f -a -perm -a=x | grep -E '(gcc|g\+\+|c\+\+)$' | whi cp $TMP $tool done +LD_TMP=$(pwd)/ld_tmp.$$ +cat > $LD_TMP << EOF +#!/bin/sh +if ! echo "\$@" | grep -q -e \-\-tizen\-no\-force\-options; then + # Use readlink in order to follow symlinks if any + \$(readlink -f \$0)-real $LD_PREFLAGS "\$@" $LD_POSTFLAGS +else + # Remove --tizen-no-force-options from the argument list + FLAGS=\$(echo \$@ | sed -e 's/--tizen-no-force-options//g') + \$(readlink -f \$0)-real \$FLAGS +fi +EOF +chmod +x $LD_TMP + +find -L /usr/*/gcc -type f -a -perm -a=x -name 'collect2' | while read tool; do + mv $tool $tool-real + cp $LD_TMP $tool +done + if [ -d /emul ]; then - find -L /emul -type f -a -perm -a=x | grep -E '(gcc|g\+\+|c\+\+)$' | while read tool; do + find -L /emul -type f -a -perm -a=x | grep -E '(gcc|g\+\+|c\+\+|collect2)$' | while read tool; do ln -sf $(basename $tool) $tool-real done fi rm $TMP +rm $LD_TMP diff --git a/packaging/gcc-unforce-options b/packaging/gcc-unforce-options index cf36d04..7e85b75 100644 --- a/packaging/gcc-unforce-options +++ b/packaging/gcc-unforce-options @@ -7,7 +7,7 @@ if [ $# -gt 0 ]; then exit 1 fi -find $(dirname $0) -name \*-real | while read tool_real; do +find $(dirname $0) /usr/*/gcc -name \*-real | while read tool_real; do tool=$(echo "$tool_real" | sed -e 's/-real$//') mv $tool_real $tool done diff --git a/packaging/linaro-gcc.spec b/packaging/linaro-gcc.spec index 49d2499..5058a99 100644 --- a/packaging/linaro-gcc.spec +++ b/packaging/linaro-gcc.spec @@ -653,7 +653,9 @@ Scripts for ASan instrumentation %post -n asan-force-options # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin -[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a+w %{libsubdir} +[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a+w /usr/bin/gcc-force-options %asan_force_options -fno-common # Add ASan-related macros cat >> /usr/lib/rpm/tizen_macros << EOF @@ -670,7 +672,9 @@ EOF %preun -n asan-force-options # Restore read-only mode chmod a-w /usr/bin -[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a-w %{libsubdir} +[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a-w /usr/bin/gcc-unforce-options %package -n ubsan-force-options @@ -685,7 +689,9 @@ Scripts for UBSan instrumentation %post -n ubsan-force-options # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin -[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a+w %{libsubdir} +[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a+w /usr/bin/gcc-force-options %ubsan_force_options # Add UBSan-related macros cat >> /usr/lib/rpm/tizen_macros << EOF @@ -699,7 +705,9 @@ EOF %preun -n ubsan-force-options # Restore read-only mode chmod a-w /usr/bin -[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a-w %{libsubdir} +[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a-w /usr/bin/gcc-unforce-options %package -n ubsan-build-env @@ -726,7 +734,9 @@ Scripts for LSan instrumentation %post -n lsan-force-options # Change mode to allow all users to run gcc-force/unforce-options chmod a+w /usr/bin -[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a+w %{libsubdir} +[ -d /emul/ ] && chmod a+w /emul/usr/bin/ && chmod a+w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a+w /usr/bin/gcc-force-options %lsan_force_options # Add LSan-related macros cat >> /usr/lib/rpm/tizen_macros << EOF @@ -740,7 +750,9 @@ EOF %preun -n lsan-force-options # Restore read-only mode chmod a-w /usr/bin -[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ +chmod a-w %{libsubdir} +[ -d /emul/ ] && chmod a-w /emul/usr/bin/ && chmod a-w /emul/home/abuild/rpmbuild/BUILD/gcc-%{version}/obj/gcc/ \ + && find -L /emul/usr/*/gcc -name 'collect2' | xargs dirname | xargs chmod a-w /usr/bin/gcc-unforce-options [ -d /emul/ ] && chmod a-w /emul/usr/bin/ -- 2.7.4 From 66898b859383e02b24865b7712423dd7e258bcdc Mon Sep 17 00:00:00 2001 From: Sangmin Seo Date: Wed, 12 Jul 2017 18:37:50 +0900 Subject: [PATCH 06/16] packaging: append -ldl -lpthread to ASan force options. When building for address sanitization, some packages complain about missing symbols due to omitted -ldl or -pthread. This patch appends -ldl and -lpthread to the ASan force options in order to resolve this kind of issue. Note that since -ldl and -lpthread are already needed by libasan, adding them to the ASan force options should not cause any problems. This patch also adds -Wl,--as-needed before -ldl -lpthread in the force options to prevent libdl or libpthread from being linked when unnecessary. Change-Id: Ic50059d4684e15773f56c589cfacda0bc944d955 Signed-off-by: Sangmin Seo --- packaging/gcc-aarch64.spec | 2 +- packaging/gcc-armv7l.spec | 2 +- packaging/linaro-gcc.spec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packaging/gcc-aarch64.spec b/packaging/gcc-aarch64.spec index ef0e568..b10c27c 100644 --- a/packaging/gcc-aarch64.spec +++ b/packaging/gcc-aarch64.spec @@ -54,7 +54,7 @@ %define libdir %{!?cross:%{_libdir}}%{?cross:%{_prefix}/lib%{?aarch64:64}} %define libsubdir %{libdir}/gcc/%{target_arch}/%{version} -%define asan_force_options -fsanitize-recover=address -fsanitize=address -fno-omit-frame-pointer -Wp,-U_FORTIFY_SOURCE +%define asan_force_options -fsanitize-recover=address -fsanitize=address -fno-omit-frame-pointer -Wp,-U_FORTIFY_SOURCE -Wl,--as-needed -ldl -lpthread %define ubsan_force_options -fsanitize=undefined,bounds-strict,float-divide-by-zero,float-cast-overflow %define lsan_force_options -fsanitize=leak -fno-omit-frame-pointer -Wp,-U_FORTIFY_SOURCE diff --git a/packaging/gcc-armv7l.spec b/packaging/gcc-armv7l.spec index 878d527..cf24112 100644 --- a/packaging/gcc-armv7l.spec +++ b/packaging/gcc-armv7l.spec @@ -54,7 +54,7 @@ %define libdir %{!?cross:%{_libdir}}%{?cross:%{_prefix}/lib%{?aarch64:64}} %define libsubdir %{libdir}/gcc/%{target_arch}/%{version} -%define asan_force_options -fsanitize-recover=address -fsanitize=address -fno-omit-frame-pointer -Wp,-U_FORTIFY_SOURCE +%define asan_force_options -fsanitize-recover=address -fsanitize=address -fno-omit-frame-pointer -Wp,-U_FORTIFY_SOURCE -Wl,--as-needed -ldl -lpthread %define ubsan_force_options -fsanitize=undefined,bounds-strict,float-divide-by-zero,float-cast-overflow %define lsan_force_options -fsanitize=leak -fno-omit-frame-pointer -Wp,-U_FORTIFY_SOURCE diff --git a/packaging/linaro-gcc.spec b/packaging/linaro-gcc.spec index 5058a99..34d9221 100644 --- a/packaging/linaro-gcc.spec +++ b/packaging/linaro-gcc.spec @@ -51,7 +51,7 @@ %define libdir %{!?cross:%{_libdir}}%{?cross:%{_prefix}/lib%{?aarch64:64}} %define libsubdir %{libdir}/gcc/%{target_arch}/%{version} -%define asan_force_options -fsanitize-recover=address -fsanitize=address -fno-omit-frame-pointer -Wp,-U_FORTIFY_SOURCE +%define asan_force_options -fsanitize-recover=address -fsanitize=address -fno-omit-frame-pointer -Wp,-U_FORTIFY_SOURCE -Wl,--as-needed -ldl -lpthread %define ubsan_force_options -fsanitize=undefined,bounds-strict,float-divide-by-zero,float-cast-overflow %define lsan_force_options -fsanitize=leak -fno-omit-frame-pointer -Wp,-U_FORTIFY_SOURCE -- 2.7.4 From d37e8fc0d311abc9e5c9dd153a91ba3731380a00 Mon Sep 17 00:00:00 2001 From: Mikhail Kashkarov Date: Thu, 13 Jul 2017 17:02:39 +0300 Subject: [PATCH 07/16] [TTC-2] Fix asan_symbolize.py for C++ function prototypes detection. Change-Id: Iffa30dce19f99506312a93b79cc8deb05404a82a --- packaging/asan_symbolize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/asan_symbolize.py b/packaging/asan_symbolize.py index 0ce93b7..235a273 100644 --- a/packaging/asan_symbolize.py +++ b/packaging/asan_symbolize.py @@ -630,7 +630,7 @@ class SymbolizationLoop(object): self.current_line = line.rstrip() #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) stack_trace_line_format = ( - '^( *#([0-9]+) *)(0x[0-9a-f]+)( *in [^ ]+)? *\((.*)\+(0x[0-9a-f]+)\)') + '^( *#([0-9]+) *)(0x[0-9a-f]+)( *in [^/]+)? *\((.*)\+(0x[0-9a-f]+)\)') match = re.match(stack_trace_line_format, line) if not match: return [self.current_line] -- 2.7.4 From b9d84e7357a3afafbf26e6f24325006546763ce6 Mon Sep 17 00:00:00 2001 From: Mikhail Kashkarov Date: Thu, 20 Jul 2017 14:19:13 +0300 Subject: [PATCH 08/16] [TTC-3] Fix asan_symbolize.py output frame numbers. If input line already symbolized and has format '#0 0x7f6e35cf2e45 in func foo:46' - fix internal frame number. packaging/ * asan_symbolized.py (have_line_to_symbolized): New function. Change-Id: I2c52c58f9e2d6dfce709e87dee2abd62b642bcad --- packaging/asan_symbolize.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) mode change 100644 => 100755 packaging/asan_symbolize.py diff --git a/packaging/asan_symbolize.py b/packaging/asan_symbolize.py old mode 100644 new mode 100755 index 235a273..f37530b --- a/packaging/asan_symbolize.py +++ b/packaging/asan_symbolize.py @@ -626,13 +626,29 @@ class SymbolizationLoop(object): def process_line_echo(self, line): return [line.rstrip()] - def process_line_posix(self, line): - self.current_line = line.rstrip() - #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) + def have_line_to_symbolize(self, line): + #0 0x7f6e35cf2e45 in func (/blah/foo.so+0x11fe45) stack_trace_line_format = ( '^( *#([0-9]+) *)(0x[0-9a-f]+)( *in [^/]+)? *\((.*)\+(0x[0-9a-f]+)\)') match = re.match(stack_trace_line_format, line) if not match: + # If already symbolized (format below) - fix given frame number + #0 0x7f6e35cf2e45 in func foo:46 + stack_trace_line_format_symbolized = ( + '^( *#([0-9]+) *)(0x[0-9a-f]+)( *in [^/]+)? *(.*)\:([0-9]+)') + match_symbolized = re.match(stack_trace_line_format_symbolized, line); + if match_symbolized: + # Frame number from line + match_frame_number = match_symbolized.group(2) + if (self.frame_no != int(match_frame_number)): + self.current_line = re.sub(match_frame_number, str(self.frame_no), line, count=1) + self.frame_no += 1 + return match + + def process_line_posix(self, line): + self.current_line = line.rstrip() + match = self.have_line_to_symbolize(line) + if not match: return [self.current_line] if DEBUG: print line -- 2.7.4 From 0198572270cc86071b4079253e526873d9cd2dca Mon Sep 17 00:00:00 2001 From: Slava Barinov Date: Fri, 14 Jul 2017 11:25:35 +0300 Subject: [PATCH 09/16] Switch on detect_leaks on 64-bit platforms by default libsanitizer/ * asan/asan_flags.cc: Switch on leak detection for 64-bit target. * lsan/lsan.cc: Likewise. * sanitizer_common/sanitizer_flags.inc: Likewise. Neither the OBS build nor sanitized firmware build are affected. When Tizen application developer enables ASan for his own package only and run it in 64-bit emulator (without any additional setup) ASan will report leaks additionally to all other error messages. Change-Id: Id16017e9bbe5221778330287c707bdf1846f0760 Signed-off-by: Slava Barinov --- libsanitizer/asan/asan_flags.cc | 2 +- libsanitizer/lsan/lsan.cc | 2 +- libsanitizer/sanitizer_common/sanitizer_flags.inc | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libsanitizer/asan/asan_flags.cc b/libsanitizer/asan/asan_flags.cc index 4f87cd2..93ac1d5 100644 --- a/libsanitizer/asan/asan_flags.cc +++ b/libsanitizer/asan/asan_flags.cc @@ -59,7 +59,7 @@ void InitializeFlags() { { CommonFlags cf; cf.CopyFrom(*common_flags()); - cf.detect_leaks = false; + cf.detect_leaks = (SANITIZER_WORDSIZE == 64); cf.external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH"); cf.malloc_context_size = kDefaultMallocContextSize; cf.intercept_tls_get_addr = true; diff --git a/libsanitizer/lsan/lsan.cc b/libsanitizer/lsan/lsan.cc index 33051ce..07ca472 100644 --- a/libsanitizer/lsan/lsan.cc +++ b/libsanitizer/lsan/lsan.cc @@ -56,7 +56,7 @@ static void InitializeFlags() { cf.CopyFrom(*common_flags()); cf.external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH"); cf.malloc_context_size = 30; - cf.detect_leaks = false; + cf.detect_leaks = (SANITIZER_WORDSIZE == 64); cf.exitcode = 0; OverrideCommonFlags(cf); } diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.inc b/libsanitizer/sanitizer_common/sanitizer_flags.inc index 7d95196..88123cc 100644 --- a/libsanitizer/sanitizer_common/sanitizer_flags.inc +++ b/libsanitizer/sanitizer_common/sanitizer_flags.inc @@ -60,7 +60,8 @@ COMMON_FLAG( COMMON_FLAG( int, verbosity, 0, "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).") -COMMON_FLAG(bool, detect_leaks, false, "Enable memory leak detection.") +COMMON_FLAG(bool, detect_leaks, (SANITIZER_WORDSIZE == 64), + "Enable memory leak detection.") COMMON_FLAG( bool, leak_check_at_exit, true, "Invoke leak checking in an atexit handler. Has no effect if " -- 2.7.4 From 25d454419a68c0f05aa64d49bb15cda1720809bb Mon Sep 17 00:00:00 2001 From: Slava Barinov Date: Thu, 13 Jul 2017 15:59:38 +0300 Subject: [PATCH 10/16] Remove target-libgfortran from default targets * configure.ac: Make target-libgfortran a configure option. * configure: Regenerate. This fixes on-host build broken by 58909fae. Now OBS build works as usual since it provides --enable-libgfortran explicitly and on-host test builds are not broken with error `GNU Fortran compiler is not working' Change-Id: I39ea37ddb0e52c1e2b43e3d304da1389ce615010 Signed-off-by: Slava Barinov --- configure | 14 +++++++++++++- configure.ac | 9 ++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/configure b/configure index b29a724..a75a44f 100755 --- a/configure +++ b/configure @@ -748,6 +748,7 @@ alphaieee_frag ospace_frag' ac_user_opts=' enable_option_checking +enable_libgfortran with_build_libsubdir with_system_zlib enable_as_accelerator_for @@ -1469,6 +1470,8 @@ Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-libgfortran[=ARG] + build target libgfortran [ARG={default,yes,no}] --enable-as-accelerator-for=ARG build as offload target compiler. Specify offload host triple by ARG @@ -2741,6 +2744,15 @@ libgcj="target-libffi \ target-zlib \ target-libjava" +# libgfortran represents the runtime libraries only used by fortran. +# Check whether --enable-libgfortran was given. +if test "${enable_libgfortran+set}" = set; then : + enableval=$enable_libgfortran; libgfortran="target-libgfortran" +else + libgfortran="" +fi + + # these libraries are built for the target environment, and are built after # the host libraries and the host tools (which may be a cross compiler) # Note that libiberty is not a target library. @@ -2759,7 +2771,7 @@ target_libraries="target-libgcc \ target-libmpx \ target-libssp \ target-libquadmath \ - target-libgfortran \ + ${libgfortran} \ target-boehm-gc \ ${libgcj} \ target-libobjc \ diff --git a/configure.ac b/configure.ac index f23463a..d720c20 100644 --- a/configure.ac +++ b/configure.ac @@ -147,6 +147,13 @@ libgcj="target-libffi \ target-zlib \ target-libjava" +# libgfortran represents the runtime libraries only used by fortran. +AC_ARG_ENABLE(libgfortran, +[AS_HELP_STRING([[--enable-libgfortran[=ARG]]], + [build target libgfortran @<:@ARG={default,yes,no}@:>@])], +libgfortran="target-libgfortran", +libgfortran="") + # these libraries are built for the target environment, and are built after # the host libraries and the host tools (which may be a cross compiler) # Note that libiberty is not a target library. @@ -165,7 +172,7 @@ target_libraries="target-libgcc \ target-libmpx \ target-libssp \ target-libquadmath \ - target-libgfortran \ + ${libgfortran} \ target-boehm-gc \ ${libgcj} \ target-libobjc \ -- 2.7.4 From eb74c5d87bc136704bb62beeb3ba1f901b5f42fe Mon Sep 17 00:00:00 2001 From: timshen Date: Sat, 23 Apr 2016 03:58:37 +0000 Subject: [PATCH 11/16] PR libstdc++/70745 * include/bits/regex_executor.tcc (_Executor<>::_M_word_boundary): Fix the match_not_bow and match_not_eow behavior. * testsuite/28_regex/regression.cc: Add testcase. (cherry-picked from commit 566d49d6947f4590609562dd5f33d0e6b24a4267) Change-Id: Ie38f6f857575432c90f9ae17576ddff4c7bc021a git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@235382 138bc75d-0d04-0410-961f-82ee72b054a4 --- libstdc++-v3/ChangeLog | 7 +++++++ libstdc++-v3/include/bits/regex_executor.tcc | 13 ++++++------- libstdc++-v3/testsuite/28_regex/regression.cc | 16 +++++++++++++++- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 8785436..554cd72 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,10 @@ +2016-04-22 Tim Shen + + PR libstdc++/70745 + * include/bits/regex_executor.tcc (_Executor<>::_M_word_boundary): + Fix the match_not_bow and match_not_eow behavior. + * testsuite/28_regex/regression.cc: Add testcase. + 2017-02-01 Jonathan Wakely PR libstdc++/78346 diff --git a/libstdc++-v3/include/bits/regex_executor.tcc b/libstdc++-v3/include/bits/regex_executor.tcc index 2abd020..6bbcb1b 100644 --- a/libstdc++-v3/include/bits/regex_executor.tcc +++ b/libstdc++-v3/include/bits/regex_executor.tcc @@ -413,6 +413,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION bool _Executor<_BiIter, _Alloc, _TraitsT, __dfs_mode>:: _M_word_boundary() const { + if (_M_current == _M_begin && (_M_flags & regex_constants::match_not_bow)) + return false; + if (_M_current == _M_end && (_M_flags & regex_constants::match_not_eow)) + return false; + bool __left_is_word = false; if (_M_current != _M_begin || (_M_flags & regex_constants::match_prev_avail)) @@ -424,13 +429,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION bool __right_is_word = _M_current != _M_end && _M_is_word(*_M_current); - if (__left_is_word == __right_is_word) - return false; - if (__left_is_word && !(_M_flags & regex_constants::match_not_eow)) - return true; - if (__right_is_word && !(_M_flags & regex_constants::match_not_bow)) - return true; - return false; + return __left_is_word != __right_is_word; } _GLIBCXX_END_NAMESPACE_VERSION diff --git a/libstdc++-v3/testsuite/28_regex/regression.cc b/libstdc++-v3/testsuite/28_regex/regression.cc index c9a3402..d367c8b 100644 --- a/libstdc++-v3/testsuite/28_regex/regression.cc +++ b/libstdc++-v3/testsuite/28_regex/regression.cc @@ -45,7 +45,20 @@ test02() "/ghci" }; auto rx = std::regex(re_str, std::regex_constants::grep | std::regex_constants::icase); - VERIFY(std::regex_search("/abcd", rx)); + VERIFY(regex_search_debug("/abcd", rx)); +} + +void +test03() +{ + bool test __attribute__((unused)) = true; + + VERIFY(regex_match_debug("a.", regex(R"(a\b.)"), regex_constants::match_not_eow)); + VERIFY(regex_match_debug(".a", regex(R"(.\ba)"), regex_constants::match_not_bow)); + VERIFY(regex_search_debug("a", regex(R"(^\b)"))); + VERIFY(regex_search_debug("a", regex(R"(\b$)"))); + VERIFY(!regex_search_debug("a", regex(R"(^\b)"), regex_constants::match_not_bow)); + VERIFY(!regex_search_debug("a", regex(R"(\b$)"), regex_constants::match_not_eow)); } int @@ -53,6 +66,7 @@ main() { test01(); test02(); + test03(); return 0; } -- 2.7.4 From fa348cd817b131784aa313b9b37aa8d3dcbc8f02 Mon Sep 17 00:00:00 2001 From: timshen Date: Sat, 27 Aug 2016 02:03:23 +0000 Subject: [PATCH 12/16] PR libstdc++/77356 * include/bits/regex_compiler.tcc(_M_insert_bracket_matcher, _M_expression_term): Modify to support dash literal. * include/bits/regex_scanner.h: Add dash as a token type to make a different from the mandated dash literal by escaping. * include/bits/regex_scanner.tcc(_M_scan_in_bracket): Emit dash token in bracket expression parsing. * testsuite/28_regex/regression.cc: Add new testcases. (cherry-picked from commit 6f2116bed6e87668a914dc27fff34c7a68576d4e) Change-Id: I8f516ef995f0fb7db479c441c9e7714bab518806 git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@239794 138bc75d-0d04-0410-961f-82ee72b054a4 --- libstdc++-v3/ChangeLog | 11 +++ libstdc++-v3/include/bits/regex_compiler.tcc | 110 +++++++++++++++++--------- libstdc++-v3/include/bits/regex_scanner.h | 5 +- libstdc++-v3/include/bits/regex_scanner.tcc | 4 +- libstdc++-v3/testsuite/28_regex/regression.cc | 23 ++++++ 5 files changed, 111 insertions(+), 42 deletions(-) diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 554cd72..c1121c8 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,14 @@ +2016-08-27 Tim Shen + + PR libstdc++/77356 + * include/bits/regex_compiler.tcc(_M_insert_bracket_matcher, + _M_expression_term): Modify to support dash literal. + * include/bits/regex_scanner.h: Add dash as a token type to make + a different from the mandated dash literal by escaping. + * include/bits/regex_scanner.tcc(_M_scan_in_bracket): Emit dash + token in bracket expression parsing. + * testsuite/28_regex/regression.cc: Add new testcases. + 2016-04-22 Tim Shen PR libstdc++/70745 diff --git a/libstdc++-v3/include/bits/regex_compiler.tcc b/libstdc++-v3/include/bits/regex_compiler.tcc index ff69e16..ef6ebdd 100644 --- a/libstdc++-v3/include/bits/regex_compiler.tcc +++ b/libstdc++-v3/include/bits/regex_compiler.tcc @@ -426,13 +426,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION pair __last_char; // Optional<_CharT> __last_char.first = false; if (!(_M_flags & regex_constants::ECMAScript)) - if (_M_try_char()) - { - __matcher._M_add_char(_M_value[0]); - __last_char.first = true; - __last_char.second = _M_value[0]; - } + { + if (_M_try_char()) + { + __last_char.first = true; + __last_char.second = _M_value[0]; + } + else if (_M_match_token(_ScannerT::_S_token_bracket_dash)) + { + __last_char.first = true; + __last_char.second = '-'; + } + } while (_M_expression_term(__last_char, __matcher)); + if (__last_char.first) + __matcher._M_add_char(__last_char.second); __matcher._M_ready(); _M_stack.push(_StateSeqT( *_M_nfa, @@ -449,19 +457,43 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (_M_match_token(_ScannerT::_S_token_bracket_end)) return false; + const auto __push_char = [&](_CharT __ch) + { + if (__last_char.first) + __matcher._M_add_char(__last_char.second); + else + __last_char.first = true; + __last_char.second = __ch; + }; + const auto __flush = [&] + { + if (__last_char.first) + { + __matcher._M_add_char(__last_char.second); + __last_char.first = false; + } + }; + if (_M_match_token(_ScannerT::_S_token_collsymbol)) { auto __symbol = __matcher._M_add_collate_element(_M_value); if (__symbol.size() == 1) - { - __last_char.first = true; - __last_char.second = __symbol[0]; - } + __push_char(__symbol[0]); + else + __flush(); } else if (_M_match_token(_ScannerT::_S_token_equiv_class_name)) - __matcher._M_add_equivalence_class(_M_value); + { + __flush(); + __matcher._M_add_equivalence_class(_M_value); + } else if (_M_match_token(_ScannerT::_S_token_char_class_name)) - __matcher._M_add_character_class(_M_value, false); + { + __flush(); + __matcher._M_add_character_class(_M_value, false); + } + else if (_M_try_char()) + __push_char(_M_value[0]); // POSIX doesn't allow '-' as a start-range char (say [a-z--0]), // except when the '-' is the first or last character in the bracket // expression ([--0]). ECMAScript treats all '-' after a range as a @@ -472,55 +504,55 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Clang (3.5) always uses ECMAScript style even in its POSIX syntax. // // It turns out that no one reads BNFs ;) - else if (_M_try_char()) + else if (_M_match_token(_ScannerT::_S_token_bracket_dash)) { if (!__last_char.first) { - __matcher._M_add_char(_M_value[0]); - if (_M_value[0] == '-' - && !(_M_flags & regex_constants::ECMAScript)) + if (!(_M_flags & regex_constants::ECMAScript)) { if (_M_match_token(_ScannerT::_S_token_bracket_end)) - return false; + { + __push_char('-'); + return false; + } __throw_regex_error( regex_constants::error_range, "Unexpected dash in bracket expression. For POSIX syntax, " "a dash is not treated literally only when it is at " "beginning or end."); } - __last_char.first = true; - __last_char.second = _M_value[0]; + __push_char('-'); } else { - if (_M_value[0] == '-') + if (_M_try_char()) { - if (_M_try_char()) - { - __matcher._M_make_range(__last_char.second , _M_value[0]); - __last_char.first = false; - } - else - { - if (_M_scanner._M_get_token() - != _ScannerT::_S_token_bracket_end) - __throw_regex_error( - regex_constants::error_range, - "Unexpected end of bracket expression."); - __matcher._M_add_char(_M_value[0]); - } + __matcher._M_make_range(__last_char.second, _M_value[0]); + __last_char.first = false; + } + else if (_M_match_token(_ScannerT::_S_token_bracket_dash)) + { + __matcher._M_make_range(__last_char.second, '-'); + __last_char.first = false; } else { - __matcher._M_add_char(_M_value[0]); - __last_char.second = _M_value[0]; + if (_M_scanner._M_get_token() + != _ScannerT::_S_token_bracket_end) + __throw_regex_error( + regex_constants::error_range, + "Character is expected after a dash."); + __push_char('-'); } } } else if (_M_match_token(_ScannerT::_S_token_quoted_class)) - __matcher._M_add_character_class(_M_value, - _M_ctype.is(_CtypeT::upper, - _M_value[0])); + { + __flush(); + __matcher._M_add_character_class(_M_value, + _M_ctype.is(_CtypeT::upper, + _M_value[0])); + } else __throw_regex_error(regex_constants::error_brack, "Unexpected character in bracket expression."); diff --git a/libstdc++-v3/include/bits/regex_scanner.h b/libstdc++-v3/include/bits/regex_scanner.h index 37dea84..ed0b723 100644 --- a/libstdc++-v3/include/bits/regex_scanner.h +++ b/libstdc++-v3/include/bits/regex_scanner.h @@ -43,7 +43,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { public: /// Token types returned from the scanner. - enum _TokenT + enum _TokenT : unsigned { _S_token_anychar, _S_token_ord_char, @@ -73,7 +73,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _S_token_comma, _S_token_dup_count, _S_token_eof, - _S_token_unknown + _S_token_bracket_dash, + _S_token_unknown = -1u }; protected: diff --git a/libstdc++-v3/include/bits/regex_scanner.tcc b/libstdc++-v3/include/bits/regex_scanner.tcc index fedba09..a734bb1 100644 --- a/libstdc++-v3/include/bits/regex_scanner.tcc +++ b/libstdc++-v3/include/bits/regex_scanner.tcc @@ -210,7 +210,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION auto __c = *_M_current++; - if (__c == '[') + if (__c == '-') + _M_token = _S_token_bracket_dash; + else if (__c == '[') { if (_M_current == _M_end) __throw_regex_error(regex_constants::error_brack, diff --git a/libstdc++-v3/testsuite/28_regex/regression.cc b/libstdc++-v3/testsuite/28_regex/regression.cc index d367c8b..fac7fa2 100644 --- a/libstdc++-v3/testsuite/28_regex/regression.cc +++ b/libstdc++-v3/testsuite/28_regex/regression.cc @@ -61,12 +61,35 @@ test03() VERIFY(!regex_search_debug("a", regex(R"(\b$)"), regex_constants::match_not_eow)); } +// PR libstdc++/77356 +void +test04() +{ + bool test __attribute__((unused)) = true; + + static const char* kNumericAnchor ="(\\$|usd)(usd|\\$|to|and|up to|[0-9,\\.\\-\\sk])+"; + const std::regex re(kNumericAnchor); + (void)re; +} + +void +test05() +{ + bool test __attribute__((unused)) = true; + + VERIFY(regex_match_debug("!", std::regex("[![:alnum:]]"))); + VERIFY(regex_match_debug("-", std::regex("[a-]", regex_constants::basic))); + VERIFY(regex_match_debug("-", std::regex("[a-]"))); +} + int main() { test01(); test02(); test03(); + test04(); + test05(); return 0; } -- 2.7.4 From 56c888a5a7232334e1d726beadbba66c26a80867 Mon Sep 17 00:00:00 2001 From: Slava Barinov Date: Tue, 25 Jul 2017 11:44:17 +0300 Subject: [PATCH 13/16] packaging: Enable ASan bootstrap for ASan projects GCC will be built in ASan projects. To test the GCC package the rpm macro `asanbootstrap' should be defined in project config. Change-Id: Id91425e2895f63ec92c8e83fb96f93aaade67728 Signed-off-by: Slava Barinov --- config/bootstrap-asan.mk | 2 +- packaging/gcc-aarch64.spec | 4 ++-- packaging/gcc-armv7l.spec | 4 ++-- packaging/linaro-gcc.spec | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/config/bootstrap-asan.mk b/config/bootstrap-asan.mk index 70baaf9..d22b10c 100644 --- a/config/bootstrap-asan.mk +++ b/config/bootstrap-asan.mk @@ -5,7 +5,7 @@ export LSAN_OPTIONS="detect_leaks=0" STAGE2_CFLAGS += -fsanitize=address STAGE3_CFLAGS += -fsanitize=address -POSTSTAGE1_LDFLAGS += -fsanitize=address -static-libasan \ +POSTSTAGE1_LDFLAGS += -fsanitize=address \ -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ \ -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/asan/ \ -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/asan/.libs diff --git a/packaging/gcc-aarch64.spec b/packaging/gcc-aarch64.spec index b10c27c..3dc3488 100644 --- a/packaging/gcc-aarch64.spec +++ b/packaging/gcc-aarch64.spec @@ -879,6 +879,7 @@ echo "" > gcc/DEV-PHASE %global gcc_release `sed -e 's/^.*-//g' %{_builddir}/gcc-%{version}/gcc/LINARO-VERSION` %build +%{?asan:%gcc_unforce_options} rm -rf obj mkdir obj cd obj @@ -923,6 +924,7 @@ TCFLAGS="$RPM_OPT_FLAGS" GCJFLAGS="$RPM_OPT_FLAGS" \ %{!?cross: \ --enable-libcc1 \ --enable-libgfortran \ + %{?asanbootstrap:--enable-bootstrap --with-build-config=bootstrap-asan} \ %ifarch armv7l --with-arch=armv7-a \ --with-tune=cortex-a8 \ @@ -969,8 +971,6 @@ TCFLAGS="$RPM_OPT_FLAGS" GCJFLAGS="$RPM_OPT_FLAGS" \ --host=%{host_arch} \ --build=%{host_arch} - - make BOOT_CFLAGS="$RPM_OPT_FLAGS" %{?_smp_mflags} %{?gcc_run_tests: echo "Run testsuite" diff --git a/packaging/gcc-armv7l.spec b/packaging/gcc-armv7l.spec index cf24112..4aeb345 100644 --- a/packaging/gcc-armv7l.spec +++ b/packaging/gcc-armv7l.spec @@ -879,6 +879,7 @@ echo "" > gcc/DEV-PHASE %global gcc_release `sed -e 's/^.*-//g' %{_builddir}/gcc-%{version}/gcc/LINARO-VERSION` %build +%{?asan:%gcc_unforce_options} rm -rf obj mkdir obj cd obj @@ -923,6 +924,7 @@ TCFLAGS="$RPM_OPT_FLAGS" GCJFLAGS="$RPM_OPT_FLAGS" \ %{!?cross: \ --enable-libcc1 \ --enable-libgfortran \ + %{?asanbootstrap:--enable-bootstrap --with-build-config=bootstrap-asan} \ %ifarch armv7l --with-arch=armv7-a \ --with-tune=cortex-a8 \ @@ -969,8 +971,6 @@ TCFLAGS="$RPM_OPT_FLAGS" GCJFLAGS="$RPM_OPT_FLAGS" \ --host=%{host_arch} \ --build=%{host_arch} - - make BOOT_CFLAGS="$RPM_OPT_FLAGS" %{?_smp_mflags} %{?gcc_run_tests: echo "Run testsuite" diff --git a/packaging/linaro-gcc.spec b/packaging/linaro-gcc.spec index 34d9221..dfb07b0 100644 --- a/packaging/linaro-gcc.spec +++ b/packaging/linaro-gcc.spec @@ -876,6 +876,7 @@ echo "" > gcc/DEV-PHASE %global gcc_release `sed -e 's/^.*-//g' %{_builddir}/gcc-%{version}/gcc/LINARO-VERSION` %build +%{?asan:%gcc_unforce_options} rm -rf obj mkdir obj cd obj @@ -920,6 +921,7 @@ TCFLAGS="$RPM_OPT_FLAGS" GCJFLAGS="$RPM_OPT_FLAGS" \ %{!?cross: \ --enable-libcc1 \ --enable-libgfortran \ + %{?asanbootstrap:--enable-bootstrap --with-build-config=bootstrap-asan} \ %ifarch armv7l --with-arch=armv7-a \ --with-tune=cortex-a8 \ @@ -966,8 +968,6 @@ TCFLAGS="$RPM_OPT_FLAGS" GCJFLAGS="$RPM_OPT_FLAGS" \ --host=%{host_arch} \ --build=%{host_arch} - - make BOOT_CFLAGS="$RPM_OPT_FLAGS" %{?_smp_mflags} %{?gcc_run_tests: echo "Run testsuite" -- 2.7.4 From db1663e7932e2e1878c40273f0488dcead9656a8 Mon Sep 17 00:00:00 2001 From: Slava Barinov Date: Thu, 6 Jul 2017 16:14:58 +0300 Subject: [PATCH 14/16] Fix cleanup location for try_finally_expr. gcc/ * tree.def: Add STATEMENT_LIST_END tree code. * tree.c: Add STATEMENT_LIST_END handling as TS_COMMON. * gimplify.c (gimplify_expr): Use STATEMENT_LIST_END location to provide right information for try_finally_expr. * tree-eh.c (lower_try_finally_onedest): Set finally location * c-family/c-semantics.c (pop_stmt_list): Support single-statement lists extraction with STATEMENT_LIST_END in the end. * fold-const.c (operand_equal_p): Add STATEMENT_LIST_END support. gcc/cp/ * parser.c (cp_parser_compound_statement): Use STATEMENT_LIST_END to keep the location of closing brace. * pt.c: Handle STATEMENT_LIST_END. * constraint.cc (check_function_concept): Handle concept definitions with STATEMENT_LIST_END. * error.c (dump_expr): Add STATEMENT_LIST_END support. gcc/testsuite/ * g++.dg/ext/statement-list-end.C: New. Change-Id: Id22e953b97b52d0f2a2ba44065337a59639578db Signed-off-by: Slava Barinov --- gcc/ChangeLog | 17 +++++++++++++++++ gcc/c-family/c-semantics.c | 6 ++++++ gcc/cp/ChangeLog | 7 +++++++ gcc/cp/constexpr.c | 7 +++++++ gcc/cp/constraint.cc | 9 +++++++++ gcc/cp/error.c | 1 + gcc/cp/parser.c | 15 ++++++++++++--- gcc/cp/pt.c | 3 +++ gcc/fold-const.c | 26 +++++++++++++++----------- gcc/gimple.c | 1 + gcc/gimplify.c | 26 ++++++++++++++++++++++++++ gcc/testsuite/ChangeLog | 4 ++++ gcc/testsuite/g++.dg/ext/statement-list-end.C | 11 +++++++++++ gcc/testsuite/g++.dg/gcov/gcov-2.C | 4 ++-- gcc/testsuite/g++.dg/parse/error26.C | 4 ++-- gcc/testsuite/g++.dg/tm/inherit2.C | 4 ++-- gcc/testsuite/g++.dg/tm/unsafe1.C | 4 ++-- gcc/tree-eh.c | 15 +++++++++++---- gcc/tree.c | 2 ++ gcc/tree.def | 4 ++++ 20 files changed, 144 insertions(+), 26 deletions(-) create mode 100644 gcc/testsuite/g++.dg/ext/statement-list-end.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 77528a5..8772afc 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,20 @@ +2017-07-06 Vyacheslav Barinov + + * tree.def: Add STATEMENT_LIST_END tree code. + * tree.c: Add STATEMENT_LIST_END handling as TS_COMMON. + * gimplify.c (gimplify_expr): Use STATEMENT_LIST_END location to + provide right information for try_finally_expr. + * tree-eh.c (lower_try_finally_onedest): Set finally location + accordingly to location of last statement in cleanup. + * gimple.c (DEFTREECODE): handle STATEMENT_LIST_END as appropriate + GIMPLE_SINGLE_RHS. + * cp/constraint.cc (check_function_concept): Handle concept + definitions with STATEMENT_LIST_END. + * cp/error.c (dump_expr): Add STATEMENT_LIST_END support. + * fold-const.c (operand_equal_p): Add STATEMENT_LIST_END support. + * c-family/c-semantics.c (pop_stmt_list): Support single-statement + lists extraction with STATEMENT_LIST_END in the end. + 2017-04-13 Denis Khalikov PR sanitizer/80414 diff --git a/gcc/c-family/c-semantics.c b/gcc/c-family/c-semantics.c index 4845a8b..bd4f379 100644 --- a/gcc/c-family/c-semantics.c +++ b/gcc/c-family/c-semantics.c @@ -66,6 +66,12 @@ pop_stmt_list (tree t) if (TREE_SIDE_EFFECTS (t)) { tree_stmt_iterator i = tsi_start (t); + if (i.ptr->next && i.ptr->next->stmt && + TREE_CODE(i.ptr->next->stmt) == STATEMENT_LIST_END) + { + tree_stmt_iterator i = tsi_last (t); + tsi_delink (&i); + } /* If the statement list contained exactly one statement, then extract it immediately. */ diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index a7f94b3..3ccddab 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,10 @@ +2017-07-06 Vyacheslav Barinov + + * parser.c (cp_parser_compound_statement): Use STATEMENT_LIST_END + to keep the location of closing brace. + * pt.c: Handle STATEMENT_LIST_END. + * constexpr.c (cxx_eval_constant_expression): Likewise. + 2017-01-26 Jason Merrill PR c++/79176 - lambda ICE with -flto -Os diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index a6ac3c1..f9c4ead 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -441,6 +441,7 @@ check_constexpr_ctor_body_1 (tree last, tree list) case USING_STMT: case STATIC_ASSERT: + case STATEMENT_LIST_END: return true; default: @@ -555,6 +556,9 @@ build_constexpr_constructor_member_initializers (tree type, tree body) body = BIND_EXPR_BODY (body); goto found; + case STATEMENT_LIST_END: + break; + default: gcc_unreachable (); } @@ -666,6 +670,7 @@ constexpr_fn_retval (tree body) return constexpr_fn_retval (BIND_EXPR_BODY (body)); case USING_STMT: + case STATEMENT_LIST_END: return NULL_TREE; default: @@ -4109,6 +4114,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, break; case EMPTY_CLASS_EXPR: + case STATEMENT_LIST_END: /* This is good enough for a function argument that might not get used, and they can't do anything with it, so just return it. */ return t; @@ -5292,6 +5298,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, case TYPE_DECL: case TAG_DEFN: + case STATEMENT_LIST_END: /* We can see these in statement-expressions. */ return true; diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 09ae301..157f7dd 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see #include "c-family/c-objc.h" #include "cp-objcp-common.h" #include "tree-inline.h" +#include "tree-iterator.h" #include "decl.h" #include "toplev.h" #include "type-utils.h" @@ -2509,6 +2510,14 @@ check_function_concept (tree fn) if (TREE_CODE (body) == CLEANUP_POINT_EXPR) body = TREE_OPERAND (body, 0); + // We need to cut the STATEMENT_LIST_END before the check. + if (TREE_CODE (body) == STATEMENT_LIST && + TREE_CODE (STATEMENT_LIST_TAIL (body)->stmt) == STATEMENT_LIST_END) + { + tree_stmt_iterator i = tsi_last (body); + tsi_delink(&i); + } + /* Check that the definition is written correctly. */ if (TREE_CODE (body) != RETURN_EXPR) { diff --git a/gcc/cp/error.c b/gcc/cp/error.c index 93fe2ffb..a852e71 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -2597,6 +2597,7 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags) case STMT_EXPR: case EXPR_STMT: case STATEMENT_LIST: + case STATEMENT_LIST_END: /* We don't yet have a way of dumping statements in a human-readable format. */ pp_string (pp, "({...})"); diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 390f7d0..187fe0e 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -10770,11 +10770,20 @@ cp_parser_compound_statement (cp_parser *parser, tree in_statement_expr, cp_parser_label_declaration (parser); /* Parse an (optional) statement-seq. */ cp_parser_statement_seq_opt (parser, in_statement_expr); + /* Consume the `}' and keep location, if there is a `}' and compound_stmt is + not empty. */ + cp_token *token = cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE); + if (token && + TREE_CODE(compound_stmt) == STATEMENT_LIST && + STATEMENT_LIST_HEAD (compound_stmt) && + STATEMENT_LIST_TAIL (compound_stmt)) + { + tree brace_stmt = build0(STATEMENT_LIST_END, void_type_node); + SET_EXPR_LOCATION(brace_stmt, token->location); + add_stmt(brace_stmt); + } /* Finish the compound-statement. */ finish_compound_stmt (compound_stmt); - /* Consume the `}'. */ - cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE); - return compound_stmt; } diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 744b461..db403c8 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -15870,6 +15870,9 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, RETURN (build2_loc (EXPR_LOCATION (t), ANNOTATE_EXPR, TREE_TYPE (tmp), tmp, RECUR (TREE_OPERAND (t, 1)))); + case STATEMENT_LIST_END: + RETURN (t); + default: gcc_assert (!STATEMENT_CODE_P (TREE_CODE (t))); diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 9ef4951..46f1ac1 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -186,7 +186,7 @@ div_if_zero_remainder (const_tree arg1, const_tree arg2) SIGNED, &quo)) return wide_int_to_tree (TREE_TYPE (arg1), quo); - return NULL_TREE; + return NULL_TREE; } /* This is nonzero if we should defer warnings about undefined @@ -1984,7 +1984,7 @@ fold_convert_const_real_from_real (tree type, const_tree arg1) and the operand is a signaling NaN. */ if (HONOR_SNANS (TYPE_MODE (TREE_TYPE (arg1))) && REAL_VALUE_ISSIGNALING_NAN (TREE_REAL_CST (arg1))) - return NULL_TREE; + return NULL_TREE; real_convert (&value, TYPE_MODE (type), &TREE_REAL_CST (arg1)); t = build_real (type, value); @@ -2750,7 +2750,7 @@ operand_equal_p (const_tree arg0, const_tree arg1, unsigned int flags) || TREE_TYPE (arg1) == error_mark_node) return 0; - /* Similar, if either does not have a type (like a released SSA name), + /* Similar, if either does not have a type (like a released SSA name), they aren't equal. */ if (!TREE_TYPE (arg0) || !TREE_TYPE (arg1)) return 0; @@ -3144,6 +3144,10 @@ operand_equal_p (const_tree arg0, const_tree arg1, unsigned int flags) case DOT_PROD_EXPR: return OP_SAME (0) && OP_SAME (1) && OP_SAME (2); + case STATEMENT_LIST_END: + return 1; + /* All STATEMENT_LIST_END are equal */ + default: return 0; } @@ -3250,10 +3254,10 @@ operand_equal_p (const_tree arg0, const_tree arg1, unsigned int flags) Double check this so we won't get false positives for GENERIC. */ || (c0->index - && (TREE_CODE (c0->index) != INTEGER_CST + && (TREE_CODE (c0->index) != INTEGER_CST || !compare_tree_int (c0->index, i))) || (c1->index - && (TREE_CODE (c1->index) != INTEGER_CST + && (TREE_CODE (c1->index) != INTEGER_CST || !compare_tree_int (c1->index, i)))) return 0; } @@ -5441,7 +5445,7 @@ unextend (tree c, int p, int unsignedp, tree mask) A || ~B or A && ~B - LOC is the location of the resulting expression. OP is the inner + LOC is the location of the resulting expression. OP is the inner logical operation; the left-hand side in the examples above, while CMPOP is the right-hand side. RHS_ONLY is used to prevent us from accidentally removing a condition that guards another, as in @@ -7163,7 +7167,7 @@ native_encode_fixed (const_tree expr, unsigned char *ptr, int len, int off) if (NULL_TREE == i_type || TYPE_PRECISION (i_type) != total_bytes) return 0; - + value = TREE_FIXED_CST (expr); i_value = double_int_to_tree (i_type, value.data); @@ -8176,7 +8180,7 @@ fold_truth_andor (location_t loc, enum tree_code code, tree type, side-effects. */ && simple_operand_p_2 (TREE_OPERAND (arg1, 0))) { - tem = fold_build2_loc (loc, ncode, type, + tem = fold_build2_loc (loc, ncode, type, arg0, TREE_OPERAND (arg1, 0)); return fold_build2_loc (loc, icode, type, tem, TREE_OPERAND (arg1, 1)); @@ -10405,7 +10409,7 @@ fold_binary_loc (location_t loc, case TRUNC_DIV_EXPR: /* Fall through */ - + case FLOOR_DIV_EXPR: /* Simplify A / (B << N) where A and B are positive and B is a power of 2, to A >> (N + log2(B)). */ @@ -10659,10 +10663,10 @@ fold_binary_loc (location_t loc, l0 = fold_convert_loc (loc, type, TREE_OPERAND (arg0, 0)); l1 = fold_convert_loc (loc, type, TREE_OPERAND (arg0, 1)); - + n0 = fold_build1_loc (loc, TRUTH_NOT_EXPR, type, l0); n1 = fold_build1_loc (loc, TRUTH_NOT_EXPR, type, l1); - + if ((operand_equal_p (n0, a0, 0) && operand_equal_p (n1, a1, 0)) || (operand_equal_p (n0, a1, 0) diff --git a/gcc/gimple.c b/gcc/gimple.c index b874c9f..9200a8b 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -2051,6 +2051,7 @@ get_gimple_rhs_num_ops (enum tree_code code) || (SYM) == ADDR_EXPR \ || (SYM) == WITH_SIZE_EXPR \ || (SYM) == SSA_NAME) ? GIMPLE_SINGLE_RHS \ + : ((SYM) == STATEMENT_LIST_END) ? GIMPLE_SINGLE_RHS \ : GIMPLE_INVALID_RHS), #define END_OF_BASE_TREE_CODES (unsigned char) GIMPLE_INVALID_RHS, diff --git a/gcc/gimplify.c b/gcc/gimplify.c index 7c5cead..22e053a 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -998,6 +998,11 @@ voidify_wrapper_expr (tree wrapper, tree temp) case STATEMENT_LIST: { tree_stmt_iterator i = tsi_last (*p); + if (TREE_CODE(*tsi_stmt_ptr (i)) == STATEMENT_LIST_END) + { + tsi_delink(&i); + i = tsi_last (*p); + } TREE_SIDE_EFFECTS (*p) = 1; TREE_TYPE (*p) = void_type_node; p = tsi_end_p (i) ? NULL : tsi_stmt_ptr (i); @@ -10722,6 +10727,19 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, end of gimplify_expr. */ input_location = UNKNOWN_LOCATION; eval = cleanup = NULL; + location_t finally_loc = 0; + /* The cleanup location can be extracted from STATEMENT_LIST_END + location added especially for this purpose. */ + if (TREE_OPERAND (*expr_p, 0) && + TREE_CODE (TREE_OPERAND (*expr_p, 0)) == STATEMENT_LIST) + { + const tree_statement_list_node* last_node = + STATEMENT_LIST_TAIL(TREE_OPERAND (*expr_p, 0)); + if (last_node && + last_node->stmt && + TREE_CODE (last_node->stmt) == STATEMENT_LIST_END) + finally_loc = EXPR_LOCATION(last_node->stmt); + } gimplify_and_add (TREE_OPERAND (*expr_p, 0), &eval); gimplify_and_add (TREE_OPERAND (*expr_p, 1), &cleanup); /* Don't create bogus GIMPLE_TRY with empty cleanup. */ @@ -10742,6 +10760,10 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, if (TREE_CODE (*expr_p) == TRY_CATCH_EXPR) gimple_try_set_catch_is_cleanup (try_, TRY_CATCH_IS_CLEANUP (*expr_p)); + + gimple *last_in_seq = gimple_seq_last_stmt (cleanup); + gimple_set_location(last_in_seq, finally_loc); + gimplify_seq_add_stmt (pre_p, try_); ret = GS_ALL_DONE; break; @@ -10803,6 +10825,10 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p, ret = gimplify_statement_list (expr_p, pre_p); break; + case STATEMENT_LIST_END: + ret = GS_ALL_DONE; + break; + case WITH_SIZE_EXPR: { gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 3ae4c95..e631179 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2017-07-06 Vyacheslav Barinov + + * g++.dg/ext/statement-list-end.C: New. + 2017-04-13 Denis Khalikov PR sanitizer/80414 diff --git a/gcc/testsuite/g++.dg/ext/statement-list-end.C b/gcc/testsuite/g++.dg/ext/statement-list-end.C new file mode 100644 index 0000000..3381172 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/statement-list-end.C @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-gimple-lineno" } */ +/* { dg-require-effective-target c++11 } */ + +#include +void i () +{ + std::unique_ptr c(new char); +} + +/* { dg-final { scan-tree-dump "9:1] std::unique_ptr::~unique_ptr" "gimple" } } */ diff --git a/gcc/testsuite/g++.dg/gcov/gcov-2.C b/gcc/testsuite/g++.dg/gcov/gcov-2.C index 6d002f5..2b4cdd8 100644 --- a/gcc/testsuite/g++.dg/gcov/gcov-2.C +++ b/gcc/testsuite/g++.dg/gcov/gcov-2.C @@ -20,9 +20,9 @@ private: void foo() { - C c; /* count(2) */ + C c; /* count(1) */ c.seti (1); /* count(1) */ -} +} /* count(1) */ int main() { diff --git a/gcc/testsuite/g++.dg/parse/error26.C b/gcc/testsuite/g++.dg/parse/error26.C index 1084e76..0897339 100644 --- a/gcc/testsuite/g++.dg/parse/error26.C +++ b/gcc/testsuite/g++.dg/parse/error26.C @@ -4,11 +4,11 @@ void foo() { if (({int c[2];})) ; // { dg-error "7:ISO C.. forbids" "7" } - // { dg-error "17:could not convert" "17" { target *-*-* } 6 } + // { dg-error "18:could not convert" "18" { target *-*-* } .-1 } } void bar() { if (({})); // { dg-error "7:ISO C.. forbids" "7" } - // { dg-error "11:could not convert" "11" { target *-*-* } 12 } + // { dg-error "11:could not convert" "11" { target *-*-* } .-1 } } diff --git a/gcc/testsuite/g++.dg/tm/inherit2.C b/gcc/testsuite/g++.dg/tm/inherit2.C index 3b696a9..366f9b3 100644 --- a/gcc/testsuite/g++.dg/tm/inherit2.C +++ b/gcc/testsuite/g++.dg/tm/inherit2.C @@ -26,8 +26,8 @@ int main() B b; // ok D1 d1; // ok B& b1 = d1; - D2 x; // { dg-error "" "destructor of D2 is not transaction-safe" } + D2 x; b1.f(); // ok, calls D1::f() delete b2; // undefined behavior: calls unsafe destructor of D2 - } + } // { dg-error "" "destructor of D2 is not transaction-safe" } } diff --git a/gcc/testsuite/g++.dg/tm/unsafe1.C b/gcc/testsuite/g++.dg/tm/unsafe1.C index 91dd7b1..e86b1bd 100644 --- a/gcc/testsuite/g++.dg/tm/unsafe1.C +++ b/gcc/testsuite/g++.dg/tm/unsafe1.C @@ -5,8 +5,8 @@ struct S { virtual ~S(); }; int f() transaction_safe { - S s; // { dg-error "unsafe" "invocation of unsafe destructor" } -} + S s; +} // { dg-error "unsafe" "invocation of unsafe destructor" } int g(int x) { // is transaction-safe if (x <= 0) diff --git a/gcc/tree-eh.c b/gcc/tree-eh.c index db72156..7b6932c 100644 --- a/gcc/tree-eh.c +++ b/gcc/tree-eh.c @@ -1134,10 +1134,16 @@ lower_try_finally_onedest (struct leh_state *state, struct leh_tf_state *tf) gimple_stmt_iterator gsi; tree finally_label; location_t loc = gimple_location (tf->try_finally_expr); + location_t finally_loc; finally = gimple_try_cleanup (tf->top_p); tf->top_p_seq = gimple_try_eval (tf->top_p); + /* The location of the finally is either the last stmt in the finally + block or the location of the TRY_FINALLY itself. */ + x = gimple_seq_last_stmt (finally); + finally_loc = x ? gimple_location (x) : loc; + /* Since there's only one destination, and the destination edge can only either be EH or non-EH, that implies that all of our incoming edges are of the same type. Therefore we can lower EH_ELSE immediately. */ @@ -1158,7 +1164,7 @@ lower_try_finally_onedest (struct leh_state *state, struct leh_tf_state *tf) if (LOCATION_LOCUS (gimple_location (stmt)) == UNKNOWN_LOCATION) { tree block = gimple_block (stmt); - gimple_set_location (stmt, gimple_location (tf->try_finally_expr)); + gimple_set_location (stmt, finally_loc); gimple_set_block (stmt, block); } } @@ -1181,7 +1187,7 @@ lower_try_finally_onedest (struct leh_state *state, struct leh_tf_state *tf) return; } - finally_label = create_artificial_label (loc); + finally_label = create_artificial_label (finally_loc); label_stmt = gimple_build_label (finally_label); gimple_seq_add_stmt (&tf->top_p_seq, label_stmt); @@ -1413,11 +1419,12 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf) x = gimple_build_assign (finally_tmp, build_int_cst (integer_type_node, fallthru_index)); + gimple_set_location (x, finally_loc); gimple_seq_add_stmt (&tf->top_p_seq, x); tmp = build_int_cst (integer_type_node, fallthru_index); last_case = build_case_label (tmp, NULL, - create_artificial_label (tf_loc)); + create_artificial_label (finally_loc)); case_label_vec.quick_push (last_case); last_case_index++; @@ -1426,7 +1433,7 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf) tmp = lower_try_finally_fallthru_label (tf); x = gimple_build_goto (tmp); - gimple_set_location (x, tf_loc); + gimple_set_location (x, finally_loc); gimple_seq_add_stmt (&switch_body, x); } diff --git a/gcc/tree.c b/gcc/tree.c index 7ce14c9..8dbaadd 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -470,6 +470,7 @@ tree_node_structure_for_code (enum tree_code code) case SSA_NAME: return TS_SSA_NAME; case PLACEHOLDER_EXPR: return TS_COMMON; case STATEMENT_LIST: return TS_STATEMENT_LIST; + case STATEMENT_LIST_END: return TS_COMMON; case BLOCK: return TS_BLOCK; case CONSTRUCTOR: return TS_CONSTRUCTOR; case TREE_BINFO: return TS_BINFO; @@ -825,6 +826,7 @@ tree_code_size (enum tree_code code) case TREE_LIST: return sizeof (struct tree_list); case ERROR_MARK: + case STATEMENT_LIST_END: case PLACEHOLDER_EXPR: return sizeof (struct tree_common); case TREE_VEC: diff --git a/gcc/tree.def b/gcc/tree.def index 44130d7..20adf2e 100644 --- a/gcc/tree.def +++ b/gcc/tree.def @@ -976,6 +976,10 @@ DEFTREECODE (POLYNOMIAL_CHREC, "polynomial_chrec", tcc_expression, 3) Use the interface in tree-iterator.h to access this node. */ DEFTREECODE (STATEMENT_LIST, "statement_list", tcc_exceptional, 0) +/* Used to keep STATEMENT_LIST end location. + The only useful field is location, which points to the closing brace. */ +DEFTREECODE (STATEMENT_LIST_END, "statement_list_end", tcc_expression, 0) + /* Predicate assertion. Artificial expression generated by the optimizers to keep track of predicate values. This expression may only appear on the RHS of assignments. -- 2.7.4 From 3aa305b606ff3bc7f9fc89be048e10e15ad2c17d Mon Sep 17 00:00:00 2001 From: chefmax Date: Tue, 8 Nov 2016 22:04:09 +0000 Subject: [PATCH 15/16] libsanitizer/ * All source files: Merge from upstream 285547. * configure.tgt (SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS): New variable. * configure.ac (SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS): Handle it. * asan/Makefile.am (asan_files): Add new files. * asan/Makefile.in: Regenerate. * ubsan/Makefile.in: Likewise. * lsan/Makefile.in: Likewise. * tsan/Makefile.am (tsan_files): Add new files. * tsan/Makefile.in: Regenerate. * sanitizer_common/Makefile.am (sanitizer_common_files): Add new files. (EXTRA_libsanitizer_common_la_SOURCES): Define. (libsanitizer_common_la_LIBADD): Likewise. (libsanitizer_common_la_DEPENDENCIES): Likewise. * sanitizer_common/Makefile.in: Regenerate. * interception/Makefile.in: Likewise. * libbacktace/Makefile.in: Likewise. * Makefile.in: Likewise. * configure: Likewise. * merge.sh: Handle builtins/assembly.h merging. * builtins/assembly.h: New file. * asan/libtool-version: Bump the libasan SONAME. Change-Id: I47a2591215c7529613d22a117644607b20db065b git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@241977 138bc75d-0d04-0410-961f-82ee72b054a4 --- libsanitizer/ChangeLog | 25 + libsanitizer/MERGE | 2 +- libsanitizer/Makefile.in | 1 + libsanitizer/asan/Makefile.am | 3 + libsanitizer/asan/Makefile.in | 14 +- libsanitizer/asan/asan_activation.cc | 8 +- libsanitizer/asan/asan_allocator.cc | 94 +- libsanitizer/asan/asan_allocator.h | 45 +- libsanitizer/asan/asan_debugging.cc | 125 +- libsanitizer/asan/asan_descriptions.cc | 484 +++++++ libsanitizer/asan/asan_descriptions.h | 251 ++++ libsanitizer/asan/asan_errors.cc | 494 +++++++ libsanitizer/asan/asan_errors.h | 376 +++++ libsanitizer/asan/asan_fake_stack.cc | 6 +- libsanitizer/asan/asan_fake_stack.h | 21 +- libsanitizer/asan/asan_flags.cc | 21 +- libsanitizer/asan/asan_flags.inc | 21 +- libsanitizer/asan/asan_globals.cc | 166 ++- libsanitizer/asan/asan_init_version.h | 14 +- libsanitizer/asan/asan_interceptors.cc | 106 +- libsanitizer/asan/asan_interceptors.h | 14 +- libsanitizer/asan/asan_interface_internal.h | 28 + libsanitizer/asan/asan_internal.h | 38 +- libsanitizer/asan/asan_linux.cc | 13 +- libsanitizer/asan/asan_mac.cc | 218 +-- libsanitizer/asan/asan_malloc_linux.cc | 8 +- libsanitizer/asan/asan_malloc_mac.cc | 4 - libsanitizer/asan/asan_malloc_win.cc | 139 +- libsanitizer/asan/asan_mapping.h | 77 +- libsanitizer/asan/asan_memory_profile.cc | 98 ++ libsanitizer/asan/asan_new_delete.cc | 20 +- libsanitizer/asan/asan_poisoning.cc | 44 +- libsanitizer/asan/asan_poisoning.h | 2 +- libsanitizer/asan/asan_posix.cc | 30 +- libsanitizer/asan/asan_report.cc | 974 ++----------- libsanitizer/asan/asan_report.h | 26 +- libsanitizer/asan/asan_rtl.cc | 89 +- libsanitizer/asan/asan_scariness_score.h | 72 + libsanitizer/asan/asan_stack.h | 5 +- libsanitizer/asan/asan_suppressions.cc | 1 + libsanitizer/asan/asan_thread.cc | 123 +- libsanitizer/asan/asan_thread.h | 41 +- libsanitizer/asan/asan_win.cc | 206 ++- libsanitizer/asan/asan_win_dll_thunk.cc | 44 +- .../asan/asan_win_dynamic_runtime_thunk.cc | 23 +- libsanitizer/asan/libtool-version | 2 +- libsanitizer/builtins/assembly.h | 169 +++ libsanitizer/configure | 2 + libsanitizer/configure.ac | 1 + libsanitizer/configure.tgt | 2 + .../include/sanitizer/allocator_interface.h | 17 + .../include/sanitizer/common_interface_defs.h | 52 +- .../include/sanitizer/coverage_interface.h | 1 + libsanitizer/include/sanitizer/esan_interface.h | 48 + .../include/sanitizer/linux_syscall_hooks.h | 23 +- libsanitizer/interception/Makefile.in | 1 + libsanitizer/interception/interception.h | 6 +- libsanitizer/interception/interception_win.cc | 995 +++++++++++-- libsanitizer/interception/interception_win.h | 30 + libsanitizer/libbacktrace/Makefile.in | 1 + libsanitizer/lsan/Makefile.in | 1 + libsanitizer/lsan/lsan.cc | 4 +- libsanitizer/lsan/lsan.h | 7 +- libsanitizer/lsan/lsan_allocator.cc | 21 +- libsanitizer/lsan/lsan_common.cc | 56 +- libsanitizer/lsan/lsan_common.h | 15 +- libsanitizer/lsan/lsan_common_linux.cc | 49 +- libsanitizer/lsan/lsan_flags.inc | 4 + libsanitizer/lsan/lsan_interceptors.cc | 25 +- libsanitizer/lsan/lsan_thread.cc | 27 +- libsanitizer/lsan/lsan_thread.h | 7 + libsanitizer/merge.sh | 4 + libsanitizer/sanitizer_common/Makefile.am | 5 + libsanitizer/sanitizer_common/Makefile.in | 62 +- .../sanitizer_common/sanitizer_allocator.cc | 107 +- .../sanitizer_common/sanitizer_allocator.h | 1455 +------------------- .../sanitizer_common/sanitizer_allocator_bytemap.h | 100 ++ .../sanitizer_allocator_combined.h | 215 +++ .../sanitizer_allocator_interface.h | 8 + .../sanitizer_allocator_internal.h | 11 +- .../sanitizer_allocator_local_cache.h | 246 ++++ .../sanitizer_allocator_primary32.h | 312 +++++ .../sanitizer_allocator_primary64.h | 513 +++++++ .../sanitizer_allocator_secondary.h | 271 ++++ .../sanitizer_allocator_size_class_map.h | 215 +++ .../sanitizer_common/sanitizer_allocator_stats.h | 103 ++ libsanitizer/sanitizer_common/sanitizer_asm.h | 14 + .../sanitizer_common/sanitizer_atomic_msvc.h | 13 +- libsanitizer/sanitizer_common/sanitizer_common.cc | 200 +-- libsanitizer/sanitizer_common/sanitizer_common.h | 213 ++- .../sanitizer_common_interceptors.inc | 966 ++++++++++--- .../sanitizer_common_interceptors_ioctl.inc | 38 +- .../sanitizer_common/sanitizer_common_libcdep.cc | 55 +- .../sanitizer_common/sanitizer_common_syscalls.inc | 58 +- .../sanitizer_common/sanitizer_coverage_libcdep.cc | 161 ++- .../sanitizer_coverage_mapping_libcdep.cc | 23 +- .../sanitizer_deadlock_detector1.cc | 9 +- .../sanitizer_deadlock_detector_interface.h | 2 +- libsanitizer/sanitizer_common/sanitizer_flags.cc | 56 +- libsanitizer/sanitizer_common/sanitizer_flags.h | 7 + libsanitizer/sanitizer_common/sanitizer_flags.inc | 44 +- .../sanitizer_interface_internal.h | 4 + .../sanitizer_common/sanitizer_internal_defs.h | 53 +- libsanitizer/sanitizer_common/sanitizer_libc.cc | 32 +- libsanitizer/sanitizer_common/sanitizer_libc.h | 14 +- libsanitizer/sanitizer_common/sanitizer_linux.cc | 275 +++- libsanitizer/sanitizer_common/sanitizer_linux.h | 9 +- .../sanitizer_common/sanitizer_linux_libcdep.cc | 120 +- .../sanitizer_common/sanitizer_linux_mips64.S | 21 + .../sanitizer_common/sanitizer_linux_s390.cc | 189 +++ .../sanitizer_common/sanitizer_linux_x86_64.S | 23 + libsanitizer/sanitizer_common/sanitizer_list.h | 32 +- libsanitizer/sanitizer_common/sanitizer_mac.cc | 402 +++++- libsanitizer/sanitizer_common/sanitizer_mac.h | 18 + .../sanitizer_common/sanitizer_malloc_mac.inc | 26 +- libsanitizer/sanitizer_common/sanitizer_platform.h | 113 +- .../sanitizer_platform_interceptors.h | 55 +- .../sanitizer_platform_limits_linux.cc | 7 +- .../sanitizer_platform_limits_posix.cc | 31 +- .../sanitizer_platform_limits_posix.h | 112 +- libsanitizer/sanitizer_common/sanitizer_posix.cc | 63 +- libsanitizer/sanitizer_common/sanitizer_posix.h | 9 + .../sanitizer_common/sanitizer_posix_libcdep.cc | 107 +- libsanitizer/sanitizer_common/sanitizer_printf.cc | 9 +- libsanitizer/sanitizer_common/sanitizer_procmaps.h | 5 +- .../sanitizer_common/sanitizer_procmaps_common.cc | 22 +- .../sanitizer_common/sanitizer_procmaps_linux.cc | 2 +- .../sanitizer_common/sanitizer_procmaps_mac.cc | 26 +- .../sanitizer_common/sanitizer_quarantine.h | 6 +- .../sanitizer_common/sanitizer_stacktrace.cc | 19 +- .../sanitizer_common/sanitizer_stacktrace.h | 7 +- .../sanitizer_stacktrace_libcdep.cc | 25 +- .../sanitizer_stacktrace_printer.cc | 29 + .../sanitizer_stacktrace_printer.h | 7 + .../sanitizer_stoptheworld_linux_libcdep.cc | 36 +- .../sanitizer_common/sanitizer_suppressions.cc | 2 +- .../sanitizer_common/sanitizer_suppressions.h | 3 +- .../sanitizer_common/sanitizer_symbolizer.cc | 3 +- .../sanitizer_common/sanitizer_symbolizer.h | 14 +- .../sanitizer_symbolizer_internal.h | 2 +- .../sanitizer_symbolizer_libcdep.cc | 48 +- .../sanitizer_common/sanitizer_symbolizer_mac.cc | 37 +- .../sanitizer_symbolizer_posix_libcdep.cc | 107 +- .../sanitizer_common/sanitizer_symbolizer_win.cc | 8 +- .../sanitizer_common/sanitizer_termination.cc | 84 ++ .../sanitizer_common/sanitizer_thread_registry.cc | 4 +- .../sanitizer_common/sanitizer_tls_get_addr.cc | 2 +- .../sanitizer_unwind_linux_libcdep.cc | 10 + libsanitizer/sanitizer_common/sanitizer_win.cc | 267 +++- libsanitizer/tsan/Makefile.am | 9 +- libsanitizer/tsan/Makefile.in | 78 +- libsanitizer/tsan/tsan_clock.cc | 2 +- libsanitizer/tsan/tsan_debugging.cc | 160 +++ libsanitizer/tsan/tsan_defs.h | 22 +- libsanitizer/tsan/tsan_dense_alloc.h | 2 +- libsanitizer/tsan/tsan_flags.cc | 9 +- libsanitizer/tsan/tsan_flags.inc | 7 +- libsanitizer/tsan/tsan_interceptors.cc | 696 +++++----- libsanitizer/tsan/tsan_interceptors.h | 16 +- libsanitizer/tsan/tsan_interceptors_mac.cc | 357 +++++ libsanitizer/tsan/tsan_interface.h | 291 ++++ libsanitizer/tsan/tsan_interface_atomic.cc | 43 +- libsanitizer/tsan/tsan_interface_java.cc | 2 +- libsanitizer/tsan/tsan_libdispatch_mac.cc | 613 ++++++++- libsanitizer/tsan/tsan_malloc_mac.cc | 43 +- libsanitizer/tsan/tsan_mman.cc | 118 +- libsanitizer/tsan/tsan_mman.h | 5 +- libsanitizer/tsan/tsan_mutex.cc | 1 + libsanitizer/tsan/tsan_mutex.h | 1 + libsanitizer/tsan/tsan_mutexset.h | 4 +- libsanitizer/tsan/tsan_new_delete.cc | 8 +- libsanitizer/tsan/tsan_platform.h | 860 +++++++++--- libsanitizer/tsan/tsan_platform_linux.cc | 179 ++- libsanitizer/tsan/tsan_platform_mac.cc | 49 +- libsanitizer/tsan/tsan_platform_posix.cc | 73 +- libsanitizer/tsan/tsan_platform_windows.cc | 3 + libsanitizer/tsan/tsan_ppc_regs.h | 94 ++ libsanitizer/tsan/tsan_preinit.cc | 25 + libsanitizer/tsan/tsan_report.cc | 52 +- libsanitizer/tsan/tsan_report.h | 3 +- libsanitizer/tsan/tsan_rtl.cc | 114 +- libsanitizer/tsan/tsan_rtl.h | 90 +- libsanitizer/tsan/tsan_rtl_aarch64.S | 2 - libsanitizer/tsan/tsan_rtl_amd64.S | 101 +- libsanitizer/tsan/tsan_rtl_mips64.S | 212 +++ libsanitizer/tsan/tsan_rtl_mutex.cc | 67 +- libsanitizer/tsan/tsan_rtl_ppc64.S | 286 ++++ libsanitizer/tsan/tsan_rtl_proc.cc | 59 + libsanitizer/tsan/tsan_rtl_report.cc | 71 +- libsanitizer/tsan/tsan_rtl_thread.cc | 46 +- libsanitizer/tsan/tsan_stat.cc | 1 + libsanitizer/tsan/tsan_stat.h | 1 + libsanitizer/tsan/tsan_suppressions.cc | 15 +- libsanitizer/tsan/tsan_symbolize.cc | 10 +- libsanitizer/tsan/tsan_sync.cc | 69 +- libsanitizer/tsan/tsan_sync.h | 20 +- libsanitizer/tsan/tsan_trace.h | 4 +- libsanitizer/ubsan/Makefile.in | 1 + libsanitizer/ubsan/ubsan_checks.inc | 56 +- libsanitizer/ubsan/ubsan_diag.cc | 232 ++-- libsanitizer/ubsan/ubsan_diag.h | 21 +- libsanitizer/ubsan/ubsan_flags.cc | 3 +- libsanitizer/ubsan/ubsan_handlers.cc | 254 ++-- libsanitizer/ubsan/ubsan_handlers.h | 19 +- libsanitizer/ubsan/ubsan_handlers_cxx.cc | 105 +- libsanitizer/ubsan/ubsan_handlers_cxx.h | 14 - libsanitizer/ubsan/ubsan_init.cc | 1 + libsanitizer/ubsan/ubsan_platform.h | 3 +- libsanitizer/ubsan/ubsan_type_hash.h | 4 + libsanitizer/ubsan/ubsan_type_hash_itanium.cc | 24 +- libsanitizer/ubsan/ubsan_value.cc | 4 +- 211 files changed, 14509 insertions(+), 5390 deletions(-) create mode 100644 libsanitizer/asan/asan_descriptions.cc create mode 100644 libsanitizer/asan/asan_descriptions.h create mode 100644 libsanitizer/asan/asan_errors.cc create mode 100644 libsanitizer/asan/asan_errors.h create mode 100644 libsanitizer/asan/asan_memory_profile.cc create mode 100644 libsanitizer/asan/asan_scariness_score.h create mode 100644 libsanitizer/builtins/assembly.h create mode 100644 libsanitizer/include/sanitizer/esan_interface.h create mode 100644 libsanitizer/sanitizer_common/sanitizer_allocator_bytemap.h create mode 100644 libsanitizer/sanitizer_common/sanitizer_allocator_combined.h create mode 100644 libsanitizer/sanitizer_common/sanitizer_allocator_local_cache.h create mode 100644 libsanitizer/sanitizer_common/sanitizer_allocator_primary32.h create mode 100644 libsanitizer/sanitizer_common/sanitizer_allocator_primary64.h create mode 100644 libsanitizer/sanitizer_common/sanitizer_allocator_secondary.h create mode 100644 libsanitizer/sanitizer_common/sanitizer_allocator_size_class_map.h create mode 100644 libsanitizer/sanitizer_common/sanitizer_allocator_stats.h create mode 100644 libsanitizer/sanitizer_common/sanitizer_linux_mips64.S create mode 100644 libsanitizer/sanitizer_common/sanitizer_linux_s390.cc create mode 100644 libsanitizer/sanitizer_common/sanitizer_linux_x86_64.S create mode 100644 libsanitizer/sanitizer_common/sanitizer_termination.cc create mode 100644 libsanitizer/tsan/tsan_debugging.cc create mode 100644 libsanitizer/tsan/tsan_interceptors_mac.cc create mode 100644 libsanitizer/tsan/tsan_ppc_regs.h create mode 100644 libsanitizer/tsan/tsan_preinit.cc create mode 100644 libsanitizer/tsan/tsan_rtl_mips64.S create mode 100644 libsanitizer/tsan/tsan_rtl_ppc64.S create mode 100644 libsanitizer/tsan/tsan_rtl_proc.cc diff --git a/libsanitizer/ChangeLog b/libsanitizer/ChangeLog index 505e58d..54a8854 100644 --- a/libsanitizer/ChangeLog +++ b/libsanitizer/ChangeLog @@ -1,3 +1,28 @@ +2016-11-09 Maxim Ostapenko + + * All source files: Merge from upstream 285547. + * configure.tgt (SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS): New + variable. + * configure.ac (SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS): Handle it. + * asan/Makefile.am (asan_files): Add new files. + * asan/Makefile.in: Regenerate. + * ubsan/Makefile.in: Likewise. + * lsan/Makefile.in: Likewise. + * tsan/Makefile.am (tsan_files): Add new files. + * tsan/Makefile.in: Regenerate. + * sanitizer_common/Makefile.am (sanitizer_common_files): Add new files. + (EXTRA_libsanitizer_common_la_SOURCES): Define. + (libsanitizer_common_la_LIBADD): Likewise. + (libsanitizer_common_la_DEPENDENCIES): Likewise. + * sanitizer_common/Makefile.in: Regenerate. + * interception/Makefile.in: Likewise. + * libbacktace/Makefile.in: Likewise. + * Makefile.in: Likewise. + * configure: Likewise. + * merge.sh: Handle builtins/assembly.h merging. + * builtins/assembly.h: New file. + * asan/libtool-version: Bump the libasan SONAME. + 2016-12-21 Release Manager * GCC 6.3.0 released. diff --git a/libsanitizer/MERGE b/libsanitizer/MERGE index dfd606a..21c2f39 100644 --- a/libsanitizer/MERGE +++ b/libsanitizer/MERGE @@ -1,4 +1,4 @@ -253555 +285547 The first line of this file holds the svn revision number of the last merge done from the master library sources. diff --git a/libsanitizer/Makefile.in b/libsanitizer/Makefile.in index b362a89..c65d0b6 100644 --- a/libsanitizer/Makefile.in +++ b/libsanitizer/Makefile.in @@ -211,6 +211,7 @@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ RPC_DEFS = @RPC_DEFS@ +SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS = @SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ diff --git a/libsanitizer/asan/Makefile.am b/libsanitizer/asan/Makefile.am index 4500e21..bea23e5 100644 --- a/libsanitizer/asan/Makefile.am +++ b/libsanitizer/asan/Makefile.am @@ -19,6 +19,8 @@ asan_files = \ asan_activation.cc \ asan_allocator.cc \ asan_debugging.cc \ + asan_descriptions.cc \ + asan_errors.cc \ asan_fake_stack.cc \ asan_flags.cc \ asan_globals.cc \ @@ -28,6 +30,7 @@ asan_files = \ asan_malloc_linux.cc \ asan_malloc_mac.cc \ asan_malloc_win.cc \ + asan_memory_profile.cc \ asan_new_delete.cc \ asan_poisoning.cc \ asan_posix.cc \ diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in index 881d1d3..2a183db 100644 --- a/libsanitizer/asan/Makefile.in +++ b/libsanitizer/asan/Makefile.in @@ -112,9 +112,10 @@ libasan_la_DEPENDENCIES = \ $(top_builddir)/lsan/libsanitizer_lsan.la $(am__append_2) \ $(am__append_3) $(am__DEPENDENCIES_1) am__objects_1 = asan_activation.lo asan_allocator.lo asan_debugging.lo \ - asan_fake_stack.lo asan_flags.lo asan_globals.lo \ - asan_interceptors.lo asan_linux.lo asan_mac.lo \ - asan_malloc_linux.lo asan_malloc_mac.lo asan_malloc_win.lo \ + asan_descriptions.lo asan_errors.lo asan_fake_stack.lo \ + asan_flags.lo asan_globals.lo asan_interceptors.lo \ + asan_linux.lo asan_mac.lo asan_malloc_linux.lo \ + asan_malloc_mac.lo asan_malloc_win.lo asan_memory_profile.lo \ asan_new_delete.lo asan_poisoning.lo asan_posix.lo \ asan_report.lo asan_rtl.lo asan_stack.lo asan_stats.lo \ asan_suppressions.lo asan_thread.lo asan_win.lo \ @@ -220,6 +221,7 @@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ RPC_DEFS = @RPC_DEFS@ +SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS = @SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ @@ -309,6 +311,8 @@ asan_files = \ asan_activation.cc \ asan_allocator.cc \ asan_debugging.cc \ + asan_descriptions.cc \ + asan_errors.cc \ asan_fake_stack.cc \ asan_flags.cc \ asan_globals.cc \ @@ -318,6 +322,7 @@ asan_files = \ asan_malloc_linux.cc \ asan_malloc_mac.cc \ asan_malloc_win.cc \ + asan_memory_profile.cc \ asan_new_delete.cc \ asan_poisoning.cc \ asan_posix.cc \ @@ -455,6 +460,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_activation.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_debugging.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_descriptions.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_errors.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_fake_stack.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_flags.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_globals.Plo@am__quote@ @@ -464,6 +471,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_linux.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_mac.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_win.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_memory_profile.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_new_delete.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_poisoning.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_posix.Plo@am__quote@ diff --git a/libsanitizer/asan/asan_activation.cc b/libsanitizer/asan/asan_activation.cc index 5886795..ecd767c 100644 --- a/libsanitizer/asan/asan_activation.cc +++ b/libsanitizer/asan/asan_activation.cc @@ -45,6 +45,7 @@ static struct AsanDeactivatedFlags { FlagParser parser; RegisterActivationFlags(&parser, &f, &cf); + cf.SetDefaults(); // Copy the current activation flags. allocator_options.CopyTo(&f, &cf); cf.malloc_context_size = malloc_context_size; @@ -59,12 +60,7 @@ static struct AsanDeactivatedFlags { parser.ParseString(env); } - // Override from getprop asan.options. - char buf[100]; - GetExtraActivationFlags(buf, sizeof(buf)); - parser.ParseString(buf); - - SetVerbosity(cf.verbosity); + InitializeCommonFlags(&cf); if (Verbosity()) ReportUnrecognizedFlags(); diff --git a/libsanitizer/asan/asan_allocator.cc b/libsanitizer/asan/asan_allocator.cc index facbb33..d3ddb90 100644 --- a/libsanitizer/asan/asan_allocator.cc +++ b/libsanitizer/asan/asan_allocator.cc @@ -221,7 +221,7 @@ void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) { struct Allocator { static const uptr kMaxAllowedMallocSize = - FIRST_32_SECOND_64(3UL << 30, 1UL << 40); + FIRST_32_SECOND_64(3UL << 30, 1ULL << 40); static const uptr kMaxThreadLocalQuarantine = FIRST_32_SECOND_64(1 << 18, 1 << 20); @@ -264,9 +264,43 @@ struct Allocator { SharedInitCode(options); } + void RePoisonChunk(uptr chunk) { + // This could a user-facing chunk (with redzones), or some internal + // housekeeping chunk, like TransferBatch. Start by assuming the former. + AsanChunk *ac = GetAsanChunk((void *)chunk); + uptr allocated_size = allocator.GetActuallyAllocatedSize((void *)ac); + uptr beg = ac->Beg(); + uptr end = ac->Beg() + ac->UsedSize(true); + uptr chunk_end = chunk + allocated_size; + if (chunk < beg && beg < end && end <= chunk_end) { + // Looks like a valid AsanChunk. Or maybe not. Be conservative and only + // poison the redzones. + PoisonShadow(chunk, beg - chunk, kAsanHeapLeftRedzoneMagic); + uptr end_aligned_down = RoundDownTo(end, SHADOW_GRANULARITY); + FastPoisonShadowPartialRightRedzone( + end_aligned_down, end - end_aligned_down, + chunk_end - end_aligned_down, kAsanHeapLeftRedzoneMagic); + } else { + // This can not be an AsanChunk. Poison everything. It may be reused as + // AsanChunk later. + PoisonShadow(chunk, allocated_size, kAsanHeapLeftRedzoneMagic); + } + } + void ReInitialize(const AllocatorOptions &options) { allocator.SetMayReturnNull(options.may_return_null); SharedInitCode(options); + + // Poison all existing allocation's redzones. + if (CanPoisonMemory()) { + allocator.ForceLock(); + allocator.ForEachChunk( + [](uptr chunk, void *alloc) { + ((Allocator *)alloc)->RePoisonChunk(chunk); + }, + this); + allocator.ForceUnlock(); + } } void GetOptions(AllocatorOptions *options) const { @@ -354,7 +388,7 @@ struct Allocator { if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n", (void*)size); - return allocator.ReturnNullOrDie(); + return allocator.ReturnNullOrDieOnBadRequest(); } AsanThread *t = GetCurrentThread(); @@ -371,8 +405,7 @@ struct Allocator { allocator.Allocate(cache, needed_size, 8, false, check_rss_limit); } - if (!allocated) - return allocator.ReturnNullOrDie(); + if (!allocated) return allocator.ReturnNullOrDieOnOOM(); if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) { // Heap poisoning is enabled, but the allocator provides an unpoisoned @@ -456,7 +489,7 @@ struct Allocator { } // Set quarantine flag if chunk is allocated, issue ASan error report on - // available and quarantined ones. Return true on success, false otherwise. + // available and quarantined chunks. Return true on success, false otherwise. bool AtomicallySetQuarantineFlagIfAllocated(AsanChunk *m, void *ptr, BufferedStackTrace *stack) { u8 old_chunk_state = CHUNK_ALLOCATED; @@ -477,14 +510,6 @@ struct Allocator { void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack, AllocType alloc_type) { CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); - - if (m->alloc_type != alloc_type) { - if (atomic_load(&alloc_dealloc_mismatch, memory_order_acquire)) { - ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type, - (AllocType)alloc_type); - } - } - CHECK_GE(m->alloc_tid, 0); if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area. CHECK_EQ(m->free_tid, kInvalidTid); @@ -521,14 +546,24 @@ struct Allocator { uptr chunk_beg = p - kChunkHeaderSize; AsanChunk *m = reinterpret_cast(chunk_beg); - if (delete_size && flags()->new_delete_type_mismatch && - delete_size != m->UsedSize()) { - ReportNewDeleteSizeMismatch(p, delete_size, stack); - } + ASAN_FREE_HOOK(ptr); // Must mark the chunk as quarantined before any changes to its metadata. // Do not quarantine given chunk if we failed to set CHUNK_QUARANTINE flag. if (!AtomicallySetQuarantineFlagIfAllocated(m, ptr, stack)) return; + + if (m->alloc_type != alloc_type) { + if (atomic_load(&alloc_dealloc_mismatch, memory_order_acquire)) { + ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type, + (AllocType)alloc_type); + } + } + + if (delete_size && flags()->new_delete_type_mismatch && + delete_size != m->UsedSize()) { + ReportNewDeleteSizeMismatch(p, delete_size, stack); + } + QuarantineChunk(m, ptr, stack, alloc_type); } @@ -559,7 +594,7 @@ struct Allocator { void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { if (CallocShouldReturnNullDueToOverflow(size, nmemb)) - return allocator.ReturnNullOrDie(); + return allocator.ReturnNullOrDieOnBadRequest(); void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false); // If the memory comes from the secondary allocator no need to clear it // as it comes directly from mmap. @@ -650,6 +685,8 @@ struct Allocator { fallback_mutex.Unlock(); allocator.ForceUnlock(); } + + void ReleaseToOS() { allocator.ReleaseToOS(); } }; static Allocator instance(LINKER_INITIALIZED); @@ -661,11 +698,17 @@ static AsanAllocator &get_allocator() { bool AsanChunkView::IsValid() { return chunk_ && chunk_->chunk_state != CHUNK_AVAILABLE; } +bool AsanChunkView::IsAllocated() { + return chunk_ && chunk_->chunk_state == CHUNK_ALLOCATED; +} uptr AsanChunkView::Beg() { return chunk_->Beg(); } uptr AsanChunkView::End() { return Beg() + UsedSize(); } uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); } uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; } uptr AsanChunkView::FreeTid() { return chunk_->free_tid; } +AllocType AsanChunkView::GetAllocType() { + return (AllocType)chunk_->alloc_type; +} static StackTrace GetStackTraceFromId(u32 id) { CHECK(id); @@ -674,16 +717,22 @@ static StackTrace GetStackTraceFromId(u32 id) { return res; } +u32 AsanChunkView::GetAllocStackId() { return chunk_->alloc_context_id; } +u32 AsanChunkView::GetFreeStackId() { return chunk_->free_context_id; } + StackTrace AsanChunkView::GetAllocStack() { - return GetStackTraceFromId(chunk_->alloc_context_id); + return GetStackTraceFromId(GetAllocStackId()); } StackTrace AsanChunkView::GetFreeStack() { - return GetStackTraceFromId(chunk_->free_context_id); + return GetStackTraceFromId(GetFreeStackId()); } +void ReleaseToOS() { instance.ReleaseToOS(); } + void InitializeAllocator(const AllocatorOptions &options) { instance.Initialize(options); + SetAllocatorReleaseToOSCallback(ReleaseToOS); } void ReInitializeAllocator(const AllocatorOptions &options) { @@ -697,6 +746,9 @@ void GetAllocatorOptions(AllocatorOptions *options) { AsanChunkView FindHeapChunkByAddress(uptr addr) { return instance.FindHeapChunkByAddress(addr); } +AsanChunkView FindHeapChunkByAllocBeg(uptr addr) { + return AsanChunkView(instance.GetAsanChunk(reinterpret_cast(addr))); +} void AsanThreadLocalMallocStorage::CommitBack() { instance.CommitBack(this); @@ -760,7 +812,7 @@ int asan_posix_memalign(void **memptr, uptr alignment, uptr size, return 0; } -uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp) { +uptr asan_malloc_usable_size(const void *ptr, uptr pc, uptr bp) { if (!ptr) return 0; uptr usable_size = instance.AllocationSize(reinterpret_cast(ptr)); if (flags()->check_malloc_usable_size && (usable_size == 0)) { diff --git a/libsanitizer/asan/asan_allocator.h b/libsanitizer/asan/asan_allocator.h index 6d9b7e4..7aa1a92 100644 --- a/libsanitizer/asan/asan_allocator.h +++ b/libsanitizer/asan/asan_allocator.h @@ -47,16 +47,20 @@ void GetAllocatorOptions(AllocatorOptions *options); class AsanChunkView { public: explicit AsanChunkView(AsanChunk *chunk) : chunk_(chunk) {} - bool IsValid(); // Checks if AsanChunkView points to a valid allocated - // or quarantined chunk. - uptr Beg(); // First byte of user memory. - uptr End(); // Last byte of user memory. - uptr UsedSize(); // Size requested by the user. + bool IsValid(); // Checks if AsanChunkView points to a valid allocated + // or quarantined chunk. + bool IsAllocated(); // Checks if the memory is currently allocated. + uptr Beg(); // First byte of user memory. + uptr End(); // Last byte of user memory. + uptr UsedSize(); // Size requested by the user. uptr AllocTid(); uptr FreeTid(); bool Eq(const AsanChunkView &c) const { return chunk_ == c.chunk_; } + u32 GetAllocStackId(); + u32 GetFreeStackId(); StackTrace GetAllocStack(); StackTrace GetFreeStack(); + AllocType GetAllocType(); bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) { if (addr >= Beg() && (addr + access_size) <= End()) { *offset = addr - Beg(); @@ -85,6 +89,7 @@ class AsanChunkView { }; AsanChunkView FindHeapChunkByAddress(uptr address); +AsanChunkView FindHeapChunkByAllocBeg(uptr address); // List of AsanChunks with total size. class AsanChunkFifoList: public IntrusiveList { @@ -112,18 +117,36 @@ struct AsanMapUnmapCallback { # if defined(__powerpc64__) const uptr kAllocatorSpace = 0xa0000000000ULL; const uptr kAllocatorSize = 0x20000000000ULL; // 2T. +typedef DefaultSizeClassMap SizeClassMap; +# elif defined(__aarch64__) && SANITIZER_ANDROID +const uptr kAllocatorSpace = 0x3000000000ULL; +const uptr kAllocatorSize = 0x2000000000ULL; // 128G. +typedef VeryCompactSizeClassMap SizeClassMap; # elif defined(__aarch64__) -// AArch64/SANITIZIER_CAN_USER_ALLOCATOR64 is only for 42-bit VMA +// AArch64/SANITIZER_CAN_USER_ALLOCATOR64 is only for 42-bit VMA // so no need to different values for different VMA. const uptr kAllocatorSpace = 0x10000000000ULL; const uptr kAllocatorSize = 0x10000000000ULL; // 3T. +typedef DefaultSizeClassMap SizeClassMap; +# elif SANITIZER_WINDOWS +const uptr kAllocatorSpace = ~(uptr)0; +const uptr kAllocatorSize = 0x8000000000ULL; // 500G +typedef DefaultSizeClassMap SizeClassMap; # else const uptr kAllocatorSpace = 0x600000000000ULL; const uptr kAllocatorSize = 0x40000000000ULL; // 4T. -# endif typedef DefaultSizeClassMap SizeClassMap; -typedef SizeClassAllocator64 PrimaryAllocator; +# endif +struct AP64 { // Allocator64 parameters. Deliberately using a short name. + static const uptr kSpaceBeg = kAllocatorSpace; + static const uptr kSpaceSize = kAllocatorSize; + static const uptr kMetadataSize = 0; + typedef __asan::SizeClassMap SizeClassMap; + typedef AsanMapUnmapCallback MapUnmapCallback; + static const uptr kFlags = 0; +}; + +typedef SizeClassAllocator64 PrimaryAllocator; #else // Fallback to SizeClassAllocator32. static const uptr kRegionSizeLog = 17; static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog; @@ -132,7 +155,7 @@ typedef FlatByteMap ByteMap; # elif SANITIZER_WORDSIZE == 64 typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap; # endif -typedef CompactSizeClassMap SizeClassMap; +typedef VeryCompactSizeClassMap SizeClassMap; typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, 16, SizeClassMap, kRegionSizeLog, ByteMap, @@ -169,7 +192,7 @@ void *asan_pvalloc(uptr size, BufferedStackTrace *stack); int asan_posix_memalign(void **memptr, uptr alignment, uptr size, BufferedStackTrace *stack); -uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp); +uptr asan_malloc_usable_size(const void *ptr, uptr pc, uptr bp); uptr asan_mz_size(const void *ptr); void asan_mz_force_lock(); diff --git a/libsanitizer/asan/asan_debugging.cc b/libsanitizer/asan/asan_debugging.cc index 6e33a9d..1c8d0df 100644 --- a/libsanitizer/asan/asan_debugging.cc +++ b/libsanitizer/asan/asan_debugging.cc @@ -12,74 +12,39 @@ //===----------------------------------------------------------------------===// #include "asan_allocator.h" +#include "asan_descriptions.h" #include "asan_flags.h" #include "asan_internal.h" #include "asan_mapping.h" #include "asan_report.h" #include "asan_thread.h" -namespace __asan { - -void GetInfoForStackVar(uptr addr, AddressDescription *descr, AsanThread *t) { - descr->name[0] = 0; - descr->region_address = 0; - descr->region_size = 0; - descr->region_kind = "stack"; +namespace { +using namespace __asan; - AsanThread::StackFrameAccess access; - if (!t->GetStackFrameAccessByAddr(addr, &access)) - return; +static void FindInfoForStackVar(uptr addr, const char *frame_descr, uptr offset, + char *name, uptr name_size, + uptr ®ion_address, uptr ®ion_size) { InternalMmapVector vars(16); - if (!ParseFrameDescription(access.frame_descr, &vars)) { + if (!ParseFrameDescription(frame_descr, &vars)) { return; } for (uptr i = 0; i < vars.size(); i++) { - if (access.offset <= vars[i].beg + vars[i].size) { - internal_strncat(descr->name, vars[i].name_pos, - Min(descr->name_size, vars[i].name_len)); - descr->region_address = addr - (access.offset - vars[i].beg); - descr->region_size = vars[i].size; + if (offset <= vars[i].beg + vars[i].size) { + // We use name_len + 1 because strlcpy will guarantee a \0 at the end, so + // if we're limiting the copy due to name_len, we add 1 to ensure we copy + // the whole name and then terminate with '\0'. + internal_strlcpy(name, vars[i].name_pos, + Min(name_size, vars[i].name_len + 1)); + region_address = addr - (offset - vars[i].beg); + region_size = vars[i].size; return; } } } -void GetInfoForHeapAddress(uptr addr, AddressDescription *descr) { - AsanChunkView chunk = FindHeapChunkByAddress(addr); - - descr->name[0] = 0; - descr->region_address = 0; - descr->region_size = 0; - - if (!chunk.IsValid()) { - descr->region_kind = "heap-invalid"; - return; - } - - descr->region_address = chunk.Beg(); - descr->region_size = chunk.UsedSize(); - descr->region_kind = "heap"; -} - -void AsanLocateAddress(uptr addr, AddressDescription *descr) { - if (DescribeAddressIfShadow(addr, descr, /* print */ false)) { - return; - } - if (GetInfoForAddressIfGlobal(addr, descr)) { - return; - } - asanThreadRegistry().Lock(); - AsanThread *thread = FindThreadByStackAddress(addr); - asanThreadRegistry().Unlock(); - if (thread) { - GetInfoForStackVar(addr, descr, thread); - return; - } - GetInfoForHeapAddress(addr, descr); -} - -static uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id, +uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id, bool alloc_stack) { AsanChunkView chunk = FindHeapChunkByAddress(addr); if (!chunk.IsValid()) return 0; @@ -106,18 +71,58 @@ static uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id, return 0; } -} // namespace __asan - -using namespace __asan; +} // namespace SANITIZER_INTERFACE_ATTRIBUTE const char *__asan_locate_address(uptr addr, char *name, uptr name_size, - uptr *region_address, uptr *region_size) { - AddressDescription descr = { name, name_size, 0, 0, nullptr }; - AsanLocateAddress(addr, &descr); - if (region_address) *region_address = descr.region_address; - if (region_size) *region_size = descr.region_size; - return descr.region_kind; + uptr *region_address_ptr, + uptr *region_size_ptr) { + AddressDescription descr(addr); + uptr region_address = 0; + uptr region_size = 0; + const char *region_kind = nullptr; + if (name && name_size > 0) name[0] = 0; + + if (auto shadow = descr.AsShadow()) { + // region_{address,size} are already 0 + switch (shadow->kind) { + case kShadowKindLow: + region_kind = "low shadow"; + break; + case kShadowKindGap: + region_kind = "shadow gap"; + break; + case kShadowKindHigh: + region_kind = "high shadow"; + break; + } + } else if (auto heap = descr.AsHeap()) { + region_kind = "heap"; + region_address = heap->chunk_access.chunk_begin; + region_size = heap->chunk_access.chunk_size; + } else if (auto stack = descr.AsStack()) { + region_kind = "stack"; + if (!stack->frame_descr) { + // region_{address,size} are already 0 + } else { + FindInfoForStackVar(addr, stack->frame_descr, stack->offset, name, + name_size, region_address, region_size); + } + } else if (auto global = descr.AsGlobal()) { + region_kind = "global"; + auto &g = global->globals[0]; + internal_strlcpy(name, g.name, name_size); + region_address = g.beg; + region_size = g.size; + } else { + // region_{address,size} are already 0 + region_kind = "heap-invalid"; + } + + CHECK(region_kind); + if (region_address_ptr) *region_address_ptr = region_address; + if (region_size_ptr) *region_size_ptr = region_size; + return region_kind; } SANITIZER_INTERFACE_ATTRIBUTE diff --git a/libsanitizer/asan/asan_descriptions.cc b/libsanitizer/asan/asan_descriptions.cc new file mode 100644 index 0000000..35d1619 --- /dev/null +++ b/libsanitizer/asan/asan_descriptions.cc @@ -0,0 +1,484 @@ +//===-- asan_descriptions.cc ------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan functions for getting information about an address and/or printing it. +//===----------------------------------------------------------------------===// + +#include "asan_descriptions.h" +#include "asan_mapping.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "sanitizer_common/sanitizer_stackdepot.h" + +namespace __asan { + +// Return " (thread_name) " or an empty string if the name is empty. +const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[], + uptr buff_len) { + const char *name = t->name; + if (name[0] == '\0') return ""; + buff[0] = 0; + internal_strncat(buff, " (", 3); + internal_strncat(buff, name, buff_len - 4); + internal_strncat(buff, ")", 2); + return buff; +} + +const char *ThreadNameWithParenthesis(u32 tid, char buff[], uptr buff_len) { + if (tid == kInvalidTid) return ""; + asanThreadRegistry().CheckLocked(); + AsanThreadContext *t = GetThreadContextByTidLocked(tid); + return ThreadNameWithParenthesis(t, buff, buff_len); +} + +void DescribeThread(AsanThreadContext *context) { + CHECK(context); + asanThreadRegistry().CheckLocked(); + // No need to announce the main thread. + if (context->tid == 0 || context->announced) { + return; + } + context->announced = true; + char tname[128]; + InternalScopedString str(1024); + str.append("Thread T%d%s", context->tid, + ThreadNameWithParenthesis(context->tid, tname, sizeof(tname))); + if (context->parent_tid == kInvalidTid) { + str.append(" created by unknown thread\n"); + Printf("%s", str.data()); + return; + } + str.append( + " created by T%d%s here:\n", context->parent_tid, + ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname))); + Printf("%s", str.data()); + StackDepotGet(context->stack_id).Print(); + // Recursively described parent thread if needed. + if (flags()->print_full_thread_history) { + AsanThreadContext *parent_context = + GetThreadContextByTidLocked(context->parent_tid); + DescribeThread(parent_context); + } +} + +// Shadow descriptions +static bool GetShadowKind(uptr addr, ShadowKind *shadow_kind) { + CHECK(!AddrIsInMem(addr)); + if (AddrIsInShadowGap(addr)) { + *shadow_kind = kShadowKindGap; + } else if (AddrIsInHighShadow(addr)) { + *shadow_kind = kShadowKindHigh; + } else if (AddrIsInLowShadow(addr)) { + *shadow_kind = kShadowKindLow; + } else { + CHECK(0 && "Address is not in memory and not in shadow?"); + return false; + } + return true; +} + +bool DescribeAddressIfShadow(uptr addr) { + ShadowAddressDescription descr; + if (!GetShadowAddressInformation(addr, &descr)) return false; + descr.Print(); + return true; +} + +bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr) { + if (AddrIsInMem(addr)) return false; + ShadowKind shadow_kind; + if (!GetShadowKind(addr, &shadow_kind)) return false; + if (shadow_kind != kShadowKindGap) descr->shadow_byte = *(u8 *)addr; + descr->addr = addr; + descr->kind = shadow_kind; + return true; +} + +// Heap descriptions +static void GetAccessToHeapChunkInformation(ChunkAccess *descr, + AsanChunkView chunk, uptr addr, + uptr access_size) { + descr->bad_addr = addr; + if (chunk.AddrIsAtLeft(addr, access_size, &descr->offset)) { + descr->access_type = kAccessTypeLeft; + } else if (chunk.AddrIsAtRight(addr, access_size, &descr->offset)) { + descr->access_type = kAccessTypeRight; + if (descr->offset < 0) { + descr->bad_addr -= descr->offset; + descr->offset = 0; + } + } else if (chunk.AddrIsInside(addr, access_size, &descr->offset)) { + descr->access_type = kAccessTypeInside; + } else { + descr->access_type = kAccessTypeUnknown; + } + descr->chunk_begin = chunk.Beg(); + descr->chunk_size = chunk.UsedSize(); + descr->alloc_type = chunk.GetAllocType(); +} + +static void PrintHeapChunkAccess(uptr addr, const ChunkAccess &descr) { + Decorator d; + InternalScopedString str(4096); + str.append("%s", d.Location()); + switch (descr.access_type) { + case kAccessTypeLeft: + str.append("%p is located %zd bytes to the left of", + (void *)descr.bad_addr, descr.offset); + break; + case kAccessTypeRight: + str.append("%p is located %zd bytes to the right of", + (void *)descr.bad_addr, descr.offset); + break; + case kAccessTypeInside: + str.append("%p is located %zd bytes inside of", (void *)descr.bad_addr, + descr.offset); + break; + case kAccessTypeUnknown: + str.append( + "%p is located somewhere around (this is AddressSanitizer bug!)", + (void *)descr.bad_addr); + } + str.append(" %zu-byte region [%p,%p)\n", descr.chunk_size, + (void *)descr.chunk_begin, + (void *)(descr.chunk_begin + descr.chunk_size)); + str.append("%s", d.EndLocation()); + Printf("%s", str.data()); +} + +bool GetHeapAddressInformation(uptr addr, uptr access_size, + HeapAddressDescription *descr) { + AsanChunkView chunk = FindHeapChunkByAddress(addr); + if (!chunk.IsValid()) { + return false; + } + descr->addr = addr; + GetAccessToHeapChunkInformation(&descr->chunk_access, chunk, addr, + access_size); + CHECK_NE(chunk.AllocTid(), kInvalidTid); + descr->alloc_tid = chunk.AllocTid(); + descr->alloc_stack_id = chunk.GetAllocStackId(); + descr->free_tid = chunk.FreeTid(); + if (descr->free_tid != kInvalidTid) + descr->free_stack_id = chunk.GetFreeStackId(); + return true; +} + +static StackTrace GetStackTraceFromId(u32 id) { + CHECK(id); + StackTrace res = StackDepotGet(id); + CHECK(res.trace); + return res; +} + +bool DescribeAddressIfHeap(uptr addr, uptr access_size) { + HeapAddressDescription descr; + if (!GetHeapAddressInformation(addr, access_size, &descr)) { + Printf( + "AddressSanitizer can not describe address in more detail " + "(wild memory access suspected).\n"); + return false; + } + descr.Print(); + return true; +} + +// Stack descriptions +bool GetStackAddressInformation(uptr addr, uptr access_size, + StackAddressDescription *descr) { + AsanThread *t = FindThreadByStackAddress(addr); + if (!t) return false; + + descr->addr = addr; + descr->tid = t->tid(); + // Try to fetch precise stack frame for this access. + AsanThread::StackFrameAccess access; + if (!t->GetStackFrameAccessByAddr(addr, &access)) { + descr->frame_descr = nullptr; + return true; + } + + descr->offset = access.offset; + descr->access_size = access_size; + descr->frame_pc = access.frame_pc; + descr->frame_descr = access.frame_descr; + +#if SANITIZER_PPC64V1 + // On PowerPC64 ELFv1, the address of a function actually points to a + // three-doubleword data structure with the first field containing + // the address of the function's code. + descr->frame_pc = *reinterpret_cast(descr->frame_pc); +#endif + descr->frame_pc += 16; + + return true; +} + +static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr, + uptr access_size, uptr prev_var_end, + uptr next_var_beg) { + uptr var_end = var.beg + var.size; + uptr addr_end = addr + access_size; + const char *pos_descr = nullptr; + // If the variable [var.beg, var_end) is the nearest variable to the + // current memory access, indicate it in the log. + if (addr >= var.beg) { + if (addr_end <= var_end) + pos_descr = "is inside"; // May happen if this is a use-after-return. + else if (addr < var_end) + pos_descr = "partially overflows"; + else if (addr_end <= next_var_beg && + next_var_beg - addr_end >= addr - var_end) + pos_descr = "overflows"; + } else { + if (addr_end > var.beg) + pos_descr = "partially underflows"; + else if (addr >= prev_var_end && addr - prev_var_end >= var.beg - addr_end) + pos_descr = "underflows"; + } + InternalScopedString str(1024); + str.append(" [%zd, %zd)", var.beg, var_end); + // Render variable name. + str.append(" '"); + for (uptr i = 0; i < var.name_len; ++i) { + str.append("%c", var.name_pos[i]); + } + str.append("'"); + if (pos_descr) { + Decorator d; + // FIXME: we may want to also print the size of the access here, + // but in case of accesses generated by memset it may be confusing. + str.append("%s <== Memory access at offset %zd %s this variable%s\n", + d.Location(), addr, pos_descr, d.EndLocation()); + } else { + str.append("\n"); + } + Printf("%s", str.data()); +} + +bool DescribeAddressIfStack(uptr addr, uptr access_size) { + StackAddressDescription descr; + if (!GetStackAddressInformation(addr, access_size, &descr)) return false; + descr.Print(); + return true; +} + +// Global descriptions +static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size, + const __asan_global &g) { + InternalScopedString str(4096); + Decorator d; + str.append("%s", d.Location()); + if (addr < g.beg) { + str.append("%p is located %zd bytes to the left", (void *)addr, + g.beg - addr); + } else if (addr + access_size > g.beg + g.size) { + if (addr < g.beg + g.size) addr = g.beg + g.size; + str.append("%p is located %zd bytes to the right", (void *)addr, + addr - (g.beg + g.size)); + } else { + // Can it happen? + str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg); + } + str.append(" of global variable '%s' defined in '", + MaybeDemangleGlobalName(g.name)); + PrintGlobalLocation(&str, g); + str.append("' (0x%zx) of size %zu\n", g.beg, g.size); + str.append("%s", d.EndLocation()); + PrintGlobalNameIfASCII(&str, g); + Printf("%s", str.data()); +} + +bool GetGlobalAddressInformation(uptr addr, uptr access_size, + GlobalAddressDescription *descr) { + descr->addr = addr; + int globals_num = GetGlobalsForAddress(addr, descr->globals, descr->reg_sites, + ARRAY_SIZE(descr->globals)); + descr->size = globals_num; + descr->access_size = access_size; + return globals_num != 0; +} + +bool DescribeAddressIfGlobal(uptr addr, uptr access_size, + const char *bug_type) { + GlobalAddressDescription descr; + if (!GetGlobalAddressInformation(addr, access_size, &descr)) return false; + + descr.Print(bug_type); + return true; +} + +void ShadowAddressDescription::Print() const { + Printf("Address %p is located in the %s area.\n", addr, ShadowNames[kind]); +} + +void GlobalAddressDescription::Print(const char *bug_type) const { + for (int i = 0; i < size; i++) { + DescribeAddressRelativeToGlobal(addr, access_size, globals[i]); + if (bug_type && + 0 == internal_strcmp(bug_type, "initialization-order-fiasco") && + reg_sites[i]) { + Printf(" registered at:\n"); + StackDepotGet(reg_sites[i]).Print(); + } + } +} + +void StackAddressDescription::Print() const { + Decorator d; + char tname[128]; + Printf("%s", d.Location()); + Printf("Address %p is located in stack of thread T%d%s", addr, tid, + ThreadNameWithParenthesis(tid, tname, sizeof(tname))); + + if (!frame_descr) { + Printf("%s\n", d.EndLocation()); + return; + } + Printf(" at offset %zu in frame%s\n", offset, d.EndLocation()); + + // Now we print the frame where the alloca has happened. + // We print this frame as a stack trace with one element. + // The symbolizer may print more than one frame if inlining was involved. + // The frame numbers may be different than those in the stack trace printed + // previously. That's unfortunate, but I have no better solution, + // especially given that the alloca may be from entirely different place + // (e.g. use-after-scope, or different thread's stack). + Printf("%s", d.EndLocation()); + StackTrace alloca_stack(&frame_pc, 1); + alloca_stack.Print(); + + InternalMmapVector vars(16); + if (!ParseFrameDescription(frame_descr, &vars)) { + Printf( + "AddressSanitizer can't parse the stack frame " + "descriptor: |%s|\n", + frame_descr); + // 'addr' is a stack address, so return true even if we can't parse frame + return; + } + uptr n_objects = vars.size(); + // Report the number of stack objects. + Printf(" This frame has %zu object(s):\n", n_objects); + + // Report all objects in this frame. + for (uptr i = 0; i < n_objects; i++) { + uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0; + uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL); + PrintAccessAndVarIntersection(vars[i], offset, access_size, prev_var_end, + next_var_beg); + } + Printf( + "HINT: this may be a false positive if your program uses " + "some custom stack unwind mechanism or swapcontext\n"); + if (SANITIZER_WINDOWS) + Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n"); + else + Printf(" (longjmp and C++ exceptions *are* supported)\n"); + + DescribeThread(GetThreadContextByTidLocked(tid)); +} + +void HeapAddressDescription::Print() const { + PrintHeapChunkAccess(addr, chunk_access); + + asanThreadRegistry().CheckLocked(); + AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(alloc_tid); + StackTrace alloc_stack = GetStackTraceFromId(alloc_stack_id); + + char tname[128]; + Decorator d; + AsanThreadContext *free_thread = nullptr; + if (free_tid != kInvalidTid) { + free_thread = GetThreadContextByTidLocked(free_tid); + Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(), + free_thread->tid, + ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)), + d.EndAllocation()); + StackTrace free_stack = GetStackTraceFromId(free_stack_id); + free_stack.Print(); + Printf("%spreviously allocated by thread T%d%s here:%s\n", d.Allocation(), + alloc_thread->tid, + ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), + d.EndAllocation()); + } else { + Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(), + alloc_thread->tid, + ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), + d.EndAllocation()); + } + alloc_stack.Print(); + DescribeThread(GetCurrentThread()); + if (free_thread) DescribeThread(free_thread); + DescribeThread(alloc_thread); +} + +AddressDescription::AddressDescription(uptr addr, uptr access_size, + bool shouldLockThreadRegistry) { + if (GetShadowAddressInformation(addr, &data.shadow)) { + data.kind = kAddressKindShadow; + return; + } + if (GetHeapAddressInformation(addr, access_size, &data.heap)) { + data.kind = kAddressKindHeap; + return; + } + + bool isStackMemory = false; + if (shouldLockThreadRegistry) { + ThreadRegistryLock l(&asanThreadRegistry()); + isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack); + } else { + isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack); + } + if (isStackMemory) { + data.kind = kAddressKindStack; + return; + } + + if (GetGlobalAddressInformation(addr, access_size, &data.global)) { + data.kind = kAddressKindGlobal; + return; + } + data.kind = kAddressKindWild; + addr = 0; +} + +void PrintAddressDescription(uptr addr, uptr access_size, + const char *bug_type) { + ShadowAddressDescription shadow_descr; + if (GetShadowAddressInformation(addr, &shadow_descr)) { + shadow_descr.Print(); + return; + } + + GlobalAddressDescription global_descr; + if (GetGlobalAddressInformation(addr, access_size, &global_descr)) { + global_descr.Print(bug_type); + return; + } + + StackAddressDescription stack_descr; + if (GetStackAddressInformation(addr, access_size, &stack_descr)) { + stack_descr.Print(); + return; + } + + HeapAddressDescription heap_descr; + if (GetHeapAddressInformation(addr, access_size, &heap_descr)) { + heap_descr.Print(); + return; + } + + // We exhausted our possibilities. Bail out. + Printf( + "AddressSanitizer can not describe address in more detail " + "(wild memory access suspected).\n"); +} +} // namespace __asan diff --git a/libsanitizer/asan/asan_descriptions.h b/libsanitizer/asan/asan_descriptions.h new file mode 100644 index 0000000..584b9ba --- /dev/null +++ b/libsanitizer/asan/asan_descriptions.h @@ -0,0 +1,251 @@ +//===-- asan_descriptions.h -------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for asan_descriptions.cc. +// TODO(filcab): Most struct definitions should move to the interface headers. +//===----------------------------------------------------------------------===// +#ifndef ASAN_DESCRIPTIONS_H +#define ASAN_DESCRIPTIONS_H + +#include "asan_allocator.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_report_decorator.h" + +namespace __asan { + +void DescribeThread(AsanThreadContext *context); +static inline void DescribeThread(AsanThread *t) { + if (t) DescribeThread(t->context()); +} +const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[], + uptr buff_len); +const char *ThreadNameWithParenthesis(u32 tid, char buff[], uptr buff_len); + +class Decorator : public __sanitizer::SanitizerCommonDecorator { + public: + Decorator() : SanitizerCommonDecorator() {} + const char *Access() { return Blue(); } + const char *EndAccess() { return Default(); } + const char *Location() { return Green(); } + const char *EndLocation() { return Default(); } + const char *Allocation() { return Magenta(); } + const char *EndAllocation() { return Default(); } + + const char *ShadowByte(u8 byte) { + switch (byte) { + case kAsanHeapLeftRedzoneMagic: + case kAsanArrayCookieMagic: + return Red(); + case kAsanHeapFreeMagic: + return Magenta(); + case kAsanStackLeftRedzoneMagic: + case kAsanStackMidRedzoneMagic: + case kAsanStackRightRedzoneMagic: + return Red(); + case kAsanStackAfterReturnMagic: + return Magenta(); + case kAsanInitializationOrderMagic: + return Cyan(); + case kAsanUserPoisonedMemoryMagic: + case kAsanContiguousContainerOOBMagic: + case kAsanAllocaLeftMagic: + case kAsanAllocaRightMagic: + return Blue(); + case kAsanStackUseAfterScopeMagic: + return Magenta(); + case kAsanGlobalRedzoneMagic: + return Red(); + case kAsanInternalHeapMagic: + return Yellow(); + case kAsanIntraObjectRedzone: + return Yellow(); + default: + return Default(); + } + } + const char *EndShadowByte() { return Default(); } + const char *MemoryByte() { return Magenta(); } + const char *EndMemoryByte() { return Default(); } +}; + +enum ShadowKind : u8 { + kShadowKindLow, + kShadowKindGap, + kShadowKindHigh, +}; +static const char *const ShadowNames[] = {"low shadow", "shadow gap", + "high shadow"}; + +struct ShadowAddressDescription { + uptr addr; + ShadowKind kind; + u8 shadow_byte; + + void Print() const; +}; + +bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr); +bool DescribeAddressIfShadow(uptr addr); + +enum AccessType { + kAccessTypeLeft, + kAccessTypeRight, + kAccessTypeInside, + kAccessTypeUnknown, // This means we have an AddressSanitizer bug! +}; + +struct ChunkAccess { + uptr bad_addr; + sptr offset; + uptr chunk_begin; + uptr chunk_size; + u32 access_type : 2; + u32 alloc_type : 2; +}; + +struct HeapAddressDescription { + uptr addr; + uptr alloc_tid; + uptr free_tid; + u32 alloc_stack_id; + u32 free_stack_id; + ChunkAccess chunk_access; + + void Print() const; +}; + +bool GetHeapAddressInformation(uptr addr, uptr access_size, + HeapAddressDescription *descr); +bool DescribeAddressIfHeap(uptr addr, uptr access_size = 1); + +struct StackAddressDescription { + uptr addr; + uptr tid; + uptr offset; + uptr frame_pc; + uptr access_size; + const char *frame_descr; + + void Print() const; +}; + +bool GetStackAddressInformation(uptr addr, uptr access_size, + StackAddressDescription *descr); + +struct GlobalAddressDescription { + uptr addr; + // Assume address is close to at most four globals. + static const int kMaxGlobals = 4; + __asan_global globals[kMaxGlobals]; + u32 reg_sites[kMaxGlobals]; + uptr access_size; + u8 size; + + void Print(const char *bug_type = "") const; +}; + +bool GetGlobalAddressInformation(uptr addr, uptr access_size, + GlobalAddressDescription *descr); +bool DescribeAddressIfGlobal(uptr addr, uptr access_size, const char *bug_type); + +// General function to describe an address. Will try to describe the address as +// a shadow, global (variable), stack, or heap address. +// bug_type is optional and is used for checking if we're reporting an +// initialization-order-fiasco +// The proper access_size should be passed for stack, global, and heap +// addresses. Defaults to 1. +// Each of the *AddressDescription functions has its own Print() member, which +// may take access_size and bug_type parameters if needed. +void PrintAddressDescription(uptr addr, uptr access_size = 1, + const char *bug_type = ""); + +enum AddressKind { + kAddressKindWild, + kAddressKindShadow, + kAddressKindHeap, + kAddressKindStack, + kAddressKindGlobal, +}; + +class AddressDescription { + struct AddressDescriptionData { + AddressKind kind; + union { + ShadowAddressDescription shadow; + HeapAddressDescription heap; + StackAddressDescription stack; + GlobalAddressDescription global; + uptr addr; + }; + }; + + AddressDescriptionData data; + + public: + AddressDescription() = default; + // shouldLockThreadRegistry allows us to skip locking if we're sure we already + // have done it. + AddressDescription(uptr addr, bool shouldLockThreadRegistry = true) + : AddressDescription(addr, 1, shouldLockThreadRegistry) {} + AddressDescription(uptr addr, uptr access_size, + bool shouldLockThreadRegistry = true); + + uptr Address() const { + switch (data.kind) { + case kAddressKindWild: + return data.addr; + case kAddressKindShadow: + return data.shadow.addr; + case kAddressKindHeap: + return data.heap.addr; + case kAddressKindStack: + return data.stack.addr; + case kAddressKindGlobal: + return data.global.addr; + } + UNREACHABLE("AddressInformation kind is invalid"); + } + void Print(const char *bug_descr = nullptr) const { + switch (data.kind) { + case kAddressKindWild: + Printf("Address %p is a wild pointer.\n", data.addr); + return; + case kAddressKindShadow: + return data.shadow.Print(); + case kAddressKindHeap: + return data.heap.Print(); + case kAddressKindStack: + return data.stack.Print(); + case kAddressKindGlobal: + // initialization-order-fiasco has a special Print() + return data.global.Print(bug_descr); + } + UNREACHABLE("AddressInformation kind is invalid"); + } + + void StoreTo(AddressDescriptionData *dst) const { *dst = data; } + + const ShadowAddressDescription *AsShadow() const { + return data.kind == kAddressKindShadow ? &data.shadow : nullptr; + } + const HeapAddressDescription *AsHeap() const { + return data.kind == kAddressKindHeap ? &data.heap : nullptr; + } + const StackAddressDescription *AsStack() const { + return data.kind == kAddressKindStack ? &data.stack : nullptr; + } + const GlobalAddressDescription *AsGlobal() const { + return data.kind == kAddressKindGlobal ? &data.global : nullptr; + } +}; + +} // namespace __asan + +#endif // ASAN_DESCRIPTIONS_H diff --git a/libsanitizer/asan/asan_errors.cc b/libsanitizer/asan/asan_errors.cc new file mode 100644 index 0000000..73c4cca --- /dev/null +++ b/libsanitizer/asan/asan_errors.cc @@ -0,0 +1,494 @@ +//===-- asan_errors.cc ------------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan implementation for error structures. +//===----------------------------------------------------------------------===// + +#include "asan_errors.h" +#include +#include "asan_descriptions.h" +#include "asan_mapping.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "sanitizer_common/sanitizer_stackdepot.h" + +namespace __asan { + +void ErrorStackOverflow::Print() { + Decorator d; + Printf("%s", d.Warning()); + Report( + "ERROR: AddressSanitizer: stack-overflow on address %p" + " (pc %p bp %p sp %p T%d)\n", + (void *)addr, (void *)pc, (void *)bp, (void *)sp, tid); + Printf("%s", d.EndWarning()); + scariness.Print(); + BufferedStackTrace stack; + GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, context, + common_flags()->fast_unwind_on_fatal); + stack.Print(); + ReportErrorSummary("stack-overflow", &stack); +} + +static void MaybeDumpInstructionBytes(uptr pc) { + if (!flags()->dump_instruction_bytes || (pc < GetPageSizeCached())) return; + InternalScopedString str(1024); + str.append("First 16 instruction bytes at pc: "); + if (IsAccessibleMemoryRange(pc, 16)) { + for (int i = 0; i < 16; ++i) { + PrintMemoryByte(&str, "", ((u8 *)pc)[i], /*in_shadow*/ false, " "); + } + str.append("\n"); + } else { + str.append("unaccessible\n"); + } + Report("%s", str.data()); +} + +void ErrorDeadlySignal::Print() { + Decorator d; + Printf("%s", d.Warning()); + const char *description = DescribeSignalOrException(signo); + Report( + "ERROR: AddressSanitizer: %s on unknown address %p (pc %p bp %p sp %p " + "T%d)\n", + description, (void *)addr, (void *)pc, (void *)bp, (void *)sp, tid); + Printf("%s", d.EndWarning()); + if (pc < GetPageSizeCached()) Report("Hint: pc points to the zero page.\n"); + if (is_memory_access) { + const char *access_type = + write_flag == SignalContext::WRITE + ? "WRITE" + : (write_flag == SignalContext::READ ? "READ" : "UNKNOWN"); + Report("The signal is caused by a %s memory access.\n", access_type); + if (addr < GetPageSizeCached()) + Report("Hint: address points to the zero page.\n"); + } + scariness.Print(); + BufferedStackTrace stack; + GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, context, + common_flags()->fast_unwind_on_fatal); + stack.Print(); + MaybeDumpInstructionBytes(pc); + Printf("AddressSanitizer can not provide additional info.\n"); + ReportErrorSummary(description, &stack); +} + +void ErrorDoubleFree::Print() { + Decorator d; + Printf("%s", d.Warning()); + char tname[128]; + Report( + "ERROR: AddressSanitizer: attempting double-free on %p in " + "thread T%d%s:\n", + addr_description.addr, tid, + ThreadNameWithParenthesis(tid, tname, sizeof(tname))); + Printf("%s", d.EndWarning()); + scariness.Print(); + GET_STACK_TRACE_FATAL(second_free_stack->trace[0], + second_free_stack->top_frame_bp); + stack.Print(); + addr_description.Print(); + ReportErrorSummary("double-free", &stack); +} + +void ErrorNewDeleteSizeMismatch::Print() { + Decorator d; + Printf("%s", d.Warning()); + char tname[128]; + Report( + "ERROR: AddressSanitizer: new-delete-type-mismatch on %p in thread " + "T%d%s:\n", + addr_description.addr, tid, + ThreadNameWithParenthesis(tid, tname, sizeof(tname))); + Printf("%s object passed to delete has wrong type:\n", d.EndWarning()); + Printf( + " size of the allocated type: %zd bytes;\n" + " size of the deallocated type: %zd bytes.\n", + addr_description.chunk_access.chunk_size, delete_size); + CHECK_GT(free_stack->size, 0); + scariness.Print(); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + stack.Print(); + addr_description.Print(); + ReportErrorSummary("new-delete-type-mismatch", &stack); + Report( + "HINT: if you don't care about these errors you may set " + "ASAN_OPTIONS=new_delete_type_mismatch=0\n"); +} + +void ErrorFreeNotMalloced::Print() { + Decorator d; + Printf("%s", d.Warning()); + char tname[128]; + Report( + "ERROR: AddressSanitizer: attempting free on address " + "which was not malloc()-ed: %p in thread T%d%s\n", + addr_description.Address(), tid, + ThreadNameWithParenthesis(tid, tname, sizeof(tname))); + Printf("%s", d.EndWarning()); + CHECK_GT(free_stack->size, 0); + scariness.Print(); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + stack.Print(); + addr_description.Print(); + ReportErrorSummary("bad-free", &stack); +} + +void ErrorAllocTypeMismatch::Print() { + static const char *alloc_names[] = {"INVALID", "malloc", "operator new", + "operator new []"}; + static const char *dealloc_names[] = {"INVALID", "free", "operator delete", + "operator delete []"}; + CHECK_NE(alloc_type, dealloc_type); + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n", + alloc_names[alloc_type], dealloc_names[dealloc_type], + addr_description.addr); + Printf("%s", d.EndWarning()); + CHECK_GT(dealloc_stack->size, 0); + scariness.Print(); + GET_STACK_TRACE_FATAL(dealloc_stack->trace[0], dealloc_stack->top_frame_bp); + stack.Print(); + addr_description.Print(); + ReportErrorSummary("alloc-dealloc-mismatch", &stack); + Report( + "HINT: if you don't care about these errors you may set " + "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); +} + +void ErrorMallocUsableSizeNotOwned::Print() { + Decorator d; + Printf("%s", d.Warning()); + Report( + "ERROR: AddressSanitizer: attempting to call malloc_usable_size() for " + "pointer which is not owned: %p\n", + addr_description.Address()); + Printf("%s", d.EndWarning()); + stack->Print(); + addr_description.Print(); + ReportErrorSummary("bad-malloc_usable_size", stack); +} + +void ErrorSanitizerGetAllocatedSizeNotOwned::Print() { + Decorator d; + Printf("%s", d.Warning()); + Report( + "ERROR: AddressSanitizer: attempting to call " + "__sanitizer_get_allocated_size() for pointer which is not owned: %p\n", + addr_description.Address()); + Printf("%s", d.EndWarning()); + stack->Print(); + addr_description.Print(); + ReportErrorSummary("bad-__sanitizer_get_allocated_size", stack); +} + +void ErrorStringFunctionMemoryRangesOverlap::Print() { + Decorator d; + char bug_type[100]; + internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function); + Printf("%s", d.Warning()); + Report( + "ERROR: AddressSanitizer: %s: memory ranges [%p,%p) and [%p, %p) " + "overlap\n", + bug_type, addr1_description.Address(), + addr1_description.Address() + length1, addr2_description.Address(), + addr2_description.Address() + length2); + Printf("%s", d.EndWarning()); + scariness.Print(); + stack->Print(); + addr1_description.Print(); + addr2_description.Print(); + ReportErrorSummary(bug_type, stack); +} + +void ErrorStringFunctionSizeOverflow::Print() { + Decorator d; + Printf("%s", d.Warning()); + const char *bug_type = "negative-size-param"; + Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", bug_type, size); + Printf("%s", d.EndWarning()); + scariness.Print(); + stack->Print(); + addr_description.Print(); + ReportErrorSummary(bug_type, stack); +} + +void ErrorBadParamsToAnnotateContiguousContainer::Print() { + Report( + "ERROR: AddressSanitizer: bad parameters to " + "__sanitizer_annotate_contiguous_container:\n" + " beg : %p\n" + " end : %p\n" + " old_mid : %p\n" + " new_mid : %p\n", + beg, end, old_mid, new_mid); + uptr granularity = SHADOW_GRANULARITY; + if (!IsAligned(beg, granularity)) + Report("ERROR: beg is not aligned by %d\n", granularity); + stack->Print(); + ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack); +} + +void ErrorODRViolation::Print() { + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: odr-violation (%p):\n", global1.beg); + Printf("%s", d.EndWarning()); + InternalScopedString g1_loc(256), g2_loc(256); + PrintGlobalLocation(&g1_loc, global1); + PrintGlobalLocation(&g2_loc, global2); + Printf(" [1] size=%zd '%s' %s\n", global1.size, + MaybeDemangleGlobalName(global1.name), g1_loc.data()); + Printf(" [2] size=%zd '%s' %s\n", global2.size, + MaybeDemangleGlobalName(global2.name), g2_loc.data()); + if (stack_id1 && stack_id2) { + Printf("These globals were registered at these points:\n"); + Printf(" [1]:\n"); + StackDepotGet(stack_id1).Print(); + Printf(" [2]:\n"); + StackDepotGet(stack_id2).Print(); + } + Report( + "HINT: if you don't care about these errors you may set " + "ASAN_OPTIONS=detect_odr_violation=0\n"); + InternalScopedString error_msg(256); + error_msg.append("odr-violation: global '%s' at %s", + MaybeDemangleGlobalName(global1.name), g1_loc.data()); + ReportErrorSummary(error_msg.data()); +} + +void ErrorInvalidPointerPair::Print() { + const char *bug_type = "invalid-pointer-pair"; + Decorator d; + Printf("%s", d.Warning()); + Report("ERROR: AddressSanitizer: invalid-pointer-pair: %p %p\n", + addr1_description.Address(), addr2_description.Address()); + Printf("%s", d.EndWarning()); + GET_STACK_TRACE_FATAL(pc, bp); + stack.Print(); + addr1_description.Print(); + addr2_description.Print(); + ReportErrorSummary(bug_type, &stack); +} + +static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) { + return s[-1] > 127 && s[1] > 127; +} + +ErrorGeneric::ErrorGeneric(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr addr, + bool is_write_, uptr access_size_) + : ErrorBase(tid), + addr_description(addr, access_size_, /*shouldLockThreadRegistry=*/false), + pc(pc_), + bp(bp_), + sp(sp_), + access_size(access_size_), + is_write(is_write_), + shadow_val(0) { + scariness.Clear(); + if (access_size) { + if (access_size <= 9) { + char desr[] = "?-byte"; + desr[0] = '0' + access_size; + scariness.Scare(access_size + access_size / 2, desr); + } else if (access_size >= 10) { + scariness.Scare(15, "multi-byte"); + } + is_write ? scariness.Scare(20, "write") : scariness.Scare(1, "read"); + + // Determine the error type. + bug_descr = "unknown-crash"; + if (AddrIsInMem(addr)) { + u8 *shadow_addr = (u8 *)MemToShadow(addr); + // If we are accessing 16 bytes, look at the second shadow byte. + if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY) shadow_addr++; + // If we are in the partial right redzone, look at the next shadow byte. + if (*shadow_addr > 0 && *shadow_addr < 128) shadow_addr++; + bool far_from_bounds = false; + shadow_val = *shadow_addr; + int bug_type_score = 0; + // For use-after-frees reads are almost as bad as writes. + int read_after_free_bonus = 0; + switch (shadow_val) { + case kAsanHeapLeftRedzoneMagic: + case kAsanArrayCookieMagic: + bug_descr = "heap-buffer-overflow"; + bug_type_score = 10; + far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); + break; + case kAsanHeapFreeMagic: + bug_descr = "heap-use-after-free"; + bug_type_score = 20; + if (!is_write) read_after_free_bonus = 18; + break; + case kAsanStackLeftRedzoneMagic: + bug_descr = "stack-buffer-underflow"; + bug_type_score = 25; + far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); + break; + case kAsanInitializationOrderMagic: + bug_descr = "initialization-order-fiasco"; + bug_type_score = 1; + break; + case kAsanStackMidRedzoneMagic: + case kAsanStackRightRedzoneMagic: + bug_descr = "stack-buffer-overflow"; + bug_type_score = 25; + far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); + break; + case kAsanStackAfterReturnMagic: + bug_descr = "stack-use-after-return"; + bug_type_score = 30; + if (!is_write) read_after_free_bonus = 18; + break; + case kAsanUserPoisonedMemoryMagic: + bug_descr = "use-after-poison"; + bug_type_score = 20; + break; + case kAsanContiguousContainerOOBMagic: + bug_descr = "container-overflow"; + bug_type_score = 10; + break; + case kAsanStackUseAfterScopeMagic: + bug_descr = "stack-use-after-scope"; + bug_type_score = 10; + break; + case kAsanGlobalRedzoneMagic: + bug_descr = "global-buffer-overflow"; + bug_type_score = 10; + far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); + break; + case kAsanIntraObjectRedzone: + bug_descr = "intra-object-overflow"; + bug_type_score = 10; + break; + case kAsanAllocaLeftMagic: + case kAsanAllocaRightMagic: + bug_descr = "dynamic-stack-buffer-overflow"; + bug_type_score = 25; + far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr); + break; + } + scariness.Scare(bug_type_score + read_after_free_bonus, bug_descr); + if (far_from_bounds) scariness.Scare(10, "far-from-bounds"); + } + } +} + +static void PrintContainerOverflowHint() { + Printf("HINT: if you don't care about these errors you may set " + "ASAN_OPTIONS=detect_container_overflow=0.\n" + "If you suspect a false positive see also: " + "https://github.com/google/sanitizers/wiki/" + "AddressSanitizerContainerOverflow.\n"); +} + +static void PrintShadowByte(InternalScopedString *str, const char *before, + u8 byte, const char *after = "\n") { + PrintMemoryByte(str, before, byte, /*in_shadow*/true, after); +} + +static void PrintLegend(InternalScopedString *str) { + str->append( + "Shadow byte legend (one shadow byte represents %d " + "application bytes):\n", + (int)SHADOW_GRANULARITY); + PrintShadowByte(str, " Addressable: ", 0); + str->append(" Partially addressable: "); + for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " "); + str->append("\n"); + PrintShadowByte(str, " Heap left redzone: ", + kAsanHeapLeftRedzoneMagic); + PrintShadowByte(str, " Freed heap region: ", kAsanHeapFreeMagic); + PrintShadowByte(str, " Stack left redzone: ", + kAsanStackLeftRedzoneMagic); + PrintShadowByte(str, " Stack mid redzone: ", + kAsanStackMidRedzoneMagic); + PrintShadowByte(str, " Stack right redzone: ", + kAsanStackRightRedzoneMagic); + PrintShadowByte(str, " Stack after return: ", + kAsanStackAfterReturnMagic); + PrintShadowByte(str, " Stack use after scope: ", + kAsanStackUseAfterScopeMagic); + PrintShadowByte(str, " Global redzone: ", kAsanGlobalRedzoneMagic); + PrintShadowByte(str, " Global init order: ", + kAsanInitializationOrderMagic); + PrintShadowByte(str, " Poisoned by user: ", + kAsanUserPoisonedMemoryMagic); + PrintShadowByte(str, " Container overflow: ", + kAsanContiguousContainerOOBMagic); + PrintShadowByte(str, " Array cookie: ", + kAsanArrayCookieMagic); + PrintShadowByte(str, " Intra object redzone: ", + kAsanIntraObjectRedzone); + PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic); + PrintShadowByte(str, " Left alloca redzone: ", kAsanAllocaLeftMagic); + PrintShadowByte(str, " Right alloca redzone: ", kAsanAllocaRightMagic); +} + +static void PrintShadowBytes(InternalScopedString *str, const char *before, + u8 *bytes, u8 *guilty, uptr n) { + Decorator d; + if (before) str->append("%s%p:", before, bytes); + for (uptr i = 0; i < n; i++) { + u8 *p = bytes + i; + const char *before = + p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " "; + const char *after = p == guilty ? "]" : ""; + PrintShadowByte(str, before, *p, after); + } + str->append("\n"); +} + +static void PrintShadowMemoryForAddress(uptr addr) { + if (!AddrIsInMem(addr)) return; + uptr shadow_addr = MemToShadow(addr); + const uptr n_bytes_per_row = 16; + uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1); + InternalScopedString str(4096 * 8); + str.append("Shadow bytes around the buggy address:\n"); + for (int i = -5; i <= 5; i++) { + const char *prefix = (i == 0) ? "=>" : " "; + PrintShadowBytes(&str, prefix, (u8 *)(aligned_shadow + i * n_bytes_per_row), + (u8 *)shadow_addr, n_bytes_per_row); + } + if (flags()->print_legend) PrintLegend(&str); + Printf("%s", str.data()); +} + +void ErrorGeneric::Print() { + Decorator d; + Printf("%s", d.Warning()); + uptr addr = addr_description.Address(); + Report("ERROR: AddressSanitizer: %s on address %p at pc %p bp %p sp %p\n", + bug_descr, (void *)addr, pc, bp, sp); + Printf("%s", d.EndWarning()); + + char tname[128]; + Printf("%s%s of size %zu at %p thread T%d%s%s\n", d.Access(), + access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", access_size, + (void *)addr, tid, + ThreadNameWithParenthesis(tid, tname, sizeof(tname)), d.EndAccess()); + + scariness.Print(); + GET_STACK_TRACE_FATAL(pc, bp); + stack.Print(); + + // Pass bug_descr because we have a special case for + // initialization-order-fiasco + addr_description.Print(bug_descr); + if (shadow_val == kAsanContiguousContainerOOBMagic) + PrintContainerOverflowHint(); + ReportErrorSummary(bug_descr, &stack); + PrintShadowMemoryForAddress(addr); +} + +} // namespace __asan diff --git a/libsanitizer/asan/asan_errors.h b/libsanitizer/asan/asan_errors.h new file mode 100644 index 0000000..6262dcf --- /dev/null +++ b/libsanitizer/asan/asan_errors.h @@ -0,0 +1,376 @@ +//===-- asan_errors.h -------------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// ASan-private header for error structures. +//===----------------------------------------------------------------------===// +#ifndef ASAN_ERRORS_H +#define ASAN_ERRORS_H + +#include "asan_descriptions.h" +#include "asan_scariness_score.h" +#include "sanitizer_common/sanitizer_common.h" + +namespace __asan { + +struct ErrorBase { + ErrorBase() = default; + explicit ErrorBase(u32 tid_) : tid(tid_) {} + ScarinessScoreBase scariness; + u32 tid; +}; + +struct ErrorStackOverflow : ErrorBase { + uptr addr, pc, bp, sp; + // ErrorStackOverflow never owns the context. + void *context; + // VS2013 doesn't implement unrestricted unions, so we need a trivial default + // constructor + ErrorStackOverflow() = default; + ErrorStackOverflow(u32 tid, const SignalContext &sig) + : ErrorBase(tid), + addr(sig.addr), + pc(sig.pc), + bp(sig.bp), + sp(sig.sp), + context(sig.context) { + scariness.Clear(); + scariness.Scare(10, "stack-overflow"); + } + void Print(); +}; + +struct ErrorDeadlySignal : ErrorBase { + uptr addr, pc, bp, sp; + // ErrorDeadlySignal never owns the context. + void *context; + int signo; + SignalContext::WriteFlag write_flag; + bool is_memory_access; + // VS2013 doesn't implement unrestricted unions, so we need a trivial default + // constructor + ErrorDeadlySignal() = default; + ErrorDeadlySignal(u32 tid, const SignalContext &sig, int signo_) + : ErrorBase(tid), + addr(sig.addr), + pc(sig.pc), + bp(sig.bp), + sp(sig.sp), + context(sig.context), + signo(signo_), + write_flag(sig.write_flag), + is_memory_access(sig.is_memory_access) { + scariness.Clear(); + if (is_memory_access) { + if (addr < GetPageSizeCached()) { + scariness.Scare(10, "null-deref"); + } else if (addr == pc) { + scariness.Scare(60, "wild-jump"); + } else if (write_flag == SignalContext::WRITE) { + scariness.Scare(30, "wild-addr-write"); + } else if (write_flag == SignalContext::READ) { + scariness.Scare(20, "wild-addr-read"); + } else { + scariness.Scare(25, "wild-addr"); + } + } else { + scariness.Scare(10, "signal"); + } + } + void Print(); +}; + +struct ErrorDoubleFree : ErrorBase { + // ErrorDoubleFree doesn't own the stack trace. + const BufferedStackTrace *second_free_stack; + HeapAddressDescription addr_description; + // VS2013 doesn't implement unrestricted unions, so we need a trivial default + // constructor + ErrorDoubleFree() = default; + ErrorDoubleFree(u32 tid, BufferedStackTrace *stack, uptr addr) + : ErrorBase(tid), second_free_stack(stack) { + CHECK_GT(second_free_stack->size, 0); + GetHeapAddressInformation(addr, 1, &addr_description); + scariness.Clear(); + scariness.Scare(42, "double-free"); + } + void Print(); +}; + +struct ErrorNewDeleteSizeMismatch : ErrorBase { + // ErrorNewDeleteSizeMismatch doesn't own the stack trace. + const BufferedStackTrace *free_stack; + HeapAddressDescription addr_description; + uptr delete_size; + // VS2013 doesn't implement unrestricted unions, so we need a trivial default + // constructor + ErrorNewDeleteSizeMismatch() = default; + ErrorNewDeleteSizeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr, + uptr delete_size_) + : ErrorBase(tid), free_stack(stack), delete_size(delete_size_) { + GetHeapAddressInformation(addr, 1, &addr_description); + scariness.Clear(); + scariness.Scare(10, "new-delete-type-mismatch"); + } + void Print(); +}; + +struct ErrorFreeNotMalloced : ErrorBase { + // ErrorFreeNotMalloced doesn't own the stack trace. + const BufferedStackTrace *free_stack; + AddressDescription addr_description; + // VS2013 doesn't implement unrestricted unions, so we need a trivial default + // constructor + ErrorFreeNotMalloced() = default; + ErrorFreeNotMalloced(u32 tid, BufferedStackTrace *stack, uptr addr) + : ErrorBase(tid), + free_stack(stack), + addr_description(addr, /*shouldLockThreadRegistry=*/false) { + scariness.Clear(); + scariness.Scare(40, "bad-free"); + } + void Print(); +}; + +struct ErrorAllocTypeMismatch : ErrorBase { + // ErrorAllocTypeMismatch doesn't own the stack trace. + const BufferedStackTrace *dealloc_stack; + HeapAddressDescription addr_description; + AllocType alloc_type, dealloc_type; + // VS2013 doesn't implement unrestricted unions, so we need a trivial default + // constructor + ErrorAllocTypeMismatch() = default; + ErrorAllocTypeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr, + AllocType alloc_type_, AllocType dealloc_type_) + : ErrorBase(tid), + dealloc_stack(stack), + alloc_type(alloc_type_), + dealloc_type(dealloc_type_) { + GetHeapAddressInformation(addr, 1, &addr_description); + scariness.Clear(); + scariness.Scare(10, "alloc-dealloc-mismatch"); + }; + void Print(); +}; + +struct ErrorMallocUsableSizeNotOwned : ErrorBase { + // ErrorMallocUsableSizeNotOwned doesn't own the stack trace. + const BufferedStackTrace *stack; + AddressDescription addr_description; + // VS2013 doesn't implement unrestricted unions, so we need a trivial default + // constructor + ErrorMallocUsableSizeNotOwned() = default; + ErrorMallocUsableSizeNotOwned(u32 tid, BufferedStackTrace *stack_, uptr addr) + : ErrorBase(tid), + stack(stack_), + addr_description(addr, /*shouldLockThreadRegistry=*/false) { + scariness.Clear(); + } + void Print(); +}; + +struct ErrorSanitizerGetAllocatedSizeNotOwned : ErrorBase { + // ErrorSanitizerGetAllocatedSizeNotOwned doesn't own the stack trace. + const BufferedStackTrace *stack; + AddressDescription addr_description; + // VS2013 doesn't implement unrestricted unions, so we need a trivial default + // constructor + ErrorSanitizerGetAllocatedSizeNotOwned() = default; + ErrorSanitizerGetAllocatedSizeNotOwned(u32 tid, BufferedStackTrace *stack_, + uptr addr) + : ErrorBase(tid), + stack(stack_), + addr_description(addr, /*shouldLockThreadRegistry=*/false) { + scariness.Clear(); + } + void Print(); +}; + +struct ErrorStringFunctionMemoryRangesOverlap : ErrorBase { + // ErrorStringFunctionMemoryRangesOverlap doesn't own the stack trace. + const BufferedStackTrace *stack; + uptr length1, length2; + AddressDescription addr1_description; + AddressDescription addr2_description; + const char *function; + // VS2013 doesn't implement unrestricted unions, so we need a trivial default + // constructor + ErrorStringFunctionMemoryRangesOverlap() = default; + ErrorStringFunctionMemoryRangesOverlap(u32 tid, BufferedStackTrace *stack_, + uptr addr1, uptr length1_, uptr addr2, + uptr length2_, const char *function_) + : ErrorBase(tid), + stack(stack_), + length1(length1_), + length2(length2_), + addr1_description(addr1, length1, /*shouldLockThreadRegistry=*/false), + addr2_description(addr2, length2, /*shouldLockThreadRegistry=*/false), + function(function_) { + char bug_type[100]; + internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function); + scariness.Clear(); + scariness.Scare(10, bug_type); + } + void Print(); +}; + +struct ErrorStringFunctionSizeOverflow : ErrorBase { + // ErrorStringFunctionSizeOverflow doesn't own the stack trace. + const BufferedStackTrace *stack; + AddressDescription addr_description; + uptr size; + // VS2013 doesn't implement unrestricted unions, so we need a trivial default + // constructor + ErrorStringFunctionSizeOverflow() = default; + ErrorStringFunctionSizeOverflow(u32 tid, BufferedStackTrace *stack_, + uptr addr, uptr size_) + : ErrorBase(tid), + stack(stack_), + addr_description(addr, /*shouldLockThreadRegistry=*/false), + size(size_) { + scariness.Clear(); + scariness.Scare(10, "negative-size-param"); + } + void Print(); +}; + +struct ErrorBadParamsToAnnotateContiguousContainer : ErrorBase { + // ErrorBadParamsToAnnotateContiguousContainer doesn't own the stack trace. + const BufferedStackTrace *stack; + uptr beg, end, old_mid, new_mid; + // VS2013 doesn't implement unrestricted unions, so we need a trivial default + // constructor + ErrorBadParamsToAnnotateContiguousContainer() = default; + // PS4: Do we want an AddressDescription for beg? + ErrorBadParamsToAnnotateContiguousContainer(u32 tid, + BufferedStackTrace *stack_, + uptr beg_, uptr end_, + uptr old_mid_, uptr new_mid_) + : ErrorBase(tid), + stack(stack_), + beg(beg_), + end(end_), + old_mid(old_mid_), + new_mid(new_mid_) {} + void Print(); +}; + +struct ErrorODRViolation : ErrorBase { + __asan_global global1, global2; + u32 stack_id1, stack_id2; + // VS2013 doesn't implement unrestricted unions, so we need a trivial default + // constructor + ErrorODRViolation() = default; + ErrorODRViolation(u32 tid, const __asan_global *g1, u32 stack_id1_, + const __asan_global *g2, u32 stack_id2_) + : ErrorBase(tid), + global1(*g1), + global2(*g2), + stack_id1(stack_id1_), + stack_id2(stack_id2_) {} + void Print(); +}; + +struct ErrorInvalidPointerPair : ErrorBase { + uptr pc, bp, sp; + AddressDescription addr1_description; + AddressDescription addr2_description; + // VS2013 doesn't implement unrestricted unions, so we need a trivial default + // constructor + ErrorInvalidPointerPair() = default; + ErrorInvalidPointerPair(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr p1, + uptr p2) + : ErrorBase(tid), + pc(pc_), + bp(bp_), + sp(sp_), + addr1_description(p1, 1, /*shouldLockThreadRegistry=*/false), + addr2_description(p2, 1, /*shouldLockThreadRegistry=*/false) {} + void Print(); +}; + +struct ErrorGeneric : ErrorBase { + AddressDescription addr_description; + uptr pc, bp, sp; + uptr access_size; + const char *bug_descr; + bool is_write; + u8 shadow_val; + // VS2013 doesn't implement unrestricted unions, so we need a trivial default + // constructor + ErrorGeneric() = default; + ErrorGeneric(u32 tid, uptr addr, uptr pc_, uptr bp_, uptr sp_, bool is_write_, + uptr access_size_); + void Print(); +}; + +// clang-format off +#define ASAN_FOR_EACH_ERROR_KIND(macro) \ + macro(StackOverflow) \ + macro(DeadlySignal) \ + macro(DoubleFree) \ + macro(NewDeleteSizeMismatch) \ + macro(FreeNotMalloced) \ + macro(AllocTypeMismatch) \ + macro(MallocUsableSizeNotOwned) \ + macro(SanitizerGetAllocatedSizeNotOwned) \ + macro(StringFunctionMemoryRangesOverlap) \ + macro(StringFunctionSizeOverflow) \ + macro(BadParamsToAnnotateContiguousContainer) \ + macro(ODRViolation) \ + macro(InvalidPointerPair) \ + macro(Generic) +// clang-format on + +#define ASAN_DEFINE_ERROR_KIND(name) kErrorKind##name, +#define ASAN_ERROR_DESCRIPTION_MEMBER(name) Error##name name; +#define ASAN_ERROR_DESCRIPTION_CONSTRUCTOR(name) \ + ErrorDescription(Error##name const &e) : kind(kErrorKind##name), name(e) {} +#define ASAN_ERROR_DESCRIPTION_PRINT(name) \ + case kErrorKind##name: \ + return name.Print(); + +enum ErrorKind { + kErrorKindInvalid = 0, + ASAN_FOR_EACH_ERROR_KIND(ASAN_DEFINE_ERROR_KIND) +}; + +struct ErrorDescription { + ErrorKind kind; + // We're using a tagged union because it allows us to have a trivially + // copiable type and use the same structures as the public interface. + // + // We can add a wrapper around it to make it "more c++-like", but that would + // add a lot of code and the benefit wouldn't be that big. + union { + ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_MEMBER) + }; + + ErrorDescription() { internal_memset(this, 0, sizeof(*this)); } + ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_CONSTRUCTOR) + + bool IsValid() { return kind != kErrorKindInvalid; } + void Print() { + switch (kind) { + ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_PRINT) + case kErrorKindInvalid: + CHECK(0); + } + CHECK(0); + } +}; + +#undef ASAN_FOR_EACH_ERROR_KIND +#undef ASAN_DEFINE_ERROR_KIND +#undef ASAN_ERROR_DESCRIPTION_MEMBER +#undef ASAN_ERROR_DESCRIPTION_CONSTRUCTOR +#undef ASAN_ERROR_DESCRIPTION_PRINT + +} // namespace __asan + +#endif // ASAN_ERRORS_H diff --git a/libsanitizer/asan/asan_fake_stack.cc b/libsanitizer/asan/asan_fake_stack.cc index de190d1..bf7566a 100644 --- a/libsanitizer/asan/asan_fake_stack.cc +++ b/libsanitizer/asan/asan_fake_stack.cc @@ -29,7 +29,7 @@ ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) { CHECK_EQ(SHADOW_SCALE, 3); // This code expects SHADOW_SCALE=3. u64 *shadow = reinterpret_cast(MemToShadow(ptr)); if (class_id <= 6) { - for (uptr i = 0; i < (1U << class_id); i++) { + for (uptr i = 0; i < (((uptr)1) << class_id); i++) { shadow[i] = magic; // Make sure this does not become memset. SanitizerBreakOptimization(nullptr); @@ -98,7 +98,7 @@ FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id, // if the signal arrives between checking and setting flags[pos], the // signal handler's fake stack will start from a different hint_position // and so will not touch this particular byte. So, it is safe to do this - // with regular non-atimic load and store (at least I was not able to make + // with regular non-atomic load and store (at least I was not able to make // this code crash). if (flags[pos]) continue; flags[pos] = 1; @@ -119,7 +119,7 @@ uptr FakeStack::AddrIsInFakeStack(uptr ptr, uptr *frame_beg, uptr *frame_end) { uptr class_id = (ptr - beg) >> stack_size_log; uptr base = beg + (class_id << stack_size_log); CHECK_LE(base, ptr); - CHECK_LT(ptr, base + (1UL << stack_size_log)); + CHECK_LT(ptr, base + (((uptr)1) << stack_size_log)); uptr pos = (ptr - base) >> (kMinStackFrameSizeLog + class_id); uptr res = base + pos * BytesInSizeClass(class_id); *frame_end = res + BytesInSizeClass(class_id); diff --git a/libsanitizer/asan/asan_fake_stack.h b/libsanitizer/asan/asan_fake_stack.h index 550a86e..6ac61dd 100644 --- a/libsanitizer/asan/asan_fake_stack.h +++ b/libsanitizer/asan/asan_fake_stack.h @@ -50,7 +50,7 @@ struct FakeFrame { // Allocate() flips the appropriate allocation flag atomically, thus achieving // async-signal safety. // This allocator does not have quarantine per se, but it tries to allocate the -// frames in round robin fasion to maximize the delay between a deallocation +// frames in round robin fashion to maximize the delay between a deallocation // and the next allocation. class FakeStack { static const uptr kMinStackFrameSizeLog = 6; // Min frame is 64B. @@ -67,12 +67,12 @@ class FakeStack { // stack_size_log is at least 15 (stack_size >= 32K). static uptr SizeRequiredForFlags(uptr stack_size_log) { - return 1UL << (stack_size_log + 1 - kMinStackFrameSizeLog); + return ((uptr)1) << (stack_size_log + 1 - kMinStackFrameSizeLog); } // Each size class occupies stack_size bytes. static uptr SizeRequiredForFrames(uptr stack_size_log) { - return (1ULL << stack_size_log) * kNumberOfSizeClasses; + return (((uptr)1) << stack_size_log) * kNumberOfSizeClasses; } // Number of bytes requires for the whole object. @@ -89,20 +89,20 @@ class FakeStack { // and so on. static uptr FlagsOffset(uptr stack_size_log, uptr class_id) { uptr t = kNumberOfSizeClasses - 1 - class_id; - const uptr all_ones = (1 << (kNumberOfSizeClasses - 1)) - 1; + const uptr all_ones = (((uptr)1) << (kNumberOfSizeClasses - 1)) - 1; return ((all_ones >> t) << t) << (stack_size_log - 15); } static uptr NumberOfFrames(uptr stack_size_log, uptr class_id) { - return 1UL << (stack_size_log - kMinStackFrameSizeLog - class_id); + return ((uptr)1) << (stack_size_log - kMinStackFrameSizeLog - class_id); } - // Divide n by the numbe of frames in size class. + // Divide n by the number of frames in size class. static uptr ModuloNumberOfFrames(uptr stack_size_log, uptr class_id, uptr n) { return n & (NumberOfFrames(stack_size_log, class_id) - 1); } - // The the pointer to the flags of the given class_id. + // The pointer to the flags of the given class_id. u8 *GetFlags(uptr stack_size_log, uptr class_id) { return reinterpret_cast(this) + kFlagsOffset + FlagsOffset(stack_size_log, class_id); @@ -112,7 +112,8 @@ class FakeStack { u8 *GetFrame(uptr stack_size_log, uptr class_id, uptr pos) { return reinterpret_cast(this) + kFlagsOffset + SizeRequiredForFlags(stack_size_log) + - (1 << stack_size_log) * class_id + BytesInSizeClass(class_id) * pos; + (((uptr)1) << stack_size_log) * class_id + + BytesInSizeClass(class_id) * pos; } // Allocate the fake frame. @@ -135,7 +136,7 @@ class FakeStack { // Number of bytes in a fake frame of this size class. static uptr BytesInSizeClass(uptr class_id) { - return 1UL << (class_id + kMinStackFrameSizeLog); + return ((uptr)1) << (class_id + kMinStackFrameSizeLog); } // The fake frame is guaranteed to have a right redzone. @@ -157,7 +158,7 @@ class FakeStack { static const uptr kFlagsOffset = 4096; // This is were the flags begin. // Must match the number of uses of DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID COMPILER_CHECK(kNumberOfSizeClasses == 11); - static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog; + static const uptr kMaxStackMallocSize = ((uptr)1) << kMaxStackFrameSizeLog; uptr hint_position_[kNumberOfSizeClasses]; uptr stack_size_log_; diff --git a/libsanitizer/asan/asan_flags.cc b/libsanitizer/asan/asan_flags.cc index 93ac1d5..39473bb 100644 --- a/libsanitizer/asan/asan_flags.cc +++ b/libsanitizer/asan/asan_flags.cc @@ -117,15 +117,7 @@ void InitializeFlags() { ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS")); #endif - // Let activation flags override current settings. On Android they come - // from a system property. On other platforms this is no-op. - if (!flags()->start_deactivated) { - char buf[100]; - GetExtraActivationFlags(buf, sizeof(buf)); - asan_parser.ParseString(buf); - } - - SetVerbosity(common_flags()->verbosity); + InitializeCommonFlags(); // TODO(eugenis): dump all flags at verbosity>=2? if (Verbosity()) ReportUnrecognizedFlags(); @@ -168,9 +160,14 @@ void InitializeFlags() { (ASAN_LOW_MEMORY) ? 1UL << 6 : 1UL << 8; f->quarantine_size_mb = kDefaultQuarantineSizeMb; } - - if (mmaped) - UnmapOrDie((void *)options, GetPageSizeCached()); + if (!f->replace_str && common_flags()->intercept_strlen) { + Report("WARNING: strlen interceptor is enabled even though replace_str=0. " + "Use intercept_strlen=0 to disable it."); + } + if (!f->replace_str && common_flags()->intercept_strchr) { + Report("WARNING: strchr* interceptors are enabled even though " + "replace_str=0. Use intercept_strchr=0 to disable them."); + } } } // namespace __asan diff --git a/libsanitizer/asan/asan_flags.inc b/libsanitizer/asan/asan_flags.inc index c94e22f..a40f217 100644 --- a/libsanitizer/asan/asan_flags.inc +++ b/libsanitizer/asan/asan_flags.inc @@ -41,10 +41,7 @@ ASAN_FLAG( "If set, uses custom wrappers and replacements for libc string functions " "to find more errors.") ASAN_FLAG(bool, replace_intrin, true, - "If set, uses custom wrappers for memset/memcpy/memmove intinsics.") -ASAN_FLAG(bool, mac_ignore_invalid_free, false, - "Ignore invalid free() calls to work around some bugs. Used on OS X " - "only.") + "If set, uses custom wrappers for memset/memcpy/memmove intrinsics.") ASAN_FLAG(bool, detect_stack_use_after_return, false, "Enables stack-use-after-return checking at run-time.") ASAN_FLAG(int, min_uar_stack_size_log, 16, // We can't do smaller anyway. @@ -78,6 +75,8 @@ ASAN_FLAG(bool, print_stats, false, "Print various statistics after printing an error message or if " "atexit=1.") ASAN_FLAG(bool, print_legend, true, "Print the legend for the shadow bytes.") +ASAN_FLAG(bool, print_scariness, false, + "Print the scariness score. Experimental.") ASAN_FLAG(bool, atexit, false, "If set, prints ASan exit stats even after program terminates " "successfully.") @@ -97,15 +96,15 @@ ASAN_FLAG(bool, poison_array_cookie, true, "Poison (or not) the array cookie after operator new[].") // Turn off alloc/dealloc mismatch checker on Mac and Windows for now. -// https://code.google.com/p/address-sanitizer/issues/detail?id=131 -// https://code.google.com/p/address-sanitizer/issues/detail?id=309 +// https://github.com/google/sanitizers/issues/131 +// https://github.com/google/sanitizers/issues/309 // TODO(glider,timurrrr): Fix known issues and enable this back. ASAN_FLAG(bool, alloc_dealloc_mismatch, - (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0), + !SANITIZER_MAC && !SANITIZER_WINDOWS && !SANITIZER_ANDROID, "Report errors on malloc/delete, new/free, new/delete[], etc.") ASAN_FLAG(bool, new_delete_type_mismatch, true, - "Report errors on mismatch betwen size of new and delete.") + "Report errors on mismatch between size of new and delete.") ASAN_FLAG( bool, strict_init_order, false, "If true, assume that dynamic initializers can never access globals from " @@ -124,8 +123,8 @@ ASAN_FLAG( "The bigger the value the harder we try.") ASAN_FLAG( bool, detect_container_overflow, true, - "If true, honor the container overflow annotations. " - "See https://code.google.com/p/address-sanitizer/wiki/ContainerOverflow") + "If true, honor the container overflow annotations. See " + "https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow") ASAN_FLAG(int, detect_odr_violation, 2, "If >=2, detect violation of One-Definition-Rule (ODR); " "If ==1, detect ODR-violation only if the two variables " @@ -136,3 +135,5 @@ ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.") ASAN_FLAG(bool, halt_on_error, false, "Crash the program after printing the first error report " "(WARNING: USE AT YOUR OWN RISK!)") +ASAN_FLAG(bool, use_odr_indicator, false, + "Use special ODR indicator symbol for ODR violation detection") diff --git a/libsanitizer/asan/asan_globals.cc b/libsanitizer/asan/asan_globals.cc index f3531cb..007fce72 100644 --- a/libsanitizer/asan/asan_globals.cc +++ b/libsanitizer/asan/asan_globals.cc @@ -23,6 +23,7 @@ #include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_symbolizer.h" namespace __asan { @@ -121,16 +122,68 @@ int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites, return res; } -bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) { - Global g = {}; - if (GetGlobalsForAddress(addr, &g, nullptr, 1)) { - internal_strncpy(descr->name, g.name, descr->name_size); - descr->region_address = g.beg; - descr->region_size = g.size; - descr->region_kind = "global"; - return true; +enum GlobalSymbolState { + UNREGISTERED = 0, + REGISTERED = 1 +}; + +// Check ODR violation for given global G via special ODR indicator. We use +// this method in case compiler instruments global variables through their +// local aliases. +static void CheckODRViolationViaIndicator(const Global *g) { + u8 *odr_indicator = reinterpret_cast(g->odr_indicator); + if (*odr_indicator == UNREGISTERED) { + *odr_indicator = REGISTERED; + return; + } + // If *odr_indicator is DEFINED, some module have already registered + // externally visible symbol with the same name. This is an ODR violation. + for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { + if (g->odr_indicator == l->g->odr_indicator && + (flags()->detect_odr_violation >= 2 || g->size != l->g->size) && + !IsODRViolationSuppressed(g->name)) + ReportODRViolation(g, FindRegistrationSite(g), + l->g, FindRegistrationSite(l->g)); + } +} + +// Check ODR violation for given global G by checking if it's already poisoned. +// We use this method in case compiler doesn't use private aliases for global +// variables. +static void CheckODRViolationViaPoisoning(const Global *g) { + if (__asan_region_is_poisoned(g->beg, g->size_with_redzone)) { + // This check may not be enough: if the first global is much larger + // the entire redzone of the second global may be within the first global. + for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { + if (g->beg == l->g->beg && + (flags()->detect_odr_violation >= 2 || g->size != l->g->size) && + !IsODRViolationSuppressed(g->name)) + ReportODRViolation(g, FindRegistrationSite(g), + l->g, FindRegistrationSite(l->g)); + } } - return false; +} + +// Clang provides two different ways for global variables protection: +// it can poison the global itself or its private alias. In former +// case we may poison same symbol multiple times, that can help us to +// cheaply detect ODR violation: if we try to poison an already poisoned +// global, we have ODR violation error. +// In latter case, we poison each symbol exactly once, so we use special +// indicator symbol to perform similar check. +// In either case, compiler provides a special odr_indicator field to Global +// structure, that can contain two kinds of values: +// 1) Non-zero value. In this case, odr_indicator is an address of +// corresponding indicator variable for given global. +// 2) Zero. This means that we don't use private aliases for global variables +// and can freely check ODR violation with the first method. +// +// This routine chooses between two different methods of ODR violation +// detection. +static inline bool UseODRIndicator(const Global *g) { + // Use ODR indicator method iff use_odr_indicator flag is set and + // indicator symbol address is not 0. + return flags()->use_odr_indicator && g->odr_indicator > 0; } // Register a global variable. @@ -142,24 +195,24 @@ static void RegisterGlobal(const Global *g) { ReportGlobal(*g, "Added"); CHECK(flags()->report_globals); CHECK(AddrIsInMem(g->beg)); - CHECK(AddrIsAlignedByGranularity(g->beg)); + if (!AddrIsAlignedByGranularity(g->beg)) { + Report("The following global variable is not properly aligned.\n"); + Report("This may happen if another global with the same name\n"); + Report("resides in another non-instrumented module.\n"); + Report("Or the global comes from a C file built w/o -fno-common.\n"); + Report("In either case this is likely an ODR violation bug,\n"); + Report("but AddressSanitizer can not provide more details.\n"); + ReportODRViolation(g, FindRegistrationSite(g), g, FindRegistrationSite(g)); + CHECK(AddrIsAlignedByGranularity(g->beg)); + } CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - // This "ODR violation" detection is fundamentally incompatible with - // how GCC registers globals. Disable as useless until rewritten upstream. - if (0 && flags()->detect_odr_violation) { + if (flags()->detect_odr_violation) { // Try detecting ODR (One Definition Rule) violation, i.e. the situation // where two globals with the same name are defined in different modules. - if (__asan_region_is_poisoned(g->beg, g->size_with_redzone)) { - // This check may not be enough: if the first global is much larger - // the entire redzone of the second global may be within the first global. - for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { - if (g->beg == l->g->beg && - (flags()->detect_odr_violation >= 2 || g->size != l->g->size) && - !IsODRViolationSuppressed(g->name)) - ReportODRViolation(g, FindRegistrationSite(g), - l->g, FindRegistrationSite(l->g)); - } - } + if (UseODRIndicator(g)) + CheckODRViolationViaIndicator(g); + else + CheckODRViolationViaPoisoning(g); } if (CanPoisonMemory()) PoisonRedZones(*g); @@ -190,6 +243,12 @@ static void UnregisterGlobal(const Global *g) { // We unpoison the shadow memory for the global but we do not remove it from // the list because that would require O(n^2) time with the current list // implementation. It might not be worth doing anyway. + + // Release ODR indicator. + if (UseODRIndicator(g)) { + u8 *odr_indicator = reinterpret_cast(g->odr_indicator); + *odr_indicator = UNREGISTERED; + } } void StopInitOrderChecking() { @@ -207,11 +266,70 @@ void StopInitOrderChecking() { } } +static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; } + +const char *MaybeDemangleGlobalName(const char *name) { + // We can spoil names of globals with C linkage, so use an heuristic + // approach to check if the name should be demangled. + bool should_demangle = false; + if (name[0] == '_' && name[1] == 'Z') + should_demangle = true; + else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?') + should_demangle = true; + + return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name; +} + +// Check if the global is a zero-terminated ASCII string. If so, print it. +void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g) { + for (uptr p = g.beg; p < g.beg + g.size - 1; p++) { + unsigned char c = *(unsigned char *)p; + if (c == '\0' || !IsASCII(c)) return; + } + if (*(char *)(g.beg + g.size - 1) != '\0') return; + str->append(" '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name), + (char *)g.beg); +} + +static const char *GlobalFilename(const __asan_global &g) { + const char *res = g.module_name; + // Prefer the filename from source location, if is available. + if (g.location) res = g.location->filename; + CHECK(res); + return res; +} + +void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g) { + str->append("%s", GlobalFilename(g)); + if (!g.location) return; + if (g.location->line_no) str->append(":%d", g.location->line_no); + if (g.location->column_no) str->append(":%d", g.location->column_no); +} + } // namespace __asan // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT + +// Apply __asan_register_globals to all globals found in the same loaded +// executable or shared library as `flag'. The flag tracks whether globals have +// already been registered or not for this image. +void __asan_register_image_globals(uptr *flag) { + if (*flag) + return; + AsanApplyToGlobals(__asan_register_globals, flag); + *flag = 1; +} + +// This mirrors __asan_register_image_globals. +void __asan_unregister_image_globals(uptr *flag) { + if (!*flag) + return; + AsanApplyToGlobals(__asan_unregister_globals, flag); + *flag = 0; +} + // Register an array of globals. void __asan_register_globals(__asan_global *globals, uptr n) { if (!flags()->report_globals) return; diff --git a/libsanitizer/asan/asan_init_version.h b/libsanitizer/asan/asan_init_version.h index 2cda188..51e8324 100644 --- a/libsanitizer/asan/asan_init_version.h +++ b/libsanitizer/asan/asan_init_version.h @@ -17,16 +17,20 @@ extern "C" { // Every time the ASan ABI changes we also change the version number in the // __asan_init function name. Objects built with incompatible ASan ABI // versions will not link with run-time. + // // Changes between ABI versions: // v1=>v2: added 'module_name' to __asan_global // v2=>v3: stack frame description (created by the compiler) - // contains the function PC as the 3-rd field (see - // DescribeAddressIfStack). - // v3=>v4: added '__asan_global_source_location' to __asan_global. + // contains the function PC as the 3rd field (see + // DescribeAddressIfStack) + // v3=>v4: added '__asan_global_source_location' to __asan_global // v4=>v5: changed the semantics and format of __asan_stack_malloc_ and - // __asan_stack_free_ functions. + // __asan_stack_free_ functions // v5=>v6: changed the name of the version check symbol - #define __asan_version_mismatch_check __asan_version_mismatch_check_v6 + // v6=>v7: added 'odr_indicator' to __asan_global + // v7=>v8: added '__asan_(un)register_image_globals' functions for dead + // stripping support on Mach-O platforms + #define __asan_version_mismatch_check __asan_version_mismatch_check_v8 } #endif // ASAN_INIT_VERSION_H diff --git a/libsanitizer/asan/asan_interceptors.cc b/libsanitizer/asan/asan_interceptors.cc index 356f2c0..743abe5 100644 --- a/libsanitizer/asan/asan_interceptors.cc +++ b/libsanitizer/asan/asan_interceptors.cc @@ -19,6 +19,7 @@ #include "asan_stack.h" #include "asan_stats.h" #include "asan_suppressions.h" +#include "lsan/lsan_common.h" #include "sanitizer_common/sanitizer_libc.h" #if SANITIZER_POSIX @@ -108,7 +109,7 @@ static inline bool RangesOverlap(const char *offset1, uptr length1, } while (0) static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) { -#if ASAN_INTERCEPT_STRNLEN +#if SANITIZER_INTERCEPT_STRNLEN if (REAL(strnlen)) { return REAL(strnlen)(s, maxlen); } @@ -141,6 +142,8 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) (void) ctx; \ #define COMMON_INTERCEPT_FUNCTION(name) ASAN_INTERCEPT_FUNC(name) +#define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \ + ASAN_INTERCEPT_FUNC_VER(name, ver) #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ ASAN_WRITE_RANGE(ctx, ptr, size) #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ @@ -176,7 +179,7 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) } while (false) #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) // Strict init-order checking is dlopen-hostile: -// https://code.google.com/p/address-sanitizer/issues/detail?id=178 +// https://github.com/google/sanitizers/issues/178 #define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \ if (flags()->strict_init_order) { \ StopInitOrderChecking(); \ @@ -193,6 +196,10 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) } else { \ *begin = *end = 0; \ } +// Asan needs custom handling of these: +#undef SANITIZER_INTERCEPT_MEMSET +#undef SANITIZER_INTERCEPT_MEMMOVE +#undef SANITIZER_INTERCEPT_MEMCPY #include "sanitizer_common/sanitizer_common_interceptors.inc" // Syscall interceptors don't have contexts, we don't support suppressions @@ -216,6 +223,7 @@ struct ThreadStartParam { atomic_uintptr_t is_registered; }; +#if ASAN_INTERCEPT_PTHREAD_CREATE static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { ThreadStartParam *param = reinterpret_cast(arg); AsanThread *t = nullptr; @@ -226,7 +234,6 @@ static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { return t->ThreadStart(GetTid(), ¶m->is_registered); } -#if ASAN_INTERCEPT_PTHREAD_CREATE INTERCEPTOR(int, pthread_create, void *thread, void *attr, void *(*start_routine)(void*), void *arg) { EnsureMainThreadIDIsCorrect(); @@ -240,7 +247,17 @@ INTERCEPTOR(int, pthread_create, void *thread, ThreadStartParam param; atomic_store(¶m.t, 0, memory_order_relaxed); atomic_store(¶m.is_registered, 0, memory_order_relaxed); - int result = REAL(pthread_create)(thread, attr, asan_thread_start, ¶m); + int result; + { + // Ignore all allocations made by pthread_create: thread stack/TLS may be + // stored by pthread for future reuse even after thread destruction, and + // the linked list it's stored in doesn't even hold valid pointers to the + // objects, the latter are calculated by obscure pointer arithmetic. +#if CAN_SANITIZE_LEAKS + __lsan::ScopedInterceptorDisabler disabler; +#endif + result = REAL(pthread_create)(thread, attr, asan_thread_start, ¶m); + } if (result == 0) { u32 current_tid = GetCurrentTidOrInvalid(); AsanThread *t = @@ -269,7 +286,8 @@ DEFINE_REAL_PTHREAD_FUNCTIONS #if SANITIZER_ANDROID INTERCEPTOR(void*, bsd_signal, int signum, void *handler) { - if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) { + if (!IsHandledDeadlySignal(signum) || + common_flags()->allow_user_segv_handler) { return REAL(bsd_signal)(signum, handler); } return 0; @@ -277,7 +295,8 @@ INTERCEPTOR(void*, bsd_signal, int signum, void *handler) { #endif INTERCEPTOR(void*, signal, int signum, void *handler) { - if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) { + if (!IsHandledDeadlySignal(signum) || + common_flags()->allow_user_segv_handler) { return REAL(signal)(signum, handler); } return nullptr; @@ -285,7 +304,8 @@ INTERCEPTOR(void*, signal, int signum, void *handler) { INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act, struct sigaction *oldact) { - if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) { + if (!IsHandledDeadlySignal(signum) || + common_flags()->allow_user_segv_handler) { return REAL(sigaction)(signum, act, oldact); } return 0; @@ -451,25 +471,6 @@ INTERCEPTOR(void*, memset, void *block, int c, uptr size) { ASAN_MEMSET_IMPL(ctx, block, c, size); } -INTERCEPTOR(char*, strchr, const char *str, int c) { - void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, strchr); - if (UNLIKELY(!asan_inited)) return internal_strchr(str, c); - // strchr is called inside create_purgeable_zone() when MallocGuardEdges=1 is - // used. - if (asan_init_is_running) { - return REAL(strchr)(str, c); - } - ENSURE_ASAN_INITED(); - char *result = REAL(strchr)(str, c); - if (flags()->replace_str) { - uptr len = REAL(strlen)(str); - uptr bytes_read = (result ? result - str : len) + 1; - ASAN_READ_STRING_OF_LEN(ctx, str, len, bytes_read); - } - return result; -} - #if ASAN_INTERCEPT_INDEX # if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX INTERCEPTOR(char*, index, const char *string, int c) @@ -547,7 +548,6 @@ INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT return REAL(strcpy)(to, from); // NOLINT } -#if ASAN_INTERCEPT_STRDUP INTERCEPTOR(char*, strdup, const char *s) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strdup); @@ -562,29 +562,28 @@ INTERCEPTOR(char*, strdup, const char *s) { REAL(memcpy)(new_mem, s, length + 1); return reinterpret_cast(new_mem); } -#endif -INTERCEPTOR(SIZE_T, strlen, const char *s) { +#if ASAN_INTERCEPT___STRDUP +INTERCEPTOR(char*, __strdup, const char *s) { void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, strlen); - if (UNLIKELY(!asan_inited)) return internal_strlen(s); - // strlen is called from malloc_default_purgeable_zone() - // in __asan::ReplaceSystemAlloc() on Mac. - if (asan_init_is_running) { - return REAL(strlen)(s); - } + ASAN_INTERCEPTOR_ENTER(ctx, strdup); + if (UNLIKELY(!asan_inited)) return internal_strdup(s); ENSURE_ASAN_INITED(); - SIZE_T length = REAL(strlen)(s); + uptr length = REAL(strlen)(s); if (flags()->replace_str) { ASAN_READ_RANGE(ctx, s, length + 1); } - return length; + GET_STACK_TRACE_MALLOC; + void *new_mem = asan_malloc(length + 1, &stack); + REAL(memcpy)(new_mem, s, length + 1); + return reinterpret_cast(new_mem); } +#endif // ASAN_INTERCEPT___STRDUP INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, wcslen); - SIZE_T length = REAL(wcslen)(s); + SIZE_T length = internal_wcslen(s); if (!asan_init_is_running) { ENSURE_ASAN_INITED(); ASAN_READ_RANGE(ctx, s, (length + 1) * sizeof(wchar_t)); @@ -605,19 +604,6 @@ INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) { return REAL(strncpy)(to, from, size); } -#if ASAN_INTERCEPT_STRNLEN -INTERCEPTOR(uptr, strnlen, const char *s, uptr maxlen) { - void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, strnlen); - ENSURE_ASAN_INITED(); - uptr length = REAL(strnlen)(s, maxlen); - if (flags()->replace_str) { - ASAN_READ_RANGE(ctx, s, Min(length + 1, maxlen)); - } - return length; -} -#endif // ASAN_INTERCEPT_STRNLEN - INTERCEPTOR(long, strtol, const char *nptr, // NOLINT char **endptr, int base) { void *ctx; @@ -700,12 +686,12 @@ INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT } #endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL +#if ASAN_INTERCEPT___CXA_ATEXIT static void AtCxaAtexit(void *unused) { (void)unused; StopInitOrderChecking(); } -#if ASAN_INTERCEPT___CXA_ATEXIT INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, void *dso_handle) { #if SANITIZER_MAC @@ -732,7 +718,7 @@ INTERCEPTOR(int, fork, void) { namespace __asan { void InitializeAsanInterceptors() { static bool was_called_once; - CHECK(was_called_once == false); + CHECK(!was_called_once); was_called_once = true; InitializeCommonInterceptors(); @@ -740,22 +726,22 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(memmove); ASAN_INTERCEPT_FUNC(memset); if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { + // In asan, REAL(memmove) is not used, but it is used in msan. ASAN_INTERCEPT_FUNC(memcpy); + } else { + ASSIGN_REAL(memcpy, memmove); } + CHECK(REAL(memcpy)); // Intercept str* functions. ASAN_INTERCEPT_FUNC(strcat); // NOLINT - ASAN_INTERCEPT_FUNC(strchr); ASAN_INTERCEPT_FUNC(strcpy); // NOLINT - ASAN_INTERCEPT_FUNC(strlen); ASAN_INTERCEPT_FUNC(wcslen); ASAN_INTERCEPT_FUNC(strncat); ASAN_INTERCEPT_FUNC(strncpy); -#if ASAN_INTERCEPT_STRDUP ASAN_INTERCEPT_FUNC(strdup); -#endif -#if ASAN_INTERCEPT_STRNLEN - ASAN_INTERCEPT_FUNC(strnlen); +#if ASAN_INTERCEPT___STRDUP + ASAN_INTERCEPT_FUNC(__strdup); #endif #if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX ASAN_INTERCEPT_FUNC(index); diff --git a/libsanitizer/asan/asan_interceptors.h b/libsanitizer/asan/asan_interceptors.h index 46c7417..7053bb7 100644 --- a/libsanitizer/asan/asan_interceptors.h +++ b/libsanitizer/asan/asan_interceptors.h @@ -21,14 +21,12 @@ #if !SANITIZER_WINDOWS # define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 1 # define ASAN_INTERCEPT__LONGJMP 1 -# define ASAN_INTERCEPT_STRDUP 1 # define ASAN_INTERCEPT_INDEX 1 # define ASAN_INTERCEPT_PTHREAD_CREATE 1 # define ASAN_INTERCEPT_FORK 1 #else # define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0 # define ASAN_INTERCEPT__LONGJMP 0 -# define ASAN_INTERCEPT_STRDUP 0 # define ASAN_INTERCEPT_INDEX 0 # define ASAN_INTERCEPT_PTHREAD_CREATE 0 # define ASAN_INTERCEPT_FORK 0 @@ -40,12 +38,6 @@ # define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0 #endif -#if !SANITIZER_MAC -# define ASAN_INTERCEPT_STRNLEN 1 -#else -# define ASAN_INTERCEPT_STRNLEN 0 -#endif - #if SANITIZER_LINUX && !SANITIZER_ANDROID # define ASAN_INTERCEPT_SWAPCONTEXT 1 #else @@ -78,6 +70,12 @@ # define ASAN_INTERCEPT___CXA_ATEXIT 0 #endif +#if SANITIZER_LINUX && !SANITIZER_ANDROID +# define ASAN_INTERCEPT___STRDUP 1 +#else +# define ASAN_INTERCEPT___STRDUP 0 +#endif + DECLARE_REAL(int, memcmp, const void *a1, const void *a2, uptr size) DECLARE_REAL(void*, memcpy, void *to, const void *from, uptr size) DECLARE_REAL(void*, memset, void *block, int c, uptr size) diff --git a/libsanitizer/asan/asan_interface_internal.h b/libsanitizer/asan/asan_interface_internal.h index 079da9c..05605a8 100644 --- a/libsanitizer/asan/asan_interface_internal.h +++ b/libsanitizer/asan/asan_interface_internal.h @@ -21,6 +21,8 @@ #include "asan_init_version.h" using __sanitizer::uptr; +using __sanitizer::u64; +using __sanitizer::u32; extern "C" { // This function should be called at the very beginning of the process, @@ -52,8 +54,17 @@ extern "C" { uptr has_dynamic_init; // Non-zero if the global has dynamic initializer. __asan_global_source_location *location; // Source location of a global, // or NULL if it is unknown. + uptr odr_indicator; // The address of the ODR indicator symbol. }; + // These functions can be called on some platforms to find globals in the same + // loaded image as `flag' and apply __asan_(un)register_globals to them, + // filtering out redundant calls. + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_register_image_globals(uptr *flag); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_unregister_image_globals(uptr *flag); + // These two functions should be called by the instrumented code. // 'globals' is an array of structures describing 'n' globals. SANITIZER_INTERFACE_ATTRIBUTE @@ -68,6 +79,20 @@ extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __asan_after_dynamic_init(); + // Sets bytes of the given range of the shadow memory into specific value. + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_set_shadow_00(uptr addr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_set_shadow_f1(uptr addr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_set_shadow_f2(uptr addr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_set_shadow_f3(uptr addr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_set_shadow_f5(uptr addr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_set_shadow_f8(uptr addr, uptr size); + // These two functions are used by instrumented code in the // use-after-scope mode. They mark memory for local variables as // unaddressable when they leave scope and addressable before the @@ -145,6 +170,9 @@ extern "C" { SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE /* OPTIONAL */ const char* __asan_default_options(); + SANITIZER_INTERFACE_ATTRIBUTE + extern uptr __asan_shadow_memory_dynamic_address; + // Global flag, copy of ASAN_OPTIONS=detect_stack_use_after_return SANITIZER_INTERFACE_ATTRIBUTE extern int __asan_option_detect_stack_use_after_return; diff --git a/libsanitizer/asan/asan_internal.h b/libsanitizer/asan/asan_internal.h index e31f264..15a28ff 100644 --- a/libsanitizer/asan/asan_internal.h +++ b/libsanitizer/asan/asan_internal.h @@ -34,9 +34,9 @@ // If set, values like allocator chunk size, as well as defaults for some flags // will be changed towards less memory overhead. #ifndef ASAN_LOW_MEMORY -#if SANITIZER_WORDSIZE == 32 +# if SANITIZER_IOS || (SANITIZER_WORDSIZE == 32) # define ASAN_LOW_MEMORY 1 -#else +# else # define ASAN_LOW_MEMORY 0 # endif #endif @@ -60,6 +60,12 @@ using __sanitizer::StackTrace; void AsanInitFromRtl(); +// asan_win.cc +void InitializePlatformExceptionHandlers(); + +// asan_win.cc / asan_posix.cc +const char *DescribeSignalOrException(int signo); + // asan_rtl.cc void NORETURN ShowStatsAndAbort(); @@ -71,10 +77,15 @@ void *AsanDoesNotSupportStaticLinkage(); void AsanCheckDynamicRTPrereqs(); void AsanCheckIncompatibleRT(); +// Support function for __asan_(un)register_image_globals. Searches for the +// loaded image containing `needle' and then enumerates all global metadata +// structures declared in that image, applying `op' (e.g., +// __asan_(un)register_globals) to them. +typedef void (*globals_op_fptr)(__asan_global *, uptr); +void AsanApplyToGlobals(globals_op_fptr op, const void *needle); + void AsanOnDeadlySignal(int, void *siginfo, void *context); -void DisableReexec(); -void MaybeReexec(); void ReadContextStack(void *context, uptr *stack, uptr *ssize); void StopInitOrderChecking(); @@ -95,16 +106,24 @@ void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name); bool PlatformHasDifferentMemcpyAndMemmove(); # define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \ (PlatformHasDifferentMemcpyAndMemmove()) +#elif SANITIZER_WINDOWS64 +# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE false #else # define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true #endif // SANITIZER_MAC // Add convenient macro for interface functions that may be represented as // weak hooks. -#define ASAN_MALLOC_HOOK(ptr, size) \ - if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(ptr, size) -#define ASAN_FREE_HOOK(ptr) \ - if (&__sanitizer_free_hook) __sanitizer_free_hook(ptr) +#define ASAN_MALLOC_HOOK(ptr, size) \ + do { \ + if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(ptr, size); \ + RunMallocHooks(ptr, size); \ + } while (false) +#define ASAN_FREE_HOOK(ptr) \ + do { \ + if (&__sanitizer_free_hook) __sanitizer_free_hook(ptr); \ + RunFreeHooks(ptr); \ + } while (false) #define ASAN_ON_ERROR() \ if (&__asan_on_error) __asan_on_error() @@ -112,15 +131,12 @@ extern int asan_inited; // Used to avoid infinite recursion in __asan_init(). extern bool asan_init_is_running; extern void (*death_callback)(void); - // These magic values are written to shadow for better error reporting. const int kAsanHeapLeftRedzoneMagic = 0xfa; -const int kAsanHeapRightRedzoneMagic = 0xfb; const int kAsanHeapFreeMagic = 0xfd; const int kAsanStackLeftRedzoneMagic = 0xf1; const int kAsanStackMidRedzoneMagic = 0xf2; const int kAsanStackRightRedzoneMagic = 0xf3; -const int kAsanStackPartialRedzoneMagic = 0xf4; const int kAsanStackAfterReturnMagic = 0xf5; const int kAsanInitializationOrderMagic = 0xf6; const int kAsanUserPoisonedMemoryMagic = 0xf7; diff --git a/libsanitizer/asan/asan_linux.cc b/libsanitizer/asan/asan_linux.cc index 4e47d5a..9f058df 100644 --- a/libsanitizer/asan/asan_linux.cc +++ b/libsanitizer/asan/asan_linux.cc @@ -67,20 +67,17 @@ asan_rt_version_t __asan_rt_version; namespace __asan { void InitializePlatformInterceptors() {} - -void DisableReexec() { - // No need to re-exec on Linux. -} - -void MaybeReexec() { - // No need to re-exec on Linux. -} +void InitializePlatformExceptionHandlers() {} void *AsanDoesNotSupportStaticLinkage() { // This will fail to link with -static. return &_DYNAMIC; // defined in link.h } +void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { + UNIMPLEMENTED(); +} + #if SANITIZER_ANDROID // FIXME: should we do anything for Android? void AsanCheckDynamicRTPrereqs() {} diff --git a/libsanitizer/asan/asan_mac.cc b/libsanitizer/asan/asan_mac.cc index ab3c656..4bf79be 100644 --- a/libsanitizer/asan/asan_mac.cc +++ b/libsanitizer/asan/asan_mac.cc @@ -22,18 +22,11 @@ #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_mac.h" -#if !SANITIZER_IOS -#include // for _NSGetArgv and _NSGetEnviron -#else -extern "C" { - extern char ***_NSGetArgv(void); -} -#endif - -#include // for dladdr() +#include #include #include #include +#include #include #include #include // for free() @@ -43,193 +36,26 @@ extern "C" { #include #include +// from , but we don't have that file on iOS +extern "C" { + extern char ***_NSGetArgv(void); + extern char ***_NSGetEnviron(void); +} + namespace __asan { void InitializePlatformInterceptors() {} +void InitializePlatformExceptionHandlers() {} bool PlatformHasDifferentMemcpyAndMemmove() { // On OS X 10.7 memcpy() and memmove() are both resolved // into memmove$VARIANT$sse42. - // See also http://code.google.com/p/address-sanitizer/issues/detail?id=34. + // See also https://github.com/google/sanitizers/issues/34. // TODO(glider): need to check dynamically that memcpy() and memmove() are // actually the same function. return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD; } -extern "C" -void __asan_init(); - -static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES"; -LowLevelAllocator allocator_for_env; - -// Change the value of the env var |name|, leaking the original value. -// If |name_value| is NULL, the variable is deleted from the environment, -// otherwise the corresponding "NAME=value" string is replaced with -// |name_value|. -void LeakyResetEnv(const char *name, const char *name_value) { - char **env = GetEnviron(); - uptr name_len = internal_strlen(name); - while (*env != 0) { - uptr len = internal_strlen(*env); - if (len > name_len) { - const char *p = *env; - if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') { - // Match. - if (name_value) { - // Replace the old value with the new one. - *env = const_cast(name_value); - } else { - // Shift the subsequent pointers back. - char **del = env; - do { - del[0] = del[1]; - } while (*del++); - } - } - } - env++; - } -} - -static bool reexec_disabled = false; - -void DisableReexec() { - reexec_disabled = true; -} - -extern "C" SANITIZER_WEAK_ATTRIBUTE double dyldVersionNumber; -static const double kMinDyldVersionWithAutoInterposition = 360.0; - -bool DyldNeedsEnvVariable() { - // Although sanitizer support was added to LLVM on OS X 10.7+, GCC users - // still may want use them on older systems. On older Darwin platforms, dyld - // doesn't export dyldVersionNumber symbol and we simply return true. - if (!&dyldVersionNumber) return true; - // If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if - // DYLD_INSERT_LIBRARIES is not set. However, checking OS version via - // GetMacosVersion() doesn't work for the simulator. Let's instead check - // `dyldVersionNumber`, which is exported by dyld, against a known version - // number from the first OS release where this appeared. - return dyldVersionNumber < kMinDyldVersionWithAutoInterposition; -} - -void MaybeReexec() { - if (reexec_disabled) return; - - // Make sure the dynamic ASan runtime library is preloaded so that the - // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec - // ourselves. - Dl_info info; - CHECK(dladdr((void*)((uptr)__asan_init), &info)); - char *dyld_insert_libraries = - const_cast(GetEnv(kDyldInsertLibraries)); - uptr old_env_len = dyld_insert_libraries ? - internal_strlen(dyld_insert_libraries) : 0; - uptr fname_len = internal_strlen(info.dli_fname); - const char *dylib_name = StripModuleName(info.dli_fname); - uptr dylib_name_len = internal_strlen(dylib_name); - - bool lib_is_in_env = - dyld_insert_libraries && REAL(strstr)(dyld_insert_libraries, dylib_name); - if (DyldNeedsEnvVariable() && !lib_is_in_env) { - // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime - // library. - char program_name[1024]; - uint32_t buf_size = sizeof(program_name); - _NSGetExecutablePath(program_name, &buf_size); - char *new_env = const_cast(info.dli_fname); - if (dyld_insert_libraries) { - // Append the runtime dylib name to the existing value of - // DYLD_INSERT_LIBRARIES. - new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2); - internal_strncpy(new_env, dyld_insert_libraries, old_env_len); - new_env[old_env_len] = ':'; - // Copy fname_len and add a trailing zero. - internal_strncpy(new_env + old_env_len + 1, info.dli_fname, - fname_len + 1); - // Ok to use setenv() since the wrappers don't depend on the value of - // asan_inited. - setenv(kDyldInsertLibraries, new_env, /*overwrite*/1); - } else { - // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name. - setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0); - } - VReport(1, "exec()-ing the program with\n"); - VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env); - VReport(1, "to enable ASan wrappers.\n"); - execv(program_name, *_NSGetArgv()); - - // We get here only if execv() failed. - Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, " - "which is required for ASan to work. ASan tried to set the " - "environment variable and re-execute itself, but execv() failed, " - "possibly because of sandbox restrictions. Make sure to launch the " - "executable with:\n%s=%s\n", kDyldInsertLibraries, new_env); - CHECK("execv failed" && 0); - } - - if (!lib_is_in_env) - return; - - // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove - // the dylib from the environment variable, because interceptors are installed - // and we don't want our children to inherit the variable. - - uptr env_name_len = internal_strlen(kDyldInsertLibraries); - // Allocate memory to hold the previous env var name, its value, the '=' - // sign and the '\0' char. - char *new_env = (char*)allocator_for_env.Allocate( - old_env_len + 2 + env_name_len); - CHECK(new_env); - internal_memset(new_env, '\0', old_env_len + 2 + env_name_len); - internal_strncpy(new_env, kDyldInsertLibraries, env_name_len); - new_env[env_name_len] = '='; - char *new_env_pos = new_env + env_name_len + 1; - - // Iterate over colon-separated pieces of |dyld_insert_libraries|. - char *piece_start = dyld_insert_libraries; - char *piece_end = NULL; - char *old_env_end = dyld_insert_libraries + old_env_len; - do { - if (piece_start[0] == ':') piece_start++; - piece_end = REAL(strchr)(piece_start, ':'); - if (!piece_end) piece_end = dyld_insert_libraries + old_env_len; - if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break; - uptr piece_len = piece_end - piece_start; - - char *filename_start = - (char *)internal_memrchr(piece_start, '/', piece_len); - uptr filename_len = piece_len; - if (filename_start) { - filename_start += 1; - filename_len = piece_len - (filename_start - piece_start); - } else { - filename_start = piece_start; - } - - // If the current piece isn't the runtime library name, - // append it to new_env. - if ((dylib_name_len != filename_len) || - (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) { - if (new_env_pos != new_env + env_name_len + 1) { - new_env_pos[0] = ':'; - new_env_pos++; - } - internal_strncpy(new_env_pos, piece_start, piece_len); - new_env_pos += piece_len; - } - // Move on to the next piece. - piece_start = piece_end; - } while (piece_start < old_env_end); - - // Can't use setenv() here, because it requires the allocator to be - // initialized. - // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in - // a separate function called after InitializeAllocator(). - if (new_env_pos == new_env + env_name_len + 1) new_env = NULL; - LeakyResetEnv(kDyldInsertLibraries, new_env); -} - // No-op. Mac does not support static linkage anyway. void *AsanDoesNotSupportStaticLinkage() { return 0; @@ -241,6 +67,30 @@ void AsanCheckDynamicRTPrereqs() {} // No-op. Mac does not support static linkage anyway. void AsanCheckIncompatibleRT() {} +void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { + // Find the Mach-O header for the image containing the needle + Dl_info info; + int err = dladdr(needle, &info); + if (err == 0) return; + +#if __LP64__ + const struct mach_header_64 *mh = (struct mach_header_64 *)info.dli_fbase; +#else + const struct mach_header *mh = (struct mach_header *)info.dli_fbase; +#endif + + // Look up the __asan_globals section in that image and register its globals + unsigned long size = 0; + __asan_global *globals = (__asan_global *)getsectiondata( + mh, + "__DATA", "__asan_globals", + &size); + + if (!globals) return; + if (size % sizeof(__asan_global) != 0) return; + op(globals, size / sizeof(__asan_global)); +} + void ReadContextStack(void *context, uptr *stack, uptr *ssize) { UNIMPLEMENTED(); } diff --git a/libsanitizer/asan/asan_malloc_linux.cc b/libsanitizer/asan/asan_malloc_linux.cc index 24829ef..28b8b1f 100644 --- a/libsanitizer/asan/asan_malloc_linux.cc +++ b/libsanitizer/asan/asan_malloc_linux.cc @@ -75,10 +75,10 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { INTERCEPTOR(void*, realloc, void *ptr, uptr size) { if (UNLIKELY(IsInDlsymAllocPool(ptr))) { - const uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym; - const uptr copy_size = Min(size, kDlsymAllocPoolSize - offset); + uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym; + uptr copy_size = Min(size, kDlsymAllocPoolSize - offset); void *new_ptr; - if (UNLIKELY(asan_init_is_running)) { + if (UNLIKELY(!asan_inited)) { new_ptr = AllocateFromLocalPool(size); } else { ENSURE_ASAN_INITED(); @@ -108,7 +108,7 @@ INTERCEPTOR(void*, aligned_alloc, uptr boundary, uptr size) { INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) { GET_STACK_TRACE_MALLOC; void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC); - DTLS_on_libc_memalign(res, size * boundary); + DTLS_on_libc_memalign(res, size); return res; } diff --git a/libsanitizer/asan/asan_malloc_mac.cc b/libsanitizer/asan/asan_malloc_mac.cc index 33ccbf0..1ca665d 100644 --- a/libsanitizer/asan/asan_malloc_mac.cc +++ b/libsanitizer/asan/asan_malloc_mac.cc @@ -52,10 +52,6 @@ using namespace __asan; #define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \ GET_STACK_TRACE_FREE; \ ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); -#define COMMON_MALLOC_IGNORE_INVALID_FREE flags()->mac_ignore_invalid_free -#define COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name) \ - GET_STACK_TRACE_FREE; \ - WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack); #define COMMON_MALLOC_NAMESPACE __asan #include "sanitizer_common/sanitizer_malloc_mac.inc" diff --git a/libsanitizer/asan/asan_malloc_win.cc b/libsanitizer/asan/asan_malloc_win.cc index f2ab188..f38cd05 100644 --- a/libsanitizer/asan/asan_malloc_win.cc +++ b/libsanitizer/asan/asan_malloc_win.cc @@ -12,6 +12,8 @@ #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_WINDOWS +#define WIN32_LEAN_AND_MEAN +#include #include "asan_allocator.h" #include "asan_interceptors.h" @@ -47,6 +49,11 @@ void _free_dbg(void *ptr, int) { } ALLOCATION_FUNCTION_ATTRIBUTE +void _free_base(void *ptr) { + free(ptr); +} + +ALLOCATION_FUNCTION_ATTRIBUTE void cfree(void *ptr) { CHECK(!"cfree() should not be used on Windows"); } @@ -58,6 +65,11 @@ void *malloc(size_t size) { } ALLOCATION_FUNCTION_ATTRIBUTE +void *_malloc_base(size_t size) { + return malloc(size); +} + +ALLOCATION_FUNCTION_ATTRIBUTE void *_malloc_dbg(size_t size, int, const char *, int) { return malloc(size); } @@ -69,6 +81,11 @@ void *calloc(size_t nmemb, size_t size) { } ALLOCATION_FUNCTION_ATTRIBUTE +void *_calloc_base(size_t nmemb, size_t size) { + return calloc(nmemb, size); +} + +ALLOCATION_FUNCTION_ATTRIBUTE void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) { return calloc(nmemb, size); } @@ -91,6 +108,11 @@ void *_realloc_dbg(void *ptr, size_t size, int) { } ALLOCATION_FUNCTION_ATTRIBUTE +void *_realloc_base(void *ptr, size_t size) { + return realloc(ptr, size); +} + +ALLOCATION_FUNCTION_ATTRIBUTE void *_recalloc(void *p, size_t n, size_t elem_size) { if (!p) return calloc(n, elem_size); @@ -101,7 +123,12 @@ void *_recalloc(void *p, size_t n, size_t elem_size) { } ALLOCATION_FUNCTION_ATTRIBUTE -size_t _msize(void *ptr) { +void *_recalloc_base(void *p, size_t n, size_t elem_size) { + return _recalloc(p, n, elem_size); +} + +ALLOCATION_FUNCTION_ATTRIBUTE +size_t _msize(const void *ptr) { GET_CURRENT_PC_BP_SP; (void)sp; return asan_malloc_usable_size(ptr, pc, bp); @@ -137,38 +164,90 @@ int _CrtSetReportMode(int, int) { } } // extern "C" +INTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags, + SIZE_T dwBytes) { + GET_STACK_TRACE_MALLOC; + void *p = asan_malloc(dwBytes, &stack); + // Reading MSDN suggests that the *entire* usable allocation is zeroed out. + // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY. + // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083 + if (dwFlags == HEAP_ZERO_MEMORY) + internal_memset(p, 0, asan_mz_size(p)); + else + CHECK(dwFlags == 0 && "unsupported heap flags"); + return p; +} + +INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) { + CHECK(dwFlags == 0 && "unsupported heap flags"); + GET_STACK_TRACE_FREE; + asan_free(lpMem, &stack, FROM_MALLOC); + return true; +} + +INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags, + LPVOID lpMem, SIZE_T dwBytes) { + GET_STACK_TRACE_MALLOC; + // Realloc should never reallocate in place. + if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY) + return nullptr; + CHECK(dwFlags == 0 && "unsupported heap flags"); + return asan_realloc(lpMem, dwBytes, &stack); +} + +INTERCEPTOR_WINAPI(SIZE_T, HeapSize, HANDLE hHeap, DWORD dwFlags, + LPCVOID lpMem) { + CHECK(dwFlags == 0 && "unsupported heap flags"); + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(lpMem, pc, bp); +} + namespace __asan { + +static void TryToOverrideFunction(const char *fname, uptr new_func) { + // Failure here is not fatal. The CRT may not be present, and different CRT + // versions use different symbols. + if (!__interception::OverrideFunction(fname, new_func)) + VPrintf(2, "Failed to override function %s\n", fname); +} + void ReplaceSystemMalloc() { #if defined(ASAN_DYNAMIC) - // We don't check the result because CRT might not be used in the process. - __interception::OverrideFunction("free", (uptr)free); - __interception::OverrideFunction("malloc", (uptr)malloc); - __interception::OverrideFunction("_malloc_crt", (uptr)malloc); - __interception::OverrideFunction("calloc", (uptr)calloc); - __interception::OverrideFunction("_calloc_crt", (uptr)calloc); - __interception::OverrideFunction("realloc", (uptr)realloc); - __interception::OverrideFunction("_realloc_crt", (uptr)realloc); - __interception::OverrideFunction("_recalloc", (uptr)_recalloc); - __interception::OverrideFunction("_recalloc_crt", (uptr)_recalloc); - __interception::OverrideFunction("_msize", (uptr)_msize); - __interception::OverrideFunction("_expand", (uptr)_expand); - - // Override different versions of 'operator new' and 'operator delete'. - // No need to override the nothrow versions as they just wrap the throw - // versions. - // FIXME: Unfortunately, MSVC miscompiles the statements that take the - // addresses of the array versions of these operators, - // see https://connect.microsoft.com/VisualStudio/feedbackdetail/view/946992 - // We might want to try to work around this by [inline] assembly or compiling - // parts of the RTL with Clang. - void *(*op_new)(size_t sz) = operator new; - void (*op_delete)(void *p) = operator delete; - void *(*op_array_new)(size_t sz) = operator new[]; - void (*op_array_delete)(void *p) = operator delete[]; - __interception::OverrideFunction("??2@YAPAXI@Z", (uptr)op_new); - __interception::OverrideFunction("??3@YAXPAX@Z", (uptr)op_delete); - __interception::OverrideFunction("??_U@YAPAXI@Z", (uptr)op_array_new); - __interception::OverrideFunction("??_V@YAXPAX@Z", (uptr)op_array_delete); + TryToOverrideFunction("free", (uptr)free); + TryToOverrideFunction("_free_base", (uptr)free); + TryToOverrideFunction("malloc", (uptr)malloc); + TryToOverrideFunction("_malloc_base", (uptr)malloc); + TryToOverrideFunction("_malloc_crt", (uptr)malloc); + TryToOverrideFunction("calloc", (uptr)calloc); + TryToOverrideFunction("_calloc_base", (uptr)calloc); + TryToOverrideFunction("_calloc_crt", (uptr)calloc); + TryToOverrideFunction("realloc", (uptr)realloc); + TryToOverrideFunction("_realloc_base", (uptr)realloc); + TryToOverrideFunction("_realloc_crt", (uptr)realloc); + TryToOverrideFunction("_recalloc", (uptr)_recalloc); + TryToOverrideFunction("_recalloc_base", (uptr)_recalloc); + TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc); + TryToOverrideFunction("_msize", (uptr)_msize); + TryToOverrideFunction("_expand", (uptr)_expand); + TryToOverrideFunction("_expand_base", (uptr)_expand); + + // Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which + // enable cross-module inlining. This means our _malloc_base hook won't catch + // all CRT allocations. This code here patches the import table of + // ucrtbase.dll so that all attempts to use the lower-level win32 heap + // allocation API will be directed to ASan's heap. We don't currently + // intercept all calls to HeapAlloc. If we did, we would have to check on + // HeapFree whether the pointer came from ASan of from the system. +#define INTERCEPT_UCRT_FUNCTION(func) \ + if (!INTERCEPT_FUNCTION_DLLIMPORT("ucrtbase.dll", \ + "api-ms-win-core-heap-l1-1-0.dll", func)) \ + VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func); + INTERCEPT_UCRT_FUNCTION(HeapAlloc); + INTERCEPT_UCRT_FUNCTION(HeapFree); + INTERCEPT_UCRT_FUNCTION(HeapReAlloc); + INTERCEPT_UCRT_FUNCTION(HeapSize); +#undef INTERCEPT_UCRT_FUNCTION #endif } } // namespace __asan diff --git a/libsanitizer/asan/asan_mapping.h b/libsanitizer/asan/asan_mapping.h index b584cfa..b9fa5f7 100644 --- a/libsanitizer/asan/asan_mapping.h +++ b/libsanitizer/asan/asan_mapping.h @@ -15,7 +15,7 @@ #include "asan_internal.h" // The full explanation of the memory mapping could be found here: -// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm +// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm // // Typical shadow mapping on Linux/x86_64 with SHADOW_OFFSET == 0x00007fff8000: // || `[0x10007fff8000, 0x7fffffffffff]` || HighMem || @@ -85,6 +85,20 @@ // || `[0x08000000000, 0x08fffffffff]` || lowshadow || // || `[0x00000000000, 0x07fffffffff]` || lowmem || // +// Default Linux/S390 mapping: +// || `[0x30000000, 0x7fffffff]` || HighMem || +// || `[0x26000000, 0x2fffffff]` || HighShadow || +// || `[0x24000000, 0x25ffffff]` || ShadowGap || +// || `[0x20000000, 0x23ffffff]` || LowShadow || +// || `[0x00000000, 0x1fffffff]` || LowMem || +// +// Default Linux/SystemZ mapping: +// || `[0x14000000000000, 0x1fffffffffffff]` || HighMem || +// || `[0x12800000000000, 0x13ffffffffffff]` || HighShadow || +// || `[0x12000000000000, 0x127fffffffffff]` || ShadowGap || +// || `[0x10000000000000, 0x11ffffffffffff]` || LowShadow || +// || `[0x00000000000000, 0x0fffffffffffff]` || LowMem || +// // Shadow mapping on FreeBSD/x86-64 with SHADOW_OFFSET == 0x400000000000: // || `[0x500000000000, 0x7fffffffffff]` || HighMem || // || `[0x4a0000000000, 0x4fffffffffff]` || HighShadow || @@ -109,17 +123,19 @@ // || `[0x00000000, 0x2fffffff]` || LowMem || static const u64 kDefaultShadowScale = 3; +static const u64 kDefaultShadowSentinel = ~(uptr)0; static const u64 kDefaultShadowOffset32 = 1ULL << 29; // 0x20000000 static const u64 kDefaultShadowOffset64 = 1ULL << 44; static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G. static const u64 kIosShadowOffset32 = 1ULL << 30; // 0x40000000 -static const u64 kIosShadowOffset64 = 0x130000000; +static const u64 kIosShadowOffset64 = 0x120200000; static const u64 kIosSimShadowOffset32 = 1ULL << 30; static const u64 kIosSimShadowOffset64 = kDefaultShadowOffset64; static const u64 kAArch64_ShadowOffset64 = 1ULL << 36; static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000; static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37; static const u64 kPPC64_ShadowOffset64 = 1ULL << 41; +static const u64 kSystemZ_ShadowOffset64 = 1ULL << 52; static const u64 kFreeBSD_ShadowOffset32 = 1ULL << 30; // 0x40000000 static const u64 kFreeBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000 static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000 @@ -136,28 +152,36 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000 # define SHADOW_OFFSET kFreeBSD_ShadowOffset32 # elif SANITIZER_WINDOWS # define SHADOW_OFFSET kWindowsShadowOffset32 -# elif SANITIZER_IOSSIM -# define SHADOW_OFFSET kIosSimShadowOffset32 # elif SANITIZER_IOS -# define SHADOW_OFFSET kIosShadowOffset32 +# if SANITIZER_IOSSIM +# define SHADOW_OFFSET kIosSimShadowOffset32 +# else +# define SHADOW_OFFSET kIosShadowOffset32 +# endif # else # define SHADOW_OFFSET kDefaultShadowOffset32 # endif #else -# if defined(__aarch64__) +# if SANITIZER_IOS +# if SANITIZER_IOSSIM +# define SHADOW_OFFSET kIosSimShadowOffset64 +# else +# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address +# endif +# elif defined(__aarch64__) # define SHADOW_OFFSET kAArch64_ShadowOffset64 # elif defined(__powerpc64__) # define SHADOW_OFFSET kPPC64_ShadowOffset64 +# elif defined(__s390x__) +# define SHADOW_OFFSET kSystemZ_ShadowOffset64 # elif SANITIZER_FREEBSD # define SHADOW_OFFSET kFreeBSD_ShadowOffset64 # elif SANITIZER_MAC # define SHADOW_OFFSET kDefaultShadowOffset64 # elif defined(__mips64) # define SHADOW_OFFSET kMIPS64_ShadowOffset64 -# elif SANITIZER_IOSSIM -# define SHADOW_OFFSET kIosSimShadowOffset64 -# elif SANITIZER_IOS -# define SHADOW_OFFSET kIosShadowOffset64 +# elif SANITIZER_WINDOWS64 +# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address # else # define SHADOW_OFFSET kDefaultShort64bitShadowOffset # endif @@ -243,9 +267,25 @@ static inline bool AddrIsInMidMem(uptr a) { return kMidMemBeg && a >= kMidMemBeg && a <= kMidMemEnd; } +static inline bool AddrIsInShadowGap(uptr a) { + PROFILE_ASAN_MAPPING(); + if (kMidMemBeg) { + if (a <= kShadowGapEnd) + return SHADOW_OFFSET == 0 || a >= kShadowGapBeg; + return (a >= kShadowGap2Beg && a <= kShadowGap2End) || + (a >= kShadowGap3Beg && a <= kShadowGap3End); + } + // In zero-based shadow mode we treat addresses near zero as addresses + // in shadow gap as well. + if (SHADOW_OFFSET == 0) + return a <= kShadowGapEnd; + return a >= kShadowGapBeg && a <= kShadowGapEnd; +} + static inline bool AddrIsInMem(uptr a) { PROFILE_ASAN_MAPPING(); - return AddrIsInLowMem(a) || AddrIsInMidMem(a) || AddrIsInHighMem(a); + return AddrIsInLowMem(a) || AddrIsInMidMem(a) || AddrIsInHighMem(a) || + (flags()->protect_shadow_gap == 0 && AddrIsInShadowGap(a)); } static inline uptr MemToShadow(uptr p) { @@ -269,21 +309,6 @@ static inline bool AddrIsInShadow(uptr a) { return AddrIsInLowShadow(a) || AddrIsInMidShadow(a) || AddrIsInHighShadow(a); } -static inline bool AddrIsInShadowGap(uptr a) { - PROFILE_ASAN_MAPPING(); - if (kMidMemBeg) { - if (a <= kShadowGapEnd) - return SHADOW_OFFSET == 0 || a >= kShadowGapBeg; - return (a >= kShadowGap2Beg && a <= kShadowGap2End) || - (a >= kShadowGap3Beg && a <= kShadowGap3End); - } - // In zero-based shadow mode we treat addresses near zero as addresses - // in shadow gap as well. - if (SHADOW_OFFSET == 0) - return a <= kShadowGapEnd; - return a >= kShadowGapBeg && a <= kShadowGapEnd; -} - static inline bool AddrIsAlignedByGranularity(uptr a) { PROFILE_ASAN_MAPPING(); return (a & (SHADOW_GRANULARITY - 1)) == 0; diff --git a/libsanitizer/asan/asan_memory_profile.cc b/libsanitizer/asan/asan_memory_profile.cc new file mode 100644 index 0000000..5a25785 --- /dev/null +++ b/libsanitizer/asan/asan_memory_profile.cc @@ -0,0 +1,98 @@ +//===-- asan_memory_profile.cc.cc -----------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// This file implements __sanitizer_print_memory_profile. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_stoptheworld.h" +#include "lsan/lsan_common.h" +#include "asan/asan_allocator.h" + +#if CAN_SANITIZE_LEAKS + +namespace __asan { + +struct AllocationSite { + u32 id; + uptr total_size; + uptr count; +}; + +class HeapProfile { + public: + HeapProfile() : allocations_(1024) {} + void Insert(u32 id, uptr size) { + total_allocated_ += size; + total_count_++; + // Linear lookup will be good enough for most cases (although not all). + for (uptr i = 0; i < allocations_.size(); i++) { + if (allocations_[i].id == id) { + allocations_[i].total_size += size; + allocations_[i].count++; + return; + } + } + allocations_.push_back({id, size, 1}); + } + + void Print(uptr top_percent) { + InternalSort(&allocations_, allocations_.size(), + [](const AllocationSite &a, const AllocationSite &b) { + return a.total_size > b.total_size; + }); + CHECK(total_allocated_); + uptr total_shown = 0; + Printf("Live Heap Allocations: %zd bytes from %zd allocations; " + "showing top %zd%%\n", total_allocated_, total_count_, top_percent); + for (uptr i = 0; i < allocations_.size(); i++) { + auto &a = allocations_[i]; + Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size, + a.total_size * 100 / total_allocated_, a.count); + StackDepotGet(a.id).Print(); + total_shown += a.total_size; + if (total_shown * 100 / total_allocated_ > top_percent) + break; + } + } + + private: + uptr total_allocated_ = 0; + uptr total_count_ = 0; + InternalMmapVector allocations_; +}; + +static void ChunkCallback(uptr chunk, void *arg) { + HeapProfile *hp = reinterpret_cast(arg); + AsanChunkView cv = FindHeapChunkByAllocBeg(chunk); + if (!cv.IsAllocated()) return; + u32 id = cv.GetAllocStackId(); + if (!id) return; + hp->Insert(id, cv.UsedSize()); +} + +static void MemoryProfileCB(const SuspendedThreadsList &suspended_threads_list, + void *argument) { + HeapProfile hp; + __lsan::ForEachChunk(ChunkCallback, &hp); + hp.Print(reinterpret_cast(argument)); +} + +} // namespace __asan + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_print_memory_profile(uptr top_percent) { + __sanitizer::StopTheWorld(__asan::MemoryProfileCB, (void*)top_percent); +} +} // extern "C" + +#endif // CAN_SANITIZE_LEAKS diff --git a/libsanitizer/asan/asan_new_delete.cc b/libsanitizer/asan/asan_new_delete.cc index 719cdfa..20fa7ef 100644 --- a/libsanitizer/asan/asan_new_delete.cc +++ b/libsanitizer/asan/asan_new_delete.cc @@ -18,9 +18,25 @@ #include -// C++ operators can't have visibility attributes on Windows. +// C++ operators can't have dllexport attributes on Windows. We export them +// anyway by passing extra -export flags to the linker, which is exactly that +// dllexport would normally do. We need to export them in order to make the +// VS2015 dynamic CRT (MD) work. #if SANITIZER_WINDOWS # define CXX_OPERATOR_ATTRIBUTE +# ifdef _WIN64 +# pragma comment(linker, "/export:??2@YAPEAX_K@Z") // operator new +# pragma comment(linker, "/export:??3@YAXPEAX@Z") // operator delete +# pragma comment(linker, "/export:??3@YAXPEAX_K@Z") // sized operator delete +# pragma comment(linker, "/export:??_U@YAPEAX_K@Z") // operator new[] +# pragma comment(linker, "/export:??_V@YAXPEAX@Z") // operator delete[] +# else +# pragma comment(linker, "/export:??2@YAPAXI@Z") // operator new +# pragma comment(linker, "/export:??3@YAXPAX@Z") // operator delete +# pragma comment(linker, "/export:??3@YAXPAXI@Z") // sized operator delete +# pragma comment(linker, "/export:??_U@YAPAXI@Z") // operator new[] +# pragma comment(linker, "/export:??_V@YAXPAX@Z") // operator delete[] +# endif #else # define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE #endif @@ -28,7 +44,7 @@ using namespace __asan; // NOLINT // This code has issues on OSX. -// See https://code.google.com/p/address-sanitizer/issues/detail?id=131. +// See https://github.com/google/sanitizers/issues/131. // Fake std::nothrow_t to avoid including . namespace std { diff --git a/libsanitizer/asan/asan_poisoning.cc b/libsanitizer/asan/asan_poisoning.cc index 39f7487..8fe2bd4 100644 --- a/libsanitizer/asan/asan_poisoning.cc +++ b/libsanitizer/asan/asan_poisoning.cc @@ -67,7 +67,7 @@ void FlushUnneededASanShadowMemory(uptr p, uptr size) { uptr page_size = GetPageSizeCached(); uptr shadow_beg = RoundUpTo(MemToShadow(p), page_size); uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size); - FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg); + ReleaseMemoryToOS(shadow_beg, shadow_end - shadow_beg); } void AsanPoisonOrUnpoisonIntraObjectRedzone(uptr ptr, uptr size, bool poison) { @@ -100,7 +100,7 @@ using namespace __asan; // NOLINT // that user program (un)poisons the memory it owns. It poisons memory // conservatively, and unpoisons progressively to make sure asan shadow // mapping invariant is preserved (see detailed mapping description here: -// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm). +// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm). // // * if user asks to poison region [left, right), the program poisons // at least [left, AlignDown(right)). @@ -115,9 +115,9 @@ void __asan_poison_memory_region(void const volatile *addr, uptr size) { ShadowSegmentEndpoint beg(beg_addr); ShadowSegmentEndpoint end(end_addr); if (beg.chunk == end.chunk) { - CHECK(beg.offset < end.offset); + CHECK_LT(beg.offset, end.offset); s8 value = beg.value; - CHECK(value == end.value); + CHECK_EQ(value, end.value); // We can only poison memory if the byte in end.offset is unaddressable. // No need to re-poison memory if it is poisoned already. if (value > 0 && value <= end.offset) { @@ -129,7 +129,7 @@ void __asan_poison_memory_region(void const volatile *addr, uptr size) { } return; } - CHECK(beg.chunk < end.chunk); + CHECK_LT(beg.chunk, end.chunk); if (beg.offset > 0) { // Mark bytes from beg.offset as unaddressable. if (beg.value == 0) { @@ -155,9 +155,9 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) { ShadowSegmentEndpoint beg(beg_addr); ShadowSegmentEndpoint end(end_addr); if (beg.chunk == end.chunk) { - CHECK(beg.offset < end.offset); + CHECK_LT(beg.offset, end.offset); s8 value = beg.value; - CHECK(value == end.value); + CHECK_EQ(value, end.value); // We unpoison memory bytes up to enbytes up to end.offset if it is not // unpoisoned already. if (value != 0) { @@ -165,7 +165,7 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) { } return; } - CHECK(beg.chunk < end.chunk); + CHECK_LT(beg.chunk, end.chunk); if (beg.offset > 0) { *beg.chunk = 0; beg.chunk++; @@ -312,6 +312,30 @@ static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) { } } +void __asan_set_shadow_00(uptr addr, uptr size) { + REAL(memset)((void *)addr, 0, size); +} + +void __asan_set_shadow_f1(uptr addr, uptr size) { + REAL(memset)((void *)addr, 0xf1, size); +} + +void __asan_set_shadow_f2(uptr addr, uptr size) { + REAL(memset)((void *)addr, 0xf2, size); +} + +void __asan_set_shadow_f3(uptr addr, uptr size) { + REAL(memset)((void *)addr, 0xf3, size); +} + +void __asan_set_shadow_f5(uptr addr, uptr size) { + REAL(memset)((void *)addr, 0xf5, size); +} + +void __asan_set_shadow_f8(uptr addr, uptr size) { + REAL(memset)((void *)addr, 0xf8, size); +} + void __asan_poison_stack_memory(uptr addr, uptr size) { VReport(1, "poisoning: %p %zx\n", (void *)addr, size); PoisonAlignedStackMemory(addr, size, true); @@ -341,7 +365,7 @@ void __sanitizer_annotate_contiguous_container(const void *beg_p, &stack); } CHECK_LE(end - beg, - FIRST_32_SECOND_64(1UL << 30, 1UL << 34)); // Sanity check. + FIRST_32_SECOND_64(1UL << 30, 1ULL << 34)); // Sanity check. uptr a = RoundDownTo(Min(old_mid, new_mid), granularity); uptr c = RoundUpTo(Max(old_mid, new_mid), granularity); @@ -352,7 +376,7 @@ void __sanitizer_annotate_contiguous_container(const void *beg_p, // Make a quick sanity check that we are indeed in this state. // // FIXME: Two of these three checks are disabled until we fix - // https://code.google.com/p/address-sanitizer/issues/detail?id=258. + // https://github.com/google/sanitizers/issues/258. // if (d1 != d2) // CHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1); if (a + granularity <= d1) diff --git a/libsanitizer/asan/asan_poisoning.h b/libsanitizer/asan/asan_poisoning.h index 30e39e9..4ddcbc3 100644 --- a/libsanitizer/asan/asan_poisoning.h +++ b/libsanitizer/asan/asan_poisoning.h @@ -84,7 +84,7 @@ ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone( } } -// Calls __sanitizer::FlushUnneededShadowMemory() on +// Calls __sanitizer::ReleaseMemoryToOS() on // [MemToShadow(p), MemToShadow(p+size)] with proper rounding. void FlushUnneededASanShadowMemory(uptr p, uptr size); diff --git a/libsanitizer/asan/asan_posix.cc b/libsanitizer/asan/asan_posix.cc index 5b532e9..532afb3 100644 --- a/libsanitizer/asan/asan_posix.cc +++ b/libsanitizer/asan/asan_posix.cc @@ -31,17 +31,39 @@ namespace __asan { +const char *DescribeSignalOrException(int signo) { + switch (signo) { + case SIGFPE: + return "FPE"; + case SIGILL: + return "ILL"; + case SIGABRT: + return "ABRT"; + default: + return "SEGV"; + } +} + void AsanOnDeadlySignal(int signo, void *siginfo, void *context) { ScopedDeadlySignal signal_scope(GetCurrentThread()); int code = (int)((siginfo_t*)siginfo)->si_code; - // Write the first message using the bullet-proof write. - if (18 != internal_write(2, "ASAN:DEADLYSIGNAL\n", 18)) Die(); + // Write the first message using fd=2, just in case. + // It may actually fail to write in case stderr is closed. + internal_write(2, "ASAN:DEADLYSIGNAL\n", 18); SignalContext sig = SignalContext::Create(siginfo, context); // Access at a reasonable offset above SP, or slightly below it (to account // for x86_64 or PowerPC redzone, ARM push of multiple registers, etc) is // probably a stack overflow. +#ifdef __s390__ + // On s390, the fault address in siginfo points to start of the page, not + // to the precise word that was accessed. Mask off the low bits of sp to + // take it into account. + bool IsStackAccess = sig.addr >= (sig.sp & ~0xFFF) && + sig.addr < sig.sp + 0xFFFF; +#else bool IsStackAccess = sig.addr + 512 > sig.sp && sig.addr < sig.sp + 0xFFFF; +#endif #if __powerpc__ // Large stack frames can be allocated with e.g. @@ -73,10 +95,8 @@ void AsanOnDeadlySignal(int signo, void *siginfo, void *context) { // unaligned memory access. if (IsStackAccess && (code == si_SEGV_MAPERR || code == si_SEGV_ACCERR)) ReportStackOverflow(sig); - else if (signo == SIGFPE) - ReportDeadlySignal("FPE", sig); else - ReportDeadlySignal("SEGV", sig); + ReportDeadlySignal(signo, sig); } // ---------------------- TSD ---------------- {{{1 diff --git a/libsanitizer/asan/asan_report.cc b/libsanitizer/asan/asan_report.cc index 5b663cd..84d6764 100644 --- a/libsanitizer/asan/asan_report.cc +++ b/libsanitizer/asan/asan_report.cc @@ -10,10 +10,13 @@ // This file contains error reporting code. //===----------------------------------------------------------------------===// +#include "asan_errors.h" #include "asan_flags.h" +#include "asan_descriptions.h" #include "asan_internal.h" #include "asan_mapping.h" #include "asan_report.h" +#include "asan_scariness_score.h" #include "asan_stack.h" #include "asan_thread.h" #include "sanitizer_common/sanitizer_common.h" @@ -28,90 +31,31 @@ namespace __asan { static void (*error_report_callback)(const char*); static char *error_message_buffer = nullptr; static uptr error_message_buffer_pos = 0; -static uptr error_message_buffer_size = 0; +static BlockingMutex error_message_buf_mutex(LINKER_INITIALIZED); static const unsigned kAsanBuggyPcPoolSize = 25; static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize]; -struct ReportData { - uptr pc; - uptr sp; - uptr bp; - uptr addr; - bool is_write; - uptr access_size; - const char *description; -}; - -static bool report_happened = false; -static ReportData report_data = {}; - void AppendToErrorMessageBuffer(const char *buffer) { - if (error_message_buffer) { - uptr length = internal_strlen(buffer); - CHECK_GE(error_message_buffer_size, error_message_buffer_pos); - uptr remaining = error_message_buffer_size - error_message_buffer_pos; - internal_strncpy(error_message_buffer + error_message_buffer_pos, - buffer, remaining); - error_message_buffer[error_message_buffer_size - 1] = '\0'; - // FIXME: reallocate the buffer instead of truncating the message. - error_message_buffer_pos += Min(remaining, length); + BlockingMutexLock l(&error_message_buf_mutex); + if (!error_message_buffer) { + error_message_buffer = + (char*)MmapOrDieQuietly(kErrorMessageBufferSize, __func__); + error_message_buffer_pos = 0; } + uptr length = internal_strlen(buffer); + RAW_CHECK(kErrorMessageBufferSize >= error_message_buffer_pos); + uptr remaining = kErrorMessageBufferSize - error_message_buffer_pos; + internal_strncpy(error_message_buffer + error_message_buffer_pos, + buffer, remaining); + error_message_buffer[kErrorMessageBufferSize - 1] = '\0'; + // FIXME: reallocate the buffer instead of truncating the message. + error_message_buffer_pos += Min(remaining, length); } -// ---------------------- Decorator ------------------------------ {{{1 -class Decorator: public __sanitizer::SanitizerCommonDecorator { - public: - Decorator() : SanitizerCommonDecorator() { } - const char *Access() { return Blue(); } - const char *EndAccess() { return Default(); } - const char *Location() { return Green(); } - const char *EndLocation() { return Default(); } - const char *Allocation() { return Magenta(); } - const char *EndAllocation() { return Default(); } - - const char *ShadowByte(u8 byte) { - switch (byte) { - case kAsanHeapLeftRedzoneMagic: - case kAsanHeapRightRedzoneMagic: - case kAsanArrayCookieMagic: - return Red(); - case kAsanHeapFreeMagic: - return Magenta(); - case kAsanStackLeftRedzoneMagic: - case kAsanStackMidRedzoneMagic: - case kAsanStackRightRedzoneMagic: - case kAsanStackPartialRedzoneMagic: - return Red(); - case kAsanStackAfterReturnMagic: - return Magenta(); - case kAsanInitializationOrderMagic: - return Cyan(); - case kAsanUserPoisonedMemoryMagic: - case kAsanContiguousContainerOOBMagic: - case kAsanAllocaLeftMagic: - case kAsanAllocaRightMagic: - return Blue(); - case kAsanStackUseAfterScopeMagic: - return Magenta(); - case kAsanGlobalRedzoneMagic: - return Red(); - case kAsanInternalHeapMagic: - return Yellow(); - case kAsanIntraObjectRedzone: - return Yellow(); - default: - return Default(); - } - } - const char *EndShadowByte() { return Default(); } - const char *MemoryByte() { return Magenta(); } - const char *EndMemoryByte() { return Default(); } -}; - // ---------------------- Helper functions ----------------------- {{{1 -static void PrintMemoryByte(InternalScopedString *str, const char *before, - u8 byte, bool in_shadow, const char *after = "\n") { +void PrintMemoryByte(InternalScopedString *str, const char *before, u8 byte, + bool in_shadow, const char *after) { Decorator d; str->append("%s%s%x%x%s%s", before, in_shadow ? d.ShadowByte(byte) : d.MemoryByte(), @@ -119,99 +63,6 @@ static void PrintMemoryByte(InternalScopedString *str, const char *before, in_shadow ? d.EndShadowByte() : d.EndMemoryByte(), after); } -static void PrintShadowByte(InternalScopedString *str, const char *before, - u8 byte, const char *after = "\n") { - PrintMemoryByte(str, before, byte, /*in_shadow*/true, after); -} - -static void PrintShadowBytes(InternalScopedString *str, const char *before, - u8 *bytes, u8 *guilty, uptr n) { - Decorator d; - if (before) str->append("%s%p:", before, bytes); - for (uptr i = 0; i < n; i++) { - u8 *p = bytes + i; - const char *before = - p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " "; - const char *after = p == guilty ? "]" : ""; - PrintShadowByte(str, before, *p, after); - } - str->append("\n"); -} - -static void PrintLegend(InternalScopedString *str) { - str->append( - "Shadow byte legend (one shadow byte represents %d " - "application bytes):\n", - (int)SHADOW_GRANULARITY); - PrintShadowByte(str, " Addressable: ", 0); - str->append(" Partially addressable: "); - for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " "); - str->append("\n"); - PrintShadowByte(str, " Heap left redzone: ", - kAsanHeapLeftRedzoneMagic); - PrintShadowByte(str, " Heap right redzone: ", - kAsanHeapRightRedzoneMagic); - PrintShadowByte(str, " Freed heap region: ", kAsanHeapFreeMagic); - PrintShadowByte(str, " Stack left redzone: ", - kAsanStackLeftRedzoneMagic); - PrintShadowByte(str, " Stack mid redzone: ", - kAsanStackMidRedzoneMagic); - PrintShadowByte(str, " Stack right redzone: ", - kAsanStackRightRedzoneMagic); - PrintShadowByte(str, " Stack partial redzone: ", - kAsanStackPartialRedzoneMagic); - PrintShadowByte(str, " Stack after return: ", - kAsanStackAfterReturnMagic); - PrintShadowByte(str, " Stack use after scope: ", - kAsanStackUseAfterScopeMagic); - PrintShadowByte(str, " Global redzone: ", kAsanGlobalRedzoneMagic); - PrintShadowByte(str, " Global init order: ", - kAsanInitializationOrderMagic); - PrintShadowByte(str, " Poisoned by user: ", - kAsanUserPoisonedMemoryMagic); - PrintShadowByte(str, " Container overflow: ", - kAsanContiguousContainerOOBMagic); - PrintShadowByte(str, " Array cookie: ", - kAsanArrayCookieMagic); - PrintShadowByte(str, " Intra object redzone: ", - kAsanIntraObjectRedzone); - PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic); - PrintShadowByte(str, " Left alloca redzone: ", kAsanAllocaLeftMagic); - PrintShadowByte(str, " Right alloca redzone: ", kAsanAllocaRightMagic); -} - -void MaybeDumpInstructionBytes(uptr pc) { - if (!flags()->dump_instruction_bytes || (pc < GetPageSizeCached())) - return; - InternalScopedString str(1024); - str.append("First 16 instruction bytes at pc: "); - if (IsAccessibleMemoryRange(pc, 16)) { - for (int i = 0; i < 16; ++i) { - PrintMemoryByte(&str, "", ((u8 *)pc)[i], /*in_shadow*/false, " "); - } - str.append("\n"); - } else { - str.append("unaccessible\n"); - } - Report("%s", str.data()); -} - -static void PrintShadowMemoryForAddress(uptr addr) { - if (!AddrIsInMem(addr)) return; - uptr shadow_addr = MemToShadow(addr); - const uptr n_bytes_per_row = 16; - uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1); - InternalScopedString str(4096 * 8); - str.append("Shadow bytes around the buggy address:\n"); - for (int i = -5; i <= 5; i++) { - const char *prefix = (i == 0) ? "=>" : " "; - PrintShadowBytes(&str, prefix, (u8 *)(aligned_shadow + i * n_bytes_per_row), - (u8 *)shadow_addr, n_bytes_per_row); - } - if (flags()->print_legend) PrintLegend(&str); - Printf("%s", str.data()); -} - static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, const char *zone_name) { if (zone_ptr) { @@ -227,191 +78,8 @@ static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, } } -static void DescribeThread(AsanThread *t) { - if (t) - DescribeThread(t->context()); -} - // ---------------------- Address Descriptions ------------------- {{{1 -static bool IsASCII(unsigned char c) { - return /*0x00 <= c &&*/ c <= 0x7F; -} - -static const char *MaybeDemangleGlobalName(const char *name) { - // We can spoil names of globals with C linkage, so use an heuristic - // approach to check if the name should be demangled. - bool should_demangle = false; - if (name[0] == '_' && name[1] == 'Z') - should_demangle = true; - else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?') - should_demangle = true; - - return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name; -} - -// Check if the global is a zero-terminated ASCII string. If so, print it. -static void PrintGlobalNameIfASCII(InternalScopedString *str, - const __asan_global &g) { - for (uptr p = g.beg; p < g.beg + g.size - 1; p++) { - unsigned char c = *(unsigned char*)p; - if (c == '\0' || !IsASCII(c)) return; - } - if (*(char*)(g.beg + g.size - 1) != '\0') return; - str->append(" '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name), - (char *)g.beg); -} - -static const char *GlobalFilename(const __asan_global &g) { - const char *res = g.module_name; - // Prefer the filename from source location, if is available. - if (g.location) - res = g.location->filename; - CHECK(res); - return res; -} - -static void PrintGlobalLocation(InternalScopedString *str, - const __asan_global &g) { - str->append("%s", GlobalFilename(g)); - if (!g.location) - return; - if (g.location->line_no) - str->append(":%d", g.location->line_no); - if (g.location->column_no) - str->append(":%d", g.location->column_no); -} - -static void DescribeAddressRelativeToGlobal(uptr addr, uptr size, - const __asan_global &g) { - InternalScopedString str(4096); - Decorator d; - str.append("%s", d.Location()); - if (addr < g.beg) { - str.append("%p is located %zd bytes to the left", (void *)addr, - g.beg - addr); - } else if (addr + size > g.beg + g.size) { - if (addr < g.beg + g.size) - addr = g.beg + g.size; - str.append("%p is located %zd bytes to the right", (void *)addr, - addr - (g.beg + g.size)); - } else { - // Can it happen? - str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg); - } - str.append(" of global variable '%s' defined in '", - MaybeDemangleGlobalName(g.name)); - PrintGlobalLocation(&str, g); - str.append("' (0x%zx) of size %zu\n", g.beg, g.size); - str.append("%s", d.EndLocation()); - PrintGlobalNameIfASCII(&str, g); - Printf("%s", str.data()); -} - -static bool DescribeAddressIfGlobal(uptr addr, uptr size, - const char *bug_type) { - // Assume address is close to at most four globals. - const int kMaxGlobalsInReport = 4; - __asan_global globals[kMaxGlobalsInReport]; - u32 reg_sites[kMaxGlobalsInReport]; - int globals_num = - GetGlobalsForAddress(addr, globals, reg_sites, ARRAY_SIZE(globals)); - if (globals_num == 0) - return false; - for (int i = 0; i < globals_num; i++) { - DescribeAddressRelativeToGlobal(addr, size, globals[i]); - if (0 == internal_strcmp(bug_type, "initialization-order-fiasco") && - reg_sites[i]) { - Printf(" registered at:\n"); - StackDepotGet(reg_sites[i]).Print(); - } - } - return true; -} - -bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr, bool print) { - if (AddrIsInMem(addr)) - return false; - const char *area_type = nullptr; - if (AddrIsInShadowGap(addr)) area_type = "shadow gap"; - else if (AddrIsInHighShadow(addr)) area_type = "high shadow"; - else if (AddrIsInLowShadow(addr)) area_type = "low shadow"; - if (area_type != nullptr) { - if (print) { - Printf("Address %p is located in the %s area.\n", addr, area_type); - } else { - CHECK(descr); - descr->region_kind = area_type; - } - return true; - } - CHECK(0 && "Address is not in memory and not in shadow?"); - return false; -} - -// Return " (thread_name) " or an empty string if the name is empty. -const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[], - uptr buff_len) { - const char *name = t->name; - if (name[0] == '\0') return ""; - buff[0] = 0; - internal_strncat(buff, " (", 3); - internal_strncat(buff, name, buff_len - 4); - internal_strncat(buff, ")", 2); - return buff; -} - -const char *ThreadNameWithParenthesis(u32 tid, char buff[], - uptr buff_len) { - if (tid == kInvalidTid) return ""; - asanThreadRegistry().CheckLocked(); - AsanThreadContext *t = GetThreadContextByTidLocked(tid); - return ThreadNameWithParenthesis(t, buff, buff_len); -} - -static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr, - uptr access_size, uptr prev_var_end, - uptr next_var_beg) { - uptr var_end = var.beg + var.size; - uptr addr_end = addr + access_size; - const char *pos_descr = nullptr; - // If the variable [var.beg, var_end) is the nearest variable to the - // current memory access, indicate it in the log. - if (addr >= var.beg) { - if (addr_end <= var_end) - pos_descr = "is inside"; // May happen if this is a use-after-return. - else if (addr < var_end) - pos_descr = "partially overflows"; - else if (addr_end <= next_var_beg && - next_var_beg - addr_end >= addr - var_end) - pos_descr = "overflows"; - } else { - if (addr_end > var.beg) - pos_descr = "partially underflows"; - else if (addr >= prev_var_end && - addr - prev_var_end >= var.beg - addr_end) - pos_descr = "underflows"; - } - InternalScopedString str(1024); - str.append(" [%zd, %zd)", var.beg, var_end); - // Render variable name. - str.append(" '"); - for (uptr i = 0; i < var.name_len; ++i) { - str.append("%c", var.name_pos[i]); - } - str.append("'"); - if (pos_descr) { - Decorator d; - // FIXME: we may want to also print the size of the access here, - // but in case of accesses generated by memset it may be confusing. - str.append("%s <== Memory access at offset %zd %s this variable%s\n", - d.Location(), addr, pos_descr, d.EndLocation()); - } else { - str.append("\n"); - } - Printf("%s", str.data()); -} - bool ParseFrameDescription(const char *frame_descr, InternalMmapVector *vars) { CHECK(frame_descr); @@ -439,195 +107,17 @@ bool ParseFrameDescription(const char *frame_descr, return true; } -bool DescribeAddressIfStack(uptr addr, uptr access_size) { - AsanThread *t = FindThreadByStackAddress(addr); - if (!t) return false; - - Decorator d; - char tname[128]; - Printf("%s", d.Location()); - Printf("Address %p is located in stack of thread T%d%s", addr, t->tid(), - ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname))); - - // Try to fetch precise stack frame for this access. - AsanThread::StackFrameAccess access; - if (!t->GetStackFrameAccessByAddr(addr, &access)) { - Printf("%s\n", d.EndLocation()); - return true; - } - Printf(" at offset %zu in frame%s\n", access.offset, d.EndLocation()); - - // Now we print the frame where the alloca has happened. - // We print this frame as a stack trace with one element. - // The symbolizer may print more than one frame if inlining was involved. - // The frame numbers may be different than those in the stack trace printed - // previously. That's unfortunate, but I have no better solution, - // especially given that the alloca may be from entirely different place - // (e.g. use-after-scope, or different thread's stack). -#if defined(__powerpc64__) && defined(__BIG_ENDIAN__) - // On PowerPC64 ELFv1, the address of a function actually points to a - // three-doubleword data structure with the first field containing - // the address of the function's code. - access.frame_pc = *reinterpret_cast(access.frame_pc); -#endif - access.frame_pc += 16; - Printf("%s", d.EndLocation()); - StackTrace alloca_stack(&access.frame_pc, 1); - alloca_stack.Print(); - - InternalMmapVector vars(16); - if (!ParseFrameDescription(access.frame_descr, &vars)) { - Printf("AddressSanitizer can't parse the stack frame " - "descriptor: |%s|\n", access.frame_descr); - // 'addr' is a stack address, so return true even if we can't parse frame - return true; - } - uptr n_objects = vars.size(); - // Report the number of stack objects. - Printf(" This frame has %zu object(s):\n", n_objects); - - // Report all objects in this frame. - for (uptr i = 0; i < n_objects; i++) { - uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0; - uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL); - PrintAccessAndVarIntersection(vars[i], access.offset, access_size, - prev_var_end, next_var_beg); - } - Printf("HINT: this may be a false positive if your program uses " - "some custom stack unwind mechanism or swapcontext\n"); - if (SANITIZER_WINDOWS) - Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n"); - else - Printf(" (longjmp and C++ exceptions *are* supported)\n"); - - DescribeThread(t); - return true; -} - -static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr, - uptr access_size) { - sptr offset; - Decorator d; - InternalScopedString str(4096); - str.append("%s", d.Location()); - if (chunk.AddrIsAtLeft(addr, access_size, &offset)) { - str.append("%p is located %zd bytes to the left of", (void *)addr, offset); - } else if (chunk.AddrIsAtRight(addr, access_size, &offset)) { - if (offset < 0) { - addr -= offset; - offset = 0; - } - str.append("%p is located %zd bytes to the right of", (void *)addr, offset); - } else if (chunk.AddrIsInside(addr, access_size, &offset)) { - str.append("%p is located %zd bytes inside of", (void*)addr, offset); - } else { - str.append("%p is located somewhere around (this is AddressSanitizer bug!)", - (void *)addr); - } - str.append(" %zu-byte region [%p,%p)\n", chunk.UsedSize(), - (void *)(chunk.Beg()), (void *)(chunk.End())); - str.append("%s", d.EndLocation()); - Printf("%s", str.data()); -} - -void DescribeHeapAddress(uptr addr, uptr access_size) { - AsanChunkView chunk = FindHeapChunkByAddress(addr); - if (!chunk.IsValid()) { - Printf("AddressSanitizer can not describe address in more detail " - "(wild memory access suspected).\n"); - return; - } - DescribeAccessToHeapChunk(chunk, addr, access_size); - CHECK(chunk.AllocTid() != kInvalidTid); - asanThreadRegistry().CheckLocked(); - AsanThreadContext *alloc_thread = - GetThreadContextByTidLocked(chunk.AllocTid()); - StackTrace alloc_stack = chunk.GetAllocStack(); - char tname[128]; - Decorator d; - AsanThreadContext *free_thread = nullptr; - if (chunk.FreeTid() != kInvalidTid) { - free_thread = GetThreadContextByTidLocked(chunk.FreeTid()); - Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(), - free_thread->tid, - ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)), - d.EndAllocation()); - StackTrace free_stack = chunk.GetFreeStack(); - free_stack.Print(); - Printf("%spreviously allocated by thread T%d%s here:%s\n", - d.Allocation(), alloc_thread->tid, - ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), - d.EndAllocation()); - } else { - Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(), - alloc_thread->tid, - ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), - d.EndAllocation()); - } - alloc_stack.Print(); - DescribeThread(GetCurrentThread()); - if (free_thread) - DescribeThread(free_thread); - DescribeThread(alloc_thread); -} - -static void DescribeAddress(uptr addr, uptr access_size, const char *bug_type) { - // Check if this is shadow or shadow gap. - if (DescribeAddressIfShadow(addr)) - return; - CHECK(AddrIsInMem(addr)); - if (DescribeAddressIfGlobal(addr, access_size, bug_type)) - return; - if (DescribeAddressIfStack(addr, access_size)) - return; - // Assume it is a heap address. - DescribeHeapAddress(addr, access_size); -} - -// ------------------- Thread description -------------------- {{{1 - -void DescribeThread(AsanThreadContext *context) { - CHECK(context); - asanThreadRegistry().CheckLocked(); - // No need to announce the main thread. - if (context->tid == 0 || context->announced) { - return; - } - context->announced = true; - char tname[128]; - InternalScopedString str(1024); - str.append("Thread T%d%s", context->tid, - ThreadNameWithParenthesis(context->tid, tname, sizeof(tname))); - if (context->parent_tid == kInvalidTid) { - str.append(" created by unknown thread\n"); - Printf("%s", str.data()); - return; - } - str.append( - " created by T%d%s here:\n", context->parent_tid, - ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname))); - Printf("%s", str.data()); - StackDepotGet(context->stack_id).Print(); - // Recursively described parent thread if needed. - if (flags()->print_full_thread_history) { - AsanThreadContext *parent_context = - GetThreadContextByTidLocked(context->parent_tid); - DescribeThread(parent_context); - } -} - // -------------------- Different kinds of reports ----------------- {{{1 // Use ScopedInErrorReport to run common actions just before and // immediately after printing error report. class ScopedInErrorReport { public: - explicit ScopedInErrorReport(ReportData *report = nullptr, - bool fatal = false) { + explicit ScopedInErrorReport(bool fatal = false) { halt_on_error_ = fatal || flags()->halt_on_error; if (lock_.TryLock()) { - StartReporting(report); + StartReporting(); return; } @@ -669,10 +159,13 @@ class ScopedInErrorReport { lock_.Lock(); } - StartReporting(report); + StartReporting(); } ~ScopedInErrorReport() { + ASAN_ON_ERROR(); + if (current_error_.IsValid()) current_error_.Print(); + // Make sure the current thread is announced. DescribeThread(GetCurrentThread()); // We may want to grab this lock again when printing stats. @@ -680,11 +173,30 @@ class ScopedInErrorReport { // Print memory stats. if (flags()->print_stats) __asan_print_accumulated_stats(); + if (common_flags()->print_cmdline) PrintCmdline(); + + // Copy the message buffer so that we could start logging without holding a + // lock that gets aquired during printing. + InternalScopedBuffer buffer_copy(kErrorMessageBufferSize); + { + BlockingMutexLock l(&error_message_buf_mutex); + internal_memcpy(buffer_copy.data(), + error_message_buffer, kErrorMessageBufferSize); + } + + LogFullErrorReport(buffer_copy.data()); + if (error_report_callback) { - error_report_callback(error_message_buffer); + error_report_callback(buffer_copy.data()); } + + // In halt_on_error = false mode, reset the current error object (before + // unlocking). + if (!halt_on_error_) + internal_memset(¤t_error_, 0, sizeof(current_error_)); + CommonSanitizerReportMutex.Unlock(); reporting_thread_tid_ = kInvalidTid; lock_.Unlock(); @@ -694,11 +206,18 @@ class ScopedInErrorReport { } } + void ReportError(const ErrorDescription &description) { + // Can only report one error per ScopedInErrorReport. + CHECK_EQ(current_error_.kind, kErrorKindInvalid); + current_error_ = description; + } + + static ErrorDescription &CurrentError() { + return current_error_; + } + private: - void StartReporting(ReportData *report) { - if (report) report_data = *report; - report_happened = true; - ASAN_ON_ERROR(); + void StartReporting() { // Make sure the registry and sanitizer report mutexes are locked while // we're printing an error report. // We can lock them only here to avoid self-deadlock in case of @@ -712,154 +231,69 @@ class ScopedInErrorReport { static StaticSpinMutex lock_; static u32 reporting_thread_tid_; + // Error currently being reported. This enables the destructor to interact + // with the debugger and point it to an error description. + static ErrorDescription current_error_; bool halt_on_error_; }; StaticSpinMutex ScopedInErrorReport::lock_; -u32 ScopedInErrorReport::reporting_thread_tid_; +u32 ScopedInErrorReport::reporting_thread_tid_ = kInvalidTid; +ErrorDescription ScopedInErrorReport::current_error_; void ReportStackOverflow(const SignalContext &sig) { - ScopedInErrorReport in_report(/*report*/ nullptr, /*fatal*/ true); - Decorator d; - Printf("%s", d.Warning()); - Report( - "ERROR: AddressSanitizer: stack-overflow on address %p" - " (pc %p bp %p sp %p T%d)\n", - (void *)sig.addr, (void *)sig.pc, (void *)sig.bp, (void *)sig.sp, - GetCurrentTidOrInvalid()); - Printf("%s", d.EndWarning()); - GET_STACK_TRACE_SIGNAL(sig); - stack.Print(); - ReportErrorSummary("stack-overflow", &stack); + ScopedInErrorReport in_report(/*fatal*/ true); + ErrorStackOverflow error(GetCurrentTidOrInvalid(), sig); + in_report.ReportError(error); } -void ReportDeadlySignal(const char *description, const SignalContext &sig) { - ScopedInErrorReport in_report(/*report*/nullptr, /*fatal*/true); - Decorator d; - Printf("%s", d.Warning()); - Report( - "ERROR: AddressSanitizer: %s on unknown address %p" - " (pc %p bp %p sp %p T%d)\n", - description, (void *)sig.addr, (void *)sig.pc, (void *)sig.bp, - (void *)sig.sp, GetCurrentTidOrInvalid()); - if (sig.pc < GetPageSizeCached()) { - Report("Hint: pc points to the zero page.\n"); - } - Printf("%s", d.EndWarning()); - GET_STACK_TRACE_SIGNAL(sig); - stack.Print(); - MaybeDumpInstructionBytes(sig.pc); - Printf("AddressSanitizer can not provide additional info.\n"); - ReportErrorSummary(description, &stack); +void ReportDeadlySignal(int signo, const SignalContext &sig) { + ScopedInErrorReport in_report(/*fatal*/ true); + ErrorDeadlySignal error(GetCurrentTidOrInvalid(), sig, signo); + in_report.ReportError(error); } void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) { ScopedInErrorReport in_report; - Decorator d; - Printf("%s", d.Warning()); - char tname[128]; - u32 curr_tid = GetCurrentTidOrInvalid(); - Report("ERROR: AddressSanitizer: attempting double-free on %p in " - "thread T%d%s:\n", - addr, curr_tid, - ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); - Printf("%s", d.EndWarning()); - CHECK_GT(free_stack->size, 0); - GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); - stack.Print(); - DescribeHeapAddress(addr, 1); - ReportErrorSummary("double-free", &stack); + ErrorDoubleFree error(GetCurrentTidOrInvalid(), free_stack, addr); + in_report.ReportError(error); } void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size, BufferedStackTrace *free_stack) { ScopedInErrorReport in_report; - Decorator d; - Printf("%s", d.Warning()); - char tname[128]; - u32 curr_tid = GetCurrentTidOrInvalid(); - Report("ERROR: AddressSanitizer: new-delete-type-mismatch on %p in " - "thread T%d%s:\n", - addr, curr_tid, - ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); - Printf("%s object passed to delete has wrong type:\n", d.EndWarning()); - Printf(" size of the allocated type: %zd bytes;\n" - " size of the deallocated type: %zd bytes.\n", - asan_mz_size(reinterpret_cast(addr)), delete_size); - CHECK_GT(free_stack->size, 0); - GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); - stack.Print(); - DescribeHeapAddress(addr, 1); - ReportErrorSummary("new-delete-type-mismatch", &stack); - Report("HINT: if you don't care about these errors you may set " - "ASAN_OPTIONS=new_delete_type_mismatch=0\n"); + ErrorNewDeleteSizeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr, + delete_size); + in_report.ReportError(error); } void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack) { ScopedInErrorReport in_report; - Decorator d; - Printf("%s", d.Warning()); - char tname[128]; - u32 curr_tid = GetCurrentTidOrInvalid(); - Report("ERROR: AddressSanitizer: attempting free on address " - "which was not malloc()-ed: %p in thread T%d%s\n", addr, - curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); - Printf("%s", d.EndWarning()); - CHECK_GT(free_stack->size, 0); - GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); - stack.Print(); - DescribeHeapAddress(addr, 1); - ReportErrorSummary("bad-free", &stack); + ErrorFreeNotMalloced error(GetCurrentTidOrInvalid(), free_stack, addr); + in_report.ReportError(error); } void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack, AllocType alloc_type, AllocType dealloc_type) { - static const char *alloc_names[] = - {"INVALID", "malloc", "operator new", "operator new []"}; - static const char *dealloc_names[] = - {"INVALID", "free", "operator delete", "operator delete []"}; - CHECK_NE(alloc_type, dealloc_type); ScopedInErrorReport in_report; - Decorator d; - Printf("%s", d.Warning()); - Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n", - alloc_names[alloc_type], dealloc_names[dealloc_type], addr); - Printf("%s", d.EndWarning()); - CHECK_GT(free_stack->size, 0); - GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); - stack.Print(); - DescribeHeapAddress(addr, 1); - ReportErrorSummary("alloc-dealloc-mismatch", &stack); - Report("HINT: if you don't care about these errors you may set " - "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); + ErrorAllocTypeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr, + alloc_type, dealloc_type); + in_report.ReportError(error); } void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack) { ScopedInErrorReport in_report; - Decorator d; - Printf("%s", d.Warning()); - Report("ERROR: AddressSanitizer: attempting to call " - "malloc_usable_size() for pointer which is " - "not owned: %p\n", addr); - Printf("%s", d.EndWarning()); - stack->Print(); - DescribeHeapAddress(addr, 1); - ReportErrorSummary("bad-malloc_usable_size", stack); + ErrorMallocUsableSizeNotOwned error(GetCurrentTidOrInvalid(), stack, addr); + in_report.ReportError(error); } void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, BufferedStackTrace *stack) { ScopedInErrorReport in_report; - Decorator d; - Printf("%s", d.Warning()); - Report("ERROR: AddressSanitizer: attempting to call " - "__sanitizer_get_allocated_size() for pointer which is " - "not owned: %p\n", addr); - Printf("%s", d.EndWarning()); - stack->Print(); - DescribeHeapAddress(addr, 1); - ReportErrorSummary("bad-__sanitizer_get_allocated_size", stack); + ErrorSanitizerGetAllocatedSizeNotOwned error(GetCurrentTidOrInvalid(), stack, + addr); + in_report.ReportError(error); } void ReportStringFunctionMemoryRangesOverlap(const char *function, @@ -867,94 +301,43 @@ void ReportStringFunctionMemoryRangesOverlap(const char *function, const char *offset2, uptr length2, BufferedStackTrace *stack) { ScopedInErrorReport in_report; - Decorator d; - char bug_type[100]; - internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function); - Printf("%s", d.Warning()); - Report("ERROR: AddressSanitizer: %s: " - "memory ranges [%p,%p) and [%p, %p) overlap\n", \ - bug_type, offset1, offset1 + length1, offset2, offset2 + length2); - Printf("%s", d.EndWarning()); - stack->Print(); - DescribeAddress((uptr)offset1, length1, bug_type); - DescribeAddress((uptr)offset2, length2, bug_type); - ReportErrorSummary(bug_type, stack); + ErrorStringFunctionMemoryRangesOverlap error( + GetCurrentTidOrInvalid(), stack, (uptr)offset1, length1, (uptr)offset2, + length2, function); + in_report.ReportError(error); } void ReportStringFunctionSizeOverflow(uptr offset, uptr size, BufferedStackTrace *stack) { ScopedInErrorReport in_report; - Decorator d; - const char *bug_type = "negative-size-param"; - Printf("%s", d.Warning()); - Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", bug_type, size); - Printf("%s", d.EndWarning()); - stack->Print(); - DescribeAddress(offset, size, bug_type); - ReportErrorSummary(bug_type, stack); + ErrorStringFunctionSizeOverflow error(GetCurrentTidOrInvalid(), stack, offset, + size); + in_report.ReportError(error); } void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, uptr old_mid, uptr new_mid, BufferedStackTrace *stack) { ScopedInErrorReport in_report; - Report("ERROR: AddressSanitizer: bad parameters to " - "__sanitizer_annotate_contiguous_container:\n" - " beg : %p\n" - " end : %p\n" - " old_mid : %p\n" - " new_mid : %p\n", - beg, end, old_mid, new_mid); - uptr granularity = SHADOW_GRANULARITY; - if (!IsAligned(beg, granularity)) - Report("ERROR: beg is not aligned by %d\n", granularity); - stack->Print(); - ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack); + ErrorBadParamsToAnnotateContiguousContainer error( + GetCurrentTidOrInvalid(), stack, beg, end, old_mid, new_mid); + in_report.ReportError(error); } void ReportODRViolation(const __asan_global *g1, u32 stack_id1, const __asan_global *g2, u32 stack_id2) { ScopedInErrorReport in_report; - Decorator d; - Printf("%s", d.Warning()); - Report("ERROR: AddressSanitizer: odr-violation (%p):\n", g1->beg); - Printf("%s", d.EndWarning()); - InternalScopedString g1_loc(256), g2_loc(256); - PrintGlobalLocation(&g1_loc, *g1); - PrintGlobalLocation(&g2_loc, *g2); - Printf(" [1] size=%zd '%s' %s\n", g1->size, - MaybeDemangleGlobalName(g1->name), g1_loc.data()); - Printf(" [2] size=%zd '%s' %s\n", g2->size, - MaybeDemangleGlobalName(g2->name), g2_loc.data()); - if (stack_id1 && stack_id2) { - Printf("These globals were registered at these points:\n"); - Printf(" [1]:\n"); - StackDepotGet(stack_id1).Print(); - Printf(" [2]:\n"); - StackDepotGet(stack_id2).Print(); - } - Report("HINT: if you don't care about these errors you may set " - "ASAN_OPTIONS=detect_odr_violation=0\n"); - InternalScopedString error_msg(256); - error_msg.append("odr-violation: global '%s' at %s", - MaybeDemangleGlobalName(g1->name), g1_loc.data()); - ReportErrorSummary(error_msg.data()); + ErrorODRViolation error(GetCurrentTidOrInvalid(), g1, stack_id1, g2, + stack_id2); + in_report.ReportError(error); } // ----------------------- CheckForInvalidPointerPair ----------- {{{1 -static NOINLINE void -ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, uptr a1, uptr a2) { +static NOINLINE void ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, + uptr a1, uptr a2) { ScopedInErrorReport in_report; - const char *bug_type = "invalid-pointer-pair"; - Decorator d; - Printf("%s", d.Warning()); - Report("ERROR: AddressSanitizer: invalid-pointer-pair: %p %p\n", a1, a2); - Printf("%s", d.EndWarning()); - GET_STACK_TRACE_FATAL(pc, bp); - stack.Print(); - DescribeAddress(a1, 1, bug_type); - DescribeAddress(a2, 1, bug_type); - ReportErrorSummary(bug_type, &stack); + ErrorInvalidPointerPair error(GetCurrentTidOrInvalid(), pc, bp, sp, a1, a2); + in_report.ReportError(error); } static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) { @@ -963,26 +346,15 @@ static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) { uptr a2 = reinterpret_cast(p2); AsanChunkView chunk1 = FindHeapChunkByAddress(a1); AsanChunkView chunk2 = FindHeapChunkByAddress(a2); - bool valid1 = chunk1.IsValid(); - bool valid2 = chunk2.IsValid(); - if ((valid1 != valid2) || (valid1 && valid2 && !chunk1.Eq(chunk2))) { - GET_CALLER_PC_BP_SP; \ + bool valid1 = chunk1.IsAllocated(); + bool valid2 = chunk2.IsAllocated(); + if (!valid1 || !valid2 || !chunk1.Eq(chunk2)) { + GET_CALLER_PC_BP_SP; return ReportInvalidPointerPair(pc, bp, sp, a1, a2); } } // ----------------------- Mac-specific reports ----------------- {{{1 -void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name, - BufferedStackTrace *stack) { - // Just print a warning here. - Printf("free_common(%p) -- attempting to free unallocated memory.\n" - "AddressSanitizer is ignoring this error on Mac OS now.\n", - addr); - PrintZoneForPointer(addr, zone_ptr, zone_name); - stack->Print(); - DescribeHeapAddress(addr, 1); -} - void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, BufferedStackTrace *stack) { ScopedInErrorReport in_report; @@ -991,18 +363,7 @@ void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, addr); PrintZoneForPointer(addr, zone_ptr, zone_name); stack->Print(); - DescribeHeapAddress(addr, 1); -} - -void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, - BufferedStackTrace *stack) { - ScopedInErrorReport in_report; - Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n" - "This is an unrecoverable problem, exiting now.\n", - addr); - PrintZoneForPointer(addr, zone_ptr, zone_name); - stack->Print(); - DescribeHeapAddress(addr, 1); + DescribeAddressIfHeap(addr); } // -------------- SuppressErrorReport -------------- {{{1 @@ -1033,87 +394,10 @@ void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write, // The reaction to a non-zero value of exp is to be defined. (void)exp; - // Determine the error type. - const char *bug_descr = "unknown-crash"; - if (AddrIsInMem(addr)) { - u8 *shadow_addr = (u8*)MemToShadow(addr); - // If we are accessing 16 bytes, look at the second shadow byte. - if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY) - shadow_addr++; - // If we are in the partial right redzone, look at the next shadow byte. - if (*shadow_addr > 0 && *shadow_addr < 128) - shadow_addr++; - switch (*shadow_addr) { - case kAsanHeapLeftRedzoneMagic: - case kAsanHeapRightRedzoneMagic: - case kAsanArrayCookieMagic: - bug_descr = "heap-buffer-overflow"; - break; - case kAsanHeapFreeMagic: - bug_descr = "heap-use-after-free"; - break; - case kAsanStackLeftRedzoneMagic: - bug_descr = "stack-buffer-underflow"; - break; - case kAsanInitializationOrderMagic: - bug_descr = "initialization-order-fiasco"; - break; - case kAsanStackMidRedzoneMagic: - case kAsanStackRightRedzoneMagic: - case kAsanStackPartialRedzoneMagic: - bug_descr = "stack-buffer-overflow"; - break; - case kAsanStackAfterReturnMagic: - bug_descr = "stack-use-after-return"; - break; - case kAsanUserPoisonedMemoryMagic: - bug_descr = "use-after-poison"; - break; - case kAsanContiguousContainerOOBMagic: - bug_descr = "container-overflow"; - break; - case kAsanStackUseAfterScopeMagic: - bug_descr = "stack-use-after-scope"; - break; - case kAsanGlobalRedzoneMagic: - bug_descr = "global-buffer-overflow"; - break; - case kAsanIntraObjectRedzone: - bug_descr = "intra-object-overflow"; - break; - case kAsanAllocaLeftMagic: - case kAsanAllocaRightMagic: - bug_descr = "dynamic-stack-buffer-overflow"; - break; - } - } - - ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size, - bug_descr }; - ScopedInErrorReport in_report(&report, fatal); - - Decorator d; - Printf("%s", d.Warning()); - Report("ERROR: AddressSanitizer: %s on address " - "%p at pc %p bp %p sp %p\n", - bug_descr, (void*)addr, pc, bp, sp); - Printf("%s", d.EndWarning()); - - u32 curr_tid = GetCurrentTidOrInvalid(); - char tname[128]; - Printf("%s%s of size %zu at %p thread T%d%s%s\n", - d.Access(), - access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", - access_size, (void*)addr, curr_tid, - ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)), - d.EndAccess()); - - GET_STACK_TRACE_FATAL(pc, bp); - stack.Print(); - - DescribeAddress(addr, access_size, bug_descr); - ReportErrorSummary(bug_descr, &stack); - PrintShadowMemoryForAddress(addr); + ScopedInErrorReport in_report(fatal); + ErrorGeneric error(GetCurrentTidOrInvalid(), pc, bp, sp, addr, is_write, + access_size); + in_report.ReportError(error); } } // namespace __asan @@ -1129,52 +413,62 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, } void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { + BlockingMutexLock l(&error_message_buf_mutex); error_report_callback = callback; - if (callback) { - error_message_buffer_size = 1 << 16; - error_message_buffer = - (char*)MmapOrDie(error_message_buffer_size, __func__); - error_message_buffer_pos = 0; - } } void __asan_describe_address(uptr addr) { // Thread registry must be locked while we're describing an address. asanThreadRegistry().Lock(); - DescribeAddress(addr, 1, ""); + PrintAddressDescription(addr, 1, ""); asanThreadRegistry().Unlock(); } int __asan_report_present() { - return report_happened ? 1 : 0; + return ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric; } uptr __asan_get_report_pc() { - return report_data.pc; + if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) + return ScopedInErrorReport::CurrentError().Generic.pc; + return 0; } uptr __asan_get_report_bp() { - return report_data.bp; + if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) + return ScopedInErrorReport::CurrentError().Generic.bp; + return 0; } uptr __asan_get_report_sp() { - return report_data.sp; + if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) + return ScopedInErrorReport::CurrentError().Generic.sp; + return 0; } uptr __asan_get_report_address() { - return report_data.addr; + if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) + return ScopedInErrorReport::CurrentError() + .Generic.addr_description.Address(); + return 0; } int __asan_get_report_access_type() { - return report_data.is_write ? 1 : 0; + if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) + return ScopedInErrorReport::CurrentError().Generic.is_write; + return 0; } uptr __asan_get_report_access_size() { - return report_data.access_size; + if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) + return ScopedInErrorReport::CurrentError().Generic.access_size; + return 0; } const char *__asan_get_report_description() { - return report_data.description; + if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric) + return ScopedInErrorReport::CurrentError().Generic.bug_descr; + return nullptr; } extern "C" { diff --git a/libsanitizer/asan/asan_report.h b/libsanitizer/asan/asan_report.h index f2a1815..111b840 100644 --- a/libsanitizer/asan/asan_report.h +++ b/libsanitizer/asan/asan_report.h @@ -23,34 +23,28 @@ struct StackVarDescr { uptr name_len; }; -struct AddressDescription { - char *name; - uptr name_size; - uptr region_address; - uptr region_size; - const char *region_kind; -}; - // Returns the number of globals close to the provided address and copies // them to "globals" array. int GetGlobalsForAddress(uptr addr, __asan_global *globals, u32 *reg_sites, int max_globals); -bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr); + +const char *MaybeDemangleGlobalName(const char *name); +void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g); +void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g); + +void PrintMemoryByte(InternalScopedString *str, const char *before, u8 byte, + bool in_shadow, const char *after = "\n"); + // The following functions prints address description depending // on the memory type (shadow/heap/stack/global). -void DescribeHeapAddress(uptr addr, uptr access_size); -bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr = nullptr, - bool print = true); bool ParseFrameDescription(const char *frame_descr, InternalMmapVector *vars); -bool DescribeAddressIfStack(uptr addr, uptr access_size); -void DescribeThread(AsanThreadContext *context); // Different kinds of error reports. void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write, uptr access_size, u32 exp, bool fatal); void ReportStackOverflow(const SignalContext &sig); -void ReportDeadlySignal(const char *description, const SignalContext &sig); +void ReportDeadlySignal(int signo, const SignalContext &sig); void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size, BufferedStackTrace *free_stack); void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack); @@ -75,8 +69,6 @@ void ReportODRViolation(const __asan_global *g1, u32 stack_id1, const __asan_global *g2, u32 stack_id2); // Mac-specific errors and warnings. -void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name, - BufferedStackTrace *stack); void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, BufferedStackTrace *stack); diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc index 7b5a101..ba8cbb3 100644 --- a/libsanitizer/asan/asan_rtl.cc +++ b/libsanitizer/asan/asan_rtl.cc @@ -31,6 +31,7 @@ #include "ubsan/ubsan_init.h" #include "ubsan/ubsan_platform.h" +uptr __asan_shadow_memory_dynamic_address; // Global interface symbol. int __asan_option_detect_stack_use_after_return; // Global interface symbol. uptr *__asan_test_only_reported_buggy_pointer; // Used only for testing asan. @@ -85,8 +86,8 @@ void ShowStatsAndAbort() { // Reserve memory range [beg, end]. // We need to use inclusive range because end+1 may not be representable. void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) { - CHECK_EQ((beg % GetPageSizeCached()), 0); - CHECK_EQ(((end + 1) % GetPageSizeCached()), 0); + CHECK_EQ((beg % GetMmapGranularity()), 0); + CHECK_EQ(((end + 1) % GetMmapGranularity()), 0); uptr size = end - beg + 1; DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb. void *res = MmapFixedNoReserve(beg, size, name); @@ -262,6 +263,7 @@ static NOINLINE void force_interface_symbols() { volatile int fake_condition = 0; // prevent dead condition elimination. // __asan_report_* functions are noreturn, so we need a switch to prevent // the compiler from removing any of them. + // clang-format off switch (fake_condition) { case 1: __asan_report_load1(0); break; case 2: __asan_report_load2(0); break; @@ -301,7 +303,14 @@ static NOINLINE void force_interface_symbols() { case 37: __asan_unpoison_stack_memory(0, 0); break; case 38: __asan_region_is_poisoned(0, 0); break; case 39: __asan_describe_address(0); break; + case 40: __asan_set_shadow_00(0, 0); break; + case 41: __asan_set_shadow_f1(0, 0); break; + case 42: __asan_set_shadow_f2(0, 0); break; + case 43: __asan_set_shadow_f3(0, 0); break; + case 44: __asan_set_shadow_f5(0, 0); break; + case 45: __asan_set_shadow_f8(0, 0); break; } + // clang-format on } static void asan_atexit() { @@ -319,26 +328,39 @@ static void InitializeHighMemEnd() { kHighMemEnd = GetMaxVirtualAddress(); // Increase kHighMemEnd to make sure it's properly // aligned together with kHighMemBeg: - kHighMemEnd |= SHADOW_GRANULARITY * GetPageSizeCached() - 1; + kHighMemEnd |= SHADOW_GRANULARITY * GetMmapGranularity() - 1; #endif // !ASAN_FIXED_MAPPING - CHECK_EQ((kHighMemBeg % GetPageSizeCached()), 0); + CHECK_EQ((kHighMemBeg % GetMmapGranularity()), 0); } static void ProtectGap(uptr addr, uptr size) { - if (!flags()->protect_shadow_gap) + if (!flags()->protect_shadow_gap) { + // The shadow gap is unprotected, so there is a chance that someone + // is actually using this memory. Which means it needs a shadow... + uptr GapShadowBeg = RoundDownTo(MEM_TO_SHADOW(addr), GetPageSizeCached()); + uptr GapShadowEnd = + RoundUpTo(MEM_TO_SHADOW(addr + size), GetPageSizeCached()) - 1; + if (Verbosity()) + Printf("protect_shadow_gap=0:" + " not protecting shadow gap, allocating gap's shadow\n" + "|| `[%p, %p]` || ShadowGap's shadow ||\n", GapShadowBeg, + GapShadowEnd); + ReserveShadowMemoryRange(GapShadowBeg, GapShadowEnd, + "unprotected gap shadow"); return; - void *res = MmapNoAccess(addr, size, "shadow gap"); + } + void *res = MmapFixedNoAccess(addr, size, "shadow gap"); if (addr == (uptr)res) return; // A few pages at the start of the address space can not be protected. // But we really want to protect as much as possible, to prevent this memory // being returned as a result of a non-FIXED mmap(). if (addr == kZeroBaseShadowStart) { - uptr step = GetPageSizeCached(); + uptr step = GetMmapGranularity(); while (size > step && addr < kZeroBaseMaxShadowStart) { addr += step; size -= step; - void *res = MmapNoAccess(addr, size, "shadow gap"); + void *res = MmapFixedNoAccess(addr, size, "shadow gap"); if (addr == (uptr)res) return; } @@ -421,10 +443,13 @@ static void AsanInitInternal() { AsanCheckIncompatibleRT(); AsanCheckDynamicRTPrereqs(); + AvoidCVE_2016_2143(); SetCanPoisonMemory(flags()->poison_heap); SetMallocContextSize(common_flags()->malloc_context_size); + InitializePlatformExceptionHandlers(); + InitializeHighMemEnd(); // Make sure we are not statically linked. @@ -437,7 +462,6 @@ static void AsanInitInternal() { __sanitizer_set_report_path(common_flags()->log_path); - // Enable UAR detection, if required. __asan_option_detect_stack_use_after_return = flags()->detect_stack_use_after_return; @@ -456,7 +480,30 @@ static void AsanInitInternal() { ReplaceSystemMalloc(); + // Set the shadow memory address to uninitialized. + __asan_shadow_memory_dynamic_address = kDefaultShadowSentinel; + uptr shadow_start = kLowShadowBeg; + // Detect if a dynamic shadow address must used and find a available location + // when necessary. When dynamic address is used, the macro |kLowShadowBeg| + // expands to |__asan_shadow_memory_dynamic_address| which is + // |kDefaultShadowSentinel|. + if (shadow_start == kDefaultShadowSentinel) { + __asan_shadow_memory_dynamic_address = 0; + CHECK_EQ(0, kLowShadowBeg); + + uptr granularity = GetMmapGranularity(); + uptr alignment = 8 * granularity; + uptr left_padding = granularity; + uptr space_size = kHighShadowEnd + left_padding; + + shadow_start = FindAvailableMemoryRange(space_size, alignment, granularity); + CHECK_NE((uptr)0, shadow_start); + CHECK(IsAligned(shadow_start, alignment)); + } + // Update the shadow memory address (potentially) used by instrumentation. + __asan_shadow_memory_dynamic_address = shadow_start; + if (kLowShadowBeg) shadow_start -= GetMmapGranularity(); bool full_shadow_is_available = @@ -545,12 +592,12 @@ static void AsanInitInternal() { force_interface_symbols(); // no-op. SanitizerInitializeUnwinder(); -#if CAN_SANITIZE_LEAKS - __lsan::InitCommonLsan(); - if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { - Atexit(__lsan::DoLeakCheck); + if (CAN_SANITIZE_LEAKS) { + __lsan::InitCommonLsan(); + if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { + Atexit(__lsan::DoLeakCheck); + } } -#endif // CAN_SANITIZE_LEAKS #if CAN_SANITIZE_UB __ubsan::InitAsPlugin(); @@ -558,6 +605,15 @@ static void AsanInitInternal() { InitializeSuppressions(); + if (CAN_SANITIZE_LEAKS) { + // LateInitialize() calls dlsym, which can allocate an error string buffer + // in the TLS. Let's ignore the allocation to avoid reporting a leak. + __lsan::ScopedInterceptorDisabler disabler; + Symbolizer::LateInitialize(); + } else { + Symbolizer::LateInitialize(); + } + VReport(1, "AddressSanitizer Init done\n"); } @@ -587,6 +643,9 @@ static AsanInitializer asan_initializer; using namespace __asan; // NOLINT void NOINLINE __asan_handle_no_return() { + if (asan_init_is_running) + return; + int local_stack; AsanThread *curr_thread = GetCurrentThread(); uptr PageSize = GetPageSizeCached(); @@ -611,7 +670,7 @@ void NOINLINE __asan_handle_no_return() { "stack top: %p; bottom %p; size: %p (%zd)\n" "False positive error reports may follow\n" "For details see " - "http://code.google.com/p/address-sanitizer/issues/detail?id=189\n", + "https://github.com/google/sanitizers/issues/189\n", top, bottom, top - bottom, top - bottom); return; } diff --git a/libsanitizer/asan/asan_scariness_score.h b/libsanitizer/asan/asan_scariness_score.h new file mode 100644 index 0000000..d72fce6 --- /dev/null +++ b/libsanitizer/asan/asan_scariness_score.h @@ -0,0 +1,72 @@ +//===-- asan_scariness_score.h ----------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Compute the level of scariness of the error message. +// Don't expect any deep science here, just a set of heuristics that suggest +// that e.g. 1-byte-read-global-buffer-overflow is less scary than +// 8-byte-write-stack-use-after-return. +// +// Every error report has one or more features, such as memory access size, +// type (read or write), type of accessed memory (e.g. free-d heap, or a global +// redzone), etc. Every such feature has an int score and a string description. +// The overall score is the sum of all feature scores and the description +// is a concatenation of feature descriptions. +// Examples: +// 17 (4-byte-read-heap-buffer-overflow) +// 65 (multi-byte-write-stack-use-after-return) +// 10 (null-deref) +// +//===----------------------------------------------------------------------===// + +#ifndef ASAN_SCARINESS_SCORE_H +#define ASAN_SCARINESS_SCORE_H + +#include "asan_flags.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libc.h" + +namespace __asan { +struct ScarinessScoreBase { + void Clear() { + descr[0] = 0; + score = 0; + } + void Scare(int add_to_score, const char *reason) { + if (descr[0]) + internal_strlcat(descr, "-", sizeof(descr)); + internal_strlcat(descr, reason, sizeof(descr)); + score += add_to_score; + }; + int GetScore() const { return score; } + const char *GetDescription() const { return descr; } + void Print() { + if (score && flags()->print_scariness) + Printf("SCARINESS: %d (%s)\n", score, descr); + } + static void PrintSimple(int score, const char *descr) { + ScarinessScoreBase SSB; + SSB.Clear(); + SSB.Scare(score, descr); + SSB.Print(); + } + + private: + int score; + char descr[1024]; +}; + +struct ScarinessScore : ScarinessScoreBase { + ScarinessScore() { + Clear(); + } +}; + +} // namespace __asan + +#endif // ASAN_SCARINESS_SCORE_H diff --git a/libsanitizer/asan/asan_stack.h b/libsanitizer/asan/asan_stack.h index 30dc592..2b87381 100644 --- a/libsanitizer/asan/asan_stack.h +++ b/libsanitizer/asan/asan_stack.h @@ -46,7 +46,10 @@ void GetStackTraceWithPcBpAndContext(BufferedStackTrace *stack, uptr max_depth, uptr stack_top = t->stack_top(); uptr stack_bottom = t->stack_bottom(); ScopedUnwinding unwind_scope(t); - stack->Unwind(max_depth, pc, bp, context, stack_top, stack_bottom, fast); + if (!SANITIZER_MIPS || IsValidFrame(bp, stack_top, stack_bottom)) { + stack->Unwind(max_depth, pc, bp, context, stack_top, stack_bottom, + fast); + } } else if (!t && !fast) { /* If GetCurrentThread() has failed, try to do slow unwind anyways. */ stack->Unwind(max_depth, pc, bp, context, 0, 0, false); diff --git a/libsanitizer/asan/asan_suppressions.cc b/libsanitizer/asan/asan_suppressions.cc index c94fff0..1dc9d47 100644 --- a/libsanitizer/asan/asan_suppressions.cc +++ b/libsanitizer/asan/asan_suppressions.cc @@ -87,6 +87,7 @@ bool IsStackTraceSuppressed(const StackTrace *stack) { if (suppression_ctx->HasSuppressionType(kInterceptorViaFunction)) { SymbolizedStack *frames = symbolizer->SymbolizePC(addr); + CHECK(frames); for (SymbolizedStack *cur = frames; cur; cur = cur->next) { const char *function_name = cur->info.function; if (!function_name) { diff --git a/libsanitizer/asan/asan_thread.cc b/libsanitizer/asan/asan_thread.cc index 92f968b..818e126 100644 --- a/libsanitizer/asan/asan_thread.cc +++ b/libsanitizer/asan/asan_thread.cc @@ -118,6 +118,77 @@ void AsanThread::Destroy() { DTLS_Destroy(); } +void AsanThread::StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom, + uptr size) { + if (atomic_load(&stack_switching_, memory_order_relaxed)) { + Report("ERROR: starting fiber switch while in fiber switch\n"); + Die(); + } + + next_stack_bottom_ = bottom; + next_stack_top_ = bottom + size; + atomic_store(&stack_switching_, 1, memory_order_release); + + FakeStack *current_fake_stack = fake_stack_; + if (fake_stack_save) + *fake_stack_save = fake_stack_; + fake_stack_ = nullptr; + SetTLSFakeStack(nullptr); + // if fake_stack_save is null, the fiber will die, delete the fakestack + if (!fake_stack_save && current_fake_stack) + current_fake_stack->Destroy(this->tid()); +} + +void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save, + uptr *bottom_old, + uptr *size_old) { + if (!atomic_load(&stack_switching_, memory_order_relaxed)) { + Report("ERROR: finishing a fiber switch that has not started\n"); + Die(); + } + + if (fake_stack_save) { + SetTLSFakeStack(fake_stack_save); + fake_stack_ = fake_stack_save; + } + + if (bottom_old) + *bottom_old = stack_bottom_; + if (size_old) + *size_old = stack_top_ - stack_bottom_; + stack_bottom_ = next_stack_bottom_; + stack_top_ = next_stack_top_; + atomic_store(&stack_switching_, 0, memory_order_release); + next_stack_top_ = 0; + next_stack_bottom_ = 0; +} + +inline AsanThread::StackBounds AsanThread::GetStackBounds() const { + if (!atomic_load(&stack_switching_, memory_order_acquire)) + return StackBounds{stack_bottom_, stack_top_}; // NOLINT + char local; + const uptr cur_stack = (uptr)&local; + // Note: need to check next stack first, because FinishSwitchFiber + // may be in process of overwriting stack_top_/bottom_. But in such case + // we are already on the next stack. + if (cur_stack >= next_stack_bottom_ && cur_stack < next_stack_top_) + return StackBounds{next_stack_bottom_, next_stack_top_}; // NOLINT + return StackBounds{stack_bottom_, stack_top_}; // NOLINT +} + +uptr AsanThread::stack_top() { + return GetStackBounds().top; +} + +uptr AsanThread::stack_bottom() { + return GetStackBounds().bottom; +} + +uptr AsanThread::stack_size() { + const auto bounds = GetStackBounds(); + return bounds.top - bounds.bottom; +} + // We want to create the FakeStack lazyly on the first use, but not eralier // than the stack size is known and the procedure has to be async-signal safe. FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() { @@ -148,6 +219,8 @@ FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() { } void AsanThread::Init() { + next_stack_top_ = next_stack_bottom_ = 0; + atomic_store(&stack_switching_, false, memory_order_release); fake_stack_ = nullptr; // Will be initialized lazily if needed. CHECK_EQ(this->stack_size(), 0U); SetThreadStackAndTls(); @@ -193,10 +266,12 @@ thread_return_t AsanThread::ThreadStart( void AsanThread::SetThreadStackAndTls() { uptr tls_size = 0; - GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size_, &tls_begin_, - &tls_size); - stack_top_ = stack_bottom_ + stack_size_; + uptr stack_size = 0; + GetThreadStackAndTls(tid() == 0, const_cast(&stack_bottom_), + const_cast(&stack_size), &tls_begin_, &tls_size); + stack_top_ = stack_bottom_ + stack_size; tls_end_ = tls_begin_ + tls_size; + dtls_ = DTLS_Get(); int local; CHECK(AddrIsInStack((uptr)&local)); @@ -247,6 +322,11 @@ bool AsanThread::GetStackFrameAccessByAddr(uptr addr, return true; } +bool AsanThread::AddrIsInStack(uptr addr) { + const auto bounds = GetStackBounds(); + return addr >= bounds.bottom && addr < bounds.top; +} + static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base, void *addr) { AsanThreadContext *tctx = static_cast(tctx_base); @@ -269,7 +349,7 @@ AsanThread *GetCurrentThread() { // limits, so only do this magic on Android, and only if the found thread // is the main thread. AsanThreadContext *tctx = GetThreadContextByTidLocked(0); - if (ThreadStackContainsAddress(tctx, &context)) { + if (tctx && ThreadStackContainsAddress(tctx, &context)) { SetCurrentThread(tctx->thread); return tctx->thread; } @@ -320,8 +400,8 @@ __asan::AsanThread *GetAsanThreadByOsIDLocked(uptr os_id) { // --- Implementation of LSan-specific functions --- {{{1 namespace __lsan { bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, - uptr *tls_begin, uptr *tls_end, - uptr *cache_begin, uptr *cache_end) { + uptr *tls_begin, uptr *tls_end, uptr *cache_begin, + uptr *cache_end, DTLS **dtls) { __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id); if (!t) return false; *stack_begin = t->stack_bottom(); @@ -331,6 +411,7 @@ bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, // ASan doesn't keep allocator caches in TLS, so these are unused. *cache_begin = 0; *cache_end = 0; + *dtls = t->dtls(); return true; } @@ -353,3 +434,33 @@ void EnsureMainThreadIDIsCorrect() { __asan::EnsureMainThreadIDIsCorrect(); } } // namespace __lsan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_start_switch_fiber(void **fakestacksave, const void *bottom, + uptr size) { + AsanThread *t = GetCurrentThread(); + if (!t) { + VReport(1, "__asan_start_switch_fiber called from unknown thread\n"); + return; + } + t->StartSwitchFiber((FakeStack**)fakestacksave, (uptr)bottom, size); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_finish_switch_fiber(void* fakestack, + const void **bottom_old, + uptr *size_old) { + AsanThread *t = GetCurrentThread(); + if (!t) { + VReport(1, "__asan_finish_switch_fiber called from unknown thread\n"); + return; + } + t->FinishSwitchFiber((FakeStack*)fakestack, + (uptr*)bottom_old, + (uptr*)size_old); +} +} diff --git a/libsanitizer/asan/asan_thread.h b/libsanitizer/asan/asan_thread.h index 27a3367..c51a58a 100644 --- a/libsanitizer/asan/asan_thread.h +++ b/libsanitizer/asan/asan_thread.h @@ -21,6 +21,10 @@ #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_thread_registry.h" +namespace __sanitizer { +struct DTLS; +} // namespace __sanitizer + namespace __asan { const u32 kInvalidTid = 0xffffff; // Must fit into 24 bits. @@ -60,11 +64,12 @@ class AsanThread { thread_return_t ThreadStart(uptr os_id, atomic_uintptr_t *signal_thread_is_registered); - uptr stack_top() { return stack_top_; } - uptr stack_bottom() { return stack_bottom_; } - uptr stack_size() { return stack_size_; } + uptr stack_top(); + uptr stack_bottom(); + uptr stack_size(); uptr tls_begin() { return tls_begin_; } uptr tls_end() { return tls_end_; } + DTLS *dtls() { return dtls_; } u32 tid() { return context_->tid; } AsanThreadContext *context() { return context_; } void set_context(AsanThreadContext *context) { context_ = context; } @@ -76,9 +81,7 @@ class AsanThread { }; bool GetStackFrameAccessByAddr(uptr addr, StackFrameAccess *access); - bool AddrIsInStack(uptr addr) { - return addr >= stack_bottom_ && addr < stack_top_; - } + bool AddrIsInStack(uptr addr); void DeleteFakeStack(int tid) { if (!fake_stack_) return; @@ -88,13 +91,20 @@ class AsanThread { t->Destroy(tid); } + void StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom, uptr size); + void FinishSwitchFiber(FakeStack *fake_stack_save, uptr *bottom_old, + uptr *size_old); + bool has_fake_stack() { - return (reinterpret_cast(fake_stack_) > 1); + return !atomic_load(&stack_switching_, memory_order_relaxed) && + (reinterpret_cast(fake_stack_) > 1); } FakeStack *fake_stack() { if (!__asan_option_detect_stack_use_after_return) return nullptr; + if (atomic_load(&stack_switching_, memory_order_relaxed)) + return nullptr; if (!has_fake_stack()) return AsyncSignalSafeLazyInitFakeStack(); return fake_stack_; @@ -120,16 +130,27 @@ class AsanThread { void ClearShadowForThreadStackAndTLS(); FakeStack *AsyncSignalSafeLazyInitFakeStack(); + struct StackBounds { + uptr bottom; + uptr top; + }; + StackBounds GetStackBounds() const; + AsanThreadContext *context_; thread_callback_t start_routine_; void *arg_; + uptr stack_top_; uptr stack_bottom_; - // stack_size_ == stack_top_ - stack_bottom_; - // It needs to be set in a async-signal-safe manner. - uptr stack_size_; + // these variables are used when the thread is about to switch stack + uptr next_stack_top_; + uptr next_stack_bottom_; + // true if switching is in progress + atomic_uint8_t stack_switching_; + uptr tls_begin_; uptr tls_end_; + DTLS *dtls_; FakeStack *fake_stack_; AsanThreadLocalMallocStorage malloc_storage_; diff --git a/libsanitizer/asan/asan_win.cc b/libsanitizer/asan/asan_win.cc index 6c12523..efd82bf 100644 --- a/libsanitizer/asan/asan_win.cc +++ b/libsanitizer/asan/asan_win.cc @@ -22,6 +22,7 @@ #include "asan_report.h" #include "asan_stack.h" #include "asan_thread.h" +#include "asan_mapping.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_mutex.h" @@ -34,7 +35,13 @@ int __asan_should_detect_stack_use_after_return() { return __asan_option_detect_stack_use_after_return; } -// -------------------- A workaround for the abscence of weak symbols ----- {{{ +SANITIZER_INTERFACE_ATTRIBUTE +uptr __asan_get_shadow_memory_dynamic_address() { + __asan_init(); + return __asan_shadow_memory_dynamic_address; +} + +// -------------------- A workaround for the absence of weak symbols ----- {{{ // We don't have a direct equivalent of weak symbols when using MSVC, but we can // use the /alternatename directive to tell the linker to default a specific // symbol to a specific value, which works nicely for allocator hooks and @@ -44,21 +51,49 @@ void __sanitizer_default_free_hook(void *ptr) { } const char* __asan_default_default_options() { return ""; } const char* __asan_default_default_suppressions() { return ""; } void __asan_default_on_error() {} +// 64-bit msvc will not prepend an underscore for symbols. +#ifdef _WIN64 +#pragma comment(linker, "/alternatename:__sanitizer_malloc_hook=__sanitizer_default_malloc_hook") // NOLINT +#pragma comment(linker, "/alternatename:__sanitizer_free_hook=__sanitizer_default_free_hook") // NOLINT +#pragma comment(linker, "/alternatename:__asan_default_options=__asan_default_default_options") // NOLINT +#pragma comment(linker, "/alternatename:__asan_default_suppressions=__asan_default_default_suppressions") // NOLINT +#pragma comment(linker, "/alternatename:__asan_on_error=__asan_default_on_error") // NOLINT +#else #pragma comment(linker, "/alternatename:___sanitizer_malloc_hook=___sanitizer_default_malloc_hook") // NOLINT #pragma comment(linker, "/alternatename:___sanitizer_free_hook=___sanitizer_default_free_hook") // NOLINT #pragma comment(linker, "/alternatename:___asan_default_options=___asan_default_default_options") // NOLINT #pragma comment(linker, "/alternatename:___asan_default_suppressions=___asan_default_default_suppressions") // NOLINT #pragma comment(linker, "/alternatename:___asan_on_error=___asan_default_on_error") // NOLINT +#endif // }}} } // extern "C" -// ---------------------- Windows-specific inteceptors ---------------- {{{ +// ---------------------- Windows-specific interceptors ---------------- {{{ +INTERCEPTOR_WINAPI(void, RtlRaiseException, EXCEPTION_RECORD *ExceptionRecord) { + CHECK(REAL(RtlRaiseException)); + // This is a noreturn function, unless it's one of the exceptions raised to + // communicate with the debugger, such as the one from OutputDebugString. + if (ExceptionRecord->ExceptionCode != DBG_PRINTEXCEPTION_C) + __asan_handle_no_return(); + REAL(RtlRaiseException)(ExceptionRecord); +} + INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) { CHECK(REAL(RaiseException)); __asan_handle_no_return(); REAL(RaiseException)(a, b, c, d); } +#ifdef _WIN64 + +INTERCEPTOR_WINAPI(int, __C_specific_handler, void *a, void *b, void *c, void *d) { // NOLINT + CHECK(REAL(__C_specific_handler)); + __asan_handle_no_return(); + return REAL(__C_specific_handler)(a, b, c, d); +} + +#else + INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) { CHECK(REAL(_except_handler3)); __asan_handle_no_return(); @@ -74,6 +109,7 @@ INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) { __asan_handle_no_return(); return REAL(_except_handler4)(a, b, c, d); } +#endif static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { AsanThread *t = (AsanThread*)arg; @@ -99,52 +135,33 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread, asan_thread_start, t, thr_flags, tid); } -namespace { -BlockingMutex mu_for_thread_tracking(LINKER_INITIALIZED); - -void EnsureWorkerThreadRegistered() { - // FIXME: GetCurrentThread relies on TSD, which might not play well with - // system thread pools. We might want to use something like reference - // counting to zero out GetCurrentThread() underlying storage when the last - // work item finishes? Or can we disable reclaiming of threads in the pool? - BlockingMutexLock l(&mu_for_thread_tracking); - if (__asan::GetCurrentThread()) - return; - - AsanThread *t = AsanThread::Create( - /* start_routine */ nullptr, /* arg */ nullptr, - /* parent_tid */ -1, /* stack */ nullptr, /* detached */ true); - t->Init(); - asanThreadRegistry().StartThread(t->tid(), 0, 0); - SetCurrentThread(t); -} -} // namespace - -INTERCEPTOR_WINAPI(DWORD, NtWaitForWorkViaWorkerFactory, DWORD a, DWORD b) { - // NtWaitForWorkViaWorkerFactory is called from system worker pool threads to - // query work scheduled by BindIoCompletionCallback, QueueUserWorkItem, etc. - // System worker pool threads are created at arbitraty point in time and - // without using CreateThread, so we wrap NtWaitForWorkViaWorkerFactory - // instead and don't register a specific parent_tid/stack. - EnsureWorkerThreadRegistered(); - return REAL(NtWaitForWorkViaWorkerFactory)(a, b); -} - // }}} namespace __asan { void InitializePlatformInterceptors() { ASAN_INTERCEPT_FUNC(CreateThread); - ASAN_INTERCEPT_FUNC(RaiseException); + +#ifdef _WIN64 + ASAN_INTERCEPT_FUNC(__C_specific_handler); +#else ASAN_INTERCEPT_FUNC(_except_handler3); ASAN_INTERCEPT_FUNC(_except_handler4); +#endif + + // Try to intercept kernel32!RaiseException, and if that fails, intercept + // ntdll!RtlRaiseException instead. + if (!::__interception::OverrideFunction("RaiseException", + (uptr)WRAP(RaiseException), + (uptr *)&REAL(RaiseException))) { + CHECK(::__interception::OverrideFunction("RtlRaiseException", + (uptr)WRAP(RtlRaiseException), + (uptr *)&REAL(RtlRaiseException))); + } +} - // NtWaitForWorkViaWorkerFactory is always linked dynamically. - CHECK(::__interception::OverrideFunction( - "NtWaitForWorkViaWorkerFactory", - (uptr)WRAP(NtWaitForWorkViaWorkerFactory), - (uptr *)&REAL(NtWaitForWorkViaWorkerFactory))); +void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { + UNIMPLEMENTED(); } // ---------------------- TSD ---------------- {{{ @@ -173,14 +190,6 @@ void PlatformTSDDtor(void *tsd) { // }}} // ---------------------- Various stuff ---------------- {{{ -void DisableReexec() { - // No need to re-exec on Windows. -} - -void MaybeReexec() { - // No need to re-exec on Windows. -} - void *AsanDoesNotSupportStaticLinkage() { #if defined(_DEBUG) #error Please build the runtime with a non-debug CRT: /MD or /MT @@ -200,20 +209,95 @@ void AsanOnDeadlySignal(int, void *siginfo, void *context) { UNIMPLEMENTED(); } +#if SANITIZER_WINDOWS64 +// Exception handler for dealing with shadow memory. +static LONG CALLBACK +ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) { + uptr page_size = GetPageSizeCached(); + // Only handle access violations. + if (exception_pointers->ExceptionRecord->ExceptionCode != + EXCEPTION_ACCESS_VIOLATION) { + return EXCEPTION_CONTINUE_SEARCH; + } + + // Only handle access violations that land within the shadow memory. + uptr addr = + (uptr)(exception_pointers->ExceptionRecord->ExceptionInformation[1]); + + // Check valid shadow range. + if (!AddrIsInShadow(addr)) return EXCEPTION_CONTINUE_SEARCH; + + // This is an access violation while trying to read from the shadow. Commit + // the relevant page and let execution continue. + + // Determine the address of the page that is being accessed. + uptr page = RoundDownTo(addr, page_size); + + // Query the existing page. + MEMORY_BASIC_INFORMATION mem_info = {}; + if (::VirtualQuery((LPVOID)page, &mem_info, sizeof(mem_info)) == 0) + return EXCEPTION_CONTINUE_SEARCH; + + // Commit the page. + uptr result = + (uptr)::VirtualAlloc((LPVOID)page, page_size, MEM_COMMIT, PAGE_READWRITE); + if (result != page) return EXCEPTION_CONTINUE_SEARCH; + + // The page mapping succeeded, so continue execution as usual. + return EXCEPTION_CONTINUE_EXECUTION; +} + +#endif + +void InitializePlatformExceptionHandlers() { +#if SANITIZER_WINDOWS64 + // On Win64, we map memory on demand with access violation handler. + // Install our exception handler. + CHECK(AddVectoredExceptionHandler(TRUE, &ShadowExceptionHandler)); +#endif +} + static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler; +// Check based on flags if we should report this exception. +static bool ShouldReportDeadlyException(unsigned code) { + switch (code) { + case EXCEPTION_ACCESS_VIOLATION: + case EXCEPTION_IN_PAGE_ERROR: + return common_flags()->handle_segv; + case EXCEPTION_BREAKPOINT: + case EXCEPTION_ILLEGAL_INSTRUCTION: { + return common_flags()->handle_sigill; + } + } + return false; +} + +// Return the textual name for this exception. +const char *DescribeSignalOrException(int signo) { + unsigned code = signo; + // Get the string description of the exception if this is a known deadly + // exception. + switch (code) { + case EXCEPTION_ACCESS_VIOLATION: + return "access-violation"; + case EXCEPTION_IN_PAGE_ERROR: + return "in-page-error"; + case EXCEPTION_BREAKPOINT: + return "breakpoint"; + case EXCEPTION_ILLEGAL_INSTRUCTION: + return "illegal-instruction"; + } + return nullptr; +} + static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) { EXCEPTION_RECORD *exception_record = info->ExceptionRecord; CONTEXT *context = info->ContextRecord; - if (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION || - exception_record->ExceptionCode == EXCEPTION_IN_PAGE_ERROR) { - const char *description = - (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) - ? "access-violation" - : "in-page-error"; + if (ShouldReportDeadlyException(exception_record->ExceptionCode)) { SignalContext sig = SignalContext::Create(exception_record, context); - ReportDeadlySignal(description, sig); + ReportDeadlySignal(exception_record->ExceptionCode, sig); } // FIXME: Handle EXCEPTION_STACK_OVERFLOW here. @@ -248,10 +332,16 @@ int __asan_set_seh_filter() { } #if !ASAN_DYNAMIC -// Put a pointer to __asan_set_seh_filter at the end of the global list -// of C initializers, after the default EH is set by the CRT. -#pragma section(".CRT$XIZ", long, read) // NOLINT -__declspec(allocate(".CRT$XIZ")) +// The CRT runs initializers in this order: +// - C initializers, from XIA to XIZ +// - C++ initializers, from XCA to XCZ +// Prior to 2015, the CRT set the unhandled exception filter at priority XIY, +// near the end of C initialization. Starting in 2015, it was moved to the +// beginning of C++ initialization. We set our priority to XCAB to run +// immediately after the CRT runs. This way, our exception filter is called +// first and we can delegate to their filter if appropriate. +#pragma section(".CRT$XCAB", long, read) // NOLINT +__declspec(allocate(".CRT$XCAB")) int (*__intercept_seh)() = __asan_set_seh_filter; #endif // }}} diff --git a/libsanitizer/asan/asan_win_dll_thunk.cc b/libsanitizer/asan/asan_win_dll_thunk.cc index 691aaf3..f7c9a37 100644 --- a/libsanitizer/asan/asan_win_dll_thunk.cc +++ b/libsanitizer/asan/asan_win_dll_thunk.cc @@ -10,16 +10,16 @@ // This file defines a family of thunks that should be statically linked into // the DLLs that have ASan instrumentation in order to delegate the calls to the // shared runtime that lives in the main binary. -// See https://code.google.com/p/address-sanitizer/issues/detail?id=209 for the -// details. +// See https://github.com/google/sanitizers/issues/209 for the details. //===----------------------------------------------------------------------===// -// Only compile this code when buidling asan_dll_thunk.lib +// Only compile this code when building asan_dll_thunk.lib // Using #ifdef rather than relying on Makefiles etc. // simplifies the build procedure. #ifdef ASAN_DLL_THUNK #include "asan_init_version.h" #include "interception/interception.h" +#include "sanitizer_common/sanitizer_platform_interceptors.h" // ---------- Function interception helper functions and macros ----------- {{{1 extern "C" { @@ -28,6 +28,8 @@ void *__stdcall GetProcAddress(void *module, const char *proc_name); void abort(); } +using namespace __sanitizer; + static uptr getRealProcAddressOrDie(const char *name) { uptr ret = __interception::InternalGetProcAddress((void *)GetModuleHandleA(0), name); @@ -196,9 +198,11 @@ static void InterceptHooks(); // Don't use the INTERFACE_FUNCTION machinery for this function as we actually // want to call it in the __asan_init interceptor. WRAP_W_V(__asan_should_detect_stack_use_after_return) +WRAP_W_V(__asan_get_shadow_memory_dynamic_address) extern "C" { int __asan_option_detect_stack_use_after_return; + uptr __asan_shadow_memory_dynamic_address; // Manually wrap __asan_init as we need to initialize // __asan_option_detect_stack_use_after_return afterwards. @@ -212,7 +216,8 @@ extern "C" { fn(); __asan_option_detect_stack_use_after_return = (__asan_should_detect_stack_use_after_return() != 0); - + __asan_shadow_memory_dynamic_address = + (uptr)__asan_get_shadow_memory_dynamic_address(); InterceptHooks(); } } @@ -255,6 +260,13 @@ INTERFACE_FUNCTION(__asan_memcpy); INTERFACE_FUNCTION(__asan_memset); INTERFACE_FUNCTION(__asan_memmove); +INTERFACE_FUNCTION(__asan_set_shadow_00); +INTERFACE_FUNCTION(__asan_set_shadow_f1); +INTERFACE_FUNCTION(__asan_set_shadow_f2); +INTERFACE_FUNCTION(__asan_set_shadow_f3); +INTERFACE_FUNCTION(__asan_set_shadow_f5); +INTERFACE_FUNCTION(__asan_set_shadow_f8); + INTERFACE_FUNCTION(__asan_alloca_poison); INTERFACE_FUNCTION(__asan_allocas_unpoison); @@ -309,8 +321,6 @@ INTERFACE_FUNCTION(__sanitizer_cov_init) INTERFACE_FUNCTION(__sanitizer_cov_module_init) INTERFACE_FUNCTION(__sanitizer_cov_trace_basic_block) INTERFACE_FUNCTION(__sanitizer_cov_trace_func_enter) -INTERFACE_FUNCTION(__sanitizer_cov_trace_cmp) -INTERFACE_FUNCTION(__sanitizer_cov_trace_switch) INTERFACE_FUNCTION(__sanitizer_cov_with_check) INTERFACE_FUNCTION(__sanitizer_get_allocated_size) INTERFACE_FUNCTION(__sanitizer_get_coverage_guards) @@ -324,6 +334,8 @@ INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage) INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes) INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file) INTERFACE_FUNCTION(__sanitizer_print_stack_trace) +INTERFACE_FUNCTION(__sanitizer_symbolize_pc) +INTERFACE_FUNCTION(__sanitizer_symbolize_global) INTERFACE_FUNCTION(__sanitizer_ptr_cmp) INTERFACE_FUNCTION(__sanitizer_ptr_sub) INTERFACE_FUNCTION(__sanitizer_report_error_summary) @@ -333,6 +345,7 @@ INTERFACE_FUNCTION(__sanitizer_update_counter_bitset_and_clear_counters) INTERFACE_FUNCTION(__sanitizer_sandbox_on_notify) INTERFACE_FUNCTION(__sanitizer_set_death_callback) INTERFACE_FUNCTION(__sanitizer_set_report_path) +INTERFACE_FUNCTION(__sanitizer_set_report_fd) INTERFACE_FUNCTION(__sanitizer_unaligned_load16) INTERFACE_FUNCTION(__sanitizer_unaligned_load32) INTERFACE_FUNCTION(__sanitizer_unaligned_load64) @@ -340,23 +353,31 @@ INTERFACE_FUNCTION(__sanitizer_unaligned_store16) INTERFACE_FUNCTION(__sanitizer_unaligned_store32) INTERFACE_FUNCTION(__sanitizer_unaligned_store64) INTERFACE_FUNCTION(__sanitizer_verify_contiguous_container) +INTERFACE_FUNCTION(__sanitizer_install_malloc_and_free_hooks) +INTERFACE_FUNCTION(__sanitizer_start_switch_fiber) +INTERFACE_FUNCTION(__sanitizer_finish_switch_fiber) // TODO(timurrrr): Add more interface functions on the as-needed basis. // ----------------- Memory allocation functions --------------------- WRAP_V_W(free) +WRAP_V_W(_free_base) WRAP_V_WW(_free_dbg) WRAP_W_W(malloc) +WRAP_W_W(_malloc_base) WRAP_W_WWWW(_malloc_dbg) WRAP_W_WW(calloc) +WRAP_W_WW(_calloc_base) WRAP_W_WWWWW(_calloc_dbg) WRAP_W_WWW(_calloc_impl) WRAP_W_WW(realloc) +WRAP_W_WW(_realloc_base) WRAP_W_WWW(_realloc_dbg) WRAP_W_WWW(_recalloc) +WRAP_W_WWW(_recalloc_base) WRAP_W_W(_msize) WRAP_W_W(_expand) @@ -369,6 +390,10 @@ WRAP_W_W(_expand_dbg) INTERCEPT_LIBRARY_FUNCTION(atoi); INTERCEPT_LIBRARY_FUNCTION(atol); + +#ifdef _WIN64 +INTERCEPT_LIBRARY_FUNCTION(__C_specific_handler); +#else INTERCEPT_LIBRARY_FUNCTION(_except_handler3); // _except_handler4 checks -GS cookie which is different for each module, so we @@ -377,10 +402,13 @@ INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) { __asan_handle_no_return(); return REAL(_except_handler4)(a, b, c, d); } +#endif INTERCEPT_LIBRARY_FUNCTION(frexp); INTERCEPT_LIBRARY_FUNCTION(longjmp); +#if SANITIZER_INTERCEPT_MEMCHR INTERCEPT_LIBRARY_FUNCTION(memchr); +#endif INTERCEPT_LIBRARY_FUNCTION(memcmp); INTERCEPT_LIBRARY_FUNCTION(memcpy); INTERCEPT_LIBRARY_FUNCTION(memmove); @@ -390,12 +418,14 @@ INTERCEPT_LIBRARY_FUNCTION(strchr); INTERCEPT_LIBRARY_FUNCTION(strcmp); INTERCEPT_LIBRARY_FUNCTION(strcpy); // NOLINT INTERCEPT_LIBRARY_FUNCTION(strcspn); +INTERCEPT_LIBRARY_FUNCTION(strdup); INTERCEPT_LIBRARY_FUNCTION(strlen); INTERCEPT_LIBRARY_FUNCTION(strncat); INTERCEPT_LIBRARY_FUNCTION(strncmp); INTERCEPT_LIBRARY_FUNCTION(strncpy); INTERCEPT_LIBRARY_FUNCTION(strnlen); INTERCEPT_LIBRARY_FUNCTION(strpbrk); +INTERCEPT_LIBRARY_FUNCTION(strrchr); INTERCEPT_LIBRARY_FUNCTION(strspn); INTERCEPT_LIBRARY_FUNCTION(strstr); INTERCEPT_LIBRARY_FUNCTION(strtol); @@ -405,7 +435,9 @@ INTERCEPT_LIBRARY_FUNCTION(wcslen); // is defined. void InterceptHooks() { INTERCEPT_HOOKS(); +#ifndef _WIN64 INTERCEPT_FUNCTION(_except_handler4); +#endif } // We want to call __asan_init before C/C++ initializers/constructors are diff --git a/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cc b/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cc index f735853..8989159 100644 --- a/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cc +++ b/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cc @@ -1,4 +1,4 @@ -//===-- asan_win_uar_thunk.cc ---------------------------------------------===// +//===-- asan_win_dynamic_runtime_thunk.cc ---------------------------------===// // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. @@ -14,11 +14,11 @@ // This includes: // - forwarding the detect_stack_use_after_return runtime option // - working around deficiencies of the MD runtime -// - installing a custom SEH handlerx +// - installing a custom SEH handler // //===----------------------------------------------------------------------===// -// Only compile this code when buidling asan_dynamic_runtime_thunk.lib +// Only compile this code when building asan_dynamic_runtime_thunk.lib // Using #ifdef rather than relying on Makefiles etc. // simplifies the build procedure. #ifdef ASAN_DYNAMIC_RUNTIME_THUNK @@ -27,7 +27,7 @@ // First, declare CRT sections we'll be using in this file #pragma section(".CRT$XID", long, read) // NOLINT -#pragma section(".CRT$XIZ", long, read) // NOLINT +#pragma section(".CRT$XCAB", long, read) // NOLINT #pragma section(".CRT$XTW", long, read) // NOLINT #pragma section(".CRT$XTY", long, read) // NOLINT @@ -40,12 +40,16 @@ // attribute adds __imp_ prefix to the symbol name of a variable. // Since in general we don't know if a given TU is going to be used // with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows -// just to work around this issue, let's clone the a variable that is -// constant after initialization anyways. +// just to work around this issue, let's clone the variable that is constant +// after initialization anyways. extern "C" { __declspec(dllimport) int __asan_should_detect_stack_use_after_return(); int __asan_option_detect_stack_use_after_return = __asan_should_detect_stack_use_after_return(); + +__declspec(dllimport) void* __asan_get_shadow_memory_dynamic_address(); +void* __asan_shadow_memory_dynamic_address = + __asan_get_shadow_memory_dynamic_address(); } //////////////////////////////////////////////////////////////////////////////// @@ -57,6 +61,7 @@ int __asan_option_detect_stack_use_after_return = // using atexit() that calls a small subset of C terminators // where LLVM global_dtors is placed. Fingers crossed, no other C terminators // are there. +extern "C" int __cdecl atexit(void (__cdecl *f)(void)); extern "C" void __cdecl _initterm(void *a, void *b); namespace { @@ -70,6 +75,7 @@ void UnregisterGlobals() { int ScheduleUnregisterGlobals() { return atexit(UnregisterGlobals); } +} // namespace // We need to call 'atexit(UnregisterGlobals);' as early as possible, but after // atexit() is initialized (.CRT$XIC). As this is executed before C++ @@ -78,8 +84,6 @@ int ScheduleUnregisterGlobals() { __declspec(allocate(".CRT$XID")) int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals; -} // namespace - //////////////////////////////////////////////////////////////////////////////// // ASan SEH handling. // We need to set the ASan-specific SEH handler at the end of CRT initialization @@ -90,7 +94,8 @@ static int SetSEHFilter() { return __asan_set_seh_filter(); } // Unfortunately, putting a pointer to __asan_set_seh_filter into // __asan_intercept_seh gets optimized out, so we have to use an extra function. -__declspec(allocate(".CRT$XIZ")) int (*__asan_seh_interceptor)() = SetSEHFilter; +__declspec(allocate(".CRT$XCAB")) int (*__asan_seh_interceptor)() = + SetSEHFilter; } #endif // ASAN_DYNAMIC_RUNTIME_THUNK diff --git a/libsanitizer/asan/libtool-version b/libsanitizer/asan/libtool-version index 7e838a5..0f14ee3 100644 --- a/libsanitizer/asan/libtool-version +++ b/libsanitizer/asan/libtool-version @@ -3,4 +3,4 @@ # a separate file so that version updates don't involve re-running # automake. # CURRENT:REVISION:AGE -3:0:0 +4:0:0 diff --git a/libsanitizer/builtins/assembly.h b/libsanitizer/builtins/assembly.h new file mode 100644 index 0000000..5e36b5a --- /dev/null +++ b/libsanitizer/builtins/assembly.h @@ -0,0 +1,169 @@ +/* ===-- assembly.h - compiler-rt assembler support macros -----------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file defines macros for use in compiler-rt assembler source. + * This file is not part of the interface of this library. + * + * ===----------------------------------------------------------------------=== + */ + +#ifndef COMPILERRT_ASSEMBLY_H +#define COMPILERRT_ASSEMBLY_H + +#if defined(__POWERPC__) || defined(__powerpc__) || defined(__ppc__) +#define SEPARATOR @ +#else +#define SEPARATOR ; +#endif + +#if defined(__APPLE__) +#define HIDDEN(name) .private_extern name +#define LOCAL_LABEL(name) L_##name +// tell linker it can break up file at label boundaries +#define FILE_LEVEL_DIRECTIVE .subsections_via_symbols +#define SYMBOL_IS_FUNC(name) +#define CONST_SECTION .const + +#define NO_EXEC_STACK_DIRECTIVE + +#elif defined(__ELF__) + +#define HIDDEN(name) .hidden name +#define LOCAL_LABEL(name) .L_##name +#define FILE_LEVEL_DIRECTIVE +#if defined(__arm__) +#define SYMBOL_IS_FUNC(name) .type name,%function +#else +#define SYMBOL_IS_FUNC(name) .type name,@function +#endif +#define CONST_SECTION .section .rodata + +#if defined(__GNU__) || defined(__ANDROID__) || defined(__FreeBSD__) +#define NO_EXEC_STACK_DIRECTIVE .section .note.GNU-stack,"",%progbits +#else +#define NO_EXEC_STACK_DIRECTIVE +#endif + +#else // !__APPLE__ && !__ELF__ + +#define HIDDEN(name) +#define LOCAL_LABEL(name) .L ## name +#define FILE_LEVEL_DIRECTIVE +#define SYMBOL_IS_FUNC(name) \ + .def name SEPARATOR \ + .scl 2 SEPARATOR \ + .type 32 SEPARATOR \ + .endef +#define CONST_SECTION .section .rdata,"rd" + +#define NO_EXEC_STACK_DIRECTIVE + +#endif + +#if defined(__arm__) +#if defined(__ARM_ARCH_4T__) || __ARM_ARCH >= 5 +#define ARM_HAS_BX +#endif +#if !defined(__ARM_FEATURE_CLZ) && \ + (__ARM_ARCH >= 6 || (__ARM_ARCH == 5 && !defined(__ARM_ARCH_5__))) +#define __ARM_FEATURE_CLZ +#endif + +#ifdef ARM_HAS_BX +#define JMP(r) bx r +#define JMPc(r, c) bx##c r +#else +#define JMP(r) mov pc, r +#define JMPc(r, c) mov##c pc, r +#endif + +// pop {pc} can't switch Thumb mode on ARMv4T +#if __ARM_ARCH >= 5 +#define POP_PC() pop {pc} +#else +#define POP_PC() \ + pop {ip}; \ + JMP(ip) +#endif + +#if __ARM_ARCH_ISA_THUMB == 2 +#define IT(cond) it cond +#define ITT(cond) itt cond +#else +#define IT(cond) +#define ITT(cond) +#endif + +#if __ARM_ARCH_ISA_THUMB == 2 +#define WIDE(op) op.w +#else +#define WIDE(op) op +#endif +#endif + +#define GLUE2(a, b) a##b +#define GLUE(a, b) GLUE2(a, b) +#define SYMBOL_NAME(name) GLUE(__USER_LABEL_PREFIX__, name) + +#ifdef VISIBILITY_HIDDEN +#define DECLARE_SYMBOL_VISIBILITY(name) \ + HIDDEN(SYMBOL_NAME(name)) SEPARATOR +#else +#define DECLARE_SYMBOL_VISIBILITY(name) +#endif + +#define DEFINE_COMPILERRT_FUNCTION(name) \ + FILE_LEVEL_DIRECTIVE SEPARATOR \ + .globl SYMBOL_NAME(name) SEPARATOR \ + SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \ + DECLARE_SYMBOL_VISIBILITY(name) \ + SYMBOL_NAME(name): + +#define DEFINE_COMPILERRT_THUMB_FUNCTION(name) \ + FILE_LEVEL_DIRECTIVE SEPARATOR \ + .globl SYMBOL_NAME(name) SEPARATOR \ + SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \ + DECLARE_SYMBOL_VISIBILITY(name) SEPARATOR \ + .thumb_func SEPARATOR \ + SYMBOL_NAME(name): + +#define DEFINE_COMPILERRT_PRIVATE_FUNCTION(name) \ + FILE_LEVEL_DIRECTIVE SEPARATOR \ + .globl SYMBOL_NAME(name) SEPARATOR \ + SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \ + HIDDEN(SYMBOL_NAME(name)) SEPARATOR \ + SYMBOL_NAME(name): + +#define DEFINE_COMPILERRT_PRIVATE_FUNCTION_UNMANGLED(name) \ + .globl name SEPARATOR \ + SYMBOL_IS_FUNC(name) SEPARATOR \ + HIDDEN(name) SEPARATOR \ + name: + +#define DEFINE_COMPILERRT_FUNCTION_ALIAS(name, target) \ + .globl SYMBOL_NAME(name) SEPARATOR \ + SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \ + DECLARE_SYMBOL_VISIBILITY(SYMBOL_NAME(name)) SEPARATOR \ + .set SYMBOL_NAME(name), SYMBOL_NAME(target) SEPARATOR + +#if defined(__ARM_EABI__) +#define DEFINE_AEABI_FUNCTION_ALIAS(aeabi_name, name) \ + DEFINE_COMPILERRT_FUNCTION_ALIAS(aeabi_name, name) +#else +#define DEFINE_AEABI_FUNCTION_ALIAS(aeabi_name, name) +#endif + +#ifdef __ELF__ +#define END_COMPILERRT_FUNCTION(name) \ + .size SYMBOL_NAME(name), . - SYMBOL_NAME(name) +#else +#define END_COMPILERRT_FUNCTION(name) +#endif + +#endif /* COMPILERRT_ASSEMBLY_H */ diff --git a/libsanitizer/configure b/libsanitizer/configure index 040ded0..dd95229 100755 --- a/libsanitizer/configure +++ b/libsanitizer/configure @@ -604,6 +604,7 @@ ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS +SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS TSAN_TARGET_DEPENDENT_OBJECTS LIBBACKTRACE_SUPPORTED_FALSE LIBBACKTRACE_SUPPORTED_TRUE @@ -16532,6 +16533,7 @@ fi + cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure diff --git a/libsanitizer/configure.ac b/libsanitizer/configure.ac index 063b1d6..06d3a66 100644 --- a/libsanitizer/configure.ac +++ b/libsanitizer/configure.ac @@ -399,5 +399,6 @@ _EOF fi AC_SUBST([TSAN_TARGET_DEPENDENT_OBJECTS]) +AC_SUBST([SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS]) AC_OUTPUT diff --git a/libsanitizer/configure.tgt b/libsanitizer/configure.tgt index 0b036a3..9a6b158 100644 --- a/libsanitizer/configure.tgt +++ b/libsanitizer/configure.tgt @@ -20,11 +20,13 @@ # Filter out unsupported systems. TSAN_TARGET_DEPENDENT_OBJECTS= +SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS= case "${target}" in x86_64-*-linux* | i?86-*-linux*) if test x$ac_cv_sizeof_void_p = x8; then TSAN_SUPPORTED=yes TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_amd64.lo + SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS=sanitizer_linux_x86_64.lo fi LSAN_SUPPORTED=yes ;; diff --git a/libsanitizer/include/sanitizer/allocator_interface.h b/libsanitizer/include/sanitizer/allocator_interface.h index 97a72a2..d4801e9 100644 --- a/libsanitizer/include/sanitizer/allocator_interface.h +++ b/libsanitizer/include/sanitizer/allocator_interface.h @@ -57,6 +57,23 @@ extern "C" { deallocation of "ptr". */ void __sanitizer_malloc_hook(const volatile void *ptr, size_t size); void __sanitizer_free_hook(const volatile void *ptr); + + /* Installs a pair of hooks for malloc/free. + Several (currently, 5) hook pairs may be installed, they are executed + in the order they were installed and after calling + __sanitizer_malloc_hook/__sanitizer_free_hook. + Unlike __sanitizer_malloc_hook/__sanitizer_free_hook these hooks can be + chained and do not rely on weak symbols working on the platform, but + require __sanitizer_install_malloc_and_free_hooks to be called at startup + and thus will not be called on malloc/free very early in the process. + Returns the number of hooks currently installed or 0 on failure. + Not thread-safe, should be called in the main thread before starting + other threads. + */ + int __sanitizer_install_malloc_and_free_hooks( + void (*malloc_hook)(const volatile void *, size_t), + void (*free_hook)(const volatile void *)); + #ifdef __cplusplus } // extern "C" #endif diff --git a/libsanitizer/include/sanitizer/common_interface_defs.h b/libsanitizer/include/sanitizer/common_interface_defs.h index a6b16a6..c1207d2 100644 --- a/libsanitizer/include/sanitizer/common_interface_defs.h +++ b/libsanitizer/include/sanitizer/common_interface_defs.h @@ -39,6 +39,9 @@ extern "C" { // Tell the tools to write their reports to "path." instead of stderr. void __sanitizer_set_report_path(const char *path); + // Tell the tools to write their reports to the provided file descriptor + // (casted to void *). + void __sanitizer_set_report_fd(void *fd); // Notify the tools that the sandbox is going to be turned on. The reserved // parameter will be used in the future to hold a structure with functions @@ -118,6 +121,9 @@ extern "C" { // lib/sanitizer_common/sanitizer_stacktrace_printer.h. void __sanitizer_symbolize_pc(void *pc, const char *fmt, char *out_buf, size_t out_buf_size); + // Same as __sanitizer_symbolize_pc, but for data section (i.e. globals). + void __sanitizer_symbolize_global(void *data_ptr, const char *fmt, + char *out_buf, size_t out_buf_size); // Sets the callback to be called right before death on error. // Passing 0 will unset the callback. @@ -130,15 +136,55 @@ extern "C" { // to know what is being passed to libc functions, e.g. memcmp. // FIXME: implement more hooks. void __sanitizer_weak_hook_memcmp(void *called_pc, const void *s1, - const void *s2, size_t n); + const void *s2, size_t n, int result); void __sanitizer_weak_hook_strncmp(void *called_pc, const char *s1, - const char *s2, size_t n); - + const char *s2, size_t n, int result); + void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1, + const char *s2, size_t n, int result); + void __sanitizer_weak_hook_strcmp(void *called_pc, const char *s1, + const char *s2, int result); + void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1, + const char *s2, int result); + void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1, + const char *s2, char *result); + void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1, + const char *s2, char *result); + void __sanitizer_weak_hook_memmem(void *called_pc, + const void *s1, size_t len1, + const void *s2, size_t len2, void *result); // Get full module name and calculate pc offset within it. // Returns 1 if pc belongs to some module, 0 if module was not found. int __sanitizer_get_module_and_offset_for_pc(void *pc, char *module_name, size_t module_name_len, void **pc_offset); + + // Prints stack traces for all live heap allocations ordered by total + // allocation size until `top_percent` of total live heap is shown. + // `top_percent` should be between 1 and 100. + // Experimental feature currently available only with asan on Linux/x86_64. + void __sanitizer_print_memory_profile(size_t top_percent); + + // Fiber annotation interface. + // Before switching to a different stack, one must call + // __sanitizer_start_switch_fiber with a pointer to the bottom of the + // destination stack and its size. When code starts running on the new stack, + // it must call __sanitizer_finish_switch_fiber to finalize the switch. + // The start_switch function takes a void** to store the current fake stack if + // there is one (it is needed when detect_stack_use_after_return is enabled). + // When restoring a stack, this pointer must be given to the finish_switch + // function. In most cases, this void* can be stored on the stack just before + // switching. When leaving a fiber definitely, null must be passed as first + // argument to the start_switch function so that the fake stack is destroyed. + // If you do not want support for stack use-after-return detection, you can + // always pass null to these two functions. + // Note that the fake stack mechanism is disabled during fiber switch, so if a + // signal callback runs during the switch, it will not benefit from the stack + // use-after-return detection. + void __sanitizer_start_switch_fiber(void **fake_stack_save, + const void *bottom, size_t size); + void __sanitizer_finish_switch_fiber(void *fake_stack_save, + const void **bottom_old, + size_t *size_old); #ifdef __cplusplus } // extern "C" #endif diff --git a/libsanitizer/include/sanitizer/coverage_interface.h b/libsanitizer/include/sanitizer/coverage_interface.h index d6e83d6..b444996 100644 --- a/libsanitizer/include/sanitizer/coverage_interface.h +++ b/libsanitizer/include/sanitizer/coverage_interface.h @@ -59,6 +59,7 @@ extern "C" { // __sanitizer_get_number_of_counters bytes long and 8-aligned. uintptr_t __sanitizer_update_counter_bitset_and_clear_counters(uint8_t *bitset); + #ifdef __cplusplus } // extern "C" #endif diff --git a/libsanitizer/include/sanitizer/esan_interface.h b/libsanitizer/include/sanitizer/esan_interface.h new file mode 100644 index 0000000..cd18f34 --- /dev/null +++ b/libsanitizer/include/sanitizer/esan_interface.h @@ -0,0 +1,48 @@ +//===-- sanitizer/esan_interface.h ------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of EfficiencySanitizer, a family of performance tuners. +// +// Public interface header. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_ESAN_INTERFACE_H +#define SANITIZER_ESAN_INTERFACE_H + +#include + +// We declare our interface routines as weak to allow the user to avoid +// ifdefs and instead use this pattern to allow building the same sources +// with and without our runtime library: +// if (__esan_report) +// __esan_report(); +#ifdef _MSC_VER +/* selectany is as close to weak as we'll get. */ +#define COMPILER_RT_WEAK __declspec(selectany) +#elif __GNUC__ +#define COMPILER_RT_WEAK __attribute__((weak)) +#else +#define COMPILER_RT_WEAK +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// This function can be called mid-run (or at the end of a run for +// a server process that doesn't shut down normally) to request that +// data for that point in the run be reported from the tool. +void COMPILER_RT_WEAK __esan_report(); + +// This function returns the number of samples that the esan tool has collected +// to this point. This is useful for testing. +unsigned int COMPILER_RT_WEAK __esan_get_sample_count(); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SANITIZER_ESAN_INTERFACE_H diff --git a/libsanitizer/include/sanitizer/linux_syscall_hooks.h b/libsanitizer/include/sanitizer/linux_syscall_hooks.h index 17742b6..34bb291 100644 --- a/libsanitizer/include/sanitizer/linux_syscall_hooks.h +++ b/libsanitizer/include/sanitizer/linux_syscall_hooks.h @@ -1833,6 +1833,17 @@ __sanitizer_syscall_pre_impl_vfork() #define __sanitizer_syscall_post_vfork(res) \ __sanitizer_syscall_post_impl_vfork(res) +#define __sanitizer_syscall_pre_sigaction(signum, act, oldact) \ + __sanitizer_syscall_pre_impl_sigaction((long)signum, (long)act, (long)oldact) +#define __sanitizer_syscall_post_sigaction(res, signum, act, oldact) \ + __sanitizer_syscall_post_impl_sigaction(res, (long)signum, (long)act, \ + (long)oldact) +#define __sanitizer_syscall_pre_rt_sigaction(signum, act, oldact, sz) \ + __sanitizer_syscall_pre_impl_rt_sigaction((long)signum, (long)act, \ + (long)oldact, (long)sz) +#define __sanitizer_syscall_post_rt_sigaction(res, signum, act, oldact, sz) \ + __sanitizer_syscall_post_impl_rt_sigaction(res, (long)signum, (long)act, \ + (long)oldact, (long)sz) // And now a few syscalls we don't handle yet. #define __sanitizer_syscall_pre_afs_syscall(...) @@ -1887,7 +1898,6 @@ #define __sanitizer_syscall_pre_query_module(...) #define __sanitizer_syscall_pre_readahead(...) #define __sanitizer_syscall_pre_readdir(...) -#define __sanitizer_syscall_pre_rt_sigaction(...) #define __sanitizer_syscall_pre_rt_sigreturn(...) #define __sanitizer_syscall_pre_rt_sigsuspend(...) #define __sanitizer_syscall_pre_security(...) @@ -1901,7 +1911,6 @@ #define __sanitizer_syscall_pre_setreuid32(...) #define __sanitizer_syscall_pre_set_thread_area(...) #define __sanitizer_syscall_pre_setuid32(...) -#define __sanitizer_syscall_pre_sigaction(...) #define __sanitizer_syscall_pre_sigaltstack(...) #define __sanitizer_syscall_pre_sigreturn(...) #define __sanitizer_syscall_pre_sigsuspend(...) @@ -1969,7 +1978,6 @@ #define __sanitizer_syscall_post_query_module(res, ...) #define __sanitizer_syscall_post_readahead(res, ...) #define __sanitizer_syscall_post_readdir(res, ...) -#define __sanitizer_syscall_post_rt_sigaction(res, ...) #define __sanitizer_syscall_post_rt_sigreturn(res, ...) #define __sanitizer_syscall_post_rt_sigsuspend(res, ...) #define __sanitizer_syscall_post_security(res, ...) @@ -1983,7 +1991,6 @@ #define __sanitizer_syscall_post_setreuid32(res, ...) #define __sanitizer_syscall_post_set_thread_area(res, ...) #define __sanitizer_syscall_post_setuid32(res, ...) -#define __sanitizer_syscall_post_sigaction(res, ...) #define __sanitizer_syscall_post_sigaltstack(res, ...) #define __sanitizer_syscall_post_sigreturn(res, ...) #define __sanitizer_syscall_post_sigsuspend(res, ...) @@ -3060,7 +3067,13 @@ void __sanitizer_syscall_pre_impl_fork(); void __sanitizer_syscall_post_impl_fork(long res); void __sanitizer_syscall_pre_impl_vfork(); void __sanitizer_syscall_post_impl_vfork(long res); - +void __sanitizer_syscall_pre_impl_sigaction(long signum, long act, long oldact); +void __sanitizer_syscall_post_impl_sigaction(long res, long signum, long act, + long oldact); +void __sanitizer_syscall_pre_impl_rt_sigaction(long signum, long act, + long oldact, long sz); +void __sanitizer_syscall_post_impl_rt_sigaction(long res, long signum, long act, + long oldact, long sz); #ifdef __cplusplus } // extern "C" #endif diff --git a/libsanitizer/interception/Makefile.in b/libsanitizer/interception/Makefile.in index c7ccb51..96d34e9 100644 --- a/libsanitizer/interception/Makefile.in +++ b/libsanitizer/interception/Makefile.in @@ -170,6 +170,7 @@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ RPC_DEFS = @RPC_DEFS@ +SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS = @SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ diff --git a/libsanitizer/interception/interception.h b/libsanitizer/interception/interception.h index f2d48c9..0db36dd 100644 --- a/libsanitizer/interception/interception.h +++ b/libsanitizer/interception/interception.h @@ -90,8 +90,8 @@ typedef __sanitizer::OFF64_T OFF64_T; // Just a pair of pointers. struct interpose_substitution { - const uptr replacement; - const uptr original; + const __sanitizer::uptr replacement; + const __sanitizer::uptr original; }; // For a function foo() create a global pair of pointers { wrap_foo, foo } in @@ -156,10 +156,12 @@ const interpose_substitution substitution_##func_name[] \ namespace __interception { \ extern FUNC_TYPE(func) PTR_TO_REAL(func); \ } +# define ASSIGN_REAL(dst, src) REAL(dst) = REAL(src) #else // __APPLE__ # define REAL(x) x # define DECLARE_REAL(ret_type, func, ...) \ extern "C" ret_type func(__VA_ARGS__); +# define ASSIGN_REAL(x, y) #endif // __APPLE__ #define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) \ diff --git a/libsanitizer/interception/interception_win.cc b/libsanitizer/interception/interception_win.cc index df51fa2..fa81162 100644 --- a/libsanitizer/interception/interception_win.cc +++ b/libsanitizer/interception/interception_win.cc @@ -8,19 +8,178 @@ // This file is a part of AddressSanitizer, an address sanity checker. // // Windows-specific interception methods. +// +// This file is implementing several hooking techniques to intercept calls +// to functions. The hooks are dynamically installed by modifying the assembly +// code. +// +// The hooking techniques are making assumptions on the way the code is +// generated and are safe under these assumptions. +// +// On 64-bit architecture, there is no direct 64-bit jump instruction. To allow +// arbitrary branching on the whole memory space, the notion of trampoline +// region is used. A trampoline region is a memory space withing 2G boundary +// where it is safe to add custom assembly code to build 64-bit jumps. +// +// Hooking techniques +// ================== +// +// 1) Detour +// +// The Detour hooking technique is assuming the presence of an header with +// padding and an overridable 2-bytes nop instruction (mov edi, edi). The +// nop instruction can safely be replaced by a 2-bytes jump without any need +// to save the instruction. A jump to the target is encoded in the function +// header and the nop instruction is replaced by a short jump to the header. +// +// head: 5 x nop head: jmp +// func: mov edi, edi --> func: jmp short +// [...] real: [...] +// +// This technique is only implemented on 32-bit architecture. +// Most of the time, Windows API are hookable with the detour technique. +// +// 2) Redirect Jump +// +// The redirect jump is applicable when the first instruction is a direct +// jump. The instruction is replaced by jump to the hook. +// +// func: jmp