From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id E1C1E3857C68 for ; Thu, 10 Mar 2022 14:18:37 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org E1C1E3857C68 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-275-ICi0Dhq1N-uHbsix1FPRlw-1; Thu, 10 Mar 2022 09:18:35 -0500 X-MC-Unique: ICi0Dhq1N-uHbsix1FPRlw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id DBC91824FA7 for ; Thu, 10 Mar 2022 14:18:34 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.2.17.210]) by smtp.corp.redhat.com (Postfix) with ESMTP id DC9E12378E; Thu, 10 Mar 2022 14:18:32 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org Subject: [committed] analyzer: check for writes to consts via access attr [PR104793] Date: Thu, 10 Mar 2022 09:18:31 -0500 Message-Id: <20220310141831.3402489-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII" X-Spam-Status: No, score=-12.7 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 10 Mar 2022 14:18:39 -0000 This patch extends: -Wanalyzer-write-to-const -Wanalyzer-write-to-string-literal so that they will check for __attribute__ ((access, ....) on calls to externally-defined functions, and complain about read-only regions pointed to by arguments marked with a "write_only" or "read_write" attribute. Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. Pushed to trunk as r12-7595-gb6eaf90c64f915. gcc/analyzer/ChangeLog: PR analyzer/104793 * region-model.cc (region_model::check_external_function_for_access_attr): New. (region_model::handle_unrecognized_call): Call it. * region-model.h (region_model::check_external_function_for_access_attr): New decl. (region_model::handle_unrecognized_call): New decl. gcc/testsuite/ChangeLog: PR analyzer/104793 * gcc.dg/analyzer/write-to-const-2.c: New test. * gcc.dg/analyzer/write-to-function-1.c: New test. * gcc.dg/analyzer/write-to-string-literal-2.c: New test. * gcc.dg/analyzer/write-to-string-literal-3.c: New test. * gcc.dg/analyzer/write-to-string-literal-4.c: New test. Signed-off-by: David Malcolm --- gcc/analyzer/region-model.cc | 58 ++++++++++++++++ gcc/analyzer/region-model.h | 3 + .../gcc.dg/analyzer/write-to-const-2.c | 60 +++++++++++++++++ .../gcc.dg/analyzer/write-to-function-1.c | 15 +++++ .../analyzer/write-to-string-literal-2.c | 19 ++++++ .../analyzer/write-to-string-literal-3.c | 66 +++++++++++++++++++ .../analyzer/write-to-string-literal-4.c | 23 +++++++ 7 files changed, 244 insertions(+) create mode 100644 gcc/testsuite/gcc.dg/analyzer/write-to-const-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/write-to-function-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/write-to-string-literal-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/write-to-string-literal-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/write-to-string-literal-4.c diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 5cfa3543f17..5760ff70938 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -1583,6 +1583,61 @@ region_model::purge_state_involving (const svalue *sval, ctxt->purge_state_involving (sval); } +/* Check CALL a call to external function CALLEE_FNDECL based on + any __attribute__ ((access, ....) on the latter, complaining to + CTXT about any issues. + + Currently we merely call check_region_for_write on any regions + pointed to by arguments marked with a "write_only" or "read_write" + attribute. */ + +void +region_model:: +check_external_function_for_access_attr (const gcall *call, + tree callee_fndecl, + region_model_context *ctxt) const +{ + gcc_assert (call); + gcc_assert (callee_fndecl); + gcc_assert (ctxt); + + tree fntype = TREE_TYPE (callee_fndecl); + if (!fntype) + return; + + if (!TYPE_ATTRIBUTES (fntype)) + return; + + /* Initialize a map of attribute access specifications for arguments + to the function call. */ + rdwr_map rdwr_idx; + init_attr_rdwr_indices (&rdwr_idx, TYPE_ATTRIBUTES (fntype)); + + unsigned argno = 0; + + for (tree iter = TYPE_ARG_TYPES (fntype); iter; + iter = TREE_CHAIN (iter), ++argno) + { + const attr_access* access = rdwr_idx.get (argno); + if (!access) + continue; + + /* Ignore any duplicate entry in the map for the size argument. */ + if (access->ptrarg != argno) + continue; + + if (access->mode == access_write_only + || access->mode == access_read_write) + { + tree ptr_tree = gimple_call_arg (call, access->ptrarg); + const svalue *ptr_sval = get_rvalue (ptr_tree, ctxt); + const region *reg = deref_rvalue (ptr_sval, ptr_tree, ctxt); + check_region_for_write (reg, ctxt); + /* We don't use the size arg for now. */ + } + } +} + /* Handle a call CALL to a function with unknown behavior. Traverse the regions in this model, determining what regions are @@ -1598,6 +1653,9 @@ region_model::handle_unrecognized_call (const gcall *call, { tree fndecl = get_fndecl_for_call (call, ctxt); + if (fndecl && ctxt) + check_external_function_for_access_attr (call, fndecl, ctxt); + reachable_regions reachable_regs (this); /* Determine the reachable regions and their mutability. */ diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h index aa489d06a38..788d0c22bca 100644 --- a/gcc/analyzer/region-model.h +++ b/gcc/analyzer/region-model.h @@ -846,6 +846,9 @@ class region_model region_model_context *ctxt) const; void check_call_args (const call_details &cd) const; + void check_external_function_for_access_attr (const gcall *call, + tree callee_fndecl, + region_model_context *ctxt) const; /* Storing this here to avoid passing it around everywhere. */ region_model_manager *const m_mgr; diff --git a/gcc/testsuite/gcc.dg/analyzer/write-to-const-2.c b/gcc/testsuite/gcc.dg/analyzer/write-to-const-2.c new file mode 100644 index 00000000000..d0f2f29e985 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/write-to-const-2.c @@ -0,0 +1,60 @@ +typedef __SIZE_TYPE__ size_t; + +void read_only (void *) + __attribute__ ((access (read_only, 1))); +void write_only (void *) + __attribute__ ((access (write_only, 1))); +void read_write (void *) + __attribute__ ((access (read_write, 1))); +void none (void *) + __attribute__ ((access (none, 1))); +void read_only_with_size (void *, size_t) + __attribute__ ((access (read_only, 1, 2))); +void write_only_with_size (void *, size_t) + __attribute__ ((access (write_only, 1, 2))); +void read_write_with_size (void *, size_t) + __attribute__ ((access (read_write, 1, 2))); +void none_with_size (void *, size_t) + __attribute__ ((access (none, 1, 2))); + +const char buf[5] = { 0 }; /* { dg-message "declared here" } */ + +void test_read_only (void) +{ + read_only ((char *)buf); +} + +void test_write_only (void) +{ + write_only ((char *)buf); /* { dg-warning "write to 'const' object 'buf'" } */ +} + +void test_read_write (void) +{ + read_write ((char *)buf); /* { dg-warning "write to 'const' object 'buf'" } */ +} + +void test_none (void) +{ + none ((char *)buf); +} + +void test_read_only_with_size (void) +{ + read_only_with_size ((char *)buf, sizeof (buf)); +} + +void test_write_only_with_size (void) +{ + write_only_with_size ((char *)buf, sizeof (buf)); /* { dg-warning "write to 'const' object 'buf'" } */ +} + +void test_read_write_with_size (void) +{ + read_write_with_size ((char *)buf, sizeof (buf)); /* { dg-warning "write to 'const' object 'buf'" } */ +} + +void test_none_with_size (void) +{ + none_with_size ((char *)buf, sizeof (buf)); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/write-to-function-1.c b/gcc/testsuite/gcc.dg/analyzer/write-to-function-1.c new file mode 100644 index 00000000000..38374ddf9e4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/write-to-function-1.c @@ -0,0 +1,15 @@ +typedef __SIZE_TYPE__ size_t; + +int getrandom (void *__buffer, size_t __length, + unsigned int __flags) + __attribute__ ((access (__write_only__, 1, 2))); + +#define GRND_RANDOM 0x02 + +void test (void) +{ + char buf[16]; + + if (getrandom(test, 16, GRND_RANDOM)) /* { dg-warning "write to function 'test'" } */ + __builtin_printf("%s\n", buf); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/write-to-string-literal-2.c b/gcc/testsuite/gcc.dg/analyzer/write-to-string-literal-2.c new file mode 100644 index 00000000000..e5906cd5546 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/write-to-string-literal-2.c @@ -0,0 +1,19 @@ +typedef __SIZE_TYPE__ size_t; + +int getrandom (void *__buffer, size_t __length, + unsigned int __flags) + __attribute__ ((access (__write_only__, 1, 2))); + +#define GRND_RANDOM 0x02 + +const char *test = "test"; + +int main(void) +{ + const char buf[5] = { 0 }; + + if (getrandom((char *)test, sizeof(buf), GRND_RANDOM)) /* { dg-warning "write to string literal" } */ + __builtin_printf("%s\n", buf); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/write-to-string-literal-3.c b/gcc/testsuite/gcc.dg/analyzer/write-to-string-literal-3.c new file mode 100644 index 00000000000..7f4fb4f3aa0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/write-to-string-literal-3.c @@ -0,0 +1,66 @@ +typedef __SIZE_TYPE__ size_t; + +void read_only (void *) + __attribute__ ((access (read_only, 1))); +void write_only (void *) + __attribute__ ((access (write_only, 1))); +void read_write (void *) + __attribute__ ((access (read_write, 1))); +void none (void *) + __attribute__ ((access (none, 1))); +void read_only_with_size (void *, size_t) + __attribute__ ((access (read_only, 1, 2))); +void write_only_with_size (void *, size_t) + __attribute__ ((access (write_only, 1, 2))); +void read_write_with_size (void *, size_t) + __attribute__ ((access (read_write, 1, 2))); +void none_with_size (void *, size_t) + __attribute__ ((access (none, 1, 2))); + +void test_read_only (void) +{ + const char *str = "hello world"; + read_only ((char *)str); +} + +void test_write_only (void) +{ + const char *str = "hello world"; + write_only ((char *)str); /* { dg-warning "write to string literal" } */ +} + +void test_read_write (void) +{ + const char *str = "hello world"; + read_write ((char *)str); /* { dg-warning "write to string literal" } */ +} + +void test_none (void) +{ + const char *str = "hello world"; + none ((char *)str); +} + +void test_read_only_with_size (void) +{ + const char *str = "hello world"; + read_only_with_size ((char *)str, sizeof (str)); +} + +void test_write_only_with_size (void) +{ + const char *str = "hello world"; + write_only_with_size ((char *)str, sizeof (str)); /* { dg-warning "write to string literal" } */ +} + +void test_read_write_with_size (void) +{ + const char *str = "hello world"; + read_write_with_size ((char *)str, sizeof (str)); /* { dg-warning "write to string literal" } */ +} + +void test_none_with_size (void) +{ + const char *str = "hello world"; + none_with_size ((char *)str, sizeof (str)); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/write-to-string-literal-4.c b/gcc/testsuite/gcc.dg/analyzer/write-to-string-literal-4.c new file mode 100644 index 00000000000..3d3270ffdbb --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/write-to-string-literal-4.c @@ -0,0 +1,23 @@ +typedef __SIZE_TYPE__ size_t; + +int getrandom (void *__buffer, size_t __length, + unsigned int __flags) + __attribute__ ((access (__write_only__, 1, 2))); + +#define GRND_RANDOM 0x02 + +void test (int flag) +{ + char *buf; + + if (flag) + buf = __builtin_malloc (1024); + else + buf = (char *)""; /* { dg-message "here" } */ + + if (getrandom(buf, 16, GRND_RANDOM)) /* { dg-warning "write to string literal" } */ + __builtin_printf("%s\n", buf); + + if (flag) + __builtin_free (buf); +} -- 2.26.3