mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
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
This commit is contained in:
@@ -81,6 +81,8 @@ CONFIG_SHADOW_CALL_STACK=y
|
|||||||
CONFIG_MODULES=y
|
CONFIG_MODULES=y
|
||||||
CONFIG_MODULE_UNLOAD=y
|
CONFIG_MODULE_UNLOAD=y
|
||||||
CONFIG_MODVERSIONS=y
|
CONFIG_MODVERSIONS=y
|
||||||
|
CONFIG_MODULE_SIG=y
|
||||||
|
CONFIG_MODULE_SIG_PROTECT=y
|
||||||
CONFIG_BLK_DEV_ZONED=y
|
CONFIG_BLK_DEV_ZONED=y
|
||||||
CONFIG_BLK_INLINE_ENCRYPTION=y
|
CONFIG_BLK_INLINE_ENCRYPTION=y
|
||||||
CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y
|
CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ CONFIG_JUMP_LABEL=y
|
|||||||
CONFIG_MODULES=y
|
CONFIG_MODULES=y
|
||||||
CONFIG_MODULE_UNLOAD=y
|
CONFIG_MODULE_UNLOAD=y
|
||||||
CONFIG_MODVERSIONS=y
|
CONFIG_MODVERSIONS=y
|
||||||
|
CONFIG_MODULE_SIG=y
|
||||||
|
CONFIG_MODULE_SIG_PROTECT=y
|
||||||
CONFIG_BLK_DEV_ZONED=y
|
CONFIG_BLK_DEV_ZONED=y
|
||||||
CONFIG_BLK_INLINE_ENCRYPTION=y
|
CONFIG_BLK_INLINE_ENCRYPTION=y
|
||||||
CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y
|
CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ KCOV_INSTRUMENT_module.o := n
|
|||||||
obj-y += main.o strict_rwx.o
|
obj-y += main.o strict_rwx.o
|
||||||
obj-$(CONFIG_MODULE_DECOMPRESS) += decompress.o
|
obj-$(CONFIG_MODULE_DECOMPRESS) += decompress.o
|
||||||
obj-$(CONFIG_MODULE_SIG) += signing.o
|
obj-$(CONFIG_MODULE_SIG) += signing.o
|
||||||
|
obj-$(CONFIG_MODULE_SIG_PROTECT) += gki_module.o
|
||||||
obj-$(CONFIG_LIVEPATCH) += livepatch.o
|
obj-$(CONFIG_LIVEPATCH) += livepatch.o
|
||||||
obj-$(CONFIG_MODULES_TREE_LOOKUP) += tree_lookup.o
|
obj-$(CONFIG_MODULES_TREE_LOOKUP) += tree_lookup.o
|
||||||
obj-$(CONFIG_DEBUG_KMEMLEAK) += debug_kmemleak.o
|
obj-$(CONFIG_DEBUG_KMEMLEAK) += debug_kmemleak.o
|
||||||
@@ -19,3 +20,14 @@ obj-$(CONFIG_SYSFS) += sysfs.o
|
|||||||
obj-$(CONFIG_KGDB_KDB) += kdb.o
|
obj-$(CONFIG_KGDB_KDB) += kdb.o
|
||||||
obj-$(CONFIG_MODVERSIONS) += version.o
|
obj-$(CONFIG_MODVERSIONS) += version.o
|
||||||
obj-$(CONFIG_MODULE_UNLOAD_TAINT_TRACKING) += tracking.o
|
obj-$(CONFIG_MODULE_UNLOAD_TAINT_TRACKING) += tracking.o
|
||||||
|
|
||||||
|
#
|
||||||
|
# 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)"
|
||||||
|
|||||||
35
kernel/module/gki_module.c
Normal file
35
kernel/module/gki_module.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -303,3 +303,12 @@ static inline int same_magic(const char *amagic, const char *bmagic, bool has_cr
|
|||||||
return strcmp(amagic, bmagic) == 0;
|
return strcmp(amagic, bmagic) == 0;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_MODVERSIONS */
|
#endif /* CONFIG_MODVERSIONS */
|
||||||
|
|
||||||
|
#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 true;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_MODULE_SIG_PROTECT */
|
||||||
|
|||||||
@@ -1095,6 +1095,21 @@ static const struct kernel_symbol *resolve_symbol(struct module *mod,
|
|||||||
goto getname;
|
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);
|
err = ref_module(mod, fsa.owner);
|
||||||
if (err) {
|
if (err) {
|
||||||
fsa.sym = ERR_PTR(err);
|
fsa.sym = ERR_PTR(err);
|
||||||
@@ -1327,9 +1342,15 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)
|
|||||||
ignore_undef_symbol(info->hdr->e_machine, name)))
|
ignore_undef_symbol(info->hdr->e_machine, name)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ret = PTR_ERR(ksym) ?: -ENOENT;
|
if (PTR_ERR(ksym) == -EACCES) {
|
||||||
pr_warn("%s: Unknown symbol %s (err %d)\n",
|
ret = -EACCES;
|
||||||
mod->name, name, ret);
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -2749,6 +2770,8 @@ static int load_module(struct load_info *info, const char __user *uargs,
|
|||||||
"kernel\n", mod->name);
|
"kernel\n", mod->name);
|
||||||
add_taint_module(mod, TAINT_UNSIGNED_MODULE, LOCKDEP_STILL_OK);
|
add_taint_module(mod, TAINT_UNSIGNED_MODULE, LOCKDEP_STILL_OK);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
mod->sig_ok = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* To avoid stressing percpu allocator, do this once we're unique. */
|
/* To avoid stressing percpu allocator, do this once we're unique. */
|
||||||
|
|||||||
@@ -19,8 +19,20 @@
|
|||||||
#undef MODULE_PARAM_PREFIX
|
#undef MODULE_PARAM_PREFIX
|
||||||
#define MODULE_PARAM_PREFIX "module."
|
#define MODULE_PARAM_PREFIX "module."
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ANDROID: GKI:
|
||||||
|
* Only enforce signature if SIG_PROTECT is not set
|
||||||
|
*/
|
||||||
|
#ifndef CONFIG_MODULE_SIG_PROTECT
|
||||||
static bool sig_enforce = IS_ENABLED(CONFIG_MODULE_SIG_FORCE);
|
static bool sig_enforce = IS_ENABLED(CONFIG_MODULE_SIG_FORCE);
|
||||||
module_param(sig_enforce, bool_enable_only, 0644);
|
module_param(sig_enforce, bool_enable_only, 0644);
|
||||||
|
void set_module_sig_enforced(void)
|
||||||
|
{
|
||||||
|
sig_enforce = true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define sig_enforce false
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Export sig_enforce kernel cmdline parameter to allow other subsystems rely
|
* Export sig_enforce kernel cmdline parameter to allow other subsystems rely
|
||||||
@@ -32,11 +44,6 @@ bool is_module_sig_enforced(void)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(is_module_sig_enforced);
|
EXPORT_SYMBOL(is_module_sig_enforced);
|
||||||
|
|
||||||
void set_module_sig_enforced(void)
|
|
||||||
{
|
|
||||||
sig_enforce = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Verify the signature on a module.
|
* Verify the signature on a module.
|
||||||
*/
|
*/
|
||||||
@@ -121,5 +128,13 @@ int module_sig_check(struct load_info *info, int flags)
|
|||||||
return -EKEYREJECTED;
|
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);
|
return security_locked_down(LOCKDOWN_MODULE_SIGNATURE);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
94
scripts/gen_gki_modules_headers.sh
Executable file
94
scripts/gen_gki_modules_headers.sh
Executable file
@@ -0,0 +1,94 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
#
|
||||||
|
# Copyright 2022 Google LLC
|
||||||
|
# Author: ramjiyani@google.com (Ramji Jiyani)
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Generates header 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 ("unprotected")
|
||||||
|
#
|
||||||
|
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"
|
||||||
|
|
||||||
Reference in New Issue
Block a user