Signed-off-by: York Jasper Niebuhr <[email protected]>
---
 include/linux/spslr.h           |  82 ++++
 include/sanemaker/traps.h       | 135 +++++++
 kernel/Makefile                 |   2 +
 kernel/spslr/Makefile           |   7 +
 kernel/spslr/pinpoint.h         |  76 ++++
 kernel/spslr/sanemaker_traps.c  | 117 ++++++
 kernel/spslr/spslr.c            | 555 +++++++++++++++++++++++++++
 kernel/spslr/spslr_env.c        |  74 ++++
 kernel/spslr/spslr_env.h        |  45 +++
 kernel/spslr/spslr_randomizer.c | 646 ++++++++++++++++++++++++++++++++
 kernel/spslr/spslr_randomizer.h |  29 ++
 11 files changed, 1768 insertions(+)
 create mode 100644 include/linux/spslr.h
 create mode 100644 include/sanemaker/traps.h
 create mode 100644 kernel/spslr/Makefile
 create mode 100644 kernel/spslr/pinpoint.h
 create mode 100644 kernel/spslr/sanemaker_traps.c
 create mode 100644 kernel/spslr/spslr.c
 create mode 100644 kernel/spslr/spslr_env.c
 create mode 100644 kernel/spslr/spslr_env.h
 create mode 100644 kernel/spslr/spslr_randomizer.c
 create mode 100644 kernel/spslr/spslr_randomizer.h

diff --git a/include/linux/spslr.h b/include/linux/spslr.h
new file mode 100644
index 000000000000..2606d66c39f0
--- /dev/null
+++ b/include/linux/spslr.h
@@ -0,0 +1,82 @@
+#ifndef SPSLR_SELFPATCH_H
+#define SPSLR_SELFPATCH_H
+
+#ifdef CONFIG_SPSLR
+
+#include <linux/types.h>
+
+#define SPSLR_START_UNITS_SYM __start_spslr_units
+#define SPSLR_STOP_UNITS_SYM __stop_spslr_units
+#define SPSLR_START_TARGETS_SYM __start_spslr_targets
+#define SPSLR_STOP_TARGETS_SYM __stop_spslr_targets
+
+extern bool spslr_enabled;
+
+enum spslr_viability { SPSLR_VIABLE, SPSLR_NONVIABLE };
+
+enum spslr_error {
+       SPSLR_OK,
+       SPSLR_ERROR_INCOMPLETE_CTX,
+       SPSLR_ERROR_INCOMPATIBLE_CTX,
+       SPSLR_ERROR_RANDOMIZER_INIT,
+       SPSLR_ERROR_INITIAL_TARGET_LAYOUT,
+       SPSLR_ERROR_RANDOMIZED_TARGET_LAYOUT,
+       SPSLR_ERROR_RANDOMIZE,
+       SPSLR_ERROR_MEMORY,
+       SPSLR_ERROR_UNINITIALIZED,
+       SPSLR_ERROR_ALREADY_PATCHED,
+       SPSLR_ERROR_PATCH_DPINS,
+       SPSLR_ERROR_PATCH_IPINS,
+       SPSLR_ERROR_MAP_TARGETS
+};
+
+struct spslr_status {
+       enum spslr_viability viability;
+       enum spslr_error error;
+};
+
+struct spslr_entry {
+       const void *start_units; // Address of SPSLR_START_UNITS_SYM
+       const void *stop_units; // Address of SPSLR_STOP_UNITS_SYM
+       const void *start_targets; // Address of SPSLR_START_TARGETS_SYM
+       const void *stop_targets; // Address of SPSLR_STOP_TARGETS_SYM
+};
+
+struct spslr_ctx {
+       struct spslr_entry entry;
+       void *workspace; // Temporary buffer of spslr_workspace_size(&entry) 
bytes
+};
+
+/*
+ * Runtime entry points are intentionally split:
+ *
+ * spslr_init() creates randomized layouts.
+ * spslr_selfpatch() patches the main executable.
+ * spslr_patch_module() patches with module-local metadata using host layouts.
+ */
+
+struct spslr_status spslr_init(void);
+struct spslr_status spslr_selfpatch(void);
+unsigned long spslr_workspace_size(const struct spslr_entry *entry);
+struct spslr_status spslr_patch_module(const struct spslr_ctx *m);
+
+/* Use spslr_target_hash(type) to get a pointer to the 16 byte md5 hash
+   of the target type. If type is not an SPSLR target, NULL is returned. */
+
+extern const unsigned char *__spslr_target_hash(const void *);
+
+#define __SPSLR_CAT2(a, b) a##b
+#define __SPSLR_CAT(a, b) __SPSLR_CAT2(a, b)
+
+#define __spslr_target_hash_impl(T, n)                                      \
+       ({                                                                  \
+               extern T __SPSLR_CAT(__spslr_target_hash_type_anchor_, n);  \
+               __spslr_target_hash(                                        \
+                       &__SPSLR_CAT(__spslr_target_hash_type_anchor_, n)); \
+       })
+
+#define spslr_target_hash(T) __spslr_target_hash_impl(T, __COUNTER__)
+
+#endif /* CONFIG_SPSLR */
+
+#endif
diff --git a/include/sanemaker/traps.h b/include/sanemaker/traps.h
new file mode 100644
index 000000000000..bcb75198b18b
--- /dev/null
+++ b/include/sanemaker/traps.h
@@ -0,0 +1,135 @@
+#ifndef SANEMAKER_TRAPS_H
+#define SANEMAKER_TRAPS_H
+
+/* Define trap API attributes */
+
+#define SANEMAKER_TRAP_API extern __attribute__((__visibility__("default")))
+
+/* Use sanemaker_target_tag(&obj) to make sanemaker watch memops to that 
object */
+
+#ifdef CONFIG_SANEMAKER
+
+SANEMAKER_TRAP_API
+void __sanemaker_target_tag_trap(const void *ptr, const unsigned char *target);
+
+#define sanemaker_target_tag(ptr, type) \
+       __sanemaker_target_tag_trap(ptr, spslr_target_hash(type))
+
+#else
+
+#define sanemaker_target_tag(ptr, type)
+
+#endif
+
+/* Use sanemaker_target_untag(&obj) to make sanemaker stop watching memops to 
that object */
+
+#ifdef CONFIG_SANEMAKER
+
+SANEMAKER_TRAP_API
+void __sanemaker_target_untag_trap(const void *ptr);
+
+#define sanemaker_target_untag(ptr) __sanemaker_target_untag_trap(ptr)
+
+#else
+
+#define sanemaker_target_untag(ptr)
+
+#endif
+
+/* The sanemaker_finish_layout(&fieldarr, &target_hash) should be called
+   by spslr selfpatch when a target layout has been randomized */
+
+#ifdef CONFIG_SANEMAKER
+
+SANEMAKER_TRAP_API
+void __sanemaker_finish_layout_trap(const void *fields,
+                                   const unsigned char *target);
+
+#define sanemaker_finish_layout(fields, target) \
+       __sanemaker_finish_layout_trap(fields, target)
+
+#else
+
+#define sanemaker_finish_layout(fields, target)
+
+#endif
+
+/* Use sanemaker_fetch(what, default) to let sanemaker make decisions at 
runtime */
+
+typedef enum {
+       SANEMAKER_FETCH_SPSLR_ENABLED = 1,
+} sanemaker_fetch_t;
+
+#ifdef CONFIG_SANEMAKER
+
+SANEMAKER_TRAP_API
+int __sanemaker_fetch_trap(sanemaker_fetch_t what, int def);
+
+#define sanemaker_fetch(what, def) __sanemaker_fetch_trap(what, def)
+
+#else
+
+#define sanemaker_fetch(what, def) (def)
+
+#endif
+
+/* Use sanemaker_signal(signal) to control sanemaker behavior */
+
+typedef enum {
+       SANEMAKER_SIGNAL_PATCH_BOUNDARY = 1, /* the image has been patched */
+       SANEMAKER_SIGNAL_PAUSE = 2,
+       SANEMAKER_SIGNAL_RESUME = 3,
+} sanemaker_signal_t;
+
+#ifdef CONFIG_SANEMAKER
+
+SANEMAKER_TRAP_API
+void __sanemaker_signal_trap(sanemaker_signal_t signal);
+
+#define sanemaker_signal(signal) __sanemaker_signal_trap(signal)
+
+#else
+
+#define sanemaker_signal(signal)
+
+#endif
+
+/* Use sanemaker_new_image(name, ptr) and sanemaker_new_image_text(image, 
begin, end)
+   to allow normalization of program counters inside dynamically loaded text 
segments */
+
+#ifdef CONFIG_SANEMAKER
+
+SANEMAKER_TRAP_API
+void __sanemaker_new_image_trap(const char *name, const void *base);
+
+SANEMAKER_TRAP_API
+void __sanemaker_new_image_text_trap(const char *image, const void *begin,
+                                    const void *end);
+
+SANEMAKER_TRAP_API
+void __sanemaker_drop_image_trap(const char *image);
+
+SANEMAKER_TRAP_API
+void __sanemaker_drop_image_text_trap(const char *image, const void *begin,
+                                     const void *end);
+
+#define sanemaker_new_image(name, base) __sanemaker_new_image_trap(name, base)
+
+#define sanemaker_new_image_text(image, begin, end) \
+       __sanemaker_new_image_text_trap(image, begin, end)
+
+#define sanemaker_drop_image(image) __sanemaker_drop_image_trap(image)
+
+#define sanemaker_drop_image_text(image, begin, end) \
+       __sanemaker_drop_image_text_trap(image, begin, end)
+
+#else
+
+#define sanemaker_new_image(name, base)
+#define sanemaker_new_image_text(image, begin, end)
+#define sanemaker_drop_image(image)
+#define sanemaker_drop_image_text(image, begin, end)
+
+#endif
+
+#endif
diff --git a/kernel/Makefile b/kernel/Makefile
index 1e1a31673577..cddfbff9f9f0 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -142,6 +142,8 @@ obj-$(CONFIG_WATCH_QUEUE) += watch_queue.o
 obj-$(CONFIG_RESOURCE_KUNIT_TEST) += resource_kunit.o
 obj-$(CONFIG_SYSCTL_KUNIT_TEST) += sysctl-test.o
 
