From patchwork Sat Nov 13 20:37:31 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 47624 X-Patchwork-Delegate: dmalcolm@redhat.com Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 48A2D3858427 for ; Sat, 13 Nov 2021 20:43:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 48A2D3858427 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1636836189; bh=pNEGZ1ZpB51RgE2sE7xsZEUqCYqVo3RJyZvwOybExj8=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=tJ9ez+GOZ1dDdOhL228E8sglj7eFdqlp/dDB/q+HnEwSY8JNUbkND6+WkqruTbEZK TkirOAZGaRUcHw5e7Eq/wQxd5QFtdaYKB/pbpcrCqI2RR5uNgd5tu5MR8XzhZ7n+qE Dq26Vc2HxrzMJFm5YU/jRJ4Jwudj9/SUAMnwS/p8= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 5D61D385800D for ; Sat, 13 Nov 2021 20:37:47 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 5D61D385800D 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-88-szsjcPW6PxGgX328kuyBnQ-1; Sat, 13 Nov 2021 15:37:44 -0500 X-MC-Unique: szsjcPW6PxGgX328kuyBnQ-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 11DFC875047; Sat, 13 Nov 2021 20:37:44 +0000 (UTC) Received: from t14s.localdomain.com (ovpn-113-54.phx2.redhat.com [10.3.113.54]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9859119D9D; Sat, 13 Nov 2021 20:37:43 +0000 (UTC) To: gcc-patches@gcc.gnu.org, linux-toolchains@vger.kernel.org Subject: [PATCH 5/6] analyzer: use region::untrusted_p in taint detection Date: Sat, 13 Nov 2021 15:37:31 -0500 Message-Id: <20211113203732.2098220-8-dmalcolm@redhat.com> In-Reply-To: <20211113203732.2098220-1-dmalcolm@redhat.com> References: <20211113203732.2098220-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 X-Spam-Status: No, score=-13.4 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_LOW, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: David Malcolm via Gcc-patches From: David Malcolm Reply-To: David Malcolm Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" This patch wires up the "untrusted" region logic to the analyzer's taint detection, so that any data copied via a __user pointer (e.g. via a suitably annotated "copy_from_user" decl) is treated as tainted. It includes a series of reproducers for detecting CVE-2011-0521. Unfortunately the analyzer doesn't yet detect the issue until the code has been significantly simplified from its original form: currently only in -5.c and -6.c in the series of tests (see notes in the individual cases). gcc/analyzer/ChangeLog: * sm-taint.cc (taint_state_machine::get_default_state): New, using region::untrusted_p. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/taint-CVE-2011-0521-1-fixed.c: New test. * gcc.dg/analyzer/taint-CVE-2011-0521-1.c: New test. * gcc.dg/analyzer/taint-CVE-2011-0521-2-fixed.c: New test. * gcc.dg/analyzer/taint-CVE-2011-0521-2.c: New test. * gcc.dg/analyzer/taint-CVE-2011-0521-3-fixed.c: New test. * gcc.dg/analyzer/taint-CVE-2011-0521-3.c: New test. * gcc.dg/analyzer/taint-CVE-2011-0521-4.c: New test. * gcc.dg/analyzer/taint-CVE-2011-0521-5.c: New test. * gcc.dg/analyzer/taint-CVE-2011-0521-6.c: New test. * gcc.dg/analyzer/taint-CVE-2011-0521.h: New test. * gcc.dg/analyzer/taint-antipatterns-1.c: New test. * gcc.dg/analyzer/taint-read-through-untrusted-ptr-1.c: New test. Signed-off-by: David Malcolm --- gcc/analyzer/sm-taint.cc | 13 ++ .../analyzer/taint-CVE-2011-0521-1-fixed.c | 113 +++++++++++++++ .../gcc.dg/analyzer/taint-CVE-2011-0521-1.c | 113 +++++++++++++++ .../analyzer/taint-CVE-2011-0521-2-fixed.c | 93 ++++++++++++ .../gcc.dg/analyzer/taint-CVE-2011-0521-2.c | 93 ++++++++++++ .../analyzer/taint-CVE-2011-0521-3-fixed.c | 56 +++++++ .../gcc.dg/analyzer/taint-CVE-2011-0521-3.c | 57 ++++++++ .../gcc.dg/analyzer/taint-CVE-2011-0521-4.c | 40 +++++ .../gcc.dg/analyzer/taint-CVE-2011-0521-5.c | 42 ++++++ .../gcc.dg/analyzer/taint-CVE-2011-0521-6.c | 37 +++++ .../gcc.dg/analyzer/taint-CVE-2011-0521.h | 136 +++++++++++++++++ .../gcc.dg/analyzer/taint-antipatterns-1.c | 137 ++++++++++++++++++ .../taint-read-through-untrusted-ptr-1.c | 37 +++++ 13 files changed, 967 insertions(+) create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-1-fixed.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-2-fixed.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-2.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-3-fixed.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-3.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-4.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-5.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-6.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521.h create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-antipatterns-1.c create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-read-through-untrusted-ptr-1.c diff --git a/gcc/analyzer/sm-taint.cc b/gcc/analyzer/sm-taint.cc index 0a51a1fe2ea..53ba6f2b30c 100644 --- a/gcc/analyzer/sm-taint.cc +++ b/gcc/analyzer/sm-taint.cc @@ -85,6 +85,19 @@ public: const extrinsic_state &ext_state) const FINAL OVERRIDE; + state_machine::state_t + get_default_state (const svalue *sval) const FINAL OVERRIDE + { + /* Default to "tainted" when reading through a pointer to an untrusted + region. */ + if (const initial_svalue *initial_sval = sval->dyn_cast_initial_svalue ()) + { + if (initial_sval->get_region ()->untrusted_p ()) + return m_tainted; + } + return m_start; + } + bool on_stmt (sm_context *sm_ctxt, const supernode *node, const gimple *stmt) const FINAL OVERRIDE; diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-1-fixed.c b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-1-fixed.c new file mode 100644 index 00000000000..a97896f2266 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-1-fixed.c @@ -0,0 +1,113 @@ +/* See notes in this header. */ +#include "taint-CVE-2011-0521.h" + +// TODO: remove need for this option +/* { dg-additional-options "-fanalyzer-checker=taint" } */ + +/* Adapted from drivers/media/dvb/ttpci/av7110_ca.c */ + +int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned long arg = (unsigned long) parg; + + /* case CA_GET_SLOT_INFO: */ + { + ca_slot_info_t *info=(ca_slot_info_t *)parg; + + if (info->num < 0 || info->num > 1) + return -EINVAL; + av7110->ci_slot[info->num].num = info->num; /* { dg-bogus "attacker-controlled value" } */ + av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? + CA_CI_LINK : CA_CI; + memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); + } + return 0; +} + +static struct dvb_device dvbdev_ca = { + .priv = NULL, + /* [...snip...] */ + .kernel_ioctl = dvb_ca_ioctl, +}; + +/* Adapted from drivers/media/dvb/dvb-core/dvbdev.c */ + +static DEFINE_MUTEX(dvbdev_mutex); + +int dvb_usercopy(struct file *file, + unsigned int cmd, unsigned long arg, + int (*func)(struct file *file, + unsigned int cmd, void *arg)) +{ + char sbuf[128]; + void *mbuf = NULL; + void *parg = NULL; + int err = -1; + + /* Copy arguments into temp kernel buffer */ + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: + /* + * For this command, the pointer is actually an integer + * argument. + */ + parg = (void *) arg; + break; + case _IOC_READ: /* some v4l ioctls are marked wrong ... */ + case _IOC_WRITE: + case (_IOC_WRITE | _IOC_READ): + if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { + parg = sbuf; + } else { + /* too big to allocate from stack */ + mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL); + if (NULL == mbuf) + return -ENOMEM; + parg = mbuf; + } + + err = -EFAULT; + if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) + goto out; + break; + } + + /* call driver */ + mutex_lock(&dvbdev_mutex); + if ((err = func(file, cmd, parg)) == -ENOIOCTLCMD) + err = -EINVAL; + mutex_unlock(&dvbdev_mutex); + + if (err < 0) + goto out; + + /* Copy results into user buffer */ + switch (_IOC_DIR(cmd)) + { + case _IOC_READ: + case (_IOC_WRITE | _IOC_READ): + if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) + err = -EFAULT; + break; + } + +out: + kfree(mbuf); + return err; +} + +long dvb_generic_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct dvb_device *dvbdev = file->private_data; + + if (!dvbdev) + return -ENODEV; + + if (!dvbdev->kernel_ioctl) + return -EINVAL; + + return dvb_usercopy(file, cmd, arg, dvbdev->kernel_ioctl); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-1.c b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-1.c new file mode 100644 index 00000000000..1279f40d948 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-1.c @@ -0,0 +1,113 @@ +/* See notes in this header. */ +#include "taint-CVE-2011-0521.h" + +// TODO: remove need for this option +/* { dg-additional-options "-fanalyzer-checker=taint" } */ + +/* Adapted from drivers/media/dvb/ttpci/av7110_ca.c */ + +int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned long arg = (unsigned long) parg; + + /* case CA_GET_SLOT_INFO: */ + { + ca_slot_info_t *info=(ca_slot_info_t *)parg; + + if (info->num > 1) + return -EINVAL; + av7110->ci_slot[info->num].num = info->num; /* { dg-warning "attacker-controlled value" "" { xfail *-*-* } } */ + av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? + CA_CI_LINK : CA_CI; + memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); + } + return 0; +} + +static struct dvb_device dvbdev_ca = { + .priv = NULL, + /* [...snip...] */ + .kernel_ioctl = dvb_ca_ioctl, +}; + +/* Adapted from drivers/media/dvb/dvb-core/dvbdev.c */ + +static DEFINE_MUTEX(dvbdev_mutex); + +int dvb_usercopy(struct file *file, + unsigned int cmd, unsigned long arg, + int (*func)(struct file *file, + unsigned int cmd, void *arg)) +{ + char sbuf[128]; + void *mbuf = NULL; + void *parg = NULL; + int err = -1; + + /* Copy arguments into temp kernel buffer */ + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: + /* + * For this command, the pointer is actually an integer + * argument. + */ + parg = (void *) arg; + break; + case _IOC_READ: /* some v4l ioctls are marked wrong ... */ + case _IOC_WRITE: + case (_IOC_WRITE | _IOC_READ): + if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { + parg = sbuf; + } else { + /* too big to allocate from stack */ + mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL); + if (NULL == mbuf) + return -ENOMEM; + parg = mbuf; + } + + err = -EFAULT; + if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) + goto out; + break; + } + + /* call driver */ + mutex_lock(&dvbdev_mutex); + if ((err = func(file, cmd, parg)) == -ENOIOCTLCMD) + err = -EINVAL; + mutex_unlock(&dvbdev_mutex); + + if (err < 0) + goto out; + + /* Copy results into user buffer */ + switch (_IOC_DIR(cmd)) + { + case _IOC_READ: + case (_IOC_WRITE | _IOC_READ): + if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) + err = -EFAULT; + break; + } + +out: + kfree(mbuf); + return err; +} + +long dvb_generic_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct dvb_device *dvbdev = file->private_data; + + if (!dvbdev) + return -ENODEV; + + if (!dvbdev->kernel_ioctl) + return -EINVAL; + + return dvb_usercopy(file, cmd, arg, dvbdev->kernel_ioctl); +} diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-2-fixed.c b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-2-fixed.c new file mode 100644 index 00000000000..2b06bde4063 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-2-fixed.c @@ -0,0 +1,93 @@ +/* See notes in this header. */ +#include "taint-CVE-2011-0521.h" + +// TODO: remove need for this option +/* { dg-additional-options "-fanalyzer-checker=taint" } */ + +/* Adapted from drivers/media/dvb/ttpci/av7110_ca.c */ + +int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned long arg = (unsigned long) parg; + + /* case CA_GET_SLOT_INFO: */ + { + ca_slot_info_t *info=(ca_slot_info_t *)parg; + + if (info->num < 0 || info->num > 1) + return -EINVAL; + av7110->ci_slot[info->num].num = info->num; /* { dg-bogus "attacker-controlled value" } */ + av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? + CA_CI_LINK : CA_CI; + memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); + } + return 0; +} + +/* Adapted from drivers/media/dvb/dvb-core/dvbdev.c + Somewhat simplified: rather than pass in a callback that can + be dvb_ca_ioctl, call dvb_ca_ioctl directly. */ + +static DEFINE_MUTEX(dvbdev_mutex); + +int dvb_usercopy(struct file *file, + unsigned int cmd, unsigned long arg) +{ + char sbuf[128]; + void *mbuf = NULL; + void *parg = NULL; + int err = -1; + + /* Copy arguments into temp kernel buffer */ + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: + /* + * For this command, the pointer is actually an integer + * argument. + */ + parg = (void *) arg; + break; + case _IOC_READ: /* some v4l ioctls are marked wrong ... */ + case _IOC_WRITE: + case (_IOC_WRITE | _IOC_READ): + if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { + parg = sbuf; + } else { + /* too big to allocate from stack */ + mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL); + if (NULL == mbuf) + return -ENOMEM; + parg = mbuf; + } + + err = -EFAULT; + if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) + goto out; + break; + } + + /* call driver */ + mutex_lock(&dvbdev_mutex); + if ((err = dvb_ca_ioctl(file, cmd, parg)) == -ENOIOCTLCMD) + err = -EINVAL; + mutex_unlock(&dvbdev_mutex); + + if (err < 0) + goto out; + + /* Copy results into user buffer */ + switch (_IOC_DIR(cmd)) + { + case _IOC_READ: + case (_IOC_WRITE | _IOC_READ): + if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) + err = -EFAULT; + break; + } + +out: + kfree(mbuf); + return err; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-2.c b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-2.c new file mode 100644 index 00000000000..c1bf748ae15 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-2.c @@ -0,0 +1,93 @@ +/* See notes in this header. */ +#include "taint-CVE-2011-0521.h" + +// TODO: remove need for this option +/* { dg-additional-options "-fanalyzer-checker=taint" } */ + +/* Adapted from drivers/media/dvb/ttpci/av7110_ca.c */ + +int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned long arg = (unsigned long) parg; + + /* case CA_GET_SLOT_INFO: */ + { + ca_slot_info_t *info=(ca_slot_info_t *)parg; + + if (info->num > 1) + return -EINVAL; + av7110->ci_slot[info->num].num = info->num; /* { dg-warning "attacker-controlled value" "" { xfail *-*-* } } */ + av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? + CA_CI_LINK : CA_CI; + memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); + } + return 0; +} + +/* Adapted from drivers/media/dvb/dvb-core/dvbdev.c + Somewhat simplified: rather than pass in a callback that can + be dvb_ca_ioctl, call dvb_ca_ioctl directly. */ + +static DEFINE_MUTEX(dvbdev_mutex); + +int dvb_usercopy(struct file *file, + unsigned int cmd, unsigned long arg) +{ + char sbuf[128]; + void *mbuf = NULL; + void *parg = NULL; + int err = -1; + + /* Copy arguments into temp kernel buffer */ + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: + /* + * For this command, the pointer is actually an integer + * argument. + */ + parg = (void *) arg; + break; + case _IOC_READ: /* some v4l ioctls are marked wrong ... */ + case _IOC_WRITE: + case (_IOC_WRITE | _IOC_READ): + if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { + parg = sbuf; + } else { + /* too big to allocate from stack */ + mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL); + if (NULL == mbuf) + return -ENOMEM; + parg = mbuf; + } + + err = -EFAULT; + if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) + goto out; + break; + } + + /* call driver */ + mutex_lock(&dvbdev_mutex); + if ((err = dvb_ca_ioctl(file, cmd, parg)) == -ENOIOCTLCMD) + err = -EINVAL; + mutex_unlock(&dvbdev_mutex); + + if (err < 0) + goto out; + + /* Copy results into user buffer */ + switch (_IOC_DIR(cmd)) + { + case _IOC_READ: + case (_IOC_WRITE | _IOC_READ): + if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) + err = -EFAULT; + break; + } + +out: + kfree(mbuf); + return err; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-3-fixed.c b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-3-fixed.c new file mode 100644 index 00000000000..0147759f4df --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-3-fixed.c @@ -0,0 +1,56 @@ +/* See notes in this header. */ +#include "taint-CVE-2011-0521.h" + +// TODO: remove need for this option +/* { dg-additional-options "-fanalyzer-checker=taint" } */ + +/* Adapted from drivers/media/dvb/ttpci/av7110_ca.c */ + +int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned long arg = (unsigned long) parg; + + /* case CA_GET_SLOT_INFO: */ + { + ca_slot_info_t *info=(ca_slot_info_t *)parg; + + if (info->num < 0 || info->num > 1) + return -EINVAL; + av7110->ci_slot[info->num].num = info->num; /* { dg-bogus "attacker-controlled value" } */ + av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? + CA_CI_LINK : CA_CI; + memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); + } + return 0; +} + +/* Adapted from drivers/media/dvb/dvb-core/dvbdev.c + Further simplified from -2; always use an on-stack buffer. */ + +static DEFINE_MUTEX(dvbdev_mutex); + +int dvb_usercopy(struct file *file, + unsigned int cmd, unsigned long arg) +{ + char sbuf[128]; + void *parg = sbuf; + int err = -EFAULT; + if (copy_from_user(parg, (void __user *)arg, sizeof(sbuf))) + goto out; + + mutex_lock(&dvbdev_mutex); + if ((err = dvb_ca_ioctl(file, cmd, parg)) == -ENOIOCTLCMD) + err = -EINVAL; + mutex_unlock(&dvbdev_mutex); + + if (err < 0) + goto out; + + if (copy_to_user((void __user *)arg, parg, sizeof(sbuf))) + err = -EFAULT; + +out: + return err; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-3.c b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-3.c new file mode 100644 index 00000000000..c53071afbab --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-3.c @@ -0,0 +1,57 @@ +/* See notes in this header. */ +#include "taint-CVE-2011-0521.h" + +// TODO: remove need for this option +/* { dg-additional-options "-fanalyzer-checker=taint" } */ + +/* Adapted from drivers/media/dvb/ttpci/av7110_ca.c */ + +int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned long arg = (unsigned long) parg; + + /* case CA_GET_SLOT_INFO: */ + { + ca_slot_info_t *info=(ca_slot_info_t *)parg; + + if (info->num > 1) + return -EINVAL; + av7110->ci_slot[info->num].num = info->num; /* { dg-warning "attacker-controlled value" "" { xfail *-*-* } } */ + // TODO(xfail) + av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? + CA_CI_LINK : CA_CI; + memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); + } + return 0; +} + +/* Adapted from drivers/media/dvb/dvb-core/dvbdev.c + Further simplified from -2; always use an on-stack buffer. */ + +static DEFINE_MUTEX(dvbdev_mutex); + +int dvb_usercopy(struct file *file, + unsigned int cmd, unsigned long arg) +{ + char sbuf[128]; + void *parg = sbuf; + int err = -EFAULT; + if (copy_from_user(parg, (void __user *)arg, sizeof(sbuf))) + goto out; + + mutex_lock(&dvbdev_mutex); + if ((err = dvb_ca_ioctl(file, cmd, parg)) == -ENOIOCTLCMD) + err = -EINVAL; + mutex_unlock(&dvbdev_mutex); + + if (err < 0) + goto out; + + if (copy_to_user((void __user *)arg, parg, sizeof(sbuf))) + err = -EFAULT; + +out: + return err; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-4.c b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-4.c new file mode 100644 index 00000000000..eab95929cd6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-4.c @@ -0,0 +1,40 @@ +/* See notes in this header. */ +#include "taint-CVE-2011-0521.h" + +// TODO: remove need for this option +/* { dg-additional-options "-fanalyzer-checker=taint" } */ + +/* Adapted from dvb_ca_ioctl in drivers/media/dvb/ttpci/av7110_ca.c and + dvb_usercopy in drivers/media/dvb/dvb-core/dvbdev.c + + Further simplified from -3; merge into a single function; drop the mutex, + remove control flow. */ + +int test_1(struct file *file, unsigned int cmd, unsigned long arg) +{ + char sbuf[128]; + void *parg = sbuf; + + copy_from_user(parg, (void __user *)arg, sizeof(sbuf)); + + { + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + unsigned long arg = (unsigned long) parg; + + /* case CA_GET_SLOT_INFO: */ + ca_slot_info_t *info=(ca_slot_info_t *)parg; + + if (info->num > 1) + return -EINVAL; + av7110->ci_slot[info->num].num = info->num; /* { dg-warning "attacker-controlled value" "" { xfail *-*-* } } */ + // TODO(xfail) + av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? + CA_CI_LINK : CA_CI; + memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); + } + + copy_to_user((void __user *)arg, parg, sizeof(sbuf)); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-5.c b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-5.c new file mode 100644 index 00000000000..9cf465204cc --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-5.c @@ -0,0 +1,42 @@ +/* See notes in this header. */ +#include "taint-CVE-2011-0521.h" + +// TODO: remove need for this option +/* { dg-additional-options "-fanalyzer-checker=taint" } */ + +/* Adapted from dvb_ca_ioctl in drivers/media/dvb/ttpci/av7110_ca.c and + dvb_usercopy in drivers/media/dvb/dvb-core/dvbdev.c + + Further simplified from -4; avoid parg and the cast to char[128]. */ + +int test_1(struct file *file, unsigned int cmd, unsigned long arg) +{ + ca_slot_info_t sbuf; + + if (copy_from_user(&sbuf, (void __user *)arg, sizeof(sbuf)) != 0) + return -1; + + { + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + + /* case CA_GET_SLOT_INFO: */ + ca_slot_info_t *info= &sbuf; + + __analyzer_dump_state ("taint", info->num); /* { dg-warning "tainted" } */ + + if (info->num > 1) + return -EINVAL; + + __analyzer_dump_state ("taint", info->num); /* { dg-warning "has_ub" } */ + + av7110->ci_slot[info->num].num = info->num; /* { dg-warning "use of attacker-controlled value '\\*info\\.num' in array lookup without checking for negative" } */ + av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? + CA_CI_LINK : CA_CI; + memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); + } + + copy_to_user((void __user *)arg, &sbuf, sizeof(sbuf)); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-6.c b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-6.c new file mode 100644 index 00000000000..35a16af2316 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521-6.c @@ -0,0 +1,37 @@ +/* See notes in this header. */ +#include "taint-CVE-2011-0521.h" + +// TODO: remove need for this option +/* { dg-additional-options "-fanalyzer-checker=taint" } */ + +/* Adapted from dvb_ca_ioctl in drivers/media/dvb/ttpci/av7110_ca.c and + dvb_usercopy in drivers/media/dvb/dvb-core/dvbdev.c + + Further simplified from -5; remove all control flow. */ + +int test_1(struct file *file, unsigned int cmd, unsigned long arg) +{ + ca_slot_info_t sbuf; + + if (copy_from_user(&sbuf, (void __user *)arg, sizeof(sbuf)) != 0) + return -1; + + { + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + + /* case CA_GET_SLOT_INFO: */ + ca_slot_info_t *info= &sbuf; + + __analyzer_dump_state ("taint", info->num); /* { dg-warning "tainted" } */ + + av7110->ci_slot[info->num].num = info->num; /* { dg-warning "use of attacker-controlled value '\\*info\\.num' in array lookup without bounds checking" } */ + av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? + CA_CI_LINK : CA_CI; + memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); + } + + copy_to_user((void __user *)arg, &sbuf, sizeof(sbuf)); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521.h b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521.h new file mode 100644 index 00000000000..0d79f9f9e08 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-0521.h @@ -0,0 +1,136 @@ +/* Shared header for the various taint-CVE-2011-0521-*.c tests. + These are a series of successively simpler reductions of the reproducer. + Ideally the analyzer would detect the issue in all of the testcases, + but currently requires some simplification of the code to do so. + + "The dvb_ca_ioctl function in drivers/media/dvb/ttpci/av7110_ca.c in the + Linux kernel before 2.6.38-rc2 does not check the sign of a certain integer + field, which allows local users to cause a denial of service (memory + corruption) or possibly have unspecified other impact via a negative value." + + Adapted from Linux 2.6.38, which is under the GPLv2. + + Fixed in e.g. cb26a24ee9706473f31d34cc259f4dcf45cd0644 on linux-2.6.38.y */ + +#include +#include "test-uaccess.h" +#include "analyzer-decls.h" + +typedef unsigned int u32; + +/* Adapted from include/linux/compiler.h */ + +#define __force + +/* Adapted from include/asm-generic/errno-base.h */ + +#define ENOMEM 12 /* Out of memory */ +#define EFAULT 14 /* Bad address */ +#define ENODEV 19 /* No such device */ +#define EINVAL 22 /* Invalid argument */ + +/* Adapted from include/linux/errno.h */ + +#define ENOIOCTLCMD 515 /* No ioctl command */ + +/* Adapted from include/linux/fs.h */ + +struct file { + /* [...snip...] */ + void *private_data; + /* [...snip...] */ +}; + +/* Adapted from drivers/media/dvb/dvb-core/dvbdev.h */ + +struct dvb_device { + /* [...snip...] */ + int (*kernel_ioctl)(struct file *file, unsigned int cmd, void *arg); + + void *priv; +}; + + +/* Adapted from include/linux/dvb/ca.h */ + +typedef struct ca_slot_info { + int num; /* slot number */ + + int type; /* CA interface this slot supports */ +#define CA_CI 1 /* CI high level interface */ +#define CA_CI_LINK 2 /* CI link layer level interface */ + /* [...snip...] */ +} ca_slot_info_t; + + +/* Adapted from drivers/media/dvb/ttpci/av7110.h */ + +struct av7110 { + /* [...snip...] */ + ca_slot_info_t ci_slot[2]; + /* [...snip...] */ + u32 arm_app; + /* [...snip...] */ +}; + +/* Adapted from drivers/media/dvb/ttpci/av7110_hw.h */ + +#define FW_CI_LL_SUPPORT(arm_app) ((arm_app) & 0x80000000) + +/* Adapted from include/asm-generic/ioctl.h */ + +#define _IOC_NRBITS 8 +#define _IOC_TYPEBITS 8 + +#define _IOC_SIZEBITS 14 +#define _IOC_DIRBITS 2 + +#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) +#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) +#define _IOC_NRSHIFT 0 +#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) +#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) +#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) + +#define _IOC_NONE 0U +#define _IOC_WRITE 1U +#define _IOC_READ 2U + +#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) +#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) + +/* Adapted from include/linux/mutex.h */ + +struct mutex { + /* [...snip...] */ +}; + +#define __MUTEX_INITIALIZER(lockname) \ + { /* [...snip...] */ } + +#define DEFINE_MUTEX(mutexname) \ + struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) + +extern void mutex_lock(struct mutex *lock); +extern void mutex_unlock(struct mutex *lock); + +/* Adapted from include/linux/types.h */ + +#define __bitwise__ +typedef unsigned __bitwise__ gfp_t; + +/* Adapted from include/linux/gfp.h */ + +#define ___GFP_WAIT 0x10u +#define ___GFP_IO 0x40u +#define ___GFP_FS 0x80u +#define __GFP_WAIT ((__force gfp_t)___GFP_WAIT) +#define __GFP_IO ((__force gfp_t)___GFP_IO) +#define __GFP_FS ((__force gfp_t)___GFP_FS) +#define GFP_KERNEL (__GFP_WAIT | __GFP_IO | __GFP_FS) + +/* Adapted from include/linux/slab.h */ + +void kfree(const void *); +void *kmalloc(size_t size, gfp_t flags) + __attribute__((malloc (kfree))); diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-antipatterns-1.c b/gcc/testsuite/gcc.dg/analyzer/taint-antipatterns-1.c new file mode 100644 index 00000000000..5e81410a847 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/taint-antipatterns-1.c @@ -0,0 +1,137 @@ +// TODO: remove need for this: +/* { dg-additional-options "-fanalyzer-checker=taint" } */ + +#include "test-uaccess.h" + +/* Adapted and simplified decls from linux kernel headers. */ + +typedef unsigned char u8; +typedef unsigned __INT16_TYPE__ u16; +typedef unsigned __INT32_TYPE__ u32; +typedef signed __INT32_TYPE__ s32; +typedef __SIZE_TYPE__ size_t; + +#define EFAULT 14 + +typedef unsigned int gfp_t; +#define GFP_KERNEL 0 + +void kfree(const void *); +void *kmalloc(size_t size, gfp_t flags) + __attribute__((malloc (kfree))); + +/* Adapted from antipatterns.ko:taint.c (GPL-v2.0). */ + +struct cmd_1 +{ + u32 idx; + u32 val; +}; + +static u32 arr[16]; + +int taint_array_access(void __user *src) +{ + struct cmd_1 cmd; + if (copy_from_user(&cmd, src, sizeof(cmd))) + return -EFAULT; + /* + * cmd.idx is an unsanitized value from user-space, hence + * this is an arbitrary kernel memory access. + */ + arr[cmd.idx] = cmd.val; /* { dg-warning "use of attacker-controlled value 'cmd.idx' in array lookup without upper-bounds checking" } */ + return 0; +} + +struct cmd_2 +{ + s32 idx; + u32 val; +}; + +int taint_signed_array_access(void __user *src) +{ + struct cmd_2 cmd; + if (copy_from_user(&cmd, src, sizeof(cmd))) + return -EFAULT; + if (cmd.idx >= 16) + return -EFAULT; + + /* + * cmd.idx hasn't been checked for being negative, hence + * this is an arbitrary kernel memory access. + */ + arr[cmd.idx] = cmd.val; /* { dg-warning "use of attacker-controlled value 'cmd.idx' in array lookup without checking for negative" } */ + return 0; +} + +struct cmd_s32_binop +{ + s32 a; + s32 b; + s32 result; +}; + +int taint_divide_by_zero_direct(void __user *uptr) +{ + struct cmd_s32_binop cmd; + if (copy_from_user(&cmd, uptr, sizeof(cmd))) + return -EFAULT; + + /* cmd.b is attacker-controlled and could be zero */ + cmd.result = cmd.a / cmd.b; /* { dg-warning "use of attacker-controlled value 'cmd.b' as divisor without checking for zero" } */ + + if (copy_to_user (uptr, &cmd, sizeof(cmd))) + return -EFAULT; + return 0; +} + +int taint_divide_by_zero_compound(void __user *uptr) +{ + struct cmd_s32_binop cmd; + if (copy_from_user(&cmd, uptr, sizeof(cmd))) + return -EFAULT; + + /* + * cmd.b is attacker-controlled and could be -1, hence + * the divisor could be zero + */ + cmd.result = cmd.a / (cmd.b + 1); /* { dg-warning "use of attacker-controlled value 'cmd.b \\+ 1' as divisor without checking for zero" } */ + + if (copy_to_user (uptr, &cmd, sizeof(cmd))) + return -EFAULT; + return 0; +} + +int taint_mod_by_zero_direct(void __user *uptr) +{ + struct cmd_s32_binop cmd; + if (copy_from_user(&cmd, uptr, sizeof(cmd))) + return -EFAULT; + + /* cmd.b is attacker-controlled and could be zero */ + cmd.result = cmd.a % cmd.b; /* { dg-warning "use of attacker-controlled value 'cmd.b' as divisor without checking for zero" } */ + + if (copy_to_user (uptr, &cmd, sizeof(cmd))) + return -EFAULT; + return 0; +} + +int taint_mod_by_zero_compound(void __user *uptr) +{ + struct cmd_s32_binop cmd; + if (copy_from_user(&cmd, uptr, sizeof(cmd))) + return -EFAULT; + + /* + * cmd.b is attacker-controlled and could be -1, hence + * the divisor could be zero + */ + cmd.result = cmd.a % (cmd.b + 1); /* { dg-warning "use of attacker-controlled value 'cmd.b \\+ 1' as divisor without checking for zero" } */ + + if (copy_to_user (uptr, &cmd, sizeof(cmd))) + return -EFAULT; + return 0; +} + +/* TODO: etc. */ diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-read-through-untrusted-ptr-1.c b/gcc/testsuite/gcc.dg/analyzer/taint-read-through-untrusted-ptr-1.c new file mode 100644 index 00000000000..cd2911683e6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/taint-read-through-untrusted-ptr-1.c @@ -0,0 +1,37 @@ +// TODO: remove need for this: +/* { dg-additional-options "-fanalyzer-checker=taint" } */ + +#include "test-uaccess.h" + +typedef unsigned __INT32_TYPE__ u32; + +struct cmd_1 +{ + u32 idx; + u32 val; +}; + +u32 arr[16]; + +int taint_array_access_1 (struct cmd_1 __user *src) +{ + /* + * src->idx is an unsanitized value from user-space, hence + * this is an arbitrary kernel memory access. + */ + arr[src->idx] = src->val; /* { dg-warning "use of attacker-controlled value '\\*src.idx' in array lookup without upper-bounds checking" } */ + return 0; +} + +int taint_array_access_2 (struct cmd_1 __user *src) +{ + struct cmd_1 cmd; + cmd = *src; + + /* + * cmd.idx is an unsanitized value from user-space, hence + * this is an arbitrary kernel memory access. + */ + arr[cmd.idx] = cmd.val; /* { dg-warning "use of attacker-controlled value '\\*src.idx' in array lookup without upper-bounds checking" } */ + return 0; +}