This adds a new gcc plugin named "latent_entropy". It is designed to
extract as much possible uncertainty from a running system at boot time as possible, hoping to capitalize on any possible variation in CPU operation (due to runtime data differences, hardware differences, SMP ordering, thermal timing variation, cache behavior, etc). At the very least, this plugin is a much more comprehensive example for how to manipulate kernel code using the gcc plugin internals. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 Comment: Kees Cook <kees@outflux.net> iQIcBAABCgAGBQJX/BAFAAoJEIly9N/cbcAmzW8QALFbCs7EFFkML+M/M/9d8zEk 1QbUs/z8covJTTT1PjSdw7JUrAMulI3S00owpcQVd/PcWjRPU80QwfsXBgIB0tvC Kub2qxn6Oaf+kTB646zwjFgjdCecw/USJP+90nfcu2+LCnE8ReclKd1aUee+Bnhm iDEUyH2ONIoWq6ta2Z9sA7+E4y2ZgOlmW0iga3Mnf+OcPtLE70fWPoe5E4g9DpYk B+kiPDrD9ql5zsHaEnKG1ldjiAZ1L6Grk8rGgLEXmbOWtTOFmnUhR+raK5NA/RCw MXNuyPay5aYPpqDHFm+OuaWQAiPWfPNWM3Ett4k0d9ZWLixTcD1z68AciExwk7aW SEA8b1Jwbg05ZNYM7NJB6t6suKC4dGPxWzKFOhmBicsh2Ni5f+Az0BQL6q8/V8/4 8UEqDLuFlPJBB50A3z5ngCVeYJKZe8Bg/Swb4zXl6mIzZ9darLzXDEV6ystfPXxJ e1AdBb41WC+O2SAI4l64yyeswkGo3Iw2oMbXG5jmFl6wY/xGp7dWxw7gfnhC6oOh afOT54p2OUDfSAbJaO0IHliWoIdmE5ZYdVYVU9Ek+uWyaIwcXhNmqRg+Uqmo32jf cP5J9x2kF3RdOcbSHXmFp++fU+wkhBtEcjkNpvkjpi4xyA47IWS7lrVBBebrCq9R pa/A7CNQwibIV6YD8+/p =1dUK -----END PGP SIGNATURE----- Merge tag 'gcc-plugins-v4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux Pull gcc plugins update from Kees Cook: "This adds a new gcc plugin named "latent_entropy". It is designed to extract as much possible uncertainty from a running system at boot time as possible, hoping to capitalize on any possible variation in CPU operation (due to runtime data differences, hardware differences, SMP ordering, thermal timing variation, cache behavior, etc). At the very least, this plugin is a much more comprehensive example for how to manipulate kernel code using the gcc plugin internals" * tag 'gcc-plugins-v4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: latent_entropy: Mark functions with __latent_entropy gcc-plugins: Add latent_entropy plugin
This commit is contained in:
commit
9ffc66941d
18
arch/Kconfig
18
arch/Kconfig
|
@ -383,6 +383,24 @@ 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_LATENT_ENTROPY
|
||||
bool "Generate some entropy during boot and runtime"
|
||||
depends on GCC_PLUGINS
|
||||
help
|
||||
By saying Y here the kernel will instrument some kernel code to
|
||||
extract some entropy from both original and artificially created
|
||||
program state. This will help especially embedded systems where
|
||||
there is little 'natural' source of entropy normally. The cost
|
||||
is some slowdown of the boot process (about 0.5%) and fork and
|
||||
irq processing.
|
||||
|
||||
Note that entropy extracted this way is not cryptographically
|
||||
secure!
|
||||
|
||||
This plugin was ported from grsecurity/PaX. More information at:
|
||||
* https://grsecurity.net/
|
||||
* https://pax.grsecurity.net/
|
||||
|
||||
config HAVE_CC_STACKPROTECTOR
|
||||
bool
|
||||
help
|
||||
|
|
|
@ -14,6 +14,11 @@ CFLAGS_prom_init.o += -fPIC
|
|||
CFLAGS_btext.o += -fPIC
|
||||
endif
|
||||
|
||||
CFLAGS_cputable.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
|
||||
CFLAGS_init.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
|
||||
CFLAGS_btext.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
|
||||
CFLAGS_prom.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
|
||||
|
||||
ifdef CONFIG_FUNCTION_TRACER
|
||||
# Do not trace early boot code
|
||||
CFLAGS_REMOVE_cputable.o = -mno-sched-epilog $(CC_FLAGS_FTRACE)
|
||||
|
|
|
@ -18,7 +18,7 @@ static DEFINE_PER_CPU(struct list_head, blk_cpu_done);
|
|||
* Softirq action handler - move entries to local list and loop over them
|
||||
* while passing them to the queue registered handler.
|
||||
*/
|
||||
static void blk_done_softirq(struct softirq_action *h)
|
||||
static __latent_entropy void blk_done_softirq(struct softirq_action *h)
|
||||
{
|
||||
struct list_head *cpu_list, local_list;
|
||||
|
||||
|
|
|
@ -479,8 +479,8 @@ static ssize_t _extract_entropy(struct entropy_store *r, void *buf,
|
|||
|
||||
static void crng_reseed(struct crng_state *crng, struct entropy_store *r);
|
||||
static void push_to_pool(struct work_struct *work);
|
||||
static __u32 input_pool_data[INPUT_POOL_WORDS];
|
||||
static __u32 blocking_pool_data[OUTPUT_POOL_WORDS];
|
||||
static __u32 input_pool_data[INPUT_POOL_WORDS] __latent_entropy;
|
||||
static __u32 blocking_pool_data[OUTPUT_POOL_WORDS] __latent_entropy;
|
||||
|
||||
static struct entropy_store input_pool = {
|
||||
.poolinfo = &poolinfo_table[0],
|
||||
|
|
|
@ -2824,6 +2824,7 @@ static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns)
|
|||
return new_ns;
|
||||
}
|
||||
|
||||
__latent_entropy
|
||||
struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
|
||||
struct user_namespace *user_ns, struct fs_struct *new_fs)
|
||||
{
|
||||
|
|
|
@ -188,6 +188,13 @@
|
|||
#endif /* GCC_VERSION >= 40300 */
|
||||
|
||||
#if GCC_VERSION >= 40500
|
||||
|
||||
#ifndef __CHECKER__
|
||||
#ifdef LATENT_ENTROPY_PLUGIN
|
||||
#define __latent_entropy __attribute__((latent_entropy))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Mark a position in code as unreachable. This can be used to
|
||||
* suppress control flow warnings after asm blocks that transfer
|
||||
|
|
|
@ -429,6 +429,10 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
|
|||
# define __attribute_const__ /* unimplemented */
|
||||
#endif
|
||||
|
||||
#ifndef __latent_entropy
|
||||
# define __latent_entropy
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Tell gcc if a function is cold. The compiler will assume any path
|
||||
* directly leading to the call is unlikely.
|
||||
|
|
|
@ -105,7 +105,7 @@ struct files_struct *get_files_struct(struct task_struct *);
|
|||
void put_files_struct(struct files_struct *fs);
|
||||
void reset_files_struct(struct files_struct *);
|
||||
int unshare_files(struct files_struct **);
|
||||
struct files_struct *dup_fd(struct files_struct *, int *);
|
||||
struct files_struct *dup_fd(struct files_struct *, int *) __latent_entropy;
|
||||
void do_close_on_exec(struct files_struct *);
|
||||
int iterate_fd(struct files_struct *, unsigned,
|
||||
int (*)(const void *, struct file *, unsigned),
|
||||
|
|
|
@ -437,7 +437,7 @@ extern void disk_flush_events(struct gendisk *disk, unsigned int mask);
|
|||
extern unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask);
|
||||
|
||||
/* drivers/char/random.c */
|
||||
extern void add_disk_randomness(struct gendisk *disk);
|
||||
extern void add_disk_randomness(struct gendisk *disk) __latent_entropy;
|
||||
extern void rand_initialize_disk(struct gendisk *disk);
|
||||
|
||||
static inline sector_t get_start_sect(struct block_device *bdev)
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
/* These are for everybody (although not all archs will actually
|
||||
discard it in modules) */
|
||||
#define __init __section(.init.text) __cold notrace
|
||||
#define __init __section(.init.text) __cold notrace __latent_entropy
|
||||
#define __initdata __section(.init.data)
|
||||
#define __initconst __section(.init.rodata)
|
||||
#define __exitdata __section(.exit.data)
|
||||
|
@ -75,7 +75,8 @@
|
|||
#define __exit __section(.exit.text) __exitused __cold notrace
|
||||
|
||||
/* Used for MEMORY_HOTPLUG */
|
||||
#define __meminit __section(.meminit.text) __cold notrace
|
||||
#define __meminit __section(.meminit.text) __cold notrace \
|
||||
__latent_entropy
|
||||
#define __meminitdata __section(.meminit.data)
|
||||
#define __meminitconst __section(.meminit.rodata)
|
||||
#define __memexit __section(.memexit.text) __exitused __cold notrace
|
||||
|
|
|
@ -18,9 +18,20 @@ struct random_ready_callback {
|
|||
};
|
||||
|
||||
extern void add_device_randomness(const void *, unsigned int);
|
||||
|
||||
#if defined(CONFIG_GCC_PLUGIN_LATENT_ENTROPY) && !defined(__CHECKER__)
|
||||
static inline void add_latent_entropy(void)
|
||||
{
|
||||
add_device_randomness((const void *)&latent_entropy,
|
||||
sizeof(latent_entropy));
|
||||
}
|
||||
#else
|
||||
static inline void add_latent_entropy(void) {}
|
||||
#endif
|
||||
|
||||
extern void add_input_randomness(unsigned int type, unsigned int code,
|
||||
unsigned int value);
|
||||
extern void add_interrupt_randomness(int irq, int irq_flags);
|
||||
unsigned int value) __latent_entropy;
|
||||
extern void add_interrupt_randomness(int irq, int irq_flags) __latent_entropy;
|
||||
|
||||
extern void get_random_bytes(void *buf, int nbytes);
|
||||
extern int add_random_ready_callback(struct random_ready_callback *rdy);
|
||||
|
|
|
@ -789,6 +789,7 @@ int __init_or_module do_one_initcall(initcall_t fn)
|
|||
}
|
||||
WARN(msgbuf[0], "initcall %pF returned with %s\n", fn, msgbuf);
|
||||
|
||||
add_latent_entropy();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -547,7 +547,8 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_MMU
|
||||
static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
|
||||
static __latent_entropy int dup_mmap(struct mm_struct *mm,
|
||||
struct mm_struct *oldmm)
|
||||
{
|
||||
struct vm_area_struct *mpnt, *tmp, *prev, **pprev;
|
||||
struct rb_node **rb_link, *rb_parent;
|
||||
|
@ -1441,7 +1442,8 @@ init_task_pid(struct task_struct *task, enum pid_type type, struct pid *pid)
|
|||
* parts of the process environment (as per the clone
|
||||
* flags). The actual kick-off is left to the caller.
|
||||
*/
|
||||
static struct task_struct *copy_process(unsigned long clone_flags,
|
||||
static __latent_entropy struct task_struct *copy_process(
|
||||
unsigned long clone_flags,
|
||||
unsigned long stack_start,
|
||||
unsigned long stack_size,
|
||||
int __user *child_tidptr,
|
||||
|
@ -1926,6 +1928,7 @@ long _do_fork(unsigned long clone_flags,
|
|||
|
||||
p = copy_process(clone_flags, stack_start, stack_size,
|
||||
child_tidptr, NULL, trace, tls, NUMA_NO_NODE);
|
||||
add_latent_entropy();
|
||||
/*
|
||||
* Do this prior waking up the new thread - the thread pointer
|
||||
* might get invalid after that point, if the thread exits quickly.
|
||||
|
|
|
@ -170,7 +170,7 @@ static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp)
|
|||
false));
|
||||
}
|
||||
|
||||
static void rcu_process_callbacks(struct softirq_action *unused)
|
||||
static __latent_entropy void rcu_process_callbacks(struct softirq_action *unused)
|
||||
{
|
||||
__rcu_process_callbacks(&rcu_sched_ctrlblk);
|
||||
__rcu_process_callbacks(&rcu_bh_ctrlblk);
|
||||
|
|
|
@ -3013,7 +3013,7 @@ __rcu_process_callbacks(struct rcu_state *rsp)
|
|||
/*
|
||||
* Do RCU core processing for the current CPU.
|
||||
*/
|
||||
static void rcu_process_callbacks(struct softirq_action *unused)
|
||||
static __latent_entropy void rcu_process_callbacks(struct softirq_action *unused)
|
||||
{
|
||||
struct rcu_state *rsp;
|
||||
|
||||
|
|
|
@ -8522,7 +8522,7 @@ static void nohz_idle_balance(struct rq *this_rq, enum cpu_idle_type idle) { }
|
|||
* run_rebalance_domains is triggered when needed from the scheduler tick.
|
||||
* Also triggered for nohz idle balancing (with nohz_balancing_kick set).
|
||||
*/
|
||||
static void run_rebalance_domains(struct softirq_action *h)
|
||||
static __latent_entropy void run_rebalance_domains(struct softirq_action *h)
|
||||
{
|
||||
struct rq *this_rq = this_rq();
|
||||
enum cpu_idle_type idle = this_rq->idle_balance ?
|
||||
|
|
|
@ -496,7 +496,7 @@ void __tasklet_hi_schedule_first(struct tasklet_struct *t)
|
|||
}
|
||||
EXPORT_SYMBOL(__tasklet_hi_schedule_first);
|
||||
|
||||
static void tasklet_action(struct softirq_action *a)
|
||||
static __latent_entropy void tasklet_action(struct softirq_action *a)
|
||||
{
|
||||
struct tasklet_struct *list;
|
||||
|
||||
|
@ -532,7 +532,7 @@ static void tasklet_action(struct softirq_action *a)
|
|||
}
|
||||
}
|
||||
|
||||
static void tasklet_hi_action(struct softirq_action *a)
|
||||
static __latent_entropy void tasklet_hi_action(struct softirq_action *a)
|
||||
{
|
||||
struct tasklet_struct *list;
|
||||
|
||||
|
|
|
@ -1633,7 +1633,7 @@ static inline void __run_timers(struct timer_base *base)
|
|||
/*
|
||||
* This function runs timers and the timer-tq in bottom half context.
|
||||
*/
|
||||
static void run_timer_softirq(struct softirq_action *h)
|
||||
static __latent_entropy void run_timer_softirq(struct softirq_action *h)
|
||||
{
|
||||
struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]);
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ void irq_poll_complete(struct irq_poll *iop)
|
|||
}
|
||||
EXPORT_SYMBOL(irq_poll_complete);
|
||||
|
||||
static void irq_poll_softirq(struct softirq_action *h)
|
||||
static void __latent_entropy irq_poll_softirq(struct softirq_action *h)
|
||||
{
|
||||
struct list_head *list = this_cpu_ptr(&blk_cpu_iopoll);
|
||||
int rearm = 0, budget = irq_poll_budget;
|
||||
|
|
|
@ -47,7 +47,7 @@ static inline void prandom_state_selftest(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
static DEFINE_PER_CPU(struct rnd_state, net_rand_state);
|
||||
static DEFINE_PER_CPU(struct rnd_state, net_rand_state) __latent_entropy;
|
||||
|
||||
/**
|
||||
* prandom_u32_state - seeded pseudo-random number generator.
|
||||
|
|
|
@ -91,6 +91,11 @@ EXPORT_PER_CPU_SYMBOL(_numa_mem_);
|
|||
int _node_numa_mem_[MAX_NUMNODES];
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_GCC_PLUGIN_LATENT_ENTROPY
|
||||
volatile u64 latent_entropy __latent_entropy;
|
||||
EXPORT_SYMBOL(latent_entropy);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Array of node states.
|
||||
*/
|
||||
|
|
|
@ -3845,7 +3845,7 @@ int netif_rx_ni(struct sk_buff *skb)
|
|||
}
|
||||
EXPORT_SYMBOL(netif_rx_ni);
|
||||
|
||||
static void net_tx_action(struct softirq_action *h)
|
||||
static __latent_entropy void net_tx_action(struct softirq_action *h)
|
||||
{
|
||||
struct softnet_data *sd = this_cpu_ptr(&softnet_data);
|
||||
|
||||
|
@ -5198,7 +5198,7 @@ static int napi_poll(struct napi_struct *n, struct list_head *repoll)
|
|||
return work;
|
||||
}
|
||||
|
||||
static void net_rx_action(struct softirq_action *h)
|
||||
static __latent_entropy void net_rx_action(struct softirq_action *h)
|
||||
{
|
||||
struct softnet_data *sd = this_cpu_ptr(&softnet_data);
|
||||
unsigned long time_limit = jiffies + 2;
|
||||
|
|
|
@ -6,6 +6,12 @@ ifdef CONFIG_GCC_PLUGINS
|
|||
|
||||
gcc-plugin-$(CONFIG_GCC_PLUGIN_CYC_COMPLEXITY) += cyc_complexity_plugin.so
|
||||
|
||||
gcc-plugin-$(CONFIG_GCC_PLUGIN_LATENT_ENTROPY) += latent_entropy_plugin.so
|
||||
gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_LATENT_ENTROPY) += -DLATENT_ENTROPY_PLUGIN
|
||||
ifdef CONFIG_PAX_LATENT_ENTROPY
|
||||
DISABLE_LATENT_ENTROPY_PLUGIN += -fplugin-arg-latent_entropy_plugin-disable
|
||||
endif
|
||||
|
||||
ifdef CONFIG_GCC_PLUGIN_SANCOV
|
||||
ifeq ($(CFLAGS_KCOV),)
|
||||
# It is needed because of the gcc-plugin.sh and gcc version checks.
|
||||
|
@ -21,7 +27,8 @@ ifdef CONFIG_GCC_PLUGINS
|
|||
|
||||
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 SANCOV_PLUGIN
|
||||
export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
|
||||
export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN
|
||||
|
||||
ifneq ($(PLUGINCC),)
|
||||
# SANCOV_PLUGIN can be only in CFLAGS_KCOV because avoid duplication.
|
||||
|
|
640
scripts/gcc-plugins/latent_entropy_plugin.c
Normal file
640
scripts/gcc-plugins/latent_entropy_plugin.c
Normal file
|
@ -0,0 +1,640 @@
|
|||
/*
|
||||
* Copyright 2012-2016 by the PaX Team <pageexec@freemail.hu>
|
||||
* Copyright 2016 by Emese Revfy <re.emese@gmail.com>
|
||||
* 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
|
||||
*
|
||||
* This gcc plugin helps generate a little bit of entropy from program state,
|
||||
* used throughout the uptime of the kernel. Here is an instrumentation example:
|
||||
*
|
||||
* before:
|
||||
* void __latent_entropy test(int argc, char *argv[])
|
||||
* {
|
||||
* if (argc <= 1)
|
||||
* printf("%s: no command arguments :(\n", *argv);
|
||||
* else
|
||||
* printf("%s: %d command arguments!\n", *argv, args - 1);
|
||||
* }
|
||||
*
|
||||
* after:
|
||||
* void __latent_entropy test(int argc, char *argv[])
|
||||
* {
|
||||
* // latent_entropy_execute() 1.
|
||||
* unsigned long local_entropy;
|
||||
* // init_local_entropy() 1.
|
||||
* void *local_entropy_frameaddr;
|
||||
* // init_local_entropy() 3.
|
||||
* unsigned long tmp_latent_entropy;
|
||||
*
|
||||
* // init_local_entropy() 2.
|
||||
* local_entropy_frameaddr = __builtin_frame_address(0);
|
||||
* local_entropy = (unsigned long) local_entropy_frameaddr;
|
||||
*
|
||||
* // init_local_entropy() 4.
|
||||
* tmp_latent_entropy = latent_entropy;
|
||||
* // init_local_entropy() 5.
|
||||
* local_entropy ^= tmp_latent_entropy;
|
||||
*
|
||||
* // latent_entropy_execute() 3.
|
||||
* if (argc <= 1) {
|
||||
* // perturb_local_entropy()
|
||||
* local_entropy += 4623067384293424948;
|
||||
* printf("%s: no command arguments :(\n", *argv);
|
||||
* // perturb_local_entropy()
|
||||
* } else {
|
||||
* local_entropy ^= 3896280633962944730;
|
||||
* printf("%s: %d command arguments!\n", *argv, args - 1);
|
||||
* }
|
||||
*
|
||||
* // latent_entropy_execute() 4.
|
||||
* tmp_latent_entropy = rol(tmp_latent_entropy, local_entropy);
|
||||
* latent_entropy = tmp_latent_entropy;
|
||||
* }
|
||||
*
|
||||
* TODO:
|
||||
* - add ipa pass to identify not explicitly marked candidate functions
|
||||
* - mix in more program state (function arguments/return values,
|
||||
* loop variables, etc)
|
||||
* - more instrumentation control via attribute parameters
|
||||
*
|
||||
* BUGS:
|
||||
* - none known
|
||||
*
|
||||
* Options:
|
||||
* -fplugin-arg-latent_entropy_plugin-disable
|
||||
*
|
||||
* Attribute: __attribute__((latent_entropy))
|
||||
* The latent_entropy gcc attribute can be only on functions and variables.
|
||||
* If it is on a function then the plugin will instrument it. If the attribute
|
||||
* is on a variable then the plugin will initialize it with a random value.
|
||||
* The variable must be an integer, an integer array type or a structure
|
||||
* with integer fields.
|
||||
*/
|
||||
|
||||
#include "gcc-common.h"
|
||||
|
||||
int plugin_is_GPL_compatible;
|
||||
|
||||
static GTY(()) tree latent_entropy_decl;
|
||||
|
||||
static struct plugin_info latent_entropy_plugin_info = {
|
||||
.version = "201606141920vanilla",
|
||||
.help = "disable\tturn off latent entropy instrumentation\n",
|
||||
};
|
||||
|
||||
static unsigned HOST_WIDE_INT seed;
|
||||
/*
|
||||
* get_random_seed() (this is a GCC function) generates the seed.
|
||||
* This is a simple random generator without any cryptographic security because
|
||||
* the entropy doesn't come from here.
|
||||
*/
|
||||
static unsigned HOST_WIDE_INT get_random_const(void)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned HOST_WIDE_INT ret = 0;
|
||||
|
||||
for (i = 0; i < 8 * sizeof(ret); i++) {
|
||||
ret = (ret << 1) | (seed & 1);
|
||||
seed >>= 1;
|
||||
if (ret & 1)
|
||||
seed ^= 0xD800000000000000ULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static tree tree_get_random_const(tree type)
|
||||
{
|
||||
unsigned long long mask;
|
||||
|
||||
mask = 1ULL << (TREE_INT_CST_LOW(TYPE_SIZE(type)) - 1);
|
||||
mask = 2 * (mask - 1) + 1;
|
||||
|
||||
if (TYPE_UNSIGNED(type))
|
||||
return build_int_cstu(type, mask & get_random_const());
|
||||
return build_int_cst(type, mask & get_random_const());
|
||||
}
|
||||
|
||||
static tree handle_latent_entropy_attribute(tree *node, tree name,
|
||||
tree args __unused,
|
||||
int flags __unused,
|
||||
bool *no_add_attrs)
|
||||
{
|
||||
tree type;
|
||||
#if BUILDING_GCC_VERSION <= 4007
|
||||
VEC(constructor_elt, gc) *vals;
|
||||
#else
|
||||
vec<constructor_elt, va_gc> *vals;
|
||||
#endif
|
||||
|
||||
switch (TREE_CODE(*node)) {
|
||||
default:
|
||||
*no_add_attrs = true;
|
||||
error("%qE attribute only applies to functions and variables",
|
||||
name);
|
||||
break;
|
||||
|
||||
case VAR_DECL:
|
||||
if (DECL_INITIAL(*node)) {
|
||||
*no_add_attrs = true;
|
||||
error("variable %qD with %qE attribute must not be initialized",
|
||||
*node, name);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!TREE_STATIC(*node)) {
|
||||
*no_add_attrs = true;
|
||||
error("variable %qD with %qE attribute must not be local",
|
||||
*node, name);
|
||||
break;
|
||||
}
|
||||
|
||||
type = TREE_TYPE(*node);
|
||||
switch (TREE_CODE(type)) {
|
||||
default:
|
||||
*no_add_attrs = true;
|
||||
error("variable %qD with %qE attribute must be an integer or a fixed length integer array type or a fixed sized structure with integer fields",
|
||||
*node, name);
|
||||
break;
|
||||
|
||||
case RECORD_TYPE: {
|
||||
tree fld, lst = TYPE_FIELDS(type);
|
||||
unsigned int nelt = 0;
|
||||
|
||||
for (fld = lst; fld; nelt++, fld = TREE_CHAIN(fld)) {
|
||||
tree fieldtype;
|
||||
|
||||
fieldtype = TREE_TYPE(fld);
|
||||
if (TREE_CODE(fieldtype) == INTEGER_TYPE)
|
||||
continue;
|
||||
|
||||
*no_add_attrs = true;
|
||||
error("structure variable %qD with %qE attribute has a non-integer field %qE",
|
||||
*node, name, fld);
|
||||
break;
|
||||
}
|
||||
|
||||
if (fld)
|
||||
break;
|
||||
|
||||
#if BUILDING_GCC_VERSION <= 4007
|
||||
vals = VEC_alloc(constructor_elt, gc, nelt);
|
||||
#else
|
||||
vec_alloc(vals, nelt);
|
||||
#endif
|
||||
|
||||
for (fld = lst; fld; fld = TREE_CHAIN(fld)) {
|
||||
tree random_const, fld_t = TREE_TYPE(fld);
|
||||
|
||||
random_const = tree_get_random_const(fld_t);
|
||||
CONSTRUCTOR_APPEND_ELT(vals, fld, random_const);
|
||||
}
|
||||
|
||||
/* Initialize the fields with random constants */
|
||||
DECL_INITIAL(*node) = build_constructor(type, vals);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Initialize the variable with a random constant */
|
||||
case INTEGER_TYPE:
|
||||
DECL_INITIAL(*node) = tree_get_random_const(type);
|
||||
break;
|
||||
|
||||
case ARRAY_TYPE: {
|
||||
tree elt_type, array_size, elt_size;
|
||||
unsigned int i, nelt;
|
||||
HOST_WIDE_INT array_size_int, elt_size_int;
|
||||
|
||||
elt_type = TREE_TYPE(type);
|
||||
elt_size = TYPE_SIZE_UNIT(TREE_TYPE(type));
|
||||
array_size = TYPE_SIZE_UNIT(type);
|
||||
|
||||
if (TREE_CODE(elt_type) != INTEGER_TYPE || !array_size
|
||||
|| TREE_CODE(array_size) != INTEGER_CST) {
|
||||
*no_add_attrs = true;
|
||||
error("array variable %qD with %qE attribute must be a fixed length integer array type",
|
||||
*node, name);
|
||||
break;
|
||||
}
|
||||
|
||||
array_size_int = TREE_INT_CST_LOW(array_size);
|
||||
elt_size_int = TREE_INT_CST_LOW(elt_size);
|
||||
nelt = array_size_int / elt_size_int;
|
||||
|
||||
#if BUILDING_GCC_VERSION <= 4007
|
||||
vals = VEC_alloc(constructor_elt, gc, nelt);
|
||||
#else
|
||||
vec_alloc(vals, nelt);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < nelt; i++) {
|
||||
tree cst = size_int(i);
|
||||
tree rand_cst = tree_get_random_const(elt_type);
|
||||
|
||||
CONSTRUCTOR_APPEND_ELT(vals, cst, rand_cst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the elements of the array with random
|
||||
* constants
|
||||
*/
|
||||
DECL_INITIAL(*node) = build_constructor(type, vals);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FUNCTION_DECL:
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
static struct attribute_spec latent_entropy_attr = {
|
||||
.name = "latent_entropy",
|
||||
.min_length = 0,
|
||||
.max_length = 0,
|
||||
.decl_required = true,
|
||||
.type_required = false,
|
||||
.function_type_required = false,
|
||||
.handler = handle_latent_entropy_attribute,
|
||||
#if BUILDING_GCC_VERSION >= 4007
|
||||
.affects_type_identity = false
|
||||
#endif
|
||||
};
|
||||
|
||||
static void register_attributes(void *event_data __unused, void *data __unused)
|
||||
{
|
||||
register_attribute(&latent_entropy_attr);
|
||||
}
|
||||
|
||||
static bool latent_entropy_gate(void)
|
||||
{
|
||||
tree list;
|
||||
|
||||
/* don't bother with noreturn functions for now */
|
||||
if (TREE_THIS_VOLATILE(current_function_decl))
|
||||
return false;
|
||||
|
||||
/* gcc-4.5 doesn't discover some trivial noreturn functions */
|
||||
if (EDGE_COUNT(EXIT_BLOCK_PTR_FOR_FN(cfun)->preds) == 0)
|
||||
return false;
|
||||
|
||||
list = DECL_ATTRIBUTES(current_function_decl);
|
||||
return lookup_attribute("latent_entropy", list) != NULL_TREE;
|
||||
}
|
||||
|
||||
static tree create_var(tree type, const char *name)
|
||||
{
|
||||
tree var;
|
||||
|
||||
var = create_tmp_var(type, name);
|
||||
add_referenced_var(var);
|
||||
mark_sym_for_renaming(var);
|
||||
return var;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the next operation and its constant operand to use in the latent
|
||||
* entropy PRNG. When RHS is specified, the request is for perturbing the
|
||||
* local latent entropy variable, otherwise it is for perturbing the global
|
||||
* latent entropy variable where the two operands are already given by the
|
||||
* local and global latent entropy variables themselves.
|
||||
*
|
||||
* The operation is one of add/xor/rol when instrumenting the local entropy
|
||||
* variable and one of add/xor when perturbing the global entropy variable.
|
||||
* Rotation is not used for the latter case because it would transmit less
|
||||
* entropy to the global variable than the other two operations.
|
||||
*/
|
||||
static enum tree_code get_op(tree *rhs)
|
||||
{
|
||||
static enum tree_code op;
|
||||
unsigned HOST_WIDE_INT random_const;
|
||||
|
||||
random_const = get_random_const();
|
||||
|
||||
switch (op) {
|
||||
case BIT_XOR_EXPR:
|
||||
op = PLUS_EXPR;
|
||||
break;
|
||||
|
||||
case PLUS_EXPR:
|
||||
if (rhs) {
|
||||
op = LROTATE_EXPR;
|
||||
/*
|
||||
* This code limits the value of random_const to
|
||||
* the size of a wide int for the rotation
|
||||
*/
|
||||
random_const &= HOST_BITS_PER_WIDE_INT - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case LROTATE_EXPR:
|
||||
default:
|
||||
op = BIT_XOR_EXPR;
|
||||
break;
|
||||
}
|
||||
if (rhs)
|
||||
*rhs = build_int_cstu(unsigned_intDI_type_node, random_const);
|
||||
return op;
|
||||
}
|
||||
|
||||
static gimple create_assign(enum tree_code code, tree lhs, tree op1,
|
||||
tree op2)
|
||||
{
|
||||
return gimple_build_assign_with_ops(code, lhs, op1, op2);
|
||||
}
|
||||
|
||||
static void perturb_local_entropy(basic_block bb, tree local_entropy)
|
||||
{
|
||||
gimple_stmt_iterator gsi;
|
||||
gimple assign;
|
||||
tree rhs;
|
||||
enum tree_code op;
|
||||
|
||||
op = get_op(&rhs);
|
||||
assign = create_assign(op, local_entropy, local_entropy, rhs);
|
||||
gsi = gsi_after_labels(bb);
|
||||
gsi_insert_before(&gsi, assign, GSI_NEW_STMT);
|
||||
update_stmt(assign);
|
||||
}
|
||||
|
||||
static void __perturb_latent_entropy(gimple_stmt_iterator *gsi,
|
||||
tree local_entropy)
|
||||
{
|
||||
gimple assign;
|
||||
tree temp;
|
||||
enum tree_code op;
|
||||
|
||||
/* 1. create temporary copy of latent_entropy */
|
||||
temp = create_var(unsigned_intDI_type_node, "tmp_latent_entropy");
|
||||
|
||||
/* 2. read... */
|
||||
add_referenced_var(latent_entropy_decl);
|
||||
mark_sym_for_renaming(latent_entropy_decl);
|
||||
assign = gimple_build_assign(temp, latent_entropy_decl);
|
||||
gsi_insert_before(gsi, assign, GSI_NEW_STMT);
|
||||
update_stmt(assign);
|
||||
|
||||
/* 3. ...modify... */
|
||||
op = get_op(NULL);
|
||||
assign = create_assign(op, temp, temp, local_entropy);
|
||||
gsi_insert_after(gsi, assign, GSI_NEW_STMT);
|
||||
update_stmt(assign);
|
||||
|
||||
/* 4. ...write latent_entropy */
|
||||
assign = gimple_build_assign(latent_entropy_decl, temp);
|
||||
gsi_insert_after(gsi, assign, GSI_NEW_STMT);
|
||||
update_stmt(assign);
|
||||
}
|
||||
|
||||
static bool handle_tail_calls(basic_block bb, tree local_entropy)
|
||||
{
|
||||
gimple_stmt_iterator gsi;
|
||||
|
||||
for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
|
||||
gcall *call;
|
||||
gimple stmt = gsi_stmt(gsi);
|
||||
|
||||
if (!is_gimple_call(stmt))
|
||||
continue;
|
||||
|
||||
call = as_a_gcall(stmt);
|
||||
if (!gimple_call_tail_p(call))
|
||||
continue;
|
||||
|
||||
__perturb_latent_entropy(&gsi, local_entropy);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void perturb_latent_entropy(tree local_entropy)
|
||||
{
|
||||
edge_iterator ei;
|
||||
edge e, last_bb_e;
|
||||
basic_block last_bb;
|
||||
|
||||
gcc_assert(single_pred_p(EXIT_BLOCK_PTR_FOR_FN(cfun)));
|
||||
last_bb_e = single_pred_edge(EXIT_BLOCK_PTR_FOR_FN(cfun));
|
||||
|
||||
FOR_EACH_EDGE(e, ei, last_bb_e->src->preds) {
|
||||
if (ENTRY_BLOCK_PTR_FOR_FN(cfun) == e->src)
|
||||
continue;
|
||||
if (EXIT_BLOCK_PTR_FOR_FN(cfun) == e->src)
|
||||
continue;
|
||||
|
||||
handle_tail_calls(e->src, local_entropy);
|
||||
}
|
||||
|
||||
last_bb = single_pred(EXIT_BLOCK_PTR_FOR_FN(cfun));
|
||||
if (!handle_tail_calls(last_bb, local_entropy)) {
|
||||
gimple_stmt_iterator gsi = gsi_last_bb(last_bb);
|
||||
|
||||
__perturb_latent_entropy(&gsi, local_entropy);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_local_entropy(basic_block bb, tree local_entropy)
|
||||
{
|
||||
gimple assign, call;
|
||||
tree frame_addr, rand_const, tmp, fndecl, udi_frame_addr;
|
||||
enum tree_code op;
|
||||
unsigned HOST_WIDE_INT rand_cst;
|
||||
gimple_stmt_iterator gsi = gsi_after_labels(bb);
|
||||
|
||||
/* 1. create local_entropy_frameaddr */
|
||||
frame_addr = create_var(ptr_type_node, "local_entropy_frameaddr");
|
||||
|
||||
/* 2. local_entropy_frameaddr = __builtin_frame_address() */
|
||||
fndecl = builtin_decl_implicit(BUILT_IN_FRAME_ADDRESS);
|
||||
call = gimple_build_call(fndecl, 1, integer_zero_node);
|
||||
gimple_call_set_lhs(call, frame_addr);
|
||||
gsi_insert_before(&gsi, call, GSI_NEW_STMT);
|
||||
update_stmt(call);
|
||||
|
||||
udi_frame_addr = fold_convert(unsigned_intDI_type_node, frame_addr);
|
||||
assign = gimple_build_assign(local_entropy, udi_frame_addr);
|
||||
gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
|
||||
update_stmt(assign);
|
||||
|
||||
/* 3. create temporary copy of latent_entropy */
|
||||
tmp = create_var(unsigned_intDI_type_node, "tmp_latent_entropy");
|
||||
|
||||
/* 4. read the global entropy variable into local entropy */
|
||||
add_referenced_var(latent_entropy_decl);
|
||||
mark_sym_for_renaming(latent_entropy_decl);
|
||||
assign = gimple_build_assign(tmp, latent_entropy_decl);
|
||||
gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
|
||||
update_stmt(assign);
|
||||
|
||||
/* 5. mix local_entropy_frameaddr into local entropy */
|
||||
assign = create_assign(BIT_XOR_EXPR, local_entropy, local_entropy, tmp);
|
||||
gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
|
||||
update_stmt(assign);
|
||||
|
||||
rand_cst = get_random_const();
|
||||
rand_const = build_int_cstu(unsigned_intDI_type_node, rand_cst);
|
||||
op = get_op(NULL);
|
||||
assign = create_assign(op, local_entropy, local_entropy, rand_const);
|
||||
gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
|
||||
update_stmt(assign);
|
||||
}
|
||||
|
||||
static bool create_latent_entropy_decl(void)
|
||||
{
|
||||
varpool_node_ptr node;
|
||||
|
||||
if (latent_entropy_decl != NULL_TREE)
|
||||
return true;
|
||||
|
||||
FOR_EACH_VARIABLE(node) {
|
||||
tree name, var = NODE_DECL(node);
|
||||
|
||||
if (DECL_NAME_LENGTH(var) < sizeof("latent_entropy") - 1)
|
||||
continue;
|
||||
|
||||
name = DECL_NAME(var);
|
||||
if (strcmp(IDENTIFIER_POINTER(name), "latent_entropy"))
|
||||
continue;
|
||||
|
||||
latent_entropy_decl = var;
|
||||
break;
|
||||
}
|
||||
|
||||
return latent_entropy_decl != NULL_TREE;
|
||||
}
|
||||
|
||||
static unsigned int latent_entropy_execute(void)
|
||||
{
|
||||
basic_block bb;
|
||||
tree local_entropy;
|
||||
|
||||
if (!create_latent_entropy_decl())
|
||||
return 0;
|
||||
|
||||
/* prepare for step 2 below */
|
||||
gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
|
||||
bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
|
||||
if (!single_pred_p(bb)) {
|
||||
split_edge(single_succ_edge(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
|
||||
gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
|
||||
bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
|
||||
}
|
||||
|
||||
/* 1. create the local entropy variable */
|
||||
local_entropy = create_var(unsigned_intDI_type_node, "local_entropy");
|
||||
|
||||
/* 2. initialize the local entropy variable */
|
||||
init_local_entropy(bb, local_entropy);
|
||||
|
||||
bb = bb->next_bb;
|
||||
|
||||
/*
|
||||
* 3. instrument each BB with an operation on the
|
||||
* local entropy variable
|
||||
*/
|
||||
while (bb != EXIT_BLOCK_PTR_FOR_FN(cfun)) {
|
||||
perturb_local_entropy(bb, local_entropy);
|
||||
bb = bb->next_bb;
|
||||
};
|
||||
|
||||
/* 4. mix local entropy into the global entropy variable */
|
||||
perturb_latent_entropy(local_entropy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void latent_entropy_start_unit(void *gcc_data __unused,
|
||||
void *user_data __unused)
|
||||
{
|
||||
tree type, id;
|
||||
int quals;
|
||||
|
||||
seed = get_random_seed(false);
|
||||
|
||||
if (in_lto_p)
|
||||
return;
|
||||
|
||||
/* extern volatile u64 latent_entropy */
|
||||
gcc_assert(TYPE_PRECISION(long_long_unsigned_type_node) == 64);
|
||||
quals = TYPE_QUALS(long_long_unsigned_type_node) | TYPE_QUAL_VOLATILE;
|
||||
type = build_qualified_type(long_long_unsigned_type_node, quals);
|
||||
id = get_identifier("latent_entropy");
|
||||
latent_entropy_decl = build_decl(UNKNOWN_LOCATION, VAR_DECL, id, type);
|
||||
|
||||
TREE_STATIC(latent_entropy_decl) = 1;
|
||||
TREE_PUBLIC(latent_entropy_decl) = 1;
|
||||
TREE_USED(latent_entropy_decl) = 1;
|
||||
DECL_PRESERVE_P(latent_entropy_decl) = 1;
|
||||
TREE_THIS_VOLATILE(latent_entropy_decl) = 1;
|
||||
DECL_EXTERNAL(latent_entropy_decl) = 1;
|
||||
DECL_ARTIFICIAL(latent_entropy_decl) = 1;
|
||||
lang_hooks.decls.pushdecl(latent_entropy_decl);
|
||||
}
|
||||
|
||||
#define PASS_NAME latent_entropy
|
||||
#define PROPERTIES_REQUIRED PROP_gimple_leh | PROP_cfg
|
||||
#define TODO_FLAGS_FINISH TODO_verify_ssa | TODO_verify_stmts | TODO_dump_func \
|
||||
| TODO_update_ssa
|
||||
#include "gcc-generate-gimple-pass.h"
|
||||
|
||||
int plugin_init(struct plugin_name_args *plugin_info,
|
||||
struct plugin_gcc_version *version)
|
||||
{
|
||||
bool enabled = true;
|
||||
const char * const plugin_name = plugin_info->base_name;
|
||||
const int argc = plugin_info->argc;
|
||||
const struct plugin_argument * const argv = plugin_info->argv;
|
||||
int i;
|
||||
|
||||
struct register_pass_info latent_entropy_pass_info;
|
||||
|
||||
latent_entropy_pass_info.pass = make_latent_entropy_pass();
|
||||
latent_entropy_pass_info.reference_pass_name = "optimized";
|
||||
latent_entropy_pass_info.ref_pass_instance_number = 1;
|
||||
latent_entropy_pass_info.pos_op = PASS_POS_INSERT_BEFORE;
|
||||
static const struct ggc_root_tab gt_ggc_r_gt_latent_entropy[] = {
|
||||
{
|
||||
.base = &latent_entropy_decl,
|
||||
.nelt = 1,
|
||||
.stride = sizeof(latent_entropy_decl),
|
||||
.cb = >_ggc_mx_tree_node,
|
||||
.pchw = >_pch_nx_tree_node
|
||||
},
|
||||
LAST_GGC_ROOT_TAB
|
||||
};
|
||||
|
||||
if (!plugin_default_version_check(version, &gcc_version)) {
|
||||
error(G_("incompatible gcc/plugin versions"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < argc; ++i) {
|
||||
if (!(strcmp(argv[i].key, "disable"))) {
|
||||
enabled = false;
|
||||
continue;
|
||||
}
|
||||
error(G_("unkown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
|
||||
}
|
||||
|
||||
register_callback(plugin_name, PLUGIN_INFO, NULL,
|
||||
&latent_entropy_plugin_info);
|
||||
if (enabled) {
|
||||
register_callback(plugin_name, PLUGIN_START_UNIT,
|
||||
&latent_entropy_start_unit, NULL);
|
||||
register_callback(plugin_name, PLUGIN_REGISTER_GGC_ROOTS,
|
||||
NULL, (void *)>_ggc_r_gt_latent_entropy);
|
||||
register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
|
||||
&latent_entropy_pass_info);
|
||||
}
|
||||
register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes,
|
||||
NULL);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user