[PATCH 5/6] analyzer: use region::untrusted_p in taint detection
David Malcolm
dmalcolm@redhat.com
Sat Nov 13 20:37:31 GMT 2021
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 <dmalcolm@redhat.com>
---
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 <string.h>
+#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;
+}
--
2.26.3
More information about the Gcc-patches
mailing list