diff options
author | Marek Polacek <polacek@redhat.com> | 2020-12-03 18:35:00 -0500 |
---|---|---|
committer | Marek Polacek <polacek@redhat.com> | 2020-12-07 11:54:30 -0500 |
commit | 1cac89da2cb1f2a7c2d93f7f325484c2d1619ca8 (patch) | |
tree | d83a2760dce18de383aec51214737555dd9ad222 /gcc/c-family/c-common.c | |
parent | ffb268ffcf9f21e2981a71887324eb0aec245eea (diff) | |
download | gcc-1cac89da2cb1f2a7c2d93f7f325484c2d1619ca8.zip gcc-1cac89da2cb1f2a7c2d93f7f325484c2d1619ca8.tar.gz gcc-1cac89da2cb1f2a7c2d93f7f325484c2d1619ca8.tar.bz2 |
c-family: Fix hang with -Wsequence-point [PR98126]
verify_sequence_points uses verify_tree to recursively walk the
subexpressions of an expression, and while recursing, it also
keeps lists of expressions found after/before a sequence point.
For a large expression, the list can grow significantly. And
merge_tlist is at least N(n^2): for a list of length n it will
iterate n(n -1) times, and call candidate_equal_p each time, and
that can recurse further. warn_for_collision also has to go
through the whole list. With a large-enough expression, the
compilation can easily get stuck here for 24 hours.
This patch is a simple kludge: if we see that the expression is
overly complex, don't even try.
gcc/c-family/ChangeLog:
PR c++/98126
* c-common.c (verify_tree_lim_r): New function.
(verify_sequence_points): Use it. Use nullptr instead of 0.
gcc/testsuite/ChangeLog:
PR c++/98126
* g++.dg/warn/Wsequence-point-4.C: New test.
Diffstat (limited to 'gcc/c-family/c-common.c')
-rw-r--r-- | gcc/c-family/c-common.c | 32 |
1 files changed, 27 insertions, 5 deletions
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index dda2352..0b348ae 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -2056,23 +2056,45 @@ verify_tree (tree x, struct tlist **pbefore_sp, struct tlist **pno_sp, } } +static constexpr size_t verify_sequence_points_limit = 1024; + +/* Called from verify_sequence_points via walk_tree. */ + +static tree +verify_tree_lim_r (tree *tp, int *walk_subtrees, void *data) +{ + if (++*((size_t *) data) > verify_sequence_points_limit) + return integer_zero_node; + + if (TYPE_P (*tp)) + *walk_subtrees = 0; + + return NULL_TREE; +} + /* Try to warn for undefined behavior in EXPR due to missing sequence points. */ void verify_sequence_points (tree expr) { - struct tlist *before_sp = 0, *after_sp = 0; + tlist *before_sp = nullptr, *after_sp = nullptr; + + /* verify_tree is highly recursive, and merge_tlist is O(n^2), + so we return early if the expression is too big. */ + size_t n = 0; + if (walk_tree (&expr, verify_tree_lim_r, &n, nullptr)) + return; - warned_ids = 0; - save_expr_cache = 0; - if (tlist_firstobj == 0) + warned_ids = nullptr; + save_expr_cache = nullptr; + if (!tlist_firstobj) { gcc_obstack_init (&tlist_obstack); tlist_firstobj = (char *) obstack_alloc (&tlist_obstack, 0); } - verify_tree (expr, &before_sp, &after_sp, 0); + verify_tree (expr, &before_sp, &after_sp, NULL_TREE); warn_for_collisions (after_sp); obstack_free (&tlist_obstack, tlist_firstobj); } |