From patchwork Sat Nov 16 01:23:36 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 1196041 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-513765-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="KfV1FyUB"; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="HXvpoido"; dkim-atps=neutral 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 47FHk22WcFz9s7T for ; Sat, 16 Nov 2019 12:31:22 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references :content-type:content-transfer-encoding; q=dns; s=default; b=HsI Y83L6h1AD5PA+ZT+gwNpVUzIW39ZUo40nK+Q23dfFYeh/kl2DFOFk45R1Gqfr9xI cQyLj4cD4RHB+akpmc4ZtxqsUoXewBnuHdwQa2VUSJjhPjdWVZCVaquaqxNBzwKX XliFF9QZBJAzpb/sPydz1YZJY4QBYpkvuuf0A+QQ= 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:from :to:cc:subject:date:message-id:in-reply-to:references :content-type:content-transfer-encoding; s=default; bh=0JpFUagqy a+YAL+5iPoDC9GI5a4=; b=KfV1FyUBNdxV5rVRRR1wjDFVKmVZqf626H98/o3CE QdyJiCe1FmXZb4Ho/qXPzWkh0MzS8AmbIqe4ZnPOUdE+PTSFOcXPpTAeNVmLFGL7 LXuIgdHQ6VxmexeZmzcJ2SFbJyFEeqvOjA4pDoqTMdrrMx9mbejeNqBCL9tQGBdb fc= Received: (qmail 83398 invoked by alias); 16 Nov 2019 01:21:38 -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 67020 invoked by uid 89); 16 Nov 2019 01:19:06 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-21.1 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_ASCII_DIVIDERS, KAM_SHORT, SPF_PASS, UNSUBSCRIBE_BODY autolearn=ham version=3.3.1 spammy=consolidation, ds, rewinding X-HELO: us-smtp-1.mimecast.com Received: from us-smtp-delivery-1.mimecast.com (HELO us-smtp-1.mimecast.com) (207.211.31.120) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sat, 16 Nov 2019 01:18:14 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1573867092; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=KjX4w6MzS5JhXM4Mq/sJHBKrYjzpvNep1fIJq94gmkw=; b=HXvpoidot3xSyQWNjuNz1UdZMwQvKYKwSKl5HfOGa+6t8Az1XlMciTcSA/kg7v0MMAovqN w4CK7fE3Szg36aPmxYKhYbI6kbhd5VKgQg1Fdd498Ac38FNfszuC3nTsjxFKHgJUjmW3LQ kTeM0dg2sY1dlLV7h5T7k9cun7mpEtE= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-3-G3Y1pNPfNcuaqW4akxsgdw-1; Fri, 15 Nov 2019 20:18:08 -0500 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id DB5E2801E76 for ; Sat, 16 Nov 2019 01:18:07 +0000 (UTC) Received: from c64.redhat.com (ovpn-112-32.phx2.redhat.com [10.3.112.32]) by smtp.corp.redhat.com (Postfix) with ESMTP id 7F88910246FB; Sat, 16 Nov 2019 01:18:06 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [PATCH 49/49] analyzer: test suite Date: Fri, 15 Nov 2019 20:23:36 -0500 Message-Id: <1573867416-55618-50-git-send-email-dmalcolm@redhat.com> In-Reply-To: <1573867416-55618-1-git-send-email-dmalcolm@redhat.com> References: <1573867416-55618-1-git-send-email-dmalcolm@redhat.com> X-Mimecast-Spam-Score: 0 X-IsSubscribed: yes This patch adds the testsuite for the analyzer. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/CVE-2005-1689-minimal.c: New test. * gcc.dg/analyzer/abort.c: New test. * gcc.dg/analyzer/alloca-leak.c: New test. * gcc.dg/analyzer/analyzer-verbosity-0.c: New test. * gcc.dg/analyzer/analyzer-verbosity-1.c: New test. * gcc.dg/analyzer/analyzer-verbosity-2.c: New test. * gcc.dg/analyzer/analyzer.exp: New suite. * gcc.dg/analyzer/attribute-nonnull.c: New test. * gcc.dg/analyzer/call-summaries-1.c: New test. * gcc.dg/analyzer/conditionals-2.c: New test. * gcc.dg/analyzer/conditionals-3.c: New test. * gcc.dg/analyzer/conditionals-notrans.c: New test. * gcc.dg/analyzer/conditionals-trans.c: New test. * gcc.dg/analyzer/data-model-1.c: New test. * gcc.dg/analyzer/data-model-2.c: New test. * gcc.dg/analyzer/data-model-3.c: New test. * gcc.dg/analyzer/data-model-4.c: New test. * gcc.dg/analyzer/data-model-5.c: New test. * gcc.dg/analyzer/data-model-5b.c: New test. * gcc.dg/analyzer/data-model-5c.c: New test. * gcc.dg/analyzer/data-model-5d.c: New test. * gcc.dg/analyzer/data-model-6.c: New test. * gcc.dg/analyzer/data-model-7.c: New test. * gcc.dg/analyzer/data-model-8.c: New test. * gcc.dg/analyzer/data-model-9.c: New test. * gcc.dg/analyzer/data-model-11.c: New test. * gcc.dg/analyzer/data-model-12.c: New test. * gcc.dg/analyzer/data-model-13.c: New test. * gcc.dg/analyzer/data-model-14.c: New test. * gcc.dg/analyzer/data-model-15.c: New test. * gcc.dg/analyzer/data-model-16.c: New test. * gcc.dg/analyzer/data-model-17.c: New test. * gcc.dg/analyzer/data-model-18.c: New test. * gcc.dg/analyzer/data-model-19.c: New test. * gcc.dg/analyzer/data-model-path-1.c: New test. * gcc.dg/analyzer/double-free-lto-1-a.c: New test. * gcc.dg/analyzer/double-free-lto-1-b.c: New test. * gcc.dg/analyzer/double-free-lto-1.h: New header. * gcc.dg/analyzer/equivalence.c: New test. * gcc.dg/analyzer/explode-1.c: New test. * gcc.dg/analyzer/explode-2.c: New test. * gcc.dg/analyzer/factorial.c: New test. * gcc.dg/analyzer/fibonacci.c: New test. * gcc.dg/analyzer/fields.c: New test. * gcc.dg/analyzer/file-1.c: New test. * gcc.dg/analyzer/file-2.c: New test. * gcc.dg/analyzer/function-ptr-1.c: New test. * gcc.dg/analyzer/function-ptr-2.c: New test. * gcc.dg/analyzer/function-ptr-3.c: New test. * gcc.dg/analyzer/gzio-2.c: New test. * gcc.dg/analyzer/gzio-3.c: New test. * gcc.dg/analyzer/gzio-3a.c: New test. * gcc.dg/analyzer/gzio.c: New test. * gcc.dg/analyzer/infinite-recursion.c: New test. * gcc.dg/analyzer/loop-2.c: New test. * gcc.dg/analyzer/loop-2a.c: New test. * gcc.dg/analyzer/loop-3.c: New test. * gcc.dg/analyzer/loop-4.c: New test. * gcc.dg/analyzer/loop.c: New test. * gcc.dg/analyzer/malloc-1.c: New test. * gcc.dg/analyzer/malloc-2.c: New test. * gcc.dg/analyzer/malloc-3.c: New test. * gcc.dg/analyzer/malloc-dce.c: New test. * gcc.dg/analyzer/malloc-dedupe-1.c: New test. * gcc.dg/analyzer/malloc-ipa-1.c: New test. * gcc.dg/analyzer/malloc-ipa-10.c: New test. * gcc.dg/analyzer/malloc-ipa-11.c: New test. * gcc.dg/analyzer/malloc-ipa-12.c: New test. * gcc.dg/analyzer/malloc-ipa-13.c: New test. * gcc.dg/analyzer/malloc-ipa-2.c: New test. * gcc.dg/analyzer/malloc-ipa-3.c: New test. * gcc.dg/analyzer/malloc-ipa-4.c: New test. * gcc.dg/analyzer/malloc-ipa-5.c: New test. * gcc.dg/analyzer/malloc-ipa-6.c: New test. * gcc.dg/analyzer/malloc-ipa-7.c: New test. * gcc.dg/analyzer/malloc-ipa-8-double-free.c: New test. * gcc.dg/analyzer/malloc-ipa-8-unchecked.c: New test. * gcc.dg/analyzer/malloc-ipa-9.c: New test. * gcc.dg/analyzer/malloc-macro-inline-events.c: New test. * gcc.dg/analyzer/malloc-macro-separate-events.c: New test. * gcc.dg/analyzer/malloc-macro.h: New header. * gcc.dg/analyzer/malloc-many-paths-1.c: New test. * gcc.dg/analyzer/malloc-many-paths-2.c: New test. * gcc.dg/analyzer/malloc-many-paths-3.c: New test. * gcc.dg/analyzer/malloc-paths-1.c: New test. * gcc.dg/analyzer/malloc-paths-10.c: New test. * gcc.dg/analyzer/malloc-paths-2.c: New test. * gcc.dg/analyzer/malloc-paths-3.c: New test. * gcc.dg/analyzer/malloc-paths-4.c: New test. * gcc.dg/analyzer/malloc-paths-5.c: New test. * gcc.dg/analyzer/malloc-paths-6.c: New test. * gcc.dg/analyzer/malloc-paths-7.c: New test. * gcc.dg/analyzer/malloc-paths-8.c: New test. * gcc.dg/analyzer/malloc-paths-9.c: New test. * gcc.dg/analyzer/malloc-vs-local-1a.c: New test. * gcc.dg/analyzer/malloc-vs-local-1b.c: New test. * gcc.dg/analyzer/malloc-vs-local-2.c: New test. * gcc.dg/analyzer/malloc-vs-local-3.c: New test. * gcc.dg/analyzer/malloc-vs-local-4.c: New test. * gcc.dg/analyzer/operations.c: New test. * gcc.dg/analyzer/params-2.c: New test. * gcc.dg/analyzer/params.c: New test. * gcc.dg/analyzer/paths-1.c: New test. * gcc.dg/analyzer/paths-1a.c: New test. * gcc.dg/analyzer/paths-2.c: New test. * gcc.dg/analyzer/paths-3.c: New test. * gcc.dg/analyzer/paths-4.c: New test. * gcc.dg/analyzer/paths-5.c: New test. * gcc.dg/analyzer/paths-6.c: New test. * gcc.dg/analyzer/paths-7.c: New test. * gcc.dg/analyzer/pattern-test-1.c: New test. * gcc.dg/analyzer/pattern-test-2.c: New test. * gcc.dg/analyzer/pointer-merging.c: New test. * gcc.dg/analyzer/pr61861.c: New test. * gcc.dg/analyzer/pragma-1.c: New test. * gcc.dg/analyzer/scope-1.c: New test. * gcc.dg/analyzer/sensitive-1.c: New test. * gcc.dg/analyzer/setjmp-1.c: New test. * gcc.dg/analyzer/setjmp-2.c: New test. * gcc.dg/analyzer/setjmp-3.c: New test. * gcc.dg/analyzer/setjmp-4.c: New test. * gcc.dg/analyzer/setjmp-5.c: New test. * gcc.dg/analyzer/setjmp-6.c: New test. * gcc.dg/analyzer/setjmp-7.c: New test. * gcc.dg/analyzer/setjmp-8.c: New test. * gcc.dg/analyzer/setjmp-9.c: New test. * gcc.dg/analyzer/switch.c: New test. * gcc.dg/analyzer/taint-1.c: New test. * gcc.dg/analyzer/zlib-1.c: New test. * gcc.dg/analyzer/zlib-2.c: New test. * gcc.dg/analyzer/zlib-3.c: New test. * gcc.dg/analyzer/zlib-4.c: New test. * gcc.dg/analyzer/zlib-5.c: New test. * gcc.dg/analyzer/zlib-6.c: New test. * lib/target-supports.exp (check_effective_target_analyzer): New. --- .../gcc.dg/analyzer/CVE-2005-1689-minimal.c | 30 + gcc/testsuite/gcc.dg/analyzer/abort.c | 71 ++ gcc/testsuite/gcc.dg/analyzer/alloca-leak.c | 8 + .../gcc.dg/analyzer/analyzer-verbosity-0.c | 133 +++ .../gcc.dg/analyzer/analyzer-verbosity-1.c | 160 +++ .../gcc.dg/analyzer/analyzer-verbosity-2.c | 191 ++++ gcc/testsuite/gcc.dg/analyzer/analyzer.exp | 49 + gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c | 57 ++ gcc/testsuite/gcc.dg/analyzer/call-summaries-1.c | 14 + gcc/testsuite/gcc.dg/analyzer/conditionals-2.c | 44 + gcc/testsuite/gcc.dg/analyzer/conditionals-3.c | 45 + .../gcc.dg/analyzer/conditionals-notrans.c | 158 +++ gcc/testsuite/gcc.dg/analyzer/conditionals-trans.c | 143 +++ gcc/testsuite/gcc.dg/analyzer/data-model-1.c | 1078 ++++++++++++++++++++ gcc/testsuite/gcc.dg/analyzer/data-model-10.c | 17 + gcc/testsuite/gcc.dg/analyzer/data-model-11.c | 6 + gcc/testsuite/gcc.dg/analyzer/data-model-12.c | 13 + gcc/testsuite/gcc.dg/analyzer/data-model-13.c | 21 + gcc/testsuite/gcc.dg/analyzer/data-model-14.c | 24 + gcc/testsuite/gcc.dg/analyzer/data-model-15.c | 34 + gcc/testsuite/gcc.dg/analyzer/data-model-16.c | 50 + gcc/testsuite/gcc.dg/analyzer/data-model-17.c | 20 + gcc/testsuite/gcc.dg/analyzer/data-model-18.c | 20 + gcc/testsuite/gcc.dg/analyzer/data-model-19.c | 31 + gcc/testsuite/gcc.dg/analyzer/data-model-2.c | 13 + gcc/testsuite/gcc.dg/analyzer/data-model-3.c | 15 + gcc/testsuite/gcc.dg/analyzer/data-model-4.c | 16 + gcc/testsuite/gcc.dg/analyzer/data-model-5.c | 100 ++ gcc/testsuite/gcc.dg/analyzer/data-model-5b.c | 91 ++ gcc/testsuite/gcc.dg/analyzer/data-model-5c.c | 84 ++ gcc/testsuite/gcc.dg/analyzer/data-model-5d.c | 63 ++ gcc/testsuite/gcc.dg/analyzer/data-model-6.c | 13 + gcc/testsuite/gcc.dg/analyzer/data-model-7.c | 19 + gcc/testsuite/gcc.dg/analyzer/data-model-8.c | 24 + gcc/testsuite/gcc.dg/analyzer/data-model-9.c | 32 + gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c | 13 + .../gcc.dg/analyzer/double-free-lto-1-a.c | 16 + .../gcc.dg/analyzer/double-free-lto-1-b.c | 8 + gcc/testsuite/gcc.dg/analyzer/double-free-lto-1.h | 1 + gcc/testsuite/gcc.dg/analyzer/equivalence.c | 29 + gcc/testsuite/gcc.dg/analyzer/explode-1.c | 60 ++ gcc/testsuite/gcc.dg/analyzer/explode-2.c | 50 + gcc/testsuite/gcc.dg/analyzer/factorial.c | 7 + gcc/testsuite/gcc.dg/analyzer/fibonacci.c | 9 + gcc/testsuite/gcc.dg/analyzer/fields.c | 41 + gcc/testsuite/gcc.dg/analyzer/file-1.c | 37 + gcc/testsuite/gcc.dg/analyzer/file-2.c | 18 + gcc/testsuite/gcc.dg/analyzer/function-ptr-1.c | 8 + gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c | 43 + gcc/testsuite/gcc.dg/analyzer/function-ptr-3.c | 17 + gcc/testsuite/gcc.dg/analyzer/gzio-2.c | 11 + gcc/testsuite/gcc.dg/analyzer/gzio-3.c | 31 + gcc/testsuite/gcc.dg/analyzer/gzio-3a.c | 27 + gcc/testsuite/gcc.dg/analyzer/gzio.c | 17 + gcc/testsuite/gcc.dg/analyzer/infinite-recursion.c | 55 + gcc/testsuite/gcc.dg/analyzer/loop-2.c | 36 + gcc/testsuite/gcc.dg/analyzer/loop-2a.c | 39 + gcc/testsuite/gcc.dg/analyzer/loop-3.c | 17 + gcc/testsuite/gcc.dg/analyzer/loop-4.c | 41 + gcc/testsuite/gcc.dg/analyzer/loop.c | 33 + gcc/testsuite/gcc.dg/analyzer/malloc-1.c | 565 ++++++++++ gcc/testsuite/gcc.dg/analyzer/malloc-2.c | 23 + gcc/testsuite/gcc.dg/analyzer/malloc-3.c | 8 + gcc/testsuite/gcc.dg/analyzer/malloc-dce.c | 12 + gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c | 46 + gcc/testsuite/gcc.dg/analyzer/malloc-ipa-1.c | 24 + gcc/testsuite/gcc.dg/analyzer/malloc-ipa-10.c | 32 + gcc/testsuite/gcc.dg/analyzer/malloc-ipa-11.c | 95 ++ gcc/testsuite/gcc.dg/analyzer/malloc-ipa-12.c | 7 + gcc/testsuite/gcc.dg/analyzer/malloc-ipa-13.c | 30 + gcc/testsuite/gcc.dg/analyzer/malloc-ipa-2.c | 34 + gcc/testsuite/gcc.dg/analyzer/malloc-ipa-3.c | 23 + gcc/testsuite/gcc.dg/analyzer/malloc-ipa-4.c | 13 + gcc/testsuite/gcc.dg/analyzer/malloc-ipa-5.c | 13 + gcc/testsuite/gcc.dg/analyzer/malloc-ipa-6.c | 22 + gcc/testsuite/gcc.dg/analyzer/malloc-ipa-7.c | 29 + .../gcc.dg/analyzer/malloc-ipa-8-double-free.c | 172 ++++ .../gcc.dg/analyzer/malloc-ipa-8-unchecked.c | 66 ++ gcc/testsuite/gcc.dg/analyzer/malloc-ipa-9.c | 18 + .../gcc.dg/analyzer/malloc-macro-inline-events.c | 45 + .../gcc.dg/analyzer/malloc-macro-separate-events.c | 15 + gcc/testsuite/gcc.dg/analyzer/malloc-macro.h | 2 + .../gcc.dg/analyzer/malloc-many-paths-1.c | 14 + .../gcc.dg/analyzer/malloc-many-paths-2.c | 30 + .../gcc.dg/analyzer/malloc-many-paths-3.c | 36 + gcc/testsuite/gcc.dg/analyzer/malloc-paths-1.c | 15 + gcc/testsuite/gcc.dg/analyzer/malloc-paths-10.c | 19 + gcc/testsuite/gcc.dg/analyzer/malloc-paths-2.c | 13 + gcc/testsuite/gcc.dg/analyzer/malloc-paths-3.c | 14 + gcc/testsuite/gcc.dg/analyzer/malloc-paths-4.c | 20 + gcc/testsuite/gcc.dg/analyzer/malloc-paths-5.c | 43 + gcc/testsuite/gcc.dg/analyzer/malloc-paths-6.c | 11 + gcc/testsuite/gcc.dg/analyzer/malloc-paths-7.c | 21 + gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c | 54 + gcc/testsuite/gcc.dg/analyzer/malloc-paths-9.c | 298 ++++++ gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1a.c | 180 ++++ gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1b.c | 175 ++++ gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-2.c | 178 ++++ gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-3.c | 65 ++ gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-4.c | 40 + gcc/testsuite/gcc.dg/analyzer/operations.c | 42 + gcc/testsuite/gcc.dg/analyzer/params-2.c | 16 + gcc/testsuite/gcc.dg/analyzer/params.c | 32 + gcc/testsuite/gcc.dg/analyzer/paths-1.c | 16 + gcc/testsuite/gcc.dg/analyzer/paths-1a.c | 16 + gcc/testsuite/gcc.dg/analyzer/paths-2.c | 25 + gcc/testsuite/gcc.dg/analyzer/paths-3.c | 48 + gcc/testsuite/gcc.dg/analyzer/paths-4.c | 49 + gcc/testsuite/gcc.dg/analyzer/paths-5.c | 10 + gcc/testsuite/gcc.dg/analyzer/paths-6.c | 118 +++ gcc/testsuite/gcc.dg/analyzer/paths-7.c | 58 ++ gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c | 28 + gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c | 29 + gcc/testsuite/gcc.dg/analyzer/pointer-merging.c | 16 + gcc/testsuite/gcc.dg/analyzer/pr61861.c | 2 + gcc/testsuite/gcc.dg/analyzer/pragma-1.c | 26 + gcc/testsuite/gcc.dg/analyzer/scope-1.c | 23 + gcc/testsuite/gcc.dg/analyzer/sensitive-1.c | 33 + gcc/testsuite/gcc.dg/analyzer/setjmp-1.c | 1 + gcc/testsuite/gcc.dg/analyzer/setjmp-2.c | 97 ++ gcc/testsuite/gcc.dg/analyzer/setjmp-3.c | 106 ++ gcc/testsuite/gcc.dg/analyzer/setjmp-4.c | 107 ++ gcc/testsuite/gcc.dg/analyzer/setjmp-5.c | 65 ++ gcc/testsuite/gcc.dg/analyzer/setjmp-6.c | 31 + gcc/testsuite/gcc.dg/analyzer/setjmp-7.c | 36 + gcc/testsuite/gcc.dg/analyzer/setjmp-8.c | 107 ++ gcc/testsuite/gcc.dg/analyzer/setjmp-9.c | 109 ++ gcc/testsuite/gcc.dg/analyzer/switch.c | 28 + gcc/testsuite/gcc.dg/analyzer/taint-1.c | 128 +++ gcc/testsuite/gcc.dg/analyzer/zlib-1.c | 67 ++ gcc/testsuite/gcc.dg/analyzer/zlib-2.c | 51 + gcc/testsuite/gcc.dg/analyzer/zlib-3.c | 214 ++++ gcc/testsuite/gcc.dg/analyzer/zlib-4.c | 20 + gcc/testsuite/gcc.dg/analyzer/zlib-5.c | 49 + gcc/testsuite/gcc.dg/analyzer/zlib-6.c | 47 + gcc/testsuite/lib/target-supports.exp | 8 + 136 files changed, 7929 insertions(+) create mode 100644 gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-minimal.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/abort.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/alloca-leak.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-0.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/analyzer.exp create mode 100644 gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/call-summaries-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/conditionals-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/conditionals-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/conditionals-trans.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-10.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-11.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-12.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-13.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-14.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-15.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-16.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-17.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-18.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-19.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-4.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-5.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-5b.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-5c.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-5d.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-6.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-7.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-8.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-9.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-a.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-b.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/double-free-lto-1.h create mode 100644 gcc/testsuite/gcc.dg/analyzer/equivalence.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/explode-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/explode-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/factorial.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/fibonacci.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/fields.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/file-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/file-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/function-ptr-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/function-ptr-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/gzio-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/gzio-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/gzio-3a.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/gzio.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/infinite-recursion.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/loop-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/loop-2a.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/loop-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/loop-4.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/loop.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-dce.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-10.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-11.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-12.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-13.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-4.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-5.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-6.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-7.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-9.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-macro-separate-events.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-macro.h create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-10.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-4.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-5.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-6.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-7.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-paths-9.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1a.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1b.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-4.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/operations.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/params-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/params.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-1a.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-4.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-5.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-6.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/paths-7.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/pointer-merging.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/pr61861.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/pragma-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/scope-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/sensitive-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-4.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-5.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-6.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-7.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-8.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/setjmp-9.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/switch.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/zlib-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/zlib-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/zlib-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/zlib-4.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/zlib-5.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/zlib-6.c diff --git a/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-minimal.c b/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-minimal.c new file mode 100644 index 0000000..aa9deb3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/CVE-2005-1689-minimal.c @@ -0,0 +1,30 @@ +#include + +typedef struct _krb5_data { + char *data; +} krb5_data; + +void +test_1 (krb5_data inbuf, int flag) +{ + free(inbuf.data); /* { dg-message "first 'free' here" } */ + free(inbuf.data); /* { dg-warning "double-'free' of 'inbuf.data'" } */ +} + +void +test_2 (krb5_data inbuf, int flag) +{ + if (flag) { + free(inbuf.data); /* { dg-message "first 'free' here" } */ + } + free(inbuf.data); /* { dg-warning "double-'free' of 'inbuf.data'" } */ +} + +void +test_3 (krb5_data inbuf, int flag) +{ + if (flag) { + free((char *)inbuf.data); /* { dg-message "first 'free' here" } */ + } + free((char *)inbuf.data); /* { dg-warning "double-'free' of 'inbuf.data'" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/abort.c b/gcc/testsuite/gcc.dg/analyzer/abort.c new file mode 100644 index 0000000..cfbf74c --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/abort.c @@ -0,0 +1,71 @@ +#include +#include +#include + +extern void foo (); +extern void bar (); + +void test_1 (int i) +{ + if (i == 42) + abort (); + + __analyzer_eval (i != 42); /* { dg-warning "TRUE" } */ +} + +void test_2 (int i) +{ + if (i) + foo (); + else + bar (); + + foo (); + + if (i) + foo (); + else + abort (); + + __analyzer_eval (i != 0); /* { dg-warning "TRUE" } */ +} + +/**************************************************************************/ + +void calls_abort (const char *msg) +{ + fprintf (stderr, "%s", msg); + abort (); +} + +void test_3 (void *ptr) +{ + if (!ptr) + calls_abort ("ptr was NULL"); + + __analyzer_eval (ptr != 0); /* { dg-warning "TRUE" } */ +} + +/**************************************************************************/ + +extern void marked_noreturn (const char *msg) + __attribute__ ((__noreturn__)); + +void test_4 (void *ptr) +{ + if (!ptr) + marked_noreturn ("ptr was NULL"); + + __analyzer_eval (ptr != 0); /* { dg-warning "TRUE" } */ +} + +/**************************************************************************/ + +void test_5 (int i) +{ + assert (i < 10); + + /* We have not defined NDEBUG, so this will call __assert_fail if + i >= 10, which is labelled with __attribute__ ((__noreturn__)). */ + __analyzer_eval (i < 10); /* { dg-warning "TRUE" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/alloca-leak.c b/gcc/testsuite/gcc.dg/analyzer/alloca-leak.c new file mode 100644 index 0000000..6d9fe34 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/alloca-leak.c @@ -0,0 +1,8 @@ +#include + +void *test (void) +{ + void *ptr = alloca (64); + return ptr; +} +/* TODO: warn about escaping alloca. */ diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-0.c b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-0.c new file mode 100644 index 0000000..98200b3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-0.c @@ -0,0 +1,133 @@ +/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret -fanalyzer-verbosity=0" } */ + +#include + +void calls_free_1 (void *ptr) +{ + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ +} + +void test_1 (void *ptr, int a, int b) +{ + if (a) + calls_free_1 (ptr); + + if (b) + { + } + else + calls_free_1 (ptr); +} + +/* { dg-begin-multiline-output "" } + NN | free (ptr); + | ^~~~~~~~~~ + 'test_1': event 1 + | + | NN | calls_free_1 (ptr); + | | ^~~~~~~~~~~~~~~~~~ + | | | + | | (1) calling 'calls_free_1' from 'test_1' + | + +--> 'calls_free_1': event 2 + | + | NN | free (ptr); + | | ^~~~~~~~~~ + | | | + | | (2) first 'free' here + | + <------+ + | + 'test_1': events 3-4 + | + | NN | calls_free_1 (ptr); + | | ^~~~~~~~~~~~~~~~~~ + | | | + | | (3) returning to 'test_1' from 'calls_free_1' + |...... + | NN | calls_free_1 (ptr); + | | ~~~~~~~~~~~~~~~~~~ + | | | + | | (4) passing freed pointer 'ptr' in call to 'calls_free_1' from 'test_1' + | + +--> 'calls_free_1': event 5 + | + | NN | free (ptr); + | | ^~~~~~~~~~ + | | | + | | (5) second 'free' here; first 'free' was at (2) + | + { dg-end-multiline-output "" } */ + +void calls_free_2 (void *ptr) +{ + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ +} + +void test_2 (void *ptr, int a, int b) +{ + switch (a) + { + default: + break; + case 1: + break; + case 3: + calls_free_2 (ptr); + break; + } + + switch (b) + { + default: + calls_free_2 (ptr); + break; + case 1: + break; + case 42: + break; + } +} + +/* { dg-begin-multiline-output "" } + NN | free (ptr); + | ^~~~~~~~~~ + 'test_2': event 1 + | + | NN | calls_free_2 (ptr); + | | ^~~~~~~~~~~~~~~~~~ + | | | + | | (1) calling 'calls_free_2' from 'test_2' + | + +--> 'calls_free_2': event 2 + | + | NN | free (ptr); + | | ^~~~~~~~~~ + | | | + | | (2) first 'free' here + | + <------+ + | + 'test_2': events 3-4 + | + | NN | calls_free_2 (ptr); + | | ^~~~~~~~~~~~~~~~~~ + | | | + | | (3) returning to 'test_2' from 'calls_free_2' + |...... + | NN | calls_free_2 (ptr); + | | ~~~~~~~~~~~~~~~~~~ + | | | + | | (4) passing freed pointer 'ptr' in call to 'calls_free_2' from 'test_2' + | + +--> 'calls_free_2': event 5 + | + | NN | free (ptr); + | | ^~~~~~~~~~ + | | | + | | (5) second 'free' here; first 'free' was at (2) + | + { dg-end-multiline-output "" } */ + +// TODO: range cases + diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-1.c b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-1.c new file mode 100644 index 0000000..7e437bb --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-1.c @@ -0,0 +1,160 @@ +/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret -fanalyzer-verbosity=1" } */ + +#include + +void calls_free_1 (void *ptr) +{ + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ +} + +void test_1 (void *ptr, int a, int b) +{ + if (a) + calls_free_1 (ptr); + + if (b) + { + } + else + calls_free_1 (ptr); +} + +/* { dg-begin-multiline-output "" } + NN | free (ptr); + | ^~~~~~~~~~ + 'test_1': events 1-2 + | + | NN | void test_1 (void *ptr, int a, int b) + | | ^~~~~~ + | | | + | | (1) entry to 'test_1' + |...... + | NN | calls_free_1 (ptr); + | | ~~~~~~~~~~~~~~~~~~ + | | | + | | (2) calling 'calls_free_1' from 'test_1' + | + +--> 'calls_free_1': events 3-4 + | + | NN | void calls_free_1 (void *ptr) + | | ^~~~~~~~~~~~ + | | | + | | (3) entry to 'calls_free_1' + | NN | { + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (4) first 'free' here + | + <------+ + | + 'test_1': events 5-6 + | + | NN | calls_free_1 (ptr); + | | ^~~~~~~~~~~~~~~~~~ + | | | + | | (5) returning to 'test_1' from 'calls_free_1' + |...... + | NN | calls_free_1 (ptr); + | | ~~~~~~~~~~~~~~~~~~ + | | | + | | (6) passing freed pointer 'ptr' in call to 'calls_free_1' from 'test_1' + | + +--> 'calls_free_1': events 7-8 + | + | NN | void calls_free_1 (void *ptr) + | | ^~~~~~~~~~~~ + | | | + | | (7) entry to 'calls_free_1' + | NN | { + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (8) second 'free' here; first 'free' was at (4) + | + { dg-end-multiline-output "" } */ + +void calls_free_2 (void *ptr) +{ + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ +} + +void test_2 (void *ptr, int a, int b) +{ + switch (a) + { + default: + break; + case 1: + break; + case 3: + calls_free_2 (ptr); + break; + } + + switch (b) + { + default: + calls_free_2 (ptr); + break; + case 1: + break; + case 42: + break; + } +} + +/* { dg-begin-multiline-output "" } + NN | free (ptr); + | ^~~~~~~~~~ + 'test_2': events 1-2 + | + | NN | void test_2 (void *ptr, int a, int b) + | | ^~~~~~ + | | | + | | (1) entry to 'test_2' + |...... + | NN | calls_free_2 (ptr); + | | ~~~~~~~~~~~~~~~~~~ + | | | + | | (2) calling 'calls_free_2' from 'test_2' + | + +--> 'calls_free_2': events 3-4 + | + | NN | void calls_free_2 (void *ptr) + | | ^~~~~~~~~~~~ + | | | + | | (3) entry to 'calls_free_2' + | NN | { + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (4) first 'free' here + | + <------+ + | + 'test_2': events 5-6 + | + | NN | calls_free_2 (ptr); + | | ^~~~~~~~~~~~~~~~~~ + | | | + | | (5) returning to 'test_2' from 'calls_free_2' + |...... + | NN | calls_free_2 (ptr); + | | ~~~~~~~~~~~~~~~~~~ + | | | + | | (6) passing freed pointer 'ptr' in call to 'calls_free_2' from 'test_2' + | + +--> 'calls_free_2': events 7-8 + | + | NN | void calls_free_2 (void *ptr) + | | ^~~~~~~~~~~~ + | | | + | | (7) entry to 'calls_free_2' + | NN | { + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (8) second 'free' here; first 'free' was at (4) + | + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-2.c b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-2.c new file mode 100644 index 0000000..2c9e5da --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/analyzer-verbosity-2.c @@ -0,0 +1,191 @@ +/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret -fanalyzer-verbosity=2" } */ + +#include + +void calls_free_1 (void *ptr) +{ + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ +} + +void test_1 (void *ptr, int a, int b) +{ + if (a) + calls_free_1 (ptr); + + if (b) + { + } + else + calls_free_1 (ptr); +} + +/* { dg-begin-multiline-output "" } + NN | free (ptr); + | ^~~~~~~~~~ + 'test_1': events 1-4 + | + | NN | void test_1 (void *ptr, int a, int b) + | | ^~~~~~ + | | | + | | (1) entry to 'test_1' + | NN | { + | NN | if (a) + | | ~ + | | | + | | (2) following 'true' branch (when 'a != 0')... + | NN | calls_free_1 (ptr); + | | ~~~~~~~~~~~~~~~~~~ + | | | + | | (3) ...to here + | | (4) calling 'calls_free_1' from 'test_1' + | + +--> 'calls_free_1': events 5-6 + | + | NN | void calls_free_1 (void *ptr) + | | ^~~~~~~~~~~~ + | | | + | | (5) entry to 'calls_free_1' + | NN | { + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (6) first 'free' here + | + <------+ + | + 'test_1': events 7-10 + | + | NN | calls_free_1 (ptr); + | | ^~~~~~~~~~~~~~~~~~ + | | | + | | (7) returning to 'test_1' from 'calls_free_1' + | NN | + | NN | if (b) + | | ~ + | | | + | | (8) following 'false' branch (when 'b == 0')... + |...... + | NN | calls_free_1 (ptr); + | | ~~~~~~~~~~~~~~~~~~ + | | | + | | (9) ...to here + | | (10) passing freed pointer 'ptr' in call to 'calls_free_1' from 'test_1' + | + +--> 'calls_free_1': events 11-12 + | + | NN | void calls_free_1 (void *ptr) + | | ^~~~~~~~~~~~ + | | | + | | (11) entry to 'calls_free_1' + | NN | { + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (12) second 'free' here; first 'free' was at (6) + | + { dg-end-multiline-output "" } */ + +void calls_free_2 (void *ptr) +{ + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ +} + +void test_2 (void *ptr, int a, int b) +{ + switch (a) + { + default: + break; + case 1: + break; + case 3: + calls_free_2 (ptr); + break; + } + + switch (b) + { + default: + calls_free_2 (ptr); + break; + case 1: + break; + case 42: + break; + } +} + +/* { dg-begin-multiline-output "" } + NN | free (ptr); + | ^~~~~~~~~~ + 'test_2': events 1-4 + | + | NN | void test_2 (void *ptr, int a, int b) + | | ^~~~~~ + | | | + | | (1) entry to 'test_2' + | NN | { + | NN | switch (a) + | | ~~~~~~ + | | | + | | (2) following 'case 3:' branch... + |...... + | NN | case 3: + | | ~~~~ + | | | + | | (3) ...to here + | NN | calls_free_2 (ptr); + | | ~~~~~~~~~~~~~~~~~~ + | | | + | | (4) calling 'calls_free_2' from 'test_2' + | + +--> 'calls_free_2': events 5-6 + | + | NN | void calls_free_2 (void *ptr) + | | ^~~~~~~~~~~~ + | | | + | | (5) entry to 'calls_free_2' + | NN | { + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (6) first 'free' here + | + <------+ + | + 'test_2': events 7-10 + | + | NN | calls_free_2 (ptr); + | | ^~~~~~~~~~~~~~~~~~ + | | | + | | (7) returning to 'test_2' from 'calls_free_2' + |...... + | NN | switch (b) + | | ~~~~~~ + | | | + | | (8) following 'default:' branch... + | NN | { + | NN | default: + | | ~~~~~~~ + | | | + | | (9) ...to here + | NN | calls_free_2 (ptr); + | | ~~~~~~~~~~~~~~~~~~ + | | | + | | (10) passing freed pointer 'ptr' in call to 'calls_free_2' from 'test_2' + | + +--> 'calls_free_2': events 11-12 + | + | NN | void calls_free_2 (void *ptr) + | | ^~~~~~~~~~~~ + | | | + | | (11) entry to 'calls_free_2' + | NN | { + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (12) second 'free' here; first 'free' was at (6) + | + { dg-end-multiline-output "" } */ + +// TODO: range cases diff --git a/gcc/testsuite/gcc.dg/analyzer/analyzer.exp b/gcc/testsuite/gcc.dg/analyzer/analyzer.exp new file mode 100644 index 0000000..a830cf7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/analyzer.exp @@ -0,0 +1,49 @@ +# Copyright (C) 2019 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# . + +# GCC testsuite that uses the `dg.exp' driver. + +# Load support procs. +load_lib gcc-dg.exp + +# If the analyzer has not been enabled, bail. +if { ![check_effective_target_analyzer] } { + return +} + +global DEFAULT_CFLAGS +if [info exists DEFAULT_CFLAGS] then { + set save_default_cflags $DEFAULT_CFLAGS +} + +# If a testcase doesn't have special options, use these. +set DEFAULT_CFLAGS "--analyzer -fdiagnostics-path-format=separate-events -Wanalyzer-too-complex -fanalyzer-call-summaries" + +# Initialize `dg'. +dg-init + +# Main loop. +dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cS\]]] \ + "" $DEFAULT_CFLAGS + +# All done. +dg-finish + +if [info exists save_default_cflags] { + set DEFAULT_CFLAGS $save_default_cflags +} else { + unset DEFAULT_CFLAGS +} diff --git a/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c b/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c new file mode 100644 index 0000000..b3673fb --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/attribute-nonnull.c @@ -0,0 +1,57 @@ +#include + +extern void foo(void *ptrA, void *ptrB, void *ptrC) /* { dg-message "argument 1 of 'foo' must be non-null" } */ + __attribute__((nonnull (1, 3))); + +extern void bar(void *ptrA, void *ptrB, void *ptrC) /* { dg-message "argument 1 of 'bar' must be non-null" } */ + __attribute__((nonnull)); + +// TODO: complain about NULL and possible NULL args +// FIXME: ought to complain about NULL args + +void test_1 (void *p, void *q, void *r) +{ + foo(p, q, r); + foo(NULL, q, r); + foo(p, NULL, r); + foo(p, q, NULL); +} + +void test_1a (void *q, void *r) +{ + void *p = NULL; + foo(p, q, r); /* { dg-warning "use of NULL 'p' where non-null expected" } */ + /* { dg-message "argument 1 \\('p'\\) NULL where non-null expected" "" { target *-*-* } .-1 } */ +} + +void test_2 (void *p, void *q, void *r) +{ + bar(p, q, r); + bar(NULL, q, r); + bar(p, NULL, r); + bar(p, q, NULL); +} + +void test_3 (void *q, void *r) +{ + void *p = malloc(1024); /* { dg-message "\\(1\\) this call could return NULL" } */ + + foo(p, q, r); /* { dg-warning "use of possibly-NULL 'p' where non-null expected" } */ + /* { dg-message "argument 1 \\('p'\\) from \\(1\\) could be NULL where non-null expected" "" { target *-*-* } .-1 } */ + + foo(p, q, r); + + free(p); +} + +void test_4 (void *q, void *r) +{ + void *p = malloc(1024); /* { dg-message "\\(1\\) this call could return NULL" } */ + + bar(p, q, r); /* { dg-warning "use of possibly-NULL 'p' where non-null expected" } */ + /* { dg-message "argument 1 \\('p'\\) from \\(1\\) could be NULL where non-null expected" "" { target *-*-* } .-1 } */ + + bar(p, q, r); + + free(p); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/call-summaries-1.c b/gcc/testsuite/gcc.dg/analyzer/call-summaries-1.c new file mode 100644 index 0000000..a64b230 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/call-summaries-1.c @@ -0,0 +1,14 @@ +/* { dg-additional-options "-fanalyzer-call-summaries" } */ + +#include + +void calls_free (void *p) +{ + free (p); /* { dg-warning "double-'free' of 'p'" } */ +} + +void test (void *q) +{ + calls_free (q); + calls_free (q); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/conditionals-2.c b/gcc/testsuite/gcc.dg/analyzer/conditionals-2.c new file mode 100644 index 0000000..f3f1ac7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/conditionals-2.c @@ -0,0 +1,44 @@ +// TODO: run this test case at every optimization level +/* { dg-additional-options "-O2" } */ + +#include + +#define Z_NULL 0 + +static void __attribute__((noinline)) +test_1_callee (void *p, void *q) +{ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + __analyzer_eval (p == Z_NULL); /* { dg-warning "FALSE" } */ + __analyzer_eval (p != Z_NULL); /* { dg-warning "TRUE" } */ + + __analyzer_eval (q == Z_NULL); /* { dg-warning "FALSE" } */ + __analyzer_eval (q != Z_NULL); /* { dg-warning "TRUE" } */ +} + +void test_1 (void *p, void *q) +{ + if (p == Z_NULL || q == Z_NULL) + return; + + test_1_callee (p, q); +} + +static void __attribute__((noinline)) +test_2_callee (void *p, void *q) +{ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + __analyzer_eval (p == Z_NULL); /* { dg-warning "FALSE" } */ + __analyzer_eval (p != Z_NULL); /* { dg-warning "TRUE" } */ + + __analyzer_eval (q == Z_NULL); /* { dg-warning "FALSE" } */ + __analyzer_eval (q != Z_NULL); /* { dg-warning "TRUE" } */ +} + +void test_2 (void *p, void *q) +{ + if (p != Z_NULL && q != Z_NULL) + test_2_callee (p, q); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/conditionals-3.c b/gcc/testsuite/gcc.dg/analyzer/conditionals-3.c new file mode 100644 index 0000000..d19a8b7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/conditionals-3.c @@ -0,0 +1,45 @@ +/* { dg-additional-options "-fno-analyzer-state-merge" } */ + +static void only_called_when_flag_a_true (int i) +{ + __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */ +} + +static void only_called_when_flag_b_true (int i) +{ + __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */ +} + +int test_1 (int flag_a, int flag_b) +{ + int i = 17; + + __analyzer_eval (flag_a); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */ + + if (flag_a) + { + __analyzer_eval (flag_a); /* { dg-warning "TRUE" } */ + __analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */ + i = 42; + } + + __analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */ + + if (flag_a) + { + __analyzer_eval (flag_a); /* { dg-warning "TRUE" } */ + __analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == 17); /* { dg-warning "FALSE" } */ + only_called_when_flag_a_true (i); + } + else + { + __analyzer_eval (flag_a); /* { dg-warning "FALSE" } */ + __analyzer_eval (flag_b); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i == 42); /* { dg-warning "FALSE" } */ + __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */ + only_called_when_flag_b_true (i); + } +} diff --git a/gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c b/gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c new file mode 100644 index 0000000..79ffe74 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/conditionals-notrans.c @@ -0,0 +1,158 @@ +/* { dg-additional-options "-fno-analyzer-transitivity" } */ + +void test (int i, int j) +{ + if (i > 4) + { + __analyzer_eval (i > 4); /* { dg-warning "TRUE" } */ + __analyzer_eval (i <= 4); /* { dg-warning "FALSE" } */ + __analyzer_eval (i > 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ + + __analyzer_eval (i > 5); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i != 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ + + __analyzer_eval (i == 3); /* { dg-warning "FALSE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ + + __analyzer_eval (i != 4); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == 4); /* { dg-warning "FALSE" } */ + __analyzer_eval (i == 5); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i != 5); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i < 5); /* { dg-warning "FALSE" } */ + __analyzer_eval (i <= 5); /* { dg-warning "UNKNOWN" } */ + + /* Tests of transitivity. */ + if (j < i) + { + __analyzer_eval (j < i); /* { dg-warning "TRUE" } */ + __analyzer_eval (j <= 4); /* { dg-warning "UNKNOWN" } */ + } + else + { + __analyzer_eval (j >= i); /* { dg-warning "TRUE" } */ + __analyzer_eval (j > 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ + } + } + else + { + __analyzer_eval (i > 4); /* { dg-warning "FALSE" } */ + __analyzer_eval (i <= 4); /* { dg-warning "TRUE" } */ + __analyzer_eval (i > 3); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i > 5); /* { dg-warning "FALSE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ + __analyzer_eval (i != 3); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i == 3); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i != 4); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i == 4); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i == 5); /* { dg-warning "FALSE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ + __analyzer_eval (i != 5); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ + __analyzer_eval (i < 5); /* { dg-warning "TRUE" } */ + __analyzer_eval (i <= 5); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ + } +} + +void test_2 (int i, int j, int k) +{ + if (i >= j) + { + __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */ + if (j >= k) + { + __analyzer_eval (i >= k); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ + __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */ + if (k >= i) + __analyzer_eval (i == k); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ + } + } +} + +void test_3 (int flag, unsigned int i) +{ + if (!flag) { + return; + } + + __analyzer_eval (flag); /* { dg-warning "TRUE" } */ + + if (i>0) { + __analyzer_eval (i > 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (flag); /* { dg-warning "TRUE" } */ + } else { + __analyzer_eval (i <= 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (flag); /* { dg-warning "TRUE" } */ + } + + __analyzer_eval (flag); /* { dg-warning "TRUE" } */ +} + +void test_range_int_gt_lt (int i) +{ + if (i > 3) + if (i < 5) + __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ +} + +void test_range_float_gt_lt (float f) +{ + if (f > 3) + if (f < 5) + __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */ +} + +void test_range_int_ge_lt (int i) +{ + if (i >= 4) + if (i < 5) + __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ +} + +void test_range_float_ge_lt (float f) +{ + if (f >= 4) + if (f < 5) + __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */ +} + +void test_range_int_gt_le (int i) +{ + if (i > 3) + if (i <= 4) + __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ +} + +void test_range_float_gt_le (float f) +{ + if (f > 3) + if (f <= 4) + __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */ +} + +void test_range_int_ge_le (int i) +{ + if (i >= 4) + if (i <= 4) + __analyzer_eval (i == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ +} + +void test_range_float_ge_le (float f) +{ + if (f >= 4) + if (f <= 4) + __analyzer_eval (f == 4); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/conditionals-trans.c b/gcc/testsuite/gcc.dg/analyzer/conditionals-trans.c new file mode 100644 index 0000000..3f7eb5b --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/conditionals-trans.c @@ -0,0 +1,143 @@ +/* { dg-additional-options "-fanalyzer-transitivity" } */ + +void test (int i, int j) +{ + if (i > 4) + { + __analyzer_eval (i > 4); /* { dg-warning "TRUE" } */ + __analyzer_eval (i <= 4); /* { dg-warning "FALSE" } */ + __analyzer_eval (i > 3); /* { dg-warning "TRUE" } */ + + __analyzer_eval (i > 5); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i != 3); /* { dg-warning "TRUE" } */ + + __analyzer_eval (i == 3); /* { dg-warning "FALSE" } */ + + __analyzer_eval (i != 4); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == 4); /* { dg-warning "FALSE" } */ + __analyzer_eval (i == 5); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i != 5); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i < 5); /* { dg-warning "FALSE" } */ + __analyzer_eval (i <= 5); /* { dg-warning "UNKNOWN" } */ + + /* Tests of transitivity. */ + if (j < i) + { + __analyzer_eval (j < i); /* { dg-warning "TRUE" } */ + __analyzer_eval (j <= 4); /* { dg-warning "UNKNOWN" } */ + } + else + { + __analyzer_eval (j >= i); /* { dg-warning "TRUE" } */ + __analyzer_eval (j > 4); /* { dg-warning "TRUE" } */ + } + } + else + { + __analyzer_eval (i > 4); /* { dg-warning "FALSE" } */ + __analyzer_eval (i <= 4); /* { dg-warning "TRUE" } */ + __analyzer_eval (i > 3); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i > 5); /* { dg-warning "FALSE" } */ + __analyzer_eval (i != 3); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i == 3); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i != 4); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i == 4); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i == 5); /* { dg-warning "FALSE" } */ + __analyzer_eval (i != 5); /* { dg-warning "TRUE" } */ + __analyzer_eval (i < 5); /* { dg-warning "TRUE" } */ + __analyzer_eval (i <= 5); /* { dg-warning "TRUE" } */ + } +} + +void test_2 (int i, int j, int k) +{ + if (i >= j) + { + __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */ + if (j >= k) + { + __analyzer_eval (i >= k); /* { dg-warning "TRUE" } */ + __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */ + if (k >= i) + __analyzer_eval (i == k); /* { dg-warning "TRUE" } */ + } + } +} + +void test_3 (int flag, unsigned int i) +{ + if (!flag) { + return; + } + + __analyzer_eval (flag); /* { dg-warning "TRUE" } */ + + if (i>0) { + __analyzer_eval (i > 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (flag); /* { dg-warning "TRUE" } */ + } else { + __analyzer_eval (i <= 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (flag); /* { dg-warning "TRUE" } */ + } + + __analyzer_eval (flag); /* { dg-warning "TRUE" } */ +} + +void test_range_int_gt_lt (int i) +{ + if (i > 3) + if (i < 5) + __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */ +} + +void test_range_float_gt_lt (float f) +{ + if (f > 3) + if (f < 5) + __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */ +} + +void test_range_int_ge_lt (int i) +{ + if (i >= 4) + if (i < 5) + __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */ +} + +void test_range_float_ge_lt (float f) +{ + if (f >= 4) + if (f < 5) + __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */ +} + +void test_range_int_gt_le (int i) +{ + if (i > 3) + if (i <= 4) + __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */ +} + +void test_range_float_gt_le (float f) +{ + if (f > 3) + if (f <= 4) + __analyzer_eval (f == 4); /* { dg-warning "UNKNOWN" } */ +} + +void test_range_int_ge_le (int i) +{ + if (i >= 4) + if (i <= 4) + __analyzer_eval (i == 4); /* { dg-warning "TRUE" } */ +} + +void test_range_float_ge_le (float f) +{ + if (f >= 4) + if (f <= 4) + __analyzer_eval (f == 4); /* { dg-warning "TRUE" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c new file mode 100644 index 0000000..c195b74 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-1.c @@ -0,0 +1,1078 @@ +#include +#include + +struct foo +{ + int i; +}; + +/* Fields of a local. */ + +void test_1 (void) +{ + struct foo f; + f.i = 1; + __analyzer_eval (f.i == 1); /* { dg-warning "TRUE" } */ +} + +/* Fields of a param. */ + +void test_2 (struct foo f) +{ + __analyzer_eval (f.i == 2); /* { dg-warning "UNKNOWN" } */ + f.i = 2; + __analyzer_eval (f.i == 2); /* { dg-warning "TRUE" } */ +} + +/* Fields of a param ptr. */ + +void test_3 (struct foo *f) +{ + __analyzer_eval (f->i == 3); /* { dg-warning "UNKNOWN" } */ + f->i = 3; + __analyzer_eval (f->i == 3); /* { dg-warning "TRUE" } */ +} + +/* Fields of a global ptr. */ +struct foo *global_foo_ptr; + +void test_3a (void) +{ + struct foo *tmp = global_foo_ptr; + __analyzer_eval (global_foo_ptr->i == 3); /* { dg-warning "UNKNOWN" } */ + global_foo_ptr->i = 3; + __analyzer_eval (global_foo_ptr->i == 3); /* { dg-warning "TRUE" } */ +} + +/* Pointer to a local. */ + +void test_4 (void) +{ + int i; + int *p = &i; + i = 1; + *p = 2; + __analyzer_eval (i == 2); /* { dg-warning "TRUE" } */ +} + +/* Local array. */ + +void test_5 (void) +{ + int a[10]; + a[3] = 5; /* ARRAY_REF. */ + __analyzer_eval (a[3] == 5); /* { dg-warning "TRUE" } */ +} + +/* Local array, but using an unknown index. */ + +void test_5a (int idx) +{ + int a[10]; + a[idx] = 5; /* ARRAY_REF. */ + __analyzer_eval (a[idx] == 5); /* { dg-warning "TRUE" } */ +} + +/* Array passed in as a param. */ + +void test_6 (int a[10]) +{ + /* POINTER_PLUS_EXPR then a MEM_REF. */ + __analyzer_eval (a[3] == 42); /* { dg-warning "UNKNOWN" } */ + a[3] = 42; + __analyzer_eval (a[3] == 42); /* { dg-warning "TRUE" } */ +} + +/* Array passed in as a param ptr. */ + +void test_7 (int *a) +{ + __analyzer_eval (a[3] == 42); /* { dg-warning "UNKNOWN" } */ + a[3] = 42; + __analyzer_eval (a[3] == 42); /* { dg-warning "TRUE" } */ +} + +/* Globals. */ + +int glob_a; + +void test_10 (void) +{ + __analyzer_eval (glob_a == 42); /* { dg-warning "UNKNOWN" } */ + glob_a = 42; + __analyzer_eval (glob_a == 42); /* { dg-warning "TRUE" } */ +} + +/* malloc. */ + +void test_11 (void) +{ + void *p = malloc (256); + void *q = malloc (256); + + /* malloc results should be unique. */ + __analyzer_eval (p == q); /* { dg-warning "FALSE" } */ + __analyzer_eval (p != q); /* { dg-warning "TRUE" } */ + __analyzer_eval (p <= q); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (p >= q); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (p < q); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (p > q); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (p == p); /* { dg-warning "TRUE" } */ + __analyzer_eval (p != p); /* { dg-warning "FALSE" } */ + __analyzer_eval (p <= p); /* { dg-warning "TRUE" } */ + __analyzer_eval (p >= p); /* { dg-warning "TRUE" } */ + __analyzer_eval (p < p); /* { dg-warning "FALSE" } */ + __analyzer_eval (p > p); /* { dg-warning "FALSE" } */ + + free (p); + free (q); + // TODO: mark freed memory as freed + //__analyzer_break (); +} + +/* alloca. */ + +void test_12 (void) +{ + void *p = alloca (256); + void *q = alloca (256); + + /* alloca results should be unique. */ + __analyzer_eval (p == q); /* { dg-warning "FALSE" } */ + + // FIXME: complain about uses of poisoned values +} + +/* Use of uninit value. */ +int test_12a (void) +{ + int i; + return i; // FIXME: do we see the return stmt? +} + +void test_12b (void *p, void *q) +{ + __analyzer_eval (p == q); /* { dg-warning "UNKNOWN" } */ +} + +int test_12c (void) +{ + int i; + int j; + + j = i; // FIXME: should complain about this + + return j; +} + +struct coord +{ + int x; + int y; +}; + +int test_12d (struct coord c) +{ + struct coord d; + d = c; + __analyzer_eval (d.x == c.x); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + /* TODO(xfail): c and d share the same unknown value of type "coord", but + attempts to access the fields lead to different unknown values. */ + + __analyzer_eval (d.y == c.y); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail): likewise + + __analyzer_eval (d.x == d.y); /* { dg-warning "UNKNOWN" } */ + /* d and c share an unknown value of type "struct coord". + But d.x and d.y should be different unknown values (although they inherit + from d's region). */ +} + +/* Nested structs. */ + +struct outer +{ + struct middle { + struct inner { + float f; + } in; + } mid; +}; + +void test_13 (struct outer *o) +{ + __analyzer_eval (o->mid.in.f == 0.f); /* { dg-warning "UNKNOWN" } */ + o->mid.in.f = 0.f; + __analyzer_eval (o->mid.in.f == 0.f); /* { dg-warning "TRUE" } */ +} + +void test_14 (struct outer o) +{ + __analyzer_eval (o.mid.in.f == 0.f); /* { dg-warning "UNKNOWN" } */ + o.mid.in.f = 0.f; + __analyzer_eval (o.mid.in.f == 0.f); /* { dg-warning "TRUE" } */ +} + +void test_15 (const char *str) +{ + char ch = str[0]; + __analyzer_eval (ch == 'a'); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (ch == str[0]); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail) + + ch = 'a'; + __analyzer_eval (ch == 'a'); /* { dg-warning "TRUE" } */ + __analyzer_eval (str[0] == 'a'); /* { dg-warning "UNKNOWN" } */ +} + +void test_16 (void) +{ + const char *msg = "hello world"; + + __analyzer_eval (msg != NULL); /* { dg-warning "TRUE" } */ + + __analyzer_eval (msg[0] == 'h'); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail) + + __analyzer_eval (msg[1] == 'e'); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail) + + __analyzer_eval (strlen (msg) == 11); /* { dg-warning "TRUE" } */ +} + +static const char *__attribute__((noinline)) +get_hello_world (void) +{ + return "hello world"; +} + +void test_16_alt (void) +{ + const char *msg = get_hello_world (); + + __analyzer_eval (msg != NULL); /* { dg-warning "TRUE" } */ + + __analyzer_eval (msg[0] == 'h'); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail) + + __analyzer_eval (msg[1] == 'e'); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail) + + __analyzer_eval (strlen (msg) == 11); /* { dg-warning "TRUE" } */ +} + +void test_16a (const char *msg) +{ + __analyzer_eval (strlen (msg) == 11); /* { dg-warning "UNKNOWN" } */ +} + +void test_16b (const char *msg) +{ + __analyzer_eval (strlen (msg) == strlen (msg)); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail) +} + +extern int unknown_result (void); + +void test_16c (int i) +{ + int j; + + j = i; + __analyzer_eval (j == i); /* { dg-warning "TRUE" } */ + + j = unknown_result (); + __analyzer_eval (j == i); /* { dg-warning "UNKNOWN" } */ +} + +void test_16c_a (void) +{ + int i, j; + + i = unknown_result (); + j = unknown_result (); + __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */ +} + +int global_int_16d; + +void test_16d (int i) +{ + global_int_16d = i; + __analyzer_eval (global_int_16d == i); /* { dg-warning "TRUE" } */ + + global_int_16d = unknown_result (); + __analyzer_eval (global_int_16d == i); /* { dg-warning "UNKNOWN" } */ +} + +extern void might_write_to (int *); + +void test_16e (int i) +{ + int j; + + j = i; + __analyzer_eval (j == i); /* { dg-warning "TRUE" } */ + + might_write_to (&j); + __analyzer_eval (j == i); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */ + /* { dg-warning "TRUE" "" { target *-*-* } .-1 } */ + // TODO(xfail) +} + +/* TODO: and more complicated graph-like examples, where anything that's + reachable from the pointer might be modified. */ + +void test_17 (int i) +{ + int j = 42; + __analyzer_eval (j == 42); /* { dg-warning "TRUE" } */ + + __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */ + i = j; + __analyzer_eval (i == j); /* { dg-warning "TRUE" } */ +} + +void test_18 (int i) +{ + int j; + __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */ + + j = i; + + __analyzer_eval (i == j); /* { dg-warning "TRUE" } */ + __analyzer_eval (i >= j); /* { dg-warning "TRUE" } */ + __analyzer_eval (i <= j); /* { dg-warning "TRUE" } */ + + __analyzer_eval (i != j); /* { dg-warning "FALSE" } */ + __analyzer_eval (i > j); /* { dg-warning "FALSE" } */ + __analyzer_eval (i < j); /* { dg-warning "FALSE" } */ +} + +void test_19 (void) +{ + int i, j; + /* Compare two uninitialized locals. */ + __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */ +} + +void test_20 (int i, int j) +{ + __analyzer_eval (i + 1); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i + j); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i - 1); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i - j); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i * 2); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i * j); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i / 2); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i / j); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i % 2); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i % j); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i & 1); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i & j); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i | 1); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i | j); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i ^ 1); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i ^ j); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i >> 1); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i >> j); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i << 1); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i << j); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i && 0); /* { dg-warning "FALSE" } */ + __analyzer_eval (i && 1); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i && j); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i || 0); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (i || 1); /* { dg-warning "TRUE" } */ + __analyzer_eval (i || j); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval (~i); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (-i); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (+i); /* { dg-warning "UNKNOWN" } */ + + /* Anything added above should be added to the next function also. */ +} + +/* As above, but where the values are known due to the region model, + but not known to GCC's regular optimizations (folding and SSA). */ + +void test_21 (void) +{ + int i, j, zero; + int *pi = &i; + int *pj = &j; + int *pzero = &zero; + *pi = 5; + *pj = 3; + *pzero = 0; + + __analyzer_eval (i + j == 8); /* { dg-warning "TRUE" } */ + __analyzer_eval (i - j == 2); /* { dg-warning "TRUE" } */ + __analyzer_eval (i * j == 15); /* { dg-warning "TRUE" } */ + __analyzer_eval (i / j == 1); /* { dg-warning "TRUE" } */ + __analyzer_eval (i % j == 2); /* { dg-warning "TRUE" } */ + + /* Division by zero. */ + // TODO: should we warn for this? + __analyzer_eval (i / zero); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i % zero); /* { dg-warning "UNKNOWN" } */ + + __analyzer_eval ((i & 1) == (5 & 1)); /* { dg-warning "TRUE" } */ + __analyzer_eval ((i & j) == (5 & 3)); /* { dg-warning "TRUE" } */ + __analyzer_eval ((i | 1) == (5 | 1)); /* { dg-warning "TRUE" } */ + __analyzer_eval ((i | j) == (5 | 3)); /* { dg-warning "TRUE" } */ + __analyzer_eval ((i ^ 1) == (5 ^ 1)); /* { dg-warning "TRUE" } */ + __analyzer_eval ((i ^ j) == (5 ^ 3)); /* { dg-warning "TRUE" } */ + __analyzer_eval ((i >> 1) == (5 >> 1)); /* { dg-warning "TRUE" } */ + __analyzer_eval ((i >> j) == (5 >> 3)); /* { dg-warning "TRUE" } */ + __analyzer_eval ((i << 1) == (5 << 1)); /* { dg-warning "TRUE" } */ + __analyzer_eval ((i << j) == (5 << 3)); /* { dg-warning "TRUE" } */ + __analyzer_eval (i && 0); /* { dg-warning "FALSE" } */ + __analyzer_eval (i && 1); /* { dg-warning "TRUE" } */ + __analyzer_eval (i && j); /* { dg-warning "TRUE" } */ + + __analyzer_eval (i || 0); /* { dg-warning "TRUE" } */ + __analyzer_eval (i || 1); /* { dg-warning "TRUE" } */ + __analyzer_eval (i || j); /* { dg-warning "TRUE" } */ + + __analyzer_eval (~i == ~5); /* { dg-warning "TRUE" } */ + __analyzer_eval (-i == -5); /* { dg-warning "TRUE" } */ + __analyzer_eval (+i == +5); /* { dg-warning "TRUE" } */ +} + +void test_22 (int i, int j) +{ + __analyzer_eval (i + j == i + j); /* { dg-warning "TRUE" } */ + // FIXME: this is getting folded; can we build a non-folded equivalent? +} + +void test_23 (struct foo *f, struct foo *g) +{ + int i, j, k; + i = f->i + g->i; + j = f->i + g->i; + k = f->i * g->i; + __analyzer_eval (i == j); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + /* TODO(xfail): we'd need to record that the two unknown values are both + the sum of the two unknown input values (and thus are the same); not + yet sure if we want arbitrary expression trees in the representation + (analysis termination concerns). */ + + __analyzer_eval (i == k); /* { dg-warning "UNKNOWN" } */ +} + +void test_24 (struct foo *f) +{ + struct foo g; + g.i = 42; + __analyzer_eval (g.i == 42); /* { dg-warning "TRUE" } */ + + /* Overwriting a whole struct should invalidate our knowledge + about fields within it. */ + g = *f; + __analyzer_eval (g.i == 42); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */ + /* { dg-warning "TRUE" "" { target *-*-* } .-1 } */ + // TODO(xfail) +} + +void test_25 (struct foo *f) +{ + struct foo g; + g.i = 42; + f->i = 43; + __analyzer_eval (f->i == 43); /* { dg-warning "TRUE" } */ + __analyzer_eval (g.i == 42); /* { dg-warning "TRUE" } */ + + /* Overwriting a whole struct where we know things about the + source value should update our knowledge about fields within + the dest value. */ + g = *f; + __analyzer_eval (g.i == 43); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */ + // TODO(xfail) +} + +void test_26 (struct coord *p, struct coord *q) +{ + p->x = 42; + q->y = 17; + __analyzer_eval (p->x == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (p->y); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (q->x); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (q->y == 17); /* { dg-warning "TRUE" } */ + + /* Overwriting a whole struct where we know some things about the + source value should update our knowledge about fields within + the dest value. */ + *p = *q; + __analyzer_eval (p->x); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */ + /* { dg-warning "TRUE" "" { target *-*-* } .-1 } */ + // TODO(xfail): should have been overwritten + __analyzer_eval (p->y == 17); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail): should have been overwritten with q->y + + __analyzer_eval (q->x); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (q->y == 17); /* { dg-warning "TRUE" } */ +} + +void test_27 (struct coord *p) +{ + memset (p, 0, sizeof (struct coord)); + __analyzer_eval (p->x == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail): + __analyzer_eval (p->y == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail): +} + +void test_28 (struct coord *p) +{ + memset (p, 0, sizeof (struct coord) * 10); + __analyzer_eval (p[0].x == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail): + __analyzer_eval (p[0].y == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail): + + __analyzer_eval (p[9].x == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail): + __analyzer_eval (p[9].y == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail): + + __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */ +} + +void test_29 (struct coord *p) +{ + struct coord *q; + + p[0].x = 100024; + p[0].y = 100025; + + p[7].x = 107024; + p[7].y = 107025; + + p[9].x = 109024; + p[9].y = 109025; + + __analyzer_eval (p[0].x == 100024); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[0].y == 100025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (p[7].x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[7].y == 107025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */ + + q = &p[7]; + + __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (q[2].x == 109024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q[2].y == 109025); /* { dg-warning "TRUE" } */ + + q += 2; + + __analyzer_eval (q->x == 109024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q->y == 109025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (q[-2].x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */ + + q -= 2; + + __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */ +} + +void test_29a (struct coord p[]) +{ + struct coord *q; + + p[0].x = 100024; + p[0].y = 100025; + + p[7].x = 107024; + p[7].y = 107025; + + p[9].x = 109024; + p[9].y = 109025; + + __analyzer_eval (p[0].x == 100024); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[0].y == 100025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (p[7].x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[7].y == 107025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */ + + q = &p[7]; + + __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (q[2].x == 109024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q[2].y == 109025); /* { dg-warning "TRUE" } */ + + q += 2; + + __analyzer_eval (q->x == 109024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q->y == 109025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (q[-2].x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */ + + q -= 2; + + __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */ +} + +void test_29b (void) +{ + struct coord p[11]; + struct coord *q; + + p[0].x = 100024; + p[0].y = 100025; + + p[7].x = 107024; + p[7].y = 107025; + + p[9].x = 109024; + p[9].y = 109025; + + __analyzer_eval (p[0].x == 100024); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[0].y == 100025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (p[7].x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[7].y == 107025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */ + + q = &p[7]; + + __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (q[2].x == 109024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q[2].y == 109025); /* { dg-warning "TRUE" } */ + + q += 2; + + __analyzer_eval (q->x == 109024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q->y == 109025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (q[-2].x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */ + + q -= 2; + + __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */ +} + +void test_29c (int len) +{ + struct coord p[len]; + struct coord *q; + + p[0].x = 100024; + p[0].y = 100025; + + p[7].x = 107024; + p[7].y = 107025; + + p[9].x = 109024; + p[9].y = 109025; + + __analyzer_eval (p[0].x == 100024); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[0].y == 100025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (p[7].x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[7].y == 107025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (p[9].x == 109024); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[9].y == 109025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (p[10].x == 0); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (p[10].y == 0); /* { dg-warning "UNKNOWN" } */ + + q = &p[7]; + + __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (q[2].x == 109024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q[2].y == 109025); /* { dg-warning "TRUE" } */ + + q += 2; + + __analyzer_eval (q->x == 109024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q->y == 109025); /* { dg-warning "TRUE" } */ + + __analyzer_eval (q[-2].x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q[-2].y == 107025); /* { dg-warning "TRUE" } */ + + q -= 2; + + __analyzer_eval (q->x == 107024); /* { dg-warning "TRUE" } */ + __analyzer_eval (q->y == 107025); /* { dg-warning "TRUE" } */ +} + +void test_30 (void *ptr) +{ + struct coord *p = (struct coord *)ptr; + struct coord *q = (struct coord *)ptr; + + p->x = 42; + + __analyzer_eval (p->x == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (q->x == 42); /* { dg-warning "TRUE" } */ +} + +void test_31 (unsigned i) +{ + int j, k; + + j = i < 100 ? i : 100; /* MIN_EXPR. */ + k = i < 100 ? 100 : i; /* MAX_EXPR. */ +} + +enum color +{ + RED, + GREEN, + BLUE +}; + +void test_32 (enum color c) +{ + __analyzer_eval (c == GREEN); /* { dg-warning "UNKNOWN" } */ + + c = RED; + + __analyzer_eval (c == RED); /* { dg-warning "TRUE" } */ + __analyzer_eval (c == GREEN); /* { dg-warning "FALSE" } */ +} + +void test_33 (void) +{ + static int s; + + __analyzer_eval (s == 42); /* { dg-warning "UNKNOWN" } */ + + s = 42; + + __analyzer_eval (s == 42); /* { dg-warning "TRUE" } */ +} + +static int __attribute__((noinline)) +only_called_by_test_34 (int parm) +{ + __analyzer_eval (parm == 42); /* { dg-warning "TRUE" } */ + + return parm * 2; +} + +void test_34 (void) +{ + int result = only_called_by_test_34 (42); + __analyzer_eval (result == 84); /* { dg-warning "TRUE" } */ +} + +void test_35 (int i, int j) +{ + __analyzer_eval (&i == &i); /* { dg-warning "TRUE" } */ + __analyzer_eval (&i != &j); /* { dg-warning "TRUE" } */ +} + +static void __attribute__((noinline)) +write_through_ptr (int *dst, int val) +{ + *dst = val; +} + +void test_36 (int i) +{ + __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */ + + write_through_ptr (&i, 42); + + __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */ +} + +/* Read through uninitialized pointer. */ + +int test_37 (void) +{ + int *ptr; + return *ptr; /* { dg-warning "use of uninitialized value 'ptr'" } */ +} + +/* Write through uninitialized pointer. */ + +void test_37a (int i) +{ + int *ptr; + *ptr = i; /* { dg-warning "use of uninitialized value 'ptr'" } */ +} + +// TODO: the various other ptr deref poisonings + +/* Read through NULL pointer. */ + +int test_38 (void) +{ + int *ptr = NULL; + return *ptr; /* { dg-warning "dereference of NULL 'ptr'" } */ +} + +/* Write through NULL pointer. */ + +int test_38a (int i) +{ + int *ptr = NULL; + *ptr = i; /* { dg-warning "dereference of NULL 'ptr'" } */ +} + +/* Read through non-NULL constant pointer. */ + +int test_39 (void) +{ + int *ptr = (int *)0x1000; + return *ptr; +} + +int test_40 (int flag) +{ + int i; + if (flag) + i = 43; + else + i = 17; + + /* With state-merging, we lose the relationship between 'flag' and 'i'. */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + if (flag) + __analyzer_eval (i == 43); /* { dg-warning "UNKNOWN" } */ + else + __analyzer_eval (i == 17); /* { dg-warning "UNKNOWN" } */ +} + +struct link +{ + struct link *next; + int f; +}; + +/* Traversing a singly-linked list. */ + +void foo (struct link *in) +{ + struct link a; + struct link b; + struct link c; + a.next = &b; + b.next = &c; + in->next = &a; + c.f = 42; + __analyzer_eval (in->next->next->next->f == 42); /* { dg-warning "TRUE" } */ +} + +union u +{ + int i; + int *ptr; +}; + +void test_41 (void) +{ + union u u; + u.i = 42; + __analyzer_eval (u.i == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (u.ptr == NULL); /* { dg-warning "UNKNOWN" } */ + + /* Writes to a union member should invalidate knowledge about other members. */ + u.ptr = NULL; + __analyzer_eval (u.ptr == NULL); /* { dg-warning "TRUE" } */ + __analyzer_eval (u.i == 42); /* { dg-warning "UNKNOWN" } */ +} + +void test_42 (void) +{ + int i; + float f; + i = 42; + f = i; + __analyzer_eval (f == 42.0); /* { dg-warning "TRUE" } */ +} + +void test_43 (void) +{ + int i; + float f; + f = 42.0f; + i = f; + __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */ +} + +struct sbits +{ + int b0 : 1; + int b123 : 3; + int b456 : 3; + int b7 : 1; +}; + +void test_44 (void) +{ + struct sbits bits; + bits.b0 = 1; + __analyzer_eval (bits.b0 == 1); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */ + // TODO(xfail): ^^^^ + + bits.b456 = 5; + __analyzer_eval (bits.b456 == 5); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "FALSE" "" { target *-*-* } .-1 } */ + // TODO(xfail): ^^^^ +}; + +struct ubits +{ + unsigned int b0 : 1; + unsigned int b123 : 3; + unsigned int b456 : 3; + unsigned int b7 : 1; +}; + +/* FIXME: this requires BIT_FIELD_REF to work. */ + +void test_45 (void) +{ + struct ubits bits; + bits.b0 = 1; + __analyzer_eval (bits.b0 == 1); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail): ^^^^ + + bits.b456 = 5; + __analyzer_eval (bits.b456 == 5); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + // TODO(xfail): ^^^^ +}; + +extern const char *char_ptr; + +int test_46 (void) +{ + if (strcmp("literal", char_ptr)) + return 1; + return 0; +} + +char test_47 (void) +{ + static const char* my_version = "1.1.3"; + return my_version[0]; +} + +unsigned test_48 (unsigned char *p, unsigned char *q) +{ + return (unsigned int)(p - q); +} + +typedef struct { + const char *filename; + short lineno; +} loc; + +static loc loc_last; + +void test_49 (void) +{ + loc_last = __extension__(loc) { "", 328 }; + loc_last = __extension__(loc) { "", 333 }; +} + +void test_50 (void *p, void *q) +{ + __analyzer_eval (p == q); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (p == p); /* { dg-warning "TRUE" } */ + __analyzer_eval (q == q); /* { dg-warning "TRUE" } */ + __analyzer_eval (p == (struct coord *)p); /* { dg-warning "TRUE" } */ + __analyzer_eval (p == (const struct coord *)p); /* { dg-warning "TRUE" } */ + + struct coord *cp = (struct coord *)p; + __analyzer_eval (p == cp); /* { dg-warning "TRUE" } */ + + struct coord *cq = (struct coord *)q; + __analyzer_eval (q == cq); /* { dg-warning "TRUE" } */ + + __analyzer_eval (cp == cq); /* { dg-warning "UNKNOWN" } */ +} + +void test_51 (struct coord c) +{ + struct coord d; + memcpy (&d, &c, sizeof (struct coord)); + __analyzer_eval (c.x == d.x); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + __analyzer_eval (c.y == d.y); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ +} + +struct big +{ + int ia[1024]; +}; + +void test_52 (struct big b) +{ + struct big d; + memcpy (&d, &b, sizeof (struct big)); + __analyzer_eval (b.ia[0] == d.ia[0]); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-10.c b/gcc/testsuite/gcc.dg/analyzer/data-model-10.c new file mode 100644 index 0000000..c261edc --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-10.c @@ -0,0 +1,17 @@ +#include + +struct foo +{ + char **m_f; +}; + +struct foo * +test (void) +{ + struct foo *new_table = (struct foo *) malloc(sizeof(struct foo)); + if (!new_table) + return NULL; + new_table->m_f = (char **)malloc(sizeof(char **)); + *new_table->m_f = NULL; /* { dg-warning "dereference of possibly-NULL ''" } */ // FIXME: something better than "unknown" here + return new_table; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-11.c b/gcc/testsuite/gcc.dg/analyzer/data-model-11.c new file mode 100644 index 0000000..2766324 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-11.c @@ -0,0 +1,6 @@ +int test (void) +{ + unsigned char *s = "abc"; + char *t = "xyz"; + return s[1] + t[1]; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-12.c b/gcc/testsuite/gcc.dg/analyzer/data-model-12.c new file mode 100644 index 0000000..653b7ad --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-12.c @@ -0,0 +1,13 @@ +/* Mismatching decl of foo. */ + +int foo (); + +int bar (void) +{ + return foo() + 1; +} + +int foo (int x, int y) +{ + return x * y; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-13.c b/gcc/testsuite/gcc.dg/analyzer/data-model-13.c new file mode 100644 index 0000000..e7cb845 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-13.c @@ -0,0 +1,21 @@ +#include + +union +{ + void *ptr_val; + int int_val; +} global_union; + +void test_1 (void) +{ + global_union.ptr_val = malloc (1024); +} + +void test_2 (void) +{ + global_union.ptr_val = malloc (1024); /* { dg-message "allocated here" } */ + global_union.int_val = 0; +} /* { dg-warning "leak of '' " } */ +/* TODO: something better than "". */ +/* TODO: better location for the leak. */ + diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-14.c b/gcc/testsuite/gcc.dg/analyzer/data-model-14.c new file mode 100644 index 0000000..f9bb540 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-14.c @@ -0,0 +1,24 @@ +/* FIXME: we shouldn't need this. */ +/* { dg-additional-options "-fanalyzer-fine-grained" } */ + +#include + +void *global_ptr; + +void test_1 (int i) +{ + global_ptr = malloc (1024); /* { dg-message "allocated here" } */ + *(int *)&global_ptr = i; /* { dg-warning "leak of ''" } */ + // TODO: something better than "" here ^^^ +} + +void test_2 (int i) +{ + void *p = malloc (1024); /* { dg-message "allocated here" "" { xfail *-*-* } } */ + // TODO(xfail) + global_ptr = p; + *(int *)&p = i; + p = global_ptr; + free (p); + free (global_ptr); /* { dg-warning "double-'free' of 'p'" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-15.c b/gcc/testsuite/gcc.dg/analyzer/data-model-15.c new file mode 100644 index 0000000..12e84a1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-15.c @@ -0,0 +1,34 @@ +#include + +struct coord +{ + double x; + double y; + double z; +}; + +struct tri { + struct coord verts[3]; +}; + +double test_1 (void) +{ + struct tri t; + memset (&t, 0, sizeof (struct tri)); + return t.verts[1].y; +} + +int test_2 (const struct coord *c1, const struct coord *c2, double r_squared) +{ + double dx = c1->x - c2->x; + double dy = c1->y - c2->y; + double dz = c1->z - c2->z; + return (dx * dx) + (dy * dy) + (dz * dz) <= r_squared; +} + +int test_3 (const struct coord *c1, const struct coord *c2, struct coord *out) +{ + out->x = c1->x + c2->x; + out->y = c1->y + c2->y; + out->z = c1->z + c2->z; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-16.c b/gcc/testsuite/gcc.dg/analyzer/data-model-16.c new file mode 100644 index 0000000..3bfdbdd --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-16.c @@ -0,0 +1,50 @@ +/* Labels as values. */ + +extern void foo (void); + +void *x, *y, *z; + +void test (void) +{ + label0: + foo (); + label1: + foo (); + label2: + foo (); + + x = &&label0; + y = &&label1; + z = &&label2; + + __analyzer_eval (x == x); /* { dg-warning "TRUE" } */ + __analyzer_eval (x == y); /* { dg-warning "FALSE" } */ +} + +void test_2 (int i) +{ + static void *array[] = { &&label0, &&label1, &&label2 }; + goto *array[i]; + + label0: + foo (); + label1: + foo (); + label2: + foo (); +} + +void test_3 (int i) +{ + static const int array[] = { &&label0 - &&label0, + &&label1 - &&label0, + &&label2 - &&label0 }; + goto *(&&label0 + array[i]); + + label0: + foo (); + label1: + foo (); + label2: + foo (); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-17.c b/gcc/testsuite/gcc.dg/analyzer/data-model-17.c new file mode 100644 index 0000000..d50b84a --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-17.c @@ -0,0 +1,20 @@ +typedef struct foo {} foo_t; + +typedef void (*func_t)(foo_t *s); + +void cb_1 (foo_t *s); +void cb_2 (foo_t *s); + +typedef struct config_s { + func_t func; +} config; + +static const config table[2] = { + { cb_1 }, + { cb_2 } +}; + +int deflate (foo_t *s, int which) +{ + (*(table[which].func))(s); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-18.c b/gcc/testsuite/gcc.dg/analyzer/data-model-18.c new file mode 100644 index 0000000..88ea41d --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-18.c @@ -0,0 +1,20 @@ +void test (int *p, int i, int j) +{ + p[3] = 42; + __analyzer_eval (p[3] == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (*(p + 3) == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[i] == 42); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (p[j] == 42); /* { dg-warning "UNKNOWN" } */ + + //__analyzer_dump (); + + p[i] = 17; + + //__analyzer_dump (); + + __analyzer_eval (p[3] == 42); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (p[i] == 17); /* { dg-warning "TRUE" } */ + __analyzer_eval (p[j] == 17); /* { dg-warning "UNKNOWN" "" { xfail *-*-* } } */ + /* { dg-bogus "TRUE" "" { xfail *-*-* } .-1 } */ + // FIXME(xfails) ^^^ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-19.c b/gcc/testsuite/gcc.dg/analyzer/data-model-19.c new file mode 100644 index 0000000..6997838 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-19.c @@ -0,0 +1,31 @@ +/* { dg-additional-options "-fgimple" } */ + +typedef long long int i64; + +int __GIMPLE (ssa) +test (i64 * pA, i64 iB) +{ + __complex__ long long int D_37702; + int D_37701; + long long int _1; + long long int _2; + long long int _3; + _Bool _4; + __complex__ long long int _8; + int _10; + + __BB(2): + _1 = __MEM (pA_6(D)); + _8 = .ADD_OVERFLOW (_1, iB_7(D)); + _2 = __real _8; + __MEM (pA_6(D)) = _2; + _3 = __imag _8; + _4 = (_Bool) _3; + _10 = (int) _4; + goto __BB3; + + __BB(3): +L0: + return _10; + +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-2.c b/gcc/testsuite/gcc.dg/analyzer/data-model-2.c new file mode 100644 index 0000000..d5be03b --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-2.c @@ -0,0 +1,13 @@ +/* { dg-additional-options "-O2" } */ +/* TODO:is there a way to automatically run the tests on various + optimizations levels, and with/without debuginfo, rather than + hardcoding options? Adapt from torture .exp, presumably. */ + + +#include +#include + +int test_1 (void) +{ + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-3.c b/gcc/testsuite/gcc.dg/analyzer/data-model-3.c new file mode 100644 index 0000000..3d572eb --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-3.c @@ -0,0 +1,15 @@ +/* { dg-additional-options "-O2" } */ +/* TODO:is there a way to automatically run the tests on various + optimizations levels, and with/without debuginfo, rather than + hardcoding options? Adapt from torture .exp, presumably. */ + +#include +int +main () +{ + FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-4.c b/gcc/testsuite/gcc.dg/analyzer/data-model-4.c new file mode 100644 index 0000000..33f9087 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-4.c @@ -0,0 +1,16 @@ +/* { dg-additional-options "-fexceptions" } */ +/* TODO:is there a way to automatically run the tests on various + optimizations levels, and with/without debuginfo, rather than + hardcoding options? Adapt from torture .exp, presumably. */ + +#include +int +main () +{ + FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} + diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5.c new file mode 100644 index 0000000..5cf3347 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5.c @@ -0,0 +1,100 @@ +/* A toy re-implementation of CPython's object model. */ + +#include +#include +#include + +typedef struct base_obj +{ + struct type_obj *ob_type; + int ob_refcnt; +} base_obj; + +typedef struct type_obj +{ + base_obj tp_base; + void (*tp_dealloc) (base_obj *); +} type_obj; + +typedef struct tuple_obj +{ + base_obj tup_base; + int num_elements; + base_obj elements[]; +} tuple_obj; + +typedef struct list_obj +{ + base_obj list_base; + int num_elements; + base_obj *elements; +} list_obj; + +typedef struct string_obj +{ + base_obj str_base; + size_t str_len; + char str_buf[]; +} string_obj; + +extern void type_del (base_obj *); +extern void tuple_del (base_obj *); +extern void str_del (base_obj *); + +type_obj type_type = { + { &type_type, 1}, + type_del +}; + +type_obj tuple_type = { + { &type_type, 1}, + tuple_del +}; + +type_obj str_type = { + { &str_type, 1}, + str_del +}; + +base_obj *alloc_obj (type_obj *ob_type, size_t sz) +{ + base_obj *obj = (base_obj *)malloc (sz); + if (!obj) + return NULL; + obj->ob_type = ob_type; + obj->ob_refcnt = 1; + return obj; +} + +base_obj *new_string_obj (const char *str) +{ + //__analyzer_dump (); + size_t len = strlen (str); +#if 1 + string_obj *str_obj + = (string_obj *)alloc_obj (&str_type, sizeof (string_obj) + len + 1); +#else + string_obj *str_obj = (string_obj *)malloc (sizeof (string_obj) + len + 1); + if (!str_obj) + return NULL; + str_obj->str_base.ob_type = &str_type; + str_obj->str_base.ob_refcnt = 1; +#endif + str_obj->str_len = len; /* { dg-warning "dereference of NULL 'str_obj'" } */ + memcpy (str_obj->str_buf, str, len); + str_obj->str_buf[len] = '\0'; + return (base_obj *)str_obj; +} + +void unref (base_obj *obj) +{ + if (--obj->ob_refcnt == 0) /* { dg-bogus "dereference of uninitialized pointer 'obj'" } */ + obj->ob_type->tp_dealloc (obj); +} + +void test_1 (const char *str) +{ + base_obj *obj = new_string_obj (str); + //__analyzer_dump(); + unref (obj); +} /* { dg-bogus "leak" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5b.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5b.c new file mode 100644 index 0000000..b0203af --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5b.c @@ -0,0 +1,91 @@ +/* A toy re-implementation of CPython's object model. */ + +#include +#include +#include + +typedef struct base_obj base_obj; +typedef struct type_obj type_obj; +typedef struct string_obj string_obj; + +struct base_obj +{ + struct type_obj *ob_type; + int ob_refcnt; +}; + +struct type_obj +{ + base_obj tp_base; + void (*tp_dealloc) (base_obj *); +}; + +struct string_obj +{ + base_obj str_base; + size_t str_len; + char str_buf[]; +}; + +extern void type_del (base_obj *); +extern void str_del (base_obj *); + +type_obj type_type = { + { &type_type, 1}, + type_del +}; + +type_obj str_type = { + { &str_type, 1}, + str_del +}; + +base_obj *alloc_obj (type_obj *ob_type, size_t sz) +{ + base_obj *obj = (base_obj *)malloc (sz); + if (!obj) + return NULL; + obj->ob_type = ob_type; + obj->ob_refcnt = 1; + return obj; +} + +string_obj *new_string_obj (const char *str) +{ + //__analyzer_dump (); + size_t len = strlen (str); +#if 1 + string_obj *str_obj + = (string_obj *)alloc_obj (&str_type, sizeof (string_obj) + len + 1); +#else + string_obj *str_obj = (string_obj *)malloc (sizeof (string_obj) + len + 1); + if (!str_obj) + return NULL; + str_obj->str_base.ob_type = &str_type; + str_obj->str_base.ob_refcnt = 1; +#endif + str_obj->str_len = len; /* { dg-warning "dereference of NULL 'str_obj'" } */ + memcpy (str_obj->str_buf, str, len); + str_obj->str_buf[len] = '\0'; + return str_obj; +} + +void unref (string_obj *obj) +{ + //__analyzer_dump(); + if (--obj->str_base.ob_refcnt == 0) + { + //__analyzer_dump(); + obj->str_base.ob_type->tp_dealloc ((base_obj *)obj); /* { dg-bogus "use of uninitialized value ''" "" { xfail *-*-* } } */ + // TODO (xfail): not sure what's going on here + } +} + +void test_1 (const char *str) +{ + string_obj *obj = new_string_obj (str); + //__analyzer_dump(); + if (obj) + unref (obj); +} /* { dg-bogus "leak of 'obj'" "" { xfail *-*-* } } */ +// TODO (xfail): not sure why this is treated as leaking diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5c.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5c.c new file mode 100644 index 0000000..1e52350 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5c.c @@ -0,0 +1,84 @@ +/* A toy re-implementation of CPython's object model. */ + +#include +#include +#include + +typedef struct base_obj base_obj; +typedef struct type_obj type_obj; +typedef struct string_obj string_obj; + +struct base_obj +{ + struct type_obj *ob_type; + int ob_refcnt; +}; + +struct type_obj +{ + base_obj tp_base; +}; + +struct string_obj +{ + base_obj str_base; + size_t str_len; + char str_buf[]; +}; + +type_obj type_type = { + { &type_type, 1}, +}; + +type_obj str_type = { + { &str_type, 1}, +}; + +base_obj *alloc_obj (type_obj *ob_type, size_t sz) +{ + base_obj *obj = (base_obj *)malloc (sz); + if (!obj) + return NULL; + obj->ob_type = ob_type; + obj->ob_refcnt = 1; + return obj; +} + +string_obj *new_string_obj (const char *str) +{ + //__analyzer_dump (); + size_t len = strlen (str); +#if 1 + string_obj *str_obj + = (string_obj *)alloc_obj (&str_type, sizeof (string_obj) + len + 1); +#else + string_obj *str_obj = (string_obj *)malloc (sizeof (string_obj) + len + 1); + if (!str_obj) + return NULL; + str_obj->str_base.ob_type = &str_type; + str_obj->str_base.ob_refcnt = 1; +#endif + str_obj->str_len = len; /* { dg-warning "dereference of NULL 'str_obj'" } */ + memcpy (str_obj->str_buf, str, len); + str_obj->str_buf[len] = '\0'; + return str_obj; +} + +void unref (string_obj *obj) +{ + //__analyzer_dump(); + if (--obj->str_base.ob_refcnt == 0) + { + //__analyzer_dump(); + free (obj); + } +} + +void test_1 (const char *str) +{ + string_obj *obj = new_string_obj (str); + //__analyzer_dump(); + if (obj) + unref (obj); +} /* { dg-bogus "leak of 'obj'" "" { xfail *-*-* } } */ +// TODO (xfail): not sure why this is treated as leaking diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-5d.c b/gcc/testsuite/gcc.dg/analyzer/data-model-5d.c new file mode 100644 index 0000000..50ab0ac --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-5d.c @@ -0,0 +1,63 @@ +/* A toy re-implementation of CPython's object model. */ + +#include +#include +#include + +typedef struct base_obj base_obj; +typedef struct type_obj type_obj; +typedef struct string_obj string_obj; + +struct base_obj +{ + struct type_obj *ob_type; + int ob_refcnt; +}; + +struct type_obj +{ + base_obj tp_base; +}; + +struct string_obj +{ + base_obj str_base; + size_t str_len; + char str_buf[]; +}; + +type_obj type_type = { + { &type_type, 1}, +}; + +type_obj str_type = { + { &str_type, 1}, +}; + +base_obj *alloc_obj (type_obj *ob_type, size_t sz) +{ + base_obj *obj = (base_obj *)malloc (sz); + if (!obj) + return NULL; + obj->ob_type = ob_type; + obj->ob_refcnt = 1; + return obj; +} + +void unref (base_obj *obj) +{ + //__analyzer_dump(); + if (--obj->ob_refcnt == 0) + free (obj); +} + +void test_1 () +{ + base_obj *obj = alloc_obj (&str_type, sizeof (string_obj)); + if (obj) + { + __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '1'" } */ + unref (obj); + __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '0'" } */ + } +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-6.c b/gcc/testsuite/gcc.dg/analyzer/data-model-6.c new file mode 100644 index 0000000..8b63ab2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-6.c @@ -0,0 +1,13 @@ +#include + +/* Verify that we don't accumulate state after a malloc/free pair. */ + +void test (void) +{ + void *ptr; + __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '0'" } */ + ptr = malloc (1024); + __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '1'" } */ + free (ptr); + __analyzer_dump_num_heap_regions (); /* { dg-warning "num heap regions: '0'" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-7.c b/gcc/testsuite/gcc.dg/analyzer/data-model-7.c new file mode 100644 index 0000000..6c8ab0a --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-7.c @@ -0,0 +1,19 @@ +/* { dg-additional-options "-fno-analyzer-state-merge" } */ + +int test_40 (int flag) +{ + int i; + if (flag) + i = 43; + else + i = 17; + + /* Without state-merging, we retain the relationship between 'flag' and 'i'. */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + if (flag) + __analyzer_eval (i == 43); /* { dg-warning "TRUE" } */ + else + __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-8.c b/gcc/testsuite/gcc.dg/analyzer/data-model-8.c new file mode 100644 index 0000000..2c83c75 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-8.c @@ -0,0 +1,24 @@ +struct base +{ + int i; +}; + +struct sub +{ + struct base b; + int j; +}; + +void test (void) +{ + struct sub s; + s.b.i = 3; + s.j = 4; + __analyzer_eval (s.b.i == 3); /* { dg-warning "TRUE" } */ + __analyzer_eval (s.j == 4); /* { dg-warning "TRUE" } */ + + struct base *bp = (struct base *)&s; + + __analyzer_eval (bp->i == 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-9.c b/gcc/testsuite/gcc.dg/analyzer/data-model-9.c new file mode 100644 index 0000000..a1a46c6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-9.c @@ -0,0 +1,32 @@ +#include +#include + +struct foo +{ + int i; +}; + +/* TODO: verify that we know that calloc zeros its memory. */ + +void test_1 (void) +{ + struct foo *f = calloc (1, sizeof (struct foo)); + if (f == NULL) + return; + __analyzer_eval (f->i == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ + free (f); +} + +/* TODO: verify that we know the behavior of memset. */ + +void test_2 (void) +{ + struct foo *f = malloc (sizeof (struct foo)); + if (f == NULL) + return; + memset (f, 0, sizeof (struct foo)); + __analyzer_eval (f->i == 0); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-1 } */ + free (f); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c b/gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c new file mode 100644 index 0000000..d7058ea --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/data-model-path-1.c @@ -0,0 +1,13 @@ +#include + +static int *__attribute__((noinline)) +callee (void) +{ + return NULL; +} + +void test_1 (void) +{ + int *p = callee (); /* { dg-message "return of NULL to 'test_1' from 'callee'" } */ + *p = 42; /* { dg-warning "dereference of NULL 'p'" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-a.c b/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-a.c new file mode 100644 index 0000000..61e7846 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-a.c @@ -0,0 +1,16 @@ +/* { dg-do link } */ +/* { dg-require-effective-target lto } */ +/* { dg-additional-options "-flto" } */ +/* { dg-additional-sources double-free-lto-1-b.c } */ + +#include +#include "double-free-lto-1.h" + +void test (void *ptr) +{ + calls_free (ptr); /* { dg-message "calling 'calls_free' from 'test'" } */ + free (ptr); /* { dg-warning "double-'free' of 'ptr_.+'" } */ + // TODO: report "ptr", rather than an SSA name +} + +int main() { return 0; } diff --git a/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-b.c b/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-b.c new file mode 100644 index 0000000..6041e69 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1-b.c @@ -0,0 +1,8 @@ +#include + +#include "double-free-lto-1.h" + +extern void calls_free (void *ptr) +{ + free (ptr); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1.h b/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1.h new file mode 100644 index 0000000..c5e2157 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/double-free-lto-1.h @@ -0,0 +1 @@ +extern void calls_free (void *ptr); diff --git a/gcc/testsuite/gcc.dg/analyzer/equivalence.c b/gcc/testsuite/gcc.dg/analyzer/equivalence.c new file mode 100644 index 0000000..fb28660 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/equivalence.c @@ -0,0 +1,29 @@ +void test (int p, int q, int r) +{ + if (p == 42) + { + __analyzer_eval (p == 42); /* { dg-warning "TRUE" } */ + __analyzer_eval (p != 42); /* { dg-warning "FALSE" } */ + if (q == 42) + { + __analyzer_eval (p == q); /* { dg-warning "TRUE" } */ + } + else + { + __analyzer_eval (p != q); /* { dg-warning "TRUE" } */ + } + } + else + { + __analyzer_eval (p == 42); /* { dg-warning "FALSE" } */ + __analyzer_eval (p != 42); /* { dg-warning "TRUE" } */ + if (q == 42) + { + __analyzer_eval (p == q); /* { dg-warning "FALSE" } */ + } + else + { + __analyzer_eval (p == q); /* { dg-warning "UNKNOWN" } */ + } + } +} diff --git a/gcc/testsuite/gcc.dg/analyzer/explode-1.c b/gcc/testsuite/gcc.dg/analyzer/explode-1.c new file mode 100644 index 0000000..6b62e8e --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/explode-1.c @@ -0,0 +1,60 @@ +/* { dg-additional-options "-Wno-analyzer-too-complex" } */ + +#include + +extern int get (void); + +/* In theory each of p0...p8 can be in various malloc states, + independently, so the total combined number of states + at any program point within the loop is NUM_VARS * NUM_STATES. */ + +void test (void) +{ + void *p0, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8; + void **pp; + while (get ()) + { + switch (get ()) + { + default: + case 0: + pp = &p0; + break; + case 1: + pp = &p1; + break; + case 2: + pp = &p2; + break; + case 3: + pp = &p3; + break; + case 4: + pp = &p4; + break; + case 5: + pp = &p5; + break; + case 6: + pp = &p6; + break; + case 7: + pp = &p7; + break; + } + + switch (get ()) + { + default: + case 0: + *pp = malloc (16); + break; + case 1: + free (*pp); + break; + case 2: + /* no-op. */ + break; + } + } +} diff --git a/gcc/testsuite/gcc.dg/analyzer/explode-2.c b/gcc/testsuite/gcc.dg/analyzer/explode-2.c new file mode 100644 index 0000000..d786aa9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/explode-2.c @@ -0,0 +1,50 @@ +/* In theory each of p0...p3 can be in various malloc states, + independently, so the total combined number of states + at any program point within the loop is NUM_VARS * NUM_STATES. + + Set the limits high enough that we can fully explore this. */ + +/* { dg-additional-options "--param analyzer-max-enodes-per-program-point=200 --param analyzer-bb-explosion-factor=50" } */ + +#include + +extern int get (void); + +void test (void) +{ + void *p0, *p1, *p2, *p3; + while (get ()) + { + switch (get ()) + { + default: + case 0: + p0 = malloc (16); + break; + case 1: + free (p0); /* { dg-warning "double-'free' of 'p0'" } */ + break; + + case 2: + p1 = malloc (16); + break; + case 3: + free (p1); /* { dg-warning "double-'free' of 'p1'" } */ + break; + + case 4: + p2 = malloc (16); + break; + case 5: + free (p2); /* { dg-warning "double-'free' of 'p2'" } */ + break; + + case 6: + p3 = malloc (16); + break; + case 7: + free (p3); /* { dg-warning "double-'free' of 'p3'" } */ + break; + } + } +} diff --git a/gcc/testsuite/gcc.dg/analyzer/factorial.c b/gcc/testsuite/gcc.dg/analyzer/factorial.c new file mode 100644 index 0000000..384713a --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/factorial.c @@ -0,0 +1,7 @@ +int factorial (int n) +{ + if (n > 1) + return n * factorial (n - 1); + else + return 1; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/fibonacci.c b/gcc/testsuite/gcc.dg/analyzer/fibonacci.c new file mode 100644 index 0000000..5d4a4e0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/fibonacci.c @@ -0,0 +1,9 @@ +int fib (int n) +{ + if (n > 1) + return fib (n - 1) + fib (n - 2); + else + return n; +} + +/* { dg-regexp "\[^\n\r\]+: warning: analysis bailed out early \\(\[0-9\]+ 'after-snode' enodes; \[0-9\]+ enodes\\) \[^\n\r\]*" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/fields.c b/gcc/testsuite/gcc.dg/analyzer/fields.c new file mode 100644 index 0000000..de55208 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/fields.c @@ -0,0 +1,41 @@ +typedef long unsigned int size_t; + +extern size_t strlen (const char *__s) + __attribute__ ((__nothrow__ , __leaf__)) + __attribute__ ((__pure__)) + __attribute__ ((__nonnull__ (1))); + +extern void *malloc (size_t __size) + __attribute__ ((__nothrow__ , __leaf__)) + __attribute__ ((__malloc__)) ; + +extern void free (void *__ptr) + __attribute__ ((__nothrow__ , __leaf__)); + +typedef struct _krb5_data { + unsigned int length; + char *data; +} krb5_data; + +typedef struct _krb5_error { + krb5_data text; +} krb5_error; + +extern const char *error_message (int); + +int +recvauth_common (int problem) +{ + if (problem) { + krb5_error error; + const char *message = error_message(problem); + error.text.length = strlen(message) + 1; + if (!(error.text.data = malloc(error.text.length))) { + goto cleanup; + } + free(error.text.data); + } + + cleanup: + return problem; /* { dg-bogus "leak" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/file-1.c b/gcc/testsuite/gcc.dg/analyzer/file-1.c new file mode 100644 index 0000000..91d9685 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/file-1.c @@ -0,0 +1,37 @@ +#include + +void +test_1 (const char *path) +{ + FILE *f = fopen (path, "r"); /* { dg-message "opened here" } */ + if (!f) + return; + + fclose (f); /* { dg-message "\\(4\\) \\.\\.\\.to here" } */ + /* { dg-message "\\(5\\) first 'fclose' here" "" { target *-*-* } .-1 } */ + fclose (f); /* { dg-warning "double 'fclose' of FILE 'f'" } */ + /* { dg-message "second 'fclose' here; first 'fclose' was at \\(5\\)" "" { target *-*-* } .-1 } */ +} + +void +test_2 (const char *src, const char *dst) +{ + FILE *f_in = fopen (src, "r"); /* { dg-message "\\(1\\) opened here" } */ + if (!f_in) + return; + + FILE *f_out = fopen (src, "w"); + if (!f_out) + return; /* { dg-warning "leak of FILE 'f_in'" } */ + /* { dg-message "\\(7\\) 'f_in' leaks here; was opened at \\(1\\)" "" { target *-*-* } .-1 } */ + + fclose (f_out); + fclose (f_in); +} + +void +test_3 (const char *path) +{ + FILE *f = fopen (path, "r"); /* { dg-message "opened here" } */ + return; /* { dg-warning "leak of FILE 'f'" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/file-2.c b/gcc/testsuite/gcc.dg/analyzer/file-2.c new file mode 100644 index 0000000..aa04570 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/file-2.c @@ -0,0 +1,18 @@ +#include + +struct foo +{ + FILE *m_f; +}; + +void test (const char *path) +{ + struct foo f; + f.m_f = fopen (path, "r"); + + if (!f.m_f) + return; /* { dg-bogus "leak of FILE" } */ + + fclose (f.m_f); + fclose (f.m_f); /* { dg-warning "double 'fclose' of FILE 'f.m_f'" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/function-ptr-1.c b/gcc/testsuite/gcc.dg/analyzer/function-ptr-1.c new file mode 100644 index 0000000..38c3e59 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/function-ptr-1.c @@ -0,0 +1,8 @@ +#include + +typedef void *(*fn_ptr_t) (void *); + +void *test_1 (fn_ptr_t fn_ptr, void *data) +{ + return fn_ptr (data); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c b/gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c new file mode 100644 index 0000000..81044a7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/function-ptr-2.c @@ -0,0 +1,43 @@ +#include + +typedef void (*fn_ptr_t) (void *); + +void +calls_free (void *victim) +{ + free (victim); +} + +void +no_op (void *ptr) +{ +} + +void test_1 (void *ptr) +{ + fn_ptr_t fn_ptr = calls_free; + __analyzer_eval (fn_ptr == calls_free); /* { dg-warning "TRUE" } */ + __analyzer_eval (fn_ptr != NULL); /* { dg-warning "TRUE" } */ + __analyzer_eval (fn_ptr == NULL); /* { dg-warning "FALSE" } */ + __analyzer_eval (fn_ptr == no_op); /* { dg-warning "FALSE" } */ + + fn_ptr (ptr); + fn_ptr (ptr); +} +// TODO: issue a double-'free' warning at 2nd call to fn_ptr. + +/* As above, but with an extra indirection to try to thwart + the optimizer. */ + +void test_2 (void *ptr, fn_ptr_t *fn_ptr) +{ + *fn_ptr = calls_free; + __analyzer_eval (*fn_ptr == calls_free); /* { dg-warning "TRUE" } */ + __analyzer_eval (*fn_ptr != NULL); /* { dg-warning "TRUE" } */ + __analyzer_eval (*fn_ptr == NULL); /* { dg-warning "FALSE" } */ + __analyzer_eval (*fn_ptr == no_op); /* { dg-warning "FALSE" } */ + + (*fn_ptr) (ptr); + (*fn_ptr) (ptr); +} +// TODO: issue a double-'free' warning at 2nd call to fn_ptr. diff --git a/gcc/testsuite/gcc.dg/analyzer/function-ptr-3.c b/gcc/testsuite/gcc.dg/analyzer/function-ptr-3.c new file mode 100644 index 0000000..348ee4a --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/function-ptr-3.c @@ -0,0 +1,17 @@ +#include + +typedef void *(*alloc_func_t) (size_t); +typedef void (*free_func_t) (void *); + +typedef struct callbacks +{ + alloc_func_t alloc_cb; + free_func_t dealloc_cb; +} callbacks_t; + +void test (void) +{ + callbacks_t cb; + cb.alloc_cb = (alloc_func_t)0; + cb.dealloc_cb = (free_func_t)0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/gzio-2.c b/gcc/testsuite/gcc.dg/analyzer/gzio-2.c new file mode 100644 index 0000000..855ecc8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/gzio-2.c @@ -0,0 +1,11 @@ +void gzseek (long offset, int whence) +{ + if (whence == 2) + return; + if (whence == 0) + offset -= 1; + if (offset < 0) + return; + while (offset > 0) { + } +} diff --git a/gcc/testsuite/gcc.dg/analyzer/gzio-3.c b/gcc/testsuite/gcc.dg/analyzer/gzio-3.c new file mode 100644 index 0000000..0a11f65 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/gzio-3.c @@ -0,0 +1,31 @@ +typedef long unsigned int size_t; +typedef struct _IO_FILE FILE; +extern size_t fread(void *__restrict __ptr, size_t __size, size_t __n, + FILE *__restrict __stream); +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef unsigned long uLong; + +typedef struct z_stream_s { + uInt avail_in; + uInt avail_out; +} z_stream; + +typedef struct gz_stream { + z_stream stream; + FILE *file; +} gz_stream; + +void test_1_callee(gz_stream *s, Byte *buf) { + Byte *next_out = buf; + uInt n = s->stream.avail_in; + if (n > 0) { + next_out += n; + } + s->stream.avail_out -= fread(next_out, 1, s->stream.avail_out, s->file); +} + +void test_1_caller(gz_stream *s) { + unsigned char c; + test_1_callee(s, &c); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/gzio-3a.c b/gcc/testsuite/gcc.dg/analyzer/gzio-3a.c new file mode 100644 index 0000000..15ed010 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/gzio-3a.c @@ -0,0 +1,27 @@ +typedef long unsigned int size_t; +typedef struct _IO_FILE FILE; +extern size_t fread(void *__restrict __ptr, size_t __size, size_t __n, + FILE *__restrict __stream); +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef unsigned long uLong; + +typedef struct gz_stream { + FILE *file; + uInt avail_in; + uInt avail_out; +} gz_stream; + +void test_1_callee(gz_stream *s, Byte *buf) { + Byte *next_out = buf; + uInt n = s->avail_in; + if (n > 0) { + next_out += n; + } + s->avail_out -= fread(next_out, 1, s->avail_out, s->file); +} + +void test_1_caller(gz_stream *s) { + unsigned char c; + test_1_callee(s, &c); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/gzio.c b/gcc/testsuite/gcc.dg/analyzer/gzio.c new file mode 100644 index 0000000..54efa77 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/gzio.c @@ -0,0 +1,17 @@ +#include +typedef struct z_stream_s { + unsigned char *next_out; +} z_stream; +typedef struct gz_stream { + z_stream stream; + unsigned char *outbuf; +} gz_stream; +gz_stream *s; +static void gz_open(const char *path) +{ + s->stream.next_out = s->outbuf = (unsigned char *)malloc(16384); /* { dg-bogus "leak" } */ +} +void gzopen(const char *path) +{ + gz_open(path); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/infinite-recursion.c b/gcc/testsuite/gcc.dg/analyzer/infinite-recursion.c new file mode 100644 index 0000000..b770e12 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/infinite-recursion.c @@ -0,0 +1,55 @@ +extern void marker_A(void); +extern void marker_B(void); +extern void marker_C(void); +extern void marker_D(void); + +void test(int flag) +{ + marker_A(); + + if (flag) { + marker_B(); + + /* Recurse, infinitely, as it happens: */ + test(flag); + + marker_C(); + } + + marker_D(); +} + +/* A cycle of 4 mutually-recursive functions (but only for certain inputs). */ + +extern void mutual_test_1 (int flag); +extern void mutual_test_2 (int flag); +extern void mutual_test_3 (int flag); +extern void mutual_test_4 (int flag); + +void mutual_test_1 (int flag) +{ + marker_A (); + if (flag) + mutual_test_2 (flag); +} + +void mutual_test_2 (int flag) +{ + marker_B (); + if (flag) + mutual_test_3 (flag); +} + +void mutual_test_3 (int flag) +{ + marker_C (); + if (flag) + mutual_test_4 (flag); +} + +void mutual_test_4 (int flag) +{ + marker_D (); + if (flag) + mutual_test_1 (flag); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-2.c b/gcc/testsuite/gcc.dg/analyzer/loop-2.c new file mode 100644 index 0000000..2a6edc4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/loop-2.c @@ -0,0 +1,36 @@ +/* { dg-additional-options "-fno-analyzer-state-purge" } */ + +struct s +{ + int i; +}; + +void test(void) +{ + struct s s; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + + for (s.i=0; s.i<256; s.i++) { + __analyzer_eval (s.i < 256); /* { dg-warning "TRUE" } */ + /* (should report TRUE twice). */ + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + //__analyzer_eval (s.i == 0); /* { d-todo-g-warning "UNKNOWN" "" { xfail *-*-* } } */ + /* { d-todo-g-warning "TRUE" "" { target *-*-* } .-1 } */ + /* TODO(xfail^^^): we're only capturing the first iteration, so + we erroneously get i == 0. */ + + //__analyzer_eval (s.i >= 0); /* { d-todo-g-warning "TRUE" } */ + } + + __analyzer_eval (s.i >= 256); /* { dg-warning "TRUE" } */ + + __analyzer_eval (s.i == 256); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + /* TODO(xfail^^^): ideally it should figure out i == 256 at exit. */ + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-2a.c b/gcc/testsuite/gcc.dg/analyzer/loop-2a.c new file mode 100644 index 0000000..94eff82 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/loop-2a.c @@ -0,0 +1,39 @@ +/* { dg-additional-options "-fno-analyzer-state-purge" } */ + +union u +{ + int i; +}; + +void test(void) +{ + union u u; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + + for (u.i=0; u.i<256; u.i++) { + __analyzer_eval (u.i < 256); /* { dg-warning "TRUE" } */ + /* { dg-warning "TRUE" "" { xfail *-*-* } .-1 } */ + /* { dg-bogus "UNKNOWN" "" { xfail *-*-* } .-2 } */ + /* (should report TRUE twice). */ + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + //__analyzer_eval (u.i == 0); /* { d-todo-g-warning "UNKNOWN" "" { xfail *-*-* } } */ + /* { d-todo-g-warning "TRUE" "" { target *-*-* } .-1 } */ + /* TODO(xfail^^^): we're only capturing the first iteration, so + we erroneously get i == 0. */ + + //__analyzer_eval (u.i >= 0); /* { d-todo-g-warning "TRUE" } */ + } + + __analyzer_eval (u.i >= 256); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + + __analyzer_eval (u.i == 256); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + /* TODO(xfail^^^): ideally it should figure out i == 256 at exit. */ + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-3.c b/gcc/testsuite/gcc.dg/analyzer/loop-3.c new file mode 100644 index 0000000..1d01771 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/loop-3.c @@ -0,0 +1,17 @@ +#include + +void test(int c) +{ + int i; + char *buffer = (char*)malloc(256); + + for (i=0; i<255; i++) { + buffer[i] = c; /* { dg-warning "use after 'free' of 'buffer'" } */ + /* BUG: the malloc could have failed + TODO: the checker doesn't yet pick up on this, perhaps + due to the pointer arithmetic not picking up on the + state */ + free(buffer); /* { dg-warning "double-'free' of 'buffer'" } */ + } + +} diff --git a/gcc/testsuite/gcc.dg/analyzer/loop-4.c b/gcc/testsuite/gcc.dg/analyzer/loop-4.c new file mode 100644 index 0000000..7e3e7ab --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/loop-4.c @@ -0,0 +1,41 @@ +// FIXME: +/* { dg-additional-options "-fno-analyzer-state-purge" } */ + +/* Example of nested loops. */ + +void test(void) +{ + int i, j, k; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + for (i=0; i<256; i++) { + + __analyzer_eval (i >= 0); /* { dg-warning "TRUE" } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + + __analyzer_eval (i < 256); /* { dg-warning "TRUE" } */ + + for (j=0; j<256; j++) { + + __analyzer_eval (j >= 0); /* { dg-warning "TRUE" } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + + __analyzer_eval (j < 256); /* { dg-warning "TRUE" } */ + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + + for (k=0; k<256; k++) { + + __analyzer_eval (k >= 0); /* { dg-warning "TRUE" } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + + __analyzer_eval (k < 256); /* { dg-warning "TRUE" } */ + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 exploded nodes" } */ + } + } + } + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/loop.c b/gcc/testsuite/gcc.dg/analyzer/loop.c new file mode 100644 index 0000000..32fb051 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/loop.c @@ -0,0 +1,33 @@ +/* { dg-additional-options "-fno-analyzer-state-purge" } */ + +void test(void) +{ + int i; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + for (i=0; i<256; i++) { + __analyzer_eval (i < 256); /* { dg-warning "TRUE" } */ + /* (should report TRUE twice). */ + + __analyzer_eval (i == 0); /* { dg-warning "TRUE" } */ + /* { dg-warning "FALSE" "" { xfail *-*-* } .-1 } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-2 } */ + /* TODO(xfail^^^): ideally we ought to figure out i > 0 after 1st iteration. */ + + __analyzer_eval (i >= 0); /* { dg-warning "TRUE" } */ + /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-2 } */ + /* TODO(xfail^^^): ideally we ought to figure out i >= 0 for all iterations. */ + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + } + + __analyzer_eval (i >= 256); /* { dg-warning "TRUE" } */ + + __analyzer_eval (i == 256); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + /* TODO(xfail^^^): it only figures out i >= 256, rather than i == 256. */ + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c new file mode 100644 index 0000000..13ed4fc --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-1.c @@ -0,0 +1,565 @@ +#include +#include + +extern int foo (void); +extern int bar (void); +extern void could_free (void *); +extern void cant_free (const void *); /* since it's a const void *. */ + +void test_1 (void) +{ + void *ptr = malloc (1024); + free (ptr); + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ +} + +void test_2 (void *ptr) +{ + free (ptr); + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ +} + +int *test_3 (void) +{ + int *ptr = (int *)malloc (sizeof (int)); + *ptr = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */ + return ptr; +} + +int *test_4 (void) +{ + int *ptr = (int *)malloc (sizeof (int)); + if (ptr) + *ptr = 42; + else + *ptr = 43; /* { dg-warning "dereference of NULL 'ptr'" } */ + return ptr; +} + +int test_5 (int *ptr) +{ + free (ptr); + return *ptr; /* { dg-warning "use after 'free' of 'ptr'" } */ +} + +void test_6 (void *ptr) +{ + void *q; + q = ptr; + free (ptr); + free (q); /* { dg-warning "double-'free' of 'q'" } */ + /* The above case requires us to handle equivalence classes in + state transitions. */ +} + +void test_7 (void) +{ + void *ptr = malloc(4096); + if (!ptr) + return; + memset(ptr, 0, 4096); + free(ptr); +} + +void *test_8 (void) +{ + void *ptr = malloc(4096); + if (!ptr) + return NULL; + memset(ptr, 0, 4096); + return ptr; + /* This needs phi nodes to affect equivalence classes, or we get a false report + of a leak. */ +} + +void test_9 (void) +{ + void *ptr = malloc (1024); + + int i; + for (i = 0; i < 1024; i++) + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ +} + +void test_10 (void) +{ + void *ptr = malloc (1024); + + int i; + for (i = 0; i < 1024; i++) + foo (); + + free (ptr); + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ +} + +void test_11 (void) +{ + void *ptr = malloc (1024); + + while (foo ()) + bar (); + + free (ptr); + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ +} + +void test_12 (void) +{ + void *ptr = malloc (1024); + + while (1) + { + free (ptr); + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ + } +} + +void test_13 (void) +{ + void *p = malloc (1024); /* { dg-message "allocated here" } */ + void *q = malloc (1024); + + foo (); + if (!q) + { + free (q); + return; /* { dg-warning "leak of 'p'" } */ + } + bar (); + free (q); + free (p); +} + +void test_14 (void) +{ + void *p, *q; + p = malloc (1024); + if (!p) + return; + + q = malloc (1024); + if (!q) + { + free (p); + free (q); + /* oops: missing "return". */ + } + bar (); + free (q); /* Although this looks like a double-'free' of q, + it's known to be NULL for the case where free is + called twice on it. */ + free (p); /* { dg-warning "double-'free' of 'p'" } */ +} + +void test_15 (void) +{ + void *p = NULL, *q = NULL; + + p = malloc (1024); + if (!p) + goto fail; + + foo (); + + q = malloc (1024); + if (!q) + goto fail; + + bar (); + + fail: + free (q); + free (p); +} + +void test_16 (void) +{ + void *p, *q; + + p = malloc (1024); + if (!p) + goto fail; + + foo (); + + q = malloc (1024); + if (!q) + goto fail; + + bar (); + + fail: + free (q); /* { dg-warning "free of uninitialized 'q'" "" { xfail *-*-* } } */ + /* TODO(xfail): implement uninitialized detection. */ + free (p); +} + +void test_17 (void) +{ + void *ptr = malloc (1024); /* { dg-message "allocated here" } */ +} /* { dg-warning "leak of 'ptr'" } */ + +void test_18 (void) +{ + void *ptr = malloc (64); /* { dg-message "allocated here" } */ + ptr = NULL; /* { dg-warning "leak of 'ptr'" } */ +} + +void test_19 (void) +{ + void *ptr = malloc (64); + free (ptr); + ptr = NULL; + free (ptr); +} + +void *global_ptr_20; + +void test_20 (void) +{ + global_ptr_20 = malloc (1024); +} + +int *test_21 (int i) +{ + int *ptr = malloc (sizeof (int)); + if (!ptr) + abort (); + *ptr = i; + return ptr; +} + +void test_22 (void) +{ + void *ptr = malloc (1024); + + int i; + for (i = 5; i < 10; i++) + foo (); + + free (ptr); + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ +} + +int *test_23 (int n) +{ + int *ptr = (int *)calloc (n, sizeof (int)); + ptr[0] = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */ + return ptr; +} + +int test_24 (void) +{ + void *ptr = alloca (sizeof (int)); /* { dg-message "memory is allocated on the stack here" } */ + free (ptr); /* { dg-warning "'free' of memory allocated on the stack by 'alloca' \\('ptr'\\) will corrupt the heap \\\[CWE-590\\\]" } */ +} + +int test_25 (void) +{ + char tmp[100]; + void *p = tmp; /* { dg-message "pointer is from here" } */ + free (p); /* { dg-warning "'free' of 'p' which points to memory not on the heap \\\[CWE-590\\\]" } */ + /* TODO: more precise messages here. */ +} + +char global_buffer[100]; + +int test_26 (void) +{ + void *p = global_buffer; /* { dg-message "pointer is from here" } */ + free (p); /* { dg-warning "'free' of 'p' which points to memory not on the heap \\\[CWE-590\\\]" } */ + /* TODO: more precise messages here. */ +} + +struct coord { + float x; + float y; +}; + +struct coord *test_27 (void) +{ + struct coord *p = (struct coord *) malloc (sizeof (struct coord)); /* { dg-message "this call could return NULL" } */ + p->x = 0.f; /* { dg-warning "dereference of possibly-NULL 'p'" } */ + + /* Only the first such usage should be reported: */ + p->y = 0.f; + + return p; +} + +struct coord *test_28 (void) +{ + struct coord *p = NULL; + p->x = 0.f; /* { dg-warning "dereference of NULL 'p'" } */ + + /* Only the first such usage should be reported: */ + p->y = 0.f; + + return p; +} + +struct link +{ + struct link *m_ptr; +}; + +struct link *test_29 (void) +{ + struct link *res = (struct link *)malloc (sizeof (struct link)); + if (!res) + return NULL; + res->m_ptr = (struct link *)malloc (sizeof (struct link)); + return res; +} + +struct link *test_29a (void) +{ + struct link *res = (struct link *)malloc (sizeof (struct link)); + if (!res) + return NULL; + res->m_ptr = (struct link *)malloc (sizeof (struct link)); + if (!res->m_ptr) + { + free (res); + return NULL; + } + res->m_ptr->m_ptr = (struct link *)malloc (sizeof (struct link)); + return res; +} + +/* Without consolidation by EC, this one shows two leaks: + warning: leak of '' + warning: leak of 'tmp.m_ptr' + We should only show the latter (favoring the most user-readable + expression in the equivalence class). */ +void test_30 (void) +{ + struct link tmp; + tmp.m_ptr = (struct link *)malloc (sizeof (struct link)); /* { dg-message "allocated here" } */ +} /* { dg-warning "leak of 'tmp.m_ptr'" } */ +/* { dg-bogus "leak of ''" "" { xfail *-*-* } .-1 } */ + +void test_31 (void) +{ + struct link tmp; + void *ptr = malloc (sizeof (struct link)); /* { dg-message "allocated here" } */ + tmp.m_ptr = (struct link *)ptr; +} /* { dg-warning "leak of 'ptr'" } */ +/* { dg-bogus "leak of 'tmp.m_ptr'" "" { xfail *-*-* } .-1 } */ + +void test_32 (void) +{ + void *ptr = malloc (1024); + could_free (ptr); +} /* { dg-bogus "leak" } */ + +void test_33 (void) +{ + void *ptr = malloc (1024); /* { dg-message "allocated here" } */ + cant_free (ptr); +} /* { dg-warning "leak of 'ptr'" } */ + +void test_34 (void) +{ + float *q; + struct coord *p = malloc (sizeof (struct coord)); + if (!p) + return; + p->x = 0.0f; + q = &p->x; + free (p); + *q = 1.0f; /* { dg-warning "use after 'free' of 'q'" } */ +}; + +int test_35 (void) +{ + void *ptr = malloc(4096); + if (!ptr) + return -1; + memset(ptr, 0, 4096); + free(ptr); + return 0; +} + +void test_36 (void) +{ + void *ptr = malloc(4096); + if (!ptr) + return; + memset(ptr, 0, 4096); + free(ptr); +} + +void *test_37a (void) +{ + void *ptr = malloc(4096); /* { dg-message "this call could return NULL" } */ + memset(ptr, 0, 4096); /* { dg-warning "use of possibly-NULL 'ptr' where non-null expected" } */ + return ptr; +} + +int test_37b (void) +{ + void *p = malloc(4096); + void *q = malloc(4096); /* { dg-message "this call could return NULL" } */ + if (p) { + memset(p, 0, 4096); /* Not a bug: checked */ + } else { + memset(q, 0, 4096); /* { dg-warning "use of possibly-NULL 'q' where non-null expected" } */ + } + free(p); + free(q); + return 0; +} + +extern void might_use_ptr (void *ptr); + +void test_38(int i) +{ + void *p; + + p = malloc(1024); + if (p) { + free(p); + might_use_ptr(p); /* { dg-warning "use after 'free' of 'p'" "" { xfail *-*-* } } */ + // TODO: xfail + } +} + +int * +test_39 (int i) +{ + int *p = (int*)malloc(sizeof(int*)); /* { dg-message "this call could return NULL" } */ + *p = i; /* { dg-warning "dereference of possibly-NULL 'p'" } */ + return p; +} + +int * +test_40 (int i) +{ + int *p = (int*)malloc(sizeof(int*)); + i = *p; /* { dg-warning "dereference of possibly-NULL 'p'" } */ + /* TODO: (it's also uninitialized) */ + return p; +} + +char * +test_41 (int flag) +{ + char *buffer; + + if (flag) { + buffer = (char*)malloc(4096); + } else { + buffer = NULL; + } + + buffer[0] = 'a'; /* { dg-warning "dereference of possibly-NULL 'buffer'" } */ + /* { dg-warning "dereference of NULL 'buffer'" "" { target *-*-* } .-1 } */ + + return buffer; +} + +void test_42a (void) +{ + void *p = malloc (1024); /* { dg-message "allocated here" } */ + free (p + 64); /* this could well corrupt the heap. */ + /* TODO: ^^^ we should warn about this. */ +} /* { dg-warning "leak of 'p'" } */ +/* TODO: presumably we should complain about the bogus free, but then + maybe not complain about the leak. */ +// CWE-761: Free of Pointer not at Start of Buffer + +void test_42b (void) +{ + void *p = malloc (1024); /* { dg-message "allocated here" } */ + free (p - 64); /* this could well corrupt the heap. */ + /* TODO: ^^^ we should warn about this. */ +} /* { dg-warning "leak of 'p'" } */ +/* TODO: presumably we should complain about the bogus free, but then + maybe not complain about the leak. */ +// CWE-761: Free of Pointer not at Start of Buffer + +void test_42c (void) +{ + void *p = malloc (1024); + void *q = p + 64; + free (q - 64); /* this is probably OK. */ +} /* { dg-bogus "leak of 'p'" "" { xfail *-*-* } } */ +// TODO(xfail) + +#if 0 +void test_31 (void *p) +{ + void *q = realloc (p, 1024); + free (p); /* FIXME: this is a double-'free'. */ + free (q); +} + +void test_32 (void) +{ + void *p = malloc (64); + p = realloc (p, 1024); /* FIXME: this leaks if it fails. */ + free (p); +} +#endif + +struct link global_link; + +void test_43 (void) +{ + global_link.m_ptr = malloc (sizeof (struct link)); /* { dg-message "allocated here" } */ + global_link.m_ptr = NULL; +} /* { dg-warning "leak of ''" } */ +/* TODO: should be more precise than just '', and + ideally would be at the assigment to NULL. */ + +struct link *global_ptr; + +void test_44 (void) +{ + global_ptr = malloc (sizeof (struct link)); + if (!global_ptr) + return; + global_ptr->m_ptr = malloc (sizeof (struct link)); /* { dg-message "allocated here" } */ + free (global_ptr); /* { dg-warning "leak of ''" } */ + /* TODO: should be more precise than just ''. */ +} + +extern void might_take_ownership (void *ptr); + +void test_45 (void) +{ + void *p = malloc (1024); + might_take_ownership (p); +} + +void test_46 (void) +{ + struct link *p = (struct link *)malloc (sizeof (struct link)); + if (!p) + return; + struct link *q = (struct link *)malloc (sizeof (struct link)); + p->m_ptr = q; + might_take_ownership (p); +} + +extern int maybe_alloc (char **); + +int test_47 (void) +{ + char *p = ((void *)0); + int p_size = 0; + + p = malloc (16); + if (p) { + free (p); + } else { + int retval = maybe_alloc (&p); /* this might write to "p". */ + if (retval) + return (retval); + p_size = strlen(p); /* { dg-bogus "non-null expected" } */ + free (p); + } + return p_size; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-2.c b/gcc/testsuite/gcc.dg/analyzer/malloc-2.c new file mode 100644 index 0000000..6d073f5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-2.c @@ -0,0 +1,23 @@ +/* Tests for precision-of-wording within malloc warnings. */ + +typedef __SIZE_TYPE__ size_t; +extern void *malloc(size_t); +extern void free(void *); +extern char *strcpy(char *__restrict __dest, const char *__restrict __src) + __attribute__((__nothrow__, __leaf__)) __attribute__((__nonnull__(1, 2))); + +void test_1 (void) +{ + void *p = malloc (1024); /* { dg-message "\\(1\\) this call could return NULL" } */ + strcpy ((char *)p, "hello world"); /* { dg-warning "use of possibly-NULL 'p' where non-null expected" } */ + /* { dg-message "\\(2\\) argument 1 \\('p'\\) from \\(1\\) could be NULL where non-null expected" "" { target *-*-* } .-1 } */ + free (p); +} + +int *test_2 (void) +{ + int *i = malloc (sizeof (int)); /* { dg-message "\\(1\\) this call could return NULL" } */ + *i = 42; /* { dg-warning "dereference of possibly-NULL 'i'" } */ + /* { dg-message "\\(2\\) 'i' could be NULL: unchecked value from \\(1\\)" "" { target *-*-* } .-1 } */ + return i; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-3.c b/gcc/testsuite/gcc.dg/analyzer/malloc-3.c new file mode 100644 index 0000000..5afb6b3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-3.c @@ -0,0 +1,8 @@ +#include + +/* Don't complain about leaks due to exiting from "main". */ + +void main (void) +{ + void *p = malloc (1024); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-dce.c b/gcc/testsuite/gcc.dg/analyzer/malloc-dce.c new file mode 100644 index 0000000..1f61b4b --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-dce.c @@ -0,0 +1,12 @@ +/* { dg-options "--analyzer -O2" } */ + +#include + +void test(void) +{ + void *ptr = malloc(512); + free(ptr); + free(ptr); /* { dg-warning "double-'free'" "" { xfail *-*-* } } */ +} +/* With optimization, the whole of test() goes away in the "cddce" pass + before the analysis pass sees it, and hence we get no error message. */ diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c new file mode 100644 index 0000000..233ab48 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-dedupe-1.c @@ -0,0 +1,46 @@ +#include + +extern void foo (void); +extern void bar (void); + +void test_1 (int flag, int n); + +void caller_1_of_test_1 (int n) +{ + test_1 (1, n); /* { dg-bogus "test_1" } */ + test_1 (0, n); /* { dg-bogus "test_1" } */ +} + +void __attribute__((noinline)) +test_1 (int flag, int n) +{ + int *ptr = (int *)malloc (sizeof (int)); + + if (flag) + { + int i; + for (i = 0; i < n; i++) + foo (); + } + else + bar (); + + free (ptr); + free (ptr); /* { dg-warning "double-'free'" } */ + /* FIXME: we get duplicates intraprocedurally, as there are two paths + through the function. + The calls in test_2 also generate additional duplicates. + How to verify lack of duplicates? + Putting a bogus on the interprocedual one detects that, at least. */ + + if (flag) + foo (); + else + bar (); +} + +void caller_2_of_test_1 (int n) +{ + test_1 (1, n); /* { dg-bogus "test_1" } */ + test_1 (0, n); /* { dg-bogus "test_1" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-1.c new file mode 100644 index 0000000..ad536ce --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-1.c @@ -0,0 +1,24 @@ +#include + +void * +calls_malloc (void) +{ + void *result = malloc (1024); + return result; +} + +int *test_1 (int i) +{ + int *ptr = (int *)calls_malloc (); + *ptr = i; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */ + return ptr; +} + +/* Same as test_1, to exercise the caches. */ + +int *test_2 (int i) +{ + int *ptr = (int *)calls_malloc (); + *ptr = i; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */ + return ptr; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-10.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-10.c new file mode 100644 index 0000000..7e8f274 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-10.c @@ -0,0 +1,32 @@ +#include + +void +calls_free (void *victim) /* { dg-message "\\(3\\) entry to 'calls_free'" } */ +/* { dg-message "\\(7\\) entry to 'calls_free'" "" { target *-*-* } .-1 } */ +{ + free (victim); /* { dg-warning "double-'free' of 'victim'" } */ + /* { dg-message "\\(4\\) first 'free' here" "" { target *-*-* } .-1 } */ + /* { dg-message "\\(8\\) second 'free' here; first 'free' was at \\(4\\)" "" { target *-*-* } .-2 } */ + + /* TODO: would this be better emitted at the callsite, + for such a simple wrapper? */ +} + +void do_stuff (void) +{ + /* Empty. Irrelevant, and thus should not be expanded into in paths. */ +} + +void test (void *ptr) /* { dg-message "\\(1\\) entry to 'test'" } */ +{ + do_stuff (); + + calls_free (ptr); /* { dg-message "\\(2\\) calling 'calls_free' from 'test'" } */ + /* { dg-message "\\(5\\) returning to 'test' from 'calls_free'" "" { target *-*-* } .-1 } */ + + do_stuff (); + + calls_free (ptr); /* { dg-message "\\(6\\) passing freed pointer 'ptr' in call to 'calls_free' from 'test'" } */ + + do_stuff (); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-11.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-11.c new file mode 100644 index 0000000..d02250f --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-11.c @@ -0,0 +1,95 @@ +/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */ + +#include + +extern int some_condition (); +extern void do_stuff (int); + +void +may_call_free (void *victim) +{ + if (some_condition ()) + return; + + free (victim); /* { dg-warning "double-'free' of 'victim'" } */ +} + +void test (void *ptr) +{ + do_stuff (1); + + may_call_free (ptr); + + do_stuff (2); + + may_call_free (ptr); + + do_stuff (3); +} + +/* { dg-begin-multiline-output "" } + NN | free (victim); + | ^~~~~~~~~~~~~ + 'test': events 1-2 + | + | NN | void test (void *ptr) + | | ^~~~ + | | | + | | (1) entry to 'test' + |...... + | NN | may_call_free (ptr); + | | ~~~~~~~~~~~~~~~~~~~ + | | | + | | (2) calling 'may_call_free' from 'test' + | + +--> 'may_call_free': events 3-6 + | + | NN | may_call_free (void *victim) + | | ^~~~~~~~~~~~~ + | | | + | | (3) entry to 'may_call_free' + | NN | { + | NN | if (some_condition ()) + | | ~ + | | | + | | (4) following 'false' branch... + |...... + | NN | free (victim); + | | ~~~~~~~~~~~~~ + | | | + | | (5) ...to here + | | (6) first 'free' here + | + <------+ + | + 'test': events 7-8 + | + | NN | may_call_free (ptr); + | | ^~~~~~~~~~~~~~~~~~~ + | | | + | | (7) returning to 'test' from 'may_call_free' + |...... + | NN | may_call_free (ptr); + | | ~~~~~~~~~~~~~~~~~~~ + | | | + | | (8) passing freed pointer 'ptr' in call to 'may_call_free' from 'test' + | + +--> 'may_call_free': events 9-12 + | + | NN | may_call_free (void *victim) + | | ^~~~~~~~~~~~~ + | | | + | | (9) entry to 'may_call_free' + | NN | { + | NN | if (some_condition ()) + | | ~ + | | | + | | (10) following 'false' branch... + |...... + | NN | free (victim); + | | ~~~~~~~~~~~~~ + | | | + | | (11) ...to here + | | (12) second 'free' here; first 'free' was at (6) + | + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-12.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-12.c new file mode 100644 index 0000000..fce5437 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-12.c @@ -0,0 +1,7 @@ +#include + +void recursive_free (void *ptr) +{ + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ + recursive_free (ptr); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-13.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-13.c new file mode 100644 index 0000000..2e3d80e --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-13.c @@ -0,0 +1,30 @@ +/* { dg-additional-options "-fanalyzer-verbosity=1" } */ + +#include + +void +calls_free (void *victim) +{ + free (victim); /* { dg-warning "double-'free' of 'victim'" } */ +} + +extern void do_stuff (void); + +struct foo +{ + void *m_p; +}; + +void test (struct foo f) +{ + do_stuff (); + + calls_free (f.m_p); + + do_stuff (); + + calls_free (f.m_p); /* { dg-message "passing freed pointer '' in call to 'calls_free' from 'test'" } */ + // TODO: something better than '' + + do_stuff (); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-2.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-2.c new file mode 100644 index 0000000..efeb94b --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-2.c @@ -0,0 +1,34 @@ +/* { dg-additional-options "-fanalyzer-verbosity=1" } */ + +#include + +void +calls_free (void *victim) /* { dg-message "\\(3\\) entry to 'calls_free'" } */ +/* { dg-message "\\(7\\) entry to 'calls_free'" "" { target *-*-* } .-1 } */ +{ + free (victim); /* { dg-warning "double-'free' of 'victim'" } */ + /* { dg-message "\\(4\\) first 'free' here" "" { target *-*-* } .-1 } */ + /* { dg-message "\\(8\\) second 'free' here; first 'free' was at \\(4\\)" "" { target *-*-* } .-2 } */ + + /* TODO: would this be better emitted at the callsite, + for such a simple wrapper? */ +} + +extern void do_stuff (void); + +void test (void *ptr) /* { dg-message "\\(1\\) entry to 'test'" } */ +{ + do_stuff (); + + calls_free (ptr); /* { dg-message "\\(2\\) calling 'calls_free' from 'test'" } */ + /* { dg-message "\\(5\\) returning to 'test' from 'calls_free'" "" { target *-*-* } .-1 } */ + + do_stuff (); + + calls_free (ptr); /* { dg-message "\\(6\\) passing freed pointer 'ptr' in call to 'calls_free' from 'test'" } */ + + do_stuff (); +} + + + diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-3.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-3.c new file mode 100644 index 0000000..3dcfae8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-3.c @@ -0,0 +1,23 @@ +#include + +void * +calls_malloc (void) +{ + void *result = malloc (1024); + return result; +} + +void +calls_free (void *victim) +{ + free (victim); /* { dg-warning "double-'free' of 'victim'" } */ + /* TODO: this would be better emitted at the callsite, + for such a simple wrapper. */ +} + +void test (void) +{ + void *ptr = calls_malloc (); + calls_free (ptr); + calls_free (ptr); /* BUG: double-'free'. */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-4.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-4.c new file mode 100644 index 0000000..82d50bd --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-4.c @@ -0,0 +1,13 @@ +#include + +static void calls_free(int *q) +{ + free(q); +} + +void test(void *p) +{ + calls_free(p); + + free(p); /* { dg-warning "double-'free' of 'p'" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-5.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-5.c new file mode 100644 index 0000000..c66ecb5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-5.c @@ -0,0 +1,13 @@ +#include + +static int *calls_malloc(void) +{ + return malloc(sizeof(int)); +} + +int *test(void) +{ + int *p = calls_malloc(); /* { dg-message "possible return of NULL to 'test' from 'calls_malloc'" } */ + *p = 42; /* { dg-warning "dereference of possibly-NULL 'p'" } */ + return p; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-6.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-6.c new file mode 100644 index 0000000..62f8b55 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-6.c @@ -0,0 +1,22 @@ +#include + +void * +calls_malloc (void) +{ + void *result = malloc (1024); /* { dg-message "allocated here" } */ + return result; /* { dg-warning "leak of 'result'" } */ +} + +void test_1 () +{ + calls_malloc (); /* { dg-message "calling 'calls_malloc' from 'test_1'" } */ +} + +static void callee (int i) +{ +} + +void test_2 (int i) +{ + callee (i); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-7.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-7.c new file mode 100644 index 0000000..0742370 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-7.c @@ -0,0 +1,29 @@ +#include + +/**************************************************************************/ + +static void maybe_calls_free_1(int *q, int flag) +{ + if (flag) + free(q); /* { dg-warning "double-'free' of 'q'" } */ +} + +void test_1(void *p) +{ + maybe_calls_free_1(p, 1); + maybe_calls_free_1(p, 1); +} + +/**************************************************************************/ + +static void maybe_calls_free_2(int *q, int flag) +{ + if (flag) + free(q); /* { dg-bogus "double-'free'" } */ +} + +void test_2(void *p) +{ + maybe_calls_free_2(p, 0); + maybe_calls_free_2(p, 0); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c new file mode 100644 index 0000000..a63e7e8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-double-free.c @@ -0,0 +1,172 @@ +/* Example of a multilevel wrapper around malloc/free, with a double-'free'. */ + +/* { dg-options "--analyzer -fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fanalyzer-checker=malloc -fanalyzer-verbose-state-changes -fdiagnostics-show-caret" } */ + +#include + +void *wrapped_malloc (size_t size) +{ + return malloc (size); +} + +void wrapped_free (void *ptr) +{ + free (ptr); /* { dg-warning "double-'free' of 'ptr' \\\[CWE-415\\\]" } */ +} + +typedef struct boxed_int +{ + int i; +} boxed_int; + +boxed_int * +make_boxed_int (int i) +{ + boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int)); + if (!result) + abort (); + result->i = i; + return result; +} + +void +free_boxed_int (boxed_int *bi) +{ + wrapped_free (bi); +} + +void test (int i) +{ + boxed_int *obj = make_boxed_int (i); + + free_boxed_int (obj); + + free_boxed_int (obj); +} + +/* double-'free'. */ +/* { dg-begin-multiline-output "" } + NN | free (ptr); + | ^~~~~~~~~~ + 'test': events 1-2 + | + | NN | void test (int i) + | | ^~~~ + | | | + | | (1) entry to 'test' + | NN | { + | NN | boxed_int *obj = make_boxed_int (i); + | | ~~~~~~~~~~~~~~~~~~ + | | | + | | (2) calling 'make_boxed_int' from 'test' + | + +--> 'make_boxed_int': events 3-6 + | + | NN | make_boxed_int (int i) + | | ^~~~~~~~~~~~~~ + | | | + | | (3) entry to 'make_boxed_int' + |...... + | NN | if (!result) + | | ~ + | | | + | | (4) following 'false' branch (when 'result' is non-NULL)... + | NN | abort (); + | NN | result->i = i; + | | ~~~~~~~~~~~~~ + | | | + | | (5) ...to here + | NN | return result; + | | ~~~~~~ + | | | + | | (6) state of '': 'start' -> 'nonnull' (origin: NULL) + | + <------+ + | + 'test': events 7-8 + | + | NN | boxed_int *obj = make_boxed_int (i); + | | ^~~~~~~~~~~~~~~~~~ + | | | + | | (7) returning to 'test' from 'make_boxed_int' + | NN | + | NN | free_boxed_int (obj); + | | ~~~~~~~~~~~~~~~~~~~~ + | | | + | | (8) calling 'free_boxed_int' from 'test' + | + +--> 'free_boxed_int': events 9-10 + | + | NN | free_boxed_int (boxed_int *bi) + | | ^~~~~~~~~~~~~~ + | | | + | | (9) entry to 'free_boxed_int' + | NN | { + | NN | wrapped_free (bi); + | | ~~~~~~~~~~~~~~~~~ + | | | + | | (10) calling 'wrapped_free' from 'free_boxed_int' + | + +--> 'wrapped_free': events 11-12 + | + | NN | void wrapped_free (void *ptr) + | | ^~~~~~~~~~~~ + | | | + | | (11) entry to 'wrapped_free' + | NN | { + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (12) first 'free' here (state of 'ptr': 'nonnull' -> 'freed', origin: NULL) + | + <------+ + | + 'free_boxed_int': event 13 + | + | NN | wrapped_free (bi); + | | ^~~~~~~~~~~~~~~~~ + | | | + | | (13) returning to 'free_boxed_int' from 'wrapped_free' + | + <------+ + | + 'test': events 14-15 + | + | NN | free_boxed_int (obj); + | | ^~~~~~~~~~~~~~~~~~~~ + | | | + | | (14) returning to 'test' from 'free_boxed_int' + | NN | + | NN | free_boxed_int (obj); + | | ~~~~~~~~~~~~~~~~~~~~ + | | | + | | (15) passing freed pointer 'obj' in call to 'free_boxed_int' from 'test' + | + +--> 'free_boxed_int': events 16-17 + | + | NN | free_boxed_int (boxed_int *bi) + | | ^~~~~~~~~~~~~~ + | | | + | | (16) entry to 'free_boxed_int' + | NN | { + | NN | wrapped_free (bi); + | | ~~~~~~~~~~~~~~~~~ + | | | + | | (17) passing freed pointer 'bi' in call to 'wrapped_free' from 'free_boxed_int' + | + +--> 'wrapped_free': events 18-19 + | + | NN | void wrapped_free (void *ptr) + | | ^~~~~~~~~~~~ + | | | + | | (18) entry to 'wrapped_free' + | NN | { + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (19) second 'free' here; first 'free' was at (12) ('ptr' is in state 'freed') + | + { dg-end-multiline-output "" } */ + +/* TODO: the event describing the allocation is uninteresting and probably + should be purged. */ diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c new file mode 100644 index 0000000..a3a3909 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-8-unchecked.c @@ -0,0 +1,66 @@ +/* Example of a multilevel wrapper around malloc, with an unchecked write. */ + +/* { dg-options "--analyzer -fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fanalyzer-checker=malloc -fdiagnostics-show-caret -fanalyzer-verbose-state-changes" } */ + +#include + +void *wrapped_malloc (size_t size) +{ + return malloc (size); +} + +typedef struct boxed_int +{ + int i; +} boxed_int; + +boxed_int * +make_boxed_int (int i) +{ + boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int)); + result->i = i; /* { dg-warning "dereference of possibly-NULL 'result'" } */ + return result; +} + +/* "dereference of possibly-NULL 'result' [CWE-690]". */ +/* { dg-begin-multiline-output "" } + NN | result->i = i; + | ~~~~~~~~~~^~~ + 'make_boxed_int': events 1-2 + | + | NN | make_boxed_int (int i) + | | ^~~~~~~~~~~~~~ + | | | + | | (1) entry to 'make_boxed_int' + | NN | { + | NN | boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int)); + | | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | | | + | | (2) calling 'wrapped_malloc' from 'make_boxed_int' + | + +--> 'wrapped_malloc': events 3-4 + | + | NN | void *wrapped_malloc (size_t size) + | | ^~~~~~~~~~~~~~ + | | | + | | (3) entry to 'wrapped_malloc' + | NN | { + | NN | return malloc (size); + | | ~~~~~~~~~~~~~ + | | | + | | (4) this call could return NULL (state of '': 'start' -> 'unchecked', origin: NULL) + | + <------+ + | + 'make_boxed_int': events 5-6 + | + | NN | boxed_int *result = (boxed_int *)wrapped_malloc (sizeof (boxed_int)); + | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | | | + | | (5) possible return of NULL to 'make_boxed_int' from 'wrapped_malloc' + | NN | result->i = i; + | | ~~~~~~~~~~~~~ + | | | + | | (6) 'result' could be NULL: unchecked value from (4) ('result' is in state 'unchecked') + | + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-9.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-9.c new file mode 100644 index 0000000..03bcbdb --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-9.c @@ -0,0 +1,18 @@ +/* { dg-options "--analyzer -fdiagnostics-path-format=none -fanalyzer-verbosity=1" } */ + +#include + +void +two_frees (void *p, void *q) +{ + free (p); + free (q); /* { dg-warning "double-'free' of 'q'" } */ + /* TODO: could be useful to identify that p == q when called from 'test'. */ +} + +extern void do_stuff (void); + +void test (void *ptr) +{ + two_frees (ptr, ptr); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c b/gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c new file mode 100644 index 0000000..a34b97b --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c @@ -0,0 +1,45 @@ +/* Test path-printing in the face of macros. */ + +/* { dg-options "--analyzer -fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */ + +#include "malloc-macro.h" + +/* { dg-warning "double-'free' of 'ptr'" "" { target *-*-* } 2 } */ + +int test (void *ptr) +{ + WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */ + WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */ + + /* Erase the spans indicating the header file + (to avoid embedding path assumptions). */ + /* { dg-regexp "\[^|\]+/malloc-macro.h:\[0-9\]+:\[0-9\]+:" } */ + /* { dg-regexp "\[^|\]+/malloc-macro.h:\[0-9\]+:\[0-9\]+:" } */ + + /* { dg-begin-multiline-output "" } + NN | #define WRAPPED_FREE(PTR) free(PTR) + | ^~~~~~~~~ + NN | WRAPPED_FREE (ptr); + | ^~~~~~~~~~~~ + 'test': event 1 + | + | + | NN | #define WRAPPED_FREE(PTR) free(PTR) + | | ^~~~~~~~~ + | | | + | | (1) first 'free' here + | NN | WRAPPED_FREE (ptr); + | | ^~~~~~~~~~~~ + | + 'test': event 2 + | + | + | NN | #define WRAPPED_FREE(PTR) free(PTR) + | | ^~~~~~~~~ + | | | + | | (2) second 'free' here; first 'free' was at (1) + | NN | WRAPPED_FREE (ptr); + | | ^~~~~~~~~~~~ + | + { dg-end-multiline-output "" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-macro-separate-events.c b/gcc/testsuite/gcc.dg/analyzer/malloc-macro-separate-events.c new file mode 100644 index 0000000..9c7e933 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-macro-separate-events.c @@ -0,0 +1,15 @@ +/* Test path-printing in the face of macros. */ + +/* { dg-options "--analyzer -fdiagnostics-path-format=separate-events" } */ + +#include "malloc-macro.h" + +/* { dg-warning "double-'free' of 'ptr'" "" { target *-*-* } 2 } */ +/* { dg-message "first 'free' here" "" { target *-*-* } 2 } */ +/* { dg-message "second 'free' here" "" { target *-*-* } 2 } */ + +int test (void *ptr) +{ + WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */ + WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-macro.h b/gcc/testsuite/gcc.dg/analyzer/malloc-macro.h new file mode 100644 index 0000000..8c05b40 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-macro.h @@ -0,0 +1,2 @@ +#include +#define WRAPPED_FREE(PTR) free(PTR) diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-1.c new file mode 100644 index 0000000..82ad547 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-1.c @@ -0,0 +1,14 @@ +#include + +/* Ensure that we don't need to laboriously walk every path to get + to the end of the function. */ + +int test_1 (int n) +{ + int i, j, k; + k = 0; + for (int i = 0; i < n; i++) + for (int j = 0; j < 1000; j++) + k++; + return k; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-2.c b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-2.c new file mode 100644 index 0000000..9f6440b --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-2.c @@ -0,0 +1,30 @@ +#include + +/* Ensure that we don't get an exponential growth in paths due to + repeated diamonds in the CFG. */ + +typedef struct obj { + int ob_refcnt; +} PyObject; + +extern void Py_Dealloc (PyObject *op); + +#define Py_DECREF(op) \ + do { \ + if (--((PyObject*)(op))->ob_refcnt == 0) \ + Py_Dealloc((PyObject *)(op)); \ + } while (0) + +int test (PyObject *obj_01, PyObject *obj_02, PyObject *obj_03, + PyObject *obj_04, PyObject *obj_05, PyObject *obj_06, + PyObject *obj_07, PyObject *obj_08, PyObject *obj_09, + PyObject *obj_10, PyObject *obj_11, PyObject *obj_12, + PyObject *obj_13, PyObject *obj_14, PyObject *obj_15 +) +{ + Py_DECREF (obj_01); Py_DECREF (obj_02); Py_DECREF (obj_03); + Py_DECREF (obj_04); Py_DECREF (obj_05); Py_DECREF (obj_06); + Py_DECREF (obj_07); Py_DECREF (obj_08); Py_DECREF (obj_09); + Py_DECREF (obj_10); Py_DECREF (obj_11); Py_DECREF (obj_12); + Py_DECREF (obj_13); Py_DECREF (obj_14); Py_DECREF (obj_15); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-3.c b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-3.c new file mode 100644 index 0000000..e5d27a4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-many-paths-3.c @@ -0,0 +1,36 @@ +#include + +extern int foo (void); + +int successes; +int failures; + +#define ONE_DIAMOND \ + do { \ + void *ptr = malloc (128); \ + if (foo ()) \ + successes++; \ + else \ + failures++; \ + free (ptr); \ + } while (0) + +#define TEN_DIAMONDS \ + do { \ + ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; \ + ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; ONE_DIAMOND; \ + } while (0) + +void test_3 (void *ptr) +{ + free (ptr); +#if 1 + ONE_DIAMOND; +#else + /* TODO: enabling this leads to numerous duplicated reports, + all of them detailing all the extraneous info about the malloc/free + within the diamonds. */ + TEN_DIAMONDS; +#endif + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-1.c new file mode 100644 index 0000000..5d989ea --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-1.c @@ -0,0 +1,15 @@ +/* Verify that we emit sane paths for state machine errors. */ + +#include + +void test_1 (void) +{ + void *ptr = malloc (1024); /* { dg-line malloc } */ + free (ptr); /* { dg-line first_free } */ + free (ptr); /* { dg-line second_free } */ + + /* { dg-warning "double-'free' of 'ptr'" "" { target *-*-* } second_free } */ + /* { dg-message "\\(1\\) allocated here" "" { target *-*-* } malloc } */ + /* { dg-message "\\(2\\) first 'free' here" "" { target *-*-* } first_free } */ + /* { dg-message "\\(3\\) second 'free' here; first 'free' was at \\(2\\)" "" { target *-*-* } second_free } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-10.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-10.c new file mode 100644 index 0000000..3eb5ffc --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-10.c @@ -0,0 +1,19 @@ +#include + +int test (int flag) +{ + int other_flag; + if (flag) + other_flag = 1; + else + other_flag = 0; + + /* With state-merging, we lose the relationship between 'flag' and 'other_flag'. */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + if (other_flag) + __analyzer_eval (flag); /* { dg-warning "UNKNOWN" } */ + else + __analyzer_eval (flag); /* { dg-warning "UNKNOWN" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-2.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-2.c new file mode 100644 index 0000000..a9bf7a9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-2.c @@ -0,0 +1,13 @@ +/* Verify that we emit sane paths for state machine errors. */ + +#include + +void test_2 (void *ptr) +{ + free (ptr); /* { dg-line first_free } */ + free (ptr); /* { dg-line second_free } */ + + /* { dg-warning "double-'free' of 'ptr'" "" { target *-*-* } second_free } */ + /* { dg-message "\\(1\\) first 'free' here" "" { target *-*-* } first_free } */ + /* { dg-message "\\(2\\) second 'free' here; first 'free' was at \\(1\\)" "" { target *-*-* } second_free } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-3.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-3.c new file mode 100644 index 0000000..ed60260 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-3.c @@ -0,0 +1,14 @@ +/* Verify that we emit sane paths for state machine errors. */ + +#include + +int *test_3 (void) +{ + int *ptr = (int *)malloc (sizeof (int)); /* { dg-line malloc } */ + *ptr = 42; /* { dg-line unchecked_deref } */ + return ptr; + + /* { dg-warning "dereference of possibly-NULL 'ptr'" "" { target *-*-* } unchecked_deref } */ + /* { dg-message "\\(1\\) this call could return NULL" "" { target *-*-* } malloc } */ + /* { dg-message "\\(2\\) 'ptr' could be NULL" "" { target *-*-* } unchecked_deref } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-4.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-4.c new file mode 100644 index 0000000..3385245 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-4.c @@ -0,0 +1,20 @@ +/* Verify that we emit sane paths for state machine errors. */ + +#include + +int *test_4 (void) +{ + int *ptr = (int *)malloc (sizeof (int)); /* { dg-line malloc } */ + if (ptr) /* { dg-line cond } */ + *ptr = 42; + else + *ptr = 43; /* { dg-line on_null_ptr } */ + return ptr; + + /* { dg-warning "dereference of NULL 'ptr'" "" { target *-*-* } on_null_ptr } */ + /* { dg-message "\\(1\\) allocated here" "" { target *-*-* } malloc } */ + /* { dg-message "\\(2\\) assuming 'ptr' is NULL" "" { target *-*-* } cond } */ + /* { dg-message "\\(3\\) following 'false' branch \\(when 'ptr' is NULL\\)\\.\\.\\." "" { target *-*-* } cond } */ + /* { dg-message "\\(4\\) \\.\\.\\.to here" "" { target *-*-* } on_null_ptr } */ + /* { dg-message "\\(5\\) dereference of NULL 'ptr'" "" { target *-*-* } on_null_ptr } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-5.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-5.c new file mode 100644 index 0000000..b54a81f --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-5.c @@ -0,0 +1,43 @@ +#include +#include + +extern void do_stuff (void); + +int test (const char *filename, int flag) +{ + FILE *f; + int *p, *q; + int i; + + p = (int *)malloc (sizeof (int)); /* { dg-line malloc_of_p } */ + if (!p) /* { dg-line test_of_p } */ + { + free (p); + return -1; + } + + q = (int *)malloc (sizeof (int)); /* { dg-line malloc_of_q } */ + if (!q) /* { dg-line test_of_q } */ + { + free (p); /* { dg-line first_free_of_p } */ + /* oops: forgot the "return" here, so it falls through. */ + } + + do_stuff (); + + free (p); /* { dg-line second_free_of_p } */ + free (q); + return 0; + + /* { dg-warning "double-'free' of 'p'" "" { target *-*-* } second_free_of_p } */ + /* { dg-message "\\(1\\) allocated here" "" { target *-*-* } malloc_of_p } */ + /* { dg-message "\\(2\\) assuming 'p' is non-NULL" "" { target *-*-* } test_of_p } */ + /* { dg-message "\\(3\\) following 'false' branch \\(when 'p' is non-NULL\\)\\.\\.\\." "" { target *-*-* } test_of_p } */ + /* { dg-message "\\(4\\) \\.\\.\\.to here" "" { target *-*-* } malloc_of_q } */ + /* { dg-message "\\(5\\) following 'true' branch \\(when 'q' is NULL\\)\\.\\.\\." "" { target *-*-* } test_of_q } */ + /* { dg-message "\\(6\\) \\.\\.\\.to here" "" { target *-*-* } first_free_of_p } */ + /* { dg-message "\\(7\\) first 'free' here" "" { target *-*-* } first_free_of_p } */ + /* { dg-message "\\(8\\) second 'free' here; first 'free' was at \\(7\\)" "" { target *-*-* } second_free_of_p } */ + + /* We don't care about the state changes to q. */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-6.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-6.c new file mode 100644 index 0000000..1df6964 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-6.c @@ -0,0 +1,11 @@ +#include + +void test (void *ptr) +{ + void *q; + q = ptr; + free (ptr); + free (q); /* { dg-warning "double-'free' of 'q'" } */ + /* The above case requires us to handle equivalence classes in + state transitions. */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-7.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-7.c new file mode 100644 index 0000000..eb55604 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-7.c @@ -0,0 +1,21 @@ +#include + +extern int foo (void); +extern int bar (void); + +void test (void) +{ + void *p = malloc (1024); /* { dg-message "\\(1\\) allocated here" } */ + void *q = malloc (1024); + + foo (); + if (!q) /* { dg-message "\\(2\\) following 'true' branch \\(when 'q' is NULL\\)\\.\\.\\." } */ + { + free (q); /* { dg-message "\\(3\\) \\.\\.\\.to here" } */ + return; /* { dg-warning "leak of 'p'" } */ + /* { dg-message "\\(4\\) 'p' leaks here; was allocated at \\(1\\)" "" { target *-*-* } .-1 } */ + } + bar (); + free (q); + free (p); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c new file mode 100644 index 0000000..bf858e0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-8.c @@ -0,0 +1,54 @@ +/* { dg-additional-options "-fanalyzer-transitivity" } */ + +#include +#include + +extern void do_stuff (const void *); + +#define LIMIT 1024 + +void test_1 (size_t sz) +{ + void *ptr; + if (sz >= LIMIT) + ptr = malloc (sz); + else + ptr = alloca (sz); + + do_stuff (ptr); + + if (sz >= LIMIT) + free (ptr); +} + +void test_2 (size_t sz) +{ + void *ptr; + if (sz < LIMIT) + ptr = alloca (sz); + else + ptr = malloc (sz); + + do_stuff (ptr); + + if (sz >= LIMIT) + free (ptr); +} + +void test_3 (size_t sz) +{ + void *ptr; + if (sz <= LIMIT) + ptr = alloca (sz); /* { dg-message "memory is allocated on the stack here" } */ + else + ptr = malloc (sz); + + do_stuff (ptr); + + /* Bug: the "sz <= LIMIT" above should have been "sz < LIMIT", + so there's a free-of-alloca when sz == LIMIT. */ + if (sz >= LIMIT) + free (ptr); /* { dg-warning "'free' of memory allocated on the stack by 'alloca'" } */ +} +/* { dg-bogus "leak of 'ptr'" } */ +/* This can't happen, as "sz > 1024" && "sz <= 1023" is impossible. */ diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-paths-9.c b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-9.c new file mode 100644 index 0000000..6bee885 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-paths-9.c @@ -0,0 +1,298 @@ +/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */ + +#include + +void test_1 (void) +{ + void *ptr = malloc (1024); + free (ptr); + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ +} +/* { dg-begin-multiline-output "" } + NN | free (ptr); + | ^~~~~~~~~~ + 'test_1': events 1-3 + | + | NN | void *ptr = malloc (1024); + | | ^~~~~~~~~~~~~ + | | | + | | (1) allocated here + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (2) first 'free' here + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (3) second 'free' here; first 'free' was at (2) + | + { dg-end-multiline-output "" } */ + +void test_2 (int x, int y) +{ + void *ptr = malloc (1024); + if (x) + free (ptr); + if (y) + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ +} /* { dg-warning "leak of 'ptr'" } */ + +/* "double-'free' of 'ptr'". */ +/* { dg-begin-multiline-output "" } + NN | free (ptr); + | ^~~~~~~~~~ + 'test_2': events 1-7 + | + | NN | void *ptr = malloc (1024); + | | ^~~~~~~~~~~~~ + | | | + | | (1) allocated here + | NN | if (x) + | | ~ + | | | + | | (2) following 'true' branch (when 'x != 0')... + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (3) ...to here + | | (4) first 'free' here + | NN | if (y) + | | ~ + | | | + | | (5) following 'true' branch (when 'y != 0')... + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (6) ...to here + | | (7) second 'free' here; first 'free' was at (4) + | + { dg-end-multiline-output "" } */ + +/* "leak of 'ptr'. */ +/* { dg-begin-multiline-output "" } + NN | } + | ^ + 'test_2': events 1-6 + | + | NN | void *ptr = malloc (1024); + | | ^~~~~~~~~~~~~ + | | | + | | (1) allocated here + | NN | if (x) + | | ~ + | | | + | | (2) following 'false' branch (when 'x == 0')... + | NN | free (ptr); + | NN | if (y) + | | ~ + | | | + | | (3) ...to here + | | (4) following 'false' branch (when 'y == 0')... + | NN | free (ptr); + | NN | } + | | ~ + | | | + | | (5) ...to here + | | (6) 'ptr' leaks here; was allocated at (1) + | + { dg-end-multiline-output "" } */ + +int test_3 (int x, int y) +{ + int *ptr = (int *)malloc (sizeof (int)); + *ptr = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */ + if (x) + free (ptr); + + *ptr = 19; /* { dg-warning "use after 'free' of 'ptr'" } */ + // TODO: two warnings here: one is from sm-malloc, the other from region model + + if (y) + free (ptr); /* No double-'free' warning: we've already attempted + to dereference it above. */ + return *ptr; /* { dg-warning "use after 'free' of 'ptr'" } */ + // TODO: two warnings here: one is from sm-malloc, the other from region model + /* { dg-warning "leak of 'ptr'" "" { target *-*-* } .-2 } */ +} + +/* "dereference of possibly-NULL 'ptr'". */ +/* { dg-begin-multiline-output "" } + NN | *ptr = 42; + | ~~~~~^~~~ + 'test_3': events 1-2 + | + | NN | int *ptr = (int *)malloc (sizeof (int)); + | | ^~~~~~~~~~~~~~~~~~~~~ + | | | + | | (1) this call could return NULL + | NN | *ptr = 42; + | | ~~~~~~~~~ + | | | + | | (2) 'ptr' could be NULL: unchecked value from (1) + | + { dg-end-multiline-output "" } */ + +/* "use after 'free' of 'ptr'". */ +/* { dg-begin-multiline-output "" } + NN | *ptr = 19; + | ~~~~~^~~~ + 'test_3': events 1-6 + | + | NN | int *ptr = (int *)malloc (sizeof (int)); + | | ^~~~~~~~~~~~~~~~~~~~~ + | | | + | | (1) allocated here + | NN | *ptr = 42; + | | ~~~~~~~~~ + | | | + | | (2) assuming 'ptr' is non-NULL + | NN | if (x) + | | ~ + | | | + | | (3) following 'true' branch (when 'x != 0')... + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (4) ...to here + | | (5) freed here + | NN | + | NN | *ptr = 19; + | | ~~~~~~~~~ + | | | + | | (6) use after 'free' of 'ptr'; freed at (5) + | + { dg-end-multiline-output "" } */ + +/* "use after 'free' of 'ptr'". */ +/* { dg-begin-multiline-output "" } + NN | return *ptr; + | ^~~~ + 'test_3': events 1-8 + | + | NN | int *ptr = (int *)malloc (sizeof (int)); + | | ^~~~~~~~~~~~~~~~~~~~~ + | | | + | | (1) allocated here + | NN | *ptr = 42; + | | ~~~~~~~~~ + | | | + | | (2) assuming 'ptr' is non-NULL + | NN | if (x) + | | ~ + | | | + | | (3) following 'false' branch (when 'x == 0')... + |...... + | NN | *ptr = 19; + | | ~~~~~~~~~ + | | | + | | (4) ...to here + |...... + | NN | if (y) + | | ~ + | | | + | | (5) following 'true' branch (when 'y != 0')... + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (6) ...to here + | | (7) freed here + | NN | + | NN | return *ptr; + | | ~~~~ + | | | + | | (8) use after 'free' of 'ptr'; freed at (7) + | + { dg-end-multiline-output "" } */ + +/* "leak of 'ptr'". */ +/* { dg-begin-multiline-output "" } + NN | return *ptr; + | ^~~~ + 'test_3': events 1-7 + | + | NN | int *ptr = (int *)malloc (sizeof (int)); + | | ^~~~~~~~~~~~~~~~~~~~~ + | | | + | | (1) allocated here + | NN | *ptr = 42; + | | ~~~~~~~~~ + | | | + | | (2) assuming 'ptr' is non-NULL + | NN | if (x) + | | ~ + | | | + | | (3) following 'false' branch (when 'x == 0')... + |...... + | NN | *ptr = 19; + | | ~~~~~~~~~ + | | | + | | (4) ...to here + |...... + | NN | if (y) + | | ~ + | | | + | | (5) following 'false' branch (when 'y == 0')... + |...... + | NN | return *ptr; + | | ~~~~ + | | | + | | (6) ...to here + | | (7) 'ptr' leaks here; was allocated at (1) + | + { dg-end-multiline-output "" } */ + +/* "use after 'free' of 'ptr'". */ +/* { dg-begin-multiline-output "" } + NN | *ptr = 19; + | ~~~~~^~~~ + 'test_3': events 1-3 + | + | NN | if (x) + | | ^ + | | | + | | (1) following 'true' branch (when 'x != 0')... + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (2) ...to here + | NN | + | NN | *ptr = 19; + | | ~~~~~~~~~ + | | | + | | (3) use after 'free' of 'ptr' here + | + { dg-end-multiline-output "" } */ + +/* "use after 'free' of 'ptr'". */ +/* { dg-begin-multiline-output "" } + NN | return *ptr; + | ^~~~ + 'test_3': events 1-5 + | + | NN | if (x) + | | ^ + | | | + | | (1) following 'false' branch (when 'x == 0')... + |...... + | NN | *ptr = 19; + | | ~~~~~~~~~ + | | | + | | (2) ...to here + |...... + | NN | if (y) + | | ~ + | | | + | | (3) following 'true' branch (when 'y != 0')... + | NN | free (ptr); + | | ~~~~~~~~~~ + | | | + | | (4) ...to here + | NN | to dereference it above + | NN | return *ptr; + | | ~~~~ + | | | + | | (5) use after 'free' of 'ptr' here + | + { dg-end-multiline-output "" } */ +/* TODO: this is really a duplicate; can we either eliminate it, or + improve the path? */ diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1a.c b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1a.c new file mode 100644 index 0000000..d7dfb06 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1a.c @@ -0,0 +1,180 @@ +/* { dg-additional-options "-fno-analyzer-call-summaries -fanalyzer-transitivity" } */ + +#include + +extern int foo (int); + +static int __attribute__((noinline)) +do_stuff (int *p, int n) +{ + int sum = 0; + int i; + for (i = 0; i < n; i++) + p[i] = i; + for (i = 0; i < n; i++) + sum += foo (p[i]); /* { dg-bogus "uninitialized" } */ + return sum; +} + +static int __attribute__((noinline)) +do_stuff_2 (int *p, int n) +{ + return 0; +} + +/* Various examples of functions that use either a malloc buffer + or a local buffer, do something, then conditionally free the + buffer, tracking whether "free" is necessary in various + ways. + + In each case, there ought to be only two paths through the function, + not four. */ + +/* Repeated (n > 10) predicate. */ + +int test_repeated_predicate_1 (int n) +{ + int buf[10]; + int *ptr; + int result; + + if (n > 10) + ptr = (int *)malloc (sizeof (int) * n); + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + result = do_stuff (ptr, n); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + // FIXME: why 3 here? + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + // FIXME: why 3 here? + + if (n > 10) + free (ptr); /* { dg-bogus "not on the heap" } */ + + return result; /* { dg-bogus "leak" } */ +} + +/* A simpler version of the above. */ + +int test_repeated_predicate_2 (int n) +{ + int buf[10]; + int *ptr; + int result; + + if (n > 10) + ptr = (int *)malloc (sizeof (int) * n); + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + result = do_stuff_2 (ptr, n); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + if (n > 10) + free (ptr); /* { dg-bogus "not on the heap" } */ + + return result; /* { dg-bogus "leak" } */ +} + +/* A predicate that sets a flag for the 2nd test. */ + +int test_explicit_flag (int n) +{ + int buf[10]; + int *ptr; + int result; + int need_to_free = 0; + + if (n > 10) + { + ptr = (int *)malloc (sizeof (int) * n); + need_to_free = 1; + } + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + result = do_stuff (ptr, n); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + // FIXME: why 3 here? + + if (need_to_free) + free (ptr); /* { dg-bogus "not on the heap" } */ + + return result; /* { dg-bogus "leak" } */ +} + +/* Pointer comparison. */ + +int test_pointer_comparison (int n) +{ + int buf[10]; + int *ptr; + int result; + + if (n > 10) + ptr = (int *)malloc (sizeof (int) * n); + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + result = do_stuff (ptr, n); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + // FIXME: why 3 here? + + if (ptr != buf) + free (ptr); /* { dg-bogus "not on the heap" } */ + + return result; /* { dg-bogus "leak" } */ +} + +/* Set a flag based on a conditional, then use it, then reuse the + conditional. */ + +int test_initial_flag (int n) +{ + int buf[10]; + int *ptr; + int result; + int on_heap = 0; + + if (n > 10) + on_heap = 1; + else + on_heap = 0; + + /* Due to state-merging, we lose the relationship between 'n > 10' + and 'on_heap' here; we have to rely on feasibility-checking + in the diagnostic_manager to reject the false warnings. */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + if (on_heap) + ptr = (int *)malloc (sizeof (int) * n); + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + result = do_stuff (ptr, n); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "5 exploded nodes" } */ + // FIXME: why 5 here? + + if (n > 10) + free (ptr); /* { dg-bogus "not on the heap" } */ + + return result; /* { dg-bogus "leak" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1b.c b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1b.c new file mode 100644 index 0000000..7f1dd91 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-1b.c @@ -0,0 +1,175 @@ +/* { dg-additional-options "-fanalyzer-call-summaries" } */ + +#include + +extern int foo (int); + +static int __attribute__((noinline)) +do_stuff (int *p, int n) +{ + int sum = 0; + int i; + for (i = 0; i < n; i++) + p[i] = i; + for (i = 0; i < n; i++) + sum += foo (p[i]); /* { dg-bogus "uninitialized" } */ + return sum; +} + +static int __attribute__((noinline)) +do_stuff_2 (int *p, int n) +{ + return 0; +} + +/* Various examples of functions that use either a malloc buffer + or a local buffer, do something, then conditionally free the + buffer, tracking whether "free" is necessary in various + ways. + + In each case, there ought to be only two paths through the function, + not four. */ + +/* Repeated (n > 10) predicate. */ + +int test_repeated_predicate_1 (int n) +{ + int buf[10]; + int *ptr; + int result; + + if (n > 10) + ptr = (int *)malloc (sizeof (int) * n); + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + result = do_stuff (ptr, n); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + if (n > 10) + free (ptr); /* { dg-bogus "not on the heap" } */ + + return result; /* { dg-bogus "leak" } */ +} + +/* A simpler version of the above. */ + +int test_repeated_predicate_2 (int n) +{ + int buf[10]; + int *ptr; + int result; + + if (n > 10) + ptr = (int *)malloc (sizeof (int) * n); + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + result = do_stuff_2 (ptr, n); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + if (n > 10) + free (ptr); /* { dg-bogus "not on the heap" } */ + + return result; /* { dg-bogus "leak" } */ +} + +/* A predicate that sets a flag for the 2nd test. */ + +int test_explicit_flag (int n) +{ + int buf[10]; + int *ptr; + int result; + int need_to_free = 0; + + if (n > 10) + { + ptr = (int *)malloc (sizeof (int) * n); + need_to_free = 1; + } + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + result = do_stuff (ptr, n); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + if (need_to_free) + free (ptr); /* { dg-bogus "not on the heap" } */ + + return result; /* { dg-bogus "leak" } */ +} + +/* Pointer comparison. */ + +int test_pointer_comparison (int n) +{ + int buf[10]; + int *ptr; + int result; + + if (n > 10) + ptr = (int *)malloc (sizeof (int) * n); + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + result = do_stuff (ptr, n); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + if (ptr != buf) + free (ptr); /* { dg-bogus "not on the heap" } */ + + return result; /* { dg-bogus "leak" } */ +} + +/* Set a flag based on a conditional, then use it, then reuse the + conditional. */ + +int test_initial_flag (int n) +{ + int buf[10]; + int *ptr; + int result; + int on_heap = 0; + + if (n > 10) + on_heap = 1; + else + on_heap = 0; + + /* Due to state-merging, we lose the relationship between 'n > 10' + and 'on_heap' here; we have to rely on feasibility-checking + in the diagnostic_manager to reject the false warnings. */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + if (on_heap) + ptr = (int *)malloc (sizeof (int) * n); + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + result = do_stuff (ptr, n); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + if (n > 10) + free (ptr); /* { dg-bogus "not on the heap" } */ + + return result; /* { dg-bogus "leak" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-2.c b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-2.c new file mode 100644 index 0000000..0307872 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-2.c @@ -0,0 +1,178 @@ +#include + +extern int foo (int); + +static int __attribute__((noinline)) +do_stuff_2 (int *p, int n) +{ + return 0; +} + +/* As malloc-vs-local.c, but hand-inlining the logic. */ + +/* Repeated (n > 10) predicate. */ + +int test_repeated_predicate_1 (int n) +{ + int buf[10]; + int *ptr; + int result; + + if (n > 10) + ptr = (int *)malloc (sizeof (int) * n); + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + { + int *p = ptr; + int sum = 0; + int i; + for (i = 0; i < n; i++) + p[i] = i; + for (i = 0; i < n; i++) + sum += foo (p[i]); /* { dg-bogus "uninitialized" } */ + result = sum; + } + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + + if (n > 10) + free (ptr); /* { dg-bogus "not on the heap" } */ + + return result; /* { dg-bogus "leak" } */ +} + +/* As above, but with just one loop. */ + +int test_repeated_predicate_1a (int n) +{ + int buf[10]; + int *ptr; + int result; + + if (n > 10) + ptr = (int *)malloc (sizeof (int) * n); + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + { + int *p = ptr; + int sum = 0; + int i; + for (i = 0; i < n; i++) + p[i] = i; + result = sum; + } + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + + if (n > 10) + free (ptr); /* { dg-bogus "not on the heap" } */ + + return result; /* { dg-bogus "leak" } */ +} + +/* A simpler version of the above. */ + +int test_repeated_predicate_2 (int n) +{ + int buf[10]; + int *ptr; + int result; + + if (n > 10) + ptr = (int *)malloc (sizeof (int) * n); + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + result = do_stuff_2 (ptr, n); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + if (n > 10) + free (ptr); /* { dg-bogus "not on the heap" } */ + + return result; /* { dg-bogus "leak" } */ +} + +/* A predicate that sets a flag for the 2nd test. */ + +int test_explicit_flag (int n) +{ + int buf[10]; + int *ptr; + int result; + int need_to_free = 0; + + if (n > 10) + { + ptr = (int *)malloc (sizeof (int) * n); + need_to_free = 1; + } + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + { + int *p = ptr; + int sum = 0; + int i; + for (i = 0; i < n; i++) + p[i] = i; + for (i = 0; i < n; i++) + sum += foo (p[i]); /* { dg-bogus "uninitialized" } */ + result = sum; + } + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + + if (need_to_free) + free (ptr); /* { dg-bogus "not on the heap" } */ + + return result; /* { dg-bogus "leak" } */ +} + +/* Pointer comparison. */ + +int test_pointer_comparison (int n) +{ + int buf[10]; + int *ptr; + int result; + + if (n > 10) + ptr = (int *)malloc (sizeof (int) * n); + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + { + int *p = ptr; + int sum = 0; + int i; + for (i = 0; i < n; i++) + p[i] = i; + for (i = 0; i < n; i++) + sum += foo (p[i]); /* { dg-bogus "uninitialized" } */ + result = sum; + } + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + + if (ptr != buf) + free (ptr); /* { dg-bogus "not on the heap" } */ + + return result; /* { dg-bogus "leak" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-3.c b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-3.c new file mode 100644 index 0000000..2a0c8a4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-3.c @@ -0,0 +1,65 @@ +#include + +extern int foo (int); + +static int __attribute__((noinline)) +do_stuff_2 (int *p, int n) +{ + return 0; +} + +/* As malloc-vs-local-2.c, but with a memory leak for the "on the heap case" + by not attempting to free at the end. */ + +int test_1 (int n) +{ + int buf[10]; + int *ptr; + int result; + + if (n > 10) + ptr = (int *)malloc (sizeof (int) * n); + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + { + int *p = ptr; + int sum = 0; + int i; + for (i = 0; i < n; i++) + p[i] = i; + for (i = 0; i < n; i++) + sum += foo (p[i]); /* { dg-bogus "uninitialized" } */ + result = sum; + } + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + + return result; /* { dg-message "leak of 'p'" } */ + /* FIXME: should this be 'ptr'? */ +} + +/* A simpler version of the above. */ + +int test_2 (int n) +{ + int buf[10]; + int *ptr; + int result; + + if (n > 10) + ptr = (int *)malloc (sizeof (int) * n); + else + ptr = buf; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + result = do_stuff_2 (ptr, n); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + return result; /* { dg-message "leak of 'ptr'" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-4.c b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-4.c new file mode 100644 index 0000000..7410a71 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/malloc-vs-local-4.c @@ -0,0 +1,40 @@ +#include + +void __attribute__((noinline)) callee_1 (int *ptr) +{ + *ptr = 42; /* { dg-warning "dereference of possibly-NULL 'ptr'" } */ +} + +int test_1 (int i, int flag) +{ + /* Double diamond CFG; either use &i, or a malloc-ed buffer. */ + int *ptr = &i; + if (flag) + ptr = (int *)malloc (sizeof (int)); + callee_1 (ptr); + if (flag) + free (ptr); + return i; +} + +void __attribute__((noinline)) callee_2 (int *ptr) +{ + *ptr = 42; +} + +int test_2 (int flag) +{ + int i; + + if (flag) + callee_2 (&i); + + callee_2 (&i); + + if (!flag) + { + void *ptr = malloc (16); + free (ptr); + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ + } +} diff --git a/gcc/testsuite/gcc.dg/analyzer/operations.c b/gcc/testsuite/gcc.dg/analyzer/operations.c new file mode 100644 index 0000000..9ea2327 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/operations.c @@ -0,0 +1,42 @@ +void test (int i, int j) +{ + int k, m; + + if (i > 42) { + __analyzer_eval (i > 42); /* { dg-warning "TRUE" } */ + + i += 3; + + __analyzer_eval (i > 45); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + /* TODO(xfail): do we really know this? what about overflow? */ + + i -= 1; + + __analyzer_eval (i > 44); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + /* TODO(xfail): do we really know this? what about overflow? */ + + i = 3 * i; + + __analyzer_eval (i > 132); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + /* TODO(xfail): do we really know this? what about overflow? */ + + i /= 2; + + __analyzer_eval (i > 66); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + /* TODO(xfail): do we really know this? what about overflow? */ + + /* We don't know anything about j, so we don't know anything about k: */ + k = i + j; + __analyzer_eval (k == 0); /* { dg-warning "UNKNOWN" } */ + + /* However, we should now know that m > 67: */ + m = i + 1; + __analyzer_eval (m > 67); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + /* TODO(xfail): do we really know this? what about overflow? */ + } +} diff --git a/gcc/testsuite/gcc.dg/analyzer/params-2.c b/gcc/testsuite/gcc.dg/analyzer/params-2.c new file mode 100644 index 0000000..1584ebd --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/params-2.c @@ -0,0 +1,16 @@ +#include + +static void ensure_equal (int a, int b) +{ + if (a != b) + abort (); +} + +void test(int i, int j) +{ + __analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */ + + ensure_equal (i, j); + + __analyzer_eval (i == j); /* { dg-warning "TRUE" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/params.c b/gcc/testsuite/gcc.dg/analyzer/params.c new file mode 100644 index 0000000..ff405f2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/params.c @@ -0,0 +1,32 @@ +static int called_function(int j) +{ + int k; + + __analyzer_eval (j > 4); /* { dg-warning "TRUE" } */ + + k = j - 1; + + __analyzer_eval (k > 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + /* TODO(xfail): we're not then updating based on the assignment. */ + + return k; +} + +void test(int i) +{ + __analyzer_eval (i > 4); /* { dg-warning "UNKNOWN" } */ + + if (i > 4) { + + __analyzer_eval (i > 4); /* { dg-warning "TRUE" } */ + + i = called_function(i); + + __analyzer_eval (i > 3); /* { dg-warning "TRUE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + /* TODO(xfail): we're not updating from the returned value. */ + } + + __analyzer_eval (i > 3); /* { dg-warning "UNKNOWN" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-1.c b/gcc/testsuite/gcc.dg/analyzer/paths-1.c new file mode 100644 index 0000000..246f6cd --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/paths-1.c @@ -0,0 +1,16 @@ +struct foo +{ + int m_flag; +}; + +extern void bar (int); + +void test (struct foo *pf) +{ + if (pf->m_flag) + bar (0); + else + bar (1); + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-1a.c b/gcc/testsuite/gcc.dg/analyzer/paths-1a.c new file mode 100644 index 0000000..a6e2679 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/paths-1a.c @@ -0,0 +1,16 @@ +union foo +{ + int m_flag; +}; + +extern void bar (int); + +void test (union foo *pf) +{ + if (pf->m_flag) + bar (0); + else + bar (1); + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-2.c b/gcc/testsuite/gcc.dg/analyzer/paths-2.c new file mode 100644 index 0000000..8637c8c --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/paths-2.c @@ -0,0 +1,25 @@ +int test (int a) +{ + if (a != 42 && a != 113) { + return (-2); + } + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + return 0; +} + +int test_2 (int a) +{ + if (a != 42 && a != 113 && a != 666) { + return (-2); + } + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 exploded nodes" } */ + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-3.c b/gcc/testsuite/gcc.dg/analyzer/paths-3.c new file mode 100644 index 0000000..d03ab02 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/paths-3.c @@ -0,0 +1,48 @@ +/* { dg-additional-options "-fanalyzer-transitivity" } */ + +#include + +int test_1 (int a, int b) +{ + void *p; + + if (a > 5) + if (b) + p = malloc (16); + else + p = malloc (32); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + if (a > 5) + { + free (p); + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + } + + return 0; /* { dg-bogus "leak" } */ +} + +int test_2 (int a, int b) +{ + void *p; + + if (a > 5) + if (b) + p = malloc (16); + else + p = malloc (32); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + if (a > 6) /* different condition */ + { + free (p); + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + } + + return 0; /* { dg-warning "leak of 'p'" } */ + /* leaks when a == 5. */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-4.c b/gcc/testsuite/gcc.dg/analyzer/paths-4.c new file mode 100644 index 0000000..307bc97 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/paths-4.c @@ -0,0 +1,49 @@ +struct state +{ + int mode; + int data; +}; + +extern void do_stuff (struct state *, int); + +int test_1 (struct state *s) +{ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + while (1) + { + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + do_stuff (s, s->mode); + } +} + +int test_2 (struct state *s) +{ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + while (1) + { + __analyzer_dump_exploded_nodes (0); /* { dg-warning "3 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + switch (s->mode) + { + case 0: + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + do_stuff (s, 0); + break; + case 1: + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + do_stuff (s, 17); + break; + case 2: + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + do_stuff (s, 5); + break; + case 3: + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + return 42; + case 4: + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + return -3; + } + } +} diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-5.c b/gcc/testsuite/gcc.dg/analyzer/paths-5.c new file mode 100644 index 0000000..d3589b5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/paths-5.c @@ -0,0 +1,10 @@ +void test (int *p, int n) +{ + int i; + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + for (i = 0; i < n; i++) + { + p[i] = i; /* { dg-bogus "uninitialized" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + } +} diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-6.c b/gcc/testsuite/gcc.dg/analyzer/paths-6.c new file mode 100644 index 0000000..bf4b6f3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/paths-6.c @@ -0,0 +1,118 @@ +#include + +/* Verify that ordering of writes doesn't matter when merging states. */ + +/* Test with locals. */ + +void test_1 (int flag) +{ + int a, b; + if (flag) + { + a = 3; + b = 4; + } + else + { + b = 4; + a = 3; + } + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + // FIXME: the above can vary between 2 and 3 exploded nodes + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + __analyzer_eval (a == 3); /* { dg-warning "TRUE" } */ + __analyzer_eval (b == 4); /* { dg-warning "TRUE" } */ +} + +/* Test with globals. */ + +int f, g, h; +void test_2 (int flag) +{ + if (flag) + { + f = 3; + g = 4; + } + else + { + g = 4; + f = 3; + } + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + __analyzer_eval (f == 3); /* { dg-warning "TRUE" } */ + __analyzer_eval (g == 4); /* { dg-warning "TRUE" } */ +} + +/* All 6 orderings of writes to 3 globals. */ + +void test_3 (int i) +{ + switch (i) + { + default: + case 0: + f = 3; + g = 4; + h = 5; + break; + + case 1: + f = 3; + h = 5; + g = 4; + break; + + case 2: + g = 4; + f = 3; + h = 5; + break; + + case 3: + g = 4; + h = 5; + f = 3; + break; + + case 4: + h = 5; + f = 3; + g = 4; + break; + + case 5: + h = 5; + g = 4; + f = 3; + break; + } + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "6 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded node" } */ + __analyzer_eval (f == 3); /* { dg-warning "TRUE" } */ + __analyzer_eval (g == 4); /* { dg-warning "TRUE" } */ + __analyzer_eval (h == 5); /* { dg-warning "TRUE" } */ +} + +void test_4 (int flag) +{ + void *p, *q; + if (flag) + { + p = malloc (256); + q = malloc (256); + } + else + { + q = malloc (256); + p = malloc (256); + } + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + free (p); + free (q); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/paths-7.c b/gcc/testsuite/gcc.dg/analyzer/paths-7.c new file mode 100644 index 0000000..7a0826d --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/paths-7.c @@ -0,0 +1,58 @@ +#include + +extern int foo (int); + +int test (int flag, void *ptr, int *p, int n) +{ + int result; + int sum = 0; + int i; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + if (flag) + free (ptr); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + for (i = 0; i < n; i++) + p[i] = i; + for (i = 0; i < n; i++) + sum += foo (p[i]); /* { dg-bogus "uninitialized" } */ + result = sum; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + if (flag) + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ + return result; +} + +int test_2 (int flag, int *p, int n) +{ + int result; + int sum = 0; + int i; + + void *ptr = malloc (16); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + if (flag) + free (ptr); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + + for (i = 0; i < n; i++) + p[i] = i; + for (i = 0; i < n; i++) + sum += foo (p[i]); /* { dg-bogus "uninitialized" } */ + result = sum; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "5 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "5 exploded nodes" } */ + // FIXME: why 5 here? + + free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */ + return result; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c b/gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c new file mode 100644 index 0000000..8a1ca58 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c @@ -0,0 +1,28 @@ +/* { dg-additional-options "-fanalyzer-checker=pattern-test" } */ + +#include + +extern void foo(void *); +extern void bar(void *); + +void test1(void *ptr) +{ + if (ptr) { /* { dg-warning "pattern match on 'ptr != 0'" } */ + /* { dg-warning "pattern match on 'ptr == 0'" "" { target *-*-* } .-1 } */ + foo(ptr); + } else { + bar(ptr); + } +} + +void test_2 (void *p, void *q) +{ + if (p == NULL || q == NULL) /* { dg-line cond_2 } */ + return; + foo(p); + + /* { dg-warning "pattern match on 'p == 0'" "" { target *-*-* } cond_2 } */ + /* { dg-warning "pattern match on 'q == 0'" "" { target *-*-* } cond_2 } */ + /* { dg-warning "pattern match on 'p != 0'" "" { target *-*-* } cond_2 } */ + /* { dg-warning "pattern match on 'q != 0'" "" { target *-*-* } cond_2 } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c b/gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c new file mode 100644 index 0000000..3924900 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c @@ -0,0 +1,29 @@ +/* { dg-additional-options "-fanalyzer-checker=pattern-test -O2" } */ +// TODO: run this at every optimization level + +#include + +extern void foo(void *); +extern void bar(void *); + +void test1(void *ptr) +{ + if (ptr) { /* { dg-warning "pattern match on 'ptr != 0'" } */ + /* { dg-warning "pattern match on 'ptr == 0'" "" { target *-*-* } .-1 } */ + foo(ptr); + } else { + bar(ptr); + } +} + +void test_2 (void *p, void *q) +{ + if (p == NULL || q == NULL) /* { dg-line cond_2 } */ + return; + foo(p); + + /* { dg-warning "pattern match on ' == 0'" "" { target *-*-* } cond_2 } */ + /* { dg-warning "pattern match on ' != 0'" "" { target *-*-* } cond_2 } */ + /* { dg-warning "pattern match on 'p != 0'" "" { target *-*-* } cond_2 } */ + /* { dg-warning "pattern match on 'q != 0'" "" { target *-*-* } cond_2 } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/pointer-merging.c b/gcc/testsuite/gcc.dg/analyzer/pointer-merging.c new file mode 100644 index 0000000..dcf0ff0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/pointer-merging.c @@ -0,0 +1,16 @@ +static char * __attribute__((noinline)) +test_1_callee (int flag, char *a, char *b) +{ + char *p; + if (flag) + p = a; + else + p = b; + return p; +} + +char test_1_caller(int flag) { + char a = 42; + char b = 43; + return *test_1_callee(flag, &a, &b); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/pr61861.c b/gcc/testsuite/gcc.dg/analyzer/pr61861.c new file mode 100644 index 0000000..a85e743 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/pr61861.c @@ -0,0 +1,2 @@ +/* { dg-additional-options "-Wno-int-conversion" } */ +#include "../pr61861.c" diff --git a/gcc/testsuite/gcc.dg/analyzer/pragma-1.c b/gcc/testsuite/gcc.dg/analyzer/pragma-1.c new file mode 100644 index 0000000..2e53348 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/pragma-1.c @@ -0,0 +1,26 @@ +/* Verify that we can disable analyzer warnings via pragmas. */ + +#include + +void test_1 (void *ptr) +{ + free (ptr); + free (ptr); /* { dg-warning "double-'free'" } */ +} + +void test_2 (void *ptr) +{ + _Pragma("GCC diagnostic push") + _Pragma("GCC diagnostic ignored \"-Wanalyzer-double-free\"") + + free (ptr); + free (ptr); /* { dg-bogus "double-'free'" } */ + + _Pragma("GCC diagnostic pop") +} + +void test_3 (void *ptr) +{ + free (ptr); + free (ptr); /* { dg-warning "double-'free'" } */ +} diff --git a/gcc/testsuite/gcc.dg/analyzer/scope-1.c b/gcc/testsuite/gcc.dg/analyzer/scope-1.c new file mode 100644 index 0000000..f8acffa --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/scope-1.c @@ -0,0 +1,23 @@ +#include + +int test_1 (void) +{ + { + int *q = malloc (1024); + } + + return 42; /* { dg-warning "leak of 'q'" } */ + // FIXME: would be better to report it at the close-of-scope +} + +int test_2 (void) +{ + { + void *q = malloc (1024); + } + + int q = 42; + + return q; /* { dg-warning "leak of 'q'" } */ + // FIXME: would be better to report it at the close-of-scope +} diff --git a/gcc/testsuite/gcc.dg/analyzer/sensitive-1.c b/gcc/testsuite/gcc.dg/analyzer/sensitive-1.c new file mode 100644 index 0000000..b884179 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/sensitive-1.c @@ -0,0 +1,33 @@ +#include +#include +#include + +char test_1 (FILE *logfile) +{ + char *password = getpass (">"); /* { dg-message "\\(1\\) state of 'password': 'start' -> 'sensitive'" } */ + fprintf (logfile, "got password %s\n", password); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */ + /* { dg-message "\\(2\\) here \\('password' is in state 'sensitive'\\)" "" { target *-*-* } .-1 } */ +} + +char test_2 (FILE *logfile, int i) +{ + char *password = getpass (">"); /* { dg-message "\\(1\\) state of 'password': 'start' -> 'sensitive'" } */ + fprintf (logfile, "got password[%i]: %s\n", i, password); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */ + /* { dg-message "\\(2\\) here \\('password' is in state 'sensitive'\\)" "" { target *-*-* } .-1 } */ +} + +char test_3 (FILE *logfile) +{ + char *password = getpass (">"); /* { dg-message "\\(1\\) state of 'password': 'start' -> 'sensitive'" } */ + printf ("got password %s\n", password); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */ + /* { dg-message "\\(2\\) here \\('password' is in state 'sensitive'\\)" "" { target *-*-* } .-1 } */ +} + +char test_4 (FILE *logfile) +{ + char *password = getpass (">"); /* { dg-message "\\(1\\) state of 'password': 'start' -> 'sensitive'" } */ + fwrite (password, strlen (password), 1, logfile); /* { dg-warning "sensitive value 'password' written to output file \\\[CWE-532\\\]" } */ + /* { dg-message "\\(2\\) here \\('password' is in state 'sensitive'\\)" "" { target *-*-* } .-1 } */ +} + +/* TODO: strdup etc, strcpy, memcpy, etc. */ diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-1.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-1.c new file mode 100644 index 0000000..c9827fb --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-1.c @@ -0,0 +1 @@ +#include "../pr26983.c" diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-2.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-2.c new file mode 100644 index 0000000..7afe66e --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-2.c @@ -0,0 +1,97 @@ +/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */ + +#include +#include + +extern void foo (int); + +void test_1 (void) +{ + setjmp (NULL); +} + +void test_2 (void) +{ + jmp_buf env; + int i; + + foo (0); + + i = setjmp(env); + + foo (1); + + if (i != 0) + { + foo (2); + __analyzer_dump_path (); /* { dg-message "path" } */ + } + else + longjmp (env, 1); + + foo (3); +} + +/* { dg-begin-multiline-output "" } + NN | __analyzer_dump_path (); + | ^~~~~~~~~~~~~~~~~~~~~~~ + 'test_2': event 1 + | + | NN | i = setjmp(env); + | | ^~~~~~ + | | | + | | (1) 'setjmp' called here + | + 'test_2': events 2-4 + | + | NN | if (i != 0) + | | ^ + | | | + | | (2) following 'false' branch (when 'i == 0')... + |...... + | NN | longjmp (env, 1); + | | ~~~~~~~~~~~~~~~~ + | | | + | | (3) ...to here + | | (4) rewinding within 'test_2' from 'longjmp'... + | + 'test_2': event 5 + | + | NN | i = setjmp(env); + | | ^~~~~~ + | | | + | | (5) ...to 'setjmp' (saved at (1)) + | + 'test_2': events 6-8 + | + | NN | if (i != 0) + | | ^ + | | | + | | (6) following 'true' branch (when 'i != 0')... + | NN | { + | NN | foo (2); + | | ~~~~~~~ + | | | + | | (7) ...to here + | NN | __analyzer_dump_path (); + | | ~~~~~~~~~~~~~~~~~~~~~~~ + | | | + | | (8) here + | + { dg-end-multiline-output "" } */ + +void test_3 (void) +{ + longjmp (NULL, 0); +} + +void test_4 (void) +{ + longjmp (NULL, 1); +} + +void test_5 (void) +{ + jmp_buf env; + longjmp (env, 1); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-3.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-3.c new file mode 100644 index 0000000..6f67ff5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-3.c @@ -0,0 +1,106 @@ +/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */ + +#include +#include + +extern void foo (int); + +static jmp_buf env; + +static void inner (void) +{ + longjmp (env, 1); +} + +void outer (void) +{ + int i; + + foo (0); + + i = setjmp(env); + + if (i != 0) + { + foo (2); + __analyzer_dump_path (); /* { dg-message "path" } */ + } + else + { + foo (1); + inner (); + } + foo (3); +} + +/* { dg-begin-multiline-output "" } + NN | __analyzer_dump_path (); + | ^~~~~~~~~~~~~~~~~~~~~~~ + 'outer': event 1 + | + | NN | void outer (void) + | | ^~~~~ + | | | + | | (1) entry to 'outer' + | + 'outer': event 2 + | + | NN | i = setjmp(env); + | | ^~~~~~ + | | | + | | (2) 'setjmp' called here + | + 'outer': events 3-5 + | + | NN | if (i != 0) + | | ^ + | | | + | | (3) following 'false' branch (when 'i == 0')... + |...... + | NN | foo (1); + | | ~~~~~~~ + | | | + | | (4) ...to here + | NN | inner (); + | | ~~~~~~~~ + | | | + | | (5) calling 'inner' from 'outer' + | + +--> 'inner': events 6-7 + | + | NN | static void inner (void) + | | ^~~~~ + | | | + | | (6) entry to 'inner' + | NN | { + | NN | longjmp (env, 1); + | | ~~~~~~~~~~~~~~~~ + | | | + | | (7) rewinding from 'longjmp' in 'inner'... + | + <------+ + | + 'outer': event 8 + | + | NN | i = setjmp(env); + | | ^~~~~~ + | | | + | | (8) ...to 'setjmp' in 'outer' (saved at (2)) + | + 'outer': events 9-11 + | + | NN | if (i != 0) + | | ^ + | | | + | | (9) following 'true' branch (when 'i != 0')... + | NN | { + | NN | foo (2); + | | ~~~~~~~ + | | | + | | (10) ...to here + | NN | __analyzer_dump_path (); + | | ~~~~~~~~~~~~~~~~~~~~~~~ + | | | + | | (11) here + | + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-4.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-4.c new file mode 100644 index 0000000..21bb87e --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-4.c @@ -0,0 +1,107 @@ +/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */ + +#include + +extern int foo (int); +static jmp_buf buf; + +void inner (int x) +{ + foo (x); + longjmp (buf, 1); + foo (x); +} + +void outer (int y) +{ + foo (y); + inner (y); + foo (y); +} + +int main (void) +{ + if (!setjmp(buf)) + outer (42); + else + __analyzer_dump_path (); /* { dg-message "path" } */ + return 0; +} + +/* { dg-begin-multiline-output "" } + NN | __analyzer_dump_path (); + | ^~~~~~~~~~~~~~~~~~~~~~~ + 'main': event 1 + | + | NN | int main (void) + | | ^~~~ + | | | + | | (1) entry to 'main' + | + 'main': event 2 + | + | NN | if (!setjmp(buf)) + | | ^~~~~~ + | | | + | | (2) 'setjmp' called here + | + 'main': events 3-5 + | + | NN | if (!setjmp(buf)) + | | ^ + | | | + | | (3) following 'true' branch... + | NN | outer (42); + | | ~~~~~~~~~~ + | | | + | | (4) ...to here + | | (5) calling 'outer' from 'main' + | + +--> 'outer': events 6-7 + | + | NN | void outer (int y) + | | ^~~~~ + | | | + | | (6) entry to 'outer' + |...... + | NN | inner (y); + | | ~~~~~~~~~ + | | | + | | (7) calling 'inner' from 'outer' + | + +--> 'inner': events 8-9 + | + | NN | void inner (int x) + | | ^~~~~ + | | | + | | (8) entry to 'inner' + |...... + | NN | longjmp (buf, 1); + | | ~~~~~~~~~~~~~~~~ + | | | + | | (9) rewinding from 'longjmp' in 'inner'... + | + <-------------+ + | + 'main': event 10 + | + | NN | if (!setjmp(buf)) + | | ^~~~~~ + | | | + | | (10) ...to 'setjmp' in 'main' (saved at (2)) + | + 'main': events 11-13 + | + | NN | if (!setjmp(buf)) + | | ^ + | | | + | | (11) following 'false' branch... + |...... + | NN | __analyzer_dump_path (); + | | ~~~~~~~~~~~~~~~~~~~~~~~ + | | | + | | (12) ...to here + | | (13) here + | + { dg-end-multiline-output "" } */ + diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-5.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-5.c new file mode 100644 index 0000000..faac51c --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-5.c @@ -0,0 +1,65 @@ +/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */ + +#include +#include + +static jmp_buf env; + +static void inner (void) +{ + setjmp (env); +} + +void outer (void) +{ + int i; + + inner (); + + longjmp (env, 42); /* { dg-warning "'longjmp' called after enclosing function of 'setjmp' has returned" } */ +} + +/* { dg-begin-multiline-output "" } + NN | longjmp (env, 42); + | ^~~~~~~~~~~~~~~~~ + 'outer': events 1-2 + | + | NN | void outer (void) + | | ^~~~~ + | | | + | | (1) entry to 'outer' + |...... + | NN | inner (); + | | ~~~~~~~~ + | | | + | | (2) calling 'inner' from 'outer' + | + +--> 'inner': event 3 + | + | NN | static void inner (void) + | | ^~~~~ + | | | + | | (3) entry to 'inner' + | + 'inner': event 4 + | + | NN | setjmp (env); + | | ^~~~~~ + | | | + | | (4) 'setjmp' called here + | + <------+ + | + 'outer': events 5-6 + | + | NN | inner (); + | | ^~~~~~~~ + | | | + | | (5) returning to 'outer' from 'inner' + | NN | + | NN | longjmp (env, 42); + | | ~~~~~~~~~~~~~~~~~ + | | | + | | (6) here + | + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-6.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-6.c new file mode 100644 index 0000000..84a6318 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-6.c @@ -0,0 +1,31 @@ +#include +#include +#include + +extern void foo (int); + +static jmp_buf env; + +static void inner (void) +{ + void *ptr = malloc (1024); /* { dg-message "allocated here" } */ + longjmp (env, 1); /* { dg-warning "leak of 'ptr'" } */ + free (ptr); +} + +void outer (void) +{ + int i; + + foo (0); + + i = setjmp(env); + + if (i == 0) + { + foo (1); + inner (); + } + + foo (3); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-7.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-7.c new file mode 100644 index 0000000..2d44926 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-7.c @@ -0,0 +1,36 @@ +#include +#include +#include + +extern void foo (int); + +static jmp_buf env; + +static void inner (void) +{ + longjmp (env, 1); /* { dg-warning "leak of 'ptr'" "" { xfail *-*-* } } */ +} + +static void middle (void) +{ + void *ptr = malloc (1024); /* { dg-message "allocated here" "" { xfail *-*-* } } */ + inner (); + free (ptr); +} + +void outer (void) +{ + int i; + + foo (0); + + i = setjmp(env); + + if (i == 0) + { + foo (1); + middle (); + } + + foo (3); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-8.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-8.c new file mode 100644 index 0000000..ad3ef8f --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-8.c @@ -0,0 +1,107 @@ +/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */ + +#include +#include + +extern void foo (int); + +static jmp_buf env; + +static void inner (void) +{ + /* Pass known 0 to longjmp. */ + longjmp (env, 0); +} + +void outer (void) +{ + int i; + + foo (0); + + i = setjmp(env); + + if (i != 0) + { + foo (2); + __analyzer_dump_path (); /* { dg-message "path" } */ + } + else + { + foo (1); + inner (); + } + foo (3); +} + +/* { dg-begin-multiline-output "" } + NN | __analyzer_dump_path (); + | ^~~~~~~~~~~~~~~~~~~~~~~ + 'outer': event 1 + | + | NN | void outer (void) + | | ^~~~~ + | | | + | | (1) entry to 'outer' + | + 'outer': event 2 + | + | NN | i = setjmp(env); + | | ^~~~~~ + | | | + | | (2) 'setjmp' called here + | + 'outer': events 3-5 + | + | NN | if (i != 0) + | | ^ + | | | + | | (3) following 'false' branch (when 'i == 0')... + |...... + | NN | foo (1); + | | ~~~~~~~ + | | | + | | (4) ...to here + | NN | inner (); + | | ~~~~~~~~ + | | | + | | (5) calling 'inner' from 'outer' + | + +--> 'inner': events 6-7 + | + | NN | static void inner (void) + | | ^~~~~ + | | | + | | (6) entry to 'inner' + |...... + | NN | longjmp (env, 0); + | | ~~~~~~~~~~~~~~~~ + | | | + | | (7) rewinding from 'longjmp' in 'inner'... + | + <------+ + | + 'outer': event 8 + | + | NN | i = setjmp(env); + | | ^~~~~~ + | | | + | | (8) ...to 'setjmp' in 'outer' (saved at (2)) + | + 'outer': events 9-11 + | + | NN | if (i != 0) + | | ^ + | | | + | | (9) following 'true' branch (when 'i != 0')... + | NN | { + | NN | foo (2); + | | ~~~~~~~ + | | | + | | (10) ...to here + | NN | __analyzer_dump_path (); + | | ~~~~~~~~~~~~~~~~~~~~~~~ + | | | + | | (11) here + | + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-9.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-9.c new file mode 100644 index 0000000..081ae047 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-9.c @@ -0,0 +1,109 @@ +/* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */ + +#include +#include + +extern void foo (int); + +static jmp_buf env; + +extern int unknown_val; + +static void inner (void) +{ + /* Pass value that might be 0 to longjmp. */ + longjmp (env, unknown_val); +} + +void outer (void) +{ + int i; + + foo (0); + + i = setjmp(env); + + if (i != 0) + { + foo (2); + __analyzer_dump_path (); /* { dg-message "path" } */ + } + else + { + foo (1); + inner (); + } + foo (3); +} + +/* { dg-begin-multiline-output "" } + NN | __analyzer_dump_path (); + | ^~~~~~~~~~~~~~~~~~~~~~~ + 'outer': event 1 + | + | NN | void outer (void) + | | ^~~~~ + | | | + | | (1) entry to 'outer' + | + 'outer': event 2 + | + | NN | i = setjmp(env); + | | ^~~~~~ + | | | + | | (2) 'setjmp' called here + | + 'outer': events 3-5 + | + | NN | if (i != 0) + | | ^ + | | | + | | (3) following 'false' branch (when 'i == 0')... + |...... + | NN | foo (1); + | | ~~~~~~~ + | | | + | | (4) ...to here + | NN | inner (); + | | ~~~~~~~~ + | | | + | | (5) calling 'inner' from 'outer' + | + +--> 'inner': events 6-7 + | + | NN | static void inner (void) + | | ^~~~~ + | | | + | | (6) entry to 'inner' + |...... + | NN | longjmp (env, unknown_val); + | | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + | | | + | | (7) rewinding from 'longjmp' in 'inner'... + | + <------+ + | + 'outer': event 8 + | + | NN | i = setjmp(env); + | | ^~~~~~ + | | | + | | (8) ...to 'setjmp' in 'outer' (saved at (2)) + | + 'outer': events 9-11 + | + | NN | if (i != 0) + | | ^ + | | | + | | (9) following 'true' branch (when 'i != 0')... + | NN | { + | NN | foo (2); + | | ~~~~~~~ + | | | + | | (10) ...to here + | NN | __analyzer_dump_path (); + | | ~~~~~~~~~~~~~~~~~~~~~~~ + | | | + | | (11) here + | + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/switch.c b/gcc/testsuite/gcc.dg/analyzer/switch.c new file mode 100644 index 0000000..e261770 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/switch.c @@ -0,0 +1,28 @@ +/* { dg-additional-options "-fanalyzer-transitivity" } */ + +void test (int i) +{ + switch (i) + { + case 0: + __analyzer_eval (i == 0); /* { dg-warning "TRUE" } */ + break; + + case 3 ... 5: + __analyzer_eval (i >= 3); /* { dg-warning "TRUE" } */ + __analyzer_eval (i <= 5); /* { dg-warning "TRUE" } */ + break; + + default: + __analyzer_eval (i == 0); /* { dg-warning "FALSE" } */ + __analyzer_eval (i == 2); /* { dg-warning "UNKNOWN" } */ + __analyzer_eval (i == 3); /* { dg-warning "FALSE" } */ + __analyzer_eval (i == 4); /* { dg-warning "FALSE" "" { xfail *-*-* } } */ + /* { dg-warning "UNKNOWN" "" { target *-*-* } .-1 } */ + /* TODO(xfail^^^): we're only checking against endpoints of case + ranges, not the insides. */ + __analyzer_eval (i == 5); /* { dg-warning "FALSE" } */ + __analyzer_eval (i == 6); /* { dg-warning "UNKNOWN" } */ + break; + } +} diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-1.c b/gcc/testsuite/gcc.dg/analyzer/taint-1.c new file mode 100644 index 0000000..cea5440 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/taint-1.c @@ -0,0 +1,128 @@ +#include +#include +#include + +struct foo +{ + signed int i; + char buf[256]; +}; + +char test_1(FILE *f) +{ + struct foo tmp; + + if (1 == fread(&tmp, sizeof(tmp), 1, f)) { /* { dg-message "\\(1\\) 'tmp' gets an unchecked value here" } */ + /* { dg-message "\\(2\\) following 'true' branch\\.\\.\\." "" { target *-*-* } .-1 } */ + /* BUG: the following array lookup trusts that the input data's index is + in the range 0 <= i < 256; otherwise it's accessing the stack */ + return tmp.buf[tmp.i]; // { dg-warning "use of tainted value 'tmp.i' in array lookup without bounds checking" } */ + /* { dg-message "23: \\(3\\) \\.\\.\\.to here" "" { target *-*-* } .-1 } */ + /* { dg-message "23: \\(4\\) 'tmp.i' has an unchecked value here \\(from 'tmp'\\)" "" { target *-*-* } .-2 } */ + /* { dg-message "\\(5\\) use of tainted value 'tmp.i' in array lookup without bounds checking" "" { target *-*-* } .-3 } */ + + // TOOD: better messages for state changes + } + return 0; +} + +char test_2(struct foo *f, int i) +{ + /* not a bug: the data is not known to be tainted: */ + return f->buf[f->i]; +} + +char test_3(FILE *f) +{ + struct foo tmp; + + if (1 == fread(&tmp, sizeof(tmp), 1, f)) { + if (tmp.i >= 0 && tmp.i < 256) { + /* not a bug: the access is guarded by upper and lower bounds: */ + return tmp.buf[tmp.i]; + } + } + return 0; +} + +char test_4(FILE *f) +{ + struct foo tmp; + + if (1 == fread(&tmp, sizeof(tmp), 1, f)) { + if (tmp.i >= 0) { /* { dg-message "'tmp.i' has an unchecked value here \\(from 'tmp'\\)" } */ + /* { dg-message "'tmp.i' has its lower bound checked here" "" { target *-*-* } .-1 } */ + return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without upper-bounds checking" } */ + } + } + return 0; +} + +char test_5(FILE *f) +{ + struct foo tmp; + + if (1 == fread(&tmp, sizeof(tmp), 1, f)) { + if (tmp.i < 256) { /* { dg-message "'tmp.i' has an unchecked value here \\(from 'tmp'\\)" } */ + /* { dg-message "'tmp.i' has its upper bound checked here" "" { target *-*-* } .-1 } */ + return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without lower-bounds checking" } */ + } + } + return 0; +} + +/* unsigned types have a natural lower bound of 0 */ +struct bar +{ + unsigned int i; + char buf[256]; +}; + +char test_6(FILE *f) +{ + struct bar tmp; + + if (1 == fread(&tmp, sizeof(tmp), 1, f)) { + return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without upper-bounds checking" } */ + } + return 0; +} + +char test_7(FILE *f) +{ + struct bar tmp; + + if (1 == fread(&tmp, sizeof(tmp), 1, f)) { + if (tmp.i >= 0) { + return tmp.buf[tmp.i]; /* { dg-warning "use of tainted value 'tmp.i' in array lookup without upper-bounds checking" } */ + } + } + return 0; +} + +char test_8(FILE *f) +{ + struct bar tmp; + + if (1 == fread(&tmp, sizeof(tmp), 1, f)) { + if (tmp.i < 256) { + /* not a bug: has an upper bound, and an implicit lower bound: */ + return tmp.buf[tmp.i]; + } + } + return 0; +} + +char test_9(FILE *f) +{ + struct foo tmp; + + if (1 == fread(&tmp, sizeof(tmp), 1, f)) { + if (tmp.i == 42) { + /* not a bug: tmp.i compared against a specific value: */ + return tmp.buf[tmp.i]; /* { dg-bogus "tainted" "" { xfail *-*-* } } */ + // TODO: xfail + } + } + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/zlib-1.c b/gcc/testsuite/gcc.dg/analyzer/zlib-1.c new file mode 100644 index 0000000..52d3c62 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/zlib-1.c @@ -0,0 +1,67 @@ +typedef void (*free_func)(void *opaque, void *address); + +typedef struct z_stream_s { + struct internal_state *state; + free_func zfree; + void *opaque; +} z_stream; + +struct internal_state { + z_stream *strm; + int status; + unsigned char *pending_buf; + unsigned char *window; + unsigned short *prev; + unsigned short *head; +}; + +int deflateEnd(z_stream *strm) +{ + int status; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + if (strm == 0 || strm->state == 0) + return (-2); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + status = strm->state->status; + if (status != 42 && status != 113 && status != 666) { + return (-2); + } + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "4 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + if (strm->state->pending_buf) + (*(strm->zfree))(strm->opaque, (void *)(strm->state->pending_buf)); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + if (strm->state->head) + (*(strm->zfree))(strm->opaque, (void *)(strm->state->head)); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + if (strm->state->prev) + (*(strm->zfree))(strm->opaque, (void *)(strm->state->prev)); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + if (strm->state->window) + (*(strm->zfree))(strm->opaque, (void *)(strm->state->window)); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 exploded nodes" } */ + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + (*(strm->zfree))(strm->opaque, (void *)(strm->state)); + strm->state = 0; + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + return status == 113 ? (-3) : 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/zlib-2.c b/gcc/testsuite/gcc.dg/analyzer/zlib-2.c new file mode 100644 index 0000000..d0b587c --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/zlib-2.c @@ -0,0 +1,51 @@ +/* { dg-additional-options "-Wno-analyzer-too-complex" } */ + +typedef void * (*alloc_func)(void * opaque, unsigned items, unsigned size); +typedef void (*free_func)(void * opaque, void * address); + +typedef struct z_stream_s { + char *msg; + alloc_func zalloc; + free_func zfree; + void * opaque; +} z_stream; + +void * zcalloc(void * opaque, unsigned items, unsigned size); +void zcfree(void * opaque, void * ptr); + +int deflateInit2_(z_stream *strm, int level, int method, int windowBits, + int memLevel, int strategy, const char *version, + int stream_size) { + int noheader = 0; + static const char *my_version = "1.1.3"; + + if (version == 0 || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return (-6); + } + if (strm == 0) + return (-2); + + strm->msg = 0; + if (strm->zalloc == 0) { + strm->zalloc = zcalloc; + strm->opaque = (void *)0; + } + if (strm->zfree == 0) + strm->zfree = zcfree; + + if (level == (-1)) + level = 6; + + if (windowBits < 0) { + noheader = 1; + windowBits = -windowBits; + } + if (memLevel < 1 || memLevel > 9 || method != 8 || windowBits < 8 || + windowBits > 15 || level < 0 || level > 9 || strategy < 0 || + strategy > 2) { + return (-2); + } + (*((strm)->zalloc))((strm)->opaque, (1), 112); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/zlib-3.c b/gcc/testsuite/gcc.dg/analyzer/zlib-3.c new file mode 100644 index 0000000..5faada1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/zlib-3.c @@ -0,0 +1,214 @@ +/* { dg-additional-options "-O3 -Wno-analyzer-too-complex" } */ +/* TODO: reduce this; was triggering this assert: + gcc_assert (pruned_state != existing_state); +*/ + +typedef unsigned char Byte; +typedef unsigned int uInt; + +typedef void *voidp; + +typedef voidp (*alloc_func)(voidp opaque, uInt items, uInt size); + +typedef struct z_stream_s { + alloc_func zalloc; + voidp opaque; +} z_stream; + +typedef z_stream *z_streamp; + +typedef struct inflate_huft_s inflate_huft; + +struct inflate_huft_s { + struct { + Byte Exop; + Byte Bits; + } what; + uInt base; +}; + +static int huft_build(uInt *, uInt, uInt, const uInt *, const uInt *, + inflate_huft **, uInt *, inflate_huft *, uInt *, uInt *); + +static int huft_build(uInt *b, uInt n, uInt s, const uInt *d, const uInt *e, + inflate_huft **t, uInt *m, inflate_huft *hp, uInt *hn, + uInt *v) { + + uInt a; + uInt c[15 + 1]; + uInt f; + int g; + int h; + register uInt i; + register uInt j; + register int k; + int l; + uInt mask; + register uInt *p; + inflate_huft *q; + struct inflate_huft_s r; + inflate_huft *u[15]; + register int w; + uInt x[15 + 1]; + uInt *xp; + int y; + uInt z; + + p = c; + + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + p = b; + i = n; + do { + c[*p++]++; + } while (--i); + if (c[0] == n) { + *t = (inflate_huft *)0; + *m = 0; + return 0; + } + + l = *m; + for (j = 1; j <= 15; j++) + if (c[j]) + break; + k = j; + if ((uInt)l < j) + l = j; + for (i = 15; i; i--) + if (c[i]) + break; + g = i; + if ((uInt)l > i) + l = i; + *m = l; + + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return (-3); + if ((y -= c[i]) < 0) + return (-3); + c[i] += y; + + x[1] = j = 0; + p = c + 1; + xp = x + 2; + while (--i) { + *xp++ = (j += *p++); + } + + p = b; + i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; + + x[0] = i = 0; + p = v; + h = -1; + w = -l; + u[0] = (inflate_huft *)0; + q = (inflate_huft *)0; + z = 0; + + for (; k <= g; k++) { + a = c[k]; + while (a--) { + + while (k > w + l) { + h++; + w += l; + + z = g - w; + z = z > (uInt)l ? l : z; + if ((f = 1 << (j = k - w)) > a + 1) { + f -= a + 1; + xp = c + k; + if (j < z) + while (++j < z) { + if ((f <<= 1) <= *++xp) + break; + f -= *xp; + } + } + z = 1 << j; + + if (*hn + z > 1440) + return (-4); + u[h] = q = hp + *hn; + *hn += z; + + if (h) { + x[h] = i; + r.what.Bits = (Byte)l; + r.what.Exop = (Byte)j; + j = i >> (w - l); + r.base = (uInt)(q - u[h - 1] - j); + u[h - 1][j] = r; + } else + *t = q; + } + + r.what.Bits = (Byte)(k - w); + if (p >= v + n) + r.what.Exop = 128 + 64; + else if (*p < s) { + r.what.Exop = (Byte)(*p < 256 ? 0 : 32 + 64); + r.base = *p++; + } else { + r.what.Exop = (Byte)(e[*p - s] + 16 + 64); + r.base = d[*p++ - s]; + } + + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + mask = (1 << w) - 1; + while ((i & mask) != x[h]) { + h--; + w -= l; + mask = (1 << w) - 1; + } + } + } + + return y != 0 && g != 1 ? (-5) : 0; +} + +extern const uInt cplens[31]; +extern const uInt cplext[31]; +extern const uInt cpdist[30]; +extern const uInt cpdext[30]; + +int inflate_trees_dynamic(uInt nl, uInt nd, uInt *c, uInt *bl, uInt *bd, + inflate_huft **tl, inflate_huft **td, + inflate_huft *hp, z_streamp z) { + int r; + uInt hn = 0; + uInt *v; + + if ((v = (uInt *)(*((z)->zalloc))((z)->opaque, (288), (sizeof(uInt)))) == 0) + return (-4); + + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/zlib-4.c b/gcc/testsuite/gcc.dg/analyzer/zlib-4.c new file mode 100644 index 0000000..0826144 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/zlib-4.c @@ -0,0 +1,20 @@ +#include +#include + +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef unsigned long uLong; + +#define Z_NULL 0 + +void test () +{ + uLong comprLen = 10000*sizeof(int); + uLong uncomprLen = comprLen; + Byte *compr = (Byte*)calloc((uInt)comprLen, 1); + Byte *uncompr = (Byte*)calloc((uInt)uncomprLen, 1); + if (compr == Z_NULL || uncompr == Z_NULL) + exit (1); + strcpy((char*)uncompr, "garbage"); + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/zlib-5.c b/gcc/testsuite/gcc.dg/analyzer/zlib-5.c new file mode 100644 index 0000000..bcf8a0a --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/zlib-5.c @@ -0,0 +1,49 @@ +/* { dg-additional-options "-O3" } */ + +typedef long unsigned int size_t; +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef unsigned long uLong; + +extern size_t strlen(const char *__s) __attribute__((__nothrow__, __leaf__)) + __attribute__((__pure__)) __attribute__((__nonnull__(1))); +extern void exit(int __status) __attribute__((__nothrow__, __leaf__)) + __attribute__((__noreturn__)); +extern char *strcpy(char *__restrict __dest, const char *__restrict __src) + __attribute__((__nothrow__, __leaf__)) __attribute__((__nonnull__(1, 2))); +extern void *calloc(size_t __nmemb, size_t __size) + __attribute__((__nothrow__, __leaf__)) __attribute__((__malloc__)); + +extern int compress(Byte *dest, uLong *destLen, const Byte *source, + uLong sourceLen); + +const char hello[] = "hello, hello!"; + +void test_compress(Byte *compr, uLong comprLen, Byte *uncompr, + uLong uncomprLen) { + int err; + uLong len = strlen(hello) + 1; + + err = compress(compr, &comprLen, (const Byte *)hello, len); + if (err != 0) + exit(1); + strcpy((char *)uncompr, "garbage"); /* { dg-bogus "NULL" } */ +} + +int main(int argc, char *argv[]) { + Byte *compr, *uncompr; + uLong comprLen = 10000 * sizeof(int); + uLong uncomprLen = comprLen; + + compr = (Byte *)calloc((uInt)comprLen, 1); + uncompr = (Byte *)calloc((uInt)uncomprLen, 1); + if (compr == 0 || uncompr == 0) + exit(1); + + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 exploded node" } */ + + test_compress(compr, comprLen, uncompr, uncomprLen); + + exit(0); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/zlib-6.c b/gcc/testsuite/gcc.dg/analyzer/zlib-6.c new file mode 100644 index 0000000..d68c387 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/zlib-6.c @@ -0,0 +1,47 @@ +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef unsigned long uLong; + +typedef struct z_stream_s { + Byte *next_in; + uInt avail_in; + uLong total_in; +} z_stream; + +typedef struct inflate_blocks_state { + uInt bitk; + uLong bitb; + Byte *write; +} inflate_blocks_statef; + +extern int inflate_flush(inflate_blocks_statef *, z_stream *, int); + +int inflate_blocks(inflate_blocks_statef *s, z_stream *z, int r) { + uInt t; + uLong b; + uInt k; + Byte *p; + uInt n; + Byte *q; + uInt m; + + while (k < (3)) { + { + if (n) + r = 0; + else { + { + s->bitb = b; + s->bitk = k; + z->avail_in = n; + z->total_in += p - z->next_in; + z->next_in = p; + s->write = q; + } + return inflate_flush(s, z, r); + } + }; + b |= ((uLong)(n--, *p++)) << k; /* { dg-warning "use of uninitialized value" } */ + k += 8; + } +} diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index 6f224fa..a936d78 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -8407,6 +8407,14 @@ proc check_effective_target_lto_incremental { } { } "-flto -r -nostdlib"] } +# Return 1 if the compiler has been configured with analyzer support. + +proc check_effective_target_analyzer { } { + return [check_no_compiler_messages analyzer object { + void foo (void) { } + } "--analyzer"] +} + # Return 1 if -mx32 -maddress-mode=short can compile, 0 otherwise. proc check_effective_target_maybe_x32 { } {