This is an ICE with -fsanitize=undefined on invalid code involving old-style parameter declarations and nested functions. Ugh.
Old-style parameter declarations cannot have initializers, and start_decl errors when it sees that something's trying to initialize a PARM_DECL, but nonetheless, we still attempt to parse the initializer, eventually arriving in build_binary_op, which means we will instrument the bogus initializer. This causes a crash in ubsan_encode_value -> create_tmp_var -> declare_vars: it thinks it's processing a nested function, but the body of the function is null, leading to the crash. I suppose the best fix is to avoid instrumenting such bogus initializers. In c_parser_declaration_or_fndef it's easy to determine if we're initializing a PARM_DECL -- just check DECL's code, but with __auto_type that's not possible, because start_decl is called *after* parsing the initializer. So instead of DECL's code I'm checking nested && !empty_ok. But we still want to instrument variables with initializers in nested functions, so I've added two run-time tests for that. Bootstrapped/regtested on x86_64-linux, ok for trunk? 2017-03-06 Marek Polacek <pola...@redhat.com> PR sanitizer/79757 * c-parser.c (c_parser_declaration_or_fndef): Don't sanitize old-style parameter declarations with initializers. * gcc.dg/ubsan/pr79757-1.c: New test. * gcc.dg/ubsan/pr79757-2.c: New test. * gcc.dg/ubsan/pr79757-3.c: New test. * gcc.dg/ubsan/pr79757-4.c: New test. * gcc.dg/ubsan/pr79757-5.c: New test. diff --git gcc/c/c-parser.c gcc/c/c-parser.c index fa4e950..6ff023c 100644 --- gcc/c/c-parser.c +++ gcc/c/c-parser.c @@ -1859,7 +1859,13 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, init_loc = c_parser_peek_token (parser)->location; rich_location richloc (line_table, init_loc); start_init (NULL_TREE, asm_name, global_bindings_p (), &richloc); + /* A parameter is initialized, which is invalid. Don't + attempt to instrument the initializer. */ + int flag_sanitize_save = flag_sanitize; + if (nested && !empty_ok) + flag_sanitize = 0; init = c_parser_expr_no_commas (parser, NULL); + flag_sanitize = flag_sanitize_save; if (TREE_CODE (init.value) == COMPONENT_REF && DECL_C_BIT_FIELD (TREE_OPERAND (init.value, 1))) error_at (here, @@ -1917,7 +1923,13 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, init_loc = c_parser_peek_token (parser)->location; rich_location richloc (line_table, init_loc); start_init (d, asm_name, global_bindings_p (), &richloc); + /* A parameter is initialized, which is invalid. Don't + attempt to instrument the initializer. */ + int flag_sanitize_save = flag_sanitize; + if (TREE_CODE (d) == PARM_DECL) + flag_sanitize = 0; init = c_parser_initializer (parser); + flag_sanitize = flag_sanitize_save; finish_init (); } if (oacc_routine_data) diff --git gcc/testsuite/gcc.dg/ubsan/pr79757-1.c gcc/testsuite/gcc.dg/ubsan/pr79757-1.c index e69de29..ca074bc 100644 --- gcc/testsuite/gcc.dg/ubsan/pr79757-1.c +++ gcc/testsuite/gcc.dg/ubsan/pr79757-1.c @@ -0,0 +1,24 @@ +/* PR sanitizer/79757 */ +/* { dg-do compile } */ +/* { dg-require-effective-target int128 } */ +/* { dg-options "-fsanitize=undefined" } */ + +unsigned __int128 x, y; + +void +fn1 (void) +{ + int a (z) + unsigned long long z = x / y; /* { dg-error "parameter 'z' is initialized" } */ + { + } +} + +void +fn2 (void) +{ + int a (z) + unsigned long long z = x >> y; /* { dg-error "parameter 'z' is initialized" } */ + { + } +} diff --git gcc/testsuite/gcc.dg/ubsan/pr79757-2.c gcc/testsuite/gcc.dg/ubsan/pr79757-2.c index e69de29..b3e1939 100644 --- gcc/testsuite/gcc.dg/ubsan/pr79757-2.c +++ gcc/testsuite/gcc.dg/ubsan/pr79757-2.c @@ -0,0 +1,18 @@ +/* PR sanitizer/79757 */ +/* { dg-do compile } */ +/* { dg-require-effective-target int128 } */ +/* { dg-options "-fsanitize=undefined" } */ + +unsigned __int128 x, y; + +void +fn1 (z) + unsigned long long z = x / y; /* { dg-error "parameter 'z' is initialized" } */ +{ +} + +void +fn2 (z) + unsigned long long z = x >> y; /* { dg-error "parameter 'z' is initialized" } */ +{ +} diff --git gcc/testsuite/gcc.dg/ubsan/pr79757-3.c gcc/testsuite/gcc.dg/ubsan/pr79757-3.c index e69de29..22fe3de 100644 --- gcc/testsuite/gcc.dg/ubsan/pr79757-3.c +++ gcc/testsuite/gcc.dg/ubsan/pr79757-3.c @@ -0,0 +1,18 @@ +/* PR sanitizer/79757 */ +/* { dg-do compile } */ +/* { dg-require-effective-target int128 } */ +/* { dg-options "-fsanitize=undefined" } */ + +unsigned __int128 x, y; + +void +fn1 (z) + __auto_type z = x / y; /* { dg-error "parameter 'z' is initialized" } */ +{ +} + +void +fn2 (z) + __auto_type z = x >> y; /* { dg-error "parameter 'z' is initialized" } */ +{ +} diff --git gcc/testsuite/gcc.dg/ubsan/pr79757-4.c gcc/testsuite/gcc.dg/ubsan/pr79757-4.c index e69de29..33b348f 100644 --- gcc/testsuite/gcc.dg/ubsan/pr79757-4.c +++ gcc/testsuite/gcc.dg/ubsan/pr79757-4.c @@ -0,0 +1,29 @@ +/* PR sanitizer/79757 */ +/* { dg-do run } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */ +/* { dg-options "-fsanitize=undefined" } */ + +int +main (void) +{ + int + div (int n) + { + int i = 5 / n; + return i; + } + + int + shift (int n) + { + int i = 5 << n; + return i; + } + + int j = shift (100); + int i = div (0); + return 0; +} + +/* { dg-output "shift exponent 100 is too large for \[^\n\r]*-bit type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero" } */ diff --git gcc/testsuite/gcc.dg/ubsan/pr79757-5.c gcc/testsuite/gcc.dg/ubsan/pr79757-5.c index e69de29..786d817 100644 --- gcc/testsuite/gcc.dg/ubsan/pr79757-5.c +++ gcc/testsuite/gcc.dg/ubsan/pr79757-5.c @@ -0,0 +1,29 @@ +/* PR sanitizer/79757 */ +/* { dg-do run } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */ +/* { dg-options "-fsanitize=undefined" } */ + +int +main (void) +{ + int + div (int n) + { + __auto_type i = 5 / n; + return i; + } + + int + shift (int n) + { + __auto_type i = 5 << n; + return i; + } + + int j = shift (100); + int i = div (0); + return 0; +} + +/* { dg-output "shift exponent 100 is too large for \[^\n\r]*-bit type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*division by zero" } */ Marek