From patchwork Wed Nov 10 10:45:56 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Siddhesh Poyarekar X-Patchwork-Id: 47386 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 3EBAC385781B for ; Wed, 10 Nov 2021 10:46:25 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from dog.elm.relay.mailchannels.net (dog.elm.relay.mailchannels.net [23.83.212.48]) by sourceware.org (Postfix) with ESMTPS id DBDEC3858436 for ; Wed, 10 Nov 2021 10:46:07 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org DBDEC3858436 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=gotplt.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gotplt.org X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id 4B9FE402B59; Wed, 10 Nov 2021 10:46:06 +0000 (UTC) Received: from pdx1-sub0-mail-a306.dreamhost.com (100-96-18-144.trex.outbound.svc.cluster.local [100.96.18.144]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id C7875402B6E; Wed, 10 Nov 2021 10:46:05 +0000 (UTC) X-Sender-Id: dreamhost|x-authsender|siddhesh@gotplt.org Received: from pdx1-sub0-mail-a306.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384) by 100.96.18.144 (trex/6.4.3); Wed, 10 Nov 2021 10:46:06 +0000 X-MC-Relay: Neutral X-MailChannels-SenderId: dreamhost|x-authsender|siddhesh@gotplt.org X-MailChannels-Auth-Id: dreamhost X-Reign-Sponge: 1a1a0f453d7b14a8_1636541166053_4165962137 X-MC-Loop-Signature: 1636541166053:3120565715 X-MC-Ingress-Time: 1636541166053 Received: from rhbox.redhat.com (unknown [1.186.121.127]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: siddhesh@gotplt.org) by pdx1-sub0-mail-a306.dreamhost.com (Postfix) with ESMTPSA id 4Hq1jS01ygz2D; Wed, 10 Nov 2021 02:46:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=gotplt.org; s=gotplt.org; t=1636541165; bh=sNmx/rZCbNRszgGRvyeH5u7rBf0=; h=From:To:Cc:Subject:Date:Content-Transfer-Encoding; b=lfSXksQyJAywxg+H06XulBsSZy49HmJR8JFpYWzpRdCYvyH5GAxhFpQBNxHb2PyUB BV5MnVbPnalclwnBAU3Qu+BXQVzN0maWzS+wJ9qlKd20stbWfXOy5KuwUfxmpEo8H6 6R86cUE7wNMP/hOT19rWUYzM4qWaCCR3g1fEX4k4= From: Siddhesh Poyarekar To: gcc-patches@gcc.gnu.org Subject: [PATCH v2] gimple-fold: Smarter optimization of _chk variants Date: Wed, 10 Nov 2021 16:15:56 +0530 Message-Id: <20211110104556.3394077-1-siddhesh@gotplt.org> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20211110093836.3337210-1-siddhesh@gotplt.org> References: <20211110093836.3337210-1-siddhesh@gotplt.org> MIME-Version: 1.0 X-Spam-Status: No, score=-3038.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_BARRACUDACENTRAL, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, RCVD_IN_SBL, 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: , Cc: jakub@redhat.com, msebor@redhat.com Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" Instead of comparing LEN and SIZE only if they are constants, use their ranges to decide if LEN will always be lower than or same as SIZE. This change ends up putting the stringop-overflow warning line number against the strcpy implementation, so adjust the warning check to be line number agnostic. gcc/ChangeLog: * gimple-fold.c (known_safe): New function. (gimple_fold_builtin_memory_chk, gimple_fold_builtin_stxcpy_chk, gimple_fold_builtin_stxncpy_chk, gimple_fold_builtin_snprintf_chk, gimple_fold_builtin_sprintf_chk): Use it. gcc/testsuite/ChangeLog: * gcc.dg/Wobjsize-1.c: Make warning change line agnostic. * gcc.dg/builtin-chk-fold.c: New test. Signed-off-by: Siddhesh Poyarekar --- Changes from v1: - Update comment that incorrectly said that known_safe emits a warning. - Add tests for strncpy and snprintf too. gcc/gimple-fold.c | 185 +++++++++--------------- gcc/testsuite/gcc.dg/Wobjsize-1.c | 5 +- gcc/testsuite/gcc.dg/builtin-chk-fold.c | 49 +++++++ 3 files changed, 121 insertions(+), 118 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/builtin-chk-fold.c diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 6e25a7c05db..36b06218c88 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -2987,6 +2987,24 @@ gimple_fold_builtin_fputs (gimple_stmt_iterator *gsi, return false; } +/* Return true if LEN is known to be less than or equal to SIZE at compile time + and false otherwise. */ + +static bool +known_safe (gimple *stmt, tree len, tree size) +{ + if (len == NULL_TREE) + return false; + + wide_int size_range[2]; + wide_int len_range[2]; + if (get_range (len, stmt, len_range) && get_range (size, stmt, size_range) + && wi::leu_p (len_range[1], size_range[0])) + return true; + + return false; +} + /* Fold a call to the __mem{cpy,pcpy,move,set}_chk builtin. DEST, SRC, LEN, and SIZE are the arguments to the call. IGNORE is true, if return value can be ignored. FCODE is the BUILT_IN_* @@ -3024,39 +3042,24 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi, } } - if (! tree_fits_uhwi_p (size)) - return false; - tree maxlen = get_maxval_strlen (len, SRK_INT_VALUE); - if (! integer_all_onesp (size)) + if (! integer_all_onesp (size) + && !known_safe (stmt, len, size) && !known_safe (stmt, maxlen, size)) { - if (! tree_fits_uhwi_p (len)) + /* MAXLEN and LEN both cannot be proved to be less than SIZE, at + least try to optimize (void) __mempcpy_chk () into + (void) __memcpy_chk () */ + if (fcode == BUILT_IN_MEMPCPY_CHK && ignore) { - /* If LEN is not constant, try MAXLEN too. - For MAXLEN only allow optimizing into non-_ocs function - if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ - if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen)) - { - if (fcode == BUILT_IN_MEMPCPY_CHK && ignore) - { - /* (void) __mempcpy_chk () can be optimized into - (void) __memcpy_chk (). */ - fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK); - if (!fn) - return false; + fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK); + if (!fn) + return false; - gimple *repl = gimple_build_call (fn, 4, dest, src, len, size); - replace_call_with_call_and_fold (gsi, repl); - return true; - } - return false; - } + gimple *repl = gimple_build_call (fn, 4, dest, src, len, size); + replace_call_with_call_and_fold (gsi, repl); + return true; } - else - maxlen = len; - - if (tree_int_cst_lt (size, maxlen)) - return false; + return false; } fn = NULL_TREE; @@ -3126,61 +3129,47 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi, return true; } - if (! tree_fits_uhwi_p (size)) - return false; - tree maxlen = get_maxval_strlen (src, SRK_STRLENMAX); if (! integer_all_onesp (size)) { len = c_strlen (src, 1); - if (! len || ! tree_fits_uhwi_p (len)) + if (!known_safe (stmt, len, size) && !known_safe (stmt, maxlen, size)) { - /* If LEN is not constant, try MAXLEN too. - For MAXLEN only allow optimizing into non-_ocs function - if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ - if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen)) + if (fcode == BUILT_IN_STPCPY_CHK) { - if (fcode == BUILT_IN_STPCPY_CHK) - { - if (! ignore) - return false; - - /* If return value of __stpcpy_chk is ignored, - optimize into __strcpy_chk. */ - fn = builtin_decl_explicit (BUILT_IN_STRCPY_CHK); - if (!fn) - return false; - - gimple *repl = gimple_build_call (fn, 3, dest, src, size); - replace_call_with_call_and_fold (gsi, repl); - return true; - } - - if (! len || TREE_SIDE_EFFECTS (len)) + if (! ignore) return false; - /* If c_strlen returned something, but not a constant, - transform __strcpy_chk into __memcpy_chk. */ - fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK); + /* If return value of __stpcpy_chk is ignored, + optimize into __strcpy_chk. */ + fn = builtin_decl_explicit (BUILT_IN_STRCPY_CHK); if (!fn) return false; - gimple_seq stmts = NULL; - len = force_gimple_operand (len, &stmts, true, NULL_TREE); - len = gimple_convert (&stmts, loc, size_type_node, len); - len = gimple_build (&stmts, loc, PLUS_EXPR, size_type_node, len, - build_int_cst (size_type_node, 1)); - gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT); - gimple *repl = gimple_build_call (fn, 4, dest, src, len, size); + gimple *repl = gimple_build_call (fn, 3, dest, src, size); replace_call_with_call_and_fold (gsi, repl); return true; } - } - else - maxlen = len; - if (! tree_int_cst_lt (maxlen, size)) - return false; + if (! len || TREE_SIDE_EFFECTS (len)) + return false; + + /* If c_strlen returned something, but not a constant, + transform __strcpy_chk into __memcpy_chk. */ + fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK); + if (!fn) + return false; + + gimple_seq stmts = NULL; + len = force_gimple_operand (len, &stmts, true, NULL_TREE); + len = gimple_convert (&stmts, loc, size_type_node, len); + len = gimple_build (&stmts, loc, PLUS_EXPR, size_type_node, len, + build_int_cst (size_type_node, 1)); + gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT); + gimple *repl = gimple_build_call (fn, 4, dest, src, len, size); + replace_call_with_call_and_fold (gsi, repl); + return true; + } } /* If __builtin_st{r,p}cpy_chk is used, assume st{r,p}cpy is available. */ @@ -3222,26 +3211,10 @@ gimple_fold_builtin_stxncpy_chk (gimple_stmt_iterator *gsi, } } - if (! tree_fits_uhwi_p (size)) - return false; - tree maxlen = get_maxval_strlen (len, SRK_INT_VALUE); - if (! integer_all_onesp (size)) - { - if (! tree_fits_uhwi_p (len)) - { - /* If LEN is not constant, try MAXLEN too. - For MAXLEN only allow optimizing into non-_ocs function - if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ - if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen)) - return false; - } - else - maxlen = len; - - if (tree_int_cst_lt (size, maxlen)) - return false; - } + if (! integer_all_onesp (size) + && !known_safe (stmt, len, size) && !known_safe (stmt, maxlen, size)) + return false; /* If __builtin_st{r,p}ncpy_chk is used, assume st{r,p}ncpy is available. */ fn = builtin_decl_explicit (fcode == BUILT_IN_STPNCPY_CHK @@ -3359,27 +3332,11 @@ gimple_fold_builtin_snprintf_chk (gimple_stmt_iterator *gsi, size = gimple_call_arg (stmt, 3); fmt = gimple_call_arg (stmt, 4); - if (! tree_fits_uhwi_p (size)) + tree maxlen = get_maxval_strlen (len, SRK_INT_VALUE); + if (! integer_all_onesp (size) + && !known_safe (stmt, len, size) && !known_safe (stmt, maxlen, size)) return false; - if (! integer_all_onesp (size)) - { - tree maxlen = get_maxval_strlen (len, SRK_INT_VALUE); - if (! tree_fits_uhwi_p (len)) - { - /* If LEN is not constant, try MAXLEN too. - For MAXLEN only allow optimizing into non-_ocs function - if SIZE is >= MAXLEN, never convert to __ocs_fail (). */ - if (maxlen == NULL_TREE || ! tree_fits_uhwi_p (maxlen)) - return false; - } - else - maxlen = len; - - if (tree_int_cst_lt (size, maxlen)) - return false; - } - if (!init_target_chars ()) return false; @@ -3438,9 +3395,6 @@ gimple_fold_builtin_sprintf_chk (gimple_stmt_iterator *gsi, size = gimple_call_arg (stmt, 2); fmt = gimple_call_arg (stmt, 3); - if (! tree_fits_uhwi_p (size)) - return false; - len = NULL_TREE; if (!init_target_chars ()) @@ -3454,7 +3408,7 @@ gimple_fold_builtin_sprintf_chk (gimple_stmt_iterator *gsi, if (strchr (fmt_str, target_percent) == 0) { if (fcode != BUILT_IN_SPRINTF_CHK || nargs == 4) - len = build_int_cstu (size_type_node, strlen (fmt_str)); + len = build_int_cstu (size_type_node, strlen (fmt_str) + 1); } /* If the format is "%s" and first ... argument is a string literal, we know the size too. */ @@ -3468,19 +3422,18 @@ gimple_fold_builtin_sprintf_chk (gimple_stmt_iterator *gsi, arg = gimple_call_arg (stmt, 4); if (POINTER_TYPE_P (TREE_TYPE (arg))) { - len = c_strlen (arg, 1); if (! len || ! tree_fits_uhwi_p (len)) len = NULL_TREE; + else + len = fold_build2 (PLUS_EXPR, ssizetype, c_strlen (arg, 1), + ssize_int (1)); } } } } - if (! integer_all_onesp (size)) - { - if (! len || ! tree_int_cst_lt (len, size)) - return false; - } + if (! integer_all_onesp (size) && !known_safe (stmt, len, size)) + return false; /* Only convert __{,v}sprintf_chk to {,v}sprintf if flag is 0 or if format doesn't contain % chars or is "%s". */ diff --git a/gcc/testsuite/gcc.dg/Wobjsize-1.c b/gcc/testsuite/gcc.dg/Wobjsize-1.c index 2bd2f93897b..988b8bcbf35 100644 --- a/gcc/testsuite/gcc.dg/Wobjsize-1.c +++ b/gcc/testsuite/gcc.dg/Wobjsize-1.c @@ -7,11 +7,12 @@ char buf[6]; int main(int argc, char **argv) { - strcpy (buf,"hello "); /* { dg-warning "\\\[-Wstringop-overflow" } */ + strcpy (buf,"hello "); return 0; } -/* { dg-message "file included" "included" { target *-*-* } 0 } +/* { dg-warning "\\\[-Wstringop-overflow" "warning" { target *-*-* } 0 } + { dg-message "file included" "included" { target *-*-* } 0 } { dg-message "inlined from" "inlined" { target *-*-* } 0 } The test might emit two warnings, one for the strcpy call and diff --git a/gcc/testsuite/gcc.dg/builtin-chk-fold.c b/gcc/testsuite/gcc.dg/builtin-chk-fold.c new file mode 100644 index 00000000000..0b415dfaf57 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-chk-fold.c @@ -0,0 +1,49 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +#define bos(__d) __builtin_object_size ((__d), 0) + +char * +safe1 (const char *src, int cond, __SIZE_TYPE__ len) +{ + char *dst; + + if (cond) + dst = __builtin_malloc (1024); + else + dst = __builtin_malloc (2048); + + len = len > 2048 ? 2048 : len; + + return __builtin___memcpy_chk (dst, src, len, bos (dst)); +} + +char * +safe2 (const char *src, int cond, unsigned char len) +{ + char *dst; + + if (cond) + dst = __builtin_malloc (1024); + else + dst = __builtin_malloc (2048); + + return __builtin___strncpy_chk (dst, src, len, bos (dst)); +} + +int +safe3 (const char *src, int cond, unsigned char len) +{ + char *dst; + + if (cond) + dst = __builtin_malloc (1024); + else + dst = __builtin_malloc (2048); + + return __builtin___snprintf_chk (dst, len, 0, bos (dst), "%s", src); +} + +/* { dg-final { scan-assembler-not "__memcpy_chk" } } */ +/* { dg-final { scan-assembler-not "__strncpy_chk" } } */ +/* { dg-final { scan-assembler-not "__snprintf_chk" } } */