Hello, This PR uncovers an issue my latest optimization patch on Address Sanitizer introduced.
This is in the context of instrumenting an access to a memory region, like in: void foo (char *a, char *b, int s) { __builtin_memmove (a, b, s); } In this case, asan has to instrument accesses to two memory regions: [a a+s[ and [b b+s[. The way asan does instrument the access to e.g [a a+s[ his is that it instruments the access to the byte pointed to by the pointer 'a', and the access to the byte pointed to by the pointer 'a+s-1'. Now what happens when we want to avoid doing redundant instrumentations like in: void bar (char *a, char *b, char *c) { __builtin_memmove (a, b, c[b[0]]); } So here, we first instrument the access to the memory of b[0] (in the expression c[b[0]]). So in the course of instrumenting the access for the memory region [b b+s[, we don't want to instrument the access to the memory pointed by 'p', as that was already been instrumented for the b[0] expression. So we just instrument the access to memory through b+s-1. This is what I'd call 'partial memory region instrumentation'; it happens in the function instrument_mem_region_access, and it was basically corrupting the CFG because instrument_mem_region_access was not correctly updating the statement iterator it receives in argument so subsequent use of that iterator (to append instrumentation statements) was leading to statements not being added to their correct place. The compiler crash reported in this PR is due to this CFG corruption. There is another underlying issue that can lead to runtime errors. When doing partial memory region instrumentation in e.g the case of the bar function above I was forgetting to enclose the instrumentation statements of [a, a+s[ and [b, b+s[ into a if (len != 0) {} clause (len being c[b[0]] here) for the cases where len is not a constant. The patch addresses this too. Bootstrapped and tested on x86_64-unknown-linux-gnu against trunk, and approved by Jakub in the audit trail of the PR at http://gcc.gnu.org/PR56330. I am about to commit in a short while. gcc/ * asan.c (get_mem_refs_of_builtin_call): White space and style cleanup. (instrument_mem_region_access): Do not forget to always put instrumentation of the of 'base' and 'base + len' in a "if (len != 0) statement, even for cases where either 'base' or 'base + len' are not instrumented -- because they have been previously instrumented. Simplify the logic by putting all the statements instrument 'base + len' inside a sequence, and then insert that sequence right before the current insertion point. Then, to instrument 'base + len', just get an iterator on that statement. And do not forget to update the pointer to iterator the function received as argument. gcc/testsuite/ * c-c++-common/asan/no-redundant-instrumentation-4.c: New test file. * c-c++-common/asan/no-redundant-instrumentation-5.c: Likewise. * c-c++-common/asan/no-redundant-instrumentation-6.c: Likewise. * c-c++-common/asan/no-redundant-instrumentation-7.c: Likewise. * c-c++-common/asan/no-redundant-instrumentation-8.c: Likewise. * c-c++-common/asan/pr56330.c: Likewise. * c-c++-common/asan/no-redundant-instrumentation-1.c (test1): Ensure the size argument of __builtin_memcpy is a constant. --- gcc/asan.c | 97 ++++++++++++---------- .../asan/no-redundant-instrumentation-1.c | 2 +- .../asan/no-redundant-instrumentation-4.c | 13 +++ .../asan/no-redundant-instrumentation-5.c | 13 +++ .../asan/no-redundant-instrumentation-6.c | 14 ++++ .../asan/no-redundant-instrumentation-7.c | 22 +++++ .../asan/no-redundant-instrumentation-8.c | 14 ++++ gcc/testsuite/c-c++-common/asan/pr56330.c | 23 +++++ 8 files changed, 153 insertions(+), 45 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-4.c create mode 100644 gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-5.c create mode 100644 gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-6.c create mode 100644 gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-7.c create mode 100644 gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-8.c create mode 100644 gcc/testsuite/c-c++-common/asan/pr56330.c diff --git a/gcc/asan.c b/gcc/asan.c index a569479..67236a9 100644 --- a/gcc/asan.c +++ b/gcc/asan.c @@ -747,20 +747,17 @@ get_mem_refs_of_builtin_call (const gimple call, got_reference_p = true; } - else - { - if (dest) - { - dst->start = dest; - dst->access_size = access_size; - *dst_len = NULL_TREE; - *dst_is_store = is_store; - *dest_is_deref = true; - got_reference_p = true; - } - } + else if (dest) + { + dst->start = dest; + dst->access_size = access_size; + *dst_len = NULL_TREE; + *dst_is_store = is_store; + *dest_is_deref = true; + got_reference_p = true; + } - return got_reference_p; + return got_reference_p; } /* Return true iff a given gimple statement has been instrumented. @@ -1535,8 +1532,15 @@ instrument_mem_region_access (tree base, tree len, /* If the beginning of the memory region has already been instrumented, do not instrument it. */ - if (has_mem_ref_been_instrumented (base, 1)) - goto after_first_instrumentation; + bool start_instrumented = has_mem_ref_been_instrumented (base, 1); + + /* If the end of the memory region has already been instrumented, do + not instrument it. */ + tree end = asan_mem_ref_get_end (base, len); + bool end_instrumented = has_mem_ref_been_instrumented (end, 1); + + if (start_instrumented && end_instrumented) + return; if (!is_gimple_constant (len)) { @@ -1562,37 +1566,39 @@ instrument_mem_region_access (tree base, tree len, /* The 'then block' of the 'if (len != 0) condition is where we'll generate the asan instrumentation code now. */ - gsi = gsi_start_bb (then_bb); + gsi = gsi_last_bb (then_bb); } - /* Instrument the beginning of the memory region to be accessed, - and arrange for the rest of the intrumentation code to be - inserted in the then block *after* the current gsi. */ - build_check_stmt (location, base, &gsi, /*before_p=*/true, is_store, 1); - - if (then_bb) - /* We are in the case where the length of the region is not - constant; so instrumentation code is being generated in the - 'then block' of the 'if (len != 0) condition. Let's arrange - for the subsequent instrumentation statements to go in the - 'then block'. */ - gsi = gsi_last_bb (then_bb); - else - *iter = gsi; - - update_mem_ref_hash_table (base, 1); + if (!start_instrumented) + { + /* Instrument the beginning of the memory region to be accessed, + and arrange for the rest of the intrumentation code to be + inserted in the then block *after* the current gsi. */ + build_check_stmt (location, base, &gsi, /*before_p=*/true, is_store, 1); + + if (then_bb) + /* We are in the case where the length of the region is not + constant; so instrumentation code is being generated in the + 'then block' of the 'if (len != 0) condition. Let's arrange + for the subsequent instrumentation statements to go in the + 'then block'. */ + gsi = gsi_last_bb (then_bb); + else + { + *iter = gsi; + /* Don't remember this access as instrumented, if length + is unknown. It might be zero and not being actually + instrumented, so we can't rely on it being instrumented. */ + update_mem_ref_hash_table (base, 1); + } + } - after_first_instrumentation: + if (end_instrumented) + return; /* We want to instrument the access at the end of the memory region, which is at (base + len - 1). */ - /* If the end of the memory region has already been instrumented, do - not instrument it. */ - tree end = asan_mem_ref_get_end (base, len); - if (has_mem_ref_been_instrumented (end, 1)) - return; - /* offset = len - 1; */ len = unshare_expr (len); tree offset; @@ -1639,8 +1645,6 @@ instrument_mem_region_access (tree base, tree len, base, NULL); gimple_set_location (region_end, location); gimple_seq_add_stmt_without_update (&seq, region_end); - gsi_insert_seq_before (&gsi, seq, GSI_SAME_STMT); - gsi_prev (&gsi); /* _2 = _1 + offset; */ region_end = @@ -1649,13 +1653,18 @@ instrument_mem_region_access (tree base, tree len, gimple_assign_lhs (region_end), offset); gimple_set_location (region_end, location); - gsi_insert_after (&gsi, region_end, GSI_NEW_STMT); + gimple_seq_add_stmt_without_update (&seq, region_end); + gsi_insert_seq_before (&gsi, seq, GSI_SAME_STMT); /* instrument access at _2; */ + gsi = gsi_for_stmt (region_end); build_check_stmt (location, gimple_assign_lhs (region_end), &gsi, /*before_p=*/false, is_store, 1); - update_mem_ref_hash_table (end, 1); + if (then_bb == NULL) + update_mem_ref_hash_table (end, 1); + + *iter = gsi_for_stmt (gsi_stmt (*iter)); } /* Instrument the call (to the builtin strlen function) pointed to by @@ -1783,7 +1792,7 @@ instrument_builtin_call (gimple_stmt_iterator *iter) } else if (src0_len || src1_len || dest_len) { - if (src0.start) + if (src0.start != NULL_TREE) instrument_mem_region_access (src0.start, src0_len, iter, loc, /*is_store=*/false); if (src1.start != NULL_TREE) diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-1.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-1.c index cc98fdb..6cf6441 100644 --- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-1.c +++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-1.c @@ -45,7 +45,7 @@ test1 () /* There are 2 calls to __builtin___asan_report_store1 and 2 calls to __builtin___asan_report_load1 to instrument the store to (subset of) the memory region of tab. */ - __builtin_memcpy (&tab[1], foo, sizeof (tab) - 1); + __builtin_memcpy (&tab[1], foo, 3); /* This should not generate a __builtin___asan_report_load1 because the reference to tab[1] has been already instrumented above. */ diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-4.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-4.c new file mode 100644 index 0000000..b2e7284 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-4.c @@ -0,0 +1,13 @@ +/* { dg-options "-fdump-tree-asan0" } */ +/* { dg-do compile } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */ + +void +foo (int *a, char *b, char *c) +{ + __builtin_memmove (c, b, a[c[0]]); +} + +/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 3 "asan0" } } */ +/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 1 "asan0" } } */ +/* { dg-final { cleanup-tree-dump "asan0" } } */ diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-5.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-5.c new file mode 100644 index 0000000..ead3f58 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-5.c @@ -0,0 +1,13 @@ +/* { dg-options "-fdump-tree-asan0" } */ +/* { dg-do compile } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */ + +void +foo (int *a, char *b, char *c) +{ + __builtin_memmove (c, b, a[b[0]]); +} + +/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 2 "asan0" } } */ +/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 2 "asan0" } } */ +/* { dg-final { cleanup-tree-dump "asan0" } } */ diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-6.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-6.c new file mode 100644 index 0000000..e4691bc --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-6.c @@ -0,0 +1,14 @@ +/* { dg-options "-fdump-tree-asan0" } */ +/* { dg-do compile } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */ + +void +foo (int *a, char *b, char *c) +{ + __builtin_memmove (c, b, a[c[0]]); + __builtin_memmove (c, b, a[b[0]]); +} + +/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 5 "asan0" } } */ +/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 2 "asan0" } } */ +/* { dg-final { cleanup-tree-dump "asan0" } } */ diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-7.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-7.c new file mode 100644 index 0000000..aba409b --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-7.c @@ -0,0 +1,22 @@ +/* { dg-options "-fdump-tree-asan0" } */ +/* { dg-do compile } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */ + +char e[200]; + +struct S +{ + char a[100]; + char b[100]; +} s; + +void +foo (int *a, char *b, char *c) +{ + __builtin_memcmp (s.a, e, 100); + __builtin_memcmp (s.a, e, 200); +} + +/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load" 6 "asan0" } } */ +/* { dg-final { scan-tree-dump-not "__builtin___asan_report_store" "asan0" } } */ +/* { dg-final { cleanup-tree-dump "asan" } } */ diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-8.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-8.c new file mode 100644 index 0000000..38ea7a2 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-8.c @@ -0,0 +1,14 @@ +/* { dg-options "-fdump-tree-asan0" } */ +/* { dg-do compile } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */ + +char +foo (int *a, char *b, char *c) +{ + __builtin_memmove (c, b, a[b[0]]); + return c[0] + b[0]; +} + +/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 3 "asan0" } } */ +/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 2 "asan0" } } */ +/* { dg-final { cleanup-tree-dump "asan0" } } */ diff --git a/gcc/testsuite/c-c++-common/asan/pr56330.c b/gcc/testsuite/c-c++-common/asan/pr56330.c new file mode 100644 index 0000000..18f1065 --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/pr56330.c @@ -0,0 +1,23 @@ +/* PR sanitizer/56330 */ +/* { dg-do compile } */ + +char e[200]; + +struct S +{ + char a[100]; + char b[100]; +} s; + +void +foo (void) +{ + __builtin_memcmp (s.a, e, 100); + __builtin_memcmp (s.a, e, 200); +} + +void +bar (int *a, char *b, char *c) +{ + __builtin_memmove (c, b, a[b[0]]); +} -- Dodji