Signed-off-by: York Jasper Niebuhr <[email protected]>
---
include/linux/compiler_types.h | 8 ++
include/linux/sched.h | 58 ++++++------
init/main.c | 35 +++++++
kernel/fork.c | 10 +-
kernel/module/main.c | 167 +++++++++++++++++++++++++++++++++
5 files changed, 250 insertions(+), 28 deletions(-)
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index c5921f139007..3ef553fb2489 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -465,6 +465,14 @@ struct ftrace_likely_data {
# define __latent_entropy
#endif
+#if defined(__SPSLR__)
+# define __spslr __attribute__((spslr))
+# define __spslr_field_fixed __attribute__((spslr_field_fixed))
+#else
+# define __spslr
+# define __spslr_field_fixed
+#endif
+
#if defined(RANDSTRUCT) && !defined(__CHECKER__)
# define __randomize_layout __designated_init __attribute__((randomize_layout))
# define __no_randomize_layout __attribute__((no_randomize_layout))
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 373bcc0598d1..8e6a87988d07 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -829,12 +829,12 @@ struct task_struct {
* For reasons of header soup (see current_thread_info()), this
* must be the first element of task_struct.
*/
- struct thread_info thread_info;
+ struct thread_info thread_info __spslr_field_fixed;
#endif
- unsigned int __state;
+ unsigned int __state __spslr_field_fixed;
/* saved state for "spinlock sleepers" */
- unsigned int saved_state;
+ unsigned int saved_state __spslr_field_fixed;
/*
* This begins the randomizable portion of task_struct. Only
@@ -877,12 +877,12 @@ struct task_struct {
int normal_prio;
unsigned int rt_priority;
- struct sched_entity se;
- struct sched_rt_entity rt;
+ struct sched_entity se __spslr_field_fixed;
+ struct sched_rt_entity rt __spslr_field_fixed;
struct sched_dl_entity dl;
struct sched_dl_entity *dl_server;
#ifdef CONFIG_SCHED_CLASS_EXT
- struct sched_ext_entity scx;
+ struct sched_ext_entity scx __spslr_field_fixed;
#endif
const struct sched_class *sched_class;
@@ -919,7 +919,7 @@ struct task_struct {
#ifdef CONFIG_PREEMPT_NOTIFIERS
/* List of struct preempt_notifier: */
- struct hlist_head preempt_notifiers;
+ struct hlist_head preempt_notifiers __spslr_field_fixed;
#endif
#ifdef CONFIG_BLK_DEV_IO_TRACE
@@ -931,15 +931,15 @@ struct task_struct {
int nr_cpus_allowed;
const cpumask_t *cpus_ptr;
cpumask_t *user_cpus_ptr;
- cpumask_t cpus_mask;
+ cpumask_t cpus_mask __spslr_field_fixed;
void *migration_pending;
unsigned short migration_disabled;
unsigned short migration_flags;
#ifdef CONFIG_PREEMPT_RCU
- int rcu_read_lock_nesting;
- union rcu_special rcu_read_unlock_special;
- struct list_head rcu_node_entry;
+ int rcu_read_lock_nesting
__spslr_field_fixed;
+ union rcu_special rcu_read_unlock_special
__spslr_field_fixed;
+ struct list_head rcu_node_entry __spslr_field_fixed;
struct rcu_node *rcu_blocked_node;
#endif /* #ifdef CONFIG_PREEMPT_RCU */
@@ -948,9 +948,9 @@ struct task_struct {
u8 rcu_tasks_holdout;
u8 rcu_tasks_idx;
int rcu_tasks_idle_cpu;
- struct list_head rcu_tasks_holdout_list;
+ struct list_head rcu_tasks_holdout_list
__spslr_field_fixed;
int rcu_tasks_exit_cpu;
- struct list_head rcu_tasks_exit_list;
+ struct list_head rcu_tasks_exit_list __spslr_field_fixed;
#endif /* #ifdef CONFIG_TASKS_RCU */
#ifdef CONFIG_TASKS_TRACE_RCU
@@ -964,8 +964,8 @@ struct task_struct {
struct sched_info sched_info;
- struct list_head tasks;
- struct plist_node pushable_tasks;
+ struct list_head tasks __spslr_field_fixed;
+ struct plist_node pushable_tasks __spslr_field_fixed;
struct rb_node pushable_dl_tasks;
struct mm_struct *mm;
@@ -1072,8 +1072,12 @@ struct task_struct {
pid_t tgid;
#ifdef CONFIG_STACKPROTECTOR
+ /* Canary can not be randomized because of arch/x86/kernel/asm-offsets.c
+ * Pinpoint plugin could recognize context of instrumented accesses
+ * and e.g. hijack asm instructions that want to use them as constants.
+ */
/* Canary value for the -fstack-protector GCC feature: */
- unsigned long stack_canary;
+ unsigned long stack_canary
__spslr_field_fixed;
#endif
/*
* Pointers to the (original) parent process, youngest child, younger
sibling,
@@ -1090,8 +1094,8 @@ struct task_struct {
/*
* Children/sibling form the list of natural children:
*/
- struct list_head children;
- struct list_head sibling;
+ struct list_head children __spslr_field_fixed;
+ struct list_head sibling __spslr_field_fixed;
struct task_struct *group_leader;
/*
@@ -1100,13 +1104,13 @@ struct task_struct {
* This includes both natural children and PTRACE_ATTACH targets.
* 'ptrace_entry' is this task's link on the p->parent->ptraced list.
*/
- struct list_head ptraced;
- struct list_head ptrace_entry;
+ struct list_head ptraced __spslr_field_fixed;
+ struct list_head ptrace_entry __spslr_field_fixed;
/* PID/PID hash table linkage. */
struct pid *thread_pid;
struct hlist_node pid_links[PIDTYPE_MAX];
- struct list_head thread_node;
+ struct list_head thread_node __spslr_field_fixed;
struct completion *vfork_done;
@@ -1211,7 +1215,7 @@ struct task_struct {
sigset_t real_blocked;
/* Restored if set_restore_sigmask() was used: */
sigset_t saved_sigmask;
- struct sigpending pending;
+ struct sigpending pending __spslr_field_fixed;
unsigned long sas_ss_sp;
size_t sas_ss_size;
unsigned int sas_ss_flags;
@@ -1340,7 +1344,7 @@ struct task_struct {
/* Control Group info protected by css_set_lock: */
struct css_set __rcu *cgroups;
/* cg_list protected by css_set_lock and tsk->alloc_lock: */
- struct list_head cg_list;
+ struct list_head cg_list __spslr_field_fixed;
#ifdef CONFIG_PREEMPT_RT
struct llist_node cg_dead_lnode;
#endif /* CONFIG_PREEMPT_RT */
@@ -1355,8 +1359,8 @@ struct task_struct {
#ifdef CONFIG_PERF_EVENTS
u8 perf_recursion[PERF_NR_CONTEXTS];
struct perf_event_context *perf_event_ctxp;
- struct mutex perf_event_mutex;
- struct list_head perf_event_list;
+ struct mutex perf_event_mutex __spslr_field_fixed;
+ struct list_head perf_event_list __spslr_field_fixed;
struct perf_ctx_data __rcu *perf_ctx_data;
#endif
#ifdef CONFIG_DEBUG_PREEMPT
@@ -1660,14 +1664,14 @@ struct task_struct {
#endif
/* CPU-specific state of this task: */
- struct thread_struct thread;
+ struct thread_struct thread __spslr_field_fixed;
/*
* 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
-} __attribute__ ((aligned (64)));
+} __spslr __attribute__ ((aligned (64)));
#ifdef CONFIG_SCHED_PROXY_EXEC
DECLARE_STATIC_KEY_TRUE(__sched_proxy_exec);
diff --git a/init/main.c b/init/main.c
index e363232b428b..fbd2967d1530 100644
--- a/init/main.c
+++ b/init/main.c
@@ -119,6 +119,22 @@
#include <kunit/test.h>
+#include <linux/spslr.h>
+#include <sanemaker/traps.h>
+
+#ifdef CONFIG_SPSLR
+
+bool spslr_enabled __ro_after_init = true;
+
+static int __init nospslr_setup(char *str)
+{
+ spslr_enabled = false;
+ return 0;
+}
+early_param("nospslr", nospslr_setup);
+
+#endif
+
static int kernel_init(void *);
/*
@@ -974,6 +990,8 @@ void start_kernel(void)
char *command_line;
char *after_dashes;
+ sanemaker_target_tag(&init_task, struct task_struct);
+
set_task_stack_end_magic(&init_task);
smp_setup_processor_id();
debug_objects_early_init();
@@ -1023,6 +1041,23 @@ void start_kernel(void)
/* Architectural and non-timekeeping rng init, before allocator init */
random_init_early(command_line);
+#ifdef CONFIG_SPSLR
+ if (spslr_enabled) {
+ /* Randomize structure layouts */
+ struct spslr_status spslr_init_status = spslr_init();
+ if (spslr_init_status.error != SPSLR_OK)
+ panic("SPSLR initialization failed");
+
+ struct spslr_status spslr_selfpatch_status = spslr_selfpatch();
+ if (spslr_selfpatch_status.error != SPSLR_OK)
+ panic("SPSLR selfpatch failed");
+
+ pr_notice("Successfully applied SPSLR\n");
+ } else {
+ pr_notice("SPSLR disabled\n");
+ }
+#endif
+
/*
* These use large bootmem allocations and must precede
* initalization of page allocator
diff --git a/kernel/fork.c b/kernel/fork.c
index f0e2e131a9a5..f07f89d8c55a 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -127,6 +127,9 @@
#include <kunit/visibility.h>
+#include <linux/spslr.h>
+#include <sanemaker/traps.h>
+
/*
* Minimum number of threads to boot the kernel
*/
@@ -185,11 +188,16 @@ static struct kmem_cache *task_struct_cachep;
static inline struct task_struct *alloc_task_struct_node(int node)
{
- return kmem_cache_alloc_node(task_struct_cachep, GFP_KERNEL, node);
+ struct task_struct *tsk =
+ kmem_cache_alloc_node(task_struct_cachep, GFP_KERNEL, node);
+
+ sanemaker_target_tag(tsk, struct task_struct);
+ return tsk;
}
static inline void free_task_struct(struct task_struct *tsk)
{
+ sanemaker_target_untag(tsk);
kmem_cache_free(task_struct_cachep, tsk);
}
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 46dd8d25a605..1f314fc12930 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -66,6 +66,70 @@
#define CREATE_TRACE_POINTS
#include <trace/events/module.h>
+#include <linux/spslr.h>
+#include <sanemaker/traps.h>
+
+/* Sanemaker image (de)registration helpers */
+
+static const void *sanemaker_module_image_base(const struct module *mod)
+{
+ unsigned long base = ULONG_MAX;
+
+ if (mod->mem[MOD_TEXT].base && mod->mem[MOD_TEXT].size)
+ base = min(base,
+ (unsigned long)mod->mem[MOD_TEXT].base);
+
+ if (mod->mem[MOD_INIT_TEXT].base && mod->mem[MOD_INIT_TEXT].size)
+ base = min(base,
+ (unsigned long)mod->mem[MOD_INIT_TEXT].base);
+
+ return base == ULONG_MAX ? NULL : (const void *)base;
+}
+
+static void sanemaker_register_module_image(struct module *mod)
+{
+ const struct module_memory *text;
+ const void *image_base;
+
+ image_base = sanemaker_module_image_base(mod);
+ if (!image_base)
+ return;
+
+ sanemaker_new_image(mod->name, image_base);
+
+ text = &mod->mem[MOD_TEXT];
+ if (text->base && text->size)
+ sanemaker_new_image_text(
+ mod->name,
+ text->base,
+ (const char *)text->base + text->size);
+
+ text = &mod->mem[MOD_INIT_TEXT];
+ if (text->base && text->size)
+ sanemaker_new_image_text(
+ mod->name,
+ text->base,
+ (const char *)text->base + text->size);
+}
+
+static void sanemaker_drop_module_init_text(struct module *mod)
+{
+ const struct module_memory *text = &mod->mem[MOD_INIT_TEXT];
+
+ if (!text->base || !text->size)
+ return;
+
+ sanemaker_drop_image_text(
+ mod->name,
+ text->base,
+ (const char *)text->base + text->size);
+}
+
+static void sanemaker_unregister_module_image(struct module *mod)
+{
+ sanemaker_drop_image(mod->name);
+}
+
/*
* Mutex protects:
* 1) List of modules (also safely readable within RCU read section),
@@ -1461,6 +1525,7 @@ static void free_module(struct module *mod)
kfree(mod->args);
percpu_modfree(mod);
+ sanemaker_unregister_module_image(mod);
free_mod_mem(mod);
}
@@ -3100,10 +3165,22 @@ static noinline int do_init_module(struct module *mod)
freeinit->init_data = mod->mem[MOD_INIT_DATA].base;
freeinit->init_rodata = mod->mem[MOD_INIT_RODATA].base;
+ /*
+ * Constructors and mod->init are the first entry points into module
text.
+ */
+ sanemaker_register_module_image(mod);
+
do_mod_ctors(mod);
/* Start the module */
if (mod->init != NULL)
ret = do_one_initcall(mod->init);
+
+ /*
+ * mod->init() has returned, so no further execution should enter
+ * MOD_INIT_TEXT. This is independent of when the allocation is freed.
+ */
+ sanemaker_drop_module_init_text(mod);
+
if (ret < 0) {
/*
* -EEXIST is reserved by [f]init_module() to signal to
userspace that
@@ -3415,6 +3492,87 @@ static int early_mod_check(struct load_info *info, int
flags)
return err;
}
+#ifdef CONFIG_SPSLR
+
+/* Find spslr symbol in module without kallsym */
+static unsigned long spslr_find_module_symbol(const struct load_info *info,
+ const char *name)
+{
+ Elf_Shdr *symsec = &info->sechdrs[info->index.sym];
+ Elf_Sym *sym = (void *)symsec->sh_addr;
+ unsigned int i, n = symsec->sh_size / sizeof(*sym);
+
+ for (i = 1; i < n; i++) {
+ const char *symname = info->strtab + sym[i].st_name;
+
+ if (strcmp(symname, name) != 0)
+ continue;
+
+ /* Ignore undefined symbols just in case. */
+ if (sym[i].st_shndx == SHN_UNDEF)
+ return 0;
+
+ return (unsigned long)sym[i].st_value;
+ }
+
+ return 0;
+}
+
+/* Apply structure layout randomization to module */
+static int __maybe_unused spslr_prepare_module(struct module *mod,
+ const struct load_info *info)
+{
+ struct spslr_ctx ctx = { };
+ struct spslr_status st;
+ unsigned long workspace_size;
+ int err = 0;
+
+ ctx.entry.start_units = (const void *)spslr_find_module_symbol(info,
+
__stringify(SPSLR_START_UNITS_SYM));
+ if (!ctx.entry.start_units) {
+ pr_err("%s: SPSLR units start symbol missing\n", mod->name);
+ return -ENOEXEC;
+ }
+
+ ctx.entry.stop_units = (const void *)spslr_find_module_symbol(info,
+
__stringify(SPSLR_STOP_UNITS_SYM));
+ if (!ctx.entry.stop_units) {
+ pr_err("%s: SPSLR units stop symbol missing\n", mod->name);
+ return -ENOEXEC;
+ }
+
+ ctx.entry.start_targets = (const void *)spslr_find_module_symbol(info,
+
__stringify(SPSLR_START_TARGETS_SYM));
+ if (!ctx.entry.start_targets) {
+ pr_err("%s: SPSLR targets start symbol missing\n", mod->name);
+ return -ENOEXEC;
+ }
+
+ ctx.entry.stop_targets = (const void *)spslr_find_module_symbol(info,
+
__stringify(SPSLR_STOP_TARGETS_SYM));
+ if (!ctx.entry.stop_targets) {
+ pr_err("%s: SPSLR targets stop symbol missing\n", mod->name);
+ return -ENOEXEC;
+ }
+
+ workspace_size = spslr_workspace_size(&ctx.entry);
+ ctx.workspace = kvmalloc(workspace_size, GFP_KERNEL);
+ if (!ctx.workspace)
+ return -ENOMEM;
+
+ st = spslr_patch_module(&ctx);
+ if (st.viability != SPSLR_VIABLE || st.error != SPSLR_OK) {
+ pr_err("%s: SPSLR patch failed: viability=%d error=%d\n",
+ mod->name, st.viability, st.error);
+ err = -ENOEXEC;
+ }
+
+ kvfree(ctx.workspace);
+ return err;
+}
+
+#endif /* CONFIG_SPSLR */
+
/*
* Allocate and load the module: note that size of section 0 is always
* zero, and we rely on this for optional sections.
@@ -3520,6 +3678,15 @@ static int load_module(struct load_info *info, const
char __user *uargs,
if (err < 0)
goto free_modinfo;
+#ifdef CONFIG_SPSLR
+ if (spslr_enabled) {
+ /* SPSLR must happen after relocation and icache should be
flushed afterwards */
+ err = spslr_prepare_module(mod, info);
+ if (err < 0)
+ goto free_modinfo;
+ }
+#endif
+
flush_module_icache(mod);
/* Now copy in args */
--
2.43.0