From patchwork Fri Oct 10 16:32:02 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Polacek X-Patchwork-Id: 398730 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4F26114009B for ; Sat, 11 Oct 2014 03:32:19 +1100 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:references:mime-version :content-type:in-reply-to; q=dns; s=default; b=RNhlfNPSfGtp26oC8 OdxV1Cxk2PVF0Y2q+BTE6aLxq9c5LTPVPrgzH4EB8aRo5nRouX3rBZDpTtlARSnl HP17xikhrpe1daRKRU5PNddH79lP3tgydZgMGkPiN1rNiePXRjByoUmbTiy+j2qD TEDtNKQMpYItiqYtgoXGifB51A= 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:date :from:to:cc:subject:message-id:references:mime-version :content-type:in-reply-to; s=default; bh=Y5hbJU90H71feZ6k3yB+PGC QTws=; b=EmS+OU4pA+y3ZZyN/5kcClQZGKr2r9f9TezvyquM+4UeeY9ztAvuENZ HCp0M25ObZmzbVpBwKUNUqqoH2nvbTh9DtOKp4kOzQ82SJ59X4P9MrPHZmb4T4qs xdX/dODIa00I0ZB6NexHU/TioQFlrktHW1wkzFJz+dxmgkgn/xqU= Received: (qmail 18006 invoked by alias); 10 Oct 2014 16:32:12 -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 17993 invoked by uid 89); 10 Oct 2014 16:32:11 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-3.1 required=5.0 tests=AWL, BAYES_00, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Fri, 10 Oct 2014 16:32:07 +0000 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s9AGW6Om018744 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL) for ; Fri, 10 Oct 2014 12:32:06 -0400 Received: from redhat.com (ovpn-116-41.ams2.redhat.com [10.36.116.41]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s9AGW2IB009009 (version=TLSv1/SSLv3 cipher=AES128-GCM-SHA256 bits=128 verify=NO); Fri, 10 Oct 2014 12:32:04 -0400 Date: Fri, 10 Oct 2014 18:32:02 +0200 From: Marek Polacek To: Jakub Jelinek Cc: GCC Patches Subject: Re: [PATCH] Implement -fsanitize=object-size Message-ID: <20141010163202.GN1002@redhat.com> References: <20140713175543.GB13277@redhat.com> <20140714115413.GK31640@tucnak.redhat.com> <20140911174751.GG20685@redhat.com> <20141002120424.GF1986@tucnak.redhat.com> <20141010100407.GM1002@redhat.com> <20141010102644.GE10376@tucnak.redhat.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20141010102644.GE10376@tucnak.redhat.com> User-Agent: Mutt/1.5.23 (2014-03-12) On Fri, Oct 10, 2014 at 12:26:44PM +0200, Jakub Jelinek wrote: > On Fri, Oct 10, 2014 at 12:04:08PM +0200, Marek Polacek wrote: > > I couldn't test bootstrap-ubsan, because of error: > > /home/polacek/x/trunk/prev-x86_64-unknown-linux-gnu/libsanitizer/ubsan/.libs/libubsan.a(ubsan_init.o): > > .preinit_array section is not allowed in DSO > > but I remember that the previous version of the patch passed fine. > > We build (intentionally) both libubsan.so.* objects and libubsan.a > objects with -fPIC, but don't build the latter with -DPIC. I guess > we need now, with -static-libubsan libubsan.a is linked into shared > libraries statically and we definitely can't use .preinit_array > in that case. > > So, I think (untested) something like: > > 2014-10-10 Jakub Jelinek > > * ubsan/Makefile.am (DEFS): Add -DPIC. > * ubsan/Makefile.in: Regenerated. > > --- libsanitizer/ubsan/Makefile.am 2014-09-24 11:08:04.183026156 +0200 > +++ libsanitizer/ubsan/Makefile.am 2014-10-10 12:15:19.124247283 +0200 > @@ -3,7 +3,7 @@ AM_CPPFLAGS = -I $(top_srcdir) -I $(top_ > # May be used by toolexeclibdir. > gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER) > > -DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS > +DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DPIC > AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros > AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS) > ACLOCAL_AMFLAGS = -I m4 > > should fix this. > > > 2014-10-09 Marek Polacek > > Check the date ;) Adjusted. > > * asan.c (pass_sanopt::execute): Handle IFN_UBSAN_OBJECT_SIZE. > > * doc/invoke.texi: Document -fsanitize=object-size. > > * flag-types.h (enum sanitize_code): Add SANITIZE_OBJECT_SIZE and > > or it into SANITIZE_UNDEFINED. > > * gimple-fold.c (gimple_fold_call): Optimize IFN_UBSAN_OBJECT_SIZE. > > * internal-fn.c (expand_UBSAN_OBJECT_SIZE): New function. > > * internal-fn.def (UBSAN_OBJECT_SIZE): Define. > > * opts.c (common_handle_option): Handle -fsanitize=object-size. > > * ubsan.c: Include "tree-object-size.h". > > I'd avoid the ""s. Adjusted. > > --- gcc/gimple-fold.c > > +++ gcc/gimple-fold.c > > @@ -2662,6 +2662,19 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace) > > gimple_call_arg (stmt, 1), > > gimple_call_arg (stmt, 2)); > > break; > > + case IFN_UBSAN_OBJECT_SIZE: > > + if (integer_all_onesp (gimple_call_arg (stmt, 2)) > > Formatting on the case line, there should be tab. Fixed. > > + > > + gcc_assert (TREE_CODE (size) == INTEGER_CST); > > + /* See if we can discard the check. */ > > + if (integer_all_onesp (size)) > > + /* Yes, __builtin_object_size couldn't determine the > > + object size. */; > > I'd just treat TREE_CODE (size) != INTEGER_CST > the same as integer_all_onesp. It is very likely you'll get > an INTEGER_CST there, but I'd be afraid if somebody disables ccp, forwprop > and similar optimizations that if you are unlucky you might actually have > an SSA_NAME there instead. Fixed. > Ok with those changes. Thanks for reviewing! > After commit, please update gcc-5/changes.html. Thanks. Sure. I'm applying the following. 2014-10-10 Marek Polacek * asan.c (pass_sanopt::execute): Handle IFN_UBSAN_OBJECT_SIZE. * doc/invoke.texi: Document -fsanitize=object-size. * flag-types.h (enum sanitize_code): Add SANITIZE_OBJECT_SIZE and or it into SANITIZE_UNDEFINED. * gimple-fold.c (gimple_fold_call): Optimize IFN_UBSAN_OBJECT_SIZE. * internal-fn.c (expand_UBSAN_OBJECT_SIZE): New function. * internal-fn.def (UBSAN_OBJECT_SIZE): Define. * opts.c (common_handle_option): Handle -fsanitize=object-size. * ubsan.c: Include tree-object-size.h. (ubsan_type_descriptor): Call tree_to_uhwi instead of tree_to_shwi. (ubsan_expand_bounds_ifn): Use false instead of 0. (ubsan_expand_objsize_ifn): New function. (instrument_object_size): New function. (pass_ubsan::execute): Add object size instrumentation. * ubsan.h (ubsan_expand_objsize_ifn): Declare. testsuite/ * c-c++-common/ubsan/object-size-1.c: New test. * c-c++-common/ubsan/object-size-2.c: New test. * c-c++-common/ubsan/object-size-3.c: New test. * c-c++-common/ubsan/object-size-4.c: New test. * c-c++-common/ubsan/object-size-5.c: New test. * c-c++-common/ubsan/object-size-6.c: New test. * c-c++-common/ubsan/object-size-7.c: New test. * c-c++-common/ubsan/object-size-8.c: New test. * c-c++-common/ubsan/object-size-9.c: New test. * g++.dg/ubsan/object-size-1.C: New test. * gcc.dg/ubsan/object-size-9.c: New test. Marek diff --git gcc/asan.c gcc/asan.c index fca4ee6..6ea3efe 100644 --- gcc/asan.c +++ gcc/asan.c @@ -2879,6 +2879,9 @@ pass_sanopt::execute (function *fun) case IFN_UBSAN_BOUNDS: no_next = ubsan_expand_bounds_ifn (&gsi); break; + case IFN_UBSAN_OBJECT_SIZE: + no_next = ubsan_expand_objsize_ifn (&gsi); + break; case IFN_ASAN_CHECK: { no_next = asan_expand_check_ifn (&gsi, use_calls); diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi index f1ba77b..2b62a73 100644 --- gcc/doc/invoke.texi +++ gcc/doc/invoke.texi @@ -5578,6 +5578,12 @@ This option enables checking of alignment of pointers when they are dereferenced, or when a reference is bound to insufficiently aligned target, or when a method or constructor is invoked on insufficiently aligned object. +@item -fsanitize=object-size +@opindex fsanitize=object-size +This option enables instrumentation of memory references using the +@code{__builtin_object_size} function. Various out of bounds pointer +accesses are detected. + @item -fsanitize=float-divide-by-zero @opindex fsanitize=float-divide-by-zero Detect floating-point division by zero. Unlike other similar options, diff --git gcc/flag-types.h gcc/flag-types.h index d0818e5..3d01c49 100644 --- gcc/flag-types.h +++ gcc/flag-types.h @@ -236,12 +236,14 @@ enum sanitize_code { SANITIZE_ALIGNMENT = 1 << 17, SANITIZE_NONNULL_ATTRIBUTE = 1 << 18, SANITIZE_RETURNS_NONNULL_ATTRIBUTE = 1 << 19, + SANITIZE_OBJECT_SIZE = 1 << 20, SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM | SANITIZE_BOUNDS | SANITIZE_ALIGNMENT | SANITIZE_NONNULL_ATTRIBUTE - | SANITIZE_RETURNS_NONNULL_ATTRIBUTE, + | SANITIZE_RETURNS_NONNULL_ATTRIBUTE + | SANITIZE_OBJECT_SIZE, SANITIZE_NONDEFAULT = SANITIZE_FLOAT_DIVIDE | SANITIZE_FLOAT_CAST }; diff --git gcc/gimple-fold.c gcc/gimple-fold.c index 8ac2211..76441c7 100644 --- gcc/gimple-fold.c +++ gcc/gimple-fold.c @@ -2662,6 +2662,19 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace) gimple_call_arg (stmt, 1), gimple_call_arg (stmt, 2)); break; + case IFN_UBSAN_OBJECT_SIZE: + if (integer_all_onesp (gimple_call_arg (stmt, 2)) + || (TREE_CODE (gimple_call_arg (stmt, 1)) == INTEGER_CST + && TREE_CODE (gimple_call_arg (stmt, 2)) == INTEGER_CST + && tree_int_cst_le (gimple_call_arg (stmt, 1), + gimple_call_arg (stmt, 2)))) + { + gsi_replace (gsi, gimple_build_nop (), true); + unlink_stmt_vdef (stmt); + release_defs (stmt); + return true; + } + break; case IFN_UBSAN_CHECK_ADD: subcode = PLUS_EXPR; break; diff --git gcc/internal-fn.c gcc/internal-fn.c index c2a44b6..c71259d 100644 --- gcc/internal-fn.c +++ gcc/internal-fn.c @@ -184,6 +184,14 @@ expand_UBSAN_BOUNDS (gimple stmt ATTRIBUTE_UNUSED) /* This should get expanded in the sanopt pass. */ static void +expand_UBSAN_OBJECT_SIZE (gimple stmt ATTRIBUTE_UNUSED) +{ + gcc_unreachable (); +} + +/* This should get expanded in the sanopt pass. */ + +static void expand_ASAN_CHECK (gimple stmt ATTRIBUTE_UNUSED) { gcc_unreachable (); diff --git gcc/internal-fn.def gcc/internal-fn.def index 54ade9f..b8e457c 100644 --- gcc/internal-fn.def +++ gcc/internal-fn.def @@ -53,6 +53,7 @@ DEF_INTERNAL_FN (UBSAN_BOUNDS, ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (UBSAN_CHECK_ADD, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (UBSAN_CHECK_SUB, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (UBSAN_CHECK_MUL, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) +DEF_INTERNAL_FN (UBSAN_OBJECT_SIZE, ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (ABNORMAL_DISPATCHER, ECF_NORETURN, NULL) DEF_INTERNAL_FN (BUILTIN_EXPECT, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL) DEF_INTERNAL_FN (ASAN_CHECK, ECF_TM_PURE | ECF_LEAF | ECF_NOTHROW, ".W...") diff --git gcc/opts.c gcc/opts.c index 5cb5a39..3d9b6a7 100644 --- gcc/opts.c +++ gcc/opts.c @@ -1513,6 +1513,8 @@ common_handle_option (struct gcc_options *opts, { "returns-nonnull-attribute", SANITIZE_RETURNS_NONNULL_ATTRIBUTE, sizeof "returns-nonnull-attribute" - 1 }, + { "object-size", SANITIZE_OBJECT_SIZE, + sizeof "object-size" - 1 }, { NULL, 0, 0 } }; const char *comma; diff --git gcc/testsuite/c-c++-common/ubsan/object-size-1.c gcc/testsuite/c-c++-common/ubsan/object-size-1.c index e69de29..7a3c87a 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-1.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-1.c @@ -0,0 +1,125 @@ +/* { dg-do run } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */ +/* { dg-options "-fsanitize=undefined" } */ + +/* Sanity-test -fsanitize=object-size. We use -fsanitize=undefined option + to check that this feature doesn't clash with -fsanitize=bounds et al. */ + +#define N 20 + +__attribute__((noinline, noclone)) void +f1 (int i) +{ + volatile int j; + char *p, *orig; + orig = p = (char *) __builtin_calloc (N, 1); + j = *(p + i); + j = p[i]; + p++; + j = p[i - 1]; + j = *(p + i - 1); + __builtin_free (orig); +} + +/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +__attribute__((noinline, noclone)) void +f2 (int i) +{ + volatile int j; + char a[N]; + __builtin_memset (a, 0, N); + j = *(a + i); + char *p = a; + j = *(p + i); + j = p[i]; + p += 10; + j = *(p + i - 10); + j = p[i - 10]; +} + +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +__attribute__((noinline, noclone)) void +f3 (int i) +{ + volatile int j; + int *p = (int *) __builtin_calloc (N, sizeof (*p)); + int *o = &p[i]; + j = *o; + j = o[0]; + __builtin_free (p); +} + +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +__attribute__((noinline, noclone)) void +f4 (void) +{ + /* The second argument to __builtin_calloc is intentional. */ + int *p = (int *) __builtin_calloc (3, 1); + *p = 42; + __builtin_free (p); +} + +/* { dg-output "\[^\n\r]*store to address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +__attribute__((noinline, noclone)) void +f5 (int *p) +{ + /* This is not instrumented. But don't ICE, etc. */ + volatile int i = p[N]; +} + +int +main () +{ + f1 (N); + f2 (N); + f3 (N); + f4 (); + int *p = (int *) __builtin_calloc (N, sizeof (*p)); + f5 (p); + __builtin_free (p); + return 0; +} diff --git gcc/testsuite/c-c++-common/ubsan/object-size-2.c gcc/testsuite/c-c++-common/ubsan/object-size-2.c index e69de29..dba1243 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-2.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-2.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */ +/* { dg-options "-fsanitize=undefined" } */ + +void +foo (unsigned long ul) +{ + unsigned int u; + u = *(unsigned long *) ul; +} diff --git gcc/testsuite/c-c++-common/ubsan/object-size-3.c gcc/testsuite/c-c++-common/ubsan/object-size-3.c index e69de29..62dc76f 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-3.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-3.c @@ -0,0 +1,56 @@ +/* { dg-do run } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */ +/* { dg-options "-fsanitize=object-size -fno-sanitize-recover" } */ + +/* Test valid uses. */ + +#define N 20 + +__attribute__((noinline, noclone)) void +f1 (int i) +{ + volatile int j; + char *p, *orig; + orig = p = (char *) __builtin_calloc (N, 1); + j = *(p + i); + j = p[i]; + p++; + j = p[i - 1]; + j = *(p + i - 1); + __builtin_free (orig); +} + +__attribute__((noinline, noclone)) void +f2 (int i) +{ + volatile int j; + char a[N]; + __builtin_memset (a, 0, N); + j = *(a + i); + char *p = a; + j = *(p + i); + j = p[i]; + p += 10; + j = *(p + i - 10); + j = p[i - 10]; +} + +__attribute__((noinline, noclone)) void +f3 (int i) +{ + volatile int j; + int *p = (int *) __builtin_calloc (N, sizeof (*p)); + int *o = &p[i]; + j = *o; + j = o[0]; + __builtin_free (p); +} + +int +main () +{ + f1 (N - 1); + f2 (N - 1); + f3 (N - 1); + return 0; +} diff --git gcc/testsuite/c-c++-common/ubsan/object-size-4.c gcc/testsuite/c-c++-common/ubsan/object-size-4.c index e69de29..8b95ec9 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-4.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-4.c @@ -0,0 +1,31 @@ +/* { dg-do run } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */ +/* { dg-options "-fsanitize=object-size" } */ + +/* Test that we instrument flexible array members. */ + +struct T { int l; int a[]; }; +struct U { int l; int a[0]; }; + +int +main (void) +{ + volatile int i; + struct T *t = (struct T *) __builtin_calloc (sizeof (struct T) + + sizeof (int), 1); + i = t->a[1]; + + struct U *u = (struct U *) __builtin_calloc (sizeof (struct U) + + sizeof (int), 1); + i = u->a[1]; + return 0; +} + +/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ diff --git gcc/testsuite/c-c++-common/ubsan/object-size-5.c gcc/testsuite/c-c++-common/ubsan/object-size-5.c index e69de29..3dada10 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-5.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-5.c @@ -0,0 +1,38 @@ +/* { dg-do run } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */ +/* { dg-options "-fsanitize=object-size" } */ + +/* Test structures with -fsanitize=object-size. */ + +#define N 20 + +struct S { char *p; int i; }; +struct T { struct S *s; }; + +__attribute__((noinline, noclone)) void +f1 (int i) +{ + volatile int j; + struct S s; + s.p = (char *) __builtin_calloc (N, 1); + j = s.p[i]; + j = *(s.p + i); + __builtin_free (s.p); +} + +/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +int +main () +{ + f1 (N); + f1 (N - 1); + return 0; +} diff --git gcc/testsuite/c-c++-common/ubsan/object-size-6.c gcc/testsuite/c-c++-common/ubsan/object-size-6.c index e69de29..0e6035d 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-6.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-6.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */ +/* { dg-options "-fsanitize=object-size" } */ + +char +foo (void *v) +{ + return *(char *) v; +} diff --git gcc/testsuite/c-c++-common/ubsan/object-size-7.c gcc/testsuite/c-c++-common/ubsan/object-size-7.c index e69de29..f5b26e5 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-7.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-7.c @@ -0,0 +1,29 @@ +/* { dg-do run } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */ +/* { dg-options "-fsanitize=object-size" } */ + +#define N 20 + +struct S { int a; }; + +__attribute__((noinline, noclone)) struct S +f1 (int i) +{ + struct S a[N]; + struct S *p = a; + struct S s; + s = p[i]; + return s; +} + +int +main () +{ + f1 (N); + return 0; +} + +/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ diff --git gcc/testsuite/c-c++-common/ubsan/object-size-8.c gcc/testsuite/c-c++-common/ubsan/object-size-8.c index e69de29..ee0945b 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-8.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-8.c @@ -0,0 +1,32 @@ +/* { dg-do run } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */ +/* { dg-options "-fsanitize=undefined" } */ + +struct S { int a; int b; }; + +static inline __attribute__((always_inline)) int +foo (struct S *p) +{ + volatile int a; + a = p->a; /* OK */ + return p->b; +} + +int +bar (void) +{ + struct S *p = (struct S *) __builtin_calloc (sizeof (int) + sizeof (int) / 2, 1); + return foo (p); +} + +int +main (void) +{ + bar (); + return 0; +} + +/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ diff --git gcc/testsuite/c-c++-common/ubsan/object-size-9.c gcc/testsuite/c-c++-common/ubsan/object-size-9.c index e69de29..829c822 100644 --- gcc/testsuite/c-c++-common/ubsan/object-size-9.c +++ gcc/testsuite/c-c++-common/ubsan/object-size-9.c @@ -0,0 +1,97 @@ +/* { dg-do run } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */ +/* { dg-options "-fsanitize=undefined" } */ + +/* Test PARM_DECLs and RESULT_DECLs. */ + +struct T { char d[8]; int e; }; +struct T t = { "abcdefg", 1 }; +#ifdef __cplusplus +struct C { C () : d("abcdefg"), e(1) {} C (const C &x) { __builtin_memcpy (d, x.d, 8); e = x.e; } ~C () {} char d[8]; int e; }; +#endif +struct U { int a : 5; int b : 19; int c : 8; }; +struct S { struct U d[10]; }; +struct S s; + +int +f1 (struct T x, int i) +{ + char *p = x.d; + p += i; + return *p; +} + +/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +#ifdef __cplusplus +struct C +f2 (int i) +{ + struct C x; + x.d[i] = 'z'; + return x; +} + +/* { dg-output "\[^\n\r]*index 12 out of bounds for type 'char \\\[8\\\]'\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */ +/* { dg-output "\[^\n\r]*store to address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */ + +struct C +f3 (int i) +{ + struct C x; + char *p = x.d; + p += i; + *p = 'z'; + return x; +} + +/* { dg-output "\[^\n\r]*store to address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" { target { c++ } } } */ + +#endif + +int +f4 (int i) +{ + return s.d[i].b; +} + +/* { dg-output "\[^\n\r]*index 12 out of bounds for type 'U \\\[10\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +int +f5 (int i) +{ + struct U *u = s.d; + u += i; + return u->b; +} + +/* { dg-output "\[^\n\r]*load of address \[^\n\r]* with insufficient space for an object of type 'unsigned int'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ + +int +main (void) +{ + f1 (t, 12); +#ifdef __cplusplus + f2 (12); + f3 (12); +#endif + f4 (12); + f5 (12); + return 0; +} diff --git gcc/testsuite/g++.dg/ubsan/object-size-1.C gcc/testsuite/g++.dg/ubsan/object-size-1.C index e69de29..e2aad46 100644 --- gcc/testsuite/g++.dg/ubsan/object-size-1.C +++ gcc/testsuite/g++.dg/ubsan/object-size-1.C @@ -0,0 +1,18 @@ +// { dg-do compile } +// { dg-options "-fsanitize=undefined -fpermissive" } + +struct T { int c; char d[]; }; + +struct T t = { 1, "a" }; // { dg-warning "initializer-string for array of chars is too long" } + +int +baz (int i) +{ + return t.d[i]; +} + +int +main (void) +{ + baz (3); +} diff --git gcc/testsuite/gcc.dg/ubsan/object-size-9.c gcc/testsuite/gcc.dg/ubsan/object-size-9.c index e69de29..bb9aa1b 100644 --- gcc/testsuite/gcc.dg/ubsan/object-size-9.c +++ gcc/testsuite/gcc.dg/ubsan/object-size-9.c @@ -0,0 +1,24 @@ +/* { dg-do run } */ +/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */ +/* { dg-options "-fsanitize=undefined" } */ + +struct T { int c; char d[]; }; +struct T t = { 1, "a" }; + +int +baz (int i) +{ + return t.d[i]; +} + +int +main (void) +{ + baz (2); + return 0; +} + +/* { dg-output "load of address \[^\n\r]* with insufficient space for an object of type 'char'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*note: pointer points here\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*\\^\[^\n\r]*(\n|\r\n|\r)" } */ diff --git gcc/ubsan.c gcc/ubsan.c index c9a72ad..dde0418 100644 --- gcc/ubsan.c +++ gcc/ubsan.c @@ -50,6 +50,7 @@ along with GCC; see the file COPYING3. If not see #include "realmpfr.h" #include "dfp.h" #include "builtins.h" +#include "tree-object-size.h" /* Map from a tree to a VAR_DECL tree. */ @@ -391,7 +392,7 @@ ubsan_type_descriptor (tree type, enum ubsan_print_style pstyle) tree dom = TYPE_DOMAIN (t); if (dom && TREE_CODE (TYPE_MAX_VALUE (dom)) == INTEGER_CST) pos += sprintf (&pretty_name[pos], HOST_WIDE_INT_PRINT_DEC, - tree_to_shwi (TYPE_MAX_VALUE (dom)) + 1); + tree_to_uhwi (TYPE_MAX_VALUE (dom)) + 1); else /* ??? We can't determine the variable name; print VLA unspec. */ pretty_name[pos++] = '*'; @@ -614,12 +615,12 @@ ubsan_expand_bounds_ifn (gimple_stmt_iterator *gsi) /* Create condition "if (index > bound)". */ basic_block then_bb, fallthru_bb; gimple_stmt_iterator cond_insert_point - = create_cond_insert_point (gsi, 0/*before_p*/, false, true, + = create_cond_insert_point (gsi, false, false, true, &then_bb, &fallthru_bb); index = fold_convert (TREE_TYPE (bound), index); index = force_gimple_operand_gsi (&cond_insert_point, index, - true/*simple_p*/, NULL_TREE, - false/*before*/, GSI_NEW_STMT); + true, NULL_TREE, + false, GSI_NEW_STMT); gimple g = gimple_build_cond (GT_EXPR, index, bound, NULL_TREE, NULL_TREE); gimple_set_location (g, loc); gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT); @@ -830,6 +831,76 @@ ubsan_expand_null_ifn (gimple_stmt_iterator *gsip) return false; } +/* Expand UBSAN_OBJECT_SIZE internal call. */ + +bool +ubsan_expand_objsize_ifn (gimple_stmt_iterator *gsi) +{ + gimple stmt = gsi_stmt (*gsi); + location_t loc = gimple_location (stmt); + gcc_assert (gimple_call_num_args (stmt) == 4); + + tree ptr = gimple_call_arg (stmt, 0); + tree offset = gimple_call_arg (stmt, 1); + tree size = gimple_call_arg (stmt, 2); + tree ckind = gimple_call_arg (stmt, 3); + gimple_stmt_iterator gsi_orig = *gsi; + gimple g; + + /* See if we can discard the check. */ + if (TREE_CODE (size) != INTEGER_CST + || integer_all_onesp (size)) + /* Yes, __builtin_object_size couldn't determine the + object size. */; + else + { + /* if (offset > objsize) */ + basic_block then_bb, fallthru_bb; + gimple_stmt_iterator cond_insert_point + = create_cond_insert_point (gsi, false, false, true, + &then_bb, &fallthru_bb); + g = gimple_build_cond (GT_EXPR, offset, size, NULL_TREE, NULL_TREE); + gimple_set_location (g, loc); + gsi_insert_after (&cond_insert_point, g, GSI_NEW_STMT); + + /* Generate __ubsan_handle_type_mismatch call. */ + *gsi = gsi_after_labels (then_bb); + if (flag_sanitize_undefined_trap_on_error) + g = gimple_build_call (builtin_decl_explicit (BUILT_IN_TRAP), 0); + else + { + tree data + = ubsan_create_data ("__ubsan_objsz_data", 1, &loc, + ubsan_type_descriptor (TREE_TYPE (ptr), + UBSAN_PRINT_POINTER), + NULL_TREE, + build_zero_cst (pointer_sized_int_node), + ckind, + NULL_TREE); + data = build_fold_addr_expr_loc (loc, data); + enum built_in_function bcode + = flag_sanitize_recover + ? BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH + : BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH_ABORT; + tree p = make_ssa_name (pointer_sized_int_node, NULL); + g = gimple_build_assign_with_ops (NOP_EXPR, p, ptr, NULL_TREE); + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + g = gimple_build_call (builtin_decl_explicit (bcode), 2, data, p); + } + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + + /* Point GSI to next logical statement. */ + *gsi = gsi_start_bb (fallthru_bb); + } + + /* Get rid of the UBSAN_OBJECT_SIZE call from the IR. */ + unlink_stmt_vdef (stmt); + gsi_remove (&gsi_orig, true); + return gsi_end_p (*gsi); +} + /* Instrument a memory reference. BASE is the base of MEM, IS_LHS says whether the pointer is on the left hand side of the assignment. */ @@ -1339,6 +1410,128 @@ instrument_nonnull_return (gimple_stmt_iterator *gsi) flag_delete_null_pointer_checks = save_flag_delete_null_pointer_checks; } +/* Instrument memory references. Here we check whether the pointer + points to an out-of-bounds location. */ + +static void +instrument_object_size (gimple_stmt_iterator *gsi, bool is_lhs) +{ + gimple stmt = gsi_stmt (*gsi); + location_t loc = gimple_location (stmt); + tree t = is_lhs ? gimple_get_lhs (stmt) : gimple_assign_rhs1 (stmt); + tree type; + HOST_WIDE_INT size_in_bytes; + + type = TREE_TYPE (t); + if (VOID_TYPE_P (type)) + return; + + switch (TREE_CODE (t)) + { + case COMPONENT_REF: + if (TREE_CODE (t) == COMPONENT_REF + && DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (t, 1)) != NULL_TREE) + { + tree repr = DECL_BIT_FIELD_REPRESENTATIVE (TREE_OPERAND (t, 1)); + t = build3 (COMPONENT_REF, TREE_TYPE (repr), TREE_OPERAND (t, 0), + repr, NULL_TREE); + } + break; + case ARRAY_REF: + case INDIRECT_REF: + case MEM_REF: + case VAR_DECL: + case PARM_DECL: + case RESULT_DECL: + break; + default: + return; + } + + size_in_bytes = int_size_in_bytes (type); + if (size_in_bytes <= 0) + return; + + HOST_WIDE_INT bitsize, bitpos; + tree offset; + enum machine_mode mode; + int volatilep = 0, unsignedp = 0; + tree inner = get_inner_reference (t, &bitsize, &bitpos, &offset, &mode, + &unsignedp, &volatilep, false); + + if (bitpos % BITS_PER_UNIT != 0 + || bitsize != size_in_bytes * BITS_PER_UNIT) + return; + + bool decl_p = DECL_P (inner); + tree base = decl_p ? inner : TREE_OPERAND (inner, 0); + tree ptr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (t)), t); + + while (TREE_CODE (base) == SSA_NAME) + { + gimple def_stmt = SSA_NAME_DEF_STMT (base); + if (gimple_assign_ssa_name_copy_p (def_stmt) + || (gimple_assign_cast_p (def_stmt) + && POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (def_stmt)))) + || (is_gimple_assign (def_stmt) + && gimple_assign_rhs_code (def_stmt) == POINTER_PLUS_EXPR)) + base = gimple_assign_rhs1 (def_stmt); + else + break; + } + + if (!POINTER_TYPE_P (TREE_TYPE (base)) && !DECL_P (base)) + return; + + tree sizet; + tree base_addr = base; + if (decl_p) + base_addr = build1 (ADDR_EXPR, + build_pointer_type (TREE_TYPE (base)), base); + unsigned HOST_WIDE_INT size = compute_builtin_object_size (base_addr, 0); + if (size != (unsigned HOST_WIDE_INT) -1) + sizet = build_int_cst (sizetype, size); + else if (optimize) + { + if (LOCATION_LOCUS (loc) == UNKNOWN_LOCATION) + loc = input_location; + /* Generate __builtin_object_size call. */ + sizet = builtin_decl_explicit (BUILT_IN_OBJECT_SIZE); + sizet = build_call_expr_loc (loc, sizet, 2, base_addr, + integer_zero_node); + sizet = force_gimple_operand_gsi (gsi, sizet, false, NULL_TREE, true, + GSI_SAME_STMT); + } + else + return; + + /* Generate UBSAN_OBJECT_SIZE (ptr, ptr+sizeof(*ptr)-base, objsize, ckind) + call. */ + /* ptr + sizeof (*ptr) - base */ + t = fold_build2 (MINUS_EXPR, sizetype, + fold_convert (pointer_sized_int_node, ptr), + fold_convert (pointer_sized_int_node, base_addr)); + t = fold_build2 (PLUS_EXPR, sizetype, t, TYPE_SIZE_UNIT (type)); + + /* Perhaps we can omit the check. */ + if (TREE_CODE (t) == INTEGER_CST + && TREE_CODE (sizet) == INTEGER_CST + && tree_int_cst_le (t, sizet)) + return; + + /* Nope. Emit the check. */ + t = force_gimple_operand_gsi (gsi, t, true, NULL_TREE, true, + GSI_SAME_STMT); + ptr = force_gimple_operand_gsi (gsi, ptr, true, NULL_TREE, true, + GSI_SAME_STMT); + tree ckind = build_int_cst (unsigned_char_type_node, + is_lhs ? UBSAN_STORE_OF : UBSAN_LOAD_OF); + gimple g = gimple_build_call_internal (IFN_UBSAN_OBJECT_SIZE, 4, + ptr, t, sizet, ckind); + gimple_set_location (g, loc); + gsi_insert_before (gsi, g, GSI_SAME_STMT); +} + namespace { const pass_data pass_data_ubsan = @@ -1368,7 +1561,8 @@ public: | SANITIZE_BOOL | SANITIZE_ENUM | SANITIZE_ALIGNMENT | SANITIZE_NONNULL_ATTRIBUTE - | SANITIZE_RETURNS_NONNULL_ATTRIBUTE) + | SANITIZE_RETURNS_NONNULL_ATTRIBUTE + | SANITIZE_OBJECT_SIZE) && current_function_decl != NULL_TREE && !lookup_attribute ("no_sanitize_undefined", DECL_ATTRIBUTES (current_function_decl)); @@ -1431,6 +1625,14 @@ pass_ubsan::execute (function *fun) bb = gimple_bb (stmt); } + if (flag_sanitize & SANITIZE_OBJECT_SIZE) + { + if (gimple_store_p (stmt)) + instrument_object_size (&gsi, true); + if (gimple_assign_load_p (stmt)) + instrument_object_size (&gsi, false); + } + gsi_next (&gsi); } } diff --git gcc/ubsan.h gcc/ubsan.h index d0b404f..27c18eb 100644 --- gcc/ubsan.h +++ gcc/ubsan.h @@ -40,6 +40,7 @@ enum ubsan_print_style { extern bool ubsan_expand_bounds_ifn (gimple_stmt_iterator *); extern bool ubsan_expand_null_ifn (gimple_stmt_iterator *); +extern bool ubsan_expand_objsize_ifn (gimple_stmt_iterator *); extern tree ubsan_instrument_unreachable (location_t); extern tree ubsan_create_data (const char *, int, const location_t *, ...); extern tree ubsan_type_descriptor (tree, enum ubsan_print_style = UBSAN_PRINT_NORMAL);