https://gcc.gnu.org/g:5175ef7f7577a1ef4151f306161a1dab904f23bb
commit r16-4919-g5175ef7f7577a1ef4151f306161a1dab904f23bb Author: Martin Uecker <[email protected]> Date: Fri Oct 31 16:10:40 2025 +0100 c: Fix ICE when using va_arg with arrays [PR97986] When array type is passed to va_arg, this is undefined behavior. Emit a warning, and insert a run-time trap after evaluating side effects, but return the correct type for sizeof / typeof. For C90 a VLA is an error. PR c/97986 gcc/c/ChangeLog: * c-parser.cc (c_parser_postfix_expression): Adapt. * c-tree.h (c_build_va_arg): Adapt prototype. * c-typeck.cc (c_build_va_arg): Handle UB. gcc/testsuite/ChangeLog: * gcc.dg/pr97986-1.c: New test. * gcc.dg/pr97986-2.c: New test. Diff: --- gcc/c/c-parser.cc | 10 ++-------- gcc/c/c-tree.h | 2 +- gcc/c/c-typeck.cc | 31 +++++++++++++++++++++++++++++-- gcc/testsuite/gcc.dg/pr97986-1.c | 27 +++++++++++++++++++++++++++ gcc/testsuite/gcc.dg/pr97986-2.c | 15 +++++++++++++++ 5 files changed, 74 insertions(+), 11 deletions(-) diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 0cf3f92a72cb..a559a4e6a060 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -11793,15 +11793,9 @@ c_parser_postfix_expression (c_parser *parser) else { tree type_expr = NULL_TREE; + tree type = groktypename (t1, &type_expr, NULL); expr.value = c_build_va_arg (start_loc, e1.value, loc, - groktypename (t1, &type_expr, NULL)); - if (type_expr) - { - expr.value = build2 (C_MAYBE_CONST_EXPR, - TREE_TYPE (expr.value), type_expr, - expr.value); - C_MAYBE_CONST_EXPR_NON_CONST (expr.value) = true; - } + type, type_expr); set_c_expr_source_range (&expr, start_loc, end_loc); } } diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h index f367cda35d7c..ff63d69e85d5 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -917,7 +917,7 @@ extern tree c_omp_finish_mapper_clauses (tree); extern tree c_omp_mapper_lookup (tree, tree); extern tree c_omp_extract_mapper_directive (tree); extern tree c_omp_map_array_section (location_t, tree); -extern tree c_build_va_arg (location_t, tree, location_t, tree); +extern tree c_build_va_arg (location_t, tree, location_t, tree, tree); extern tree c_finish_transaction (location_t, tree, int); extern bool c_tree_equal (tree, tree); extern tree c_build_function_call_vec (location_t, const vec<location_t>&, diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index bc0fb6b59e55..eba57afac095 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -18435,7 +18435,8 @@ c_build_qualified_type (tree type, int type_quals, tree orig_qual_type, /* Build a VA_ARG_EXPR for the C parser. */ tree -c_build_va_arg (location_t loc1, tree expr, location_t loc2, tree type) +c_build_va_arg (location_t loc1, tree expr, location_t loc2, tree type, + tree type_expr) { if (error_operand_p (type)) return error_mark_node; @@ -18459,10 +18460,36 @@ c_build_va_arg (location_t loc1, tree expr, location_t loc2, tree type) type); return error_mark_node; } + else if (TREE_CODE (type) == ARRAY_TYPE && C_TYPE_VARIABLE_SIZE (type) + && !flag_isoc99) + { + error_at (loc2, "second argument to %<va_arg%> is an array type %qT", + type); + return error_mark_node; + } else if (warn_cxx_compat && TREE_CODE (type) == ENUMERAL_TYPE) warning_at (loc2, OPT_Wc___compat, "C++ requires promoted type, not enum type, in %<va_arg%>"); - return build_va_arg (loc2, expr, type); + + if (flag_isoc99 && TREE_CODE (type) == ARRAY_TYPE) + { + warning_at (loc2, 0, "second argument to %<va_arg%> is an array type %qT", + type); + /* We create a trap but evaluate side effects first. */ + tree trapfn = builtin_decl_explicit (BUILT_IN_TRAP); + trapfn = build_call_expr_loc (loc2, trapfn, 0); + tree e2 = build2 (COMPOUND_EXPR, void_type_node, expr, trapfn); + /* Return a compound literal of the right type. */ + tree e1 = build_compound_literal (loc2, type, NULL, true, 0, NULL); + expr = build2 (COMPOUND_EXPR, type, e2, e1); + } + else + expr = build_va_arg (loc2, expr, type); + + if (type_expr) + expr = build2 (COMPOUND_EXPR, TREE_TYPE (expr), type_expr, expr); + + return expr; } /* Return truthvalue of whether T1 is the same tree structure as T2. diff --git a/gcc/testsuite/gcc.dg/pr97986-1.c b/gcc/testsuite/gcc.dg/pr97986-1.c new file mode 100644 index 000000000000..87ee3d8a7445 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr97986-1.c @@ -0,0 +1,27 @@ +/* { dg-do run } */ +/* { dg-options "-std=gnu23" } */ + +#include <stdarg.h> + +int f(int n, ...) +{ + __label__ b, d; + va_list ap; + va_start(ap, n); + _Static_assert(5 == sizeof(va_arg(ap, char[5]))); /* { dg-warning "array type" } */ + void g(void) { n++; goto b; } + int *a = va_arg((g(), ap), int[n]); /* { dg-warning "array type" } */ +b: + void h(void) { n++; goto d; } + typeof(va_arg(ap, int[(h(), n)])) c; /* { dg-warning "array type" } */ +d: + return n; +} + +int main() +{ + if (9 != f(7)) + __builtin_abort(); + return 0; +} + diff --git a/gcc/testsuite/gcc.dg/pr97986-2.c b/gcc/testsuite/gcc.dg/pr97986-2.c new file mode 100644 index 000000000000..fc23a57907c8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr97986-2.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-std=c90" } */ + +#include <stdarg.h> + + +int f(int n, ...) +{ + va_list ap; + va_start(ap, n); + _Static_assert(5 == sizeof(va_arg(ap, char[5]))); + va_arg(ap, int[n]); /* { dg-error "array type" } */ + int * a = va_arg(ap, int[3]); /* { dg-error "invalid use of non-lvalue array" } */ +} +