+obj-$(CONFIG_SPSLR) += spslr/
+
 CFLAGS_kstack_erase.o += $(DISABLE_KSTACK_ERASE)
 CFLAGS_kstack_erase.o += $(call cc-option,-mgeneral-regs-only)
 obj-$(CONFIG_KSTACK_ERASE) += kstack_erase.o
diff --git a/kernel/spslr/Makefile b/kernel/spslr/Makefile
new file mode 100644
index 000000000000..a736d0aab05c
--- /dev/null
+++ b/kernel/spslr/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_SPSLR) += spslr.o spslr_env.o spslr_randomizer.o
+obj-$(CONFIG_SANEMAKER) += sanemaker_traps.o
+
+CFLAGS_REMOVE_spslr.o += $(PINPOINT_PLUGIN_CFLAGS)
+CFLAGS_REMOVE_spslr_env.o += $(PINPOINT_PLUGIN_CFLAGS)
+CFLAGS_REMOVE_spslr_randomizer.o += $(PINPOINT_PLUGIN_CFLAGS)
+CFLAGS_REMOVE_sanemaker_traps.o += $(PINPOINT_PLUGIN_CFLAGS)
diff --git a/kernel/spslr/pinpoint.h b/kernel/spslr/pinpoint.h
new file mode 100644
index 000000000000..39ca81e6491d
--- /dev/null
+++ b/kernel/spslr/pinpoint.h
@@ -0,0 +1,76 @@
+#ifndef SPSLR_PINPOINT_H
+#define SPSLR_PINPOINT_H
+
+#include "spslr_env.h"
+
+/* Field must remain at its original offset during layout randomization. */
+#define SPSLR_FLAG_FIELD_FIXED 1
+
+struct spslr_unit;
+struct spslr_ipin;
+struct spslr_ipin_expr;
+struct spslr_dpin;
+struct spslr_target;
+struct spslr_target_layout;
+struct spslr_target_field;
+
+/* CU-local target reference; points into the global deduplicated target 
table. */
+typedef const struct spslr_target *spslr_target_ref;
+
+/*
+ * Metadata for one compilation unit. The target array is CU-local and maps
+ * unit_target_idx values used by pins to deduplicated global target headers.
+ */
+struct spslr_unit {
+       const char *source; // Source file name
+       spslr_u64 target_cnt;
+       const spslr_target_ref *target_refs; // CU-local target ref array
+       spslr_u64 ipin_cnt;
+       const struct spslr_ipin *ipins;
+       spslr_u64 dpin_cnt;
+       const struct spslr_dpin *dpins;
+} __packed;
+
+/* Instruction patch site: address of patchable immediate/displacement bytes. 
*/
+struct spslr_ipin {
+       void *addr;
+       spslr_u64 size;
+       const struct spslr_ipin_expr *expr;
+} __packed;
+
+/* Data patch site: address of an object/subobject whose layout must be 
adjusted. */
+struct spslr_dpin {
+       void *addr;
+       spslr_u64 unit_target_idx;
+} __packed;
+
+/* Current simple expression: randomized offset of one field in one CU-local 
target. */
+struct spslr_ipin_expr {
+       spslr_u64 unit_target_idx;
+       spslr_u64 field_idx;
+} __packed;
+
+/* Deduplicated target type descriptor, keyed by deterministic layout hash. */
+struct spslr_target {
+       unsigned char hash[16];
+       const char *name;
+       const struct spslr_target_layout *layout;
+} __packed;
+
+/* Physical layout of a target type before runtime randomization. */
+struct spslr_target_layout {
+       spslr_u64 size;
+       spslr_u64 field_cnt;
+       const struct spslr_target_field *fields;
+} __packed;
+
+/* One randomizable or fixed field/range within a target layout. */
+struct spslr_target_field {
+       const char *name;
+       spslr_u64 size;
+       spslr_u64 offset;
+       spslr_u64 alignment;
+       spslr_u64 flags;
+} __packed;
+
+#endif
diff --git a/kernel/spslr/sanemaker_traps.c b/kernel/spslr/sanemaker_traps.c
new file mode 100644
index 000000000000..63aa1a4b9182
--- /dev/null
+++ b/kernel/spslr/sanemaker_traps.c
@@ -0,0 +1,117 @@
+#include <sanemaker/traps.h>
+
+#define SANEMAKER_TRAP_FN                                      \
+       __attribute__((__noinline__, __noclone__, __used__,     \
+               __externally_visible__,                         \
+               __visibility__("default"), __naked__))
+
+/* Sanemaker reads object pointer from rdi and target hash pointer from rsi */
+SANEMAKER_TRAP_FN
+void __sanemaker_target_tag_trap(const void *ptr, const unsigned char *target)
+{
+       __asm__ volatile(
+               ".globl __sanemaker_target_tag_trap_incision\n"
+               ".type __sanemaker_target_tag_trap_incision, @notype\n"
+               "__sanemaker_target_tag_trap_incision:\n"
+               "ret\n"
+       );
+}
+
+/* Sanemaker reads object pointer from rdi */
+SANEMAKER_TRAP_FN
+void __sanemaker_target_untag_trap(const void *ptr)
+{
+       __asm__ volatile(
+               ".globl __sanemaker_target_untag_trap_incision\n"
+               ".type __sanemaker_target_untag_trap_incision, @notype\n"
+               "__sanemaker_target_untag_trap_incision:\n"
+               "ret\n"
+       );
+}
+
+/* Sanemaker reads field pointer from rdi and target hash pointer from rsi */
+SANEMAKER_TRAP_FN
+void __sanemaker_finish_layout_trap(const void *fields, const unsigned char 
*target)
+{
+       __asm__ volatile(
+               ".globl __sanemaker_finish_layout_trap_incision\n"
+               ".type __sanemaker_finish_layout_trap_incision, @notype\n"
+               "__sanemaker_finish_layout_trap_incision:\n"
+               "ret\n"
+       );
+}
+
+/* Sanemaker reads name from rdi and overwrites rsi to set a value */
+SANEMAKER_TRAP_FN
+int __sanemaker_fetch_trap(sanemaker_fetch_t what, int def)
+{
+       __asm__ volatile(
+               ".globl __sanemaker_fetch_trap_incision\n"
+               ".type __sanemaker_fetch_trap_incision, @notype\n"
+               "__sanemaker_fetch_trap_incision:\n"
+               "nop\n"
+               "movl %esi, %eax\n"
+               "ret\n"
+       );
+}
+
+/* Sanemaker reads the signal event from rdi */
+SANEMAKER_TRAP_FN
+void __sanemaker_signal_trap(sanemaker_signal_t signal)
+{
+       __asm__ volatile(
+               ".globl __sanemaker_signal_trap_incision\n"
+               ".type __sanemaker_signal_trap_incision, @notype\n"
+               "__sanemaker_signal_trap_incision:\n"
+               "ret\n"
+       );
+}
+
+/* Sanemaker reads name from rdi and base from rsi */
+SANEMAKER_TRAP_FN
+void __sanemaker_new_image_trap(const char *name, const void *base)
+{
+       __asm__ volatile(
+               ".globl __sanemaker_new_image_trap_incision\n"
+               ".type __sanemaker_new_image_trap_incision, @notype\n"
+               "__sanemaker_new_image_trap_incision:\n"
+               "ret\n"
+       );
+}
+
+/* Sanemaker reads image name from rdi, begin from rsi and end from rdx */
+SANEMAKER_TRAP_FN
+void __sanemaker_new_image_text_trap(const char *image, const void *begin, 
const void *end)
+{
+       __asm__ volatile(
+               ".globl __sanemaker_new_image_text_trap_incision\n"
+               ".type __sanemaker_new_image_text_trap_incision, @notype\n"
+               "__sanemaker_new_image_text_trap_incision:\n"
+               "ret\n"
+       );
+}
+
+/* Sanemaker reads image name from rdi */
+SANEMAKER_TRAP_FN
+void __sanemaker_drop_image_trap(const char *image)
+{
+       __asm__ volatile(
+               ".globl __sanemaker_drop_image_trap_incision\n"
+               ".type __sanemaker_drop_image_trap_incision, @notype\n"
+               "__sanemaker_drop_image_trap_incision:\n"
+               "ret\n"
+       );
+}
+
+/* Sanemaker reads image name from rdi, begin from rsi and end from rdx */
+SANEMAKER_TRAP_FN
+void __sanemaker_drop_image_text_trap(const char *image, const void *begin, 
const void *end)
+{
+       __asm__ volatile(
+               ".globl __sanemaker_drop_image_text_trap_incision\n"
+               ".type __sanemaker_drop_image_text_trap_incision, @notype\n"
+               "__sanemaker_drop_image_text_trap_incision:\n"
+               "ret\n"
+       );
+}
+
diff --git a/kernel/spslr/spslr.c b/kernel/spslr/spslr.c
new file mode 100644
index 000000000000..5ef36731f75e
--- /dev/null
+++ b/kernel/spslr/spslr.c
@@ -0,0 +1,555 @@
+#include <linux/spslr.h>
+#include <sanemaker/traps.h>
+
+#include "spslr_randomizer.h"
+#include "spslr_env.h"
+#include "pinpoint.h"
+
+/*
+ * Runtime portion of SPSLR.
+ *
+ * This code consumes the metadata emitted by pinpoint, randomizes target
+ * layouts, rewrites static data objects, and patches instruction immediates
+ * that encode structure field offsets.
+ */
+
+#define SPSLR_SANITY_CHECK
+
+struct target_map {
+       spslr_u64 *map;
+       spslr_u64 size;
+};
+
+static void init_spslr_meta(void);
+static int spslr_targets_compatible(const struct spslr_target *begin,
+                                   const struct spslr_target *end);
+static enum spslr_error spslr_patch_unit(const struct spslr_unit *unit,
+                                        spslr_u64 *tmap_buffer,
+                                        void *reorder_buffer);
+static struct spslr_status spslr_patch(const struct spslr_ctx *ctx);
+static spslr_u64 spslr_target_mapping_size(void);
+static spslr_u64 *workspace_target_mapping(void *workspace);
+static void *workspace_reorder_buffer(void *workspace);
+
+static int spslr_patch_dpins(const struct spslr_dpin *dpins, spslr_u64 cnt,
+                            const struct target_map *tmap,
+                            void *reorder_buffer);
+static int spslr_patch_dpin(void *addr, spslr_u64 target, void 
*reorder_buffer);
+static int spslr_patch_ipins(const struct spslr_ipin *ipins, spslr_u64 cnt,
+                            const struct target_map *tmap);
+
+static int reorder_object(void *dst, const void *src, spslr_u64 target);
+static int spslr_calculate_ipin_value(const struct spslr_ipin_expr *expr,
+                                     spslr_s64 *res,
+                                     const struct target_map *tmap);
+
+static int spslr_map_target(const struct spslr_target *target, spslr_u64 *idx);
+static int spslr_map_targets(const struct spslr_unit *unit,
+                            struct target_map *tmap);
+
+static int initialized = 0, patched = 0;
+static enum spslr_viability viable = SPSLR_VIABLE;
+
+spslr_u64 spslr_target_cnt = 0;
+const struct spslr_target *spslr_targets = NULL;
+
+/* Host image spslr metadata entry point */
+extern const struct spslr_unit SPSLR_START_UNITS_SYM[];
+extern const struct spslr_unit SPSLR_STOP_UNITS_SYM[];
+extern const struct spslr_target SPSLR_START_TARGETS_SYM[];
+extern const struct spslr_target SPSLR_STOP_TARGETS_SYM[];
+
+/*
+ * Initialize runtime randomization state.
+ *
+ * After this point target layouts are randomized, but code/data does still
+ * contain original-layout offsets until the patching entry points run.
+ */
+
+static void __init init_spslr_meta(void)
+{
+       spslr_target_cnt =
+               (spslr_u64)(SPSLR_STOP_TARGETS_SYM - SPSLR_START_TARGETS_SYM);
+       spslr_targets = SPSLR_START_TARGETS_SYM;
+}
+
+struct spslr_status __init spslr_init(void)
+{
+       if (initialized)
+               return (struct spslr_status){ .viability = viable,
+                                             .error = SPSLR_OK };
+
+       init_spslr_meta();
+
+       if (spslr_randomizer_init() < 0)
+               return (struct spslr_status){
+                       .viability = viable,
+                       .error = SPSLR_ERROR_RANDOMIZER_INIT
+               };
+
+#ifdef SPSLR_SANITY_CHECK
+       for (spslr_u64 tidx = 0; tidx < spslr_target_cnt; tidx++) {
+               if (spslr_randomizer_validate_target(tidx) < 0)
+                       return (struct spslr_status){
+                               .viability = viable,
+                               .error = SPSLR_ERROR_INITIAL_TARGET_LAYOUT
+                       };
+       }
+#endif
+
+       if (sanemaker_fetch(SANEMAKER_FETCH_SPSLR_ENABLED, 1)) {
+               if (spslr_randomize() < 0)
+                       return (struct spslr_status){
+                               .viability = viable,
+                               .error = SPSLR_ERROR_RANDOMIZE
+                       };
+       }
+
+#ifdef SPSLR_SANITY_CHECK
+       for (spslr_u64 tidx = 0; tidx < spslr_target_cnt; tidx++) {
+               if (spslr_randomizer_validate_target(tidx) < 0)
+                       return (struct spslr_status){
+                               .viability = viable,
+                               .error = SPSLR_ERROR_RANDOMIZED_TARGET_LAYOUT
+                       };
+       }
+#endif
+
+       initialized = 1;
+       return (struct spslr_status){ .viability = viable, .error = SPSLR_OK };
+}
+
+/*
+ * Calculate required workspace buffer size. This includes the reorder
+ * buffer for data pins and the storage for the mapping of local to
+ * global target indices.
+ */
+
+static spslr_u64 spslr_target_mapping_size(void)
+{
+       return spslr_target_cnt * sizeof(spslr_u64);
+}
+
+unsigned long spslr_workspace_size(const struct spslr_entry *entry)
+{
+       if (!entry || !entry->start_units || !entry->stop_units)
+               return 0;
+
+       const struct spslr_unit *start_units =
+               (const struct spslr_unit *)entry->start_units;
+       const struct spslr_unit *stop_units =
+               (const struct spslr_unit *)entry->stop_units;
+
+       spslr_u64 max_dpin_size = 0;
+       for (const struct spslr_unit *unit = start_units; unit != stop_units;
+            unit++) {
+               for (spslr_u64 dpin = 0; dpin < unit->dpin_cnt; dpin++) {
+                       const struct spslr_target *target =
+                               unit->target_refs[unit->dpins[dpin]
+                                                         .unit_target_idx];
+
+                       if (target->layout->size > max_dpin_size)
+                               max_dpin_size = target->layout->size;
+               }
+       }
+
+       return spslr_target_mapping_size() + max_dpin_size;
+}
+
+/*
+ * Check if the given target space is compatible with that of the
+ * host.
+ */
+
+static int spslr_meta_known_target(const struct spslr_target *t)
+{
+       for (spslr_u64 i = 0; i < spslr_target_cnt; i++) {
+               if (spslr_env_memcmp(t->hash, spslr_targets[i].hash,
+                                    sizeof(t->hash)) == 0)
+                       return 1;
+       }
+
+       return 0;
+}
+
+static int spslr_targets_compatible(const struct spslr_target *begin,
+                                   const struct spslr_target *end)
+{
+       spslr_u64 cnt = (spslr_u64)(end - begin);
+       if (cnt > spslr_target_cnt)
+               return 0;
+
+       for (spslr_u64 i = 0; i < cnt; i++) {
+               if (!spslr_meta_known_target(begin + i))
+                       return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * For each CU, map local target indices to global target indices and then
+ * to host indices. Afterwards, patch ipins and dpins.
+ */
+
+static spslr_u64 *workspace_target_mapping(void *workspace)
+{
+       return (spslr_u64 *)workspace;
+}
+
+static void *workspace_reorder_buffer(void *workspace)
+{
+       return (spslr_u8 *)workspace + spslr_target_mapping_size();
+}
+
+static int spslr_map_target(const struct spslr_target *target, spslr_u64 *idx)
+{
+       for (spslr_u64 i = 0; i < spslr_target_cnt; i++) {
+               if (spslr_env_memcmp(target->hash, spslr_targets[i].hash,
+                                    sizeof(target->hash)) == 0) {
+                       *idx = i;
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+static int spslr_map_targets(const struct spslr_unit *unit,
+                            struct target_map *tmap)
+{
+       tmap->size = 0;
+
+       for (spslr_u64 i = 0; i < unit->target_cnt; i++) {
+               if (spslr_map_target(unit->target_refs[i], tmap->map + i) < 0)
+                       return -1;
+       }
+
+       tmap->size = unit->target_cnt;
+       return 0;
+}
+
+static enum spslr_error spslr_patch_unit(const struct spslr_unit *unit,
+                                        spslr_u64 *tmap_buffer,
+                                        void *reorder_buffer)
+{
+       struct target_map tmap = { .map = tmap_buffer, .size = 0 };
+
+       if (spslr_map_targets(unit, &tmap) < 0)
+               return SPSLR_ERROR_MAP_TARGETS;
+
+       if (spslr_patch_dpins(unit->dpins, unit->dpin_cnt, &tmap,
+                             reorder_buffer) < 0)
+               return SPSLR_ERROR_PATCH_DPINS;
+
+       if (spslr_patch_ipins(unit->ipins, unit->ipin_cnt, &tmap) < 0)
+               return SPSLR_ERROR_PATCH_IPINS;
+
+       return SPSLR_OK;
+}
+
+static struct spslr_status spslr_patch(const struct spslr_ctx *ctx)
+{
+       enum spslr_error err = SPSLR_OK;
+       enum spslr_viability via = SPSLR_VIABLE;
+
+       spslr_u64 *target_map_buffer = NULL;
+       void *reorder_buffer = NULL;
+
+       const struct spslr_unit *start_units = NULL;
+       const struct spslr_unit *stop_units = NULL;
+       const struct spslr_target *start_targets = NULL;
+       const struct spslr_target *stop_targets = NULL;
+
+       if (!ctx || !ctx->entry.start_units || !ctx->entry.stop_units ||
+           !ctx->entry.start_targets || !ctx->entry.stop_targets ||
+           !ctx->workspace) {
+               err = SPSLR_ERROR_INCOMPLETE_CTX;
+               goto finish;
+       }
+
+       start_units = (const struct spslr_unit *)ctx->entry.start_units;
+       stop_units = (const struct spslr_unit *)ctx->entry.stop_units;
+       start_targets = (const struct spslr_target *)ctx->entry.start_targets;
+       stop_targets = (const struct spslr_target *)ctx->entry.stop_targets;
+
+       target_map_buffer = workspace_target_mapping(ctx->workspace);
+       reorder_buffer = workspace_reorder_buffer(ctx->workspace);
+
+       if (!spslr_targets_compatible(start_targets, stop_targets)) {
+               err = SPSLR_ERROR_INCOMPATIBLE_CTX;
+               goto finish;
+       }
+
+       via = SPSLR_NONVIABLE;
+
+       if (sanemaker_fetch(SANEMAKER_FETCH_SPSLR_ENABLED, 1)) {
+               for (const struct spslr_unit *unit = start_units;
+                    unit != stop_units; unit++) {
+                       err = spslr_patch_unit(unit, target_map_buffer,
+                                              reorder_buffer);
+                       if (err != SPSLR_OK)
+                               goto finish;
+               }
+       }
+
+       via = SPSLR_VIABLE;
+
+finish:
+       return (struct spslr_status){ .viability = via, .error = err };
+}
+
+/*
+ * Patch the main executable.
+ *
+ * Instruction pins rewrite immediate operands in text, while data pins rewrite
+ * existing static objects from original layout into randomized layout.
+ */
+
+struct spslr_status __init spslr_selfpatch(void)
+{
+       enum spslr_error err = SPSLR_OK;
+
+       spslr_u64 host_workspace_size;
+
+       struct spslr_ctx host_ctx;
+       host_ctx.entry.start_units = SPSLR_START_UNITS_SYM;
+       host_ctx.entry.stop_units = SPSLR_STOP_UNITS_SYM;
+       host_ctx.entry.start_targets = SPSLR_START_TARGETS_SYM;
+       host_ctx.entry.stop_targets = SPSLR_STOP_TARGETS_SYM;
+       host_ctx.workspace = NULL;
+
+       struct spslr_status internal_patch_status;
+
+       if (patched) {
+               err = SPSLR_ERROR_ALREADY_PATCHED;
+               goto finish;
+       }
+
+       if (!initialized) {
+               err = SPSLR_ERROR_UNINITIALIZED;
+               goto finish;
+       }
+
+       host_workspace_size = spslr_workspace_size(&host_ctx.entry);
+       host_ctx.workspace = spslr_env_malloc(host_workspace_size);
+
+       if (!host_ctx.workspace) {
+               err = SPSLR_ERROR_MEMORY;
+               goto finish;
+       }
+
+       internal_patch_status = spslr_patch(&host_ctx);
+       if (internal_patch_status.error == SPSLR_OK)
+               patched = 1;
+
+       viable = internal_patch_status.viability;
+       err = internal_patch_status.error;
+
+finish:
+       if (host_ctx.workspace)
+               spslr_env_free(host_ctx.workspace, host_workspace_size);
+
+       sanemaker_signal(SANEMAKER_SIGNAL_PATCH_BOUNDARY);
+       return (struct spslr_status){ .viability = viable, .error = err };
+}
+
+/*
+ * Patch metadata belonging to a separately loaded module.
+ *
+ * Modules reuse the target randomization state created by the main executable;
+ * they contribute only their own instruction and data patch sites.
+ */
+
+struct spslr_status spslr_patch_module(const struct spslr_ctx *m)
+{
+       if (!initialized)
+               return (struct spslr_status){
+                       .viability = SPSLR_VIABLE,
+                       .error = SPSLR_ERROR_UNINITIALIZED
+               };
+
+       if (!m || !m->entry.start_units || !m->entry.stop_units ||
+           !m->entry.start_targets || !m->entry.stop_targets || !m->workspace)
+               return (struct spslr_status){
+                       .viability = SPSLR_VIABLE,
+                       .error = SPSLR_ERROR_INCOMPLETE_CTX
+               };
+
+       struct spslr_status s = spslr_patch(m);
+       return (struct spslr_status){ .viability = (s.error == SPSLR_OK ?
+                                                           SPSLR_VIABLE :
+                                                           SPSLR_NONVIABLE),
+                                     .error = s.error };
+}
+
+/*
+ * Rewrite one object instance from original layout into randomized layout.
+ *
+ * A temporary buffer is used so overlapping source/destination field ranges do
+ * not corrupt data while fields are moved.
+ */
+
+static int reorder_object(void *dst, const void *src, spslr_u64 target)
+{
+       spslr_u64 field_count;
+       if (spslr_randomizer_get_target(target, NULL, &field_count))
+               return -1;
+
+       const spslr_u8 *src_countable = (const spslr_u8 *)src;
+       spslr_u8 *dst_countable = (spslr_u8 *)dst;
+
+       for (spslr_u64 i = 0; i < field_count; i++) {
+               struct spslr_randomizer_field_info finfo;
+               if (spslr_randomizer_get_field(
+                           target, i, SPSLR_RANDOMIZER_FIELD_IDX_MODE_FINAL,
+                           &finfo))
+                       return -1;
+
+               spslr_env_memcpy(dst_countable + finfo.offset,
+                                src_countable + finfo.initial_offset,
+                                finfo.size);
+       }
+
+       return 0;
+}
+
+/*
+ * Apply data pin patches.
+ *
+ * Each pin's address already points at an existing object in original layout. 
Patching
+ * converts that storage in-place to the target's randomized layout.
+ */
+
+static int spslr_patch_dpins(const struct spslr_dpin *dpins, spslr_u64 cnt,
+                            const struct target_map *tmap,
+                            void *reorder_buffer)
+{
+       for (spslr_u64 dpidx = 0; dpidx < cnt; dpidx++) {
+               const struct spslr_dpin *dp = &dpins[dpidx];
+
+               if (dp->unit_target_idx >= tmap->size)
+                       return -1;
+
+               if (spslr_patch_dpin((void *)dp->addr,
+                                    tmap->map[dp->unit_target_idx],
+                                    reorder_buffer) < 0)
+                       return -1;
+       }
+
+       return 0;
+}
+
+static int spslr_patch_dpin(void *addr, spslr_u64 target, void *reorder_buffer)
+{
+       if (target >= spslr_target_cnt)
+               return -1;
+
+       int res = -1;
+       const struct spslr_target *t = &spslr_targets[target];
+
+       sanemaker_signal(SANEMAKER_SIGNAL_PAUSE);
+
+       spslr_env_memset(reorder_buffer, 0, t->layout->size);
+
+       if (reorder_object(reorder_buffer, addr, target) < 0)
+               goto finish;
+
+       if (spslr_env_poke_data(addr, reorder_buffer, t->layout->size) < 0)
+               goto finish;
+
+       res = 0;
+finish:
+       sanemaker_signal(SANEMAKER_SIGNAL_RESUME);
+       return res;
+}
+
+static int spslr_ipin_value_fits(spslr_u64 value, spslr_u64 size)
+{
+       if (size == 0)
+               return 0;
+
+       spslr_u64 bound = (spslr_u64)1 << (8 * size);
+       return value < bound;
+}
+
+static int spslr_patch_ipins(const struct spslr_ipin *ipins, spslr_u64 cnt,
+                            const struct target_map *tmap)
+{
+       for (spslr_u64 ipidx = 0; ipidx < cnt; ipidx++) {
+               const struct spslr_ipin *ip = &ipins[ipidx];
+
+               spslr_s64 value;
+               if (spslr_calculate_ipin_value(ip->expr, &value, tmap) < 0)
+                       return -1;
+
+               if (value < 0 ||
+                   !spslr_ipin_value_fits((spslr_u64)value, ip->size))
+                       return -1;
+
+               /*
+                * Text patching is deliberately scoped to the immediate field 
only.
+                * The surrounding instruction bytes were fixed by 
pinpoint/patchcompile and
+                * must not change at runtime.
+                */
+
+               switch (ip->size) {
+               case 1:
+                       if (spslr_env_poke_text_8((void *)ip->addr,
+                                                 (spslr_u8)value) < 0)
+                               return -1;
+                       break;
+               case 2:
+                       if (spslr_env_poke_text_16((void *)ip->addr,
+                                                  (spslr_u16)value) < 0)
+                               return -1;
+                       break;
+               case 4:
+                       if (spslr_env_poke_text_32((void *)ip->addr,
+                                                  (spslr_u32)value) < 0)
+                               return -1;
+                       break;
+               case 8:
+                       if (spslr_env_poke_text_64((void *)ip->addr,
+                                                  (spslr_u64)value) < 0)
+                               return -1;
+                       break;
+               default:
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * Interpret one ipin program and compute the replacement immediate value.
+ *
+ * The program describes original target/field references; this function maps
+ * them through the randomized runtime layout and returns the value written 
into
+ * the instruction stream.
+ */
+
+static int spslr_calculate_ipin_value(const struct spslr_ipin_expr *expr,
+                                     spslr_s64 *res,
+                                     const struct target_map *tmap)
+{
+       if (!res)
+               return -1;
+
+       *res = 0;
+
+       if (expr->unit_target_idx >= tmap->size)
+               return -1;
+
+       spslr_u64 global_target_idx = tmap->map[expr->unit_target_idx];
+
+       struct spslr_randomizer_field_info finfo;
+       if (spslr_randomizer_get_field(global_target_idx, expr->field_idx,
+                                      SPSLR_RANDOMIZER_FIELD_IDX_MODE_ORIGINAL,
+                                      &finfo) != 0)
+               return -1;
+
+       *res = finfo.offset;
+       return 0;
+}
diff --git a/kernel/spslr/spslr_env.c b/kernel/spslr/spslr_env.c
new file mode 100644
index 000000000000..a4a2d2389dcd
--- /dev/null
+++ b/kernel/spslr/spslr_env.c
@@ -0,0 +1,74 @@
+#include "spslr_env.h"
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/random.h>
+#include <linux/memblock.h>
+
+#ifdef CONFIG_X86
+
+#include <asm/text-patching.h>
+
+static __always_inline int spslr_env_poke_text(void *dst, const void *src, 
size_t n)
+{
+       text_poke_early(dst, src, n);
+       return 0;
+}
+
+#endif
+
+int spslr_env_poke_text_8(void *dst, u8 value)
+{
+       return spslr_env_poke_text(dst, &value, sizeof(value));
+}
+
+int spslr_env_poke_text_16(void *dst, u16 value)
+{
+       return spslr_env_poke_text(dst, &value, sizeof(value));
+}
+
+int spslr_env_poke_text_32(void *dst, u32 value)
+{
+       return spslr_env_poke_text(dst, &value, sizeof(value));
+}
+
+int spslr_env_poke_text_64(void *dst, u64 value)
+{
+       return spslr_env_poke_text(dst, &value, sizeof(value));
+}
+
+/*
+ * Hook runs before slab allocators are available.
+ * memblock_alloc() is the correct early-boot allocator.
+ */
+void* __init spslr_env_malloc(spslr_u64 n) {
+       size_t size = PAGE_ALIGN(n ? n : 1);
+       return memblock_alloc(size, SMP_CACHE_BYTES);
+}
+
+void __init spslr_env_free(void *ptr, spslr_u64 n) {
+       if (ptr)
+               memblock_free(ptr, PAGE_ALIGN(n ? n : 1));
+}
+
+int spslr_env_poke_data(void* dst, const void* src, spslr_u64 n) {
+       memcpy(dst, src, n);
+       return 0;
+}
+
+void spslr_env_memset(void* dst, int v, spslr_u64 n) {
+       memset(dst, v, n);
+}
+
+void spslr_env_memcpy(void* dst, const void* src, spslr_u64 n) {
+       memcpy(dst, src, n);
+}
+
+int spslr_env_memcmp(const void *x, const void *y, spslr_u64 n) {
+       return memcmp(x, y, n);
+}
+
+spslr_u64 __init spslr_env_random_u64(void) {
+       return get_random_u64(); // Hook runs after random_init_early()
+}
+
diff --git a/kernel/spslr/spslr_env.h b/kernel/spslr/spslr_env.h
new file mode 100644
index 000000000000..f48d7e02c57e
--- /dev/null
+++ b/kernel/spslr/spslr_env.h
@@ -0,0 +1,45 @@
+#ifndef SPSLR_ENV_H
+#define SPSLR_ENV_H
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/init.h>
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+#ifndef __init
+#define __init /* only required in kernel */
+#endif
+
+#ifndef __printf
+#define __printf(fmt_pos, arg_pos) \
+       __attribute__((format(printf, fmt_pos, arg_pos)))
+#endif
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+typedef uint8_t spslr_u8;
+typedef uint16_t spslr_u16;
+typedef uint32_t spslr_u32;
+typedef uint64_t spslr_u64;
+typedef int32_t spslr_s32;
+typedef int64_t spslr_s64;
+typedef uintptr_t spslr_uintptr;
+
+int spslr_env_poke_text_8(void *dst, spslr_u8 value);
+int spslr_env_poke_text_16(void *dst, spslr_u16 value);
+int spslr_env_poke_text_32(void *dst, spslr_u32 value);
+int spslr_env_poke_text_64(void *dst, spslr_u64 value);
+int spslr_env_poke_data(void *dst, const void *src, spslr_u64 n);
+void *spslr_env_malloc(spslr_u64 n);
+void spslr_env_free(void *ptr, spslr_u64 n);
+void spslr_env_memset(void *dst, int v, spslr_u64 n);
+void spslr_env_memcpy(void *dst, const void *src, spslr_u64 n);
+int spslr_env_memcmp(const void *x, const void *y, spslr_u64 n);
+spslr_u64 spslr_env_random_u64(void);
+
+#endif
diff --git a/kernel/spslr/spslr_randomizer.c b/kernel/spslr/spslr_randomizer.c
new file mode 100644
index 000000000000..879ead0a2384
--- /dev/null
+++ b/kernel/spslr/spslr_randomizer.c
@@ -0,0 +1,646 @@
+#include "spslr_randomizer.h"
+
+#include "spslr_env.h"
+#include "pinpoint.h"
+
+#include <sanemaker/traps.h>
+
+/*
+ * Target layout randomizer.
+ *
+ * The randomizer builds a permutation from original field order to randomized
+ * field order while preserving field size, alignment, and fixed-field
+ * constraints.
+ */
+
+/*
+ * Field tracks both directions of the permutation:
+ *
+ *   original index -> randomized position
+ *   randomized position -> original index
+ *
+ * The runtime needs both: data patching copies from original offsets to new
+ * offsets, while ipin patching maps an original field offset to its randomized
+ * offset.
+ */
+struct Field {
+       spslr_u64 offset; /* Final field offset -> fields[i].offset = offset of 
field i in final layout */
+       spslr_u64 oidx; /* Original field idx -> fields[i].oidx = original 
position of field i in final layout */
+       spslr_u64 fidx; /* Final field idx -> fields[i].fidx = randomized/final 
position of original field i */
+};
+
+extern spslr_u64 spslr_target_cnt;
+extern const struct spslr_target *spslr_targets;
+
+static spslr_u64 *field_base_indices = NULL;
+static struct Field *fields = NULL;
+
+static int init_field_base_indices(void);
+static int init_fields_buffer(void);
+
+static const struct spslr_target_field *meta_original_field(spslr_u64 target,
+                                                           spslr_u64 field);
+static struct Field *state_current_field_base(spslr_u64 target);
+static struct Field *state_current_field(spslr_u64 target, spslr_u64 field);
+
+static int __init init_field_base_indices(void)
+{
+       field_base_indices = (spslr_u64 *)spslr_env_malloc(sizeof(spslr_u64) *
+                                                          spslr_target_cnt);
+       if (!field_base_indices)
+               return -1;
+
+       spslr_u64 current_field_base_idx = 0;
+       for (spslr_u64 i = 0; i < spslr_target_cnt; i++) {
+               field_base_indices[i] = current_field_base_idx;
+               current_field_base_idx += spslr_targets[i].layout->field_cnt;
+       }
+
+       return 0;
+}
+
+static const struct spslr_target_field *meta_original_field(spslr_u64 target,
+                                                           spslr_u64 field)
+{
+       if (target >= spslr_target_cnt)
+               return NULL;
+
+       const struct spslr_target_layout *layout = spslr_targets[target].layout;
+
+       if (field >= layout->field_cnt)
+               return NULL;
+
+       return layout->fields + field;
+}
+
+static struct Field *state_current_field_base(spslr_u64 target)
+{
+       if (target >= spslr_target_cnt)
+               return NULL;
+
+       spslr_u64 field_base_idx = field_base_indices[target];
+       return fields + field_base_idx;
+}
+
+static struct Field *state_current_field(spslr_u64 target, spslr_u64 field)
+{
+       if (target >= spslr_target_cnt)
+               return NULL;
+
+       struct Field *field_base = state_current_field_base(target);
+       const struct spslr_target_layout *layout = spslr_targets[target].layout;
+
+       if (!field_base || field >= layout->field_cnt)
+               return NULL;
+
+       return field_base + field;
+}
+
+static int __init init_fields_buffer(void)
+{
+       spslr_u64 total_field_count = 0;
+       for (spslr_u64 i = 0; i < spslr_target_cnt; i++)
+               total_field_count += spslr_targets[i].layout->field_cnt;
+
+       fields = (struct Field *)spslr_env_malloc(sizeof(struct Field) *
+                                                 total_field_count);
+       if (!fields)
+               return -1;
+
+       for (spslr_u64 i = 0; i < spslr_target_cnt; i++) {
+               spslr_u64 field_base_idx = field_base_indices[i];
+
+               for (spslr_u64 field_idx = 0;
+                    field_idx < spslr_targets[i].layout->field_cnt;
+                    field_idx++) {
+                       const struct spslr_target_field *src_field =
+                               spslr_targets[i].layout->fields + field_idx;
+                       struct Field *dst_field =
+                               &fields[field_base_idx + field_idx];
+
+                       dst_field->offset = src_field->offset;
+                       dst_field->oidx = field_idx;
+                       dst_field->fidx = field_idx;
+               }
+       }
+
+       return 0;
+}
+
+int __init spslr_randomizer_init(void)
+{
+       if (init_field_base_indices() != 0)
+               return -1;
+
+       if (init_fields_buffer() != 0)
+               return -1;
+
+       return 0;
+}
+
+int spslr_randomizer_get_target(spslr_u64 target, spslr_u64 *size,
+                               spslr_u64 *fieldcnt)
+{
+       if (target >= spslr_target_cnt)
+               return -1;
+
+       const struct spslr_target *t = &spslr_targets[target];
+
+       if (size)
+               *size = t->layout->size;
+
+       if (fieldcnt)
+               *fieldcnt = t->layout->field_cnt;
+
+       return 0;
+}
+
+int spslr_randomizer_get_field(spslr_u64 target, spslr_u64 field,
+                              int field_idx_mode,
+                              struct spslr_randomizer_field_info *info)
+{
+       if (target >= spslr_target_cnt)
+               return -1;
+
+       if (!info)
+               return 0;
+
+       const struct spslr_target *t = &spslr_targets[target];
+
+       if (field >= t->layout->field_cnt)
+               return -1;
+
+       const struct spslr_target_field *of = NULL;
+       const struct Field *rf = NULL;
+
+       switch (field_idx_mode) {
+       case SPSLR_RANDOMIZER_FIELD_IDX_MODE_ORIGINAL:
+               of = meta_original_field(target, field);
+               rf = state_current_field(
+                       target, state_current_field(target, field)->fidx);
+               break;
+       case SPSLR_RANDOMIZER_FIELD_IDX_MODE_FINAL:
+               of = meta_original_field(
+                       target, state_current_field(target, field)->oidx);
+               rf = state_current_field(target, field);
+               break;
+       default:
+               return -1;
+       }
+
+       info->size = of->size;
+       info->offset = rf->offset;
+       info->initial_offset = of->offset;
+       info->alignment = of->alignment;
+       info->flags = of->flags;
+
+       return 0;
+}
+
+int __init spslr_randomizer_validate_target(spslr_u64 target)
+{
+       spslr_u64 tsize, fieldcnt;
+
+       if (spslr_randomizer_get_target(target, &tsize, &fieldcnt) < 0)
+               return -1;
+
+       spslr_u64 cur_end = 0;
+
+       for (spslr_u64 i = 0; i < fieldcnt; i++) {
+               struct spslr_randomizer_field_info finfo;
+               if (spslr_randomizer_get_field(
+                           target, i, SPSLR_RANDOMIZER_FIELD_IDX_MODE_FINAL,
+                           &finfo) < 0)
+                       return -1;
+
+               if (finfo.alignment == 0)
+                       return -1;
+
+               if (finfo.offset % finfo.alignment != 0)
+                       return -1;
+
+               if ((finfo.flags & SPSLR_FLAG_FIELD_FIXED) &&
+                   finfo.offset != finfo.initial_offset)
+                       return -1;
+
+               if (finfo.offset > tsize)
+                       return -1;
+
+               /* Zero-sized metadata entries occupy no storage. */
+               if (finfo.size == 0)
+                       continue;
+
+               if (finfo.offset < cur_end)
+                       return -1;
+
+               /* Avoid overflow in offset + size. */
+               if (finfo.size > tsize - finfo.offset)
+                       return -1;
+
+               cur_end = finfo.offset + finfo.size;
+       }
+
+       return 0;
+}
+
+// RANDOMIZATION CODE
+
+struct ShuffleRegion {
+       spslr_u64 begin;
+       spslr_u64 end;
+       spslr_u64 fill_begin;
+       spslr_u64 fill_end;
+};
+
+static spslr_u64 rand_u64(void);
+static void get_origin_region(spslr_u64 target, spslr_u64 final_idx,
+                             struct ShuffleRegion *region);
+static int option_is_valid(spslr_u64 target, spslr_u64 origin_final_idx,
+                          const struct ShuffleRegion *origin,
+                          spslr_u64 offset);
+static int pick_shuffle_option(spslr_u64 target, spslr_u64 origin_final_idx,
+                              const struct ShuffleRegion *origin,
+                              spslr_u64 alignment, spslr_u64 *selected);
+static void do_swap(spslr_u64 target, spslr_u64 origin_final_idx,
+                   const struct ShuffleRegion *origin_region,
+                   spslr_u64 new_offset);
+static void shuffle_one_target(spslr_u64 target);
+static void shuffle_target(spslr_u64 target);
+
+static spslr_u64 __init rand_u64(void)
+{
+       return spslr_env_random_u64();
+}
+
+static void __init get_origin_region(spslr_u64 target, spslr_u64 final_idx,
+                                    struct ShuffleRegion *region)
+{
+       const struct spslr_target *t = &spslr_targets[target];
+       const struct Field *rf = state_current_field(target, final_idx);
+       const struct spslr_target_field *of =
+               meta_original_field(target, rf->oidx);
+
+       region->fill_begin = rf->offset;
+       region->fill_end = region->fill_begin + of->size;
+
+       if (final_idx == 0) {
+               region->begin = 0;
+       } else {
+               const struct Field *pred_rf =
+                       state_current_field(target, final_idx - 1);
+               const struct spslr_target_field *pred_of =
+                       meta_original_field(target, pred_rf->oidx);
+               region->begin = pred_rf->offset + pred_of->size;
+       }
+
+       if (final_idx + 1 >= t->layout->field_cnt) {
+               region->end = t->layout->size;
+       } else {
+               const struct Field *succ_rf =
+                       state_current_field(target, final_idx + 1);
+               region->end = succ_rf->offset;
+       }
+}
+
+/*
+ * Check whether a proposed field move preserves layout constraints.
+ *
+ * A move is valid only if displaced fields can be packed into the freed region
+ * without violating alignment or moving fields marked fixed.
+ */
+
+static int __init option_is_valid(spslr_u64 target, spslr_u64 origin_final_idx,
+                                 const struct ShuffleRegion *origin,
+                                 spslr_u64 offset)
+{
+       const struct spslr_target *t = &spslr_targets[target];
+       const struct spslr_target_field *origin_of = meta_original_field(
+               target, state_current_field(target, origin_final_idx)->oidx);
+
+       // When placed at offset, field will occupy [offset, option_would_end)
+       spslr_u64 option_would_end = offset + origin_of->size;
+       if (option_would_end > t->layout->size)
+               return 0;
+
+       // Field may overlap with origin region. Moving field to offset truly 
frees:
+       // [true_origin_region_begin, true_origin_region_end)
+       spslr_u64 true_origin_region_begin = origin->begin;
+       spslr_u64 true_origin_region_end = origin->end;
+
+       if (offset <= origin->fill_begin &&
+           option_would_end > true_origin_region_begin)
+               true_origin_region_begin = option_would_end;
+
+       if (offset >= origin->fill_begin && offset < true_origin_region_end)
+               true_origin_region_end = offset;
+
+       // Iterate over fields in target region [offset, option_would_end] and 
see if they fit into true origin region
+       spslr_u64 origin_region_ptr = true_origin_region_begin;
+       for (spslr_u64 it = 0; it < t->layout->field_cnt; it++) {
+               const struct Field *rf = state_current_field(target, it);
+               const struct spslr_target_field *of =
+                       meta_original_field(target, rf->oidx);
+
+               // The field being moved does not need to go into origin region
+               if (it == origin_final_idx)
+                       continue;
+
+               /*
+                * Zero-sized metadata entries occupy no storage, but they still
+                * mark an ordering boundary. A moved field must neither 
straddle
+                * one nor start at one: equal-offset entries have an ordering
+                * relationship that do_swap() does not preserve while 
displacing
+                * fields.
+         */
+               if (of->size == 0) {
+                       if (rf->offset >= offset &&
+                           rf->offset < option_would_end)
+                               return 0;
+
+                       continue;
+               }
+
+               // Field ends before target region -> must not be moved to 
origin region
+               if (rf->offset + of->size <= offset)
+                       continue;
+
+               // Field starts after target region -> must not be moved to 
origin region
+               if (rf->offset >= option_would_end)
+                       break;
+
+               // Fixed fields in target region unconditionally deny option
+               if (of->flags & SPSLR_FLAG_FIELD_FIXED)
+                       return 0;
+
+               // Field from target region must be moved to aligned position 
in origin region
+               if (origin_region_ptr % of->alignment != 0)
+                       origin_region_ptr +=
+                               of->alignment -
+                               (origin_region_ptr % of->alignment);
+
+               origin_region_ptr += of->size;
+
+               // Field does not fit into origin region -> option not possible
+               if (origin_region_ptr > true_origin_region_end)
+                       return 0;
+       }
+
+       return 1;
+}
+
+static int __init pick_shuffle_option(spslr_u64 target,
+                                     spslr_u64 origin_final_idx,
+                                     const struct ShuffleRegion *origin,
+                                     spslr_u64 alignment, spslr_u64 *selected)
+{
+       const struct spslr_target *t = &spslr_targets[target];
+       spslr_u64 seen = 0;
+
+       /*
+       Note: Instead of looping over entire field array for each option, loops 
can be merged into one.
+       */
+
+       for (spslr_u64 offset = 0; offset < t->layout->size;
+            offset += alignment) {
+               if (!option_is_valid(target, origin_final_idx, origin, offset))
+                       continue;
+
+               // Reservoir sampling -> uniform distribution with O(1) memory 
consumption
+               seen++;
+               if ((rand_u64() % seen) == 0)
+                       *selected = offset;
+       }
+
+       return seen ? 0 : -1;
+}
+
+/*
+ * Move one field into a new slot and repack the fields it displaced.
+ *
+ * This is not a simple pairwise swap: structure layout has byte ranges and
+ * alignment holes, so the displaced region may contain several fields.
+ */
+
+static void __init do_swap(spslr_u64 target, spslr_u64 origin_idx,
+                          const struct ShuffleRegion *origin_region,
+                          spslr_u64 new_offset)
+{
+       const struct spslr_target *t = &spslr_targets[target];
+       int pulled = 0;
+
+       spslr_u64 option_fill_end = new_offset + (origin_region->fill_end -
+                                                 origin_region->fill_begin);
+
+       spslr_u64 true_origin_region_begin = origin_region->begin;
+       if (new_offset <= origin_region->fill_begin &&
+           option_fill_end > true_origin_region_begin)
+               true_origin_region_begin = option_fill_end;
+
+       spslr_u64 origin_oidx = state_current_field(target, origin_idx)->oidx;
+
+       spslr_u64 origin_region_ptr = true_origin_region_begin;
+       for (spslr_u64 it = 0; it < t->layout->field_cnt; it++) {
+               struct Field *itf = state_current_field(target, it);
+
+               if (itf->oidx == origin_oidx)
+                       continue;
+
+               const struct spslr_target_field *itof =
+                       meta_original_field(target, itf->oidx);
+
+               // Zero-sized metadata entries occupy no storage.
+               if (itof->size == 0)
+                       continue;
+
+               if (itf->offset + itof->size <= new_offset)
+                       continue;
+
+               if (itf->offset >= option_fill_end)
+                       break;
+
+               spslr_u64 falign = itof->alignment;
+               if (origin_region_ptr % falign != 0)
+                       origin_region_ptr +=
+                               falign - (origin_region_ptr % falign);
+
+               if (!pulled) {
+                       pulled = 1;
+
+                       struct Field tmp = *state_current_field(target, it);
+                       *state_current_field(target, it) =
+                               *state_current_field(target, origin_idx);
+                       *state_current_field(target, origin_idx) = tmp;
+
+                       state_current_field(target, it)->offset = new_offset;
+
+                       state_current_field(target, origin_idx)->offset =
+                               origin_region_ptr;
+                       origin_region_ptr +=
+                               meta_original_field(
+                                       target,
+                                       state_current_field(target, origin_idx)
+                                               ->oidx)
+                                       ->size;
+                       continue;
+               }
+
+               {
+                       struct Field tmp = *state_current_field(target, it);
+
+                       if (origin_idx >= it) {
+                               for (spslr_u64 pull_it = it + 1;
+                                    pull_it <= origin_idx; pull_it++)
+                                       *state_current_field(target,
+                                                            pull_it - 1) =
+                                               *state_current_field(target,
+                                                                    pull_it);
+
+                               *state_current_field(target, origin_idx) = tmp;
+                               state_current_field(target, origin_idx)->offset 
=
+                                       origin_region_ptr;
+                               origin_region_ptr +=
+                                       meta_original_field(
+                                               target,
+                                               state_current_field(target,
+                                                                   origin_idx)
+                                                       ->oidx)
+                                               ->size;
+
+                               it--; // Must still look at the element now at 
it
+                       } else {
+                               for (spslr_u64 pull_it = it;
+                                    pull_it > origin_idx + (spslr_u64)pulled;
+                                    pull_it--)
+                                       *state_current_field(target, pull_it) =
+                                               *state_current_field(
+                                                       target, pull_it - 1);
+
+                               *state_current_field(
+                                       target,
+                                       origin_idx + (spslr_u64)pulled) = tmp;
+                               state_current_field(
+                                       target, origin_idx + (spslr_u64)pulled)
+                                       ->offset = origin_region_ptr;
+                               origin_region_ptr +=
+                                       meta_original_field(
+                                               target,
+                                               state_current_field(
+                                                       target,
+                                                       origin_idx +
+                                                               (spslr_u64)
+                                                                       pulled)
+                                                       ->oidx)
+                                               ->size;
+                       }
+               }
+
+               pulled++;
+       }
+
+       /*
+        * The selected destination may not overlap any other field. It may be 
an
+        * empty padding gap, or it may partially overlap the origin field 
itself
+        * when the field slides into adjacent padding.
+        *
+        * In either case the loop above never displaces another field and
+        * `pulled` remains zero. We still need to update the origin field's
+        * offset and reinsert it at the correct position in final-offset order 
so
+        * that the field array remains sorted.
+        */
+       if (!pulled) {
+               struct Field origin = *state_current_field(target, origin_idx);
+               spslr_u64 insert_idx = t->layout->field_cnt;
+
+               for (spslr_u64 it = 0; it < t->layout->field_cnt; it++) {
+                       if (it == origin_idx)
+                               continue;
+
+                       if (state_current_field(target, it)->offset >=
+                           new_offset) {
+                               insert_idx = it;
+                               break;
+                       }
+               }
+
+               if (insert_idx > origin_idx)
+                       insert_idx--;
+
+               if (origin_idx < insert_idx) {
+                       for (spslr_u64 it = origin_idx + 1; it <= insert_idx;
+                            it++)
+                               *state_current_field(target, it - 1) =
+                                       *state_current_field(target, it);
+               } else if (origin_idx > insert_idx) {
+                       for (spslr_u64 it = origin_idx; it > insert_idx; it--)
+                               *state_current_field(target, it) =
+                                       *state_current_field(target, it - 1);
+               }
+
+               *state_current_field(target, insert_idx) = origin;
+               state_current_field(target, insert_idx)->offset = new_offset;
+       }
+
+       /*
+        * Rebuild original->final mapping for this target.
+        */
+       for (spslr_u64 final_idx = 0; final_idx < t->layout->field_cnt;
+            final_idx++) {
+               struct Field *rf = state_current_field(target, final_idx);
+               state_current_field(target, rf->oidx)->fidx = final_idx;
+       }
+}
+
+/*
+Note: final version should not shuffle random fields but try to shuffle each 
original field idx once
+*/
+static void __init shuffle_one_target(spslr_u64 target)
+{
+       const struct spslr_target *t = &spslr_targets[target];
+       if (t->layout->field_cnt == 0)
+               return;
+
+       spslr_u64 origin_final_idx = rand_u64() % t->layout->field_cnt;
+       struct Field *origin_rf = state_current_field(target, origin_final_idx);
+       const struct spslr_target_field *origin_of =
+               meta_original_field(target, origin_rf->oidx);
+
+       /* Zero-sized entries are metadata markers, not shuffleable storage. */
+       if (origin_of->size == 0)
+               return;
+
+       if (origin_of->flags & SPSLR_FLAG_FIELD_FIXED)
+               return;
+
+       struct ShuffleRegion origin_region;
+       spslr_u64 selected_option;
+
+       get_origin_region(target, origin_final_idx, &origin_region);
+
+       if (pick_shuffle_option(target, origin_final_idx, &origin_region,
+                               origin_of->alignment, &selected_option) < 0)
+               return;
+
+       do_swap(target, origin_final_idx, &origin_region, selected_option);
+}
+
+static void __init shuffle_target(spslr_u64 target)
+{
+       const struct spslr_target *t = &spslr_targets[target];
+       spslr_u64 shuffle_count = t->layout->field_cnt * 2;
+
+       for (spslr_u64 i = 0; i < shuffle_count; i++)
+               shuffle_one_target(target);
+
+       sanemaker_finish_layout(state_current_field_base(target), t->hash);
+}
+
+int __init spslr_randomize(void)
+{
+       if (!fields)
+               return -1;
+
+       for (spslr_u64 tidx = 0; tidx < spslr_target_cnt; tidx++)
+               shuffle_target(tidx);
+
+       return 0;
+}
diff --git a/kernel/spslr/spslr_randomizer.h b/kernel/spslr/spslr_randomizer.h
new file mode 100644
index 000000000000..c360750894e7
--- /dev/null
+++ b/kernel/spslr/spslr_randomizer.h
@@ -0,0 +1,29 @@
+#ifndef SPSLR_RANDOMIZER_H
+#define SPSLR_RANDOMIZER_H
+
+#include "spslr_env.h"
+#include "pinpoint.h"
+
+#define SPSLR_RANDOMIZER_FIELD_IDX_MODE_ORIGINAL 1
+#define SPSLR_RANDOMIZER_FIELD_IDX_MODE_FINAL 2
+
+struct spslr_randomizer_field_info {
+       spslr_u64 size;
+       spslr_u64 offset;
+       spslr_u64 initial_offset;
+       spslr_u64 alignment;
+       spslr_u64 flags;
+};
+
+int spslr_randomizer_init(void);
+int spslr_randomize(void);
+
+int spslr_randomizer_get_target(spslr_u64 target, spslr_u64 *size,
+                               spslr_u64 *fieldcnt);
+int spslr_randomizer_get_field(spslr_u64 target, spslr_u64 field,
+                              int field_idx_mode,
+                              struct spslr_randomizer_field_info *info);
+
+int spslr_randomizer_validate_target(spslr_u64 target);
+
+#endif
-- 
2.43.0


Reply via email to