public inbox for gcc-regression@sourceware.org
help / color / mirror / Atom feed
* [TCWG CI] Regression caused by gcc: Add -Wuse-after-free [PR80532].
@ 2022-01-16 6:05 ci_notify
0 siblings, 0 replies; only message in thread
From: ci_notify @ 2022-01-16 6:05 UTC (permalink / raw)
To: Martin Sebor; +Cc: gcc-regression
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=UTF-8, Size: 39661 bytes --]
[TCWG CI] Regression caused by gcc: Add -Wuse-after-free [PR80532].:
commit 671a283636de75f7ed638ee6b01ed2d44361b8b6
Author: Martin Sebor <msebor@redhat.com>
Add -Wuse-after-free [PR80532].
Results regressed to
# reset_artifacts:
-10
# true:
0
# build_abe binutils:
1
# First few build errors in logs:
# 00:06:01 make[3]: [Makefile:1776: armv8l-unknown-linux-gnueabihf/bits/largefile-config.h] Error 1 (ignored)
# 00:09:11 /home/tcwg-buildslave/workspace/tcwg_gnu_15/abe/snapshots/gcc.git~master/libcpp/lex.c:1523:9: error: pointer used after âvoid operator delete(void*, std::size_t)â [-Werror=use-after-free]
# 00:09:18 make[3]: *** [Makefile:227: lex.o] Error 1
# 00:09:18 make[2]: *** [Makefile:9486: all-stage2-libcpp] Error 2
# 00:10:36 make[1]: *** [Makefile:25739: stage2-bubble] Error 2
# 00:10:36 make: *** [Makefile:1072: all] Error 2
from
# reset_artifacts:
-10
# true:
0
# build_abe binutils:
1
# build_abe bootstrap:
2
THIS IS THE END OF INTERESTING STUFF. BELOW ARE LINKS TO BUILDS, REPRODUCTION INSTRUCTIONS, AND THE RAW COMMIT.
This commit has regressed these CI configurations:
- tcwg_gcc_bootstrap/master-arm-bootstrap
First_bad build: https://ci.linaro.org/job/tcwg_gcc_bootstrap-bisect-master-arm-bootstrap/10/artifact/artifacts/build-671a283636de75f7ed638ee6b01ed2d44361b8b6/
Last_good build: https://ci.linaro.org/job/tcwg_gcc_bootstrap-bisect-master-arm-bootstrap/10/artifact/artifacts/build-29401b7b4581e9131e7057e263dcea8b40a6b5ab/
Baseline build: https://ci.linaro.org/job/tcwg_gcc_bootstrap-bisect-master-arm-bootstrap/10/artifact/artifacts/build-baseline/
Even more details: https://ci.linaro.org/job/tcwg_gcc_bootstrap-bisect-master-arm-bootstrap/10/artifact/artifacts/
Reproduce builds:
<cut>
mkdir investigate-gcc-671a283636de75f7ed638ee6b01ed2d44361b8b6
cd investigate-gcc-671a283636de75f7ed638ee6b01ed2d44361b8b6
# Fetch scripts
git clone https://git.linaro.org/toolchain/jenkins-scripts
# Fetch manifests and test.sh script
mkdir -p artifacts/manifests
curl -o artifacts/manifests/build-baseline.sh https://ci.linaro.org/job/tcwg_gcc_bootstrap-bisect-master-arm-bootstrap/10/artifact/artifacts/manifests/build-baseline.sh --fail
curl -o artifacts/manifests/build-parameters.sh https://ci.linaro.org/job/tcwg_gcc_bootstrap-bisect-master-arm-bootstrap/10/artifact/artifacts/manifests/build-parameters.sh --fail
curl -o artifacts/test.sh https://ci.linaro.org/job/tcwg_gcc_bootstrap-bisect-master-arm-bootstrap/10/artifact/artifacts/test.sh --fail
chmod +x artifacts/test.sh
# Reproduce the baseline build (build all pre-requisites)
./jenkins-scripts/tcwg_gnu-build.sh @@ artifacts/manifests/build-baseline.sh
# Save baseline build state (which is then restored in artifacts/test.sh)
mkdir -p ./bisect
rsync -a --del --delete-excluded --exclude /bisect/ --exclude /artifacts/ --exclude /gcc/ ./ ./bisect/baseline/
cd gcc
# Reproduce first_bad build
git checkout --detach 671a283636de75f7ed638ee6b01ed2d44361b8b6
../artifacts/test.sh
# Reproduce last_good build
git checkout --detach 29401b7b4581e9131e7057e263dcea8b40a6b5ab
../artifacts/test.sh
cd ..
</cut>
Full commit (up to 1000 lines):
<cut>
commit 671a283636de75f7ed638ee6b01ed2d44361b8b6
Author: Martin Sebor <msebor@redhat.com>
Date: Sat Jan 15 16:37:54 2022 -0700
Add -Wuse-after-free [PR80532].
gcc/c-family/ChangeLog
PR tree-optimization/80532
* c.opt (-Wuse-after-free): New options.
gcc/ChangeLog:
PR tree-optimization/80532
* common.opt (-Wuse-after-free): New options.
* diagnostic-spec.c (nowarn_spec_t::nowarn_spec_t): Handle
OPT_Wreturn_local_addr and OPT_Wuse_after_free_.
* diagnostic-spec.h (NW_DANGLING): New enumerator.
* doc/invoke.texi (-Wuse-after-free): Document new option.
* gimple-ssa-warn-access.cc (pass_waccess::check_call): Rename...
(pass_waccess::check_call_access): ...to this.
(pass_waccess::check): Rename...
(pass_waccess::check_block): ...to this.
(pass_waccess::check_pointer_uses): New function.
(pass_waccess::gimple_call_return_arg): New function.
(pass_waccess::warn_invalid_pointer): New function.
(pass_waccess::check_builtin): Handle free and realloc.
(gimple_use_after_inval_p): New function.
(get_realloc_lhs): New function.
(maybe_warn_mismatched_realloc): New function.
(pointers_related_p): New function.
(pass_waccess::check_call): Call check_pointer_uses.
(pass_waccess::execute): Compute and free dominance info.
libcpp/ChangeLog:
* files.c (_cpp_find_file): Substitute a valid pointer for
an invalid one to avoid -Wuse-after-free.
libiberty/ChangeLog:
* regex.c: Suppress -Wuse-after-free.
gcc/testsuite/ChangeLog:
PR tree-optimization/80532
* gcc.dg/Wmismatched-dealloc-2.c: Avoid -Wuse-after-free.
* gcc.dg/Wmismatched-dealloc-3.c: Same.
* gcc.dg/analyzer/file-1.c: Prune expected warning.
* gcc.dg/analyzer/file-2.c: Same.
* gcc.dg/attr-alloc_size-6.c: Disable -Wuse-after-free.
* gcc.dg/attr-alloc_size-7.c: Same.
* c-c++-common/Wuse-after-free-2.c: New test.
* c-c++-common/Wuse-after-free-3.c: New test.
* c-c++-common/Wuse-after-free-4.c: New test.
* c-c++-common/Wuse-after-free-5.c: New test.
* c-c++-common/Wuse-after-free-6.c: New test.
* c-c++-common/Wuse-after-free-7.c: New test.
* c-c++-common/Wuse-after-free.c: New test.
* g++.dg/warn/Wmismatched-dealloc-3.C: New test.
* g++.dg/warn/Wuse-after-free.C: New test.
---
gcc/c-family/c.opt | 12 +
gcc/common.opt | 8 +
gcc/diagnostic-spec.c | 5 +
gcc/diagnostic-spec.h | 6 +-
gcc/doc/invoke.texi | 60 +++
gcc/gimple-ssa-warn-access.cc | 461 +++++++++++++++++++++-
gcc/testsuite/c-c++-common/Wuse-after-free-2.c | 169 ++++++++
gcc/testsuite/c-c++-common/Wuse-after-free-3.c | 83 ++++
gcc/testsuite/c-c++-common/Wuse-after-free-4.c | 102 +++++
gcc/testsuite/c-c++-common/Wuse-after-free-5.c | 103 +++++
gcc/testsuite/c-c++-common/Wuse-after-free-6.c | 105 +++++
gcc/testsuite/c-c++-common/Wuse-after-free-7.c | 103 +++++
gcc/testsuite/c-c++-common/Wuse-after-free.c | 167 ++++++++
gcc/testsuite/g++.dg/warn/Wmismatched-dealloc-3.C | 70 ++++
gcc/testsuite/g++.dg/warn/Wuse-after-free.C | 158 ++++++++
gcc/testsuite/gcc.dg/Wmismatched-dealloc-2.c | 13 +-
gcc/testsuite/gcc.dg/Wmismatched-dealloc-3.c | 5 +
gcc/testsuite/gcc.dg/analyzer/file-1.c | 3 +
gcc/testsuite/gcc.dg/analyzer/file-2.c | 3 +
gcc/testsuite/gcc.dg/attr-alloc_size-6.c | 2 +-
gcc/testsuite/gcc.dg/attr-alloc_size-7.c | 2 +-
libcpp/files.c | 13 +-
libiberty/regex.c | 4 +
23 files changed, 1625 insertions(+), 32 deletions(-)
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index dcda1cccedd..28363643664 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1366,6 +1366,18 @@ Wunused-const-variable=
C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_unused_const_variable) Warning LangEnabledBy(C ObjC,Wunused-variable, 1, 0) IntegerRange(0, 2)
Warn when a const variable is unused.
+# Defining these options here in addition to common.opt is necessary
+# in order for the default -Wall setting of -Wuse-after-free=2 to take
+# effect.
+
+Wuse-after-free
+LangEnabledBy(C ObjC C++ LTO ObjC++)
+; in common.opt
+
+Wuse-after-free=
+LangEnabledBy(C ObjC C++ LTO ObjC++, Wall,2,0)
+; in common.opt
+
Wvariadic-macros
C ObjC C++ ObjC++ CPP(warn_variadic_macros) CppReason(CPP_W_VARIADIC_MACROS) Var(cpp_warn_variadic_macros) Init(0) Warning LangEnabledBy(C ObjC C++ ObjC++,Wpedantic || Wtraditional)
Warn about using variadic macros.
diff --git a/gcc/common.opt b/gcc/common.opt
index fde3f722ede..c3f6472be04 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -552,6 +552,14 @@ Warray-bounds=
Common Joined RejectNegative UInteger Var(warn_array_bounds) Warning IntegerRange(0, 2)
Warn if an array is accessed out of bounds.
+Wuse-after-free
+Common Var(warn_use_after_free) Warning
+Warn for uses of pointers to deallocated strorage.
+
+Wuse-after-free=
+Common Joined RejectNegative UInteger Var(warn_use_after_free) Warning IntegerRange(0, 3)
+Warn for uses of pointers to deallocated strorage.
+
Wattributes
Common Var(warn_attributes) Init(1) Warning
Warn about inappropriate attribute usage.
diff --git a/gcc/diagnostic-spec.c b/gcc/diagnostic-spec.c
index 5479abcc391..c9e1c1be91d 100644
--- a/gcc/diagnostic-spec.c
+++ b/gcc/diagnostic-spec.c
@@ -99,6 +99,11 @@ nowarn_spec_t::nowarn_spec_t (opt_code opt)
m_bits = NW_UNINIT;
break;
+ case OPT_Wreturn_local_addr:
+ case OPT_Wuse_after_free_:
+ m_bits = NW_DANGLING;
+ break;
+
default:
/* A catchall group for everything else. */
m_bits = NW_OTHER;
diff --git a/gcc/diagnostic-spec.h b/gcc/diagnostic-spec.h
index ef398dfebb6..28e5e5ccc75 100644
--- a/gcc/diagnostic-spec.h
+++ b/gcc/diagnostic-spec.h
@@ -41,11 +41,13 @@ public:
NW_UNINIT = 1 << 3,
/* Warnings about arithmetic overflow. */
NW_VFLOW = 1 << 4,
+ /* Warnings about dangling pointers. */
+ NW_DANGLING = 1 << 5,
/* All other unclassified warnings. */
- NW_OTHER = 1 << 5,
+ NW_OTHER = 1 << 6,
/* All groups of warnings. */
NW_ALL = (NW_ACCESS | NW_LEXICAL | NW_NONNULL
- | NW_UNINIT | NW_VFLOW | NW_OTHER)
+ | NW_UNINIT | NW_VFLOW | NW_DANGLING | NW_OTHER)
};
nowarn_spec_t (): m_bits () { }
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 5504971ea81..121c8ea827f 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -4383,6 +4383,65 @@ annotations.
Warn about overriding virtual functions that are not marked with the
@code{override} keyword.
+@item -Wuse-after-free
+@itemx -Wuse-after-free=@var{n}
+@opindex Wuse-after-free
+@opindex Wno-use-after-free
+Warn about uses of pointers to dynamically allocated objects that have
+been rendered indeterminate by a call to a deallocation function.
+
+@table @gcctabopt
+@item -Wuse-after-free=1
+At level 1 the warning attempts to diagnose only unconditional uses
+of pointers made indeterminate by a deallocation call or a successful
+call to @code{realloc}, regardless of whether or not the call resulted
+in an actual reallocatio of memory. This includes double-@code{free}
+calls as well as uses in arithmetic and relational expressions. Although
+undefined, uses of indeterminate pointers in equality (or inequality)
+expressions are not diagnosed at this level.
+@item -Wuse-after-free=2
+At level 2, in addition to unconditional uses, the warning also diagnoses
+conditional uses of pointers made indeterminate by a deallocation call.
+As at level 2, uses in equality (or inequality) expressions are not
+diagnosed. For example, the second call to @code{free} in the following
+function is diagnosed at this level:
+@smallexample
+struct A @{ int refcount; void *data; @};
+
+void release (struct A *p)
+@{
+ int refcount = --p->refcount;
+ free (p);
+ if (refcount == 0)
+ free (p->data); // warning: p may be used after free
+@}
+@end smallexample
+@item -Wuse-after-free=3
+At level 3, the warning also diagnoses uses of indeterminate pointers in
+equality expressions. All uses of indeterminate pointers are undefined
+but equality tests sometimes appear after calls to @code{realloc} as
+an attempt to determine whether the call resulted in relocating the object
+to a different address. They are diagnosed at a separate level to aid
+legacy code gradually transition to safe alternatives. For example,
+the equality test in the function below is diagnosed at this level:
+@smallexample
+void adjust_pointers (int**, int);
+
+void grow (int **p, int n)
+@{
+ int **q = (int**)realloc (p, n *= 2);
+ if (q == p)
+ return;
+ adjust_pointers ((int**)q, n);
+@}
+@end smallexample
+To avoid the warning at this level, store offsets into allocated memory
+instead of pointers. This approach obviates needing to adjust the stored
+pointers after reallocation.
+@end table
+
+@option{-Wuse-after-free=2} is included in @option{-Wall}.
+
@item -Wuseless-cast @r{(C++ and Objective-C++ only)}
@opindex Wuseless-cast
@opindex Wno-useless-cast
@@ -5703,6 +5762,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
-Wunused-label @gol
-Wunused-value @gol
-Wunused-variable @gol
+-Wuse-after-free=3 @gol
-Wvla-parameter @r{(C and Objective-C only)} @gol
-Wvolatile-register-var @gol
-Wzero-length-bounds}
diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index e4c078a8e43..882129143a1 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -53,6 +53,7 @@
#include "stringpool.h"
#include "attribs.h"
#include "demangle.h"
+#include "attr-fnspec.h"
#include "pointer-query.h"
/* Return true if tree node X has an associated location. */
@@ -2071,6 +2072,7 @@ class pass_waccess : public gimple_opt_pass
opt_pass *clone () { return new pass_waccess (m_ctxt); }
virtual bool gate (function *);
+
virtual unsigned int execute (function *);
private:
@@ -2084,14 +2086,14 @@ private:
/* Check a call to a built-in function. */
bool check_builtin (gcall *);
- /* Check a call to an ordinary function. */
- bool check_call (gcall *);
+ /* Check a call to an ordinary function for invalid accesses. */
+ bool check_call_access (gcall *);
/* Check statements in a basic block. */
- void check (basic_block);
+ void check_block (basic_block);
/* Check a call to a function. */
- void check (gcall *);
+ void check_call (gcall *);
/* Check a call to the named built-in function. */
void check_alloca (gcall *);
@@ -2109,10 +2111,27 @@ private:
bool maybe_warn_memmodel (gimple *, tree, tree, const unsigned char *);
void check_atomic_memmodel (gimple *, tree, tree, const unsigned char *);
+ /* Check for uses of indeterminate pointers. */
+ void check_pointer_uses (gimple *, tree);
+
+ /* Return the argument that a call returns. */
+ tree gimple_call_return_arg (gcall *);
+
+ void warn_invalid_pointer (tree, gimple *, gimple *, bool, bool = false);
+
+ /* Return true if use follows an invalidating statement. */
+ bool use_after_inval_p (gimple *, gimple *);
+
/* A pointer_query object and its cache to store information about
pointers and their targets in. */
pointer_query m_ptr_qry;
pointer_query::cache_type m_var_cache;
+
+ /* A bit is set for each basic block whose statements have been assigned
+ valid UIDs. */
+ bitmap m_bb_uids_set;
+ /* The current function. */
+ function *m_func;
};
/* Construct the pass. */
@@ -2120,7 +2139,9 @@ private:
pass_waccess::pass_waccess (gcc::context *ctxt)
: gimple_opt_pass (pass_data_waccess, ctxt),
m_ptr_qry (NULL, &m_var_cache),
- m_var_cache ()
+ m_var_cache (),
+ m_bb_uids_set (),
+ m_func ()
{
}
@@ -3071,6 +3092,15 @@ pass_waccess::check_builtin (gcall *stmt)
check_read_access (stmt, call_arg (stmt, 0));
return true;
+ case BUILT_IN_FREE:
+ case BUILT_IN_REALLOC:
+ {
+ tree arg = call_arg (stmt, 0);
+ if (TREE_CODE (arg) == SSA_NAME)
+ check_pointer_uses (stmt, arg);
+ }
+ return true;
+
case BUILT_IN_GETTEXT:
case BUILT_IN_PUTS:
case BUILT_IN_PUTS_UNLOCKED:
@@ -3175,6 +3205,7 @@ pass_waccess::check_builtin (gcall *stmt)
return true;
break;
}
+
return false;
}
@@ -3504,7 +3535,7 @@ pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
accesses. Return true if a call has been handled. */
bool
-pass_waccess::check_call (gcall *stmt)
+pass_waccess::check_call_access (gcall *stmt)
{
tree fntype = gimple_call_fntype (stmt);
if (!fntype)
@@ -3692,46 +3723,442 @@ pass_waccess::maybe_check_dealloc_call (gcall *call)
}
}
+/* Return true if either USE_STMT's basic block (that of a pointer's use)
+ is dominated by INVAL_STMT's (that of a pointer's invalidating statement,
+ or if they're in the same block, USE_STMT follows INVAL_STMT. */
+
+bool
+pass_waccess::use_after_inval_p (gimple *inval_stmt, gimple *use_stmt)
+{
+ basic_block inval_bb = gimple_bb (inval_stmt);
+ basic_block use_bb = gimple_bb (use_stmt);
+
+ if (inval_bb != use_bb)
+ return dominated_by_p (CDI_DOMINATORS, use_bb, inval_bb);
+
+ if (bitmap_set_bit (m_bb_uids_set, inval_bb->index))
+ /* The first time this basic block is visited assign increasing ids
+ to consecutive statements in it. Use the ids to determine which
+ precedes which. This avoids the linear traversal on subsequent
+ visits to the same block. */
+ for (auto si = gsi_start_bb (inval_bb); !gsi_end_p (si);
+ gsi_next_nondebug (&si))
+ {
+ gimple *stmt = gsi_stmt (si);
+ unsigned uid = inc_gimple_stmt_max_uid (m_func);
+ gimple_set_uid (stmt, uid);
+ }
+
+ return gimple_uid (inval_stmt) < gimple_uid (use_stmt);
+}
+
+/* Issue a warning for the USE_STMT of pointer PTR rendered invalid
+ by INVAL_STMT. PTR may be null when it's been optimized away.
+ MAYBE is true to issue the "maybe" kind of warning. EQUALITY is
+ true when the pointer is used in an equality expression. */
+
+void
+pass_waccess::warn_invalid_pointer (tree ptr, gimple *use_stmt,
+ gimple *inval_stmt,
+ bool maybe,
+ bool equality /* = false */)
+{
+ /* Avoid printing the unhelpful "<unknown>" in the diagnostics. */
+ if (ptr && TREE_CODE (ptr) == SSA_NAME
+ && (!SSA_NAME_VAR (ptr) || DECL_ARTIFICIAL (SSA_NAME_VAR (ptr))))
+ ptr = NULL_TREE;
+
+ location_t use_loc = gimple_location (use_stmt);
+ if (use_loc == UNKNOWN_LOCATION)
+ {
+ use_loc = cfun->function_end_locus;
+ if (!ptr)
+ /* Avoid issuing a warning with no context other than
+ the function. That would make it difficult to debug
+ in any but very simple cases. */
+ return;
+ }
+
+ if (is_gimple_call (inval_stmt))
+ {
+ if ((equality && warn_use_after_free < 3)
+ || (maybe && warn_use_after_free < 2)
+ || warning_suppressed_p (use_stmt, OPT_Wuse_after_free))
+ return;
+
+ const tree inval_decl = gimple_call_fndecl (inval_stmt);
+
+ if ((ptr && warning_at (use_loc, OPT_Wuse_after_free,
+ (maybe
+ ? G_("pointer %qE may be used after %qD")
+ : G_("pointer %qE used after %qD")),
+ ptr, inval_decl))
+ || (!ptr && warning_at (use_loc, OPT_Wuse_after_free,
+ (maybe
+ ? G_("pointer may be used after %qD")
+ : G_("pointer used after %qD")),
+ inval_decl)))
+ {
+ location_t loc = gimple_location (inval_stmt);
+ inform (loc, "call to %qD here", inval_decl);
+ suppress_warning (use_stmt, OPT_Wuse_after_free);
+ }
+ return;
+ }
+}
+
+/* If STMT is a call to either the standard realloc or to a user-defined
+ reallocation function returns its LHS and set *PTR to the reallocated
+ pointer. Otherwise return null. */
+
+static tree
+get_realloc_lhs (gimple *stmt, tree *ptr)
+{
+ if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC))
+ {
+ *ptr = gimple_call_arg (stmt, 0);
+ return gimple_call_lhs (stmt);
+ }
+
+ gcall *call = dyn_cast<gcall *>(stmt);
+ if (!call)
+ return NULL_TREE;
+
+ tree fnattr = NULL_TREE;
+ tree fndecl = gimple_call_fndecl (call);
+ if (fndecl)
+ fnattr = DECL_ATTRIBUTES (fndecl);
+ else
+ {
+ tree fntype = gimple_call_fntype (stmt);
+ if (!fntype)
+ return NULL_TREE;
+ fnattr = TYPE_ATTRIBUTES (fntype);
+ }
+
+ if (!fnattr)
+ return NULL_TREE;
+
+ for (tree ats = fnattr; (ats = lookup_attribute ("*dealloc", ats));
+ ats = TREE_CHAIN (ats))
+ {
+ tree args = TREE_VALUE (ats);
+ if (!args)
+ continue;
+
+ tree alloc = TREE_VALUE (args);
+ if (!alloc)
+ continue;
+
+ if (alloc == DECL_NAME (fndecl))
+ {
+ unsigned argno = 0;
+ if (tree index = TREE_CHAIN (args))
+ argno = TREE_INT_CST_LOW (TREE_VALUE (index)) - 1;
+ *ptr = gimple_call_arg (stmt, argno);
+ return gimple_call_lhs (stmt);
+ }
+ }
+
+ return NULL_TREE;
+}
+
+/* Warn if STMT is a call to a deallocation function that's not a match
+ for the REALLOC_STMT call. Return true if warned. */
+
+static bool
+maybe_warn_mismatched_realloc (tree ptr, gimple *realloc_stmt, gimple *stmt)
+{
+ if (!is_gimple_call (stmt))
+ return false;
+
+ tree fndecl = gimple_call_fndecl (stmt);
+ if (!fndecl)
+ return false;
+
+ unsigned argno = fndecl_dealloc_argno (fndecl);
+ if (call_nargs (stmt) <= argno)
+ return false;
+
+ if (matching_alloc_calls_p (realloc_stmt, fndecl))
+ return false;
+
+ /* Avoid printing the unhelpful "<unknown>" in the diagnostics. */
+ if (ptr && TREE_CODE (ptr) == SSA_NAME
+ && (!SSA_NAME_VAR (ptr) || DECL_ARTIFICIAL (SSA_NAME_VAR (ptr))))
+ ptr = NULL_TREE;
+
+ location_t loc = gimple_location (stmt);
+ tree realloc_decl = gimple_call_fndecl (realloc_stmt);
+ tree dealloc_decl = gimple_call_fndecl (stmt);
+ if (ptr && !warning_at (loc, OPT_Wmismatched_dealloc,
+ "%qD called on pointer %qE passed to mismatched "
+ "allocation function %qD",
+ dealloc_decl, ptr, realloc_decl))
+ return false;
+ if (!ptr && !warning_at (loc, OPT_Wmismatched_dealloc,
+ "%qD called on a pointer passed to mismatched "
+ "reallocation function %qD",
+ dealloc_decl, realloc_decl))
+ return false;
+
+ inform (gimple_location (realloc_stmt),
+ "call to %qD", realloc_decl);
+ return true;
+}
+
+/* Return true if P and Q point to the same object, and false if they
+ either don't or their relationship cannot be determined. */
+
+static bool
+pointers_related_p (gimple *stmt, tree p, tree q, pointer_query &qry)
+{
+ if (!ptr_derefs_may_alias_p (p, q))
+ return false;
+
+ /* TODO: Work harder to rule out relatedness. */
+ access_ref pref, qref;
+ if (!qry.get_ref (p, stmt, &pref, 0)
+ || !qry.get_ref (q, stmt, &qref, 0))
+ return true;
+
+ return pref.ref == qref.ref;
+}
+
+/* For a STMT either a call to a deallocation function or a clobber, warn
+ for uses of the pointer PTR it was called with (including its copies
+ or others derived from it by pointer arithmetic). */
+
+void
+pass_waccess::check_pointer_uses (gimple *stmt, tree ptr)
+{
+ gcc_assert (TREE_CODE (ptr) == SSA_NAME);
+
+ const bool check_dangling = !is_gimple_call (stmt);
+ basic_block stmt_bb = gimple_bb (stmt);
+
+ /* If STMT is a reallocation function set to the reallocated pointer
+ and the LHS of the call, respectively. */
+ tree realloc_ptr = NULL_TREE;
+ tree realloc_lhs = get_realloc_lhs (stmt, &realloc_ptr);
+
+ auto_bitmap visited;
+
+ auto_vec<tree> pointers;
+ pointers.safe_push (ptr);
+
+ /* Starting with PTR, iterate over POINTERS added by the loop, and
+ either warn for their uses in basic blocks dominated by the STMT
+ or in statements that follow it in the same basic block, or add
+ them to POINTERS if they point into the same object as PTR (i.e.,
+ are obtained by pointer arithmetic on PTR). */
+ for (unsigned i = 0; i != pointers.length (); ++i)
+ {
+ tree ptr = pointers[i];
+ if (TREE_CODE (ptr) == SSA_NAME
+ && !bitmap_set_bit (visited, SSA_NAME_VERSION (ptr)))
+ /* Avoid revisiting the same pointer. */
+ continue;
+
+ use_operand_p use_p;
+ imm_use_iterator iter;
+ FOR_EACH_IMM_USE_FAST (use_p, iter, ptr)
+ {
+ gimple *use_stmt = USE_STMT (use_p);
+ if (use_stmt == stmt || is_gimple_debug (use_stmt))
+ continue;
+
+ if (realloc_lhs)
+ {
+ /* Check to see if USE_STMT is a mismatched deallocation
+ call for the pointer passed to realloc. That's a bug
+ regardless of the pointer's value and so warn. */
+ if (maybe_warn_mismatched_realloc (*use_p->use, stmt, use_stmt))
+ continue;
+
+ /* Pointers passed to realloc that are used in basic blocks
+ where the realloc call is known to have failed are valid.
+ Ignore pointers that nothing is known about. Those could
+ have escaped along with their nullness. */
+ value_range vr;
+ if (m_ptr_qry.rvals->range_of_expr (vr, realloc_lhs, use_stmt))
+ {
+ if (vr.zero_p ())
+ continue;
+
+ if (!pointers_related_p (stmt, ptr, realloc_ptr, m_ptr_qry))
+ continue;
+ }
+ }
+
+ if (check_dangling
+ && gimple_code (use_stmt) == GIMPLE_RETURN)
+ /* Avoid interfering with -Wreturn-local-addr (which runs only
+ with optimization enabled so it won't diagnose cases that
+ would be caught here when optimization is disabled). */
+ continue;
+
+ bool equality = false;
+ if (is_gimple_assign (use_stmt))
+ {
+ tree_code code = gimple_assign_rhs_code (use_stmt);
+ equality = code == EQ_EXPR || code == NE_EXPR;
+ }
+ else if (gcond *cond = dyn_cast<gcond *>(use_stmt))
+ {
+ tree_code code = gimple_cond_code (cond);
+ equality = code == EQ_EXPR || code == NE_EXPR;
+ }
+
+ /* Warn if USE_STMT is dominated by the deallocation STMT.
+ Otherwise, add the pointer to POINTERS so that the uses
+ of any other pointers derived from it can be checked. */
+ if (use_after_inval_p (stmt, use_stmt))
+ {
+ /* TODO: Handle PHIs but careful of false positives. */
+ if (gimple_code (use_stmt) != GIMPLE_PHI)
+ {
+ basic_block use_bb = gimple_bb (use_stmt);
+ bool this_maybe
+ = !dominated_by_p (CDI_POST_DOMINATORS, use_bb, stmt_bb);
+ warn_invalid_pointer (*use_p->use, use_stmt, stmt,
+ this_maybe, equality);
+ continue;
+ }
+ }
+
+ if (is_gimple_assign (use_stmt))
+ {
+ tree lhs = gimple_assign_lhs (use_stmt);
+ if (TREE_CODE (lhs) == SSA_NAME)
+ {
+ tree_code rhs_code = gimple_assign_rhs_code (use_stmt);
+ if (rhs_code == POINTER_PLUS_EXPR || rhs_code == SSA_NAME)
+ pointers.safe_push (lhs);
+ }
+ continue;
+ }
+
+ if (gcall *call = dyn_cast <gcall *>(use_stmt))
+ {
+ if (gimple_call_return_arg (call))
+ if (tree lhs = gimple_call_lhs (call))
+ if (TREE_CODE (lhs) == SSA_NAME)
+ pointers.safe_push (lhs);
+ continue;
+ }
+ }
+ }
+}
+
/* Check call STMT for invalid accesses. */
void
-pass_waccess::check (gcall *stmt)
+pass_waccess::check_call (gcall *stmt)
{
if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
check_builtin (stmt);
- if (is_gimple_call (stmt))
- check_call (stmt);
+ if (tree callee = gimple_call_fndecl (stmt))
+ {
+ /* Check for uses of the pointer passed to either a standard
+ or a user-defined deallocation function. */
+ unsigned argno = fndecl_dealloc_argno (callee);
+ if (argno < (unsigned) call_nargs (stmt))
+ {
+ tree arg = call_arg (stmt, argno);
+ if (TREE_CODE (arg) == SSA_NAME)
+ check_pointer_uses (stmt, arg);
+ }
+ }
- maybe_check_dealloc_call (stmt);
+ check_call_access (stmt);
+ maybe_check_dealloc_call (stmt);
check_nonstring_args (stmt);
}
+
/* Check basic block BB for invalid accesses. */
void
-pass_waccess::check (basic_block bb)
+pass_waccess::check_block (basic_block bb)
{
/* Iterate over statements, looking for function calls. */
- for (auto si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
+ for (auto si = gsi_start_bb (bb); !gsi_end_p (si);
+ gsi_next_nondebug (&si))
{
- if (gcall *call = dyn_cast <gcall *> (gsi_stmt (si)))
- check (call);
+ gimple *stmt = gsi_stmt (si);
+ if (gcall *call = dyn_cast <gcall *> (stmt))
+ check_call (call);
}
}
+/* Return the argument that the call STMT to a built-in function returns
+ (including with an offset) or null if it doesn't. */
+
+tree
+pass_waccess::gimple_call_return_arg (gcall *call)
+{
+ /* Check for attribute fn spec to see if the function returns one
+ of its arguments. */
+ attr_fnspec fnspec = gimple_call_fnspec (call);
+ unsigned int argno;
+ if (!fnspec.returns_arg (&argno))
+ {
+ if (gimple_call_num_args (call) < 1)
+ return NULL_TREE;
+
+ if (!gimple_call_builtin_p (call, BUILT_IN_NORMAL))
+ return NULL_TREE;
+
+ tree fndecl = gimple_call_fndecl (call);
+ switch (DECL_FUNCTION_CODE (fndecl))
+ {
+ case BUILT_IN_MEMPCPY:
+ case BUILT_IN_MEMPCPY_CHK:
+ case BUILT_IN_MEMCHR:
+ case BUILT_IN_STRCHR:
+ case BUILT_IN_STRRCHR:
+ case BUILT_IN_STRSTR:
+ case BUILT_IN_STPCPY:
+ case BUILT_IN_STPCPY_CHK:
+ case BUILT_IN_STPNCPY:
+ case BUILT_IN_STPNCPY_CHK:
+ argno = 0;
+ break;
+
+ default:
+ return NULL_TREE;
+ }
+ }
+
+ if (gimple_call_num_args (call) <= argno)
+ return NULL_TREE;
+
+ return gimple_call_arg (call, argno);
+}
+
/* Check function FUN for invalid accesses. */
unsigned
pass_waccess::execute (function *fun)
{
+ calculate_dominance_info (CDI_DOMINATORS);
+ calculate_dominance_info (CDI_POST_DOMINATORS);
+
/* Create a new ranger instance and associate it with FUN. */
m_ptr_qry.rvals = enable_ranger (fun);
+ m_func = fun;
+
+ auto_bitmap bb_uids_set (&bitmap_default_obstack);
+ m_bb_uids_set = bb_uids_set;
+
+ set_gimple_stmt_max_uid (m_func, 0);
basic_block bb;
FOR_EACH_BB_FN (bb, fun)
- check (bb);
+ check_block (bb);
if (dump_file)
m_ptr_qry.dump (dump_file, (dump_flags & TDF_DETAILS) != 0);
@@ -3743,6 +4170,10 @@ pass_waccess::execute (function *fun)
disable_ranger (fun);
m_ptr_qry.rvals = NULL;
+ m_bb_uids_set = NULL;
+
+ free_dominance_info (CDI_POST_DOMINATORS);
+ free_dominance_info (CDI_DOMINATORS);
return 0;
}
diff --git a/gcc/testsuite/c-c++-common/Wuse-after-free-2.c b/gcc/testsuite/c-c++-common/Wuse-after-free-2.c
new file mode 100644
index 00000000000..e309bdf041a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wuse-after-free-2.c
@@ -0,0 +1,169 @@
+/* Verify that accessing freed objects by built-in functions is diagnosed.
+ { dg-do compile }
+ { dg-options "-Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+#if __cplusplus
+# define EXTERN_C extern "C"
+#else
+# define EXTERN_C extern
+#endif
+
+EXTERN_C void free (void*);
+EXTERN_C void* realloc (void*, size_t);
+
+EXTERN_C void* memcpy (void*, const void*, size_t);
+EXTERN_C char* strcpy (char*, const char*);
+EXTERN_C size_t strlen (const char*);
+
+
+void sink (void*, ...);
+
+struct Member { char *p; char a[4]; };
+
+int nowarn_strcpy_memptr (struct Member *p)
+{
+ char *q = strcpy (p->p, p->a);
+ free (p);
+ return *q;
+}
+
+int nowarn_strlen_memptr (struct Member *p)
+{
+ const char *q = p->p;
+
+ free (p);
+
+ return strlen (q);
+}
+
+int warn_strlen_memptr (struct Member *p)
+{
+ free (p); // { dg-message "call to '\(void \)?free\(\\(void\\*\\)\)?'" "note" }
+ return strlen (p->p); // { dg-warning "-Wuse-after-free" }
+}
+
+int warn_strlen_memarray (struct Member *p)
+{
+ {
+ free (p);
+ return strlen (p->a); // { dg-warning "-Wuse-after-free" }
+ }
+
+ {
+ char *q = p->a;
+
+ free (p);
+ return strlen (q); // { dg-warning "-Wuse-after-free" "pr??????" { xfail *-*-* } }
+ }
+}
+
+void* nowarn_realloc_success (void *p)
+{
+ void *q = realloc (p, 7);
+ if (!q)
+ /* When realloc fails the original pointer remains valid. */
+ return p;
+
+ return q;
+}
+
+void* nowarn_realloc_equal (void *p, int *moved)
+{
+ void *q = realloc (p, 7);
+ /* Verify that equality is not diagnosed at the default level
+ (it is diagnosed at level 3). */
+ *moved = !(p == q);
+ return q;
+}
+
+void* nowarn_realloc_unequal (void *p, int *moved)
+{
+ void *q = realloc (p, 7);
+ /* Verify that inequality is not diagnosed at the default level
+ (it is diagnosed at level 3). */
+ *moved = p != q;
+ return q;
+}
+
+void* warn_realloc_relational (void *p, int *rel)
+{
+ void *q = realloc (p, 7); // { dg-message "call to '\(void\\* \)?realloc\(\\(void\\*, size_t\\)\)?'" "note" }
+ /* Verify that all relational expressions are diagnosed at the default
+ level. */
+ rel[0] = (char*)p < (char*)q; // { dg-warning "-Wuse-after-free" }
+ rel[1] = (char*)p <= (char*)q; // { dg-warning "-Wuse-after-free" }
+ rel[2] = (char*)p >= (char*)q; // { dg-warning "-Wuse-after-free" }
+ rel[3] = (char*)p > (char*)q; // { dg-warning "-Wuse-after-free" }
+ return q;
+}
+
+void* warn_realloc_unchecked (void *p, int *moved)
+{
+ void *q = realloc (p, 7); // { dg-message "call to '\(void\\* \)?realloc\(\\(void\\*, size_t\\)\)?'" "note" }
+ /* Use subtraction rather than inequality to trigger the warning
+ at the default level (equality is diagnosed only at level 3). */
+ *moved = (char*)p - (char*)q; // { dg-warning "-Wuse-after-free" }
+ return q;
+}
+
+void* nowarn_realloc_unchecked_copy (void *p1, void *p2, const void *s,
+ int n, int *x)
+{
+ void *p3 = memcpy (p1, s, n);
+ void *p4 = realloc (p2, 7);
+ *x = p3 != p4;
+ return p4;
+}
+
+void* warn_realloc_unchecked_copy (void *p, const void *s, int n, int *moved)
+{
+ void *p2 = memcpy (p, s, n);
+ void *q = realloc (p, 7); // { dg-message "call to '\(void\\* \)?realloc\(\\(void\\*, size_t\\)\)?'" "note" }
+ *moved = (char*)p2 - (char*)q; // { dg-warning "-Wuse-after-free" }
+ return q;
+}
+
+void* warn_realloc_failed (void *p, int *moved)
+{
+ void *q = realloc (p, 7); // { dg-message "call to '\(void\\* \)?realloc\(\\(void\\*, size_t\\)\)?'" "note" }
+ if (q)
+ {
+ /* When realloc succeeds the original pointer is invalid. */
+ *moved = (char*)p - (char*)q; // { dg-warning "-Wuse-after-free" }
+ return q;
+ }
+
+ return p;
+}
+
+extern void *evp;
+
+void* warn_realloc_extern (void *p, int *moved)
+{
+ evp = realloc (p, 7);
+ if (evp)
+ {
+ /* When realloc succeeds the original pointer is invalid. */
+ *moved = (char*)p - (char*)evp; // { dg-warning "-Wuse-after-free" "escaped" }
+ return evp;
+ }
+
+ return p; // { dg-bogus "-Wuse-after-free" "safe use after realloc failure" { xfail *-*-* } }
+}
+
+struct A { void *p, *q; int moved; };
+
+void* warn_realloc_arg (struct A *p)
+{
+ p->q = realloc (p->p, 7);
+ if (p->q)
+ {
+ /* When realloc succeeds the original pointer is invalid. */
+ p->moved = p->p != p->q; // { dg-warning "-Wuse-after-free" "escaped" { xfail *-*-* } }
+ return p->q;
+ }
+
+ return p->p;
+}
diff --git a/gcc/testsuite/c-c++-common/Wuse-after-free-3.c b/gcc/testsuite/c-c++-common/Wuse-after-free-3.c
new file mode 100644
index 00000000000..0a2db1a16c8
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wuse-after-free-3.c
@@ -0,0 +1,83 @@
+/* Exercise -Wuse-after-free with user-defined deallocators.
+ { dg-do compile }
</cut>
>From hjl@sc.intel.com Sun Jan 16 06:21:34 2022
Return-Path: <hjl@sc.intel.com>
X-Original-To: gcc-regression@gcc.gnu.org
Delivered-To: gcc-regression@gcc.gnu.org
Received: from mga07.intel.com (mga07.intel.com [134.134.136.100])
by sourceware.org (Postfix) with ESMTPS id B13A33858D3C
for <gcc-regression@gcc.gnu.org>; Sun, 16 Jan 2022 06:21:32 +0000 (GMT)
DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org B13A33858D3C
X-IronPort-AV: E=McAfee;i="6200,9189,10228"; a="307803850"
X-IronPort-AV: E=Sophos;i="5.88,292,1635231600"; d="scan'208";a="307803850"
Received: from orsmga006.jf.intel.com ([10.7.209.51])
by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;
15 Jan 2022 22:21:31 -0800
X-ExtLoop1: 1
X-IronPort-AV: E=Sophos;i="5.88,292,1635231600"; d="scan'208";a="476242261"
Received: from scymds01.sc.intel.com ([10.148.94.138])
by orsmga006.jf.intel.com with ESMTP; 15 Jan 2022 22:21:31 -0800
Received: from gnu-34.sc.intel.com (gnu-34.sc.intel.com [172.25.70.212])
by scymds01.sc.intel.com with ESMTP id 20G6LVk6019425;
Sat, 15 Jan 2022 22:21:31 -0800
Received: by gnu-34.sc.intel.com (Postfix, from userid 1000)
id 1375863FC9; Sat, 15 Jan 2022 22:21:31 -0800 (PST)
Date: Sat, 15 Jan 2022 22:21:30 -0800
To: skpgkp2@gmail.com, hjl.tools@gmail.com, gcc-regression@gcc.gnu.org
Subject: Regressions on master at commit r12-6607 vs commit r12-6603 on
Linux/x86_64
User-Agent: Heirloom mailx 12.5 7/5/10
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Message-Id: <20220116062131.1375863FC9@gnu-34.sc.intel.com>
From: "H.J. Lu" <hjl@sc.intel.com>
X-Spam-Status: No, score=-3467.3 required=5.0 testsºYES_00, KAM_DMARC_STATUS,
KAM_LAZY_DOMAIN_SECURITY, KAM_NUMSUBJECT, SPF_HELO_NONE, SPF_NONE,
TXREP autolearn=no autolearn_force=no version=3.4.4
X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on
server2.sourceware.org
X-BeenThere: gcc-regression@gcc.gnu.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: Gcc-regression mailing list <gcc-regression.gcc.gnu.org>
List-Unsubscribe: <https://gcc.gnu.org/mailman/options/gcc-regression>,
<mailto:gcc-regression-request@gcc.gnu.org?subject=unsubscribe>
List-Archive: <https://gcc.gnu.org/pipermail/gcc-regression/>
List-Post: <mailto:gcc-regression@gcc.gnu.org>
List-Help: <mailto:gcc-regression-request@gcc.gnu.org?subject=help>
List-Subscribe: <https://gcc.gnu.org/mailman/listinfo/gcc-regression>,
<mailto:gcc-regression-request@gcc.gnu.org?subject=subscribe>
X-List-Received-Date: Sun, 16 Jan 2022 06:21:34 -0000
New failures:
FAIL: gcc.dg/torture/pr57147-2.c -O2 -flto -fno-use-linker-plugin -flto-partition=none (test for excess errors)
FAIL: gcc.dg/torture/pr57147-2.c -O2 -flto -fno-use-linker-plugin -flto-partition=none (test for excess errors)
FAIL: gcc.dg/torture/pr57147-2.c -O2 -flto -fno-use-linker-plugin -flto-partition=none (test for excess errors)
FAIL: g++.dg/asan/asan_test.C -O2 (test for excess errors)
FAIL: g++.dg/asan/asan_test.C -O2 (test for excess errors)
FAIL: g++.dg/asan/asan_test.C -O2 (test for excess errors)
New passes:
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2022-01-16 6:05 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-16 6:05 [TCWG CI] Regression caused by gcc: Add -Wuse-after-free [PR80532] ci_notify
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).