summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRamji Jiyani <ramjiyani@google.com>2022-09-21 14:26:52 -0700
committerRamji Jiyani <ramjiyani@google.com>2022-09-30 17:41:39 +0000
commite7451150cb701de01d6b836c496e1bd012d885cd (patch)
tree2f6ec7300a836ac90295d1b4d9963ac934e0e195
parentea705b4ac32fd227e2becb3d0fd84843c16f8386 (diff)
ANDROID: GKI: Add module load time symbol protection
Add CONFIG_MODULE_SIG_PROTECT to enable lookup for the unprotected symbols from the build time generated list of symbols. Module loading behavior will change as follows: - Allows Android GKI Modules signed using MODULE_SIG_ALL during build. - Allows other modules to load if they don't violate the access to Android GKI protected symbols. Loading will fail and return -EACCES (Permission denied) if these modules access the symbol which is not allowlisted via symbol list or exported by a GKI module. Bug: 232430739 Test: TH Signed-off-by: Ramji Jiyani <ramjiyani@google.com> Change-Id: I751b1951241b45712c20ac0e3878abd2152dd002
-rw-r--r--arch/arm64/configs/gki_defconfig2
-rw-r--r--arch/x86/configs/gki_defconfig2
-rw-r--r--init/Kconfig12
-rw-r--r--kernel/Makefile12
-rw-r--r--kernel/gki_module.c35
-rw-r--r--kernel/module-internal.h9
-rw-r--r--kernel/module.c39
-rwxr-xr-xscripts/gen_gki_modules_headers.sh94
8 files changed, 201 insertions, 4 deletions
diff --git a/arch/arm64/configs/gki_defconfig b/arch/arm64/configs/gki_defconfig
index 03e12a4f175b..38178c5340f8 100644
--- a/arch/arm64/configs/gki_defconfig
+++ b/arch/arm64/configs/gki_defconfig
@@ -98,6 +98,8 @@ CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y
CONFIG_MODULE_SCMVERSION=y
+CONFIG_MODULE_SIG=y
+CONFIG_MODULE_SIG_PROTECT=y
CONFIG_BLK_DEV_ZONED=y
CONFIG_BLK_CGROUP_IOCOST=y
CONFIG_BLK_INLINE_ENCRYPTION=y
diff --git a/arch/x86/configs/gki_defconfig b/arch/x86/configs/gki_defconfig
index 3da32df3869e..526f5458b7e2 100644
--- a/arch/x86/configs/gki_defconfig
+++ b/arch/x86/configs/gki_defconfig
@@ -79,6 +79,8 @@ CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y
CONFIG_MODULE_SCMVERSION=y
+CONFIG_MODULE_SIG=y
+CONFIG_MODULE_SIG_PROTECT=y
CONFIG_BLK_DEV_ZONED=y
CONFIG_BLK_CGROUP_IOCOST=y
CONFIG_BLK_INLINE_ENCRYPTION=y
diff --git a/init/Kconfig b/init/Kconfig
index e10bd5748a83..ed153ba4d2fe 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -2205,6 +2205,18 @@ config MODULE_SIG_FORCE
Reject unsigned modules or signed modules for which we don't have a
key. Without this, such modules will simply taint the kernel.
+config MODULE_SIG_PROTECT
+ bool "Android GKI module protection"
+ depends on MODULE_SIG && !MODULE_SIG_FORCE
+ help
+ Enables Android GKI symbol protection support.
+
+ This modifies the behavior of the MODULE_SIG_FORCE as follows:
+ - Allows Android GKI Modules signed using MODULE_SIG_ALL during build.
+ - Allows other modules to load if they don't violate the access to
+ Android GKI protected symbols. Loading will fail and return
+ -EACCES (Permission denied) if symbol access contidions are not met.
+
config MODULE_SIG_ALL
bool "Automatically sign all modules"
default y
diff --git a/kernel/Makefile b/kernel/Makefile
index 0e119c52a2cd..d50fd85b9abb 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_UID16) += uid16.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_MODULE_SIG) += module_signing.o
obj-$(CONFIG_MODULE_SIG_FORMAT) += module_signature.o
+obj-$(CONFIG_MODULE_SIG_PROTECT) += gki_module.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
obj-$(CONFIG_CRASH_CORE) += crash_core.o
@@ -160,3 +161,14 @@ $(obj)/kheaders_data.tar.xz: FORCE
$(call cmd,genikh)
clean-files := kheaders_data.tar.xz kheaders.md5
+
+#
+# ANDROID: GKI: Generate headerfile required for gki_module.o
+#
+# Dependencies on generated files need to be listed explicitly
+$(obj)/gki_module.o: $(obj)/gki_module_unprotected.h
+
+$(obj)/gki_module_unprotected.h: $(srctree)/scripts/gen_gki_modules_headers.sh \
+ $(if $(wildcard ${OUT_DIR}/abi_symbollist.raw), ${OUT_DIR}/abi_symbollist.raw)
+ $(Q)$(CONFIG_SHELL) $(srctree)/scripts/gen_gki_modules_headers.sh $@ \
+ "$(srctree)"
diff --git a/kernel/gki_module.c b/kernel/gki_module.c
new file mode 100644
index 000000000000..04989540b7b4
--- /dev/null
+++ b/kernel/gki_module.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2022 Google LLC
+ * Author: ramjiyani@google.com (Ramji Jiyani)
+ */
+
+#include <linux/bsearch.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/string.h>
+
+/*
+ * Build time generated header files
+ *
+ * gki_module_unprotected.h -- Symbols allowed to _access_ by unsigned modules
+ */
+#include "gki_module_unprotected.h"
+
+/* bsearch() comparision callback */
+static int cmp_name(const void *sym, const void *protected_sym)
+{
+ return strncmp(sym, protected_sym, MAX_UNPROTECTED_NAME_LEN);
+}
+
+/**
+ * gki_is_module_unprotected_symbol - Is a symbol unprotected for unsigned module?
+ *
+ * @name: Symbol being checked in list of unprotected symbols
+ */
+bool gki_is_module_unprotected_symbol(const char *name)
+{
+ return bsearch(name, gki_unprotected_symbols, NO_OF_UNPROTECTED_SYMBOLS,
+ MAX_UNPROTECTED_NAME_LEN, cmp_name) != NULL;
+}
diff --git a/kernel/module-internal.h b/kernel/module-internal.h
index 33783abc377b..2a8e3a2062e8 100644
--- a/kernel/module-internal.h
+++ b/kernel/module-internal.h
@@ -29,3 +29,12 @@ struct load_info {
};
extern int mod_verify_sig(const void *mod, struct load_info *info);
+
+#ifdef CONFIG_MODULE_SIG_PROTECT
+extern bool gki_is_module_unprotected_symbol(const char *name);
+#else
+static inline bool gki_is_module_unprotected_symbol(const char *name)
+{
+ return 1;
+}
+#endif /* CONFIG_MODULE_SIG_PROTECT */
diff --git a/kernel/module.c b/kernel/module.c
index f5c6e0a3a877..1a08745b84c0 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -271,7 +271,7 @@ static void module_assert_mutex_or_preempt(void)
#endif
}
-#ifdef CONFIG_MODULE_SIG
+#if defined(CONFIG_MODULE_SIG) && !defined(CONFIG_MODULE_SIG_PROTECT)
static bool sig_enforce = IS_ENABLED(CONFIG_MODULE_SIG_FORCE);
module_param(sig_enforce, bool_enable_only, 0644);
@@ -1440,6 +1440,21 @@ static const struct kernel_symbol *resolve_symbol(struct module *mod,
goto getname;
}
+ /*
+ * ANDROID: GKI:
+ * In case of an unsigned module symbol resolves only if:
+ * 1. Symbol is in the list of unprotected symbol list OR
+ * 2. If symbol owner is not NULL i.e. owner is another module;
+ * it has to be an unsigned module and not signed GKI module
+ * to protect symbols exported by signed GKI modules.
+ */
+ if (!mod->sig_ok &&
+ !gki_is_module_unprotected_symbol(name) &&
+ fsa.owner && fsa.owner->sig_ok) {
+ fsa.sym = ERR_PTR(-EACCES);
+ goto getname;
+ }
+
err = ref_module(mod, fsa.owner);
if (err) {
fsa.sym = ERR_PTR(err);
@@ -2347,9 +2362,15 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)
ignore_undef_symbol(info->hdr->e_machine, name)))
break;
- ret = PTR_ERR(ksym) ?: -ENOENT;
- pr_warn("%s: Unknown symbol %s (err %d)\n",
- mod->name, name, ret);
+ if (PTR_ERR(ksym) == -EACCES) {
+ ret = -EACCES;
+ pr_warn("%s: Protected symbol: %s (err %d)\n",
+ mod->name, name, ret);
+ } else {
+ ret = PTR_ERR(ksym) ?: -ENOENT;
+ pr_warn("%s: Unknown symbol %s (err %d)\n",
+ mod->name, name, ret);
+ }
break;
default:
@@ -2945,7 +2966,15 @@ static int module_sig_check(struct load_info *info, int flags)
return -EKEYREJECTED;
}
+/*
+ * ANDROID: GKI: Do not prevent loading of unsigned modules;
+ * as all modules except GKI modules are not signed.
+ */
+#ifndef CONFIG_MODULE_SIG_PROTECT
return security_locked_down(LOCKDOWN_MODULE_SIGNATURE);
+#else
+ return 0;
+#endif
}
#else /* !CONFIG_MODULE_SIG */
static int module_sig_check(struct load_info *info, int flags)
@@ -4045,6 +4074,8 @@ static int load_module(struct load_info *info, const char __user *uargs,
"kernel\n", mod->name);
add_taint_module(mod, TAINT_UNSIGNED_MODULE, LOCKDEP_STILL_OK);
}
+#else
+ mod->sig_ok = 0;
#endif
/* To avoid stressing percpu allocator, do this once we're unique. */
diff --git a/scripts/gen_gki_modules_headers.sh b/scripts/gen_gki_modules_headers.sh
new file mode 100755
index 000000000000..b9ab5dcc4c75
--- /dev/null
+++ b/scripts/gen_gki_modules_headers.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright 2022 Google LLC
+# Author: ramjiyani@google.com (Ramji Jiyani)
+#
+
+#
+# Generates hearder file with list of unprotected symbols
+#
+# Called By: KERNEL_SRC/kernel/Makefile if CONFIG_MODULE_SIG_PROTECT=y
+#
+# gki_module_unprotected.h: Symbols allowed to _access_ by unsigned modules
+#
+# If valid symbol file doesn't exists then still generates valid C header files for
+# compilation to proceed with no symbols to protect
+#
+
+# Collect arguments from Makefile
+TARGET=$1
+SRCTREE=$2
+
+set -e
+
+#
+# Common Definitions
+#
+# Use "make V=1" to debug this script.
+case "$KBUILD_VERBOSE" in
+*1*)
+ set -x
+ ;;
+esac
+
+#
+# generate_header():
+# Args: $1 = Name of the header file
+# $2 = Input symbol list
+# $3 = Symbol type (protected/exported)
+#
+generate_header() {
+ local header_file=$1
+ local symbol_file=$2
+ local symbol_type=$3
+
+ if [ -f "${header_file}" ]; then
+ rm -f -- "${header_file}"
+ fi
+
+ # Find Maximum symbol name length if valid symbol_file exist
+ if [ -s "${symbol_file}" ]; then
+ # Skip 1st line (symbol header), Trim white spaces & +1 for null termination
+ local max_name_len=$(awk '
+ {
+ $1=$1;
+ if ( length > L && NR > 1) {
+ L=length
+ }
+ } END { print ++L }' "${symbol_file}")
+ else
+ # Set to 1 to generate valid C header file
+ local max_name_len=1
+ fi
+
+ # Header generation
+ cat > "${header_file}" <<- EOT
+ /*
+ * DO NOT EDIT
+ *
+ * Build generated header file with unprotected symbols/exports
+ */
+
+ #define NO_OF_$(printf ${symbol_type} | tr [:lower:] [:upper:])_SYMBOLS \\
+ $(printf '\t')(sizeof(gki_${symbol_type}_symbols) / sizeof(gki_${symbol_type}_symbols[0]))
+ #define MAX_$(printf ${symbol_type} | tr [:lower:] [:upper:])_NAME_LEN (${max_name_len})
+
+ static const char gki_${symbol_type}_symbols[][MAX_$(printf ${symbol_type} |
+ tr [:lower:] [:upper:])_NAME_LEN] = {
+ EOT
+
+ # If a valid symbol_file present add symbols in an array except the 1st line
+ if [ -s "${symbol_file}" ]; then
+ sed -e 's/^[ \t]*/\t"/;s/[ \t]*$/",/' "${symbol_file}" >> "${header_file}"
+ fi
+
+ # Terminate the file
+ echo "};" >> "${header_file}"
+}
+
+# Sorted list of vendor symbols
+GKI_VENDOR_SYMBOLS="${OUT_DIR}/abi_symbollist.raw"
+
+generate_header "${TARGET}" "${GKI_VENDOR_SYMBOLS}" "unprotected"
+