ANDROID: arm64: module: preserve RELA sections FIPS140 module integrity selfcheck

The FIPS 140-2 integrity check compares the runtime code with a digest
that was created at build time. Given that the module's placement in
virtual memory is an a priori unknown, we cannot account for this at
build time, and so we need to do so at runtime instead.

In order to revert the code to the build time state, we need to know
which changes the module loader applied to it. These changes are based
on the RELA ELF section that describes the changes that the module
loader must apply, and so to unapply these changes, we need to preserve
the RELA section when loading the module.

So add a special case for a module called 'fips140' in the module
loader, and copy the RELA section applying to .text to a temporary
buffer that the fips140.ko init code can access.

Bug: 153614920
Bug: 188620248
Change-Id: I97d69053c6657b104a3a9ea10af78a53ce52c6e5
Signed-off-by: Ard Biesheuvel <ardb@google.com>
diff --git a/arch/arm64/kernel/module-plts.c b/arch/arm64/kernel/module-plts.c
index 2e22443..ecbe2ee 100644
--- a/arch/arm64/kernel/module-plts.c
+++ b/arch/arm64/kernel/module-plts.c
@@ -7,6 +7,7 @@
 #include <linux/ftrace.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/sort.h>
 
 static struct plt_entry __get_adrp_add_pair(u64 dst, u64 pc,
@@ -290,6 +291,7 @@
 int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
 			      char *secstrings, struct module *mod)
 {
+	bool copy_rela_for_fips140 = false;
 	unsigned long core_plts = 0;
 	unsigned long init_plts = 0;
 	Elf64_Sym *syms = NULL;
@@ -321,18 +323,51 @@
 		return -ENOEXEC;
 	}
 
+	if (IS_ENABLED(CONFIG_CRYPTO_FIPS140_INTEGRITY_CHECK) &&
+	    !strcmp(mod->name, "fips140"))
+		copy_rela_for_fips140 = true;
+
 	for (i = 0; i < ehdr->e_shnum; i++) {
 		Elf64_Rela *rels = (void *)ehdr + sechdrs[i].sh_offset;
 		int nents, numrels = sechdrs[i].sh_size / sizeof(Elf64_Rela);
 		Elf64_Shdr *dstsec = sechdrs + sechdrs[i].sh_info;
+		void *p;
 
 		if (sechdrs[i].sh_type != SHT_RELA)
 			continue;
 
+#ifdef CONFIG_CRYPTO_FIPS140_INTEGRITY_CHECK
+		if (copy_rela_for_fips140 &&
+		    !strcmp(secstrings + dstsec->sh_name, ".rodata")) {
+			p = kcalloc(numrels, sizeof(Elf64_Rela), GFP_KERNEL);
+			if (!p) {
+				pr_err("%s: failed to allocate .rodata RELA buffer\n");
+				return -ENOMEM;
+			}
+			memcpy(p, rels, numrels * sizeof(Elf64_Rela));
+			mod->arch.rodata_relocations = p;
+			mod->arch.num_rodata_relocations = numrels;
+		}
+#endif
+
 		/* ignore relocations that operate on non-exec sections */
 		if (!(dstsec->sh_flags & SHF_EXECINSTR))
 			continue;
 
+#ifdef CONFIG_CRYPTO_FIPS140_INTEGRITY_CHECK
+		if (copy_rela_for_fips140 &&
+		    !strcmp(secstrings + dstsec->sh_name, ".text")) {
+			p = kcalloc(numrels, sizeof(Elf64_Rela), GFP_KERNEL);
+			if (!p) {
+				pr_err("%s: failed to allocate .text RELA buffer\n");
+				return -ENOMEM;
+			}
+			memcpy(p, rels, numrels * sizeof(Elf64_Rela));
+			mod->arch.text_relocations = p;
+			mod->arch.num_text_relocations = numrels;
+		}
+#endif
+
 		/*
 		 * sort branch relocations requiring a PLT by type, symbol index
 		 * and addend