aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/Kconfig18
-rw-r--r--arch/powerpc/kernel/Makefile5
-rw-r--r--block/blk-softirq.c2
-rw-r--r--drivers/char/random.c4
-rw-r--r--fs/namespace.c1
-rw-r--r--include/linux/compiler-gcc.h7
-rw-r--r--include/linux/compiler.h4
-rw-r--r--include/linux/fdtable.h2
-rw-r--r--include/linux/genhd.h2
-rw-r--r--include/linux/init.h5
-rw-r--r--include/linux/random.h15
-rw-r--r--init/main.c1
-rw-r--r--kernel/fork.c7
-rw-r--r--kernel/rcu/tiny.c2
-rw-r--r--kernel/rcu/tree.c2
-rw-r--r--kernel/sched/fair.c2
-rw-r--r--kernel/softirq.c4
-rw-r--r--kernel/time/timer.c2
-rw-r--r--lib/irq_poll.c2
-rw-r--r--lib/random32.c2
-rw-r--r--mm/page_alloc.c5
-rw-r--r--net/core/dev.c4
-rw-r--r--scripts/Makefile.gcc-plugins9
-rw-r--r--scripts/gcc-plugins/latent_entropy_plugin.c640
24 files changed, 725 insertions, 22 deletions
diff --git a/arch/Kconfig b/arch/Kconfig
index 11d349561ece..659bdd079277 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -383,6 +383,24 @@ config GCC_PLUGIN_SANCOV
383 gcc-4.5 on). It is based on the commit "Add fuzzing coverage support" 383 gcc-4.5 on). It is based on the commit "Add fuzzing coverage support"
384 by Dmitry Vyukov <dvyukov@google.com>. 384 by Dmitry Vyukov <dvyukov@google.com>.
385 385
386config GCC_PLUGIN_LATENT_ENTROPY
387 bool "Generate some entropy during boot and runtime"
388 depends on GCC_PLUGINS
389 help
390 By saying Y here the kernel will instrument some kernel code to
391 extract some entropy from both original and artificially created
392 program state. This will help especially embedded systems where
393 there is little 'natural' source of entropy normally. The cost
394 is some slowdown of the boot process (about 0.5%) and fork and
395 irq processing.
396
397 Note that entropy extracted this way is not cryptographically
398 secure!
399
400 This plugin was ported from grsecurity/PaX. More information at:
401 * https://grsecurity.net/
402 * https://pax.grsecurity.net/
403
386config HAVE_CC_STACKPROTECTOR 404config HAVE_CC_STACKPROTECTOR
387 bool 405 bool
388 help 406 help
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 6913f6725ce1..1925341dbb9c 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -14,6 +14,11 @@ CFLAGS_prom_init.o += -fPIC
14CFLAGS_btext.o += -fPIC 14CFLAGS_btext.o += -fPIC
15endif 15endif
16 16
17CFLAGS_cputable.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
18CFLAGS_init.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
19CFLAGS_btext.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
20CFLAGS_prom.o += $(DISABLE_LATENT_ENTROPY_PLUGIN)
21
17ifdef CONFIG_FUNCTION_TRACER 22ifdef CONFIG_FUNCTION_TRACER
18# Do not trace early boot code 23# Do not trace early boot code
19CFLAGS_REMOVE_cputable.o = -mno-sched-epilog $(CC_FLAGS_FTRACE) 24CFLAGS_REMOVE_cputable.o = -mno-sched-epilog $(CC_FLAGS_FTRACE)
diff --git a/block/blk-softirq.c b/block/blk-softirq.c
index 96631e6a22b9..06cf9807f49a 100644
--- a/block/blk-softirq.c
+++ b/block/blk-softirq.c
@@ -18,7 +18,7 @@ static DEFINE_PER_CPU(struct list_head, blk_cpu_done);
18 * Softirq action handler - move entries to local list and loop over them 18 * Softirq action handler - move entries to local list and loop over them
19 * while passing them to the queue registered handler. 19 * while passing them to the queue registered handler.
20 */ 20 */
21static void blk_done_softirq(struct softirq_action *h) 21static __latent_entropy void blk_done_softirq(struct softirq_action *h)
22{ 22{
23 struct list_head *cpu_list, local_list; 23 struct list_head *cpu_list, local_list;
24 24
diff --git a/drivers/char/random.c b/drivers/char/random.c
index d131e152c8ce..d6876d506220 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -479,8 +479,8 @@ static ssize_t _extract_entropy(struct entropy_store *r, void *buf,
479 479
480static void crng_reseed(struct crng_state *crng, struct entropy_store *r); 480static void crng_reseed(struct crng_state *crng, struct entropy_store *r);
481static void push_to_pool(struct work_struct *work); 481static void push_to_pool(struct work_struct *work);
482static __u32 input_pool_data[INPUT_POOL_WORDS]; 482static __u32 input_pool_data[INPUT_POOL_WORDS] __latent_entropy;
483static __u32 blocking_pool_data[OUTPUT_POOL_WORDS]; 483static __u32 blocking_pool_data[OUTPUT_POOL_WORDS] __latent_entropy;
484 484
485static struct entropy_store input_pool = { 485static struct entropy_store input_pool = {
486 .poolinfo = &poolinfo_table[0], 486 .poolinfo = &poolinfo_table[0],
diff --git a/fs/namespace.c b/fs/namespace.c
index 58aca9c931ac..e6c234b1a645 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2824,6 +2824,7 @@ static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns)
2824 return new_ns; 2824 return new_ns;
2825} 2825}
2826 2826
2827__latent_entropy
2827struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns, 2828struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
2828 struct user_namespace *user_ns, struct fs_struct *new_fs) 2829 struct user_namespace *user_ns, struct fs_struct *new_fs)
2829{ 2830{
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 573c5a18908f..432f5c97e18f 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -188,6 +188,13 @@
188#endif /* GCC_VERSION >= 40300 */ 188#endif /* GCC_VERSION >= 40300 */
189 189
190#if GCC_VERSION >= 40500 190#if GCC_VERSION >= 40500
191
192#ifndef __CHECKER__
193#ifdef LATENT_ENTROPY_PLUGIN
194#define __latent_entropy __attribute__((latent_entropy))
195#endif
196#endif
197
191/* 198/*
192 * Mark a position in code as unreachable. This can be used to 199 * Mark a position in code as unreachable. This can be used to
193 * suppress control flow warnings after asm blocks that transfer 200 * suppress control flow warnings after asm blocks that transfer
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index f1bfa15b6f9b..cf0fa5d86059 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -429,6 +429,10 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
429# define __attribute_const__ /* unimplemented */ 429# define __attribute_const__ /* unimplemented */
430#endif 430#endif
431 431
432#ifndef __latent_entropy
433# define __latent_entropy
434#endif
435
432/* 436/*
433 * Tell gcc if a function is cold. The compiler will assume any path 437 * Tell gcc if a function is cold. The compiler will assume any path
434 * directly leading to the call is unlikely. 438 * directly leading to the call is unlikely.
diff --git a/include/linux/fdtable.h b/include/linux/fdtable.h
index aca2a6a1d035..6e84b2cae6ad 100644
--- a/include/linux/fdtable.h
+++ b/include/linux/fdtable.h
@@ -105,7 +105,7 @@ struct files_struct *get_files_struct(struct task_struct *);
105void put_files_struct(struct files_struct *fs); 105void put_files_struct(struct files_struct *fs);
106void reset_files_struct(struct files_struct *); 106void reset_files_struct(struct files_struct *);
107int unshare_files(struct files_struct **); 107int unshare_files(struct files_struct **);
108struct files_struct *dup_fd(struct files_struct *, int *); 108struct files_struct *dup_fd(struct files_struct *, int *) __latent_entropy;
109void do_close_on_exec(struct files_struct *); 109void do_close_on_exec(struct files_struct *);
110int iterate_fd(struct files_struct *, unsigned, 110int iterate_fd(struct files_struct *, unsigned,
111 int (*)(const void *, struct file *, unsigned), 111 int (*)(const void *, struct file *, unsigned),
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index 1dbf52f9c24b..e0341af6950e 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -437,7 +437,7 @@ extern void disk_flush_events(struct gendisk *disk, unsigned int mask);
437extern unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask); 437extern unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask);
438 438
439/* drivers/char/random.c */ 439/* drivers/char/random.c */
440extern void add_disk_randomness(struct gendisk *disk); 440extern void add_disk_randomness(struct gendisk *disk) __latent_entropy;
441extern void rand_initialize_disk(struct gendisk *disk); 441extern void rand_initialize_disk(struct gendisk *disk);
442 442
443static inline sector_t get_start_sect(struct block_device *bdev) 443static inline sector_t get_start_sect(struct block_device *bdev)
diff --git a/include/linux/init.h b/include/linux/init.h
index 024a0b5b3ed0..e30104ceb86d 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -39,7 +39,7 @@
39 39
40/* These are for everybody (although not all archs will actually 40/* These are for everybody (although not all archs will actually
41 discard it in modules) */ 41 discard it in modules) */
42#define __init __section(.init.text) __cold notrace 42#define __init __section(.init.text) __cold notrace __latent_entropy
43#define __initdata __section(.init.data) 43#define __initdata __section(.init.data)
44#define __initconst __section(.init.rodata) 44#define __initconst __section(.init.rodata)
45#define __exitdata __section(.exit.data) 45#define __exitdata __section(.exit.data)
@@ -75,7 +75,8 @@
75#define __exit __section(.exit.text) __exitused __cold notrace 75#define __exit __section(.exit.text) __exitused __cold notrace
76 76
77/* Used for MEMORY_HOTPLUG */ 77/* Used for MEMORY_HOTPLUG */
78#define __meminit __section(.meminit.text) __cold notrace 78#define __meminit __section(.meminit.text) __cold notrace \
79 __latent_entropy
79#define __meminitdata __section(.meminit.data) 80#define __meminitdata __section(.meminit.data)
80#define __meminitconst __section(.meminit.rodata) 81#define __meminitconst __section(.meminit.rodata)
81#define __memexit __section(.memexit.text) __exitused __cold notrace 82#define __memexit __section(.memexit.text) __exitused __cold notrace
diff --git a/include/linux/random.h b/include/linux/random.h
index f7bb7a355cf7..7bd2403e4fef 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -18,9 +18,20 @@ struct random_ready_callback {
18}; 18};
19 19
20extern void add_device_randomness(const void *, unsigned int); 20extern void add_device_randomness(const void *, unsigned int);
21
22#if defined(CONFIG_GCC_PLUGIN_LATENT_ENTROPY) && !defined(__CHECKER__)
23static inline void add_latent_entropy(void)
24{
25 add_device_randomness((const void *)&latent_entropy,
26 sizeof(latent_entropy));
27}
28#else
29static inline void add_latent_entropy(void) {}
30#endif
31
21extern void add_input_randomness(unsigned int type, unsigned int code, 32extern void add_input_randomness(unsigned int type, unsigned int code,
22 unsigned int value); 33 unsigned int value) __latent_entropy;
23extern void add_interrupt_randomness(int irq, int irq_flags); 34extern void add_interrupt_randomness(int irq, int irq_flags) __latent_entropy;
24 35
25extern void get_random_bytes(void *buf, int nbytes); 36extern void get_random_bytes(void *buf, int nbytes);
26extern int add_random_ready_callback(struct random_ready_callback *rdy); 37extern int add_random_ready_callback(struct random_ready_callback *rdy);
diff --git a/init/main.c b/init/main.c
index a8a58e2794a5..2858be732f6d 100644
--- a/init/main.c
+++ b/init/main.c
@@ -789,6 +789,7 @@ int __init_or_module do_one_initcall(initcall_t fn)
789 } 789 }
790 WARN(msgbuf[0], "initcall %pF returned with %s\n", fn, msgbuf); 790 WARN(msgbuf[0], "initcall %pF returned with %s\n", fn, msgbuf);
791 791
792 add_latent_entropy();
792 return ret; 793 return ret;
793} 794}
794 795
diff --git a/kernel/fork.c b/kernel/fork.c
index 6d42242485cb..623259fc794d 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -547,7 +547,8 @@ free_tsk:
547} 547}
548 548
549#ifdef CONFIG_MMU 549#ifdef CONFIG_MMU
550static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) 550static __latent_entropy int dup_mmap(struct mm_struct *mm,
551 struct mm_struct *oldmm)
551{ 552{
552 struct vm_area_struct *mpnt, *tmp, *prev, **pprev; 553 struct vm_area_struct *mpnt, *tmp, *prev, **pprev;
553 struct rb_node **rb_link, *rb_parent; 554 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)
1441 * parts of the process environment (as per the clone 1442 * parts of the process environment (as per the clone
1442 * flags). The actual kick-off is left to the caller. 1443 * flags). The actual kick-off is left to the caller.
1443 */ 1444 */
1444static struct task_struct *copy_process(unsigned long clone_flags, 1445static __latent_entropy struct task_struct *copy_process(
1446 unsigned long clone_flags,
1445 unsigned long stack_start, 1447 unsigned long stack_start,
1446 unsigned long stack_size, 1448 unsigned long stack_size,
1447 int __user *child_tidptr, 1449 int __user *child_tidptr,
@@ -1926,6 +1928,7 @@ long _do_fork(unsigned long clone_flags,
1926 1928
1927 p = copy_process(clone_flags, stack_start, stack_size, 1929 p = copy_process(clone_flags, stack_start, stack_size,
1928 child_tidptr, NULL, trace, tls, NUMA_NO_NODE); 1930 child_tidptr, NULL, trace, tls, NUMA_NO_NODE);
1931 add_latent_entropy();
1929 /* 1932 /*
1930 * Do this prior waking up the new thread - the thread pointer 1933 * Do this prior waking up the new thread - the thread pointer
1931 * might get invalid after that point, if the thread exits quickly. 1934 * might get invalid after that point, if the thread exits quickly.
diff --git a/kernel/rcu/tiny.c b/kernel/rcu/tiny.c
index 944b1b491ed8..1898559e6b60 100644
--- a/kernel/rcu/tiny.c
+++ b/kernel/rcu/tiny.c
@@ -170,7 +170,7 @@ static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp)
170 false)); 170 false));
171} 171}
172 172
173static void rcu_process_callbacks(struct softirq_action *unused) 173static __latent_entropy void rcu_process_callbacks(struct softirq_action *unused)
174{ 174{
175 __rcu_process_callbacks(&rcu_sched_ctrlblk); 175 __rcu_process_callbacks(&rcu_sched_ctrlblk);
176 __rcu_process_callbacks(&rcu_bh_ctrlblk); 176 __rcu_process_callbacks(&rcu_bh_ctrlblk);
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 7e2e03879c2e..69a5611a7e7c 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -3013,7 +3013,7 @@ __rcu_process_callbacks(struct rcu_state *rsp)
3013/* 3013/*
3014 * Do RCU core processing for the current CPU. 3014 * Do RCU core processing for the current CPU.
3015 */ 3015 */
3016static void rcu_process_callbacks(struct softirq_action *unused) 3016static __latent_entropy void rcu_process_callbacks(struct softirq_action *unused)
3017{ 3017{
3018 struct rcu_state *rsp; 3018 struct rcu_state *rsp;
3019 3019
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 502e95a6e927..2d4ad72f8f3c 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -8522,7 +8522,7 @@ static void nohz_idle_balance(struct rq *this_rq, enum cpu_idle_type idle) { }
8522 * run_rebalance_domains is triggered when needed from the scheduler tick. 8522 * run_rebalance_domains is triggered when needed from the scheduler tick.
8523 * Also triggered for nohz idle balancing (with nohz_balancing_kick set). 8523 * Also triggered for nohz idle balancing (with nohz_balancing_kick set).
8524 */ 8524 */
8525static void run_rebalance_domains(struct softirq_action *h) 8525static __latent_entropy void run_rebalance_domains(struct softirq_action *h)
8526{ 8526{
8527 struct rq *this_rq = this_rq(); 8527 struct rq *this_rq = this_rq();
8528 enum cpu_idle_type idle = this_rq->idle_balance ? 8528 enum cpu_idle_type idle = this_rq->idle_balance ?
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 66762645f9e8..1bf81ef91375 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -496,7 +496,7 @@ void __tasklet_hi_schedule_first(struct tasklet_struct *t)
496} 496}
497EXPORT_SYMBOL(__tasklet_hi_schedule_first); 497EXPORT_SYMBOL(__tasklet_hi_schedule_first);
498 498
499static void tasklet_action(struct softirq_action *a) 499static __latent_entropy void tasklet_action(struct softirq_action *a)
500{ 500{
501 struct tasklet_struct *list; 501 struct tasklet_struct *list;
502 502
@@ -532,7 +532,7 @@ static void tasklet_action(struct softirq_action *a)
532 } 532 }
533} 533}
534 534
535static void tasklet_hi_action(struct softirq_action *a) 535static __latent_entropy void tasklet_hi_action(struct softirq_action *a)
536{ 536{
537 struct tasklet_struct *list; 537 struct tasklet_struct *list;
538 538
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 32bf6f75a8fe..2d47980a1bc4 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -1633,7 +1633,7 @@ static inline void __run_timers(struct timer_base *base)
1633/* 1633/*
1634 * This function runs timers and the timer-tq in bottom half context. 1634 * This function runs timers and the timer-tq in bottom half context.
1635 */ 1635 */
1636static void run_timer_softirq(struct softirq_action *h) 1636static __latent_entropy void run_timer_softirq(struct softirq_action *h)
1637{ 1637{
1638 struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]); 1638 struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]);
1639 1639
diff --git a/lib/irq_poll.c b/lib/irq_poll.c
index 2be55692aa43..1d6565e81030 100644
--- a/lib/irq_poll.c
+++ b/lib/irq_poll.c
@@ -74,7 +74,7 @@ void irq_poll_complete(struct irq_poll *iop)
74} 74}
75EXPORT_SYMBOL(irq_poll_complete); 75EXPORT_SYMBOL(irq_poll_complete);
76 76
77static void irq_poll_softirq(struct softirq_action *h) 77static void __latent_entropy irq_poll_softirq(struct softirq_action *h)
78{ 78{
79 struct list_head *list = this_cpu_ptr(&blk_cpu_iopoll); 79 struct list_head *list = this_cpu_ptr(&blk_cpu_iopoll);
80 int rearm = 0, budget = irq_poll_budget; 80 int rearm = 0, budget = irq_poll_budget;
diff --git a/lib/random32.c b/lib/random32.c
index 915982b304bb..fa594b1140e6 100644
--- a/lib/random32.c
+++ b/lib/random32.c
@@ -47,7 +47,7 @@ static inline void prandom_state_selftest(void)
47} 47}
48#endif 48#endif
49 49
50static DEFINE_PER_CPU(struct rnd_state, net_rand_state); 50static DEFINE_PER_CPU(struct rnd_state, net_rand_state) __latent_entropy;
51 51
52/** 52/**
53 * prandom_u32_state - seeded pseudo-random number generator. 53 * prandom_u32_state - seeded pseudo-random number generator.
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index ca423cc20b59..2b3bf6767d54 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -91,6 +91,11 @@ EXPORT_PER_CPU_SYMBOL(_numa_mem_);
91int _node_numa_mem_[MAX_NUMNODES]; 91int _node_numa_mem_[MAX_NUMNODES];
92#endif 92#endif
93 93
94#ifdef CONFIG_GCC_PLUGIN_LATENT_ENTROPY
95volatile u64 latent_entropy __latent_entropy;
96EXPORT_SYMBOL(latent_entropy);
97#endif
98
94/* 99/*
95 * Array of node states. 100 * Array of node states.
96 */ 101 */
diff --git a/net/core/dev.c b/net/core/dev.c
index f1fe26f66458..4bc19a164ba5 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3845,7 +3845,7 @@ int netif_rx_ni(struct sk_buff *skb)
3845} 3845}
3846EXPORT_SYMBOL(netif_rx_ni); 3846EXPORT_SYMBOL(netif_rx_ni);
3847 3847
3848static void net_tx_action(struct softirq_action *h) 3848static __latent_entropy void net_tx_action(struct softirq_action *h)
3849{ 3849{
3850 struct softnet_data *sd = this_cpu_ptr(&softnet_data); 3850 struct softnet_data *sd = this_cpu_ptr(&softnet_data);
3851 3851
@@ -5198,7 +5198,7 @@ out_unlock:
5198 return work; 5198 return work;
5199} 5199}
5200 5200
5201static void net_rx_action(struct softirq_action *h) 5201static __latent_entropy void net_rx_action(struct softirq_action *h)
5202{ 5202{
5203 struct softnet_data *sd = this_cpu_ptr(&softnet_data); 5203 struct softnet_data *sd = this_cpu_ptr(&softnet_data);
5204 unsigned long time_limit = jiffies + 2; 5204 unsigned long time_limit = jiffies + 2;
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index 61f0e6db909b..060d2cb373db 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -6,6 +6,12 @@ ifdef CONFIG_GCC_PLUGINS
6 6
7 gcc-plugin-$(CONFIG_GCC_PLUGIN_CYC_COMPLEXITY) += cyc_complexity_plugin.so 7 gcc-plugin-$(CONFIG_GCC_PLUGIN_CYC_COMPLEXITY) += cyc_complexity_plugin.so
8 8
9 gcc-plugin-$(CONFIG_GCC_PLUGIN_LATENT_ENTROPY) += latent_entropy_plugin.so
10 gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_LATENT_ENTROPY) += -DLATENT_ENTROPY_PLUGIN
11 ifdef CONFIG_PAX_LATENT_ENTROPY
12 DISABLE_LATENT_ENTROPY_PLUGIN += -fplugin-arg-latent_entropy_plugin-disable
13 endif
14
9 ifdef CONFIG_GCC_PLUGIN_SANCOV 15 ifdef CONFIG_GCC_PLUGIN_SANCOV
10 ifeq ($(CFLAGS_KCOV),) 16 ifeq ($(CFLAGS_KCOV),)
11 # It is needed because of the gcc-plugin.sh and gcc version checks. 17 # It is needed because of the gcc-plugin.sh and gcc version checks.
@@ -21,7 +27,8 @@ ifdef CONFIG_GCC_PLUGINS
21 27
22 GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y)) 28 GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y))
23 29
24 export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR SANCOV_PLUGIN 30 export PLUGINCC GCC_PLUGINS_CFLAGS GCC_PLUGIN GCC_PLUGIN_SUBDIR
31 export SANCOV_PLUGIN DISABLE_LATENT_ENTROPY_PLUGIN
25 32
26 ifneq ($(PLUGINCC),) 33 ifneq ($(PLUGINCC),)
27 # SANCOV_PLUGIN can be only in CFLAGS_KCOV because avoid duplication. 34 # SANCOV_PLUGIN can be only in CFLAGS_KCOV because avoid duplication.
diff --git a/scripts/gcc-plugins/latent_entropy_plugin.c b/scripts/gcc-plugins/latent_entropy_plugin.c
new file mode 100644
index 000000000000..ff1939b804ae
--- /dev/null
+++ b/scripts/gcc-plugins/latent_entropy_plugin.c
@@ -0,0 +1,640 @@
1/*
2 * Copyright 2012-2016 by the PaX Team <pageexec@freemail.hu>
3 * Copyright 2016 by Emese Revfy <re.emese@gmail.com>
4 * Licensed under the GPL v2
5 *
6 * Note: the choice of the license means that the compilation process is
7 * NOT 'eligible' as defined by gcc's library exception to the GPL v3,
8 * but for the kernel it doesn't matter since it doesn't link against
9 * any of the gcc libraries
10 *
11 * This gcc plugin helps generate a little bit of entropy from program state,
12 * used throughout the uptime of the kernel. Here is an instrumentation example:
13 *
14 * before:
15 * void __latent_entropy test(int argc, char *argv[])
16 * {
17 * if (argc <= 1)
18 * printf("%s: no command arguments :(\n", *argv);
19 * else
20 * printf("%s: %d command arguments!\n", *argv, args - 1);
21 * }
22 *
23 * after:
24 * void __latent_entropy test(int argc, char *argv[])
25 * {
26 * // latent_entropy_execute() 1.
27 * unsigned long local_entropy;
28 * // init_local_entropy() 1.
29 * void *local_entropy_frameaddr;
30 * // init_local_entropy() 3.
31 * unsigned long tmp_latent_entropy;
32 *
33 * // init_local_entropy() 2.
34 * local_entropy_frameaddr = __builtin_frame_address(0);
35 * local_entropy = (unsigned long) local_entropy_frameaddr;
36 *
37 * // init_local_entropy() 4.
38 * tmp_latent_entropy = latent_entropy;
39 * // init_local_entropy() 5.
40 * local_entropy ^= tmp_latent_entropy;
41 *
42 * // latent_entropy_execute() 3.
43 * if (argc <= 1) {
44 * // perturb_local_entropy()
45 * local_entropy += 4623067384293424948;
46 * printf("%s: no command arguments :(\n", *argv);
47 * // perturb_local_entropy()
48 * } else {
49 * local_entropy ^= 3896280633962944730;
50 * printf("%s: %d command arguments!\n", *argv, args - 1);
51 * }
52 *
53 * // latent_entropy_execute() 4.
54 * tmp_latent_entropy = rol(tmp_latent_entropy, local_entropy);
55 * latent_entropy = tmp_latent_entropy;
56 * }
57 *
58 * TODO:
59 * - add ipa pass to identify not explicitly marked candidate functions
60 * - mix in more program state (function arguments/return values,
61 * loop variables, etc)
62 * - more instrumentation control via attribute parameters
63 *
64 * BUGS:
65 * - none known
66 *
67 * Options:
68 * -fplugin-arg-latent_entropy_plugin-disable
69 *
70 * Attribute: __attribute__((latent_entropy))
71 * The latent_entropy gcc attribute can be only on functions and variables.
72 * If it is on a function then the plugin will instrument it. If the attribute
73 * is on a variable then the plugin will initialize it with a random value.
74 * The variable must be an integer, an integer array type or a structure
75 * with integer fields.
76 */
77
78#include "gcc-common.h"
79
80int plugin_is_GPL_compatible;
81
82static GTY(()) tree latent_entropy_decl;
83
84static struct plugin_info latent_entropy_plugin_info = {
85 .version = "201606141920vanilla",
86 .help = "disable\tturn off latent entropy instrumentation\n",
87};
88
89static unsigned HOST_WIDE_INT seed;
90/*
91 * get_random_seed() (this is a GCC function) generates the seed.
92 * This is a simple random generator without any cryptographic security because
93 * the entropy doesn't come from here.
94 */
95static unsigned HOST_WIDE_INT get_random_const(void)
96{
97 unsigned int i;
98 unsigned HOST_WIDE_INT ret = 0;
99
100 for (i = 0; i < 8 * sizeof(ret); i++) {
101 ret = (ret << 1) | (seed & 1);
102 seed >>= 1;
103 if (ret & 1)
104 seed ^= 0xD800000000000000ULL;
105 }
106
107 return ret;
108}
109
110static tree tree_get_random_const(tree type)
111{
112 unsigned long long mask;
113
114 mask = 1ULL << (TREE_INT_CST_LOW(TYPE_SIZE(type)) - 1);
115 mask = 2 * (mask - 1) + 1;
116
117 if (TYPE_UNSIGNED(type))
118 return build_int_cstu(type, mask & get_random_const());
119 return build_int_cst(type, mask & get_random_const());
120}
121
122static tree handle_latent_entropy_attribute(tree *node, tree name,
123 tree args __unused,
124 int flags __unused,
125 bool *no_add_attrs)
126{
127 tree type;
128#if BUILDING_GCC_VERSION <= 4007
129 VEC(constructor_elt, gc) *vals;
130#else
131 vec<constructor_elt, va_gc> *vals;
132#endif
133
134 switch (TREE_CODE(*node)) {
135 default:
136 *no_add_attrs = true;
137 error("%qE attribute only applies to functions and variables",
138 name);
139 break;
140
141 case VAR_DECL:
142 if (DECL_INITIAL(*node)) {
143 *no_add_attrs = true;
144 error("variable %qD with %qE attribute must not be initialized",
145 *node, name);
146 break;
147 }
148
149 if (!TREE_STATIC(*node)) {
150 *no_add_attrs = true;
151 error("variable %qD with %qE attribute must not be local",
152 *node, name);
153 break;
154 }
155
156 type = TREE_TYPE(*node);
157 switch (TREE_CODE(type)) {
158 default:
159 *no_add_attrs = true;
160 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",
161 *node, name);
162 break;
163
164 case RECORD_TYPE: {
165 tree fld, lst = TYPE_FIELDS(type);
166 unsigned int nelt = 0;
167
168 for (fld = lst; fld; nelt++, fld = TREE_CHAIN(fld)) {
169 tree fieldtype;
170
171 fieldtype = TREE_TYPE(fld);
172 if (TREE_CODE(fieldtype) == INTEGER_TYPE)
173 continue;
174
175 *no_add_attrs = true;
176 error("structure variable %qD with %qE attribute has a non-integer field %qE",
177 *node, name, fld);
178 break;
179 }
180
181 if (fld)
182 break;
183
184#if BUILDING_GCC_VERSION <= 4007
185 vals = VEC_alloc(constructor_elt, gc, nelt);
186#else
187 vec_alloc(vals, nelt);
188#endif
189
190 for (fld = lst; fld; fld = TREE_CHAIN(fld)) {
191 tree random_const, fld_t = TREE_TYPE(fld);
192
193 random_const = tree_get_random_const(fld_t);
194 CONSTRUCTOR_APPEND_ELT(vals, fld, random_const);
195 }
196
197 /* Initialize the fields with random constants */
198 DECL_INITIAL(*node) = build_constructor(type, vals);
199 break;
200 }
201
202 /* Initialize the variable with a random constant */
203 case INTEGER_TYPE:
204 DECL_INITIAL(*node) = tree_get_random_const(type);
205 break;
206
207 case ARRAY_TYPE: {
208 tree elt_type, array_size, elt_size;
209 unsigned int i, nelt;
210 HOST_WIDE_INT array_size_int, elt_size_int;
211
212 elt_type = TREE_TYPE(type);
213 elt_size = TYPE_SIZE_UNIT(TREE_TYPE(type));
214 array_size = TYPE_SIZE_UNIT(type);
215
216 if (TREE_CODE(elt_type) != INTEGER_TYPE || !array_size
217 || TREE_CODE(array_size) != INTEGER_CST) {
218 *no_add_attrs = true;
219 error("array variable %qD with %qE attribute must be a fixed length integer array type",
220 *node, name);
221 break;
222 }
223
224 array_size_int = TREE_INT_CST_LOW(array_size);
225 elt_size_int = TREE_INT_CST_LOW(elt_size);
226 nelt = array_size_int / elt_size_int;
227
228#if BUILDING_GCC_VERSION <= 4007
229 vals = VEC_alloc(constructor_elt, gc, nelt);
230#else
231 vec_alloc(vals, nelt);
232#endif
233
234 for (i = 0; i < nelt; i++) {
235 tree cst = size_int(i);
236 tree rand_cst = tree_get_random_const(elt_type);
237
238 CONSTRUCTOR_APPEND_ELT(vals, cst, rand_cst);
239 }
240
241 /*
242 * Initialize the elements of the array with random
243 * constants
244 */
245 DECL_INITIAL(*node) = build_constructor(type, vals);
246 break;
247 }
248 }
249 break;
250
251 case FUNCTION_DECL:
252 break;
253 }
254
255 return NULL_TREE;
256}
257
258static struct attribute_spec latent_entropy_attr = {
259 .name = "latent_entropy",
260 .min_length = 0,
261 .max_length = 0,
262 .decl_required = true,
263 .type_required = false,
264 .function_type_required = false,
265 .handler = handle_latent_entropy_attribute,
266#if BUILDING_GCC_VERSION >= 4007
267 .affects_type_identity = false
268#endif
269};
270
271static void register_attributes(void *event_data __unused, void *data __unused)
272{
273 register_attribute(&latent_entropy_attr);
274}
275
276static bool latent_entropy_gate(void)
277{
278 tree list;
279
280 /* don't bother with noreturn functions for now */
281 if (TREE_THIS_VOLATILE(current_function_decl))
282 return false;
283
284 /* gcc-4.5 doesn't discover some trivial noreturn functions */
285 if (EDGE_COUNT(EXIT_BLOCK_PTR_FOR_FN(cfun)->preds) == 0)
286 return false;
287
288 list = DECL_ATTRIBUTES(current_function_decl);
289 return lookup_attribute("latent_entropy", list) != NULL_TREE;
290}
291
292static tree create_var(tree type, const char *name)
293{
294 tree var;
295
296 var = create_tmp_var(type, name);
297 add_referenced_var(var);
298 mark_sym_for_renaming(var);
299 return var;
300}
301
302/*
303 * Set up the next operation and its constant operand to use in the latent
304 * entropy PRNG. When RHS is specified, the request is for perturbing the
305 * local latent entropy variable, otherwise it is for perturbing the global
306 * latent entropy variable where the two operands are already given by the
307 * local and global latent entropy variables themselves.
308 *
309 * The operation is one of add/xor/rol when instrumenting the local entropy
310 * variable and one of add/xor when perturbing the global entropy variable.
311 * Rotation is not used for the latter case because it would transmit less
312 * entropy to the global variable than the other two operations.
313 */
314static enum tree_code get_op(tree *rhs)
315{
316 static enum tree_code op;
317 unsigned HOST_WIDE_INT random_const;
318
319 random_const = get_random_const();
320
321 switch (op) {
322 case BIT_XOR_EXPR:
323 op = PLUS_EXPR;
324 break;
325
326 case PLUS_EXPR:
327 if (rhs) {
328 op = LROTATE_EXPR;
329 /*
330 * This code limits the value of random_const to
331 * the size of a wide int for the rotation
332 */
333 random_const &= HOST_BITS_PER_WIDE_INT - 1;
334 break;
335 }
336
337 case LROTATE_EXPR:
338 default:
339 op = BIT_XOR_EXPR;
340 break;
341 }
342 if (rhs)
343 *rhs = build_int_cstu(unsigned_intDI_type_node, random_const);
344 return op;
345}
346
347static gimple create_assign(enum tree_code code, tree lhs, tree op1,
348 tree op2)
349{
350 return gimple_build_assign_with_ops(code, lhs, op1, op2);
351}
352
353static void perturb_local_entropy(basic_block bb, tree local_entropy)
354{
355 gimple_stmt_iterator gsi;
356 gimple assign;
357 tree rhs;
358 enum tree_code op;
359
360 op = get_op(&rhs);
361 assign = create_assign(op, local_entropy, local_entropy, rhs);
362 gsi = gsi_after_labels(bb);
363 gsi_insert_before(&gsi, assign, GSI_NEW_STMT);
364 update_stmt(assign);
365}
366
367static void __perturb_latent_entropy(gimple_stmt_iterator *gsi,
368 tree local_entropy)
369{
370 gimple assign;
371 tree temp;
372 enum tree_code op;
373
374 /* 1. create temporary copy of latent_entropy */
375 temp = create_var(unsigned_intDI_type_node, "tmp_latent_entropy");
376
377 /* 2. read... */
378 add_referenced_var(latent_entropy_decl);
379 mark_sym_for_renaming(latent_entropy_decl);
380 assign = gimple_build_assign(temp, latent_entropy_decl);
381 gsi_insert_before(gsi, assign, GSI_NEW_STMT);
382 update_stmt(assign);
383
384 /* 3. ...modify... */
385 op = get_op(NULL);
386 assign = create_assign(op, temp, temp, local_entropy);
387 gsi_insert_after(gsi, assign, GSI_NEW_STMT);
388 update_stmt(assign);
389
390 /* 4. ...write latent_entropy */
391 assign = gimple_build_assign(latent_entropy_decl, temp);
392 gsi_insert_after(gsi, assign, GSI_NEW_STMT);
393 update_stmt(assign);
394}
395
396static bool handle_tail_calls(basic_block bb, tree local_entropy)
397{
398 gimple_stmt_iterator gsi;
399
400 for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
401 gcall *call;
402 gimple stmt = gsi_stmt(gsi);
403
404 if (!is_gimple_call(stmt))
405 continue;
406
407 call = as_a_gcall(stmt);
408 if (!gimple_call_tail_p(call))
409 continue;
410
411 __perturb_latent_entropy(&gsi, local_entropy);
412 return true;
413 }
414
415 return false;
416}
417
418static void perturb_latent_entropy(tree local_entropy)
419{
420 edge_iterator ei;
421 edge e, last_bb_e;
422 basic_block last_bb;
423
424 gcc_assert(single_pred_p(EXIT_BLOCK_PTR_FOR_FN(cfun)));
425 last_bb_e = single_pred_edge(EXIT_BLOCK_PTR_FOR_FN(cfun));
426
427 FOR_EACH_EDGE(e, ei, last_bb_e->src->preds) {
428 if (ENTRY_BLOCK_PTR_FOR_FN(cfun) == e->src)
429 continue;
430 if (EXIT_BLOCK_PTR_FOR_FN(cfun) == e->src)
431 continue;
432
433 handle_tail_calls(e->src, local_entropy);
434 }
435
436 last_bb = single_pred(EXIT_BLOCK_PTR_FOR_FN(cfun));
437 if (!handle_tail_calls(last_bb, local_entropy)) {
438 gimple_stmt_iterator gsi = gsi_last_bb(last_bb);
439
440 __perturb_latent_entropy(&gsi, local_entropy);
441 }
442}
443
444static void init_local_entropy(basic_block bb, tree local_entropy)
445{
446 gimple assign, call;
447 tree frame_addr, rand_const, tmp, fndecl, udi_frame_addr;
448 enum tree_code op;
449 unsigned HOST_WIDE_INT rand_cst;
450 gimple_stmt_iterator gsi = gsi_after_labels(bb);
451
452 /* 1. create local_entropy_frameaddr */
453 frame_addr = create_var(ptr_type_node, "local_entropy_frameaddr");
454
455 /* 2. local_entropy_frameaddr = __builtin_frame_address() */
456 fndecl = builtin_decl_implicit(BUILT_IN_FRAME_ADDRESS);
457 call = gimple_build_call(fndecl, 1, integer_zero_node);
458 gimple_call_set_lhs(call, frame_addr);
459 gsi_insert_before(&gsi, call, GSI_NEW_STMT);
460 update_stmt(call);
461
462 udi_frame_addr = fold_convert(unsigned_intDI_type_node, frame_addr);
463 assign = gimple_build_assign(local_entropy, udi_frame_addr);
464 gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
465 update_stmt(assign);
466
467 /* 3. create temporary copy of latent_entropy */
468 tmp = create_var(unsigned_intDI_type_node, "tmp_latent_entropy");
469
470 /* 4. read the global entropy variable into local entropy */
471 add_referenced_var(latent_entropy_decl);
472 mark_sym_for_renaming(latent_entropy_decl);
473 assign = gimple_build_assign(tmp, latent_entropy_decl);
474 gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
475 update_stmt(assign);
476
477 /* 5. mix local_entropy_frameaddr into local entropy */
478 assign = create_assign(BIT_XOR_EXPR, local_entropy, local_entropy, tmp);
479 gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
480 update_stmt(assign);
481
482 rand_cst = get_random_const();
483 rand_const = build_int_cstu(unsigned_intDI_type_node, rand_cst);
484 op = get_op(NULL);
485 assign = create_assign(op, local_entropy, local_entropy, rand_const);
486 gsi_insert_after(&gsi, assign, GSI_NEW_STMT);
487 update_stmt(assign);
488}
489
490static bool create_latent_entropy_decl(void)
491{
492 varpool_node_ptr node;
493
494 if (latent_entropy_decl != NULL_TREE)
495 return true;
496
497 FOR_EACH_VARIABLE(node) {
498 tree name, var = NODE_DECL(node);
499
500 if (DECL_NAME_LENGTH(var) < sizeof("latent_entropy") - 1)
501 continue;
502
503 name = DECL_NAME(var);
504 if (strcmp(IDENTIFIER_POINTER(name), "latent_entropy"))
505 continue;
506
507 latent_entropy_decl = var;
508 break;
509 }
510
511 return latent_entropy_decl != NULL_TREE;
512}
513
514static unsigned int latent_entropy_execute(void)
515{
516 basic_block bb;
517 tree local_entropy;
518
519 if (!create_latent_entropy_decl())
520 return 0;
521
522 /* prepare for step 2 below */
523 gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
524 bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
525 if (!single_pred_p(bb)) {
526 split_edge(single_succ_edge(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
527 gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
528 bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
529 }
530
531 /* 1. create the local entropy variable */
532 local_entropy = create_var(unsigned_intDI_type_node, "local_entropy");
533
534 /* 2. initialize the local entropy variable */
535 init_local_entropy(bb, local_entropy);
536
537 bb = bb->next_bb;
538
539 /*
540 * 3. instrument each BB with an operation on the
541 * local entropy variable
542 */
543 while (bb != EXIT_BLOCK_PTR_FOR_FN(cfun)) {
544 perturb_local_entropy(bb, local_entropy);
545 bb = bb->next_bb;
546 };
547
548 /* 4. mix local entropy into the global entropy variable */
549 perturb_latent_entropy(local_entropy);
550 return 0;
551}
552
553static void latent_entropy_start_unit(void *gcc_data __unused,
554 void *user_data __unused)
555{
556 tree type, id;
557 int quals;
558
559 seed = get_random_seed(false);
560
561 if (in_lto_p)
562 return;
563
564 /* extern volatile u64 latent_entropy */
565 gcc_assert(TYPE_PRECISION(long_long_unsigned_type_node) == 64);
566 quals = TYPE_QUALS(long_long_unsigned_type_node) | TYPE_QUAL_VOLATILE;
567 type = build_qualified_type(long_long_unsigned_type_node, quals);
568 id = get_identifier("latent_entropy");
569 latent_entropy_decl = build_decl(UNKNOWN_LOCATION, VAR_DECL, id, type);
570
571 TREE_STATIC(latent_entropy_decl) = 1;
572 TREE_PUBLIC(latent_entropy_decl) = 1;
573 TREE_USED(latent_entropy_decl) = 1;
574 DECL_PRESERVE_P(latent_entropy_decl) = 1;
575 TREE_THIS_VOLATILE(latent_entropy_decl) = 1;
576 DECL_EXTERNAL(latent_entropy_decl) = 1;
577 DECL_ARTIFICIAL(latent_entropy_decl) = 1;
578 lang_hooks.decls.pushdecl(latent_entropy_decl);
579}
580
581#define PASS_NAME latent_entropy
582#define PROPERTIES_REQUIRED PROP_gimple_leh | PROP_cfg
583#define TODO_FLAGS_FINISH TODO_verify_ssa | TODO_verify_stmts | TODO_dump_func \
584 | TODO_update_ssa
585#include "gcc-generate-gimple-pass.h"
586
587int plugin_init(struct plugin_name_args *plugin_info,
588 struct plugin_gcc_version *version)
589{
590 bool enabled = true;
591 const char * const plugin_name = plugin_info->base_name;
592 const int argc = plugin_info->argc;
593 const struct plugin_argument * const argv = plugin_info->argv;
594 int i;
595
596 struct register_pass_info latent_entropy_pass_info;
597
598 latent_entropy_pass_info.pass = make_latent_entropy_pass();
599 latent_entropy_pass_info.reference_pass_name = "optimized";
600 latent_entropy_pass_info.ref_pass_instance_number = 1;
601 latent_entropy_pass_info.pos_op = PASS_POS_INSERT_BEFORE;
602 static const struct ggc_root_tab gt_ggc_r_gt_latent_entropy[] = {
603 {
604 .base = &latent_entropy_decl,
605 .nelt = 1,
606 .stride = sizeof(latent_entropy_decl),
607 .cb = &gt_ggc_mx_tree_node,
608 .pchw = &gt_pch_nx_tree_node
609 },
610 LAST_GGC_ROOT_TAB
611 };
612
613 if (!plugin_default_version_check(version, &gcc_version)) {
614 error(G_("incompatible gcc/plugin versions"));
615 return 1;
616 }
617
618 for (i = 0; i < argc; ++i) {
619 if (!(strcmp(argv[i].key, "disable"))) {
620 enabled = false;
621 continue;
622 }
623 error(G_("unkown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
624 }
625
626 register_callback(plugin_name, PLUGIN_INFO, NULL,
627 &latent_entropy_plugin_info);
628 if (enabled) {
629 register_callback(plugin_name, PLUGIN_START_UNIT,
630 &latent_entropy_start_unit, NULL);
631 register_callback(plugin_name, PLUGIN_REGISTER_GGC_ROOTS,
632 NULL, (void *)&gt_ggc_r_gt_latent_entropy);
633 register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
634 &latent_entropy_pass_info);
635 }
636 register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes,
637 NULL);
638
639 return 0;
640}