Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/36dd40dcbeeb83964e66b65be7a96136 to your computer and use it in GitHub Desktop.
Save anonymous/36dd40dcbeeb83964e66b65be7a96136 to your computer and use it in GitHub Desktop.
Pointer dereference checker
From 5b34eb8fa70b8866fa56c037343f8217c2d5cdf3 Mon Sep 17 00:00:00 2001
From: Nicolas Iooss <nicolas.iooss_linux@m4x.org>
Date: Wed, 3 Aug 2016 23:39:50 +0200
Subject: [PATCH 1/1] Introduce a pointer dereference checker GCC plugin
---
arch/Kconfig | 6 +
arch/arm/boot/compressed/decompress.c | 2 +
arch/arm/boot/compressed/string.c | 6 +
arch/arm/include/asm/string.h | 12 +
arch/x86/events/core.c | 9 +-
arch/x86/include/asm/string_64.h | 17 +
arch/x86/kernel/x8664_ksyms_64.c | 4 +
drivers/acpi/property.c | 6 +-
drivers/base/property.c | 2 +-
drivers/block/DAC960.c | 2 +-
drivers/block/nbd.c | 2 +-
drivers/clocksource/time-pistachio.c | 9 +-
drivers/dma/cppi41.c | 4 +-
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 2 +-
drivers/gpu/drm/drm_bufs.c | 12 +-
drivers/gpu/drm/radeon/radeon_cs.c | 2 +-
drivers/iio/industrialio-buffer.c | 2 +-
drivers/infiniband/hw/i40iw/i40iw_verbs.c | 2 +-
drivers/isdn/i4l/isdn_common.c | 4 +-
drivers/misc/ibmasm/lowlevel.c | 4 +-
drivers/misc/mic/scif/scif_fd.c | 6 +-
drivers/net/wireless/ti/wlcore/main.c | 15 +-
drivers/nvdimm/namespace_devs.c | 2 +-
drivers/scsi/csiostor/csio_hw.c | 6 +-
drivers/scsi/pm8001/pm8001_ctl.c | 4 +-
drivers/scsi/pm8001/pm8001_hwi.c | 2 +-
drivers/staging/gdm724x/hci_packet.h | 2 +-
drivers/staging/lustre/lustre/osc/osc_request.c | 6 +-
drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c | 2 +-
drivers/staging/rtl8192e/rtl8192e/rtl_core.c | 4 +-
drivers/staging/rtl8192u/r8192U_core.c | 2 +-
drivers/staging/rtl8192u/r819xU_cmdpkt.c | 2 +-
drivers/staging/rtl8192u/r819xU_firmware.c | 2 +-
drivers/thermal/int340x_thermal/int3402_thermal.c | 2 +-
drivers/usb/core/devio.c | 12 +-
drivers/xen/xenbus/xenbus_xs.c | 4 +-
fs/cifs/cifsacl.c | 12 +-
fs/hfs/dir.c | 2 +-
include/linux/compiler.h | 23 ++
kernel/debug/kdb/kdb_support.c | 10 +-
kernel/trace/trace.c | 7 +-
kernel/trace/trace_functions.c | 6 +-
kernel/trace/trace_kprobe.c | 13 +-
kernel/workqueue.c | 2 +-
mm/percpu-vm.c | 7 +-
mm/slub.c | 6 +-
net/bluetooth/mgmt.c | 8 +-
net/core/drop_monitor.c | 10 +-
net/ipv4/netfilter/nf_nat_snmp_basic.c | 8 +-
net/phonet/socket.c | 7 +-
net/rxrpc/key.c | 2 +-
net/rxrpc/rxkad.c | 2 +-
scripts/Makefile.gcc-plugins | 2 +
scripts/gcc-plugins/deref_checker_plugin.c | 384 ++++++++++++++++++++++
sound/core/pcm_native.c | 2 +-
55 files changed, 621 insertions(+), 74 deletions(-)
create mode 100644 scripts/gcc-plugins/deref_checker_plugin.c
diff --git a/arch/Kconfig b/arch/Kconfig
index e9c9334507dd..1e5ca7fc1ab9 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -394,6 +394,12 @@ config GCC_PLUGIN_SANCOV
gcc-4.5 on). It is based on the commit "Add fuzzing coverage support"
by Dmitry Vyukov <dvyukov@google.com>.
+config GCC_PLUGIN_DEREF_CHECKER
+ bool "Check pointer derefecencing in function calls"
+ depends on GCC_PLUGINS
+ help
+ Check function calls involving pointers about some consistencies.
+
config HAVE_CC_STACKPROTECTOR
bool
help
diff --git a/arch/arm/boot/compressed/decompress.c b/arch/arm/boot/compressed/decompress.c
index a0765e7ed6c7..20619f3b2e0c 100644
--- a/arch/arm/boot/compressed/decompress.c
+++ b/arch/arm/boot/compressed/decompress.c
@@ -46,8 +46,10 @@ extern char * strstr(const char * s1, const char *s2);
#endif
#ifdef CONFIG_KERNEL_XZ
+#ifndef CONFIG_GCC_PLUGIN_DEREF_CHECKER
#define memmove memmove
#define memcpy memcpy
+#endif
#include "../../../../lib/decompress_unxz.c"
#endif
diff --git a/arch/arm/boot/compressed/string.c b/arch/arm/boot/compressed/string.c
index 689467448736..bc62334c277b 100644
--- a/arch/arm/boot/compressed/string.c
+++ b/arch/arm/boot/compressed/string.c
@@ -6,6 +6,12 @@
#include <linux/string.h>
+#ifdef CONFIG_GCC_PLUGIN_DEREF_CHECKER
+/* Disable definitions with __same_pointer_depths_chk */
+#undef memcpy
+#undef memmove
+#endif
+
void *memcpy(void *__dest, __const void *__src, size_t __n)
{
int i = 0;
diff --git a/arch/arm/include/asm/string.h b/arch/arm/include/asm/string.h
index cf4f3aad0fc1..18589ae11bcb 100644
--- a/arch/arm/include/asm/string.h
+++ b/arch/arm/include/asm/string.h
@@ -38,4 +38,16 @@ extern void __memzero(void *ptr, __kernel_size_t n);
(__p); \
})
+#ifdef CONFIG_GCC_PLUGIN_DEREF_CHECKER
+#define memcpy(t, f, l) ({ __same_pointer_depths_chk(t, f); memcpy(t, f, l); })
+#define memmove(t, f, l) ({ __same_pointer_depths_chk(t, f); memmove(t, f, l); })
+/* Tell the plugin that the difference is intentional */
+static noinline void *memcpy_with_cast(void *dest, const void *src, size_t count)
+{
+ return memcpy(dest, src, count);
+}
+#else
+#define memcpy_with_cast memcpy
+#endif
+
#endif
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index d0efb5cb1b00..2f7dd8e1dde4 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -2408,7 +2408,14 @@ perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs
if (!access_ok(VERIFY_READ, fp, sizeof(*fp) * 2))
break;
- bytes = __copy_from_user_nmi(&frame.next_frame, fp, sizeof(*fp));
+ /* make the pointer checker plugin happy about
+ * copying userspace data into a pointer
+ */
+ {
+ unsigned long next_frame;
+ bytes = __copy_from_user_nmi(&next_frame, fp, sizeof(*fp));
+ frame.next_frame = (struct stack_frame *)next_frame;
+ }
if (bytes != 0)
break;
bytes = __copy_from_user_nmi(&frame.return_address, fp + 1, sizeof(*fp));
diff --git a/arch/x86/include/asm/string_64.h b/arch/x86/include/asm/string_64.h
index 90dbbd9666d4..a8b2f7de29c1 100644
--- a/arch/x86/include/asm/string_64.h
+++ b/arch/x86/include/asm/string_64.h
@@ -9,6 +9,7 @@
static __always_inline void *__inline_memcpy(void *to, const void *from, size_t n)
{
unsigned long d0, d1, d2;
+ __same_pointer_depths_chk(to, from);
asm volatile("rep ; movsl\n\t"
"testb $2,%b4\n\t"
"je 1f\n\t"
@@ -65,6 +66,18 @@ char *strcpy(char *dest, const char *src);
char *strcat(char *dest, const char *src);
int strcmp(const char *cs, const char *ct);
+#ifdef CONFIG_GCC_PLUGIN_DEREF_CHECKER
+#define __memcpy(t, f, l) ({ __same_pointer_depths_chk(t, f); __memcpy(t, f, l); })
+#define __memmove(t, f, l) ({ __same_pointer_depths_chk(t, f); __memmove(t, f, l); })
+/* Tell the plugin that the difference is intentional */
+static noinline void *memcpy_with_cast(void *dest, const void *src, size_t count)
+{
+ return memcpy(dest, src, count);
+}
+#else
+#define memcpy_with_cast memcpy
+#endif
+
#if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__)
/*
@@ -76,6 +89,10 @@ int strcmp(const char *cs, const char *ct);
#define memcpy(dst, src, len) __memcpy(dst, src, len)
#define memmove(dst, src, len) __memmove(dst, src, len)
#define memset(s, c, n) __memset(s, c, n)
+
+#elif defined(CONFIG_GCC_PLUGIN_DEREF_CHECKER)
+#define memcpy(t, f, l) ({ __same_pointer_depths_chk(t, f); memcpy(t, f, l); })
+#define memmove(t, f, l) ({ __same_pointer_depths_chk(t, f); memmove(t, f, l); })
#endif
/**
diff --git a/arch/x86/kernel/x8664_ksyms_64.c b/arch/x86/kernel/x8664_ksyms_64.c
index 95e49f6e4fc3..87a48a710bf3 100644
--- a/arch/x86/kernel/x8664_ksyms_64.c
+++ b/arch/x86/kernel/x8664_ksyms_64.c
@@ -56,6 +56,10 @@ EXPORT_SYMBOL(__sw_hweight64);
#undef memset
#undef memmove
+#ifdef CONFIG_GCC_PLUGIN_DEREF_CHECKER
+#undef __memcpy
+#undef __memmove
+#endif
extern void *__memset(void *, int, __kernel_size_t);
extern void *__memcpy(void *, const void *, __kernel_size_t);
extern void *__memmove(void *, const void *, __kernel_size_t);
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
index f2fd3fee588a..ae7635820334 100644
--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -758,7 +758,11 @@ static int acpi_data_prop_read(struct acpi_device_data *data,
ret = acpi_copy_property_array_u64(items, (u64 *)val, nval);
break;
case DEV_PROP_STRING:
- ret = acpi_copy_property_array_string(items, (char **)val, nval);
+ {
+ /* Play nice with the pointer deref checker */
+ char **valp = (char **)val;
+ ret = acpi_copy_property_array_string(items, valp, nval);
+ }
break;
default:
ret = -EINVAL;
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 43a36d68c3fd..5ecbb90afb15 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -146,7 +146,7 @@ static int pset_prop_read_string_array(struct property_set *pset,
const char *propname,
const char **strings, size_t nval)
{
- void *pointer;
+ char **pointer; /* Play nice with the pointer deref checker */
size_t length = nval * sizeof(*strings);
pointer = pset_prop_find(pset, propname, length);
diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c
index 811e11c82f32..12890c4e9a3e 100644
--- a/drivers/block/DAC960.c
+++ b/drivers/block/DAC960.c
@@ -3731,7 +3731,7 @@ static void DAC960_V1_ProcessCompletedCommand(DAC960_Command_T *Command)
Controller->V1.PendingRebuildFlag = NewEnquiry->RebuildFlag;
Controller->V1.RebuildFlagPending = true;
}
- memcpy(&Controller->V1.Enquiry, &Controller->V1.NewEnquiry,
+ memcpy(&Controller->V1.Enquiry, Controller->V1.NewEnquiry, /* BUG! */
sizeof(DAC960_V1_Enquiry_T));
}
else if (CommandOpcode == DAC960_V1_PerformEventLogOperation)
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index a9e398019f38..a0691b818072 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -298,7 +298,7 @@ static int nbd_send_req(struct nbd_device *nbd, struct request *req)
request.from = cpu_to_be64((u64)blk_rq_pos(req) << 9);
request.len = htonl(size);
}
- memcpy(request.handle, &req, sizeof(req));
+ memcpy_with_cast(request.handle, &req, sizeof(req));
dev_dbg(nbd_to_dev(nbd), "request %p: sending control (%s@%llu,%uB)\n",
req, nbdcmd_to_ascii(type),
diff --git a/drivers/clocksource/time-pistachio.c b/drivers/clocksource/time-pistachio.c
index a7d9a08e4b0e..55594c9dcbd5 100644
--- a/drivers/clocksource/time-pistachio.c
+++ b/drivers/clocksource/time-pistachio.c
@@ -202,10 +202,11 @@ static int __init pistachio_clksrc_of_init(struct device_node *node)
rate = clk_get_rate(fast_clk);
/* Disable irq's for clocksource usage */
- gpt_writel(&pcs_gpt.base, 0, TIMER_IRQ_MASK, 0);
- gpt_writel(&pcs_gpt.base, 0, TIMER_IRQ_MASK, 1);
- gpt_writel(&pcs_gpt.base, 0, TIMER_IRQ_MASK, 2);
- gpt_writel(&pcs_gpt.base, 0, TIMER_IRQ_MASK, 3);
+ /* BUG? */
+ gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 0);
+ gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 1);
+ gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 2);
+ gpt_writel(pcs_gpt.base, 0, TIMER_IRQ_MASK, 3);
/* Enable timer block */
writel(TIMER_ME_GLOBAL, pcs_gpt.base);
diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c
index 4b2317426c8e..497178bab919 100644
--- a/drivers/dma/cppi41.c
+++ b/drivers/dma/cppi41.c
@@ -255,12 +255,12 @@ static struct cppi41_channel *desc_to_chan(struct cppi41_dd *cdd, u32 desc)
return c;
}
-static void cppi_writel(u32 val, void *__iomem *mem)
+static void cppi_writel(u32 val, void __iomem *mem)
{
__raw_writel(val, mem);
}
-static u32 cppi_readl(void *__iomem *mem)
+static u32 cppi_readl(void __iomem *mem)
{
return __raw_readl(mem);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index 0307ff5887c5..2f27a715f109 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -155,7 +155,7 @@ int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data)
}
for (i = 0; i < p->nchunks; i++) {
- struct drm_amdgpu_cs_chunk __user **chunk_ptr = NULL;
+ struct drm_amdgpu_cs_chunk __user *chunk_ptr = NULL; /* harmless BUG */
struct drm_amdgpu_cs_chunk user_chunk;
uint32_t __user *cdata;
diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c
index c3a12cd8bd0d..2c5635b95ebf 100644
--- a/drivers/gpu/drm/drm_bufs.c
+++ b/drivers/gpu/drm/drm_bufs.c
@@ -1513,10 +1513,14 @@ int drm_legacy_mapbufs(struct drm_device *dev, void *data,
goto done;
}
address = virtual + dma->buflist[i]->offset; /* *** */
- if (copy_to_user(&request->list[i].address,
- &address, sizeof(address))) {
- retcode = -EFAULT;
- goto done;
+ {
+ /* Play nice with the pointer deref checker */
+ void __user **src = (void __user **)&address;
+ if (copy_to_user(&request->list[i].address,
+ src, sizeof(address))) {
+ retcode = -EFAULT;
+ goto done;
+ }
}
}
}
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index 510ea371dacc..a67f9d99643a 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -285,7 +285,7 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
return -ENOMEM;
}
for (i = 0; i < p->nchunks; i++) {
- struct drm_radeon_cs_chunk __user **chunk_ptr = NULL;
+ struct drm_radeon_cs_chunk __user *chunk_ptr = NULL;
struct drm_radeon_cs_chunk user_chunk;
uint32_t __user *cdata;
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 90462fcf5436..ebb38c9c2245 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1143,7 +1143,7 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
goto error_free_scan_mask;
}
if (buffer->scan_el_attrs)
- memcpy(buffer->scan_el_group.attrs, buffer->scan_el_attrs,
+ memcpy(buffer->scan_el_group.attrs, buffer->scan_el_attrs->attrs, /* BUG? I did not understand this code */
sizeof(buffer->scan_el_group.attrs[0])*attrcount_orig);
attrn = attrcount_orig;
diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c
index 6329c971c22f..4ee2655041e6 100644
--- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c
+++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c
@@ -2501,7 +2501,7 @@ static int i40iw_get_hw_stats(struct ib_device *ibdev,
return -ENOSYS;
}
- memcpy(&stats->value[0], &hw_stats, sizeof(*hw_stats));
+ memcpy(&stats->value[0], hw_stats, sizeof(*hw_stats)); /* BUG? */
return stats->num_counters;
}
diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c
index 9b856e1890d1..ac2a46dbb669 100644
--- a/drivers/isdn/i4l/isdn_common.c
+++ b/drivers/isdn/i4l/isdn_common.c
@@ -1655,7 +1655,9 @@ isdn_ioctl(struct file *file, uint cmd, ulong arg)
return -EINVAL;
case IIOCDBGVAR:
if (arg) {
- if (copy_to_user(argp, &dev, sizeof(ulong)))
+ /* Make the pointer checker plugin happy about copying a pointer into a buffer */
+ ulong dev_value = (ulong)dev;
+ if (copy_to_user(argp, &dev_value, sizeof(ulong)))
return -EFAULT;
return 0;
} else
diff --git a/drivers/misc/ibmasm/lowlevel.c b/drivers/misc/ibmasm/lowlevel.c
index 5319ea261c05..a1863712df83 100644
--- a/drivers/misc/ibmasm/lowlevel.c
+++ b/drivers/misc/ibmasm/lowlevel.c
@@ -47,7 +47,7 @@ int ibmasm_send_i2o_message(struct service_processor *sp)
message = get_i2o_message(sp->base_address, mfa);
memcpy_toio(&message->header, &header, sizeof(struct i2o_header));
- memcpy_toio(&message->data, command->buffer, command_size);
+ memcpy_toio(message->data, command->buffer, command_size); /* BUG? */
set_mfa_inbound(sp->base_address, mfa);
@@ -74,7 +74,7 @@ irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id)
mfa = get_mfa_outbound(base_address);
if (valid_mfa(mfa)) {
struct i2o_message *msg = get_i2o_message(base_address, mfa);
- ibmasm_receive_message(sp, &msg->data, incoming_data_size(msg));
+ ibmasm_receive_message(sp, msg->data, incoming_data_size(msg)); /* BUG? */
} else
dbg("didn't get a valid MFA\n");
diff --git a/drivers/misc/mic/scif/scif_fd.c b/drivers/misc/mic/scif/scif_fd.c
index f7e826142a72..d011669a6a16 100644
--- a/drivers/misc/mic/scif/scif_fd.c
+++ b/drivers/misc/mic/scif/scif_fd.c
@@ -173,7 +173,11 @@ static long scif_fdioctl(struct file *f, unsigned int cmd, unsigned long arg)
struct list_head *pos, *tmpq;
/* Finally replace the pointer to the accepted endpoint */
- if (copy_from_user(&newep, argp, sizeof(void *)))
+ /* Slient the pointer deref checker here even though the code looks strange:
+ * how is the kernel pointer which is obtained by userspace sanitized?
+ */
+ void *__user *argpp = (void *__user *)argp;
+ if (copy_from_user(&newep, argpp, sizeof(void *)))
return -EFAULT;
/* Remove form the user accept queue */
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 9e1f2d9c9865..e5c123833186 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -1462,7 +1462,20 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
field->flags = filter->fields[i].flags;
field->len = filter->fields[i].len;
- memcpy(&field->pattern, filter->fields[i].pattern, field->len);
+ /* FIXME: how is it supposed to work?
+ struct wl12xx_rx_filter_field {
+ __le16 offset;
+ u8 len;
+ u8 flags;
+ u8 *pattern;
+ } __packed;
+ * => offset to end of header (u8 pattern[0]) or allocated mem?
+ * Let's guess that it is offset to end of header (need to check the allocation one day...)
+ */
+ {
+ void *dst = &field->pattern;
+ memcpy(dst, filter->fields[i].pattern, field->len);
+ }
buf += sizeof(struct wl12xx_rx_filter_field) -
sizeof(u8 *) + field->len;
}
diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c
index c5e3196c45b0..e7dddacc2fab 100644
--- a/drivers/nvdimm/namespace_devs.c
+++ b/drivers/nvdimm/namespace_devs.c
@@ -1836,7 +1836,7 @@ static struct device **create_namespace_blk(struct nd_region *nd_region)
ndd = to_ndd(nd_mapping);
for_each_label(l, nd_label, nd_mapping->labels) {
u32 flags = __le32_to_cpu(nd_label->flags);
- char *name[NSLABEL_NAME_LEN];
+ char name[NSLABEL_NAME_LEN] /* BUG: mainline kernels use char*[] instead of char[] (not a security vuln as it only multiplies the length by sizeof(char*) */;
struct device **__devs;
if (flags & NSLABEL_FLAG_LOCAL)
diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c
index 622bdabc8894..8b83efa8aa4d 100644
--- a/drivers/scsi/csiostor/csio_hw.c
+++ b/drivers/scsi/csiostor/csio_hw.c
@@ -3570,7 +3570,11 @@ csio_evtq_worker(struct work_struct *work)
break;
case CSIO_EVT_DEV_LOSS:
- memcpy(&rn, evt_msg->data, sizeof(rn));
+ {
+ /* Play nice with the pointer deref checker */
+ const struct csio_rnode **src = (const struct csio_rnode **)(evt_msg->data);
+ memcpy(&rn, src, sizeof(rn));
+ }
csio_rnode_devloss_handler(rn);
break;
diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c
index be8269c8d127..4748d85032ad 100644
--- a/drivers/scsi/pm8001/pm8001_ctl.c
+++ b/drivers/scsi/pm8001/pm8001_ctl.c
@@ -539,7 +539,7 @@ static int pm8001_set_nvmd(struct pm8001_hba_info *pm8001_ha)
return -ENOMEM;
}
payload = (struct pm8001_ioctl_payload *)ioctlbuffer;
- memcpy((u8 *)&payload->func_specific, (u8 *)pm8001_ha->fw_image->data,
+ memcpy((u8 *)payload->func_specific, (u8 *)pm8001_ha->fw_image->data, /* BUG? */
pm8001_ha->fw_image->size);
payload->length = pm8001_ha->fw_image->size;
payload->id = 0;
@@ -590,7 +590,7 @@ static int pm8001_update_flash(struct pm8001_hba_info *pm8001_ha)
payload->length = 1024*16;
payload->id = 0;
fwControl =
- (struct fw_control_info *)&payload->func_specific;
+ (struct fw_control_info *)payload->func_specific; /* BUG? */
fwControl->len = IOCTL_BUF_SIZE; /* IN */
fwControl->size = partitionSize + HEADER_LEN;/* IN */
fwControl->retcode = 0;/* OUT */
diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c
index 04e67a190652..2ae35c723331 100644
--- a/drivers/scsi/pm8001/pm8001_hwi.c
+++ b/drivers/scsi/pm8001/pm8001_hwi.c
@@ -4831,7 +4831,7 @@ int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
return -ENOMEM;
circularQ = &pm8001_ha->inbnd_q_tbl[0];
memcpy(pm8001_ha->memoryMap.region[NVMD].virt_ptr,
- &ioctl_payload->func_specific,
+ ioctl_payload->func_specific, /* BUG? */
ioctl_payload->length);
memset(&nvmd_req, 0, sizeof(nvmd_req));
rc = pm8001_tag_alloc(pm8001_ha, &tag);
diff --git a/drivers/staging/gdm724x/hci_packet.h b/drivers/staging/gdm724x/hci_packet.h
index dbc4446cf78d..f171770790f0 100644
--- a/drivers/staging/gdm724x/hci_packet.h
+++ b/drivers/staging/gdm724x/hci_packet.h
@@ -44,7 +44,7 @@ struct hci_packet {
struct tlv {
u8 type;
u8 len;
- u8 *data[1];
+ u8 data[0];
} __packed;
struct sdu_header {
diff --git a/drivers/staging/lustre/lustre/osc/osc_request.c b/drivers/staging/lustre/lustre/osc/osc_request.c
index 536b868ff776..7e840673ba8c 100644
--- a/drivers/staging/lustre/lustre/osc/osc_request.c
+++ b/drivers/staging/lustre/lustre/osc/osc_request.c
@@ -2708,7 +2708,11 @@ static int osc_iocontrol(unsigned int cmd, struct obd_export *exp, int len,
goto out;
}
case LL_IOC_LOV_SETSTRIPE:
- err = obd_alloc_memmd(exp, karg);
+ {
+ /* Play nice with the pointer deref checker */
+ struct lov_stripe_md **mem_tgt = (struct lov_stripe_md **)karg;
+ err = obd_alloc_memmd(exp, mem_tgt);
+ }
if (err > 0)
err = 0;
goto out;
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c
index f9003a28cae2..16fff712f998 100644
--- a/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c
@@ -54,7 +54,7 @@ bool rtl92e_send_cmd_pkt(struct net_device *dev, u32 type, const void *data,
goto Failed;
}
- memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
+ memcpy_with_cast((unsigned char *)(skb->cb), &dev, sizeof(dev));
tcb_desc = (struct cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
tcb_desc->queue_index = TXCMD_QUEUE;
tcb_desc->bCmdOrInit = type;
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c
index 13a5ddc2bea5..c70fd4fcf54f 100644
--- a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c
@@ -1627,7 +1627,7 @@ static void _rtl92e_hard_data_xmit(struct sk_buff *skb, struct net_device *dev,
netdev_warn(dev, "%s(): queue index == TXCMD_QUEUE\n",
__func__);
- memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
+ memcpy_with_cast((unsigned char *)(skb->cb), &dev, sizeof(dev));
skb_push(skb, priv->rtllib->tx_headroom);
ret = _rtl92e_tx(dev, skb);
if (ret != 0)
@@ -1656,7 +1656,7 @@ static int _rtl92e_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
}
- memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
+ memcpy_with_cast((unsigned char *)(skb->cb), &dev, sizeof(dev));
if (queue_index == TXCMD_QUEUE) {
_rtl92e_tx_cmd(dev, skb);
return 0;
diff --git a/drivers/staging/rtl8192u/r8192U_core.c b/drivers/staging/rtl8192u/r8192U_core.c
index dd0970facdf5..63019f9be10c 100644
--- a/drivers/staging/rtl8192u/r8192U_core.c
+++ b/drivers/staging/rtl8192u/r8192U_core.c
@@ -1082,7 +1082,7 @@ static int rtl8192_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
spin_lock_irqsave(&priv->tx_lock, flags);
- memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
+ memcpy_with_cast((unsigned char *)(skb->cb), &dev, sizeof(dev));
if (queue_index == TXCMD_QUEUE) {
skb_push(skb, USB_HWDESC_HEADER_LEN);
rtl819xU_tx_cmd(dev, skb);
diff --git a/drivers/staging/rtl8192u/r819xU_cmdpkt.c b/drivers/staging/rtl8192u/r819xU_cmdpkt.c
index 545f49ec9c03..a8dc82d4a601 100644
--- a/drivers/staging/rtl8192u/r819xU_cmdpkt.c
+++ b/drivers/staging/rtl8192u/r819xU_cmdpkt.c
@@ -38,7 +38,7 @@ rt_status SendTxCommandPacket(struct net_device *dev, void *pData, u32 DataLen)
skb = dev_alloc_skb(USB_HWDESC_HEADER_LEN + DataLen + 4);
if (!skb)
return RT_STATUS_FAILURE;
- memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
+ memcpy_with_cast((unsigned char *)(skb->cb), &dev, sizeof(dev));
tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
tcb_desc->queue_index = TXCMD_QUEUE;
tcb_desc->bCmdOrInit = DESC_PACKET_TYPE_NORMAL;
diff --git a/drivers/staging/rtl8192u/r819xU_firmware.c b/drivers/staging/rtl8192u/r819xU_firmware.c
index 08302dfb0d90..276aade7b509 100644
--- a/drivers/staging/rtl8192u/r819xU_firmware.c
+++ b/drivers/staging/rtl8192u/r819xU_firmware.c
@@ -66,7 +66,7 @@ static bool fw_download_code(struct net_device *dev, u8 *code_virtual_address,
skb = dev_alloc_skb(USB_HWDESC_HEADER_LEN + frag_length + 4);
if (!skb)
return false;
- memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev));
+ memcpy_with_cast((unsigned char *)(skb->cb), &dev, sizeof(dev));
tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE);
tcb_desc->queue_index = TXCMD_QUEUE;
tcb_desc->bCmdOrInit = DESC_PACKET_TYPE_INIT;
diff --git a/drivers/thermal/int340x_thermal/int3402_thermal.c b/drivers/thermal/int340x_thermal/int3402_thermal.c
index 69df3d960303..d8a5f2e202ef 100644
--- a/drivers/thermal/int340x_thermal/int3402_thermal.c
+++ b/drivers/thermal/int340x_thermal/int3402_thermal.c
@@ -20,7 +20,7 @@
#define INT3402_THERMAL_EVENT 0x90
struct int3402_thermal_data {
- acpi_handle *handle;
+ acpi_handle handle; /* harmless bug: too many stars */
struct int34x_thermal_zone *int340x_zone;
};
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index e6a6d67c8705..78589dcc9609 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -1895,12 +1895,13 @@ static struct async *reap_as(struct usb_dev_state *ps)
static int proc_reapurb(struct usb_dev_state *ps, void __user *arg)
{
struct async *as = reap_as(ps);
+ void __user * __user *parg = (void __user * __user *)arg;
if (as) {
int retval;
snoop(&ps->dev->dev, "reap %p\n", as->userurb);
- retval = processcompl(as, (void __user * __user *)arg);
+ retval = processcompl(as, parg);
free_async(as);
return retval;
}
@@ -1913,11 +1914,12 @@ static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)
{
int retval;
struct async *as;
+ void __user * __user *parg = (void __user * __user *)arg;
as = async_getcompleted(ps);
if (as) {
snoop(&ps->dev->dev, "reap %p\n", as->userurb);
- retval = processcompl(as, (void __user * __user *)arg);
+ retval = processcompl(as, parg);
free_async(as);
} else {
retval = (connected(ps) ? -EAGAIN : -ENODEV);
@@ -2043,12 +2045,13 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
static int proc_reapurb_compat(struct usb_dev_state *ps, void __user *arg)
{
struct async *as = reap_as(ps);
+ void __user * __user *parg = (void __user * __user *)arg;
if (as) {
int retval;
snoop(&ps->dev->dev, "reap %p\n", as->userurb);
- retval = processcompl_compat(as, (void __user * __user *)arg);
+ retval = processcompl_compat(as, parg);
free_async(as);
return retval;
}
@@ -2061,11 +2064,12 @@ static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *ar
{
int retval;
struct async *as;
+ void __user * __user *parg = (void __user * __user *)arg;
as = async_getcompleted(ps);
if (as) {
snoop(&ps->dev->dev, "reap %p\n", as->userurb);
- retval = processcompl_compat(as, (void __user * __user *)arg);
+ retval = processcompl_compat(as, parg);
free_async(as);
} else {
retval = (connected(ps) ? -EAGAIN : -ENODEV);
diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c
index 22f7cd711c57..489822df8e95 100644
--- a/drivers/xen/xenbus/xenbus_xs.c
+++ b/drivers/xen/xenbus/xenbus_xs.c
@@ -372,7 +372,9 @@ static char **split(char *strings, unsigned int len, unsigned int *num)
kfree(strings);
return ERR_PTR(-ENOMEM);
}
- memcpy(&ret[*num], strings, len);
+ /* Be nice with pointer deref checker: the ned of ret is char* */
+ p = (char *)&ret[*num];
+ memcpy(p, strings, len);
kfree(strings);
strings = (char *)&ret[*num];
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
index 71e8a56e9479..f201c2a5985b 100644
--- a/fs/cifs/cifsacl.c
+++ b/fs/cifs/cifsacl.c
@@ -304,16 +304,20 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
}
if (sidtype == SIDOWNER) {
- kuid_t uid;
+ kuid_t uid, *ppayload_uid;
uid_t id;
- memcpy(&id, &sidkey->payload.data[0], sizeof(uid_t));
+ /* Play nice with the pointer deref checker */
+ ppayload_uid = (kuid_t *)&sidkey->payload.data[0];
+ memcpy(&id, ppayload_uid, sizeof(uid_t));
uid = make_kuid(&init_user_ns, id);
if (uid_valid(uid))
fuid = uid;
} else {
- kgid_t gid;
+ kgid_t gid, *ppayload_gid;
gid_t id;
- memcpy(&id, &sidkey->payload.data[0], sizeof(gid_t));
+ /* Play nice with the pointer deref checker */
+ ppayload_gid = (kgid_t *)&sidkey->payload.data[0];
+ memcpy(&id, ppayload_gid, sizeof(gid_t));
gid = make_kgid(&init_user_ns, id);
if (gid_valid(gid))
fgid = gid;
diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c
index 163190ecc0d2..26bdd506751e 100644
--- a/fs/hfs/dir.c
+++ b/fs/hfs/dir.c
@@ -169,7 +169,7 @@ static int hfs_readdir(struct file *file, struct dir_context *ctx)
* Can be done after the list insertion; exclusion with
* hfs_delete_cat() is provided by directory lock.
*/
- memcpy(&rd->key, &fd.key, sizeof(struct hfs_cat_key));
+ memcpy(&rd->key, fd.key, sizeof(struct hfs_cat_key)); /* BUG? */
out:
hfs_find_exit(&fd);
return err;
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index 436aa4e42221..3184dc19348d 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -78,6 +78,27 @@ extern void __chk_io_ptr(const volatile void __iomem *);
#include <linux/compiler-clang.h>
#endif
+#ifdef CONFIG_GCC_PLUGIN_DEREF_CHECKER
+/* Make pointer deref checker able to check the consistency of optimized functions
+ *
+ * This is VERY ugly and a better way would be to insert the plugin before gcc
+ * inlines calls to memcpy, but I have not yet found how to do this.
+ */
+static noinline __used void __same_pointer_depths_chk(const void *p1, const void *p2)
+{
+}
+static noinline __used void __same_pointer_depths_with_volatile1_chk(const volatile void *p1, const void *p2)
+{
+}
+static noinline __used void __same_pointer_depths_with_volatile2_chk(const void *p1, const volatile void *p2)
+{
+}
+#else
+#define __same_pointer_depths_chk(p1, p2) do { } while (0)
+#define __same_pointer_depths_with_volatile1_chk(p1, p2) do { } while (0)
+#define __same_pointer_depths_with_volatile2_chk(p1, p2) do { } while (0)
+#endif
+
/*
* Generic compiler-dependent macros required for kernel
* build go below this comment. Actual compiler/compiler version
@@ -202,6 +223,7 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
#define __READ_ONCE_SIZE \
({ \
+ __same_pointer_depths_with_volatile1_chk(p, res); \
switch (size) { \
case 1: *(__u8 *)res = *(volatile __u8 *)p; break; \
case 2: *(__u16 *)res = *(volatile __u16 *)p; break; \
@@ -242,6 +264,7 @@ void __read_once_size_nocheck(const volatile void *p, void *res, int size)
static __always_inline void __write_once_size(volatile void *p, void *res, int size)
{
+ __same_pointer_depths_with_volatile1_chk(p, res);
switch (size) {
case 1: *(volatile __u8 *)p = *(__u8 *)res; break;
case 2: *(volatile __u16 *)p = *(__u16 *)res; break;
diff --git a/kernel/debug/kdb/kdb_support.c b/kernel/debug/kdb/kdb_support.c
index d35cc2d3a4cc..0b45978c2f2b 100644
--- a/kernel/debug/kdb/kdb_support.c
+++ b/kernel/debug/kdb/kdb_support.c
@@ -129,9 +129,13 @@ int kdbnearsym(unsigned long addr, kdb_symtab_t *symtab)
}
if (i >= ARRAY_SIZE(kdb_name_table)) {
debug_kfree(kdb_name_table[0]);
- memcpy(kdb_name_table, kdb_name_table+1,
- sizeof(kdb_name_table[0]) *
- (ARRAY_SIZE(kdb_name_table)-1));
+ {
+ /* For pointer checker */
+ void *target = kdb_name_table;
+ memcpy(target, kdb_name_table+1,
+ sizeof(kdb_name_table[0]) *
+ (ARRAY_SIZE(kdb_name_table)-1));
+ }
} else {
debug_kfree(knt1);
knt1 = kdb_name_table[i];
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index dade4c9559cc..5cec735d5100 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -6449,7 +6449,12 @@ ftrace_trace_snapshot_callback(struct ftrace_hash *hash,
* We use the callback data field (which is a pointer)
* as our counter.
*/
- ret = kstrtoul(number, 0, (unsigned long *)&count);
+ /* Play nice with the pointer deref checker */
+ {
+ unsigned long count_ul = -1UL;
+ ret = kstrtoul(number, 0, &count_ul);
+ count = (void *)count_ul;
+ }
if (ret)
return ret;
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c
index 0efa00d80623..1faedf25e9c9 100644
--- a/kernel/trace/trace_functions.c
+++ b/kernel/trace/trace_functions.c
@@ -529,7 +529,7 @@ ftrace_trace_probe_callback(struct ftrace_probe_ops *ops,
struct ftrace_hash *hash, char *glob,
char *cmd, char *param, int enable)
{
- void *count = (void *)-1;
+ unsigned long count = -1UL;
char *number;
int ret;
@@ -554,12 +554,12 @@ ftrace_trace_probe_callback(struct ftrace_probe_ops *ops,
* We use the callback data field (which is a pointer)
* as our counter.
*/
- ret = kstrtoul(number, 0, (unsigned long *)&count);
+ ret = kstrtoul(number, 0, &count);
if (ret)
return ret;
out_reg:
- ret = register_ftrace_function_probe(glob, ops, count);
+ ret = register_ftrace_function_probe(glob, ops, (void *)count);
return ret < 0 ? ret : 0;
}
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 9aedb0b06683..b093b888df2f 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -669,10 +669,15 @@ static int create_trace_kprobe(int argc, char **argv)
return -EINVAL;
}
/* an address specified */
- ret = kstrtoul(&argv[1][0], 0, (unsigned long *)&addr);
- if (ret) {
- pr_info("Failed to parse address.\n");
- return ret;
+ {
+ /* Play nice with the pointer deref checker */
+ unsigned long addr_ul = 0;
+ ret = kstrtoul(&argv[1][0], 0, &addr_ul);
+ if (ret) {
+ pr_info("Failed to parse address.\n");
+ return ret;
+ }
+ addr = (void *)addr_ul;
}
} else {
/* a symbol specified */
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index ef071ca73fc3..069139f4877e 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -4234,7 +4234,7 @@ void set_worker_desc(const char *fmt, ...)
*/
void print_worker_info(const char *log_lvl, struct task_struct *task)
{
- work_func_t *fn = NULL;
+ work_func_t fn = NULL;
char name[WQ_NAME_LEN] = { };
char desc[WORKER_DESC_LEN] = { };
struct pool_workqueue *pwq = NULL;
diff --git a/mm/percpu-vm.c b/mm/percpu-vm.c
index 538998a137d2..c11bf5209859 100644
--- a/mm/percpu-vm.c
+++ b/mm/percpu-vm.c
@@ -349,8 +349,11 @@ static struct pcpu_chunk *pcpu_create_chunk(void)
static void pcpu_destroy_chunk(struct pcpu_chunk *chunk)
{
- if (chunk && chunk->data)
- pcpu_free_vm_areas(chunk->data, pcpu_nr_groups);
+ if (chunk && chunk->data) {
+ /* Play nice with the pointer deref checker */
+ struct vm_struct **vms = (struct vm_struct **)(chunk->data);
+ pcpu_free_vm_areas(vms, pcpu_nr_groups);
+ }
pcpu_free_chunk(chunk);
}
diff --git a/mm/slub.c b/mm/slub.c
index 9adae58462f8..ac6952eb7987 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -252,12 +252,14 @@ static void prefetch_freepointer(const struct kmem_cache *s, void *object)
static inline void *get_freepointer_safe(struct kmem_cache *s, void *object)
{
- void *p;
+ void *p, **addr;
if (!debug_pagealloc_enabled())
return get_freepointer(s, object);
- probe_kernel_read(&p, (void **)(object + s->offset), sizeof(p));
+ /* Play nice with the pointer deref checker */
+ addr = (void **)(object + s->offset);
+ probe_kernel_read(&p, addr, sizeof(p));
return p;
}
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 7639290b6de3..f99ee322438c 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -4869,7 +4869,13 @@ static int clock_info_cmd_complete(struct mgmt_pending_cmd *cmd, u8 status)
int err;
memset(&rp, 0, sizeof(rp));
- memcpy(&rp.addr, &cmd->param, sizeof(rp.addr));
+ {
+ /* Hide pointer deref mismatch with a local variable.
+ * However it may be a bug. Why is param addr copied and not its content? Optimisation?
+ */
+ const void *src = &cmd->param;
+ memcpy(&rp.addr, src, sizeof(rp.addr));
+ }
if (status)
goto complete;
diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
index d6b3b579560d..3aef7b841ae9 100644
--- a/net/core/drop_monitor.c
+++ b/net/core/drop_monitor.c
@@ -157,7 +157,9 @@ static void trace_drop_common(struct sk_buff *skb, void *location)
nla = genlmsg_data(nlmsg_data(nlh));
msg = nla_data(nla);
for (i = 0; i < msg->entries; i++) {
- if (!memcmp(&location, msg->points[i].pc, sizeof(void *))) {
+ /* Play nice with the pointer deref checker */
+ void **msg_points_pc = (void **)(msg->points[i].pc);
+ if (!memcmp(&location, msg_points_pc, sizeof(void *))) {
msg->points[i].count++;
goto out;
}
@@ -169,7 +171,11 @@ static void trace_drop_common(struct sk_buff *skb, void *location)
*/
__nla_reserve_nohdr(dskb, sizeof(struct net_dm_drop_point));
nla->nla_len += NLA_ALIGN(sizeof(struct net_dm_drop_point));
- memcpy(msg->points[msg->entries].pc, &location, sizeof(void *));
+ {
+ /* Play nice with the pointer deref checker */
+ void **msg_points_pc = (void **)(msg->points[msg->entries].pc);
+ memcpy(msg_points_pc, &location, sizeof(void *));
+ }
msg->points[msg->entries].count = 1;
msg->entries++;
diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic.c b/net/ipv4/netfilter/nf_nat_snmp_basic.c
index c9b52c361da2..ff79af4092f9 100644
--- a/net/ipv4/netfilter/nf_nat_snmp_basic.c
+++ b/net/ipv4/netfilter/nf_nat_snmp_basic.c
@@ -944,8 +944,12 @@ static unsigned char snmp_trap_decode(struct asn1_ctx *ctx,
(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)))
goto err_id_free;
- if (!asn1_octets_decode(ctx, end, (unsigned char **)&trap->ip_address, &len))
- goto err_id_free;
+ {
+ /* Play nice with the pointer deref checker */
+ unsigned char **ipaddr_buf = (unsigned char **)&trap->ip_address;
+ if (!asn1_octets_decode(ctx, end, ipaddr_buf, &len))
+ goto err_id_free;
+ }
/* IPv4 only */
if (len != 4)
diff --git a/net/phonet/socket.c b/net/phonet/socket.c
index ffd5f2297584..ae2cdbf5ad0f 100644
--- a/net/phonet/socket.c
+++ b/net/phonet/socket.c
@@ -772,8 +772,11 @@ static void *pn_res_seq_next(struct seq_file *seq, void *v, loff_t *pos)
if (v == SEQ_START_TOKEN)
sk = pn_res_get_idx(seq, 0);
- else
- sk = pn_res_get_next(seq, v);
+ else {
+ /* Play nice with the pointer deref checker */
+ struct sock **sk_v = (struct sock **)v;
+ sk = pn_res_get_next(seq, sk_v);
+ }
(*pos)++;
return sk;
}
diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c
index 18c737a61d80..2885d2e49632 100644
--- a/net/rxrpc/key.c
+++ b/net/rxrpc/key.c
@@ -833,7 +833,7 @@ static int rxrpc_preparse_s(struct key_preparsed_payload *prep)
if (prep->datalen != 8)
return -EINVAL;
- memcpy(&prep->payload.data[2], prep->data, 8);
+ memcpy_with_cast(&prep->payload.data[2], prep->data, 8); /* I did not understand this */
ci = crypto_alloc_skcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(ci)) {
diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c
index 63afa9e9cc08..19d2ec099d9d 100644
--- a/net/rxrpc/rxkad.c
+++ b/net/rxrpc/rxkad.c
@@ -821,7 +821,7 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
ASSERT(conn->server_key->payload.data[0] != NULL);
ASSERTCMP((unsigned long) ticket & 7UL, ==, 0);
- memcpy(&iv, &conn->server_key->payload.data[2], sizeof(iv));
+ memcpy_with_cast(&iv, &conn->server_key->payload.data[2], sizeof(iv)); /* I did not understand this */
req = skcipher_request_alloc(conn->server_key->payload.data[0],
GFP_NOFS);
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index 61f0e6db909b..f7eeffe2da2b 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -6,6 +6,8 @@ ifdef CONFIG_GCC_PLUGINS
gcc-plugin-$(CONFIG_GCC_PLUGIN_CYC_COMPLEXITY) += cyc_complexity_plugin.so
+ gcc-plugin-$(CONFIG_GCC_PLUGIN_DEREF_CHECKER) += deref_checker_plugin.so
+
ifdef CONFIG_GCC_PLUGIN_SANCOV
ifeq ($(CFLAGS_KCOV),)
# It is needed because of the gcc-plugin.sh and gcc version checks.
diff --git a/scripts/gcc-plugins/deref_checker_plugin.c b/scripts/gcc-plugins/deref_checker_plugin.c
new file mode 100644
index 000000000000..927c1e5f546b
--- /dev/null
+++ b/scripts/gcc-plugins/deref_checker_plugin.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright 2016 by Nicolas IOOSS
+ * Licensed under the GPL v2, or (at your option) v3
+ *
+ * Check that pointer dereferencing is used consistently accross function calls
+ */
+
+#include "gcc-common.h"
+
+int plugin_is_GPL_compatible;
+
+static struct plugin_info deref_checker_plugin_info = {
+ .version = "20160806",
+ .help = "Pointer dereferencing checker\n",
+};
+
+/**
+ * Identifier match helpers
+ */
+static bool identifier_is(const_tree identifier, const char *str)
+{
+ size_t len = strlen(str);
+
+ if (!identifier)
+ return false;
+
+ return IDENTIFIER_LENGTH(identifier) == len && !strncmp(str, IDENTIFIER_POINTER(identifier), len);
+}
+
+/**
+ * Get the number of pointers to a relevant data an expression holds
+ */
+static unsigned int get_pointer_depth(const_tree tree)
+{
+ unsigned int recursive_depth;
+
+ switch (TREE_CODE(tree)) {
+ case POINTER_TYPE:
+ recursive_depth = 1 + get_pointer_depth(TREE_TYPE(tree));
+ /* Remove 1 if the sub-type is an array because it is the way
+ * vector dereferencing is stored.
+ */
+ if (TREE_CODE(TREE_TYPE(tree)) == ARRAY_TYPE)
+ recursive_depth -= 1;
+ return recursive_depth;
+
+ case ARRAY_TYPE:
+ /* Unfold arrays so that char[6][7] counts as char[42] */
+ tree = TREE_TYPE(tree);
+ while (TREE_CODE(tree) == ARRAY_TYPE)
+ tree = TREE_TYPE(tree);
+ return 1 + get_pointer_depth(tree);
+
+ case VECTOR_TYPE:
+ return 1 + get_pointer_depth(TREE_TYPE(tree));
+
+ case BOOLEAN_TYPE:
+ case ENUMERAL_TYPE:
+ case FUNCTION_TYPE:
+ case INTEGER_TYPE:
+ case QUAL_UNION_TYPE:
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ case VOID_TYPE:
+ return 0;
+
+ case REFERENCE_TYPE: /* for __va_list on ARM */
+ if (TREE_CODE(TREE_TYPE(tree)) == RECORD_TYPE && identifier_is(TYPE_IDENTIFIER(TREE_TYPE(tree)), "__va_list"))
+ return 1;
+
+ default:
+ debug_tree(tree); /* TODO: better error reporting */
+ error(G_("unknown type!"));
+ return 0;
+ }
+}
+
+/**
+ * Whitelist some functions for the pointer checker
+ * TODO: function attribute?
+ */
+static bool is_whitelisted(const_tree fndecl, unsigned int argidx,
+ unsigned int ptrdepth_type, unsigned int ptrdepth_value)
+{
+ const_tree fnident = DECL_NAME(fndecl);
+
+ /* TODO: add a check for not void* in type, so that there are real bugs which appear (or just cast errors?) */
+
+ /* Many functions have their first argument a void* pointer */
+ if (identifier_is(fnident, "__builtin_prefetch") ||
+ identifier_is(fnident, "__raw_readl") ||
+ identifier_is(fnident, "__read_once_size") ||
+ identifier_is(fnident, "__sync_cache_range_w") ||
+ identifier_is(fnident, "__write_once_size") ||
+ identifier_is(fnident, "ERR_CAST") ||
+ identifier_is(fnident, "IS_ERR") ||
+ identifier_is(fnident, "IS_ERR_OR_NULL") ||
+ identifier_is(fnident, "PTR_ERR") ||
+ identifier_is(fnident, "acpi_os_free") ||
+ identifier_is(fnident, "bit_waitqueue") ||
+ identifier_is(fnident, "bm_vk_free") ||
+ identifier_is(fnident, "cfs_percpt_free") ||
+ identifier_is(fnident, "cfs_percpt_number") ||
+ identifier_is(fnident, "choke_free") ||
+ identifier_is(fnident, "clocksource_mmio_init") ||
+ identifier_is(fnident, "devres_free") ||
+ identifier_is(fnident, "drm_free_large") ||
+ identifier_is(fnident, "is_vmalloc_addr") ||
+ identifier_is(fnident, "jhash") ||
+ identifier_is(fnident, "free_percpu") ||
+ identifier_is(fnident, "kfree") ||
+ identifier_is(fnident, "kmemdup") ||
+ identifier_is(fnident, "kmem_free") ||
+ identifier_is(fnident, "krealloc") ||
+ identifier_is(fnident, "kvfree") ||
+ identifier_is(fnident, "kzfree") ||
+ identifier_is(fnident, "memset") ||
+ identifier_is(fnident, "memdup_user") ||
+ identifier_is(fnident, "mempool_free") ||
+ identifier_is(fnident, "prefetch") ||
+ identifier_is(fnident, "scif_free") ||
+ identifier_is(fnident, "sort") ||
+ identifier_is(fnident, "t4_free_mem") ||
+ identifier_is(fnident, "vfree") ||
+ identifier_is(fnident, "wake_up_bit") ||
+ false)
+ return argidx == 1 && ptrdepth_type < ptrdepth_value;
+
+ if (identifier_is(fnident, "__kfifo_in_r") || /* kfifo can be used to transmit pointers */
+ identifier_is(fnident, "__kfifo_in") ||
+ identifier_is(fnident, "__kfifo_init") ||
+ identifier_is(fnident, "__kfifo_out") ||
+ identifier_is(fnident, "__raw_writel") ||
+ identifier_is(fnident, "constant_test_bit") || /* even though there is a cast to unsigned long*, using &private_data (which is void**) is being reported */
+ identifier_is(fnident, "_clear_bit") ||
+ identifier_is(fnident, "clear_bit") ||
+ identifier_is(fnident, "_set_bit") ||
+ identifier_is(fnident, "set_bit") ||
+ identifier_is(fnident, "test_bit") ||
+ identifier_is(fnident, "free_percpu_irq") ||
+
+ identifier_is(fnident, "device_for_each_child") || /* data for the callback */
+ identifier_is(fnident, "device_for_each_child_reverse") || /* data for the callback */
+ identifier_is(fnident, "devm_kfree") ||
+ identifier_is(fnident, "devm_kmemdup") ||
+ identifier_is(fnident, "devres_add") || /* resource registered to the device */
+ false)
+ return argidx == 2 && ptrdepth_type < ptrdepth_value;
+
+ if (identifier_is(fnident, "blkdev_get") || /* Third arg "holder" is a pointer to a stack variable */
+ identifier_is(fnident, "driver_for_each_device") ||
+ identifier_is(fnident, "drm_for_each_detailed_block") ||
+ identifier_is(fnident, "flex_array_put") ||
+ identifier_is(fnident, "hashtab_map") || /* args */
+ identifier_is(fnident, "idr_for_each") ||
+ identifier_is(fnident, "of_clk_add_provider") ||
+ identifier_is(fnident, "single_open") ||
+ identifier_is(fnident, "snd_seq_dump_var_event") || /* private_data */
+ identifier_is(fnident, "twsk_unique") ||
+ false)
+ return argidx == 3 && ptrdepth_type < ptrdepth_value;
+
+ if (identifier_is(fnident, "debugfs_create_file") || /* Private data */
+ identifier_is(fnident, "devres_release") || /* match_data */
+ identifier_is(fnident, "request_percpu_irq") ||
+ false)
+ return argidx == 4 && ptrdepth_type < ptrdepth_value;
+
+ if (identifier_is(fnident, "get_res") && argidx == 5 && ptrdepth_value == 2)
+ return true;
+
+ if (identifier_is(fnident, "param_free_charp") && argidx == 1 && ptrdepth_value == 2) /* This function waits for a char** but needs to have the prototype of free() */
+ return true;
+
+ if (identifier_is(fnident, "fetch_robust_entry") && argidx == 3 && ptrdepth_value == 2) /* compat_uptr_t __user * depth is 2 */
+ return true;
+
+ /* lib/radix-tree.c does strange things */
+ if (identifier_is(fnident, "delete_sibling_entries") ||
+ identifier_is(fnident, "node_to_entry") ||
+ identifier_is(fnident, "radix_tree_is_internal_node"))
+ return argidx == 1 && ptrdepth_type < ptrdepth_value;
+
+/* automatic processing, TODO: review! */
+ if ((identifier_is(fnident, "__be64_to_cpup") && argidx == 1 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "__cq_dequeue") && argidx == 2 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "__install_special_mapping") && argidx == 5 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "__ocfs2_find_path") && argidx == 5 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "__test_and_clear_bit") && argidx == 2 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "__test_and_set_bit") && argidx == 2 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "__uvcg_iter_frm_intrv") && argidx == 4 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "__uvcg_iter_strm_cls") && argidx == 2 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "acpi_bus_get_device") && argidx == 1 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "acpi_get_physical_device_location") && argidx == 1 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "acpi_node_prop_read") && argidx == 4 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "acpi_rs_out_string") && argidx == 2 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "acpi_ut_trace_ptr") && argidx == 5 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "acpi_walk_namespace") && argidx == 6 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "acpi_walk_resources") && argidx == 4 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "alloc_ring") && argidx == 6 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "alloc_usemap_and_memmap") && argidx == 2 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "ath10k_wmi_tlv_iter") && argidx == 5 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "cfs_array_free") && argidx == 1 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "csio_enqueue_evt") && argidx == 3 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "cxgbi_free_big_mem") && argidx == 1 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "deactivate_slab") && argidx == 3 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "devm_request_irq") && argidx == 6 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "free_irq") && argidx == 2 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "gigaset_add_event") && argidx == 4 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "hlist_add_head") && argidx == 2 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "i2400m_zrealloc_2x") && argidx == 1 && ptrdepth_value == 3) ||
+ (identifier_is(fnident, "ieee80211_iter_chan_contexts_atomic") && argidx == 3 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "iommu_group_for_each_dev") && argidx == 2 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "lbs_deb_hex") && argidx == 3 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "lustre_cfg_bufs_set") && argidx == 3 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "mgmt_pending_foreach") && argidx == 4 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "platform_device_add_data") && argidx == 2 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "platform_set_drvdata") && argidx == 2 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "pool_proc_next") && argidx == 2 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "rbd_img_request_fill") && argidx == 3 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "request_irq") && argidx == 5 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "set_freepointer") && argidx == 3 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "snd_pcm_hw_rule_add") && argidx == 5 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "trace_ocfs2_read_virt_blocks") && argidx == 4 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "ulist_add_merge") && argidx == 4 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "usb_acpi_get_connect_type") && argidx == 1 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "usb_control_msg") && argidx == 7 && ptrdepth_value == 2) ||
+ (identifier_is(fnident, "xenbus_grant_ring") && argidx == 2 && ptrdepth_value == 2) ||
+
+ (identifier_is(fnident, "acpi_walk_namespace") && argidx == 7 && ptrdepth_value == 1) ||
+ false)
+ return true;
+
+ if (identifier_is(fnident, "__uvcg_iter_strm_cls"))
+ return argidx == 2 || argidx == 3; /* priv2 and priv3 */
+
+ if (identifier_is(fnident, "memcpy_with_cast"))
+ return argidx == 1 || argidx == 2;
+ if (identifier_is(fnident, "__xchg") ||
+ identifier_is(fnident, "__cmpxchg"))
+ return argidx == 1 || argidx == 2;
+
+ if (identifier_is(fnident, "__builtin___ubsan_handle_divrem_overflow") ||
+ identifier_is(fnident, "__builtin___ubsan_handle_shift_out_of_bounds"))
+ return ptrdepth_value == 0 && (argidx == 2 || argidx == 3); /* UBSAN built-in functions */
+ if (identifier_is(fnident, "__builtin___ubsan_handle_vla_bound_not_positive"))
+ return ptrdepth_value == 0 && argidx == 2; /* UBSAN built-in functions */
+
+ return false;
+}
+
+static unsigned int deref_checker_execute(void)
+{
+ basic_block bb;
+
+ FOR_EACH_BB_FN(bb, cfun) {
+ gimple_stmt_iterator gsi;
+
+ for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
+ gimple stmt;
+ gcall *call_stmt;
+ const_tree fndecl, arg_type;
+ unsigned int argidx;
+ function_args_iterator arg_iter;
+ bool expect_next_depth = false;
+ unsigned int expected_ptrdepth_type, expected_ptrdepth_value;
+
+ /* Get the statement */
+ stmt = gsi_stmt(gsi);
+ if (!is_gimple_call(stmt))
+ continue;
+ call_stmt = as_a_gcall(stmt);
+ /*debug_gimple_stmt(stmt);*/
+ //debug_tree(gimple_call_fn(stmt));
+
+ /* Get the function declaration */
+ fndecl = gimple_call_fndecl(stmt);
+ if (fndecl == NULL_TREE)
+ continue;
+
+ argidx = 1;
+ FOREACH_FUNCTION_ARGS(TREE_TYPE(fndecl), arg_type, arg_iter) {
+ const_tree arg_tree;
+ unsigned int ptrdepth_type, ptrdepth_value;
+
+ if (TREE_CODE(arg_type) == VOID_TYPE) {
+ /* End of function arguments */
+ if (argidx != 1 + gimple_call_num_args(call_stmt)) {
+ error(G_("Call to %qF with too many parameters"), fndecl);
+ break;
+ }
+ argidx++;
+ continue; /* the loop should exit by itself. Otherwise the error is catched later */
+ }
+
+ /* This should not happen */
+ if (argidx > gimple_call_num_args(call_stmt)) {
+ error(G_("Call to %qF with not enough parameters"), fndecl);
+ break;
+ }
+
+ ptrdepth_type = get_pointer_depth(arg_type);
+
+ arg_tree = gimple_call_arg(call_stmt, argidx - 1);
+ ptrdepth_value = get_pointer_depth(TREE_TYPE(arg_tree));
+
+ /* memcpy and memcmp are special as two pointers of the same depth may be used */
+ if (expect_next_depth) {
+ expect_next_depth = false;
+ /* This should not happen: memcmp has a known prototype */
+ if (ptrdepth_type != expected_ptrdepth_type) {
+ warning_at(
+ gimple_location(stmt), 0,
+ G_("Internal error while analyzing a call to %qF\n"),
+ fndecl);
+ }
+ if (ptrdepth_value != expected_ptrdepth_value) {
+/* debug_tree(arg_tree); */
+ warning_at(
+ gimple_location(stmt), 0,
+ G_("Invalid pointer depth in function call %qF: value of arg %u is %u, not expected %u\n"),
+ fndecl, argidx, ptrdepth_value, expected_ptrdepth_value);
+ }
+ } else if (argidx == 1 && (
+ identifier_is(DECL_NAME(fndecl), "__memcpy") ||
+ identifier_is(DECL_NAME(fndecl), "__same_pointer_depths_chk") ||
+ identifier_is(DECL_NAME(fndecl), "__same_pointers_depths_with_volatile1_chk") ||
+ identifier_is(DECL_NAME(fndecl), "__same_pointers_depths_with_volatile2_chk") ||
+ identifier_is(DECL_NAME(fndecl), "copy_from_user") ||
+ identifier_is(DECL_NAME(fndecl), "copy_to_user") ||
+ identifier_is(DECL_NAME(fndecl), "memcmp") ||
+ identifier_is(DECL_NAME(fndecl), "memcpy") ||
+ identifier_is(DECL_NAME(fndecl), "memcpy_fromio") ||
+ identifier_is(DECL_NAME(fndecl), "memcpy_toio") ||
+ identifier_is(DECL_NAME(fndecl), "memmove") ||
+ identifier_is(DECL_NAME(fndecl), "probe_kernel_read"))) {
+ expect_next_depth = true;
+ expected_ptrdepth_type = ptrdepth_type;
+ expected_ptrdepth_value = ptrdepth_value;
+ } else if (ptrdepth_type != ptrdepth_value && !is_whitelisted(fndecl, argidx, ptrdepth_type, ptrdepth_value)) {
+ warning_at(
+ gimple_location(stmt), 0,
+ G_("Invalid pointer depth in function call %qF: type of argument %u says %u but value is %u\n"),
+ fndecl, argidx, ptrdepth_type, ptrdepth_value);
+ }
+
+ argidx++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+#define PASS_NAME deref_checker
+
+#define NO_GATE
+#define TODO_FLAGS_FINISH TODO_dump_func
+
+#include "gcc-generate-gimple-pass.h"
+
+int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
+{
+ const char * const plugin_name = plugin_info->base_name;
+ struct register_pass_info deref_checker_pass_info;
+
+ deref_checker_pass_info.pass = make_deref_checker_pass();
+ deref_checker_pass_info.reference_pass_name = "ssa";
+ deref_checker_pass_info.ref_pass_instance_number = 1;
+ deref_checker_pass_info.pos_op = PASS_POS_INSERT_AFTER;
+
+ if (!plugin_default_version_check(version, &gcc_version)) {
+ error(G_("incompatible gcc/plugin versions"));
+ return 1;
+ }
+
+
+ register_callback(plugin_name, PLUGIN_INFO, NULL,
+ &deref_checker_plugin_info);
+ register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+ &deref_checker_pass_info);
+
+ return 0;
+}
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index c61fd50f771f..3da16464c657 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -2926,7 +2926,7 @@ static int snd_pcm_capture_ioctl1(struct file *file,
struct snd_xfern xfern;
struct snd_xfern __user *_xfern = arg;
struct snd_pcm_runtime *runtime = substream->runtime;
- void *bufs;
+ void __user **bufs;
snd_pcm_sframes_t result;
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
--
2.9.3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment