From patchwork Fri Oct 28 02:19:08 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sebor X-Patchwork-Id: 79826 Delivered-To: patch@linaro.org Received: by 10.140.97.247 with SMTP id m110csp949112qge; Thu, 27 Oct 2016 19:19:42 -0700 (PDT) X-Received: by 10.99.219.81 with SMTP id x17mr16919364pgi.138.1477621182729; Thu, 27 Oct 2016 19:19:42 -0700 (PDT) Return-Path: Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id z184si10907575pfb.121.2016.10.27.19.19.42 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 27 Oct 2016 19:19:42 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-return-439789-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) client-ip=209.132.180.131; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org; spf=pass (google.com: domain of gcc-patches-return-439789-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=gcc-patches-return-439789-patch=linaro.org@gcc.gnu.org; dmarc=fail (p=NONE dis=NONE) header.from=gmail.com DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:to :from:subject:message-id:date:mime-version:content-type; q=dns; s=default; b=Xr92QRvma6hAlTrSj+XFVIKlzvxjUz5VZdkieA2Nb//s9Qbzqv cIJlb+5eLjQyX8ZKs5HlfTexMAMg9i+bb6tYqTIqqzJZcOo3NJ53jXY36Dxy6bjG BPX3WnvG6Sw9RA0dChANFObsAt+tIYfz4vAM6WgHAdzvD8vG1AjSr818Q= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:to :from:subject:message-id:date:mime-version:content-type; s= default; bh=/nGKKQd/BFsGEMRljsZiAhsvzFU=; b=IvAP5ze/9merhvopZuj0 h4CbH9Cb8IkRHzPlDEanz5j9ll3Np8qmgx35sVQQFmptkSsTG8FovP6Yo52H3FTh WJQSa+Lh65lxjG3t3szqvGSSJxVIEhiSAe6I80jbjtvytLZy+Zb3xOEERBuIgAQ+ HDLn0YoZVg7mdbXKp09LlKU= Received: (qmail 105703 invoked by alias); 28 Oct 2016 02:19:21 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 105663 invoked by uid 89); 28 Oct 2016 02:19:20 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-0.8 required=5.0 tests=AWL, BAYES_05, FREEMAIL_FROM, RCVD_IN_DNSWL_LOW, RCVD_IN_SORBS_SPAM, SPF_PASS autolearn=ham version=3.3.2 spammy=enhances, sk:wsugges, sk:Wsugges, 20, 7 X-HELO: mail-qk0-f170.google.com Received: from mail-qk0-f170.google.com (HELO mail-qk0-f170.google.com) (209.85.220.170) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 28 Oct 2016 02:19:14 +0000 Received: by mail-qk0-f170.google.com with SMTP id x11so22507308qka.1 for ; Thu, 27 Oct 2016 19:19:13 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:to:from:subject:message-id:date:user-agent :mime-version; bh=y/r++Li7xIjs3qOqPjv/w1GdbFFyfXF59eeU4FPgoDg=; b=kxawtldeKqJub7MUT7rkuZRM1bbmuMkUqE49itkn8hsFy6Mee0WaCAfmmhRcqe9VsJ I8eUvV/kKlvltsoD2Rgc8r/WCVVjK1nEJC+b/XaS4Cf04Rm5w9t3vNGXENb+khIkzLmI eQerNmeGbktJt3uBtC0Te6zZZ+j/uYPAg/uVYO0lKD/uH8q1SO4IoCvm8Az3YfTC1HZs TFg3z7tpMZUQkmaCf4iOlkAIcCW7h4s13hJmgcVTAPBgWgtBT5+Oq9lkKZPx3QA6zZtT 4+yTZTfpjQvHYd0UW+XyqUJOTym2MmrVCGTnf1syWF+r75MMTvMrt41VvgFWaxcEOWmE Vqlw== X-Gm-Message-State: ABUngvdW+4837nJQdzXdFVM/r6FHMFM+x24S18CuVMggzUtBiqSRgvjk44DTYAIhi/z5TA== X-Received: by 10.55.108.5 with SMTP id h5mr8781104qkc.266.1477621152072; Thu, 27 Oct 2016 19:19:12 -0700 (PDT) Received: from [192.168.0.26] (71-212-250-41.hlrn.qwest.net. [71.212.250.41]) by smtp.gmail.com with ESMTPSA id j8sm5221130qta.6.2016.10.27.19.19.10 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 27 Oct 2016 19:19:11 -0700 (PDT) To: Gcc Patch List From: Martin Sebor Subject: [PATCH] enhance buffer overflow warnings (and c/53562) Message-ID: Date: Thu, 27 Oct 2016 20:19:08 -0600 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.3.0 MIME-Version: 1.0 X-IsSubscribed: yes The attached patch enhances the compile-time detection of buffer overflow in functions like __builtin___memcpy_chk to consider non-constant lengths known to be in a certain range and warn when the lower bound of the range doesn't fit in the destination object. The patch does the same thing for the non-checking functions like __builtin_memcpy and issues buffer overflow warnings for those. For string functions like __builtin_strcpy, the patch also makes use of ranges of lengths of non-constant strings. To make reasoning about the warnings easier (and to help with debugging the problems), the patch also extends the warnings to print the ranges of lengths and sizes of the operands. The text and content of the warning messages is based on those issued by the -Wformat-length warning pass. Finally, as requested in bug 53562, the patch adds a new warning option, -Wstringop-overflow, to control these warnings (the option is on by default). I chose a different name for the option than suggested in the bug to avoid giving the impression that it actually inserts the checking calls (all it does is warn on buffer overflows detectable at compile-time). I was originally going to submit a more modest version of this patch as part of a bigger project I'm working on (bug 77608) but then decided to submit this one first because it's independent of the other. Possible enhancements include letting the option accept a level argument and at level 2 using the upper bound of the size or string length ranges similarly to the -Wformat-length option. With that, the following could be diagnosed as a potential buffer overflow: char d[5]; strcpy (d, x ? "123" : "123456"); Thanks Martin PR c/53562 - Add -Werror= support for -D_FORTIFY_SOURCE / __builtin___memcpy_chk gcc/ChangeLog: 2016-10-27 Martin Sebor PR c/53562 * builtins.c (check_sizes): New function. (expand_builtin_memcpy): Call it. (expand_builtin_mempcpy): Same. (expand_builtin_strcpy): Same. (expand_builtin_memset): Same. (expand_builtin_bzero): Same. (expand_builtin_memory_chk): Same. (maybe_emit_sprintf_chk_warning): Same. * doc/invoke.texi (Warning Options): Document -Wstringop-overflow. gcc/c-family/ChangeLog: 2016-10-27 Martin Sebor PR c/53562 * c.opt (-Wstringop-overflow): New option. gcc/testsuite/ChangeLog: 2016-10-27 Martin Sebor PR c/53562 * gcc.dg/builtin-stringop-chk-4.c: New test. * c-c++-common/Wsizeof-pointer-memaccess2.c: Adjust. * g++.dg/ext/builtin-object-size3.C: Same. * g++.dg/ext/strncpy-chk1.C: Same. * g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same. * gcc.c-torture/execute/builtins/lib/chk.c: Same. * gcc.c-torture/execute/builtins/mempcpy-chk.c: Same. * gcc.dg/Wobjsize-1.c: Same. * gcc.dg/attr-alloc_size.c (test): Same. * gcc.dg/builtin-object-size-1.c: Same. * gcc.dg/builtin-object-size-5.c: Same. * gcc.dg/builtin-stringop-chk-1.c: Same. * gcc.dg/builtin-stringop-chk-2.c: Same. * gcc.dg/builtin-strncat-chk-1.c: Same. * gcc.dg/memcpy-2.c: Same. * gcc.dg/pr40340-1.c: Same. * gcc.dg/pr40340-2.c: Same. * gcc.dg/pr40340-5.c: Same. * gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Same. diff --git a/gcc/builtins.c b/gcc/builtins.c index facecd3..d1aef0a 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -67,7 +67,7 @@ along with GCC; see the file COPYING3. If not see #include "internal-fn.h" #include "case-cfn-macros.h" #include "gimple-fold.h" - +#include "intl.h" struct target_builtins default_target_builtins; #if SWITCHABLE_TARGET @@ -2967,6 +2967,133 @@ expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp) return dest_addr; } +/* Try to verify that the sizes and lengths of the arguments to a string + manipulation function given by EXP are within valid bounds and that + the operation does not lead to buffer overflow. Arguments other than + EXP may be null. When non-null, the arguments have the following + meaning: + SIZE is the user-supplied size argument to the function (such as in + memcpy(d, s, SIZE). + N is the user-supplied length of the source sequence (such as in + strncpy(d, s, N). + LEN is the length of the source sequence (such as in strcpy(d, s), + LEN = strlen(s)). + OBJSIZE is the size of the destination object specified by the last + argument to the _chk builtins, typically resulting from the expansion + of __builtin_object_size (such as in __builtin___strcpy_chk(d, s, + OBJSIZE). + + When SIZE is null LEN is checked to verify that it doesn't exceed + SIZE_MAX. + + If the call is successfully verfified as safe from buffer overflow + the function returns EXP. Otherwise, if the call is determined to + overflow the function returns error_mark_node. Otherwise, if + the call cannot be verified, the function returns NULL. */ + +static tree +check_sizes (tree exp, tree size, tree n, tree len, tree objsize) +{ + tree minlen = NULL_TREE; + tree maxlen = NULL_TREE; + + if (!size) + size = objsize; + + if (!len) + len = size_zero_node; + + if (tree_fits_uhwi_p (len)) + minlen = maxlen = len; + else if (TREE_CODE (len) == SSA_NAME) + { + wide_int min, max; + enum value_range_type range_type = get_range_info (len, &min, &max); + + if (range_type == VR_RANGE) + { + /* Interpret the bounds in the variable's type. */ + minlen = wide_int_to_tree (TREE_TYPE (len), min); + maxlen = wide_int_to_tree (TREE_TYPE (len), max); + } + else if (range_type == VR_ANTI_RANGE) + { + min = min - 1; + max = max + 1; + minlen = wide_int_to_tree (TREE_TYPE (len), min); + maxlen = wide_int_to_tree (TREE_TYPE (len), max); + } + } + + /* The SIZE is known if it's non-null, constant, and in range + of unsigned HOST_WIDE_INT. */ + bool knownsize = size && tree_fits_uhwi_p (size); + if (minlen || !knownsize || integer_all_onesp (size)) + { + if ((!knownsize && + minlen && tree_int_cst_lt (TYPE_MAX_VALUE (ssizetype), minlen)) + || (knownsize + && (tree_int_cst_lt (size, minlen)))) + { + unsigned HOST_WIDE_INT uwilen = tree_to_uhwi (minlen); + + location_t loc = tree_nonartificial_location (exp); + + if (!knownsize) + warning_at (loc, OPT_Wstringop_overflow, + (minlen == maxlen + ? G_("%K%D writing %wu bytes overflows " + "the destination") + : G_("%K%D writing between %wu and %wu bytes " + "overflows the destination")), + exp, get_callee_fndecl (exp), uwilen, + tree_to_uhwi (maxlen)); + else if (minlen == maxlen) + warning_at (loc, OPT_Wstringop_overflow, + (uwilen == 1 + ? G_("%K%D writing %wu byte into a region " + "of size %wu overflows the destination") + : G_("%K%D writing %wu bytes into a region " + "of size %wu overflows the destination")), + exp, get_callee_fndecl (exp), uwilen, + tree_to_uhwi (size)); + else + warning_at (loc, OPT_Wstringop_overflow, + (ubvflow + ? G_("%K%D writing between %wu and %wu bytes " + "into a region of size %wu may overflow " + "the destination") + : G_("%K%D writing between %wu and %wu bytes " + "into a region of size %wu overflows " + "the destination")), + exp, get_callee_fndecl (exp), uwilen, + tree_to_uhwi (maxlen), tree_to_uhwi (size)); + + /* Return error when an overflow has been detected. */ + return error_mark_node; + } + + if (!knownsize) + return error_mark_node; + } + + if (size == objsize && n) + size = n; + + if (size && objsize && tree_fits_uhwi_p (size) + && tree_int_cst_lt (objsize, size)) + { + location_t loc = tree_nonartificial_location (exp); + + warning_at (loc, OPT_Wstringop_overflow, + "specified size %wu exceeds the size %wu " + "of the destination object", + tree_to_uhwi (size), tree_to_uhwi (objsize)); + } + + return NULL_TREE; +} + /* Expand a call EXP to the memcpy builtin. Return NULL_RTX if we failed, the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in @@ -2983,6 +3110,14 @@ expand_builtin_memcpy (tree exp, rtx target) tree dest = CALL_EXPR_ARG (exp, 0); tree src = CALL_EXPR_ARG (exp, 1); tree len = CALL_EXPR_ARG (exp, 2); + + tree objsize = NULL_TREE; + unsigned HOST_WIDE_INT uhwisize; + if (compute_builtin_object_size (dest, 1, &uhwisize)) + objsize = build_int_cst (sizetype, uhwisize); + + check_sizes (exp, NULL_TREE, NULL_TREE, len, objsize); + return expand_builtin_memcpy_args (dest, src, len, target, exp); } } @@ -3037,10 +3172,22 @@ expand_builtin_mempcpy (tree exp, rtx target, machine_mode mode) tree dest = CALL_EXPR_ARG (exp, 0); tree src = CALL_EXPR_ARG (exp, 1); tree len = CALL_EXPR_ARG (exp, 2); - return expand_builtin_mempcpy_args (dest, src, len, - target, mode, /*endp=*/ 1, - exp); + + tree destsize = NULL_TREE; + unsigned HOST_WIDE_INT objsize; + if (compute_builtin_object_size (dest, 1, &objsize)) + destsize = build_int_cst (sizetype, objsize); + + /* Avoid expanding mempcpy into memcpy when the call is determined + to overflow the buffer. This also prevents the same overflow + from being diagnosed again when expanding memcpy. */ + tree ret = check_sizes (exp, NULL_TREE, NULL_TREE, len, destsize); + if (ret != error_mark_node) + return expand_builtin_mempcpy_args (dest, src, len, + target, mode, /*endp=*/ 1, + exp); } + return NULL_RTX; } /* Expand an instrumented call EXP to the mempcpy builtin. @@ -3224,6 +3371,30 @@ expand_builtin_strcpy (tree exp, rtx target) { tree dest = CALL_EXPR_ARG (exp, 0); tree src = CALL_EXPR_ARG (exp, 1); + + /* Try to determine the range of lengths that the source expression + refers to. */ + tree lenrange[2]; + get_range_strlen (src, lenrange); + + /* Try to verify that the destination is big enough for the shortest + string. At a (future) stricter warning setting the longest length + should be used instead. */ + if (lenrange[0]) + { + /* Try to determine the size of the destination object into + which the source is being copied. */ + tree destsize = NULL_TREE; + unsigned HOST_WIDE_INT objsize; + if (compute_builtin_object_size (dest, 1, &objsize)) + destsize = build_int_cst (sizetype, objsize); + + /* Add one for the terminating nul. */ + tree len = fold_build2 (PLUS_EXPR, size_type_node, lenrange[0], + size_one_node); + check_sizes (exp, NULL_TREE, NULL_TREE, len, destsize); + } + return expand_builtin_strcpy_args (dest, src, target); } return NULL_RTX; @@ -3442,6 +3613,15 @@ expand_builtin_memset (tree exp, rtx target, machine_mode mode) tree dest = CALL_EXPR_ARG (exp, 0); tree val = CALL_EXPR_ARG (exp, 1); tree len = CALL_EXPR_ARG (exp, 2); + + /* Try to determine the size of the destination object. */ + tree objsize = NULL_TREE; + unsigned HOST_WIDE_INT uhwisize; + if (compute_builtin_object_size (dest, 1, &uhwisize)) + objsize = build_int_cst (sizetype, uhwisize); + + check_sizes (exp, NULL_TREE, NULL_TREE, len, objsize); + return expand_builtin_memset_args (dest, val, len, target, mode, exp); } } @@ -3633,6 +3813,14 @@ expand_builtin_bzero (tree exp) dest = CALL_EXPR_ARG (exp, 0); size = CALL_EXPR_ARG (exp, 1); + /* Try to determine the size of the destination object. */ + tree objsize = NULL_TREE; + unsigned HOST_WIDE_INT uhwisize; + if (compute_builtin_object_size (dest, 1, &uhwisize)) + objsize = build_int_cst (sizetype, uhwisize); + + check_sizes (exp, NULL_TREE, NULL_TREE, size, objsize); + /* New argument list transforming bzero(ptr x, int y) to memset(ptr x, int 0, size_t y). This is done this way so that if it isn't expanded inline, we fallback to @@ -9212,22 +9408,15 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode, len = CALL_EXPR_ARG (exp, 2); size = CALL_EXPR_ARG (exp, 3); - if (! tree_fits_uhwi_p (size)) - return NULL_RTX; - - if (tree_fits_uhwi_p (len) || integer_all_onesp (size)) + if (tree ret = check_sizes (exp, NULL_TREE, NULL_TREE, len, size)) { - tree fn; - - if (! integer_all_onesp (size) && tree_int_cst_lt (size, len)) - { - warning_at (tree_nonartificial_location (exp), - 0, "%Kcall to %D will always overflow destination buffer", - exp, get_callee_fndecl (exp)); - return NULL_RTX; - } + /* Avoid transforming the checking call to an ordinary one when + an overflow has been detected or when the call couldn't be + validated because the size is not constant. */ + if (ret == error_mark_node) + return NULL_RTX; - fn = NULL_TREE; + tree fn = NULL_TREE; /* If __builtin_mem{cpy,pcpy,move,set}_chk is used, assume mem{cpy,pcpy,move,set} is available. */ switch (fcode) @@ -9313,68 +9502,96 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode, static void maybe_emit_chk_warning (tree exp, enum built_in_function fcode) { - int is_strlen = 0; - tree len, size; - location_t loc = tree_nonartificial_location (exp); + /* The length of the source sequence of the memory operation, and + the size of the destination object. */ + tree srclen = NULL_TREE; + tree objsize = NULL_TREE; + /* The length of the sequence that the source sequence is being + concatenated with (as with __strcat_chk) or null if it isn't. */ + tree catlen = NULL_TREE; + /* The maximum length of the source sequence in a bounded operation + (such as __strncat_chk) or null if the operation isn't bounded + (such as __strcat_chk). */ + tree maxlen = NULL_TREE; switch (fcode) { case BUILT_IN_STRCPY_CHK: case BUILT_IN_STPCPY_CHK: - /* For __strcat_chk the warning will be emitted only if overflowing - by at least strlen (dest) + 1 bytes. */ + srclen = CALL_EXPR_ARG (exp, 1); + objsize = CALL_EXPR_ARG (exp, 2); + break; + case BUILT_IN_STRCAT_CHK: - len = CALL_EXPR_ARG (exp, 1); - size = CALL_EXPR_ARG (exp, 2); - is_strlen = 1; + /* For __strcat_chk the warning will be emitted only if overflowing + by at least strlen (dest) + 1 bytes. */ + catlen = CALL_EXPR_ARG (exp, 0); + srclen = CALL_EXPR_ARG (exp, 1); + objsize = CALL_EXPR_ARG (exp, 2); break; + case BUILT_IN_STRNCAT_CHK: + catlen = CALL_EXPR_ARG (exp, 0); + srclen = CALL_EXPR_ARG (exp, 1); + maxlen = CALL_EXPR_ARG (exp, 2); + objsize = CALL_EXPR_ARG (exp, 3); + break; + case BUILT_IN_STRNCPY_CHK: case BUILT_IN_STPNCPY_CHK: - len = CALL_EXPR_ARG (exp, 2); - size = CALL_EXPR_ARG (exp, 3); + srclen = CALL_EXPR_ARG (exp, 1); + maxlen = CALL_EXPR_ARG (exp, 2); + objsize = CALL_EXPR_ARG (exp, 3); break; + case BUILT_IN_SNPRINTF_CHK: case BUILT_IN_VSNPRINTF_CHK: - len = CALL_EXPR_ARG (exp, 1); - size = CALL_EXPR_ARG (exp, 3); + maxlen = CALL_EXPR_ARG (exp, 1); + objsize = CALL_EXPR_ARG (exp, 3); break; default: gcc_unreachable (); } - if (!len || !size) - return; - - if (! tree_fits_uhwi_p (size) || integer_all_onesp (size)) - return; + /* When the type of SRCLEN is a pointer compute the length of string + the pointer points to. Otherwise it is an integer that gives the + upper bound on the length of the operation (as in __strncpy_chk). */ + if (srclen && TREE_CODE (TREE_TYPE (srclen)) == POINTER_TYPE) + srclen = c_strlen (srclen, 1); - if (is_strlen) - { - len = c_strlen (len, 1); - if (! len || ! tree_fits_uhwi_p (len) || tree_int_cst_lt (len, size)) - return; - } - else if (fcode == BUILT_IN_STRNCAT_CHK) + if (catlen) { - tree src = CALL_EXPR_ARG (exp, 1); - if (! src || ! tree_fits_uhwi_p (len) || tree_int_cst_lt (len, size)) - return; - src = c_strlen (src, 1); - if (! src || ! tree_fits_uhwi_p (src)) + if ((!srclen || !tree_fits_uhwi_p (srclen)) + && (!maxlen + || (tree_fits_uhwi_p (maxlen) + && tree_fits_uhwi_p (objsize) + && !tree_int_cst_lt (maxlen, objsize)))) { + location_t loc = tree_nonartificial_location (exp); + warning_at (loc, 0, "%Kcall to %D might overflow destination buffer", exp, get_callee_fndecl (exp)); return; } - else if (tree_int_cst_lt (src, size)) + + if (!srclen) return; + + catlen = c_strlen (catlen, 1); + if (catlen) + srclen = fold_build2 (PLUS_EXPR, TREE_TYPE (srclen), srclen, catlen); + } + + if (srclen) + { + srclen = fold_build2 (PLUS_EXPR, TREE_TYPE (srclen), srclen, + size_one_node); + if (maxlen && tree_fits_uhwi_p (maxlen) + && tree_int_cst_lt (maxlen, srclen)) + srclen = maxlen; } - else if (! tree_fits_uhwi_p (len) || ! tree_int_cst_lt (size, len)) - return; - warning_at (loc, 0, "%Kcall to %D will always overflow destination buffer", - exp, get_callee_fndecl (exp)); + check_sizes (exp, NULL_TREE, maxlen, srclen, objsize); } /* Emit warning if a buffer overflow is detected at compile time @@ -9428,10 +9645,9 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode) else return; - if (! tree_int_cst_lt (len, size)) - warning_at (tree_nonartificial_location (exp), - 0, "%Kcall to %D will always overflow destination buffer", - exp, get_callee_fndecl (exp)); + /* Add one for the terminating nul. */ + len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node); + check_sizes (exp, NULL_TREE, NULL_TREE, len, size); } /* Emit warning if a free is called with address of a variable. */ diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index e146781..6635464 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -647,6 +647,10 @@ Wsizeof-array-argument C ObjC C++ ObjC++ Var(warn_sizeof_array_argument) Warning Init(1) Warn when sizeof is applied on a parameter declared as an array. +Wstringop-overflow +C ObjC C++ ObjC++ Var(warn_stringop_overflow) Init(1) Warning +Warn about buffer overflow in string manipulation functions. + Wsuggest-attribute=format C ObjC C++ ObjC++ Var(warn_suggest_attribute_format) Warning Warn about functions which might be candidates for format attributes. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index d9667e7..81cbe63 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -299,6 +299,7 @@ Objective-C and Objective-C++ Dialects}. -Wsizeof-pointer-memaccess -Wsizeof-array-argument @gol -Wstack-protector -Wstack-usage=@var{len} -Wstrict-aliasing @gol -Wstrict-aliasing=n -Wstrict-overflow -Wstrict-overflow=@var{n} @gol +-Wstringop-overflow @gol -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]} @gol -Wsuggest-final-types @gol -Wsuggest-final-methods -Wsuggest-override @gol -Wmissing-format-attribute -Wsubobject-linkage @gol @@ -4831,6 +4832,27 @@ comparisons, so this warning level gives a very large number of false positives. @end table +@item -Wstringop-overflow +@opindex Wstringop-overflow +@opindex Wno-stringop-overflow +Warn for calls to string manipulation functions such as memcpy and strcpy +hat are determined to overflow the destination buffer. The option works +best with optimization enabled but it can detect a small subset of buffer +overflows even without optimization. In any case, the option warns about +just a subset of buffer overflows detected by the corresponding overflow +checking built-ins. @xref{Object Size Checking}. For example, +@option{-Wstringop-overflow} will warn on the following: + +@smallexample +char buf[6]; +void f (int i) +@{ + strcpy (buf, i < 0 ? "yellow" : "orange"); +@}; +@end smallexample + +The @option{-Wstringop-overflow} option is enabled by default. + @item -Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{]} @opindex Wsuggest-attribute= @opindex Wno-suggest-attribute= diff --git a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c index d9ec7e2..9a02373 100644 --- a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c +++ b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c @@ -481,4 +481,4 @@ f4 (char *x, char **y, int z, char w[64]) stpncpy (x, s3, sizeof (s3)); } -/* { dg-prune-output "\[\n\r\]*will always overflow\[\n\r\]*" } */ +/* { dg-prune-output "\[\n\r\]*writing\[\n\r\]*" } */ diff --git a/gcc/testsuite/g++.dg/ext/builtin-object-size3.C b/gcc/testsuite/g++.dg/ext/builtin-object-size3.C index 09263e5..0207f9a 100644 --- a/gcc/testsuite/g++.dg/ext/builtin-object-size3.C +++ b/gcc/testsuite/g++.dg/ext/builtin-object-size3.C @@ -20,7 +20,7 @@ bar () { int *p = new int; int *q = new int[4]; - MEMCPY (p, "abcdefghijklmnopqrstuvwxyz", sizeof (int) + 1); // { dg-warning "will always overflow destination buffer" } - MEMCPY (q, "abcdefghijklmnopqrstuvwxyz", 4 * sizeof (int) + 1); // { dg-warning "will always overflow destination buffer" } + MEMCPY (p, "abcdefghijklmnopqrstuvwxyz", sizeof (int) + 1); // { dg-warning "writing" } + MEMCPY (q, "abcdefghijklmnopqrstuvwxyz", 4 * sizeof (int) + 1); // { dg-warning "writing" } baz (p, q); } diff --git a/gcc/testsuite/g++.dg/ext/strncpy-chk1.C b/gcc/testsuite/g++.dg/ext/strncpy-chk1.C index ebafc99..85b3977 100644 --- a/gcc/testsuite/g++.dg/ext/strncpy-chk1.C +++ b/gcc/testsuite/g++.dg/ext/strncpy-chk1.C @@ -9,7 +9,7 @@ struct B { char z[50]; }; inline void foo (char *dest, const char *__restrict src, __SIZE_TYPE__ n) { - __builtin___strncpy_chk (dest, src, n, __builtin_object_size (dest, 0)); // { dg-warning "will always overflow" } + __builtin___strncpy_chk (dest, src, n, __builtin_object_size (dest, 0)); // { dg-warning "overflows" } } void bar (const char *, int); diff --git a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C index 8b5c33e..2e6189b 100644 --- a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C +++ b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C @@ -713,4 +713,4 @@ f4 (char *x, char **y, int z, char w[64]) return z; } -// { dg-prune-output "\[\n\r\]*will always overflow\[\n\r\]*" } +// { dg-prune-output "\[\n\r\]*overflows\[\n\r\]*" } diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c index b19d7bf..d2ea835 100644 --- a/gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/chk.c @@ -5,6 +5,11 @@ extern void abort (void); +#define abort() \ + (__builtin_printf ("failure at %s:%i: %s\n", \ + __FILE__, __LINE__, __FUNCTION__), \ + __builtin_abort ()) + extern int inside_main; void *chk_fail_buf[256] __attribute__((aligned (16))); volatile int chk_fail_allowed, chk_calls; diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c b/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c index 7a1737c..f8cb0f4 100644 --- a/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/mempcpy-chk.c @@ -17,6 +17,8 @@ volatile char *s2 = "defg"; /* prevent constant propagation to happen when whole volatile char *s3 = "FGH"; /* prevent constant propagation to happen when whole program assumptions are made. */ volatile size_t l1 = 1; /* prevent constant propagation to happen when whole program assumptions are made. */ +#define abort() (__builtin_printf ("failure on line %i\n", __LINE__), __builtin_abort ()) + void __attribute__((noinline)) test1 (void) @@ -326,6 +328,8 @@ test4 (void) char buf3[20]; chk_fail_allowed = 1; + mempcpy_disallowed = 0; + /* Runtime checks. */ if (__builtin_setjmp (chk_fail_buf) == 0) { @@ -343,7 +347,9 @@ test4 (void) vx = mempcpy (&buf3[19], "ab", 2); abort (); } + chk_fail_allowed = 0; + mempcpy_disallowed = 1; } #ifndef MAX_OFFSET diff --git a/gcc/testsuite/gcc.dg/Wobjsize-1.c b/gcc/testsuite/gcc.dg/Wobjsize-1.c index 291cfb9..211e068 100644 --- a/gcc/testsuite/gcc.dg/Wobjsize-1.c +++ b/gcc/testsuite/gcc.dg/Wobjsize-1.c @@ -10,6 +10,6 @@ int main(int argc, char **argv) return 0; } -/* { dg-warning "will always overflow destination buffer" "" { target *-*-* } 6 } */ +/* { dg-warning "writing" "" { target *-*-* } 6 } */ /* { dg-message "file included" "included" { target *-*-* } 0 } */ /* { dg-message "inlined from" "inlined" { target *-*-* } 0 } */ diff --git a/gcc/testsuite/gcc.dg/attr-alloc_size.c b/gcc/testsuite/gcc.dg/attr-alloc_size.c index e8129ce..f50ba7c 100644 --- a/gcc/testsuite/gcc.dg/attr-alloc_size.c +++ b/gcc/testsuite/gcc.dg/attr-alloc_size.c @@ -22,15 +22,15 @@ test (void) strcpy (p, "Hello"); p = malloc1 (6); strcpy (p, "Hello"); - strcpy (p, "Hello World"); /* { dg-warning "will always overflow" "strcpy" } */ + strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */ p = malloc2 (__INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 6); strcpy (p, "World"); - strcpy (p, "Hello World"); /* { dg-warning "will always overflow" "strcpy" } */ + strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */ p = calloc1 (2, 5); strcpy (p, "World"); - strcpy (p, "Hello World"); /* { dg-warning "will always overflow" "strcpy" } */ + strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */ p = calloc2 (2, __INT_MAX__ >= 1700000 ? 424242 : __INT_MAX__ / 4, 5); strcpy (p, "World"); - strcpy (p, "Hello World"); /* { dg-warning "will always overflow" "strcpy" } */ + strcpy (p, "Hello World"); /* { dg-warning "writing" "strcpy" } */ } diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c index 8cdae49..2919503 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c @@ -12,6 +12,8 @@ extern void *memcpy (void *, const void *, size_t); extern void *memset (void *, int, size_t); extern char *strcpy (char *, const char *); +#define abort() (__builtin_printf ("failure in %s on line %i\n", __func__, __LINE__), __builtin_abort ()) + struct A { char a[10]; diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-object-size-5.c index 7c274cd..35f1350 100644 --- a/gcc/testsuite/gcc.dg/builtin-object-size-5.c +++ b/gcc/testsuite/gcc.dg/builtin-object-size-5.c @@ -2,7 +2,10 @@ /* { dg-options "-O2" } */ typedef __SIZE_TYPE__ size_t; -extern void abort (void); +extern void abort1 (void); +extern void abort2 (void); +extern void abort3 (void); +extern void abort4 (void); extern char buf[0x40000000]; void @@ -14,7 +17,7 @@ test1 (size_t x) for (i = 0; i < x; ++i) p = p + 4; if (__builtin_object_size (p, 0) != sizeof (buf) - 8) - abort (); + abort1 (); } void @@ -26,7 +29,7 @@ test2 (size_t x) for (i = 0; i < x; ++i) p = p + 4; if (__builtin_object_size (p, 1) != sizeof (buf) - 8) - abort (); + abort2 (); } void @@ -38,7 +41,7 @@ test3 (size_t x) for (i = 0; i < x; ++i) p = p + 4; if (__builtin_object_size (p, 2) != 0) - abort (); + abort3 (); } void @@ -50,7 +53,7 @@ test4 (size_t x) for (i = 0; i < x; ++i) p = p + 4; if (__builtin_object_size (p, 3) != 0) - abort (); + abort4 (); } /* { dg-final { scan-assembler-not "abort" } } */ diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c index e491ff5..49e7f95 100644 --- a/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c +++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c @@ -8,7 +8,10 @@ extern void abort (void); #include "../gcc.c-torture/execute/builtins/chk.h" -#include + +#define va_list __builtin_va_list +#define va_start __builtin_va_start +#define va_end __builtin_va_end volatile void *vx; char buf1[20]; @@ -22,60 +25,61 @@ test (int arg, ...) char *p = &buf1[10], *q; memcpy (&buf2[19], "ab", 1); - memcpy (&buf2[19], "ab", 2); /* { dg-warning "will always overflow" "memcpy" } */ + memcpy (&buf2[19], "ab", 2); /* { dg-warning "writing 2 bytes into a region of size 1" "memcpy" } */ vx = mempcpy (&buf2[19], "ab", 1); - vx = mempcpy (&buf2[19], "ab", 2); /* { dg-warning "will always overflow" "mempcpy" } */ + vx = mempcpy (&buf2[19], "ab", 2); /* { dg-warning "writing 2 " "mempcpy" } */ memmove (&buf2[18], &buf1[10], 2); - memmove (&buf2[18], &buf1[10], 3); /* { dg-warning "will always overflow" "memmove" } */ + memmove (&buf2[18], &buf1[10], 3); /* { dg-warning "writing 3 " "memmove" } */ memset (&buf2[16], 'a', 4); - memset (&buf2[15], 'b', 6); /* { dg-warning "will always overflow" "memset" } */ + memset (&buf2[15], 'b', 6); /* { dg-warning "writing 6 " "memset" } */ strcpy (&buf2[18], "a"); - strcpy (&buf2[18], "ab"); /* { dg-warning "will always overflow" "strcpy" } */ + strcpy (&buf2[18], "ab"); /* { dg-warning "writing 3 " "strcpy" } */ vx = stpcpy (&buf2[18], "a"); - vx = stpcpy (&buf2[18], "ab"); /* { dg-warning "will always overflow" "stpcpy" } */ + vx = stpcpy (&buf2[18], "ab"); /* { dg-warning "writing 3" "stpcpy" } */ strncpy (&buf2[18], "a", 2); - strncpy (&buf2[18], "a", 3); /* { dg-warning "will always overflow" "strncpy" } */ + strncpy (&buf2[18], "a", 3); /* { dg-warning "specified size 3 exceeds the size 2 of the destination" "strncpy" } */ strncpy (&buf2[18], "abc", 2); - strncpy (&buf2[18], "abc", 3); /* { dg-warning "will always overflow" "strncpy" } */ + strncpy (&buf2[18], "abc", 3); /* { dg-warning "writing 3 " "strncpy" } */ memset (buf2, '\0', sizeof (buf2)); strcat (&buf2[18], "a"); memset (buf2, '\0', sizeof (buf2)); - strcat (&buf2[18], "ab"); /* { dg-warning "will always overflow" "strcat" } */ + strcat (&buf2[18], "ab"); /* { dg-warning "writing 3 " "strcat" } */ sprintf (&buf2[18], "%s", buf1); sprintf (&buf2[18], "%s", "a"); - sprintf (&buf2[18], "%s", "ab"); /* { dg-warning "will always overflow" "sprintf" } */ + sprintf (&buf2[18], "%s", "ab"); /* { dg-warning "writing 3 " "sprintf" } */ sprintf (&buf2[18], "a"); - sprintf (&buf2[18], "ab"); /* { dg-warning "will always overflow" "sprintf" } */ + sprintf (&buf2[18], "ab"); /* { dg-warning "writing 3 " "sprintf" } */ snprintf (&buf2[18], 2, "%d", x); /* N argument to snprintf is the size of the buffer. Although this particular call wouldn't overflow buf2, incorrect buffer size was passed to it and therefore we want a warning and runtime failure. */ - snprintf (&buf2[18], 3, "%d", x); /* { dg-warning "will always overflow" "snprintf" } */ + snprintf (&buf2[18], 3, "%d", x); /* { dg-warning "specified size 3 exceeds the size 2 of the destination" "snprintf" } */ va_start (ap, arg); vsprintf (&buf2[18], "a", ap); va_end (ap); + va_start (ap, arg); - vsprintf (&buf2[18], "ab", ap); /* { dg-warning "will always overflow" "vsprintf" } */ + vsprintf (&buf2[18], "ab", ap); /* { dg-warning "writing 3" "vsprintf" } */ va_end (ap); va_start (ap, arg); vsnprintf (&buf2[18], 2, "%s", ap); va_end (ap); va_start (ap, arg); /* See snprintf above. */ - vsnprintf (&buf2[18], 3, "%s", ap); /* { dg-warning "will always overflow" "vsnprintf" } */ + vsnprintf (&buf2[18], 3, "%s", ap); /* { dg-warning "specified size 3 exceeds the size 2 of the destination object" "vsnprintf" } */ va_end (ap); p = p + 10; memset (p, 'd', 0); - q = strcpy (p, ""); /* { dg-warning "will always overflow" "strcpy" } */ + q = strcpy (p, ""); /* { dg-warning "writing 1 " "strcpy" } */ /* This invokes undefined behavior, since we are past the end of buf1. */ p = p + 10; - memset (p, 'd', 1); /* { dg-warning "will always overflow" "memset" } */ + memset (p, 'd', 1); /* { dg-warning "writing 1 " "memset" } */ memset (q, 'd', 0); - memset (q, 'd', 1); /* { dg-warning "will always overflow" "memset" } */ + memset (q, 'd', 1); /* { dg-warning "writing 1 " "memset" } */ q = q - 10; memset (q, 'd', 10); } @@ -90,26 +94,26 @@ void test2 (const H h) { char c; - strncpy (&c, str, 3); /* { dg-warning "will always overflow" "strncpy" } */ + strncpy (&c, str, 3); /* { dg-warning "specified size 3 exceeds the size 1 of the destination object" "strncpy" } */ struct { char b[4]; } x; - sprintf (x.b, "%s", "ABCD"); /* { dg-warning "will always overflow" "sprintf" } */ + sprintf (x.b, "%s", "ABCD"); /* { dg-warning "writing 5" "sprintf" } */ unsigned int i; - memcpy (&i, &h, sizeof (h)); /* { dg-warning "will always overflow" "memcpy" } */ + memcpy (&i, &h, sizeof (h)); /* { dg-warning "writing 16 " "memcpy" } */ unsigned char buf[21]; - memset (buf + 16, 0, 8); /* { dg-warning "will always overflow" "memset" } */ + memset (buf + 16, 0, 8); /* { dg-warning "writing 8 " "memset" } */ typedef struct { int i, j, k, l; } S; S *s[3]; - memset (s, 0, sizeof (S) * 3); /* { dg-warning "will always overflow" "memset" } */ + memset (s, 0, sizeof (S) * 3); /* { dg-warning "writing 48 " "memset" } */ struct T { char a[8]; char b[4]; char c[10]; } t; - stpcpy (t.c,"Testing..."); /* { dg-warning "will always overflow" "stpcpy" } */ + stpcpy (t.c,"Testing..."); /* { dg-warning "writing" "stpcpy" } */ char b1[7]; char b2[4]; memset (b1, 0, sizeof (b1)); - memset (b2, 0, sizeof (b1)); /* { dg-warning "will always overflow" "memset" } */ + memset (b2, 0, sizeof (b1)); /* { dg-warning "writing 7" "memset" } */ } diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-2.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-2.c index 7c2bb60..d537fb0 100644 --- a/gcc/testsuite/gcc.dg/builtin-stringop-chk-2.c +++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-2.c @@ -6,7 +6,7 @@ /* { dg-options "-O2 -ftrack-macro-expansion=0" } */ #include "../gcc.c-torture/execute/builtins/chk.h" - + void *bar (int); extern void *malloc (__SIZE_TYPE__); @@ -115,7 +115,7 @@ baz (const struct A *x, const unsigned char *z) else do { - memcpy (e, d, 513); /* { dg-warning "will always overflow" "memcpy" } */ + memcpy (e, d, 513); /* { dg-warning "writing" "memcpy" } */ e += 4; } while (--h); diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c new file mode 100644 index 0000000..4e12193 --- /dev/null +++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-4.c @@ -0,0 +1,145 @@ +/* Test exercising buffer overflow warnings emitted for __*_chk builtins + in cases where the destination involves a non-constant offset into + an object of known size. */ +/* { dg-do compile } */ +/* { dg-options "-O2 -ftrack-macro-expansion=0" } */ + +#define INT_MAX __INT_MAX__ +#define PTRDIFF_MAX __PTRDIFF_MAX__ +#define SIZE_MAX __SIZE_MAX__ + +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef __SIZE_TYPE__ size_t; + +#define memcpy(d, s, n) (memcpy ((d), (s), (n)), sink ((d))) +extern void* (memcpy)(void*, const void*, size_t); + +#define mempcpy(d, s, n) (mempcpy ((d), (s), (n)), sink ((d))) +extern void* (mempcpy)(void*, const void*, size_t); + +#define memset(d, c, n) (memset ((d), (c), (n)), sink ((d))) +extern void* (memset)(void*, int, size_t); + +#define strcpy(d, s) (strcpy ((d), (s)), sink ((d))) +extern char* (strcpy)(char*, const char*); + +void sink (void*); + +/* Function to "generate" a unique number each time it's called. Declared + (but not defined) and used to prevent GCC from making assumptions about + their values based on the variables uses in the tested expressions. */ +ptrdiff_t random_value (void); + +/* Return a random value between MIN and MAX. */ +static inline size_t +value_range (size_t min, size_t max) +{ + const size_t val = random_value (); + return val < min || max < val ? min : val; +} + +/* For brevity. */ +#define R(min, max) value_range (min, max) + +/* Test memcpy with a number of bytes bounded by a known range. */ + +void test_memcpy_range (const void *p) +{ + char buf[5]; + + memcpy (buf, p, R (0, 5)); + memcpy (buf, p, R (1, 5)); + memcpy (buf, p, R (2, 5)); + memcpy (buf, p, R (3, 5)); + memcpy (buf, p, R (4, 5)); + + memcpy (buf, p, R (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */ + + const size_t max = SIZE_MAX; + memcpy (buf, p, R (max - 1, max)); /* { dg-warning "writing" } */ + + memcpy (buf, p, R (max / 2 + 1, max)); /* { dg-warning "writing" } */ +} + +/* Test mempcpy with a number of bytes bounded by a known range. */ + +void test_mempcpy_range (const void *p) +{ + char buf[5]; + + mempcpy (buf, p, R (0, 5)); + mempcpy (buf, p, R (1, 5)); + mempcpy (buf, p, R (2, 5)); + mempcpy (buf, p, R (3, 5)); + mempcpy (buf, p, R (4, 5)); + + mempcpy (buf, p, R (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */ + + const size_t max = SIZE_MAX; + mempcpy (buf, p, R (max - 1, max)); /* { dg-warning "writing" } */ + + mempcpy (buf, p, R (max / 2 + 1, max)); /* { dg-warning "writing" } */ +} + +/* Test memset with a number of bytes bounded by a known range. */ + +void test_memset_range (const void *p) +{ + char buf[5]; + + memset (buf, 0, R (0, 5)); + memset (buf, 1, R (1, 5)); + memset (buf, 2, R (2, 5)); + memset (buf, 3, R (3, 5)); + memset (buf, 4, R (4, 5)); + + memset (buf, 5, R (6, 7)); /* { dg-warning "writing between 6 and 7 bytes into a region of size 5 overflows the destination" } */ + + const size_t max = SIZE_MAX; + memset (buf, 6, R (max - 1, max)); /* { dg-warning "writing" } */ + + memset (buf, 7, R (max / 2 + 1, max)); /* { dg-warning "writing" } */ +} + +/* Return a pointer to constant string whose length is at least MINLEN + and at most 10. */ +static inline const char* +string_range (size_t minlen) +{ + static const char str[] = "0123456789"; + + const size_t len = value_range (minlen, sizeof str - 1); + + switch (len) + { + case 10: return "0123456789"; + case 9: return "012345678"; + case 8: return "01234567"; + case 7: return "0123456"; + case 6: return "012345"; + case 5: return "01234"; + case 4: return "0123"; + case 3: return "012"; + case 2: return "01"; + case 1: return "0"; + case 0: return ""; + } +} + +#define S(minlen) string_range (minlen) + +void test_strcpy_range (const char *s) +{ + char buf[5]; + + strcpy (buf, S (0)); + strcpy (buf, S (1)); + strcpy (buf, S (2)); + strcpy (buf, S (4)); + strcpy (buf, S (5)); /* { dg-warning "writing 6 bytes into a region of size 5 " } */ + strcpy (buf, S (6)); /* { dg-warning "writing 7 bytes into a region of size 5 " } */ + strcpy (buf, S (7)); /* { dg-warning "writing 8 bytes into a region of size 5 " } */ + strcpy (buf, S (8)); /* { dg-warning "writing 9 bytes into a region of size 5 " } */ + strcpy (buf, S (9)); /* { dg-warning "writing 10 bytes into a region of size 5 " } */ + strcpy (buf, S (10)); /* { dg-warning "writing 11 bytes into a region of size 5 " } */ +} diff --git a/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c b/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c index 44677f1..4568680 100644 --- a/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c +++ b/gcc/testsuite/gcc.dg/builtin-strncat-chk-1.c @@ -24,11 +24,11 @@ test (int arg, ...) *p = 0; strncat (p, "abcdefghi", 10); *p = 0; - strncat (p, "abcdefghij", 10); /* { dg-warning "will always overflow" } */ + strncat (p, "abcdefghij", 10); /* { dg-warning "writing 11 bytes" } */ *p = 0; strncat (p, "abcdefgh", 11); *p = 0; - strncat (p, "abcdefghijkl", 11); /* { dg-warning "will always overflow" } */ + strncat (p, "abcdefghijkl", 11); /* { dg-warning "writing 11 bytes" } */ *p = 0; strncat (p, q, 9); *p = 0; diff --git a/gcc/testsuite/gcc.dg/memcpy-2.c b/gcc/testsuite/gcc.dg/memcpy-2.c index 24464abd..7f839d2 100644 --- a/gcc/testsuite/gcc.dg/memcpy-2.c +++ b/gcc/testsuite/gcc.dg/memcpy-2.c @@ -7,7 +7,7 @@ typedef __SIZE_TYPE__ size_t; extern inline __attribute__((gnu_inline, always_inline, artificial)) void * memcpy (void *__restrict dest, const void *__restrict src, size_t len) { - return __builtin___memcpy_chk (dest, /* { dg-warning "will always overflow destination buffer" } */ + return __builtin___memcpy_chk (dest, /* { dg-warning "writing" } */ src, len, __builtin_object_size (dest, 0)); } diff --git a/gcc/testsuite/gcc.dg/pr40340-1.c b/gcc/testsuite/gcc.dg/pr40340-1.c index aae84c6..78540a2 100644 --- a/gcc/testsuite/gcc.dg/pr40340-1.c +++ b/gcc/testsuite/gcc.dg/pr40340-1.c @@ -20,5 +20,5 @@ main (void) return 0; } -/* { dg-warning "will always overflow destination buffer" "" { target *-*-* } 10 } */ +/* { dg-warning "writing" "" { target *-*-* } 10 } */ /* { dg-message "file included" "In file included" { target *-*-* } 0 } */ diff --git a/gcc/testsuite/gcc.dg/pr40340-2.c b/gcc/testsuite/gcc.dg/pr40340-2.c index a0d6e084..1dc21d1 100644 --- a/gcc/testsuite/gcc.dg/pr40340-2.c +++ b/gcc/testsuite/gcc.dg/pr40340-2.c @@ -12,5 +12,5 @@ main (void) return 0; } -/* { dg-warning "will always overflow destination buffer" "" { target *-*-* } 10 } */ +/* { dg-warning "writing" "" { target *-*-* } 10 } */ /* { dg-message "file included" "In file included" { target *-*-* } 0 } */ diff --git a/gcc/testsuite/gcc.dg/pr40340-5.c b/gcc/testsuite/gcc.dg/pr40340-5.c index f50514c..e517147 100644 --- a/gcc/testsuite/gcc.dg/pr40340-5.c +++ b/gcc/testsuite/gcc.dg/pr40340-5.c @@ -13,5 +13,5 @@ main (void) return 0; } -/* { dg-warning "will always overflow destination buffer" "" { target *-*-* } 10 } */ +/* { dg-warning "writing" "" { target *-*-* } 10 } */ /* { dg-message "file included" "In file included" { target *-*-* } 0 } */ diff --git a/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c index 7ce9eae..b5a59f4 100644 --- a/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c +++ b/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c @@ -710,4 +710,4 @@ f4 (char *x, char **y, int z, char w[64]) return z; } -/* { dg-prune-output "\[\n\r\]*will always overflow\[\n\r\]*" } */ +/* { dg-prune-output "\[\n\r\]*writing\[\n\r\]*" } */