aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2017-06-30 15:48:36 -0700
committerKees Cook <keescook@chromium.org>2017-06-30 15:48:36 -0700
commitcb00e6b58abbb1b41f3e41890cee7550ac8c1853 (patch)
tree60b6a7768c0004fc5988b857fc0eac770f071741
parentf136e090c73ed2ed568dcd013c8e51ed4693a279 (diff)
parent8acdf5055974e49d337d51ac7011449cfd7b7d05 (diff)
Merge branch 'for-next/gcc-plugin/randstruct' into for-next/kspp
-rw-r--r--Documentation/dontdiff2
-rw-r--r--arch/Kconfig39
-rw-r--r--arch/arm/include/asm/assembler.h2
-rw-r--r--arch/arm/include/asm/cacheflush.h2
-rw-r--r--arch/arm/kernel/entry-armv.S5
-rw-r--r--arch/arm/mm/proc-macros.S10
-rw-r--r--arch/x86/include/asm/paravirt_types.h16
-rw-r--r--arch/x86/include/asm/processor.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c10
-rw-r--r--drivers/gpu/drm/amd/powerplay/hwmgr/vega10_thermal.c20
-rw-r--r--drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c8
-rw-r--r--drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c8
-rw-r--r--drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c8
-rw-r--r--drivers/misc/sgi-xp/xp.h12
-rw-r--r--drivers/misc/sgi-xp/xp_main.c36
-rw-r--r--fs/mount.h4
-rw-r--r--fs/namei.c2
-rw-r--r--fs/nfs/namespace.c2
-rw-r--r--fs/ntfs/namei.c2
-rw-r--r--fs/ocfs2/export.c2
-rw-r--r--fs/proc/internal.h6
-rw-r--r--include/linux/binfmts.h4
-rw-r--r--include/linux/cdev.h2
-rw-r--r--include/linux/compiler-gcc.h26
-rw-r--r--include/linux/compiler.h17
-rw-r--r--include/linux/cred.h4
-rw-r--r--include/linux/dcache.h2
-rw-r--r--include/linux/fs.h17
-rw-r--r--include/linux/fs_struct.h2
-rw-r--r--include/linux/ipc.h2
-rw-r--r--include/linux/ipc_namespace.h2
-rw-r--r--include/linux/key-type.h4
-rw-r--r--include/linux/kmod.h2
-rw-r--r--include/linux/kobject.h2
-rw-r--r--include/linux/lsm_hooks.h4
-rw-r--r--include/linux/mm_types.h4
-rw-r--r--include/linux/module.h4
-rw-r--r--include/linux/mount.h2
-rw-r--r--include/linux/msg.h2
-rw-r--r--include/linux/path.h2
-rw-r--r--include/linux/pid_namespace.h2
-rw-r--r--include/linux/proc_ns.h2
-rw-r--r--include/linux/sched.h16
-rw-r--r--include/linux/sched/signal.h2
-rw-r--r--include/linux/sem.h2
-rw-r--r--include/linux/shm.h2
-rw-r--r--include/linux/sysctl.h2
-rw-r--r--include/linux/tty.h2
-rw-r--r--include/linux/tty_driver.h4
-rw-r--r--include/linux/user_namespace.h2
-rw-r--r--include/linux/utsname.h2
-rw-r--r--include/linux/vermagic.h9
-rw-r--r--include/net/af_unix.h2
-rw-r--r--include/net/neighbour.h2
-rw-r--r--include/net/net_namespace.h2
-rw-r--r--include/net/sock.h2
-rw-r--r--kernel/futex.c4
-rw-r--r--scripts/Makefile.gcc-plugins4
-rw-r--r--scripts/gcc-plugins/.gitignore1
-rw-r--r--scripts/gcc-plugins/Makefile8
-rw-r--r--scripts/gcc-plugins/gcc-common.h12
-rw-r--r--scripts/gcc-plugins/gen-random-seed.sh8
-rw-r--r--scripts/gcc-plugins/randomize_layout_plugin.c1028
-rw-r--r--security/keys/internal.h2
64 files changed, 1290 insertions, 134 deletions
diff --git a/Documentation/dontdiff b/Documentation/dontdiff
index 77b92221f951..e10a484629e4 100644
--- a/Documentation/dontdiff
+++ b/Documentation/dontdiff
@@ -207,6 +207,8 @@ r200_reg_safe.h
r300_reg_safe.h
r420_reg_safe.h
r600_reg_safe.h
+randomize_layout_hash.h
+randomize_layout_seed.h
recordmcount
relocs
rlim_names.h
diff --git a/arch/Kconfig b/arch/Kconfig
index e0dced4da4bf..3eac97a4c7b3 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -443,6 +443,45 @@ config GCC_PLUGIN_STRUCTLEAK_VERBOSE
initialized. Since not all existing initializers are detected
by the plugin, this can produce false positive warnings.
+config GCC_PLUGIN_RANDSTRUCT
+ bool "Randomize layout of sensitive kernel structures"
+ depends on GCC_PLUGINS
+ select MODVERSIONS if MODULES
+ help
+ If you say Y here, the layouts of structures explicitly
+ marked by __randomize_layout will be randomized at
+ compile-time. This can introduce the requirement of an
+ additional information exposure vulnerability for exploits
+ targeting these structure types.
+
+ Enabling this feature will introduce some performance impact,
+ slightly increase memory usage, and prevent the use of forensic
+ tools like Volatility against the system (unless the kernel
+ source tree isn't cleaned after kernel installation).
+
+ The seed used for compilation is located at
+ scripts/gcc-plgins/randomize_layout_seed.h. It remains after
+ a make clean to allow for external modules to be compiled with
+ the existing seed and will be removed by a make mrproper or
+ make distclean.
+
+ Note that the implementation requires gcc 4.7 or newer.
+
+ This plugin was ported from grsecurity/PaX. More information at:
+ * https://grsecurity.net/
+ * https://pax.grsecurity.net/
+
+config GCC_PLUGIN_RANDSTRUCT_PERFORMANCE
+ bool "Use cacheline-aware structure randomization"
+ depends on GCC_PLUGIN_RANDSTRUCT
+ depends on !COMPILE_TEST
+ help
+ If you say Y here, the RANDSTRUCT randomization will make a
+ best effort at restricting randomization to cacheline-sized
+ groups of elements. It will further not randomize bitfields
+ in structures. This reduces the performance hit of RANDSTRUCT
+ at the cost of weakened randomization.
+
config HAVE_CC_STACKPROTECTOR
bool
help
diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h
index 68b06f9c65de..ad301f107dd2 100644
--- a/arch/arm/include/asm/assembler.h
+++ b/arch/arm/include/asm/assembler.h
@@ -87,6 +87,8 @@
#define CALGN(code...)
#endif
+#define IMM12_MASK 0xfff
+
/*
* Enable and disable interrupts
*/
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index d69bebf697e7..74504b154256 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -116,7 +116,7 @@ struct cpu_cache_fns {
void (*dma_unmap_area)(const void *, size_t, int);
void (*dma_flush_range)(const void *, const void *);
-};
+} __no_randomize_layout;
/*
* Select the calling method
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 9f157e7c51e7..c731f0d2b2af 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -797,7 +797,10 @@ ENTRY(__switch_to)
#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
ldr r7, [r2, #TI_TASK]
ldr r8, =__stack_chk_guard
- ldr r7, [r7, #TSK_STACK_CANARY]
+ .if (TSK_STACK_CANARY > IMM12_MASK)
+ add r7, r7, #TSK_STACK_CANARY & ~IMM12_MASK
+ .endif
+ ldr r7, [r7, #TSK_STACK_CANARY & IMM12_MASK]
#endif
#ifdef CONFIG_CPU_USE_DOMAINS
mcr p15, 0, r6, c3, c0, 0 @ Set domain register
diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S
index 0d40c285bd86..f944836da8a2 100644
--- a/arch/arm/mm/proc-macros.S
+++ b/arch/arm/mm/proc-macros.S
@@ -25,11 +25,6 @@
ldr \rd, [\rn, #VMA_VM_FLAGS]
.endm
- .macro tsk_mm, rd, rn
- ldr \rd, [\rn, #TI_TASK]
- ldr \rd, [\rd, #TSK_ACTIVE_MM]
- .endm
-
/*
* act_mm - get current->active_mm
*/
@@ -37,7 +32,10 @@
bic \rd, sp, #8128
bic \rd, \rd, #63
ldr \rd, [\rd, #TI_TASK]
- ldr \rd, [\rd, #TSK_ACTIVE_MM]
+ .if (TSK_ACTIVE_MM > IMM12_MASK)
+ add \rd, \rd, #TSK_ACTIVE_MM & ~IMM12_MASK
+ .endif
+ ldr \rd, [\rd, #TSK_ACTIVE_MM & IMM12_MASK]
.endm
/*
diff --git a/arch/x86/include/asm/paravirt_types.h b/arch/x86/include/asm/paravirt_types.h
index 7465d6fe336f..96c7e3cf43fa 100644
--- a/arch/x86/include/asm/paravirt_types.h
+++ b/arch/x86/include/asm/paravirt_types.h
@@ -83,7 +83,7 @@ struct pv_init_ops {
*/
unsigned (*patch)(u8 type, u16 clobber, void *insnbuf,
unsigned long addr, unsigned len);
-};
+} __no_randomize_layout;
struct pv_lazy_ops {
@@ -91,12 +91,12 @@ struct pv_lazy_ops {
void (*enter)(void);
void (*leave)(void);
void (*flush)(void);
-};
+} __no_randomize_layout;
struct pv_time_ops {
unsigned long long (*sched_clock)(void);
unsigned long long (*steal_clock)(int cpu);
-};
+} __no_randomize_layout;
struct pv_cpu_ops {
/* hooks for various privileged instructions */
@@ -175,7 +175,7 @@ struct pv_cpu_ops {
void (*start_context_switch)(struct task_struct *prev);
void (*end_context_switch)(struct task_struct *next);
-};
+} __no_randomize_layout;
struct pv_irq_ops {
/*
@@ -198,7 +198,7 @@ struct pv_irq_ops {
#ifdef CONFIG_X86_64
void (*adjust_exception_frame)(void);
#endif
-};
+} __no_randomize_layout;
struct pv_mmu_ops {
unsigned long (*read_cr2)(void);
@@ -306,7 +306,7 @@ struct pv_mmu_ops {
an mfn. We can tell which is which from the index. */
void (*set_fixmap)(unsigned /* enum fixed_addresses */ idx,
phys_addr_t phys, pgprot_t flags);
-};
+} __no_randomize_layout;
struct arch_spinlock;
#ifdef CONFIG_SMP
@@ -323,7 +323,7 @@ struct pv_lock_ops {
void (*kick)(int cpu);
struct paravirt_callee_save vcpu_is_preempted;
-};
+} __no_randomize_layout;
/* This contains all the paravirt structures: we get a convenient
* number for each function using the offset which we use to indicate
@@ -335,7 +335,7 @@ struct paravirt_patch_template {
struct pv_irq_ops pv_irq_ops;
struct pv_mmu_ops pv_mmu_ops;
struct pv_lock_ops pv_lock_ops;
-};
+} __no_randomize_layout;
extern struct pv_info pv_info;
extern struct pv_init_ops pv_init_ops;
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 3cada998a402..e2335edb9fc5 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -129,7 +129,7 @@ struct cpuinfo_x86 {
/* Index into per_cpu list: */
u16 cpu_index;
u32 microcode;
-};
+} __randomize_layout;
struct cpuid_regs {
u32 eax, ebx, ecx, edx;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
index a4831fe0223b..a2c59a08b2bd 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
@@ -220,9 +220,9 @@ static void amdgpu_vram_mgr_debug(struct ttm_mem_type_manager *man,
}
const struct ttm_mem_type_manager_func amdgpu_vram_mgr_func = {
- amdgpu_vram_mgr_init,
- amdgpu_vram_mgr_fini,
- amdgpu_vram_mgr_new,
- amdgpu_vram_mgr_del,
- amdgpu_vram_mgr_debug
+ .init = amdgpu_vram_mgr_init,
+ .takedown = amdgpu_vram_mgr_fini,
+ .get_node = amdgpu_vram_mgr_new,
+ .put_node = amdgpu_vram_mgr_del,
+ .debug = amdgpu_vram_mgr_debug
};
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_thermal.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_thermal.c
index d5f53d04fa08..83e40fe51b62 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_thermal.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_thermal.c
@@ -709,17 +709,17 @@ static int tf_vega10_thermal_disable_alert(struct pp_hwmgr *hwmgr,
static struct phm_master_table_item
vega10_thermal_start_thermal_controller_master_list[] = {
- {NULL, tf_vega10_thermal_initialize},
- {NULL, tf_vega10_thermal_set_temperature_range},
- {NULL, tf_vega10_thermal_enable_alert},
+ { .tableFunction = tf_vega10_thermal_initialize },
+ { .tableFunction = tf_vega10_thermal_set_temperature_range },
+ { .tableFunction = tf_vega10_thermal_enable_alert },
/* We should restrict performance levels to low before we halt the SMC.
* On the other hand we are still in boot state when we do this
* so it would be pointless.
* If this assumption changes we have to revisit this table.
*/
- {NULL, tf_vega10_thermal_setup_fan_table},
- {NULL, tf_vega10_thermal_start_smc_fan_control},
- {NULL, NULL}
+ { .tableFunction = tf_vega10_thermal_setup_fan_table },
+ { .tableFunction = tf_vega10_thermal_start_smc_fan_control },
+ { }
};
static struct phm_master_table_header
@@ -731,10 +731,10 @@ vega10_thermal_start_thermal_controller_master = {
static struct phm_master_table_item
vega10_thermal_set_temperature_range_master_list[] = {
- {NULL, tf_vega10_thermal_disable_alert},
- {NULL, tf_vega10_thermal_set_temperature_range},
- {NULL, tf_vega10_thermal_enable_alert},
- {NULL, NULL}
+ { .tableFunction = tf_vega10_thermal_disable_alert },
+ { .tableFunction = tf_vega10_thermal_set_temperature_range },
+ { .tableFunction = tf_vega10_thermal_enable_alert },
+ { }
};
struct phm_master_table_header
diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c
index 57a842ff3097..b7731b18ecae 100644
--- a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c
+++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c
@@ -493,10 +493,10 @@ static int vdec_h264_get_param(unsigned long h_vdec,
}
static struct vdec_common_if vdec_h264_if = {
- vdec_h264_init,
- vdec_h264_decode,
- vdec_h264_get_param,
- vdec_h264_deinit,
+ .init = vdec_h264_init,
+ .decode = vdec_h264_decode,
+ .get_param = vdec_h264_get_param,
+ .deinit = vdec_h264_deinit,
};
struct vdec_common_if *get_h264_dec_comm_if(void);
diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c
index 6e7a62ae0842..b9fad6a48879 100644
--- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c
+++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c
@@ -620,10 +620,10 @@ static void vdec_vp8_deinit(unsigned long h_vdec)
}
static struct vdec_common_if vdec_vp8_if = {
- vdec_vp8_init,
- vdec_vp8_decode,
- vdec_vp8_get_param,
- vdec_vp8_deinit,
+ .init = vdec_vp8_init,
+ .decode = vdec_vp8_decode,
+ .get_param = vdec_vp8_get_param,
+ .deinit = vdec_vp8_deinit,
};
struct vdec_common_if *get_vp8_dec_comm_if(void);
diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c
index 5539b1853f16..1daee1207469 100644
--- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c
+++ b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c
@@ -979,10 +979,10 @@ static int vdec_vp9_get_param(unsigned long h_vdec,
}
static struct vdec_common_if vdec_vp9_if = {
- vdec_vp9_init,
- vdec_vp9_decode,
- vdec_vp9_get_param,
- vdec_vp9_deinit,
+ .init = vdec_vp9_init,
+ .decode = vdec_vp9_decode,
+ .get_param = vdec_vp9_get_param,
+ .deinit = vdec_vp9_deinit,
};
struct vdec_common_if *get_vp9_dec_comm_if(void);
diff --git a/drivers/misc/sgi-xp/xp.h b/drivers/misc/sgi-xp/xp.h
index c862cd4583cc..b8069eec18cb 100644
--- a/drivers/misc/sgi-xp/xp.h
+++ b/drivers/misc/sgi-xp/xp.h
@@ -309,6 +309,9 @@ static inline enum xp_retval
xpc_send(short partid, int ch_number, u32 flags, void *payload,
u16 payload_size)
{
+ if (!xpc_interface.send)
+ return xpNotLoaded;
+
return xpc_interface.send(partid, ch_number, flags, payload,
payload_size);
}
@@ -317,6 +320,9 @@ static inline enum xp_retval
xpc_send_notify(short partid, int ch_number, u32 flags, void *payload,
u16 payload_size, xpc_notify_func func, void *key)
{
+ if (!xpc_interface.send_notify)
+ return xpNotLoaded;
+
return xpc_interface.send_notify(partid, ch_number, flags, payload,
payload_size, func, key);
}
@@ -324,12 +330,16 @@ xpc_send_notify(short partid, int ch_number, u32 flags, void *payload,
static inline void
xpc_received(short partid, int ch_number, void *payload)
{
- return xpc_interface.received(partid, ch_number, payload);
+ if (xpc_interface.received)
+ xpc_interface.received(partid, ch_number, payload);
}
static inline enum xp_retval
xpc_partid_to_nasids(short partid, void *nasids)
{
+ if (!xpc_interface.partid_to_nasids)
+ return xpNotLoaded;
+
return xpc_interface.partid_to_nasids(partid, nasids);
}
diff --git a/drivers/misc/sgi-xp/xp_main.c b/drivers/misc/sgi-xp/xp_main.c
index 01be66d02ca8..6d7f557fd1c1 100644
--- a/drivers/misc/sgi-xp/xp_main.c
+++ b/drivers/misc/sgi-xp/xp_main.c
@@ -69,23 +69,9 @@ struct xpc_registration xpc_registrations[XPC_MAX_NCHANNELS];
EXPORT_SYMBOL_GPL(xpc_registrations);
/*
- * Initialize the XPC interface to indicate that XPC isn't loaded.
+ * Initialize the XPC interface to NULL to indicate that XPC isn't loaded.
*/
-static enum xp_retval
-xpc_notloaded(void)
-{
- return xpNotLoaded;
-}
-
-struct xpc_interface xpc_interface = {
- (void (*)(int))xpc_notloaded,
- (void (*)(int))xpc_notloaded,
- (enum xp_retval(*)(short, int, u32, void *, u16))xpc_notloaded,
- (enum xp_retval(*)(short, int, u32, void *, u16, xpc_notify_func,
- void *))xpc_notloaded,
- (void (*)(short, int, void *))xpc_notloaded,
- (enum xp_retval(*)(short, void *))xpc_notloaded
-};
+struct xpc_interface xpc_interface = { };
EXPORT_SYMBOL_GPL(xpc_interface);
/*
@@ -115,17 +101,7 @@ EXPORT_SYMBOL_GPL(xpc_set_interface);
void
xpc_clear_interface(void)
{
- xpc_interface.connect = (void (*)(int))xpc_notloaded;
- xpc_interface.disconnect = (void (*)(int))xpc_notloaded;
- xpc_interface.send = (enum xp_retval(*)(short, int, u32, void *, u16))
- xpc_notloaded;
- xpc_interface.send_notify = (enum xp_retval(*)(short, int, u32, void *,
- u16, xpc_notify_func,
- void *))xpc_notloaded;
- xpc_interface.received = (void (*)(short, int, void *))
- xpc_notloaded;
- xpc_interface.partid_to_nasids = (enum xp_retval(*)(short, void *))
- xpc_notloaded;
+ memset(&xpc_interface, 0, sizeof(xpc_interface));
}
EXPORT_SYMBOL_GPL(xpc_clear_interface);
@@ -188,7 +164,8 @@ xpc_connect(int ch_number, xpc_channel_func func, void *key, u16 payload_size,
mutex_unlock(&registration->mutex);
- xpc_interface.connect(ch_number);
+ if (xpc_interface.connect)
+ xpc_interface.connect(ch_number);
return xpSuccess;
}
@@ -237,7 +214,8 @@ xpc_disconnect(int ch_number)
registration->assigned_limit = 0;
registration->idle_limit = 0;
- xpc_interface.disconnect(ch_number);
+ if (xpc_interface.disconnect)
+ xpc_interface.disconnect(ch_number);
mutex_unlock(&registration->mutex);
diff --git a/fs/mount.h b/fs/mount.h
index bf1fda6eed8f..e406b286fba1 100644
--- a/fs/mount.h
+++ b/fs/mount.h
@@ -16,7 +16,7 @@ struct mnt_namespace {
u64 event;
unsigned int mounts; /* # of mounts in the namespace */
unsigned int pending_mounts;
-};
+} __randomize_layout;
struct mnt_pcp {
int mnt_count;
@@ -68,7 +68,7 @@ struct mount {
struct hlist_head mnt_pins;
struct fs_pin mnt_umount;
struct dentry *mnt_ex_mountpoint;
-};
+} __randomize_layout;
#define MNT_NS_INTERNAL ERR_PTR(-EINVAL) /* distinct from any mnt_namespace */
diff --git a/fs/namei.c b/fs/namei.c
index 6571a5f5112e..1764620ac383 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -524,7 +524,7 @@ struct nameidata {
struct inode *link_inode;
unsigned root_seq;
int dfd;
-};
+} __randomize_layout;
static void set_nameidata(struct nameidata *p, int dfd, struct filename *name)
{
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index 1a224a33a6c2..e5686be67be8 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -246,7 +246,7 @@ struct vfsmount *nfs_do_submount(struct dentry *dentry, struct nfs_fh *fh,
devname = nfs_devname(dentry, page, PAGE_SIZE);
if (IS_ERR(devname))
- mnt = (struct vfsmount *)devname;
+ mnt = ERR_CAST(devname);
else
mnt = nfs_do_clone_mount(NFS_SB(dentry->d_sb), devname, &mountdata);
diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
index 358258364616..4690cd75d8d7 100644
--- a/fs/ntfs/namei.c
+++ b/fs/ntfs/namei.c
@@ -159,7 +159,7 @@ static struct dentry *ntfs_lookup(struct inode *dir_ino, struct dentry *dent,
PTR_ERR(dent_inode));
kfree(name);
/* Return the error code. */
- return (struct dentry *)dent_inode;
+ return ERR_CAST(dent_inode);
}
/* It is guaranteed that @name is no longer allocated at this point. */
if (MREF_ERR(mref) == -ENOENT) {
diff --git a/fs/ocfs2/export.c b/fs/ocfs2/export.c
index 827fc9809bc2..9f88188060db 100644
--- a/fs/ocfs2/export.c
+++ b/fs/ocfs2/export.c
@@ -119,7 +119,7 @@ check_err:
if (IS_ERR(inode)) {
mlog_errno(PTR_ERR(inode));
- result = (void *)inode;
+ result = ERR_CAST(inode);
goto bail;
}
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index c5ae09b6c726..07b16318223f 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -51,7 +51,7 @@ struct proc_dir_entry {
spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
u8 namelen;
char name[];
-};
+} __randomize_layout;
union proc_op {
int (*proc_get_link)(struct dentry *, struct path *);
@@ -70,7 +70,7 @@ struct proc_inode {
struct list_head sysctl_inodes;
const struct proc_ns_operations *ns_ops;
struct inode vfs_inode;
-};
+} __randomize_layout;
/*
* General functions
@@ -279,7 +279,7 @@ struct proc_maps_private {
#ifdef CONFIG_NUMA
struct mempolicy *task_mempolicy;
#endif
-};
+} __randomize_layout;
struct mm_struct *proc_mem_open(struct inode *inode, unsigned int mode);
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index 05488da3aee9..3ae9013eeaaa 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -46,7 +46,7 @@ struct linux_binprm {
unsigned interp_flags;
unsigned interp_data;
unsigned long loader, exec;
-};
+} __randomize_layout;
#define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0
#define BINPRM_FLAGS_ENFORCE_NONDUMP (1 << BINPRM_FLAGS_ENFORCE_NONDUMP_BIT)
@@ -81,7 +81,7 @@ struct linux_binfmt {
int (*load_shlib)(struct file *);
int (*core_dump)(struct coredump_params *cprm);
unsigned long min_coredump; /* minimal dump size */
-};
+} __randomize_layout;
extern void __register_binfmt(struct linux_binfmt *fmt, int insert);
diff --git a/include/linux/cdev.h b/include/linux/cdev.h
index 408bc09ce497..cb28eb21e3ca 100644
--- a/include/linux/cdev.h
+++ b/include/linux/cdev.h
@@ -17,7 +17,7 @@ struct cdev {
struct list_head list;
dev_t dev;
unsigned int count;
-};
+} __randomize_layout;
void cdev_init(struct cdev *, const struct file_operations *);
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 0efef9cf014f..c4a66c036692 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -223,9 +223,15 @@
/* Mark a function definition as prohibited from being cloned. */
#define __noclone __attribute__((__noclone__, __optimize__("no-tracer")))
+#ifdef RANDSTRUCT_PLUGIN
+#define __randomize_layout __attribute__((randomize_layout))
+#define __no_randomize_layout __attribute__((no_randomize_layout))
+#endif
+
#endif /* GCC_VERSION >= 40500 */
#if GCC_VERSION >= 40600
+
/*
* When used with Link Time Optimization, gcc can optimize away C functions or
* variables which are referenced only from assembly code. __visible tells the
@@ -233,7 +239,17 @@
* this.
*/
#define __visible __attribute__((externally_visible))
-#endif
+
+/*
+ * RANDSTRUCT_PLUGIN wants to use an anonymous struct, but it is only
+ * possible since GCC 4.6. To provide as much build testing coverage
+ * as possible, this is used for all GCC 4.6+ builds, and not just on
+ * RANDSTRUCT_PLUGIN builds.
+ */
+#define randomized_struct_fields_start struct {
+#define randomized_struct_fields_end } __randomize_layout;
+
+#endif /* GCC_VERSION >= 40600 */
#if GCC_VERSION >= 40900 && !defined(__CHECKER__)
@@ -294,6 +310,14 @@
#define __no_sanitize_address __attribute__((no_sanitize_address))
#endif
+#if GCC_VERSION >= 50100
+/*
+ * Mark structures as requiring designated initializers.
+ * https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html
+ */
+#define __designated_init __attribute__((designated_init))
+#endif
+
#endif /* gcc version >= 40000 specific checks */
#if !defined(__noclone)
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index f8110051188f..0b4ac3e8c63e 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -440,10 +440,27 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
# define __attribute_const__ /* unimplemented */
#endif
+#ifndef __designated_init
+# define __designated_init
+#endif
+
#ifndef __latent_entropy
# define __latent_entropy
#endif
+#ifndef __randomize_layout
+# define __randomize_layout __designated_init
+#endif
+
+#ifndef __no_randomize_layout
+# define __no_randomize_layout
+#endif
+
+#ifndef randomized_struct_fields_start
+# define randomized_struct_fields_start
+# define randomized_struct_fields_end
+#endif
+
/*
* Tell gcc if a function is cold. The compiler will assume any path
* directly leading to the call is unlikely.
diff --git a/include/linux/cred.h b/include/linux/cred.h
index b03e7d049a64..82c8a9e1aabb 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -31,7 +31,7 @@ struct group_info {
atomic_t usage;
int ngroups;
kgid_t gid[0];
-};
+} __randomize_layout;
/**
* get_group_info - Get a reference to a group info structure
@@ -145,7 +145,7 @@ struct cred {
struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
struct group_info *group_info; /* supplementary groups for euid/fsgid */
struct rcu_head rcu; /* RCU deletion hook */
-};
+} __randomize_layout;
extern void __put_cred(struct cred *);
extern void exit_creds(struct task_struct *);
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index d2e38dc6172c..7eb262e13d3c 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -113,7 +113,7 @@ struct dentry {
struct hlist_bl_node d_in_lookup_hash; /* only for in-lookup ones */
struct rcu_head d_rcu;
} d_u;
-};
+} __randomize_layout;
/*
* dentry->d_lock spinlock nesting subclasses:
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 803e5a9b2654..8f28143486c4 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -275,7 +275,7 @@ struct kiocb {
void (*ki_complete)(struct kiocb *iocb, long ret, long ret2);
void *private;
int ki_flags;
-};
+} __randomize_layout;
static inline bool is_sync_kiocb(struct kiocb *kiocb)
{
@@ -392,7 +392,7 @@ struct address_space {
gfp_t gfp_mask; /* implicit gfp mask for allocations */
struct list_head private_list; /* ditto */
void *private_data; /* ditto */
-} __attribute__((aligned(sizeof(long))));
+} __attribute__((aligned(sizeof(long)))) __randomize_layout;
/*
* On most architectures that alignment is already the case; but
* must be enforced here for CRIS, to let the least significant bit
@@ -435,7 +435,7 @@ struct block_device {
int bd_fsfreeze_count;
/* Mutex for freeze */
struct mutex bd_fsfreeze_mutex;
-};
+} __randomize_layout;
/*
* Radix-tree tags, for tagging dirty and writeback pages within the pagecache
@@ -653,7 +653,7 @@ struct inode {
#endif
void *i_private; /* fs or device private pointer */
-};
+} __randomize_layout;
static inline unsigned int i_blocksize(const struct inode *node)
{
@@ -868,7 +868,8 @@ struct file {
struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
-} __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
+} __randomize_layout
+ __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
struct file_handle {
__u32 handle_bytes;
@@ -1005,7 +1006,7 @@ struct file_lock {
int state; /* state of grant or error if -ve */
} afs;
} fl_u;
-};
+} __randomize_layout;
struct file_lock_context {
spinlock_t flc_lock;
@@ -1404,7 +1405,7 @@ struct super_block {
spinlock_t s_inode_wblist_lock;
struct list_head s_inodes_wb; /* writeback inodes */
-};
+} __randomize_layout;
/* Helper functions so that in most cases filesystems will
* not need to deal directly with kuid_t and kgid_t and can
@@ -1690,7 +1691,7 @@ struct file_operations {
u64);
ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
u64);
-};
+} __randomize_layout;
struct inode_operations {
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
diff --git a/include/linux/fs_struct.h b/include/linux/fs_struct.h
index 0efc3e62843a..7a026240cbb1 100644
--- a/include/linux/fs_struct.h
+++ b/include/linux/fs_struct.h
@@ -12,7 +12,7 @@ struct fs_struct {
int umask;
int in_exec;
struct path root, pwd;
-};
+} __randomize_layout;
extern struct kmem_cache *fs_cachep;
diff --git a/include/linux/ipc.h b/include/linux/ipc.h
index 71fd92d81b26..ea0eb0b5f98c 100644
--- a/include/linux/ipc.h
+++ b/include/linux/ipc.h
@@ -20,6 +20,6 @@ struct kern_ipc_perm {
umode_t mode;
unsigned long seq;
void *security;
-} ____cacheline_aligned_in_smp;
+} ____cacheline_aligned_in_smp __randomize_layout;
#endif /* _LINUX_IPC_H */
diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h
index 848e5796400e..65327ee0936b 100644
--- a/include/linux/ipc_namespace.h
+++ b/include/linux/ipc_namespace.h
@@ -61,7 +61,7 @@ struct ipc_namespace {
struct ucounts *ucounts;
struct ns_common ns;
-};
+} __randomize_layout;
extern struct ipc_namespace init_ipc_ns;
extern spinlock_t mq_lock;
diff --git a/include/linux/key-type.h b/include/linux/key-type.h
index 8496cf64575c..9520fc3c3b9a 100644
--- a/include/linux/key-type.h
+++ b/include/linux/key-type.h
@@ -45,7 +45,7 @@ struct key_preparsed_payload {
size_t datalen; /* Raw datalen */
size_t quotalen; /* Quota length for proposed payload */
time_t expiry; /* Expiry time of key */
-};
+} __randomize_layout;
typedef int (*request_key_actor_t)(struct key_construction *key,
const char *op, void *aux);
@@ -158,7 +158,7 @@ struct key_type {
/* internal fields */
struct list_head link; /* link in types list */
struct lock_class_key lock_class; /* key->sem lock class */
-};
+} __randomize_layout;
extern struct key_type key_type_keyring;
diff --git a/include/linux/kmod.h b/include/linux/kmod.h
index c4e441e00db5..655082c88fd9 100644
--- a/include/linux/kmod.h
+++ b/include/linux/kmod.h
@@ -64,7 +64,7 @@ struct subprocess_info {
int (*init)(struct subprocess_info *info, struct cred *new);
void (*cleanup)(struct subprocess_info *info);
void *data;
-};
+} __randomize_layout;
extern int
call_usermodehelper(const char *path, char **argv, char **envp, int wait);
diff --git a/include/linux/kobject.h b/include/linux/kobject.h
index ca85cb80e99a..084513350317 100644
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -172,7 +172,7 @@ struct kset {
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
-};
+} __randomize_layout;
extern void kset_init(struct kset *kset);
extern int __must_check kset_register(struct kset *kset);
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 080f34e66017..565163fc9ad4 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1876,7 +1876,7 @@ struct security_hook_heads {
struct list_head audit_rule_match;
struct list_head audit_rule_free;
#endif /* CONFIG_AUDIT */
-};
+} __randomize_layout;
/*
* Security module hook list structure.
@@ -1887,7 +1887,7 @@ struct security_hook_list {
struct list_head *head;
union security_list_options hook;
char *lsm;
-};
+} __randomize_layout;
/*
* Initializing a security_hook_list structure takes
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 45cdb27791a3..ff151814a02d 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -342,7 +342,7 @@ struct vm_area_struct {
struct mempolicy *vm_policy; /* NUMA policy for the VMA */
#endif
struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
-};
+} __randomize_layout;
struct core_thread {
struct task_struct *task;
@@ -500,7 +500,7 @@ struct mm_struct {
atomic_long_t hugetlb_usage;
#endif
struct work_struct async_put_work;
-};
+} __randomize_layout;
extern struct mm_struct init_mm;
diff --git a/include/linux/module.h b/include/linux/module.h
index 21f56393602f..d93111d7def6 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -45,7 +45,7 @@ struct module_kobject {
struct kobject *drivers_dir;
struct module_param_attrs *mp;
struct completion *kobj_completion;
-};
+} __randomize_layout;
struct module_attribute {
struct attribute attr;
@@ -475,7 +475,7 @@ struct module {
ctor_fn_t *ctors;
unsigned int num_ctors;
#endif
-} ____cacheline_aligned;
+} ____cacheline_aligned __randomize_layout;
#ifndef MODULE_ARCH_INIT
#define MODULE_ARCH_INIT {}
#endif
diff --git a/include/linux/mount.h b/include/linux/mount.h
index 8e0352af06b7..1ce85e6fd95f 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -67,7 +67,7 @@ struct vfsmount {
struct dentry *mnt_root; /* root of the mounted tree */
struct super_block *mnt_sb; /* pointer to superblock */
int mnt_flags;
-};
+} __randomize_layout;
struct file; /* forward dec */
struct path;
diff --git a/include/linux/msg.h b/include/linux/msg.h
index f3f302f9c197..a001305f5a79 100644
--- a/include/linux/msg.h
+++ b/include/linux/msg.h
@@ -29,7 +29,7 @@ struct msg_queue {
struct list_head q_messages;
struct list_head q_receivers;
struct list_head q_senders;
-};
+} __randomize_layout;
/* Helper routines for sys_msgsnd and sys_msgrcv */
extern long do_msgsnd(int msqid, long mtype, void __user *mtext,
diff --git a/include/linux/path.h b/include/linux/path.h
index d1372186f431..cde895cc4af4 100644
--- a/include/linux/path.h
+++ b/include/linux/path.h
@@ -7,7 +7,7 @@ struct vfsmount;
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
-};
+} __randomize_layout;
extern void path_get(const struct path *);
extern void path_put(const struct path *);
diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h
index c2a989dee876..b09136f88cf4 100644
--- a/include/linux/pid_namespace.h
+++ b/include/linux/pid_namespace.h
@@ -52,7 +52,7 @@ struct pid_namespace {
int hide_pid;
int reboot; /* group exit code if this pidns was rebooted */
struct ns_common ns;
-};
+} __randomize_layout;
extern struct pid_namespace init_pid_ns;
diff --git a/include/linux/proc_ns.h b/include/linux/proc_ns.h
index 58ab28d81fc2..06844b54dfc1 100644
--- a/include/linux/proc_ns.h
+++ b/include/linux/proc_ns.h
@@ -21,7 +21,7 @@ struct proc_ns_operations {
int (*install)(struct nsproxy *nsproxy, struct ns_common *ns);
struct user_namespace *(*owner)(struct ns_common *ns);
struct ns_common *(*get_parent)(struct ns_common *ns);
-};
+} __randomize_layout;
extern const struct proc_ns_operations netns_operations;
extern const struct proc_ns_operations utsns_operations;
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 2b69fc650201..e2ad3531e7fe 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -408,7 +408,7 @@ struct sched_rt_entity {
/* rq "owned" by this entity/group: */
struct rt_rq *my_q;
#endif
-};
+} __randomize_layout;
struct sched_dl_entity {
struct rb_node rb_node;
@@ -490,6 +490,13 @@ struct task_struct {
#endif
/* -1 unrunnable, 0 runnable, >0 stopped: */
volatile long state;
+
+ /*
+ * This begins the randomizable portion of task_struct. Only
+ * scheduling-critical items should be added above here.
+ */
+ randomized_struct_fields_start
+
void *stack;
atomic_t usage;
/* Per task flags (PF_*), defined further below: */
@@ -1051,6 +1058,13 @@ struct task_struct {
/* Used by LSM modules for access restriction: */
void *security;
#endif
+
+ /*
+ * New fields for task_struct should be added above here, so that
+ * they are included in the randomized portion of task_struct.
+ */
+ randomized_struct_fields_end
+
/* CPU-specific state of this task: */
struct thread_struct thread;
diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h
index c06d63b3a583..2a0dd40b15db 100644
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -222,7 +222,7 @@ struct signal_struct {
struct mutex cred_guard_mutex; /* guard against foreign influences on
* credential calculations
* (notably. ptrace) */
-};
+} __randomize_layout;
/*
* Bits in flags field of signal_struct.
diff --git a/include/linux/sem.h b/include/linux/sem.h
index 9edec926e9d9..23bcbdfad4a6 100644
--- a/include/linux/sem.h
+++ b/include/linux/sem.h
@@ -21,7 +21,7 @@ struct sem_array {
int sem_nsems; /* no. of semaphores in array */
int complex_count; /* pending complex operations */
unsigned int use_global_lock;/* >0: global lock required */
-};
+} __randomize_layout;
#ifdef CONFIG_SYSVIPC
diff --git a/include/linux/shm.h b/include/linux/shm.h
index 04e881829625..0fb7061ec54c 100644
--- a/include/linux/shm.h
+++ b/include/linux/shm.h
@@ -22,7 +22,7 @@ struct shmid_kernel /* private to the kernel */
/* The task created the shm object. NULL if the task is dead. */
struct task_struct *shm_creator;
struct list_head shm_clist; /* list by creator */
-};
+} __randomize_layout;
/* shm_mode upper byte flags */
#define SHM_DEST 01000 /* segment will be destroyed on last detach */
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 80d07816def0..9ddeef2c03e2 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -117,7 +117,7 @@ struct ctl_table
struct ctl_table_poll *poll;
void *extra1;
void *extra2;
-};
+} __randomize_layout;
struct ctl_node {
struct rb_node node;
diff --git a/include/linux/tty.h b/include/linux/tty.h
index d07cd2105a6c..73f8d0977bb0 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -333,7 +333,7 @@ struct tty_struct {
/* If the tty has a pending do_SAK, queue it here - akpm */
struct work_struct SAK_work;
struct tty_port *port;
-};
+} __randomize_layout;
/* Each of a tty's open files has private_data pointing to tty_file_private */
struct tty_file_private {
diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h
index b742b5e47cc2..00b2213f6a35 100644
--- a/include/linux/tty_driver.h
+++ b/include/linux/tty_driver.h
@@ -291,7 +291,7 @@ struct tty_operations {
void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
#endif
const struct file_operations *proc_fops;
-};
+} __randomize_layout;
struct tty_driver {
int magic; /* magic number for this structure */
@@ -325,7 +325,7 @@ struct tty_driver {
const struct tty_operations *ops;
struct list_head tty_drivers;
-};
+} __randomize_layout;
extern struct list_head tty_drivers;
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 32354b4b4b2b..b3575ce29148 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -66,7 +66,7 @@ struct user_namespace {
#endif
struct ucounts *ucounts;
int ucount_max[UCOUNT_COUNTS];
-};
+} __randomize_layout;
struct ucounts {
struct hlist_node node;
diff --git a/include/linux/utsname.h b/include/linux/utsname.h
index 60f0bb83b313..da826ed059cf 100644
--- a/include/linux/utsname.h
+++ b/include/linux/utsname.h
@@ -26,7 +26,7 @@ struct uts_namespace {
struct user_namespace *user_ns;
struct ucounts *ucounts;
struct ns_common ns;
-};
+} __randomize_layout;
extern struct uts_namespace init_uts_ns;
#ifdef CONFIG_UTS_NS
diff --git a/include/linux/vermagic.h b/include/linux/vermagic.h
index 6f8fbcf10dfb..af6c03f7f986 100644
--- a/include/linux/vermagic.h
+++ b/include/linux/vermagic.h
@@ -24,10 +24,17 @@
#ifndef MODULE_ARCH_VERMAGIC
#define MODULE_ARCH_VERMAGIC ""
#endif
+#ifdef RANDSTRUCT_PLUGIN
+#include <generated/randomize_layout_hash.h>
+#define MODULE_RANDSTRUCT_PLUGIN "RANDSTRUCT_PLUGIN_" RANDSTRUCT_HASHED_SEED
+#else
+#define MODULE_RANDSTRUCT_PLUGIN
+#endif
#define VERMAGIC_STRING \
UTS_RELEASE " " \
MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT \
MODULE_VERMAGIC_MODULE_UNLOAD MODULE_VERMAGIC_MODVERSIONS \
- MODULE_ARCH_VERMAGIC
+ MODULE_ARCH_VERMAGIC \
+ MODULE_RANDSTRUCT_PLUGIN
diff --git a/include/net/af_unix.h b/include/net/af_unix.h
index fd60eccb59a6..64e2a1e24a2c 100644
--- a/include/net/af_unix.h
+++ b/include/net/af_unix.h
@@ -36,7 +36,7 @@ struct unix_skb_parms {
u32 secid; /* Security ID */
#endif
u32 consumed;
-};
+} __randomize_layout;
#define UNIXCB(skb) (*(struct unix_skb_parms *)&((skb)->cb))
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index e4dd3a214034..a62959d2b3f7 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -155,7 +155,7 @@ struct neighbour {
struct rcu_head rcu;
struct net_device *dev;
u8 primary_key[0];
-};
+} __randomize_layout;
struct neigh_ops {
int family;
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index fe80bb48ab1f..a224196d16ac 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -147,7 +147,7 @@ struct net {
#endif
struct sock *diag_nlsk;
atomic_t fnhe_genid;
-};
+} __randomize_layout;
#include <linux/seq_file_net.h>
diff --git a/include/net/sock.h b/include/net/sock.h
index f33e3d134e0b..d349297db9e9 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -1113,7 +1113,7 @@ struct proto {
atomic_t socks;
#endif
int (*diag_destroy)(struct sock *sk, int err);
-};
+} __randomize_layout;
int proto_register(struct proto *prot, int alloc_slab);
void proto_unregister(struct proto *prot);
diff --git a/kernel/futex.c b/kernel/futex.c
index 357348a6cf6b..5616511abf39 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -212,7 +212,7 @@ struct futex_pi_state {
atomic_t refcount;
union futex_key key;
-};
+} __randomize_layout;
/**
* struct futex_q - The hashed futex queue entry, one per waiting task
@@ -246,7 +246,7 @@ struct futex_q {
struct rt_mutex_waiter *rt_waiter;
union futex_key *requeue_pi_key;
u32 bitset;
-};
+} __randomize_layout;
static const struct futex_q futex_q_init = {
/* list gets initialized in queue_me()*/
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index 82335533620e..2e0e2eaa397f 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -29,6 +29,10 @@ ifdef CONFIG_GCC_PLUGINS
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK_VERBOSE) += -fplugin-arg-structleak_plugin-verbose
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK) += -DSTRUCTLEAK_PLUGIN
+ gcc-plugin-$(CONFIG_GCC_PLUGIN_RANDSTRUCT) += randomize_layout_plugin.so
+ gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT) += -DRANDSTRUCT_PLUGIN
+ gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT_PERFORMANCE) += -fplugin-arg-randomize_layout_plugin-performance-mode
+
GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
diff --git a/scripts/gcc-plugins/.gitignore b/scripts/gcc-plugins/.gitignore
new file mode 100644
index 000000000000..de92ed9e3d83
--- /dev/null
+++ b/scripts/gcc-plugins/.gitignore
@@ -0,0 +1 @@
+randomize_layout_seed.h
diff --git a/scripts/gcc-plugins/Makefile b/scripts/gcc-plugins/Makefile
index 8b29dc17c73c..214eb2335c31 100644
--- a/scripts/gcc-plugins/Makefile
+++ b/scripts/gcc-plugins/Makefile
@@ -18,6 +18,14 @@ endif
export HOSTLIBS
+$(obj)/randomize_layout_plugin.o: $(objtree)/$(obj)/randomize_layout_seed.h
+quiet_cmd_create_randomize_layout_seed = GENSEED $@
+cmd_create_randomize_layout_seed = \
+ $(CONFIG_SHELL) $(srctree)/$(src)/gen-random-seed.sh $@ $(objtree)/include/generated/randomize_layout_hash.h
+$(objtree)/$(obj)/randomize_layout_seed.h: FORCE
+ $(call if_changed,create_randomize_layout_seed)
+targets = randomize_layout_seed.h randomize_layout_hash.h
+
$(HOSTLIBS)-y := $(foreach p,$(GCC_PLUGIN),$(if $(findstring /,$(p)),,$(p)))
always := $($(HOSTLIBS)-y)
diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h
index b232ab15624c..6948898b3cdf 100644
--- a/scripts/gcc-plugins/gcc-common.h
+++ b/scripts/gcc-plugins/gcc-common.h
@@ -63,6 +63,13 @@
#endif
#if BUILDING_GCC_VERSION >= 4006
+/*
+ * The c-family headers were moved into a subdirectory in GCC version
+ * 4.7, but most plugin-building users of GCC 4.6 are using the Debian
+ * or Ubuntu package, which has an out-of-tree patch to move this to the
+ * same location as found in 4.7 and later:
+ * https://sources.debian.net/src/gcc-4.6/4.6.3-14/debian/patches/pr45078.diff/
+ */
#include "c-family/c-common.h"
#else
#include "c-common.h"
@@ -946,4 +953,9 @@ static inline void debug_gimple_stmt(const_gimple s)
get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep)
#endif
+#if BUILDING_GCC_VERSION < 7000
+#define SET_DECL_ALIGN(decl, align) DECL_ALIGN(decl) = (align)
+#define SET_DECL_MODE(decl, mode) DECL_MODE(decl) = (mode)
+#endif
+
#endif
diff --git a/scripts/gcc-plugins/gen-random-seed.sh b/scripts/gcc-plugins/gen-random-seed.sh
new file mode 100644
index 000000000000..7514850f4815
--- /dev/null
+++ b/scripts/gcc-plugins/gen-random-seed.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+if [ ! -f "$1" ]; then
+ SEED=`od -A n -t x8 -N 32 /dev/urandom | tr -d ' \n'`
+ echo "const char *randstruct_seed = \"$SEED\";" > "$1"
+ HASH=`echo -n "$SEED" | sha256sum | cut -d" " -f1 | tr -d ' \n'`
+ echo "#define RANDSTRUCT_HASHED_SEED \"$HASH\"" > "$2"
+fi
diff --git a/scripts/gcc-plugins/randomize_layout_plugin.c b/scripts/gcc-plugins/randomize_layout_plugin.c
new file mode 100644
index 000000000000..cdaac8c66734
--- /dev/null
+++ b/scripts/gcc-plugins/randomize_layout_plugin.c
@@ -0,0 +1,1028 @@
+/*
+ * Copyright 2014-2016 by Open Source Security, Inc., Brad Spengler <spender@grsecurity.net>
+ * and PaX Team <pageexec@freemail.hu>
+ * Licensed under the GPL v2
+ *
+ * Note: the choice of the license means that the compilation process is
+ * NOT 'eligible' as defined by gcc's library exception to the GPL v3,
+ * but for the kernel it doesn't matter since it doesn't link against
+ * any of the gcc libraries
+ *
+ * Usage:
+ * $ # for 4.5/4.6/C based 4.7
+ * $ gcc -I`gcc -print-file-name=plugin`/include -I`gcc -print-file-name=plugin`/include/c-family -fPIC -shared -O2 -o randomize_layout_plugin.so randomize_layout_plugin.c
+ * $ # for C++ based 4.7/4.8+
+ * $ g++ -I`g++ -print-file-name=plugin`/include -I`g++ -print-file-name=plugin`/include/c-family -fPIC -shared -O2 -o randomize_layout_plugin.so randomize_layout_plugin.c
+ * $ gcc -fplugin=./randomize_layout_plugin.so test.c -O2
+ */
+
+#include "gcc-common.h"
+#include "randomize_layout_seed.h"
+
+#if BUILDING_GCC_MAJOR < 4 || (BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR < 7)
+#error "The RANDSTRUCT plugin requires GCC 4.7 or newer."
+#endif
+
+#define ORIG_TYPE_NAME(node) \
+ (TYPE_NAME(TYPE_MAIN_VARIANT(node)) != NULL_TREE ? ((const unsigned char *)IDENTIFIER_POINTER(TYPE_NAME(TYPE_MAIN_VARIANT(node)))) : (const unsigned char *)"anonymous")
+
+#define INFORM(loc, msg, ...) inform(loc, "randstruct: " msg, ##__VA_ARGS__)
+#define MISMATCH(loc, how, ...) INFORM(loc, "casting between randomized structure pointer types (" how "): %qT and %qT\n", __VA_ARGS__)
+
+__visible int plugin_is_GPL_compatible;
+
+static int performance_mode;
+
+static struct plugin_info randomize_layout_plugin_info = {
+ .version = "201402201816vanilla",
+ .help = "disable\t\t\tdo not activate plugin\n"
+ "performance-mode\tenable cacheline-aware layout randomization\n"
+};
+
+struct whitelist_entry {
+ const char *pathname;
+ const char *lhs;
+ const char *rhs;
+};
+
+static const struct whitelist_entry whitelist[] = {
+ /* NIU overloads mapping with page struct */
+ { "drivers/net/ethernet/sun/niu.c", "page", "address_space" },
+ /* unix_skb_parms via UNIXCB() buffer */
+ { "net/unix/af_unix.c", "unix_skb_parms", "char" },
+ /* big_key payload.data struct splashing */
+ { "security/keys/big_key.c", "path", "void *" },
+ /* walk struct security_hook_heads as an array of struct list_head */
+ { "security/security.c", "list_head", "security_hook_heads" },
+ { }
+};
+
+/* from old Linux dcache.h */
+static inline unsigned long
+partial_name_hash(unsigned long c, unsigned long prevhash)
+{
+ return (prevhash + (c << 4) + (c >> 4)) * 11;
+}
+static inline unsigned int
+name_hash(const unsigned char *name)
+{
+ unsigned long hash = 0;
+ unsigned int len = strlen((const char *)name);
+ while (len--)
+ hash = partial_name_hash(*name++, hash);
+ return (unsigned int)hash;
+}
+
+static tree handle_randomize_layout_attr(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
+{
+ tree type;
+
+ *no_add_attrs = true;
+ if (TREE_CODE(*node) == FUNCTION_DECL) {
+ error("%qE attribute does not apply to functions (%qF)", name, *node);
+ return NULL_TREE;
+ }
+
+ if (TREE_CODE(*node) == PARM_DECL) {
+ error("%qE attribute does not apply to function parameters (%qD)", name, *node);
+ return NULL_TREE;
+ }
+
+ if (TREE_CODE(*node) == VAR_DECL) {
+ error("%qE attribute does not apply to variables (%qD)", name, *node);
+ return NULL_TREE;
+ }
+
+ if (TYPE_P(*node)) {
+ type = *node;
+ } else {
+ gcc_assert(TREE_CODE(*node) == TYPE_DECL);
+ type = TREE_TYPE(*node);
+ }
+
+ if (TREE_CODE(type) != RECORD_TYPE) {
+ error("%qE attribute used on %qT applies to struct types only", name, type);
+ return NULL_TREE;
+ }
+
+ if (lookup_attribute(IDENTIFIER_POINTER(name), TYPE_ATTRIBUTES(type))) {
+ error("%qE attribute is already applied to the type %qT", name, type);
+ return NULL_TREE;
+ }
+
+ *no_add_attrs = false;
+
+ return NULL_TREE;
+}
+
+/* set on complete types that we don't need to inspect further at all */
+static tree handle_randomize_considered_attr(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
+{
+ *no_add_attrs = false;
+ return NULL_TREE;
+}
+
+/*
+ * set on types that we've performed a shuffle on, to prevent re-shuffling
+ * this does not preclude us from inspecting its fields for potential shuffles
+ */
+static tree handle_randomize_performed_attr(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
+{
+ *no_add_attrs = false;
+ return NULL_TREE;
+}
+
+/*
+ * 64bit variant of Bob Jenkins' public domain PRNG
+ * 256 bits of internal state
+ */
+
+typedef unsigned long long u64;
+
+typedef struct ranctx { u64 a; u64 b; u64 c; u64 d; } ranctx;
+
+#define rot(x,k) (((x)<<(k))|((x)>>(64-(k))))
+static u64 ranval(ranctx *x) {
+ u64 e = x->a - rot(x->b, 7);
+ x->a = x->b ^ rot(x->c, 13);
+ x->b = x->c + rot(x->d, 37);
+ x->c = x->d + e;
+ x->d = e + x->a;
+ return x->d;
+}
+
+static void raninit(ranctx *x, u64 *seed) {
+ int i;
+
+ x->a = seed[0];
+ x->b = seed[1];
+ x->c = seed[2];
+ x->d = seed[3];
+
+ for (i=0; i < 30; ++i)
+ (void)ranval(x);
+}
+
+static u64 shuffle_seed[4];
+
+struct partition_group {
+ tree tree_start;
+ unsigned long start;
+ unsigned long length;
+};
+
+static void partition_struct(tree *fields, unsigned long length, struct partition_group *size_groups, unsigned long *num_groups)
+{
+ unsigned long i;
+ unsigned long accum_size = 0;
+ unsigned long accum_length = 0;
+ unsigned long group_idx = 0;
+
+ gcc_assert(length < INT_MAX);
+
+ memset(size_groups, 0, sizeof(struct partition_group) * length);
+
+ for (i = 0; i < length; i++) {
+ if (size_groups[group_idx].tree_start == NULL_TREE) {
+ size_groups[group_idx].tree_start = fields[i];
+ size_groups[group_idx].start = i;
+ accum_length = 0;
+ accum_size = 0;
+ }
+ accum_size += (unsigned long)int_size_in_bytes(TREE_TYPE(fields[i]));
+ accum_length++;
+ if (accum_size >= 64) {
+ size_groups[group_idx].length = accum_length;
+ accum_length = 0;
+ group_idx++;
+ }
+ }
+
+ if (size_groups[group_idx].tree_start != NULL_TREE &&
+ !size_groups[group_idx].length) {
+ size_groups[group_idx].length = accum_length;
+ group_idx++;
+ }
+
+ *num_groups = group_idx;
+}
+
+static void performance_shuffle(tree *newtree, unsigned long length, ranctx *prng_state)
+{
+ unsigned long i, x;
+ struct partition_group size_group[length];
+ unsigned long num_groups = 0;
+ unsigned long randnum;
+
+ partition_struct(newtree, length, (struct partition_group *)&size_group, &num_groups);
+ for (i = num_groups - 1; i > 0; i--) {
+ struct partition_group tmp;
+ randnum = ranval(prng_state) % (i + 1);
+ tmp = size_group[i];
+ size_group[i] = size_group[randnum];
+ size_group[randnum] = tmp;
+ }
+
+ for (x = 0; x < num_groups; x++) {
+ for (i = size_group[x].start + size_group[x].length - 1; i > size_group[x].start; i--) {
+ tree tmp;
+ if (DECL_BIT_FIELD_TYPE(newtree[i]))
+ continue;
+ randnum = ranval(prng_state) % (i + 1);
+ // we could handle this case differently if desired
+ if (DECL_BIT_FIELD_TYPE(newtree[randnum]))
+ continue;
+ tmp = newtree[i];
+ newtree[i] = newtree[randnum];
+ newtree[randnum] = tmp;
+ }
+ }
+}
+
+static void full_shuffle(tree *newtree, unsigned long length, ranctx *prng_state)
+{
+ unsigned long i, randnum;
+
+ for (i = length - 1; i > 0; i--) {
+ tree tmp;
+ randnum = ranval(prng_state) % (i + 1);
+ tmp = newtree[i];
+ newtree[i] = newtree[randnum];
+ newtree[randnum] = tmp;
+ }
+}
+
+/* modern in-place Fisher-Yates shuffle */
+static void shuffle(const_tree type, tree *newtree, unsigned long length)
+{
+ unsigned long i;
+ u64 seed[4];
+ ranctx prng_state;
+ const unsigned char *structname;
+
+ if (length == 0)
+ return;
+
+ gcc_assert(TREE_CODE(type) == RECORD_TYPE);
+
+ structname = ORIG_TYPE_NAME(type);
+
+#ifdef __DEBUG_PLUGIN
+ fprintf(stderr, "Shuffling struct %s %p\n", (const char *)structname, type);
+#ifdef __DEBUG_VERBOSE
+ debug_tree((tree)type);
+#endif
+#endif
+
+ for (i = 0; i < 4; i++) {
+ seed[i] = shuffle_seed[i];
+ seed[i] ^= name_hash(structname);
+ }
+
+ raninit(&prng_state, (u64 *)&seed);
+
+ if (performance_mode)
+ performance_shuffle(newtree, length, &prng_state);
+ else
+ full_shuffle(newtree, length, &prng_state);
+}
+
+static bool is_flexible_array(const_tree field)
+{
+ const_tree fieldtype;
+ const_tree typesize;
+ const_tree elemtype;
+ const_tree elemsize;
+
+ fieldtype = TREE_TYPE(field);
+ typesize = TYPE_SIZE(fieldtype);
+
+ if (TREE_CODE(fieldtype) != ARRAY_TYPE)
+ return false;
+
+ elemtype = TREE_TYPE(fieldtype);
+ elemsize = TYPE_SIZE(elemtype);
+
+ /* size of type is represented in bits */
+
+ if (typesize == NULL_TREE && TYPE_DOMAIN(fieldtype) != NULL_TREE &&
+ TYPE_MAX_VALUE(TYPE_DOMAIN(fieldtype)) == NULL_TREE)
+ return true;
+
+ if (typesize != NULL_TREE &&
+ (TREE_CONSTANT(typesize) && (!tree_to_uhwi(typesize) ||
+ tree_to_uhwi(typesize) == tree_to_uhwi(elemsize))))
+ return true;
+
+ return false;
+}
+
+static int relayout_struct(tree type)
+{
+ unsigned long num_fields = (unsigned long)list_length(TYPE_FIELDS(type));
+ unsigned long shuffle_length = num_fields;
+ tree field;
+ tree newtree[num_fields];
+ unsigned long i;
+ tree list;
+ tree variant;
+ tree main_variant;
+ expanded_location xloc;
+ bool has_flexarray = false;
+
+ if (TYPE_FIELDS(type) == NULL_TREE)
+ return 0;
+
+ if (num_fields < 2)
+ return 0;
+
+ gcc_assert(TREE_CODE(type) == RECORD_TYPE);
+
+ gcc_assert(num_fields < INT_MAX);
+
+ if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(type)) ||
+ lookup_attribute("no_randomize_layout", TYPE_ATTRIBUTES(TYPE_MAIN_VARIANT(type))))
+ return 0;
+
+ /* Workaround for 3rd-party VirtualBox source that we can't modify ourselves */
+ if (!strcmp((const char *)ORIG_TYPE_NAME(type), "INTNETTRUNKFACTORY") ||
+ !strcmp((const char *)ORIG_TYPE_NAME(type), "RAWPCIFACTORY"))
+ return 0;
+
+ /* throw out any structs in uapi */
+ xloc = expand_location(DECL_SOURCE_LOCATION(TYPE_FIELDS(type)));
+
+ if (strstr(xloc.file, "/uapi/"))
+ error(G_("attempted to randomize userland API struct %s"), ORIG_TYPE_NAME(type));
+
+ for (field = TYPE_FIELDS(type), i = 0; field; field = TREE_CHAIN(field), i++) {
+ gcc_assert(TREE_CODE(field) == FIELD_DECL);
+ newtree[i] = field;
+ }
+
+ /*
+ * enforce that we don't randomize the layout of the last
+ * element of a struct if it's a 0 or 1-length array
+ * or a proper flexible array
+ */
+ if (is_flexible_array(newtree[num_fields - 1])) {
+ has_flexarray = true;
+ shuffle_length--;
+ }
+
+ shuffle(type, (tree *)newtree, shuffle_length);
+
+ /*
+ * set up a bogus anonymous struct field designed to error out on unnamed struct initializers
+ * as gcc provides no other way to detect such code
+ */
+ list = make_node(FIELD_DECL);
+ TREE_CHAIN(list) = newtree[0];
+ TREE_TYPE(list) = void_type_node;
+ DECL_SIZE(list) = bitsize_zero_node;
+ DECL_NONADDRESSABLE_P(list) = 1;
+ DECL_FIELD_BIT_OFFSET(list) = bitsize_zero_node;
+ DECL_SIZE_UNIT(list) = size_zero_node;
+ DECL_FIELD_OFFSET(list) = size_zero_node;
+ DECL_CONTEXT(list) = type;
+ // to satisfy the constify plugin
+ TREE_READONLY(list) = 1;
+
+ for (i = 0; i < num_fields - 1; i++)
+ TREE_CHAIN(newtree[i]) = newtree[i+1];
+ TREE_CHAIN(newtree[num_fields - 1]) = NULL_TREE;
+
+ main_variant = TYPE_MAIN_VARIANT(type);
+ for (variant = main_variant; variant; variant = TYPE_NEXT_VARIANT(variant)) {
+ TYPE_FIELDS(variant) = list;
+ TYPE_ATTRIBUTES(variant) = copy_list(TYPE_ATTRIBUTES(variant));
+ TYPE_ATTRIBUTES(variant) = tree_cons(get_identifier("randomize_performed"), NULL_TREE, TYPE_ATTRIBUTES(variant));
+ TYPE_ATTRIBUTES(variant) = tree_cons(get_identifier("designated_init"), NULL_TREE, TYPE_ATTRIBUTES(variant));
+ if (has_flexarray)
+ TYPE_ATTRIBUTES(type) = tree_cons(get_identifier("has_flexarray"), NULL_TREE, TYPE_ATTRIBUTES(type));
+ }
+
+ /*
+ * force a re-layout of the main variant
+ * the TYPE_SIZE for all variants will be recomputed
+ * by finalize_type_size()
+ */
+ TYPE_SIZE(main_variant) = NULL_TREE;
+ layout_type(main_variant);
+ gcc_assert(TYPE_SIZE(main_variant) != NULL_TREE);
+
+ return 1;
+}
+
+/* from constify plugin */
+static const_tree get_field_type(const_tree field)
+{
+ return strip_array_types(TREE_TYPE(field));
+}
+
+/* from constify plugin */
+static bool is_fptr(const_tree fieldtype)
+{
+ if (TREE_CODE(fieldtype) != POINTER_TYPE)
+ return false;
+
+ return TREE_CODE(TREE_TYPE(fieldtype)) == FUNCTION_TYPE;
+}
+
+/* derived from constify plugin */
+static int is_pure_ops_struct(const_tree node)
+{
+ const_tree field;
+
+ gcc_assert(TREE_CODE(node) == RECORD_TYPE || TREE_CODE(node) == UNION_TYPE);
+
+ /* XXX: Do not apply randomization to all-ftpr structs yet. */
+ return 0;
+
+ for (field = TYPE_FIELDS(node); field; field = TREE_CHAIN(field)) {
+ const_tree fieldtype = get_field_type(field);
+ enum tree_code code = TREE_CODE(fieldtype);
+
+ if (node == fieldtype)
+ continue;
+
+ if (!is_fptr(fieldtype))
+ return 0;
+
+ if (code != RECORD_TYPE && code != UNION_TYPE)
+ continue;
+
+ if (!is_pure_ops_struct(fieldtype))
+ return 0;
+ }
+
+ return 1;
+}
+
+static void randomize_type(tree type)
+{
+ tree variant;
+
+ gcc_assert(TREE_CODE(type) == RECORD_TYPE);
+
+ if (lookup_attribute("randomize_considered", TYPE_ATTRIBUTES(type)))
+ return;
+
+ if (lookup_attribute("randomize_layout", TYPE_ATTRIBUTES(TYPE_MAIN_VARIANT(type))) || is_pure_ops_struct(type))
+ relayout_struct(type);
+
+ for (variant = TYPE_MAIN_VARIANT(type); variant; variant = TYPE_NEXT_VARIANT(variant)) {
+ TYPE_ATTRIBUTES(type) = copy_list(TYPE_ATTRIBUTES(type));
+ TYPE_ATTRIBUTES(type) = tree_cons(get_identifier("randomize_considered"), NULL_TREE, TYPE_ATTRIBUTES(type));
+ }
+#ifdef __DEBUG_PLUGIN
+ fprintf(stderr, "Marking randomize_considered on struct %s\n", ORIG_TYPE_NAME(type));
+#ifdef __DEBUG_VERBOSE
+ debug_tree(type);
+#endif
+#endif
+}
+
+static void update_decl_size(tree decl)
+{
+ tree lastval, lastidx, field, init, type, flexsize;
+ unsigned HOST_WIDE_INT len;
+
+ type = TREE_TYPE(decl);
+
+ if (!lookup_attribute("has_flexarray", TYPE_ATTRIBUTES(type)))
+ return;
+
+ init = DECL_INITIAL(decl);
+ if (init == NULL_TREE || init == error_mark_node)
+ return;
+
+ if (TREE_CODE(init) != CONSTRUCTOR)
+ return;
+
+ len = CONSTRUCTOR_NELTS(init);
+ if (!len)
+ return;
+
+ lastval = CONSTRUCTOR_ELT(init, CONSTRUCTOR_NELTS(init) - 1)->value;
+ lastidx = CONSTRUCTOR_ELT(init, CONSTRUCTOR_NELTS(init) - 1)->index;
+
+ for (field = TYPE_FIELDS(TREE_TYPE(decl)); TREE_CHAIN(field); field = TREE_CHAIN(field))
+ ;
+
+ if (lastidx != field)
+ return;
+
+ if (TREE_CODE(lastval) != STRING_CST) {
+ error("Only string constants are supported as initializers "
+ "for randomized structures with flexible arrays");
+ return;
+ }
+
+ flexsize = bitsize_int(TREE_STRING_LENGTH(lastval) *
+ tree_to_uhwi(TYPE_SIZE(TREE_TYPE(TREE_TYPE(lastval)))));
+
+ DECL_SIZE(decl) = size_binop(PLUS_EXPR, TYPE_SIZE(type), flexsize);
+
+ return;
+}
+
+
+static void randomize_layout_finish_decl(void *event_data, void *data)
+{
+ tree decl = (tree)event_data;
+ tree type;
+
+ if (decl == NULL_TREE || decl == error_mark_node)
+ return;
+
+ type = TREE_TYPE(decl);
+
+ if (TREE_CODE(decl) != VAR_DECL)
+ return;
+
+ if (TREE_CODE(type) != RECORD_TYPE && TREE_CODE(type) != UNION_TYPE)
+ return;
+
+ if (!lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(type)))
+ return;
+
+ DECL_SIZE(decl) = 0;
+ DECL_SIZE_UNIT(decl) = 0;
+ SET_DECL_ALIGN(decl, 0);
+ SET_DECL_MODE (decl, VOIDmode);
+ SET_DECL_RTL(decl, 0);
+ update_decl_size(decl);
+ layout_decl(decl, 0);
+}
+
+static void finish_type(void *event_data, void *data)
+{
+ tree type = (tree)event_data;
+
+ if (type == NULL_TREE || type == error_mark_node)
+ return;
+
+ if (TREE_CODE(type) != RECORD_TYPE)
+ return;
+
+ if (TYPE_FIELDS(type) == NULL_TREE)
+ return;
+
+ if (lookup_attribute("randomize_considered", TYPE_ATTRIBUTES(type)))
+ return;
+
+#ifdef __DEBUG_PLUGIN
+ fprintf(stderr, "Calling randomize_type on %s\n", ORIG_TYPE_NAME(type));
+#endif
+#ifdef __DEBUG_VERBOSE
+ debug_tree(type);
+#endif
+ randomize_type(type);
+
+ return;
+}
+
+static struct attribute_spec randomize_layout_attr = {
+ .name = "randomize_layout",
+ // related to args
+ .min_length = 0,
+ .max_length = 0,
+ .decl_required = false,
+ // need type declaration
+ .type_required = true,
+ .function_type_required = false,
+ .handler = handle_randomize_layout_attr,
+#if BUILDING_GCC_VERSION >= 4007
+ .affects_type_identity = true
+#endif
+};
+
+static struct attribute_spec no_randomize_layout_attr = {
+ .name = "no_randomize_layout",
+ // related to args
+ .min_length = 0,
+ .max_length = 0,
+ .decl_required = false,
+ // need type declaration
+ .type_required = true,
+ .function_type_required = false,
+ .handler = handle_randomize_layout_attr,
+#if BUILDING_GCC_VERSION >= 4007
+ .affects_type_identity = true
+#endif
+};
+
+static struct attribute_spec randomize_considered_attr = {
+ .name = "randomize_considered",
+ // related to args
+ .min_length = 0,
+ .max_length = 0,
+ .decl_required = false,
+ // need type declaration
+ .type_required = true,
+ .function_type_required = false,
+ .handler = handle_randomize_considered_attr,
+#if BUILDING_GCC_VERSION >= 4007
+ .affects_type_identity = false
+#endif
+};
+
+static struct attribute_spec randomize_performed_attr = {
+ .name = "randomize_performed",
+ // related to args
+ .min_length = 0,
+ .max_length = 0,
+ .decl_required = false,
+ // need type declaration
+ .type_required = true,
+ .function_type_required = false,
+ .handler = handle_randomize_performed_attr,
+#if BUILDING_GCC_VERSION >= 4007
+ .affects_type_identity = false
+#endif
+};
+
+static void register_attributes(void *event_data, void *data)
+{
+ register_attribute(&randomize_layout_attr);
+ register_attribute(&no_randomize_layout_attr);
+ register_attribute(&randomize_considered_attr);
+ register_attribute(&randomize_performed_attr);
+}
+
+static void check_bad_casts_in_constructor(tree var, tree init)
+{
+ unsigned HOST_WIDE_INT idx;
+ tree field, val;
+ tree field_type, val_type;
+
+ FOR_EACH_CONSTRUCTOR_ELT(CONSTRUCTOR_ELTS(init), idx, field, val) {
+ if (TREE_CODE(val) == CONSTRUCTOR) {
+ check_bad_casts_in_constructor(var, val);
+ continue;
+ }
+
+ /* pipacs' plugin creates franken-arrays that differ from those produced by
+ normal code which all have valid 'field' trees. work around this */
+ if (field == NULL_TREE)
+ continue;
+ field_type = TREE_TYPE(field);
+ val_type = TREE_TYPE(val);
+
+ if (TREE_CODE(field_type) != POINTER_TYPE || TREE_CODE(val_type) != POINTER_TYPE)
+ continue;
+
+ if (field_type == val_type)
+ continue;
+
+ field_type = TYPE_MAIN_VARIANT(strip_array_types(TYPE_MAIN_VARIANT(TREE_TYPE(field_type))));
+ val_type = TYPE_MAIN_VARIANT(strip_array_types(TYPE_MAIN_VARIANT(TREE_TYPE(val_type))));
+
+ if (field_type == void_type_node)
+ continue;
+ if (field_type == val_type)
+ continue;
+ if (TREE_CODE(val_type) != RECORD_TYPE)
+ continue;
+
+ if (!lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(val_type)))
+ continue;
+ MISMATCH(DECL_SOURCE_LOCATION(var), "constructor\n", TYPE_MAIN_VARIANT(field_type), TYPE_MAIN_VARIANT(val_type));
+ }
+}
+
+/* derived from the constify plugin */
+static void check_global_variables(void *event_data, void *data)
+{
+ struct varpool_node *node;
+ tree init;
+
+ FOR_EACH_VARIABLE(node) {
+ tree var = NODE_DECL(node);
+ init = DECL_INITIAL(var);
+ if (init == NULL_TREE)
+ continue;
+
+ if (TREE_CODE(init) != CONSTRUCTOR)
+ continue;
+
+ check_bad_casts_in_constructor(var, init);
+ }
+}
+
+static bool dominated_by_is_err(const_tree rhs, basic_block bb)
+{
+ basic_block dom;
+ gimple dom_stmt;
+ gimple call_stmt;
+ const_tree dom_lhs;
+ const_tree poss_is_err_cond;
+ const_tree poss_is_err_func;
+ const_tree is_err_arg;
+
+ dom = get_immediate_dominator(CDI_DOMINATORS, bb);
+ if (!dom)
+ return false;
+
+ dom_stmt = last_stmt(dom);
+ if (!dom_stmt)
+ return false;
+
+ if (gimple_code(dom_stmt) != GIMPLE_COND)
+ return false;
+
+ if (gimple_cond_code(dom_stmt) != NE_EXPR)
+ return false;
+
+ if (!integer_zerop(gimple_cond_rhs(dom_stmt)))
+ return false;
+
+ poss_is_err_cond = gimple_cond_lhs(dom_stmt);
+
+ if (TREE_CODE(poss_is_err_cond) != SSA_NAME)
+ return false;
+
+ call_stmt = SSA_NAME_DEF_STMT(poss_is_err_cond);
+
+ if (gimple_code(call_stmt) != GIMPLE_CALL)
+ return false;
+
+ dom_lhs = gimple_get_lhs(call_stmt);
+ poss_is_err_func = gimple_call_fndecl(call_stmt);
+ if (!poss_is_err_func)
+ return false;
+ if (dom_lhs != poss_is_err_cond)
+ return false;
+ if (strcmp(DECL_NAME_POINTER(poss_is_err_func), "IS_ERR"))
+ return false;
+
+ is_err_arg = gimple_call_arg(call_stmt, 0);
+ if (!is_err_arg)
+ return false;
+
+ if (is_err_arg != rhs)
+ return false;
+
+ return true;
+}
+
+static void handle_local_var_initializers(void)
+{
+ tree var;
+ unsigned int i;
+
+ FOR_EACH_LOCAL_DECL(cfun, i, var) {
+ tree init = DECL_INITIAL(var);
+ if (!init)
+ continue;
+ if (TREE_CODE(init) != CONSTRUCTOR)
+ continue;
+ check_bad_casts_in_constructor(var, init);
+ }
+}
+
+static bool type_name_eq(gimple stmt, const_tree type_tree, const char *wanted_name)
+{
+ const char *type_name;
+
+ if (type_tree == NULL_TREE)
+ return false;
+
+ switch (TREE_CODE(type_tree)) {
+ case RECORD_TYPE:
+ type_name = TYPE_NAME_POINTER(type_tree);
+ break;
+ case INTEGER_TYPE:
+ if (TYPE_PRECISION(type_tree) == CHAR_TYPE_SIZE)
+ type_name = "char";
+ else {
+ INFORM(gimple_location(stmt), "found non-char INTEGER_TYPE cast comparison: %qT\n", type_tree);
+ debug_tree(type_tree);
+ return false;
+ }
+ break;
+ case POINTER_TYPE:
+ if (TREE_CODE(TREE_TYPE(type_tree)) == VOID_TYPE) {
+ type_name = "void *";
+ break;
+ } else {
+ INFORM(gimple_location(stmt), "found non-void POINTER_TYPE cast comparison %qT\n", type_tree);
+ debug_tree(type_tree);
+ return false;
+ }
+ default:
+ INFORM(gimple_location(stmt), "unhandled cast comparison: %qT\n", type_tree);
+ debug_tree(type_tree);
+ return false;
+ }
+
+ return strcmp(type_name, wanted_name) == 0;
+}
+
+static bool whitelisted_cast(gimple stmt, const_tree lhs_tree, const_tree rhs_tree)
+{
+ const struct whitelist_entry *entry;
+ expanded_location xloc = expand_location(gimple_location(stmt));
+
+ for (entry = whitelist; entry->pathname; entry++) {
+ if (!strstr(xloc.file, entry->pathname))
+ continue;
+
+ if (type_name_eq(stmt, lhs_tree, entry->lhs) && type_name_eq(stmt, rhs_tree, entry->rhs))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * iterate over all statements to find "bad" casts:
+ * those where the address of the start of a structure is cast
+ * to a pointer of a structure of a different type, or a
+ * structure pointer type is cast to a different structure pointer type
+ */
+static unsigned int find_bad_casts_execute(void)
+{
+ basic_block bb;
+
+ handle_local_var_initializers();
+
+ 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;
+ const_tree lhs;
+ const_tree lhs_type;
+ const_tree rhs1;
+ const_tree rhs_type;
+ const_tree ptr_lhs_type;
+ const_tree ptr_rhs_type;
+ const_tree op0;
+ const_tree op0_type;
+ enum tree_code rhs_code;
+
+ stmt = gsi_stmt(gsi);
+
+#ifdef __DEBUG_PLUGIN
+#ifdef __DEBUG_VERBOSE
+ debug_gimple_stmt(stmt);
+ debug_tree(gimple_get_lhs(stmt));
+#endif
+#endif
+
+ if (gimple_code(stmt) != GIMPLE_ASSIGN)
+ continue;
+
+#ifdef __DEBUG_PLUGIN
+#ifdef __DEBUG_VERBOSE
+ debug_tree(gimple_assign_rhs1(stmt));
+#endif
+#endif
+
+
+ rhs_code = gimple_assign_rhs_code(stmt);
+
+ if (rhs_code != ADDR_EXPR && rhs_code != SSA_NAME)
+ continue;
+
+ lhs = gimple_get_lhs(stmt);
+ lhs_type = TREE_TYPE(lhs);
+ rhs1 = gimple_assign_rhs1(stmt);
+ rhs_type = TREE_TYPE(rhs1);
+
+ if (TREE_CODE(rhs_type) != POINTER_TYPE ||
+ TREE_CODE(lhs_type) != POINTER_TYPE)
+ continue;
+
+ ptr_lhs_type = TYPE_MAIN_VARIANT(strip_array_types(TYPE_MAIN_VARIANT(TREE_TYPE(lhs_type))));
+ ptr_rhs_type = TYPE_MAIN_VARIANT(strip_array_types(TYPE_MAIN_VARIANT(TREE_TYPE(rhs_type))));
+
+ if (ptr_rhs_type == void_type_node)
+ continue;
+
+ if (ptr_lhs_type == void_type_node)
+ continue;
+
+ if (dominated_by_is_err(rhs1, bb))
+ continue;
+
+ if (TREE_CODE(ptr_rhs_type) != RECORD_TYPE) {
+#ifndef __DEBUG_PLUGIN
+ if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(ptr_lhs_type)))
+#endif
+ {
+ if (!whitelisted_cast(stmt, ptr_lhs_type, ptr_rhs_type))
+ MISMATCH(gimple_location(stmt), "rhs", ptr_lhs_type, ptr_rhs_type);
+ }
+ continue;
+ }
+
+ if (rhs_code == SSA_NAME && ptr_lhs_type == ptr_rhs_type)
+ continue;
+
+ if (rhs_code == ADDR_EXPR) {
+ op0 = TREE_OPERAND(rhs1, 0);
+
+ if (op0 == NULL_TREE)
+ continue;
+
+ if (TREE_CODE(op0) != VAR_DECL)
+ continue;
+
+ op0_type = TYPE_MAIN_VARIANT(strip_array_types(TYPE_MAIN_VARIANT(TREE_TYPE(op0))));
+ if (op0_type == ptr_lhs_type)
+ continue;
+
+#ifndef __DEBUG_PLUGIN
+ if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(op0_type)))
+#endif
+ {
+ if (!whitelisted_cast(stmt, ptr_lhs_type, op0_type))
+ MISMATCH(gimple_location(stmt), "op0", ptr_lhs_type, op0_type);
+ }
+ } else {
+ const_tree ssa_name_var = SSA_NAME_VAR(rhs1);
+ /* skip bogus type casts introduced by container_of */
+ if (ssa_name_var != NULL_TREE && DECL_NAME(ssa_name_var) &&
+ !strcmp((const char *)DECL_NAME_POINTER(ssa_name_var), "__mptr"))
+ continue;
+#ifndef __DEBUG_PLUGIN
+ if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(ptr_rhs_type)))
+#endif
+ {
+ if (!whitelisted_cast(stmt, ptr_lhs_type, ptr_rhs_type))
+ MISMATCH(gimple_location(stmt), "ssa", ptr_lhs_type, ptr_rhs_type);
+ }
+ }
+
+ }
+ }
+ return 0;
+}
+
+#define PASS_NAME find_bad_casts
+#define NO_GATE
+#define TODO_FLAGS_FINISH TODO_dump_func
+#include "gcc-generate-gimple-pass.h"
+
+__visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
+{
+ int i;
+ const char * const plugin_name = plugin_info->base_name;
+ const int argc = plugin_info->argc;
+ const struct plugin_argument * const argv = plugin_info->argv;
+ bool enable = true;
+ int obtained_seed = 0;
+ struct register_pass_info find_bad_casts_pass_info;
+
+ find_bad_casts_pass_info.pass = make_find_bad_casts_pass();
+ find_bad_casts_pass_info.reference_pass_name = "ssa";
+ find_bad_casts_pass_info.ref_pass_instance_number = 1;
+ find_bad_casts_pass_info.pos_op = PASS_POS_INSERT_AFTER;
+
+ if (!plugin_default_version_check(version, &gcc_version)) {
+ error(G_("incompatible gcc/plugin versions"));
+ return 1;
+ }
+
+ if (strncmp(lang_hooks.name, "GNU C", 5) && !strncmp(lang_hooks.name, "GNU C+", 6)) {
+ inform(UNKNOWN_LOCATION, G_("%s supports C only, not %s"), plugin_name, lang_hooks.name);
+ enable = false;
+ }
+
+ for (i = 0; i < argc; ++i) {
+ if (!strcmp(argv[i].key, "disable")) {
+ enable = false;
+ continue;
+ }
+ if (!strcmp(argv[i].key, "performance-mode")) {
+ performance_mode = 1;
+ continue;
+ }
+ error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
+ }
+
+ if (strlen(randstruct_seed) != 64) {
+ error(G_("invalid seed value supplied for %s plugin"), plugin_name);
+ return 1;
+ }
+ obtained_seed = sscanf(randstruct_seed, "%016llx%016llx%016llx%016llx",
+ &shuffle_seed[0], &shuffle_seed[1], &shuffle_seed[2], &shuffle_seed[3]);
+ if (obtained_seed != 4) {
+ error(G_("Invalid seed supplied for %s plugin"), plugin_name);
+ return 1;
+ }
+
+ register_callback(plugin_name, PLUGIN_INFO, NULL, &randomize_layout_plugin_info);
+ if (enable) {
+ register_callback(plugin_name, PLUGIN_ALL_IPA_PASSES_START, check_global_variables, NULL);
+ register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &find_bad_casts_pass_info);
+ register_callback(plugin_name, PLUGIN_FINISH_TYPE, finish_type, NULL);
+ register_callback(plugin_name, PLUGIN_FINISH_DECL, randomize_layout_finish_decl, NULL);
+ }
+ register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes, NULL);
+
+ return 0;
+}
diff --git a/security/keys/internal.h b/security/keys/internal.h
index c0f8682eba69..6494954e9980 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -197,7 +197,7 @@ struct request_key_auth {
void *callout_info;
size_t callout_len;
pid_t pid;
-};
+} __randomize_layout;
extern struct key_type key_type_request_key_auth;
extern struct key *request_key_auth_new(struct key *target,