Created
August 28, 2016 18:35
-
-
Save anonymous/36dd40dcbeeb83964e66b65be7a96136 to your computer and use it in GitHub Desktop.
Pointer dereference checker
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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