From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm1-x334.google.com (mail-wm1-x334.google.com [IPv6:2a00:1450:4864:20::334]) by sourceware.org (Postfix) with ESMTPS id 410793858D35 for ; Sun, 16 Jan 2022 06:05:58 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 410793858D35 Received: by mail-wm1-x334.google.com with SMTP id k5so4686049wmj.3 for ; Sat, 15 Jan 2022 22:05:58 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:date:to:cc:message-id:subject:mime-version :list-id; bh=/Y+aYRLY/y7XvydNlqgpPSHyZdBPxMr54lKCioAoTeM=; b=h/duRrrSNBid0AA8rugv9Gv6G1sW6MXZ55r7MwBDl6cqHRCoZMLrI/vWy1f0wB/UXH b/Xx3+tInQtRb75smrXlj6DdjQeyPeHnGWzu7f1tUFpxvt62jOMahvgrE/xR7oSS5nB8 qKY1Mj9KSYccl7JEIgeTywjP2hFt6ohiXRFZeJ18347cLdI21XumIAYditGwe1HFhoE7 TmSIU7eJh96ubso/xnLzvVXmxq3PlAUTfAIqHIfNBYlXLxVOD5kUlWct0t7O6RDa53fz K7kzPyHDLcEMbxuTIApxV2ErE17rSeoPpe/21/q2Y1JWwNPA9IF3v3iy4ZjzD32X4JL4 xKJA== X-Gm-Message-State: AOAM533SWctUL1oUZrbpZ1nfkTMWik8h689Q4mVWnlqeIqhXcv0QQijw EuHTL8p2vceT8LpRSP0NFUW8pw== X-Google-Smtp-Source: ABdhPJw+pFTMGM9TMEYi7WAhIZ0f8kTYlXfs3K6MNGZbIZyeW4Yy99ppE7d3srdyQP4+goX3jsn9Fw== X-Received: by 2002:adf:ffc7:: with SMTP id x7mr13915506wrs.623.1642313156189; Sat, 15 Jan 2022 22:05:56 -0800 (PST) Received: from jenkins.jenkins (ci.linaro.org. [88.99.136.175]) by smtp.gmail.com with ESMTPSA id i15sm7763391wry.99.2022.01.15.22.05.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 15 Jan 2022 22:05:55 -0800 (PST) From: ci_notify@linaro.org X-Google-Original-From: linaro-infrastructure-errors@lists.linaro.org Date: Sun, 16 Jan 2022 06:05:54 +0000 (UTC) To: Martin Sebor Cc: gcc-regression@gcc.gnu.org Message-ID: <239943168.12442.1642313155667@jenkins.jenkins> Subject: [TCWG CI] Regression caused by gcc: Add -Wuse-after-free [PR80532]. MIME-Version: 1.0 X-Jenkins-Job: TCWG Bisect tcwg_gcc_bootstrap/master-arm-bootstrap X-Jenkins-Result: SUCCESS X-Spam-Status: No, score=-13.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_LOTSOFHASH, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable X-Content-Filtered-By: Mailman/MimeDel 2.1.29 X-BeenThere: gcc-regression@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-regression mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 16 Jan 2022 06:06:02 -0000 [TCWG CI] Regression caused by gcc: Add -Wuse-after-free [PR80532].: commit 671a283636de75f7ed638ee6b01ed2d44361b8b6 Author: Martin Sebor 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/lar= gefile-config.h] Error 1 (ignored) # 00:09:11 /home/tcwg-buildslave/workspace/tcwg_gnu_15/abe/snapshots/gcc.gi= t~master/libcpp/lex.c:1523:9: error: pointer used after =E2=80=98void opera= tor delete(void*, std::size_t)=E2=80=99 [-Werror=3Duse-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, REPRODUCT= ION 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-671a283636de75f7ed638ee6b01ed2d4= 4361b8b6/ Last_good build: https://ci.linaro.org/job/tcwg_gcc_bootstrap-bisect-master= -arm-bootstrap/10/artifact/artifacts/build-29401b7b4581e9131e7057e263dcea8b= 40a6b5ab/ 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-mast= er-arm-bootstrap/10/artifact/artifacts/ Reproduce builds: 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/tcw= g_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/t= cwg_gcc_bootstrap-bisect-master-arm-bootstrap/10/artifact/artifacts/manifes= ts/build-parameters.sh --fail curl -o artifacts/test.sh https://ci.linaro.org/job/tcwg_gcc_bootstrap-bise= ct-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.s= h # 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 .. Full commit (up to 1000 lines): commit 671a283636de75f7ed638ee6b01ed2d44361b8b6 Author: Martin Sebor Date: Sat Jan 15 16:37:54 2022 -0700 Add -Wuse-after-free [PR80532]. =20 gcc/c-family/ChangeLog =20 PR tree-optimization/80532 * c.opt (-Wuse-after-free): New options. =20 gcc/ChangeLog: =20 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. =20 libcpp/ChangeLog: =20 * files.c (_cpp_find_file): Substitute a valid pointer for an invalid one to avoid -Wuse-after-free. =20 libiberty/ChangeLog: =20 * regex.c: Suppress -Wuse-after-free. =20 gcc/testsuite/ChangeLog: =20 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=3D C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_unused_const_var= iable) Warning LangEnabledBy(C ObjC,Wunused-variable, 1, 0) IntegerRange(0,= 2) Warn when a const variable is unused. =20 +# Defining these options here in addition to common.opt is necessary +# in order for the default -Wall setting of -Wuse-after-free=3D2 to take +# effect. + +Wuse-after-free +LangEnabledBy(C ObjC C++ LTO ObjC++) +; in common.opt + +Wuse-after-free=3D +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_MACRO= S) Var(cpp_warn_variadic_macros) Init(0) Warning LangEnabledBy(C ObjC C++ O= bjC++,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=3D Common Joined RejectNegative UInteger Var(warn_array_bounds) Warning Integ= erRange(0, 2) Warn if an array is accessed out of bounds. =20 +Wuse-after-free +Common Var(warn_use_after_free) Warning +Warn for uses of pointers to deallocated strorage. + +Wuse-after-free=3D +Common Joined RejectNegative UInteger Var(warn_use_after_free) Warning Int= egerRange(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) =09m_bits =3D NW_UNINIT; break; =20 + case OPT_Wreturn_local_addr: + case OPT_Wuse_after_free_: + m_bits =3D NW_DANGLING; + break; + default: /* A catchall group for everything else. */ m_bits =3D 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 =3D 1 << 3, /* Warnings about arithmetic overflow. */ NW_VFLOW =3D 1 << 4, + /* Warnings about dangling pointers. */ + NW_DANGLING =3D 1 << 5, /* All other unclassified warnings. */ - NW_OTHER =3D 1 << 5, + NW_OTHER =3D 1 << 6, /* All groups of warnings. */ NW_ALL =3D (NW_ACCESS | NW_LEXICAL | NW_NONNULL -=09 | NW_UNINIT | NW_VFLOW | NW_OTHER) +=09 | NW_UNINIT | NW_VFLOW | NW_DANGLING | NW_OTHER) }; =20 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. =20 +@item -Wuse-after-free +@itemx -Wuse-after-free=3D@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=3D1 +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=3D2 +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 =3D --p->refcount; + free (p); + if (refcount =3D=3D 0) + free (p->data); // warning: p may be used after free +@} +@end smallexample +@item -Wuse-after-free=3D3 +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 =3D (int**)realloc (p, n *=3D 2); + if (q =3D=3D 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=3D2} 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++ Diale= ct Options}. -Wunused-label @gol -Wunused-value @gol -Wunused-variable @gol +-Wuse-after-free=3D3 @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" =20 /* 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); } =20 virtual bool gate (function *); + virtual unsigned int execute (function *); =20 private: @@ -2084,14 +2086,14 @@ private: /* Check a call to a built-in function. */ bool check_builtin (gcall *); =20 - /* 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 *); =20 /* Check statements in a basic block. */ - void check (basic_block); + void check_block (basic_block); =20 /* Check a call to a function. */ - void check (gcall *); + void check_call (gcall *); =20 /* 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 *)= ; =20 + /* 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 =3D fals= e); + + /* 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; }; =20 /* 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 () { } =20 @@ -3071,6 +3092,15 @@ pass_waccess::check_builtin (gcall *stmt) check_read_access (stmt, call_arg (stmt, 0)); return true; =20 + case BUILT_IN_FREE: + case BUILT_IN_REALLOC: + { +=09tree arg =3D call_arg (stmt, 0); +=09if (TREE_CODE (arg) =3D=3D SSA_NAME) +=09 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) =09return true; break; } + return false; } =20 @@ -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. */ =20 bool -pass_waccess::check_call (gcall *stmt) +pass_waccess::check_call_access (gcall *stmt) { tree fntype =3D gimple_call_fntype (stmt); if (!fntype) @@ -3692,46 +3723,442 @@ pass_waccess::maybe_check_dealloc_call (gcall *cal= l) } } =20 +/* 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 statemen= t, + 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 =3D gimple_bb (inval_stmt); + basic_block use_bb =3D gimple_bb (use_stmt); + + if (inval_bb !=3D 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 =3D gsi_start_bb (inval_bb); !gsi_end_p (si); +=09 gsi_next_nondebug (&si)) + { +=09gimple *stmt =3D gsi_stmt (si); +=09unsigned uid =3D inc_gimple_stmt_max_uid (m_func); +=09gimple_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, +=09=09=09=09 gimple *inval_stmt, +=09=09=09=09 bool maybe, +=09=09=09=09 bool equality /* =3D false */) +{ + /* Avoid printing the unhelpful "" in the diagnostics. */ + if (ptr && TREE_CODE (ptr) =3D=3D SSA_NAME + && (!SSA_NAME_VAR (ptr) || DECL_ARTIFICIAL (SSA_NAME_VAR (ptr)))) + ptr =3D NULL_TREE; + + location_t use_loc =3D gimple_location (use_stmt); + if (use_loc =3D=3D UNKNOWN_LOCATION) + { + use_loc =3D cfun->function_end_locus; + if (!ptr) +=09/* Avoid issuing a warning with no context other than +=09 the function. That would make it difficult to debug +=09 in any but very simple cases. */ +=09return; + } + + if (is_gimple_call (inval_stmt)) + { + if ((equality && warn_use_after_free < 3) +=09 || (maybe && warn_use_after_free < 2) +=09 || warning_suppressed_p (use_stmt, OPT_Wuse_after_free)) +=09return; + + const tree inval_decl =3D gimple_call_fndecl (inval_stmt); + + if ((ptr && warning_at (use_loc, OPT_Wuse_after_free, +=09=09=09 (maybe +=09=09=09 ? G_("pointer %qE may be used after %qD") +=09=09=09 : G_("pointer %qE used after %qD")), +=09=09=09 ptr, inval_decl)) +=09 || (!ptr && warning_at (use_loc, OPT_Wuse_after_free, +=09=09=09 (maybe +=09=09=09 ? G_("pointer may be used after %qD") +=09=09=09 : G_("pointer used after %qD")), +=09=09=09=09 inval_decl))) +=09{ +=09 location_t loc =3D gimple_location (inval_stmt); +=09 inform (loc, "call to %qD here", inval_decl); +=09 suppress_warning (use_stmt, OPT_Wuse_after_free); +=09} + 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 =3D gimple_call_arg (stmt, 0); + return gimple_call_lhs (stmt); + } + + gcall *call =3D dyn_cast(stmt); + if (!call) + return NULL_TREE; + + tree fnattr =3D NULL_TREE; + tree fndecl =3D gimple_call_fndecl (call); + if (fndecl) + fnattr =3D DECL_ATTRIBUTES (fndecl); + else + { + tree fntype =3D gimple_call_fntype (stmt); + if (!fntype) +=09return NULL_TREE; + fnattr =3D TYPE_ATTRIBUTES (fntype); + } + + if (!fnattr) + return NULL_TREE; + + for (tree ats =3D fnattr; (ats =3D lookup_attribute ("*dealloc", ats)); + ats =3D TREE_CHAIN (ats)) + { + tree args =3D TREE_VALUE (ats); + if (!args) +=09continue; + + tree alloc =3D TREE_VALUE (args); + if (!alloc) +=09continue; + + if (alloc =3D=3D DECL_NAME (fndecl)) +=09{ +=09 unsigned argno =3D 0; +=09 if (tree index =3D TREE_CHAIN (args)) +=09 argno =3D TREE_INT_CST_LOW (TREE_VALUE (index)) - 1; +=09 *ptr =3D gimple_call_arg (stmt, argno); +=09 return gimple_call_lhs (stmt); +=09} + } + + 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 *stm= t) +{ + if (!is_gimple_call (stmt)) + return false; + + tree fndecl =3D gimple_call_fndecl (stmt); + if (!fndecl) + return false; + + unsigned argno =3D fndecl_dealloc_argno (fndecl); + if (call_nargs (stmt) <=3D argno) + return false; + + if (matching_alloc_calls_p (realloc_stmt, fndecl)) + return false; + + /* Avoid printing the unhelpful "" in the diagnostics. */ + if (ptr && TREE_CODE (ptr) =3D=3D SSA_NAME + && (!SSA_NAME_VAR (ptr) || DECL_ARTIFICIAL (SSA_NAME_VAR (ptr)))) + ptr =3D NULL_TREE; + + location_t loc =3D gimple_location (stmt); + tree realloc_decl =3D gimple_call_fndecl (realloc_stmt); + tree dealloc_decl =3D gimple_call_fndecl (stmt); + if (ptr && !warning_at (loc, OPT_Wmismatched_dealloc, +=09=09=09 "%qD called on pointer %qE passed to mismatched " +=09=09=09 "allocation function %qD", +=09=09=09 dealloc_decl, ptr, realloc_decl)) + return false; + if (!ptr && !warning_at (loc, OPT_Wmismatched_dealloc, +=09=09=09 "%qD called on a pointer passed to mismatched " +=09=09=09 "reallocation function %qD", +=09=09=09 dealloc_decl, realloc_decl)) + return false; + + inform (gimple_location (realloc_stmt), +=09 "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 =3D=3D 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) =3D=3D SSA_NAME); + + const bool check_dangling =3D !is_gimple_call (stmt); + basic_block stmt_bb =3D gimple_bb (stmt); + + /* If STMT is a reallocation function set to the reallocated pointer + and the LHS of the call, respectively. */ + tree realloc_ptr =3D NULL_TREE; + tree realloc_lhs =3D get_realloc_lhs (stmt, &realloc_ptr); + + auto_bitmap visited; + + auto_vec 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 =3D 0; i !=3D pointers.length (); ++i) + { + tree ptr =3D pointers[i]; + if (TREE_CODE (ptr) =3D=3D SSA_NAME +=09 && !bitmap_set_bit (visited, SSA_NAME_VERSION (ptr))) +=09/* Avoid revisiting the same pointer. */ +=09continue; + + use_operand_p use_p; + imm_use_iterator iter; + FOR_EACH_IMM_USE_FAST (use_p, iter, ptr) +=09{ +=09 gimple *use_stmt =3D USE_STMT (use_p); +=09 if (use_stmt =3D=3D stmt || is_gimple_debug (use_stmt)) +=09 continue; + +=09 if (realloc_lhs) +=09 { +=09 /* Check to see if USE_STMT is a mismatched deallocation +=09=09 call for the pointer passed to realloc. That's a bug +=09=09 regardless of the pointer's value and so warn. */ +=09 if (maybe_warn_mismatched_realloc (*use_p->use, stmt, use_stmt)) +=09=09continue; + +=09 /* Pointers passed to realloc that are used in basic blocks +=09=09 where the realloc call is known to have failed are valid. +=09=09 Ignore pointers that nothing is known about. Those could +=09=09 have escaped along with their nullness. */ +=09 value_range vr; +=09 if (m_ptr_qry.rvals->range_of_expr (vr, realloc_lhs, use_stmt)) +=09=09{ +=09=09 if (vr.zero_p ()) +=09=09 continue; + +=09=09 if (!pointers_related_p (stmt, ptr, realloc_ptr, m_ptr_qry)) +=09=09 continue; +=09=09} +=09 } + +=09 if (check_dangling +=09 && gimple_code (use_stmt) =3D=3D GIMPLE_RETURN) +=09 /* Avoid interfering with -Wreturn-local-addr (which runs only +=09 with optimization enabled so it won't diagnose cases that +=09 would be caught here when optimization is disabled). */ +=09 continue; + +=09 bool equality =3D false; +=09 if (is_gimple_assign (use_stmt)) +=09 { +=09 tree_code code =3D gimple_assign_rhs_code (use_stmt); +=09 equality =3D code =3D=3D EQ_EXPR || code =3D=3D NE_EXPR; +=09 } +=09 else if (gcond *cond =3D dyn_cast(use_stmt)) +=09 { +=09 tree_code code =3D gimple_cond_code (cond); +=09 equality =3D code =3D=3D EQ_EXPR || code =3D=3D NE_EXPR; +=09 } + +=09 /* Warn if USE_STMT is dominated by the deallocation STMT. +=09 Otherwise, add the pointer to POINTERS so that the uses +=09 of any other pointers derived from it can be checked. */ +=09 if (use_after_inval_p (stmt, use_stmt)) +=09 { +=09 /* TODO: Handle PHIs but careful of false positives. */ +=09 if (gimple_code (use_stmt) !=3D GIMPLE_PHI) +=09=09{ +=09=09 basic_block use_bb =3D gimple_bb (use_stmt); +=09=09 bool this_maybe +=09=09 =3D !dominated_by_p (CDI_POST_DOMINATORS, use_bb, stmt_bb); +=09=09 warn_invalid_pointer (*use_p->use, use_stmt, stmt, +=09=09=09=09=09this_maybe, equality); +=09=09 continue; +=09=09} +=09 } + +=09 if (is_gimple_assign (use_stmt)) +=09 { +=09 tree lhs =3D gimple_assign_lhs (use_stmt); +=09 if (TREE_CODE (lhs) =3D=3D SSA_NAME) +=09=09{ +=09=09 tree_code rhs_code =3D gimple_assign_rhs_code (use_stmt); +=09=09 if (rhs_code =3D=3D POINTER_PLUS_EXPR || rhs_code =3D=3D SSA_NAME) +=09=09 pointers.safe_push (lhs); +=09=09} +=09 continue; +=09 } + +=09 if (gcall *call =3D dyn_cast (use_stmt)) +=09 { +=09 if (gimple_call_return_arg (call)) +=09=09if (tree lhs =3D gimple_call_lhs (call)) +=09=09 if (TREE_CODE (lhs) =3D=3D SSA_NAME) +=09=09 pointers.safe_push (lhs); +=09 continue; +=09 } +=09} + } +} + /* Check call STMT for invalid accesses. */ =20 void -pass_waccess::check (gcall *stmt) +pass_waccess::check_call (gcall *stmt) { if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)) check_builtin (stmt); =20 - if (is_gimple_call (stmt)) - check_call (stmt); + if (tree callee =3D gimple_call_fndecl (stmt)) + { + /* Check for uses of the pointer passed to either a standard +=09 or a user-defined deallocation function. */ + unsigned argno =3D fndecl_dealloc_argno (callee); + if (argno < (unsigned) call_nargs (stmt)) +=09{ +=09 tree arg =3D call_arg (stmt, argno); +=09 if (TREE_CODE (arg) =3D=3D SSA_NAME) +=09 check_pointer_uses (stmt, arg); +=09} + } =20 - maybe_check_dealloc_call (stmt); + check_call_access (stmt); =20 + maybe_check_dealloc_call (stmt); check_nonstring_args (stmt); } =20 + /* Check basic block BB for invalid accesses. */ =20 void -pass_waccess::check (basic_block bb) +pass_waccess::check_block (basic_block bb) { /* Iterate over statements, looking for function calls. */ - for (auto si =3D gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) + for (auto si =3D gsi_start_bb (bb); !gsi_end_p (si); + gsi_next_nondebug (&si)) { - if (gcall *call =3D dyn_cast (gsi_stmt (si))) -=09check (call); + gimple *stmt =3D gsi_stmt (si); + if (gcall *call =3D dyn_cast (stmt)) +=09check_call (call); } } =20 +/* 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 =3D gimple_call_fnspec (call); + unsigned int argno; + if (!fnspec.returns_arg (&argno)) + { + if (gimple_call_num_args (call) < 1) +=09return NULL_TREE; + + if (!gimple_call_builtin_p (call, BUILT_IN_NORMAL)) +=09return NULL_TREE; + + tree fndecl =3D gimple_call_fndecl (call); + switch (DECL_FUNCTION_CODE (fndecl)) +=09{ +=09case BUILT_IN_MEMPCPY: +=09case BUILT_IN_MEMPCPY_CHK: +=09case BUILT_IN_MEMCHR: +=09case BUILT_IN_STRCHR: +=09case BUILT_IN_STRRCHR: +=09case BUILT_IN_STRSTR: +=09case BUILT_IN_STPCPY: +=09case BUILT_IN_STPCPY_CHK: +=09case BUILT_IN_STPNCPY: +=09case BUILT_IN_STPNCPY_CHK: +=09 argno =3D 0; +=09 break; + +=09default: +=09 return NULL_TREE; +=09} + } + + if (gimple_call_num_args (call) <=3D argno) + return NULL_TREE; + + return gimple_call_arg (call, argno); +} + /* Check function FUN for invalid accesses. */ =20 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 =3D enable_ranger (fun); + m_func =3D fun; + + auto_bitmap bb_uids_set (&bitmap_default_obstack); + m_bb_uids_set =3D bb_uids_set; + + set_gimple_stmt_max_uid (m_func, 0); =20 basic_block bb; FOR_EACH_BB_FN (bb, fun) - check (bb); + check_block (bb); =20 if (dump_file) m_ptr_qry.dump (dump_file, (dump_flags & TDF_DETAILS) !=3D 0); @@ -3743,6 +4170,10 @@ pass_waccess::execute (function *fun) disable_ranger (fun); m_ptr_qry.rvals =3D NULL; =20 + m_bb_uids_set =3D NULL; + + free_dominance_info (CDI_POST_DOMINATORS); + free_dominance_info (CDI_DOMINATORS); return 0; } =20 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 =3D strcpy (p->p, p->a); + free (p); + return *q; +} + +int nowarn_strlen_memptr (struct Member *p) +{ + const char *q =3D 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 =3D p->a; + + free (p); + return strlen (q); // { dg-warning "-Wuse-after-free" "pr??????= " { xfail *-*-* } } + } +} + +void* nowarn_realloc_success (void *p) +{ + void *q =3D 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 =3D realloc (p, 7); + /* Verify that equality is not diagnosed at the default level + (it is diagnosed at level 3). */ + *moved =3D !(p =3D=3D q); + return q; +} + +void* nowarn_realloc_unequal (void *p, int *moved) +{ + void *q =3D realloc (p, 7); + /* Verify that inequality is not diagnosed at the default level + (it is diagnosed at level 3). */ + *moved =3D p !=3D q; + return q; +} + +void* warn_realloc_relational (void *p, int *rel) +{ + void *q =3D 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] =3D (char*)p < (char*)q; // { dg-warning "-Wuse-after-free" } + rel[1] =3D (char*)p <=3D (char*)q; // { dg-warning "-Wuse-after-free" } + rel[2] =3D (char*)p >=3D (char*)q; // { dg-warning "-Wuse-after-free" } + rel[3] =3D (char*)p > (char*)q; // { dg-warning "-Wuse-after-free" } + return q; +} + +void* warn_realloc_unchecked (void *p, int *moved) +{ + void *q =3D 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 =3D (char*)p - (char*)q; // { dg-warning "-Wuse-after-free" } + return q; +} + +void* nowarn_realloc_unchecked_copy (void *p1, void *p2, const void *s, +=09=09=09=09 int n, int *x) +{ + void *p3 =3D memcpy (p1, s, n); + void *p4 =3D realloc (p2, 7); + *x =3D p3 !=3D p4; + return p4; +} + +void* warn_realloc_unchecked_copy (void *p, const void *s, int n, int *mov= ed) +{ + void *p2 =3D memcpy (p, s, n); + void *q =3D realloc (p, 7); // { dg-message "call to '\(void\\* \)= ?realloc\(\\(void\\*, size_t\\)\)?'" "note" } + *moved =3D (char*)p2 - (char*)q; // { dg-warning "-Wuse-after-free" } + return q; +} + +void* warn_realloc_failed (void *p, int *moved) +{ + void *q =3D realloc (p, 7); // { dg-message "call to '\(void\\= * \)?realloc\(\\(void\\*, size_t\\)\)?'" "note" } + if (q) + { + /* When realloc succeeds the original pointer is invalid. */ + *moved =3D (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 =3D realloc (p, 7); + if (evp) + { + /* When realloc succeeds the original pointer is invalid. */ + *moved =3D (char*)p - (char*)evp; // { dg-warning "-Wuse-after-fre= e" "escaped" } + return evp; + } + + return p; // { dg-bogus "-Wuse-after-free" "safe use a= fter realloc failure" { xfail *-*-* } } +} + +struct A { void *p, *q; int moved; }; + +void* warn_realloc_arg (struct A *p) +{ + p->q =3D realloc (p->p, 7); + if (p->q) + { + /* When realloc succeeds the original pointer is invalid. */ + p->moved =3D p->p !=3D p->q; // { dg-warning "-Wuse-after-free" "es= caped" { 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 } >>From hjl@sc.intel.com Sun Jan 16 06:21:34 2022 Return-Path: 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 ; 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" X-Spam-Status: No, score=-3467.3 required=5.0 tests=BAYES_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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-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: