From patchwork Sat Oct 23 23:49:15 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sebor X-Patchwork-Id: 46569 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 869AF3857815 for ; Sat, 23 Oct 2021 23:50:02 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 869AF3857815 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1635033002; bh=zTOa4th/HOwEfO48+vRdxgKsR/ltpEM8lgjWAgISdUA=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=gbWozEzynn5FSVwU7PZmLV5atgGdYCB4mdTo7V8vugYqH4wnbW61BUDkW/9p9puCz L8cFpA6hXn4AMkASH8zySpEmX2jeI46XIn+BNC1gdQSwtt7BU6Znm/XDY8IWsOcR0D FC2/8EpgZZO4UWondj3j7XZjRb5opWOImeJKEkF0= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pj1-x102f.google.com (mail-pj1-x102f.google.com [IPv6:2607:f8b0:4864:20::102f]) by sourceware.org (Postfix) with ESMTPS id 86C403858D28 for ; Sat, 23 Oct 2021 23:49:21 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 86C403858D28 Received: by mail-pj1-x102f.google.com with SMTP id n36-20020a17090a5aa700b0019fa884ab85so8468543pji.5 for ; Sat, 23 Oct 2021 16:49:21 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:to:cc:from:subject:message-id:date:user-agent :mime-version:content-language; bh=zTOa4th/HOwEfO48+vRdxgKsR/ltpEM8lgjWAgISdUA=; b=fcUp0D6ld3olMi/70hl48ZFgaOVBuvm3GbnpBnTqPw1Mz1PCPrjp1zYz199m6/s3ye sVV1DeoAJoYqSz0eO9I9MwnP0QYvcjYwe7f4EdGm53zgTRl17XIJ/scg1ueGpMrh0fVX REmmDqwP7xR0UcbwqLzRzeK2idmwd0G3G+QYs19b0JeMnDc0QxBQH9v8jsVCycYZ61A0 FD5y1B0Xemgr6+L3vJKj4EK6C1xWeWHeBN0H7MOBAQ2lLMKf3LO2AGKV63nyUD39nx4L LMTaClf+h7cLocaxle7Dyjz/mVCsn4Nk/PVMKJhAFwCcGIqigVXXDw8jjyVTMVP9hmPz cmnQ== X-Gm-Message-State: AOAM533wX7CizrQGicodooFMbfluwUrsq6SiRMr4pQ38FuXMFeWrQGXT +2pbJwz0Z+KGgwHCURa+NLA= X-Google-Smtp-Source: ABdhPJzfwM0tXz1rXfh085fG6ZLFOSSS/uVO4mdbv+kOKq103x7d3G2H/Bky8w1aT/0k3V5frirWDA== X-Received: by 2002:a17:902:d485:b0:13f:2212:d631 with SMTP id c5-20020a170902d48500b0013f2212d631mr8096341plg.44.1635032960549; Sat, 23 Oct 2021 16:49:20 -0700 (PDT) Received: from [192.168.0.41] (184-96-250-116.hlrn.qwest.net. [184.96.250.116]) by smtp.gmail.com with ESMTPSA id p13sm10213272pgr.27.2021.10.23.16.49.18 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Sat, 23 Oct 2021 16:49:20 -0700 (PDT) To: gcc-patches Subject: Make full use of context-sensitive ranges in access warnings Message-ID: <1648e958-ece9-dca8-4d47-cded635d48c3@gmail.com> Date: Sat, 23 Oct 2021 17:49:15 -0600 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.2.2 MIME-Version: 1.0 Content-Language: en-US X-Spam-Status: No, score=-10.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, 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 X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Martin Sebor via Gcc-patches From: Martin Sebor Reply-To: Martin Sebor Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Somewhat belatedly following Aldy's lead on finishing the conversion to Ranger, the attached patch modifies gimple-ssa-warn-access and other passes that use the pointer_query machinery to provide Ranger with the statement it's being called to determine ranges for. The changes are almost completely mechanical, involving passing a GIMPLE statement around (and a range_query pointer) all the way into the bowels of the pointer_query class to make them available when range info is being determined. There might be some overlap with Aldy's tree-ssa-strlen.c changes to do the same there. I'll deal with any conflicts when it comes time to commit the work. The changes trigger a couple of -Wstringop-overread instances in libstdc++ tests. The warnings look valid for the IL but the code they're in is unreachable. One of the tests already suppresses -Wstringop-overflow so also suppressing -Wstringop-overread doesn't seem out of line. Tested on x86_64-linux. Martin PS The warning for the u8path-char8_t.cc test is this: /ssd/test/build/gcc-test/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/char_traits.h:355: warning: 'void* __builtin_memcpy(void*, const void*, long unsigned int)' reading between 16 and 4611686018427387903 bytes from a region of size 10 [-Wstringop-overread] The IL for it is below. The loop iN BB 3 exits with __i_22 equal to 10 so BBs 5, 6 and 7 are unreachable. It's surprising to me that the loop isn't optimized into something better (like a MEM array assignment or memcpy). [local count: 1073741824]: MEM[(struct basic_string *)&s1] ={v} {CLOBBER}; MEM[(struct _Alloc_hider *)&s1] ={v} {CLOBBER}; MEM[(struct _Alloc_hider *)&s1]._M_p = &s1.D.30357._M_local_buf; [local count: 8687547547]: # __i_109 = PHI <__i_22(3), 0(2)> __i_22 = __i_109 + 1; _24 = MEM[(const char_type &)"filename2" + __i_22 * 1]; if (_24 != 0) goto ; [89.00%] else goto ; [11.00%] [local count: 1073741824]: <<< __i_22 == 10 here if (__i_22 > 15) goto ; [33.00%] else goto ; [67.00%] [local count: 354334802]: if (__i_22 > 4611686018427387903) goto ; [0.04%] else goto ; [99.96%] >>> __i_22 in [16, 4611686018427387903] [local count: 141736]: std::__throw_length_error ("basic_string::_M_create"); [local count: 354193066]: _85 = __i_109 + 2; _42 = operator new (_85); s1._M_dataplus._M_p = _42; s1.D.30357._M_allocated_capacity = __i_22; __builtin_memcpy (_42, "filename2", __i_22); << -Wstringop-overread Make full use of context-sensitive ranges in access warnings. gcc/ChangeLog: * builtins.c (check_strncat_sizes): Pass access_data ctor additional arguments. (expand_builtin_memcmp): Move code to gimple-ssa-warn-access.cc. (expand_builtin_fork_or_exec): Same. * gimple-array-bounds.cc (array_bounds_checker::check_mem_ref): Pass compute_objsize additional arguments. (inbounds_memaccess_p): Same. (array_bounds_checker::check_array_bounds): Add an assert. Stash statement in a member. (check_array_bounds_dom_walker::before_dom_children): * gimple-array-bounds.h (array_bounds_checker::m_stmt): New member. * gimple-ssa-sprintf.c (get_destination_size): Add an argument. (handle_printf_call): Pass a new argument. * gimple-ssa-warn-access.cc (get_size_range): Add an argument. (check_access): Add an argument and pass it along to callees. (check_read_access): Make a member function. (pass_waccess::check_strcat): Pass access_data ctor additional arguments. (pass_waccess::check_strncat): Same. (pass_waccess::check_stxcpy): Same. (pass_waccess::check_stxncpy): Same. (pass_waccess::check_strncmp): Same. (pass_waccess::check_read_access): (pass_waccess::check_builtin): (pass_waccess::maybe_check_access_sizes): (pass_waccess::maybe_check_dealloc_call): * gimple-ssa-warn-access.h (check_read_access): Declare a new member function. * pointer-query.cc (compute_objsize_r): Add an argument. (gimple_call_return_array): Same. (gimple_call_alloc_size): Same. (access_ref::access_ref): Same. (access_ref::get_ref): Same. (pointer_query::get_ref): Same. (handle_min_max_size): Pass an arguments to callees. (handle_array_ref): Add an argument. (handle_mem_ref): Same. (compute_objsize): Same. * pointer-query.h (struct access_ref): Adjust signatures. (struct access_data): Same. (gimple_call_alloc_size): Add an argument. (gimple_parm_array_size): Same. (compute_objsize): Same. * tree-ssa-strlen.c (strlen_pass::adjust_last_stmt): Pass an additional argument to compute_objsize. (strlen_pass::maybe_warn_overflow): Same. (maybe_diag_stxncpy_trunc): Same. gcc/testsuite/ChangeLog: * gcc.dg/Wstringop-overflow-22.c: Correct typos. * gcc.dg/Wstringop-overflow-77.c: New test. libstdc++/testsuite/ChangeLog: * 21_strings/basic_string/capacity/1.cc: Also suppress -Wstringop-overread. * 27_io/filesystem/path/factory/u8path-char8_t.cc: Same. diff --git a/gcc/builtins.c b/gcc/builtins.c index f1c3fea3583..7d0f61fc98b 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -3600,7 +3600,7 @@ check_strncat_sizes (tree exp, tree objsize) /* Try to verify that the destination is big enough for the shortest string. */ - access_data data (exp, access_read_write, maxread, true); + access_data data (nullptr, exp, access_read_write, maxread, true); if (!objsize && warn_stringop_overflow) { /* If it hasn't been provided by __strncat_chk, try to determine @@ -4260,12 +4260,6 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq) tree arg2 = CALL_EXPR_ARG (exp, 1); tree len = CALL_EXPR_ARG (exp, 2); - /* Diagnose calls where the specified length exceeds the size of either - object. */ - if (!check_read_access (exp, arg1, len, 0) - || !check_read_access (exp, arg2, len, 0)) - return NULL_RTX; - /* Due to the performance benefit, always inline the calls first when result_eq is false. */ rtx result = NULL_RTX; @@ -5486,27 +5480,6 @@ expand_builtin_fork_or_exec (tree fn, tree exp, rtx target, int ignore) tree id, decl; tree call; - if (DECL_FUNCTION_CODE (fn) != BUILT_IN_FORK) - { - tree path = CALL_EXPR_ARG (exp, 0); - /* Detect unterminated path. */ - if (!check_read_access (exp, path)) - return NULL_RTX; - - /* Also detect unterminated first argument. */ - switch (DECL_FUNCTION_CODE (fn)) - { - case BUILT_IN_EXECL: - case BUILT_IN_EXECLE: - case BUILT_IN_EXECLP: - if (!check_read_access (exp, path)) - return NULL_RTX; - default: - break; - } - } - - /* If we are not profiling, just call the function. */ if (!profile_arc_flag) return NULL_RTX; diff --git a/gcc/gimple-array-bounds.cc b/gcc/gimple-array-bounds.cc index 0517e5ddd8e..a3535598998 100644 --- a/gcc/gimple-array-bounds.cc +++ b/gcc/gimple-array-bounds.cc @@ -426,7 +426,7 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref, axssize = wi::to_offset (access_size); access_ref aref; - if (!compute_objsize (ref, 0, &aref, ranges)) + if (!compute_objsize (ref, m_stmt, 0, &aref, ranges)) return false; if (aref.offset_in_range (axssize)) @@ -667,7 +667,7 @@ array_bounds_checker::check_addr_expr (location_t location, tree t, problems discussed in pr98266 and pr97595. */ static bool -inbounds_memaccess_p (tree t) +inbounds_memaccess_p (tree t, gimple *stmt) { if (TREE_CODE (t) != COMPONENT_REF) return false; @@ -686,7 +686,7 @@ inbounds_memaccess_p (tree t) allocated). */ access_ref aref; // unused tree refop = TREE_OPERAND (mref, 0); - tree refsize = compute_objsize (refop, 1, &aref); + tree refsize = compute_objsize (refop, stmt, 1, &aref); if (!refsize || TREE_CODE (refsize) != INTEGER_CST) return false; @@ -724,6 +724,7 @@ array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree, { tree t = *tp; struct walk_stmt_info *wi = (struct walk_stmt_info *) data; + location_t location; if (EXPR_HAS_LOCATION (t)) @@ -735,6 +736,8 @@ array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree, bool warned = false; array_bounds_checker *checker = (array_bounds_checker *) wi->info; + gcc_assert (checker->m_stmt == wi->stmt); + if (TREE_CODE (t) == ARRAY_REF) warned = checker->check_array_ref (location, t, wi->stmt, false/*ignore_off_by_one*/); @@ -746,7 +749,7 @@ array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree, checker->check_addr_expr (location, t, wi->stmt); *walk_subtree = false; } - else if (inbounds_memaccess_p (t)) + else if (inbounds_memaccess_p (t, wi->stmt)) /* Hack: Skip MEM_REF checks in accesses to a member of a base class at an offset that's within the bounds of the enclosing object. See pr98266 and pr97595. */ @@ -794,14 +797,13 @@ check_array_bounds_dom_walker::before_dom_children (basic_block bb) for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) { gimple *stmt = gsi_stmt (si); - struct walk_stmt_info wi; if (!gimple_has_location (stmt) || is_gimple_debug (stmt)) continue; - memset (&wi, 0, sizeof (wi)); - + struct walk_stmt_info wi{ }; wi.info = checker; + checker->m_stmt = stmt; walk_gimple_op (stmt, array_bounds_checker::check_array_bounds, &wi); } diff --git a/gcc/gimple-array-bounds.h b/gcc/gimple-array-bounds.h index d8f7ff7a89f..d0e665eff11 100644 --- a/gcc/gimple-array-bounds.h +++ b/gcc/gimple-array-bounds.h @@ -36,8 +36,12 @@ private: void check_addr_expr (location_t, tree, gimple *); const value_range *get_value_range (const_tree op, gimple *); + /* Current function. */ struct function *fun; + /* Ranger instance. */ range_query *ranges; + /* Current statement. */ + gimple *m_stmt; }; #endif // GCC_GIMPLE_ARRAY_BOUNDS_H diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index 8e90b7cfc43..8f42cb59540 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -4030,11 +4030,11 @@ compute_format_length (call_info &info, format_result *res, range_query *query) return success; } -/* Return the size of the object referenced by the expression DEST if - available, or the maximum possible size otherwise. */ +/* Return the size of the object referenced by the expression DEST in + statement STMT, if available, or the maximum possible size otherwise. */ static unsigned HOST_WIDE_INT -get_destination_size (tree dest, pointer_query &ptr_qry) +get_destination_size (tree dest, gimple *stmt, pointer_query &ptr_qry) { /* When there is no destination return the maximum. */ if (!dest) @@ -4042,7 +4042,7 @@ get_destination_size (tree dest, pointer_query &ptr_qry) /* Use compute_objsize to determine the size of the destination object. */ access_ref aref; - if (!ptr_qry.get_ref (dest, &aref)) + if (!ptr_qry.get_ref (dest, stmt, &aref)) return HOST_WIDE_INT_MAX; offset_int remsize = aref.size_remaining (); @@ -4516,7 +4516,7 @@ handle_printf_call (gimple_stmt_iterator *gsi, pointer_query &ptr_qry) /* For non-bounded functions like sprintf, determine the size of the destination from the object or pointer passed to it as the first argument. */ - dstsize = get_destination_size (dstptr, ptr_qry); + dstsize = get_destination_size (dstptr, info.callstmt, ptr_qry); } else if (tree size = gimple_call_arg (info.callstmt, idx_dstsize)) { diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc index 00c3ea0f505..d3616ef725f 100644 --- a/gcc/gimple-ssa-warn-access.cc +++ b/gcc/gimple-ssa-warn-access.cc @@ -1190,11 +1190,11 @@ warn_for_access (location_t loc, tree func, tree expr, int opt, by BNDRNG if nonnull and valid. */ static void -get_size_range (range_query *query, tree bound, tree range[2], +get_size_range (range_query *query, tree bound, gimple *stmt, tree range[2], const offset_int bndrng[2]) { if (bound) - get_size_range (query, bound, NULL, range); + get_size_range (query, bound, stmt, range); if (!bndrng || (bndrng[0] == 0 && bndrng[1] == HOST_WIDE_INT_M1U)) return; @@ -1251,7 +1251,8 @@ template static bool check_access (GimpleOrTree exp, tree dstwrite, tree maxread, tree srcstr, tree dstsize, - access_mode mode, const access_data *pad /* = NULL */) + access_mode mode, const access_data *pad, + range_query *rvals) { /* The size of the largest object is half the address space, or PTRDIFF_MAX. (This is way too permissive.) */ @@ -1338,7 +1339,8 @@ check_access (GimpleOrTree exp, tree dstwrite, /* Set RANGE to that of DSTWRITE if non-null, bounded by PAD->DST.BNDRNG if valid. */ - get_size_range (NULL, dstwrite, range, pad ? pad->dst.bndrng : NULL); + gimple *stmt = pad ? pad->stmt : nullptr; + get_size_range (rvals, dstwrite, stmt, range, pad ? pad->dst.bndrng : NULL); tree func = get_callee_fndecl (exp); /* Read vs write access by built-ins can be determined from the const @@ -1432,7 +1434,7 @@ check_access (GimpleOrTree exp, tree dstwrite, { /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if PAD is nonnull and BNDRNG is valid. */ - get_size_range (NULL, maxread, range, pad ? pad->src.bndrng : NULL); + get_size_range (rvals, maxread, stmt, range, pad ? pad->src.bndrng : NULL); location_t loc = get_location (exp); tree size = dstsize; @@ -1479,7 +1481,7 @@ check_access (GimpleOrTree exp, tree dstwrite, { /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if PAD is nonnull and BNDRNG is valid. */ - get_size_range (NULL, maxread, range, pad ? pad->src.bndrng : NULL); + get_size_range (rvals, maxread, stmt, range, pad ? pad->src.bndrng : NULL); /* Set OVERREAD for reads starting just past the end of an object. */ overread = pad->src.sizrng[1] - pad->src.offrng[0] < pad->src.bndrng[0]; range[0] = wide_int_to_tree (sizetype, pad->src.bndrng[0]); @@ -1512,13 +1514,14 @@ check_access (GimpleOrTree exp, tree dstwrite, return true; } -bool +static bool check_access (gimple *stmt, tree dstwrite, tree maxread, tree srcstr, tree dstsize, - access_mode mode, const access_data *pad /* = NULL */) + access_mode mode, const access_data *pad, + range_query *rvals) { - return check_access(stmt, dstwrite, maxread, srcstr, dstsize, - mode, pad); + return check_access (stmt, dstwrite, maxread, srcstr, dstsize, + mode, pad, rvals); } bool @@ -1526,45 +1529,8 @@ check_access (tree expr, tree dstwrite, tree maxread, tree srcstr, tree dstsize, access_mode mode, const access_data *pad /* = NULL */) { - return check_access(expr, dstwrite, maxread, srcstr, dstsize, - mode, pad); -} - -/* A convenience wrapper for check_access above to check access - by a read-only function like puts. */ - -template -static bool -check_read_access (GimpleOrTree expr, tree src, tree bound, int ost) -{ - if (!warn_stringop_overread) - return true; - - if (bound && !useless_type_conversion_p (size_type_node, TREE_TYPE (bound))) - bound = fold_convert (size_type_node, bound); - - tree fndecl = get_callee_fndecl (expr); - maybe_warn_nonstring_arg (fndecl, expr); - - access_data data (expr, access_read_only, NULL_TREE, false, bound, true); - compute_objsize (src, ost, &data.src); - return check_access (expr, /*dstwrite=*/ NULL_TREE, /*maxread=*/ bound, - /*srcstr=*/ src, /*dstsize=*/ NULL_TREE, data.mode, - &data); -} - -bool -check_read_access (gimple *stmt, tree src, tree bound /* = NULL_TREE */, - int ost /* = 1 */) -{ - return check_read_access(stmt, src, bound, ost); -} - -bool -check_read_access (tree expr, tree src, tree bound /* = NULL_TREE */, - int ost /* = 1 */) -{ - return check_read_access(expr, src, bound, ost); + return check_access (expr, dstwrite, maxread, srcstr, dstsize, + mode, pad, nullptr); } /* Return true if STMT is a call to an allocation function. Unless @@ -2130,6 +2096,7 @@ private: void check_stxncpy (gcall *); void check_strncmp (gcall *); void check_memop_access (gimple *, tree, tree, tree); + void check_read_access (gimple *, tree, tree = NULL_TREE, int = 1); void maybe_check_dealloc_call (gcall *); void maybe_check_access_sizes (rdwr_map *, tree, tree, gimple *); @@ -2425,14 +2392,14 @@ pass_waccess::check_strcat (gcall *stmt) the destination to which the SRC string is being appended so just diagnose cases when the souce string is longer than the destination object. */ - access_data data (stmt, access_read_write, NULL_TREE, true, - NULL_TREE, true); + access_data data (m_ptr_qry.rvals, stmt, access_read_write, NULL_TREE, + true, NULL_TREE, true); const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1; - compute_objsize (src, ost, &data.src, &m_ptr_qry); - tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry); + compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry); + tree destsize = compute_objsize (dest, stmt, ost, &data.dst, &m_ptr_qry); check_access (stmt, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE, - src, destsize, data.mode, &data); + src, destsize, data.mode, &data, m_ptr_qry.rvals); } /* Check a call STMT to strcat() for overflow and warn if it does. */ @@ -2466,12 +2433,12 @@ pass_waccess::check_strncat (gcall *stmt) maxlen = lendata.maxbound; } - access_data data (stmt, access_read_write); + access_data data (m_ptr_qry.rvals, stmt, access_read_write); /* Try to verify that the destination is big enough for the shortest string. First try to determine the size of the destination object into which the source is being copied. */ const int ost = warn_stringop_overflow - 1; - tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry); + tree destsize = compute_objsize (dest, stmt, ost, &data.dst, &m_ptr_qry); /* Add one for the terminating nul. */ tree srclen = (maxlen @@ -2500,7 +2467,7 @@ pass_waccess::check_strncat (gcall *stmt) srclen = maxread; check_access (stmt, /*dstwrite=*/NULL_TREE, maxread, srclen, - destsize, data.mode, &data); + destsize, data.mode, &data, m_ptr_qry.rvals); } /* Check a call STMT to stpcpy() or strcpy() for overflow and warn @@ -2524,14 +2491,14 @@ pass_waccess::check_stxcpy (gcall *stmt) if (warn_stringop_overflow) { - access_data data (stmt, access_read_write, NULL_TREE, true, - NULL_TREE, true); + access_data data (m_ptr_qry.rvals, stmt, access_read_write, NULL_TREE, + true, NULL_TREE, true); const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1; - compute_objsize (src, ost, &data.src, &m_ptr_qry); - tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry); + compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry); + tree dstsize = compute_objsize (dst, stmt, ost, &data.dst, &m_ptr_qry); check_access (stmt, /*dstwrite=*/ NULL_TREE, /*maxread=*/ NULL_TREE, /*srcstr=*/ src, - dstsize, data.mode, &data); + dstsize, data.mode, &data, m_ptr_qry.rvals); } /* Check to see if the argument was declared attribute nonstring @@ -2555,13 +2522,14 @@ pass_waccess::check_stxncpy (gcall *stmt) /* The number of bytes to write (not the maximum). */ tree len = call_arg (stmt, 2); - access_data data (stmt, access_read_write, len, true, len, true); + access_data data (m_ptr_qry.rvals, stmt, access_read_write, len, true, len, + true); const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1; - compute_objsize (src, ost, &data.src, &m_ptr_qry); - tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry); + compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry); + tree dstsize = compute_objsize (dst, stmt, ost, &data.dst, &m_ptr_qry); - check_access (stmt, /*dstwrite=*/len, - /*maxread=*/len, src, dstsize, data.mode, &data); + check_access (stmt, /*dstwrite=*/len, /*maxread=*/len, src, dstsize, + data.mode, &data, m_ptr_qry.rvals); } /* Check a call STMT to stpncpy() or strncpy() for overflow and warn @@ -2594,6 +2562,11 @@ pass_waccess::check_strncmp (gcall *stmt) tree len1 = c_strlen (arg1, 1, &lendata1); tree len2 = c_strlen (arg2, 1, &lendata2); + if (len1 && TREE_CODE (len1) != INTEGER_CST) + len1 = NULL_TREE; + if (len2 && TREE_CODE (len2) != INTEGER_CST) + len2 = NULL_TREE; + if (len1 && len2) /* If the length of both arguments was computed they must both be nul-terminated and no further checking is necessary regardless @@ -2606,13 +2579,15 @@ pass_waccess::check_strncmp (gcall *stmt) if (maybe_warn_nonstring_arg (get_callee_fndecl (stmt), stmt)) return; - access_data adata1 (stmt, access_read_only, NULL_TREE, false, bound, true); - access_data adata2 (stmt, access_read_only, NULL_TREE, false, bound, true); + access_data adata1 (m_ptr_qry.rvals, stmt, access_read_only, NULL_TREE, false, + bound, true); + access_data adata2 (m_ptr_qry.rvals, stmt, access_read_only, NULL_TREE, false, + bound, true); /* Determine the range of the bound first and bail if it fails; it's cheaper than computing the size of the objects. */ tree bndrng[2] = { NULL_TREE, NULL_TREE }; - get_size_range (m_ptr_qry.rvals, bound, bndrng, adata1.src.bndrng); + get_size_range (m_ptr_qry.rvals, bound, stmt, bndrng, adata1.src.bndrng); if (!bndrng[0] || integer_zerop (bndrng[0])) return; @@ -2623,8 +2598,8 @@ pass_waccess::check_strncmp (gcall *stmt) /* compute_objsize almost never fails (and ultimately should never fail). Don't bother to handle the rare case when it does. */ - if (!compute_objsize (arg1, 1, &adata1.src, &m_ptr_qry) - || !compute_objsize (arg2, 1, &adata2.src, &m_ptr_qry)) + if (!compute_objsize (arg1, stmt, 1, &adata1.src, &m_ptr_qry) + || !compute_objsize (arg2, stmt, 1, &adata2.src, &m_ptr_qry)) return; /* Compute the size of the remaining space in each array after @@ -2672,15 +2647,41 @@ pass_waccess::check_memop_access (gimple *stmt, tree dest, tree src, tree size) try to determine the size of the largest source and destination object using type-0 Object Size regardless of the object size type specified by the option. */ - access_data data (stmt, access_read_write); + access_data data (m_ptr_qry.rvals, stmt, access_read_write); tree srcsize - = src ? compute_objsize (src, 0, &data.src, &m_ptr_qry) : NULL_TREE; - tree dstsize = compute_objsize (dest, 0, &data.dst, &m_ptr_qry); + = src ? compute_objsize (src, stmt, 0, &data.src, &m_ptr_qry) : NULL_TREE; + tree dstsize = compute_objsize (dest, stmt, 0, &data.dst, &m_ptr_qry); + + check_access (stmt, size, /*maxread=*/NULL_TREE, srcsize, dstsize, + data.mode, &data, m_ptr_qry.rvals); +} + +/* A convenience wrapper for check_access to check access by a read-only + function like puts or strcmp. */ + +void +pass_waccess::check_read_access (gimple *stmt, tree src, + tree bound /* = NULL_TREE */, + int ost /* = 1 */) +{ + if (!warn_stringop_overread) + return; + + if (bound && !useless_type_conversion_p (size_type_node, TREE_TYPE (bound))) + bound = fold_convert (size_type_node, bound); + + tree fndecl = get_callee_fndecl (stmt); + maybe_warn_nonstring_arg (fndecl, stmt); - check_access (stmt, size, /*maxread=*/NULL_TREE, - srcsize, dstsize, data.mode, &data); + access_data data (m_ptr_qry.rvals, stmt, access_read_only, NULL_TREE, + false, bound, true); + compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry); + check_access (stmt, /*dstwrite=*/ NULL_TREE, /*maxread=*/ bound, + /*srcstr=*/ src, /*dstsize=*/ NULL_TREE, data.mode, + &data, m_ptr_qry.rvals); } + /* Check call STMT to a built-in function for invalid accesses. Return true if a call has been handled. */ @@ -2699,6 +2700,15 @@ pass_waccess::check_builtin (gcall *stmt) check_alloca (stmt); return true; + case BUILT_IN_EXECL: + case BUILT_IN_EXECLE: + case BUILT_IN_EXECLP: + case BUILT_IN_EXECV: + case BUILT_IN_EXECVE: + case BUILT_IN_EXECVP: + check_read_access (stmt, call_arg (stmt, 0)); + return true; + case BUILT_IN_GETTEXT: case BUILT_IN_PUTS: case BUILT_IN_PUTS_UNLOCKED: @@ -2721,8 +2731,12 @@ pass_waccess::check_builtin (gcall *stmt) case BUILT_IN_STRNDUP: case BUILT_IN_STRNLEN: - check_read_access (stmt, call_arg (stmt, 0), call_arg (stmt, 1)); - return true; + { + tree str = call_arg (stmt, 0); + tree len = call_arg (stmt, 1); + check_read_access (stmt, str, len); + return true; + } case BUILT_IN_STRCAT: check_strcat (stmt); @@ -2900,7 +2914,7 @@ pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype, /* Format the value or range to avoid an explosion of messages. */ char sizstr[80]; tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) }; - if (get_size_range (m_ptr_qry.rvals, access_size, NULL, sizrng, 1)) + if (get_size_range (m_ptr_qry.rvals, access_size, stmt, sizrng, 1)) { char *s0 = print_generic_expr_to_str (sizrng[0]); if (tree_int_cst_equal (sizrng[0], sizrng[1])) @@ -3028,11 +3042,11 @@ pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype, } } - access_data data (ptr, access.second.mode, NULL_TREE, false, - NULL_TREE, false); + access_data data (m_ptr_qry.rvals, stmt, access.second.mode, + NULL_TREE, false, NULL_TREE, false); access_ref* const pobj = (access.second.mode == access_write_only ? &data.dst : &data.src); - tree objsize = compute_objsize (ptr, 1, pobj, &m_ptr_qry); + tree objsize = compute_objsize (ptr, stmt, 1, pobj, &m_ptr_qry); /* The size of the destination or source object. */ tree dstsize = NULL_TREE, srcsize = NULL_TREE; @@ -3064,7 +3078,7 @@ pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype, if (mode == access_deferred) mode = TYPE_READONLY (argtype) ? access_read_only : access_read_write; check_access (stmt, access_size, /*maxread=*/ NULL_TREE, srcsize, - dstsize, mode, &data); + dstsize, mode, &data, m_ptr_qry.rvals); if (warning_suppressed_p (stmt, OPT_Wstringop_overflow_)) opt_warned = OPT_Wstringop_overflow_; @@ -3187,7 +3201,7 @@ pass_waccess::maybe_check_dealloc_call (gcall *call) return; access_ref aref; - if (!compute_objsize (ptr, 0, &aref, &m_ptr_qry)) + if (!compute_objsize (ptr, call, 0, &aref, &m_ptr_qry)) return; tree ref = aref.ref; diff --git a/gcc/gimple-ssa-warn-access.h b/gcc/gimple-ssa-warn-access.h index 00f5bb1a7b2..124f3f530d3 100644 --- a/gcc/gimple-ssa-warn-access.h +++ b/gcc/gimple-ssa-warn-access.h @@ -45,7 +45,4 @@ class access_data; extern bool check_access (tree, tree, tree, tree, tree, access_mode, const access_data * = NULL); -extern bool check_read_access (gimple *, tree, tree = NULL_TREE, int ost = 1); -extern bool check_read_access (tree, tree, tree = NULL_TREE, int = 1); - #endif // GCC_GIMPLE_SSA_WARN_ACCESS_H diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc index 910f452868e..3b9f970846b 100644 --- a/gcc/pointer-query.cc +++ b/gcc/pointer-query.cc @@ -43,8 +43,8 @@ #include "tree-ssanames.h" #include "target.h" -static bool compute_objsize_r (tree, int, access_ref *, ssa_name_limit_t &, - pointer_query *); +static bool compute_objsize_r (tree, gimple *, int, access_ref *, + ssa_name_limit_t &, pointer_query *); /* Wrapper around the wide_int overload of get_range that accepts offset_int instead. For middle end expressions returns the same @@ -115,7 +115,7 @@ get_offset_range (tree x, gimple *stmt, offset_int r[2], range_query *rvals) static tree gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end, - range_query *rvals) + ssa_name_limit_t &snlim, pointer_query *qry) { /* Clear and set below for the rare function(s) that might return a past-the-end pointer. */ @@ -191,7 +191,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end, offrng[0] = 0; offrng[1] = HOST_WIDE_INT_M1U; tree off = gimple_call_arg (stmt, 2); - bool off_valid = get_offset_range (off, stmt, offrng, rvals); + bool off_valid = get_offset_range (off, stmt, offrng, qry->rvals); if (!off_valid || offrng[0] != offrng[1]) { /* If the offset is either indeterminate or in some range, @@ -199,7 +199,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end, of the source object. */ access_ref aref; tree src = gimple_call_arg (stmt, 1); - if (compute_objsize (src, 1, &aref, rvals) + if (compute_objsize (src, stmt, 1, &aref, qry) && aref.sizrng[1] < offrng[1]) offrng[1] = aref.sizrng[1]; } @@ -212,7 +212,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end, case BUILT_IN_MEMCHR: { tree off = gimple_call_arg (stmt, 2); - if (get_offset_range (off, stmt, offrng, rvals)) + if (get_offset_range (off, stmt, offrng, qry->rvals)) offrng[1] -= 1; else offrng[1] = HOST_WIDE_INT_M1U; @@ -233,7 +233,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end, { access_ref aref; tree src = gimple_call_arg (stmt, 1); - if (compute_objsize (src, 1, &aref, rvals)) + if (compute_objsize_r (src, stmt, 1, &aref, snlim, qry)) offrng[1] = aref.sizrng[1] - 1; else offrng[1] = HOST_WIDE_INT_M1U; @@ -250,7 +250,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end, and the source object size. */ offrng[1] = HOST_WIDE_INT_M1U; tree off = gimple_call_arg (stmt, 2); - if (!get_offset_range (off, stmt, offrng, rvals) + if (!get_offset_range (off, stmt, offrng, qry->rvals) || offrng[0] != offrng[1]) { /* If the offset is either indeterminate or in some range, @@ -258,7 +258,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end, of the source object. */ access_ref aref; tree src = gimple_call_arg (stmt, 1); - if (compute_objsize (src, 1, &aref, rvals) + if (compute_objsize_r (src, stmt, 1, &aref, snlim, qry) && aref.sizrng[1] < offrng[1]) offrng[1] = aref.sizrng[1]; } @@ -445,7 +445,7 @@ get_size_range (tree exp, tree range[2], int flags /* = 0 */) tree gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */, - range_query * /* = NULL */) + range_query *qry /* = NULL */) { if (!stmt || !is_gimple_call (stmt)) return NULL_TREE; @@ -503,7 +503,7 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */, { tree r[2]; /* Determine the largest valid range size, including zero. */ - if (!get_size_range (size, r, SR_ALLOW_ZERO | SR_USE_LARGEST)) + if (!get_size_range (qry, size, stmt, r, SR_ALLOW_ZERO | SR_USE_LARGEST)) return NULL_TREE; rng1[0] = wi::to_wide (r[0], prec); rng1[1] = wi::to_wide (r[1], prec); @@ -519,7 +519,7 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */, { tree r[2]; /* As above, use the full non-negative range on failure. */ - if (!get_size_range (n, r, SR_ALLOW_ZERO | SR_USE_LARGEST)) + if (!get_size_range (qry, n, stmt, r, SR_ALLOW_ZERO | SR_USE_LARGEST)) return NULL_TREE; rng2[0] = wi::to_wide (r[0], prec); rng2[1] = wi::to_wide (r[1], prec); @@ -546,7 +546,7 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */, Set STATIC_ARRAY if the array parameter has been declared [static]. Return the function parameter on success and null otherwise. */ -tree +static tree gimple_parm_array_size (tree ptr, wide_int rng[2], bool *static_array /* = NULL */) { @@ -596,10 +596,17 @@ gimple_parm_array_size (tree ptr, wide_int rng[2], return var; } -access_ref::access_ref (tree bound /* = NULL_TREE */, +/* Given a statement STMT, set the bounds of the reference to at most + as many bytes as BOUND or unknown when null, and at least one when + the MINACCESS is true unless BOUND is a constant zero. STMT is + used for context to get accurate range info. */ + +access_ref::access_ref (range_query *qry /* = nullptr */, + tree bound /* = NULL_TREE */, + gimple *stmt /* = nullptr */, bool minaccess /* = false */) -: ref (), eval ([](tree x){ return x; }), deref (), trail1special (true), - base0 (true), parmarray () + : ref (), eval ([](tree x){ return x; }), deref (), trail1special (true), + base0 (true), parmarray () { /* Set to valid. */ offrng[0] = offrng[1] = 0; @@ -615,7 +622,7 @@ access_ref::access_ref (tree bound /* = NULL_TREE */, set the bounds of the access to reflect both it and MINACCESS. BNDRNG[0] is the size of the minimum access. */ tree rng[2]; - if (bound && get_size_range (bound, rng, SR_ALLOW_ZERO)) + if (bound && get_size_range (qry, bound, stmt, rng, SR_ALLOW_ZERO)) { bndrng[0] = wi::to_offset (rng[0]); bndrng[1] = wi::to_offset (rng[1]); @@ -696,7 +703,8 @@ access_ref::get_ref (vec *all_refs, { access_ref phi_arg_ref; tree arg = gimple_phi_arg_def (phi_stmt, i); - if (!compute_objsize_r (arg, ostype, &phi_arg_ref, *psnlim, qry) + if (!compute_objsize_r (arg, phi_stmt, ostype, &phi_arg_ref, *psnlim, + qry) || phi_arg_ref.sizrng[0] < 0) /* A PHI with all null pointer arguments. */ return NULL_TREE; @@ -1312,7 +1320,7 @@ pointer_query::get_ref (tree ptr, int ostype /* = 1 */) const there or compute it and insert it into the cache if it's nonnonull. */ bool -pointer_query::get_ref (tree ptr, access_ref *pref, int ostype /* = 1 */) +pointer_query::get_ref (tree ptr, gimple *stmt, access_ref *pref, int ostype /* = 1 */) { const unsigned version = TREE_CODE (ptr) == SSA_NAME ? SSA_NAME_VERSION (ptr) : 0; @@ -1335,7 +1343,7 @@ pointer_query::get_ref (tree ptr, access_ref *pref, int ostype /* = 1 */) ++misses; } - if (!compute_objsize (ptr, ostype, pref, this)) + if (!compute_objsize (ptr, stmt, ostype, pref, this)) { ++failures; return false; @@ -1502,7 +1510,7 @@ static bool handle_min_max_size (tree ptr, int ostype, access_ref *pref, ssa_name_limit_t &snlim, pointer_query *qry) { - const gimple *stmt = SSA_NAME_DEF_STMT (ptr); + gimple *stmt = SSA_NAME_DEF_STMT (ptr); const tree_code code = gimple_assign_rhs_code (stmt); /* In a valid MAX_/MIN_EXPR both operands must refer to the same array. @@ -1512,7 +1520,7 @@ handle_min_max_size (tree ptr, int ostype, access_ref *pref, for the expression. */ access_ref aref[2] = { *pref, *pref }; tree arg1 = gimple_assign_rhs1 (stmt); - if (!compute_objsize_r (arg1, ostype, &aref[0], snlim, qry)) + if (!compute_objsize_r (arg1, stmt, ostype, &aref[0], snlim, qry)) { aref[0].base0 = false; aref[0].offrng[0] = aref[0].offrng[1] = 0; @@ -1521,7 +1529,7 @@ handle_min_max_size (tree ptr, int ostype, access_ref *pref, } tree arg2 = gimple_assign_rhs2 (stmt); - if (!compute_objsize_r (arg2, ostype, &aref[1], snlim, qry)) + if (!compute_objsize_r (arg2, stmt, ostype, &aref[1], snlim, qry)) { aref[1].base0 = false; aref[1].offrng[0] = aref[1].offrng[1] = 0; @@ -1589,8 +1597,9 @@ handle_min_max_size (tree ptr, int ostype, access_ref *pref, on success and false on failure. */ static bool -handle_array_ref (tree aref, bool addr, int ostype, access_ref *pref, - ssa_name_limit_t &snlim, pointer_query *qry) +handle_array_ref (tree aref, gimple *stmt, bool addr, int ostype, + access_ref *pref, ssa_name_limit_t &snlim, + pointer_query *qry) { gcc_assert (TREE_CODE (aref) == ARRAY_REF); @@ -1603,7 +1612,7 @@ handle_array_ref (tree aref, bool addr, int ostype, access_ref *pref, of known bound. */ return false; - if (!compute_objsize_r (arefop, ostype, pref, snlim, qry)) + if (!compute_objsize_r (arefop, stmt, ostype, pref, snlim, qry)) return false; offset_int orng[2]; @@ -1668,7 +1677,7 @@ handle_array_ref (tree aref, bool addr, int ostype, access_ref *pref, MREF. Return true on success and false on failure. */ static bool -handle_mem_ref (tree mref, int ostype, access_ref *pref, +handle_mem_ref (tree mref, gimple *stmt, int ostype, access_ref *pref, ssa_name_limit_t &snlim, pointer_query *qry) { gcc_assert (TREE_CODE (mref) == MEM_REF); @@ -1690,7 +1699,7 @@ handle_mem_ref (tree mref, int ostype, access_ref *pref, } tree mrefop = TREE_OPERAND (mref, 0); - if (!compute_objsize_r (mrefop, ostype, pref, snlim, qry)) + if (!compute_objsize_r (mrefop, stmt, ostype, pref, snlim, qry)) return false; offset_int orng[2]; @@ -1723,7 +1732,7 @@ handle_mem_ref (tree mref, int ostype, access_ref *pref, to influence code generation or optimization. */ static bool -compute_objsize_r (tree ptr, int ostype, access_ref *pref, +compute_objsize_r (tree ptr, gimple *stmt, int ostype, access_ref *pref, ssa_name_limit_t &snlim, pointer_query *qry) { STRIP_NOPS (ptr); @@ -1774,7 +1783,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref, if (code == BIT_FIELD_REF) { tree ref = TREE_OPERAND (ptr, 0); - if (!compute_objsize_r (ref, ostype, pref, snlim, qry)) + if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry)) return false; offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2))); @@ -1796,7 +1805,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref, /* In OSTYPE zero (for raw memory functions like memcpy), use the maximum size instead if the identity of the enclosing object cannot be determined. */ - if (!compute_objsize_r (ref, ostype, pref, snlim, qry)) + if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry)) return false; /* Otherwise, use the size of the enclosing object and add @@ -1850,15 +1859,15 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref, } if (code == ARRAY_REF) - return handle_array_ref (ptr, addr, ostype, pref, snlim, qry); + return handle_array_ref (ptr, stmt, addr, ostype, pref, snlim, qry); if (code == MEM_REF) - return handle_mem_ref (ptr, ostype, pref, snlim, qry); + return handle_mem_ref (ptr, stmt, ostype, pref, snlim, qry); if (code == TARGET_MEM_REF) { tree ref = TREE_OPERAND (ptr, 0); - if (!compute_objsize_r (ref, ostype, pref, snlim, qry)) + if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry)) return false; /* TODO: Handle remaining operands. Until then, add maximum offset. */ @@ -1903,7 +1912,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref, if (code == POINTER_PLUS_EXPR) { tree ref = TREE_OPERAND (ptr, 0); - if (!compute_objsize_r (ref, ostype, pref, snlim, qry)) + if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry)) return false; /* Clear DEREF since the offset is being applied to the target @@ -1922,7 +1931,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref, if (code == VIEW_CONVERT_EXPR) { ptr = TREE_OPERAND (ptr, 0); - return compute_objsize_r (ptr, ostype, pref, snlim, qry); + return compute_objsize_r (ptr, stmt, ostype, pref, snlim, qry); } if (code == SSA_NAME) @@ -1951,7 +1960,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref, } } - gimple *stmt = SSA_NAME_DEF_STMT (ptr); + stmt = SSA_NAME_DEF_STMT (ptr); if (is_gimple_call (stmt)) { /* If STMT is a call to an allocation function get the size @@ -1979,9 +1988,9 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref, bool past_end; offset_int offrng[2]; if (tree ret = gimple_call_return_array (stmt, offrng, - &past_end, rvals)) + &past_end, snlim, qry)) { - if (!compute_objsize_r (ret, ostype, pref, snlim, qry)) + if (!compute_objsize_r (ret, stmt, ostype, pref, snlim, qry)) return false; /* Cap OFFRNG[1] to at most the remaining size of @@ -2076,14 +2085,14 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref, if (code == ASSERT_EXPR) { rhs = TREE_OPERAND (rhs, 0); - return compute_objsize_r (rhs, ostype, pref, snlim, qry); + return compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry); } if (code == POINTER_PLUS_EXPR && TREE_CODE (TREE_TYPE (rhs)) == POINTER_TYPE) { /* Compute the size of the object first. */ - if (!compute_objsize_r (rhs, ostype, pref, snlim, qry)) + if (!compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry)) return false; offset_int orng[2]; @@ -2099,7 +2108,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref, if (code == ADDR_EXPR || code == SSA_NAME) { - if (!compute_objsize_r (rhs, ostype, pref, snlim, qry)) + if (!compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry)) return false; qry->put_ref (ptr, *pref); return true; @@ -2128,18 +2137,21 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref, instead. */ tree -compute_objsize (tree ptr, int ostype, access_ref *pref, - range_query *rvals /* = NULL */) +compute_objsize (tree ptr, gimple *stmt, int ostype, access_ref *pref, + pointer_query *ptr_qry) { pointer_query qry; - qry.rvals = rvals; + if (ptr_qry) + ptr_qry->depth = 0; + else + ptr_qry = &qry; /* Clear and invalidate in case *PREF is being reused. */ pref->offrng[0] = pref->offrng[1] = 0; pref->sizrng[0] = pref->sizrng[1] = -1; ssa_name_limit_t snlim; - if (!compute_objsize_r (ptr, ostype, pref, snlim, &qry)) + if (!compute_objsize_r (ptr, stmt, ostype, pref, snlim, ptr_qry)) return NULL_TREE; offset_int maxsize = pref->size_remaining (); @@ -2152,26 +2164,12 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, transition to the pointer_query API. */ tree -compute_objsize (tree ptr, int ostype, access_ref *pref, pointer_query *ptr_qry) +compute_objsize (tree ptr, gimple *stmt, int ostype, access_ref *pref, + range_query *rvals /* = NULL */) { pointer_query qry; - if (ptr_qry) - ptr_qry->depth = 0; - else - ptr_qry = &qry; - - /* Clear and invalidate in case *PREF is being reused. */ - pref->offrng[0] = pref->offrng[1] = 0; - pref->sizrng[0] = pref->sizrng[1] = -1; - - ssa_name_limit_t snlim; - if (!compute_objsize_r (ptr, ostype, pref, snlim, ptr_qry)) - return NULL_TREE; - - offset_int maxsize = pref->size_remaining (); - if (pref->base0 && pref->offrng[0] < 0 && pref->offrng[1] >= 0) - pref->offrng[0] = 0; - return wide_int_to_tree (sizetype, maxsize); + qry.rvals = rvals; + return compute_objsize (ptr, stmt, ostype, pref, &qry); } /* Legacy wrapper around the above. The function should be removed @@ -2184,7 +2182,7 @@ compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */, /* Set the initial offsets to zero and size to negative to indicate none has been computed yet. */ access_ref ref; - tree size = compute_objsize (ptr, ostype, &ref, rvals); + tree size = compute_objsize (ptr, nullptr, ostype, &ref, rvals); if (!size || !ref.base0) return NULL_TREE; diff --git a/gcc/pointer-query.h b/gcc/pointer-query.h index 3c8172c652d..96c500132b6 100644 --- a/gcc/pointer-query.h +++ b/gcc/pointer-query.h @@ -60,18 +60,16 @@ class pointer_query; /* Describes a reference to an object used in an access. */ struct access_ref { - /* Set the bounds of the reference to at most as many bytes - as the first argument or unknown when null, and at least - one when the second argument is true unless the first one - is a constant zero. */ - access_ref (tree = NULL_TREE, bool = false); + /* Set the bounds of the reference. */ + access_ref (range_query *query = nullptr, tree = NULL_TREE, + gimple * = nullptr, bool = false); /* Return the PHI node REF refers to or null if it doesn't. */ gphi *phi () const; /* Return the object to which REF refers. */ - tree get_ref (vec *, access_ref * = NULL, int = 1, - ssa_name_limit_t * = NULL, pointer_query * = NULL) const; + tree get_ref (vec *, access_ref * = nullptr, int = 1, + ssa_name_limit_t * = nullptr, pointer_query * = nullptr) const; /* Return true if OFFRNG is the constant zero. */ bool offset_zero () const @@ -85,7 +83,7 @@ struct access_ref /* Return the maximum amount of space remaining and if non-null, set argument to the minimum. */ - offset_int size_remaining (offset_int * = NULL) const; + offset_int size_remaining (offset_int * = nullptr) const; /* Return true if the offset and object size are in range for SIZE. */ bool offset_in_range (const offset_int &) const; @@ -172,13 +170,13 @@ public: }; /* Construct an object with the given Ranger instance and cache. */ - explicit pointer_query (range_query * = NULL, cache_type * = NULL); + explicit pointer_query (range_query * = nullptr, cache_type * = nullptr); /* Retrieve the access_ref for a variable from cache if it's there. */ const access_ref* get_ref (tree, int = 1) const; /* Retrieve the access_ref for a variable from cache or compute it. */ - bool get_ref (tree, access_ref*, int = 1); + bool get_ref (tree, gimple *, access_ref*, int = 1); /* Add an access_ref for the SSA_NAME to the cache. */ void put_ref (tree, const access_ref&, int = 1); @@ -208,19 +206,23 @@ struct access_data { /* Set the access to at most MAXWRITE and MAXREAD bytes, and at least 1 when MINWRITE or MINREAD, respectively, is set. */ - access_data (gimple *stmt, access_mode mode, + access_data (range_query *query, gimple *stmt, access_mode mode, tree maxwrite = NULL_TREE, bool minwrite = false, tree maxread = NULL_TREE, bool minread = false) : stmt (stmt), call (), - dst (maxwrite, minwrite), src (maxread, minread), mode (mode) { } + dst (query, maxwrite, stmt, minwrite), + src (query, maxread, stmt, minread), + mode (mode) { } /* Set the access to at most MAXWRITE and MAXREAD bytes, and at least 1 when MINWRITE or MINREAD, respectively, is set. */ - access_data (tree expr, access_mode mode, + access_data (range_query *query, tree expr, access_mode mode, tree maxwrite = NULL_TREE, bool minwrite = false, tree maxread = NULL_TREE, bool minread = false) : stmt (), call (expr), - dst (maxwrite, minwrite), src (maxread, minread), mode (mode) { } + dst (query, maxwrite, nullptr, minwrite), + src (query, maxread, nullptr, minread), + mode (mode) { } /* Access statement. */ gimple *stmt; @@ -245,14 +247,23 @@ extern bool get_size_range (tree, tree[2], int = 0); extern bool get_size_range (range_query *, tree, gimple *, tree[2], int = 0); class range_query; -extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL, - range_query * = NULL); -extern tree gimple_parm_array_size (tree, wide_int[2], bool * = NULL); +extern tree gimple_call_alloc_size (gimple *, wide_int[2] = nullptr, + range_query * = nullptr); + +/* Compute the size of an object referenced by the first argument in + a statement given by second argument, using Object Size Type given + by third argument. Store result in an access_ref. */ +extern tree compute_objsize (tree, gimple *, int, access_ref *, + range_query * = nullptr); +extern tree compute_objsize (tree, gimple *, int, access_ref *, + pointer_query *); +inline tree compute_objsize (tree ptr, int ostype, access_ref *pref) +{ + return compute_objsize (ptr, nullptr, ostype, pref, (range_query *)nullptr); +} -extern tree compute_objsize (tree, int, access_ref *, range_query * = NULL); /* Legacy/transitional API. Should not be used in new code. */ -extern tree compute_objsize (tree, int, access_ref *, pointer_query *); -extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL, - range_query * = NULL); +extern tree compute_objsize (tree, int, tree * = nullptr, tree * = nullptr, + range_query * = nullptr); #endif // GCC_POINTER_QUERY_H diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c index 8eaaa713275..764b1990276 100644 --- a/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c @@ -260,13 +260,12 @@ T (puts_unlocked, a); // { dg-warning "missing terminating nul" "puts_unlo // Exerise exec functions. T (execl, a, s, NULL); // { dg-warning "missing terminating nul" "execl" } -T (execl, a, s, NULL); // { dg-warning "missing terminating nul" "execl" } -T (execle, a, s, NULL, NULL); // { dg-warning "missing terminating nul" "execl" } -T (execlp, a, s, NULL); // { dg-warning "missing terminating nul" "execl" } +T (execle, a, s, NULL, NULL); // { dg-warning "missing terminating nul" "execle" } +T (execlp, a, s, NULL); // { dg-warning "missing terminating nul" "execlp" } -T (execv, a, &d); // { dg-warning "missing terminating nul" "execl" } -T (execve, a, &d, &d); // { dg-warning "missing terminating nul" "execl" } -T (execvp, a, &d); // { dg-warning "missing terminating nul" "execl" } +T (execv, a, &d); // { dg-warning "missing terminating nul" "execv" } +T (execve, a, &d, &d); // { dg-warning "missing terminating nul" "execve" } +T (execvp, a, &d); // { dg-warning "missing terminating nul" "execvp" } T (gettext, a); // { dg-warning "missing terminating nul" "gettext" } diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-77.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-77.c new file mode 100644 index 00000000000..e8bc327722a --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-77.c @@ -0,0 +1,38 @@ +/* Verify that -Wstringop-overflow uses context-sensitive range info + even at -O0. + { dg-do compile } + { dg-options "-O0 -Wall" } */ + +extern void* memset (void*, int, __SIZE_TYPE__); + +char a[8]; + +void warn_offset_range (int i) +{ + if (i < 4) + i = 4; + memset (a + i, 0, 5); // { dg-warning "writing 5 bytes into a region of size 4 " } +} + +void warn_size_range (int i, int n) +{ + if (n < 5) + n = 5; + + memset (a + 4, 1, n); // { dg-warning "writing between 5 and \\d+ bytes into a region of size 4 " } +} + +void warn_offset_and_size_range (int i, int n) +{ + if (n < 5) + n = 5; + + if (i < 4) + { + if (n < 9) + n = 9; + memset (a + i, 1, n); // { dg-warning "writing between 9 and \\d+ bytes into a region of size 8 " } + } + else + memset (a + i, 0, n); // { dg-warning "writing between 5 and \\d+ bytes into a region of size 4 " } +} diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index 8c39869616f..2de7cb1a6a0 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -1833,7 +1833,7 @@ strlen_pass::adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat) tree dst = gimple_call_arg (last.stmt, 0); access_ref aref; - tree size = compute_objsize (dst, 1, &aref, &ptr_qry); + tree size = compute_objsize (dst, stmt, 1, &aref, &ptr_qry); if (size && tree_int_cst_lt (size, len)) return; } @@ -2035,7 +2035,7 @@ strlen_pass::maybe_warn_overflow (gimple *stmt, bool call_lhs, tree len, access_ref aref; /* The size of the destination region (which is smaller than the destination object for stores at a non-zero offset). */ - tree destsize = compute_objsize (dest, ostype, &aref, &ptr_qry); + tree destsize = compute_objsize (dest, stmt, ostype, &aref, &ptr_qry); if (!destsize) { @@ -3115,7 +3115,7 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt, } access_ref aref; - if (tree dstsize = compute_objsize (dst, 1, &aref, ptr_qry)) + if (tree dstsize = compute_objsize (dst, stmt, 1, &aref, ptr_qry)) { /* The source length is unknown. Try to determine the destination size and see if it matches the specified bound. If not, bail. @@ -3130,7 +3130,7 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt, /* Avoid warning for strncpy(a, b, N) calls where the following equalities hold: N == sizeof a && N == sizeof b */ - if (tree srcsize = compute_objsize (src, 1, &aref, ptr_qry)) + if (tree srcsize = compute_objsize (src, stmt, 1, &aref, ptr_qry)) if (wi::to_wide (srcsize) == cntrange[1]) return false; diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/1.cc b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/1.cc index a2320f2b564..28059748ab7 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/1.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/1.cc @@ -17,7 +17,7 @@ // with this library; see the file COPYING3. If not see // . -// { dg-options "-Wno-stringop-overflow" } +// { dg-options "-Wno-stringop-overflow -Wstringop-overread" } // 21.3.3 string capacity diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/factory/u8path-char8_t.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/factory/u8path-char8_t.cc index f52900d7fda..f5d281fd25a 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/path/factory/u8path-char8_t.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/factory/u8path-char8_t.cc @@ -15,7 +15,7 @@ // with this library; see the file COPYING3. If not see // . -// { dg-options "-fchar8_t" } +// { dg-options "-fchar8_t -Wstringop-overread" } // { dg-do run { target c++17 } } #include @@ -36,6 +36,7 @@ test01() p = fs::u8path(u8"\xf0\x9d\x84\x9e"); VERIFY( p.u8string() == u8"\U0001D11E" ); + // The following triggers a -Wstringop-overread. std::u8string s1 = u8"filename2"; p = fs::u8path(s1); VERIFY( p.u8string() == u8"filename2" );