ANDROID: fips140 - add option for debugging the integrity check

There now have been two times where I've had to debug the fips140
integrity check failing due to a new type of runtime code patching.
Debugging such issues requires dumping the text and rodata actually used
for the integrity check and comparing them with the originals.  Add a
kconfig option to make this easier.  Similar to
CRYPTO_FIPS140_MOD_EVAL_TESTING, the production build won't use this.

Bug: 188620248
Change-Id: I392de466ff31f999d65997dbc610e23e9eeca49d
Signed-off-by: Eric Biggers <ebiggers@google.com>
This commit is contained in:
Eric Biggers 2024-03-08 23:24:00 +00:00 committed by Treehugger Robot
parent fd3551098b
commit 1616e03be1
2 changed files with 99 additions and 3 deletions

View File

@ -79,6 +79,33 @@ config CRYPTO_FIPS140_MOD_EVAL_TESTING
errors and support for a userspace interface to some of the module's
services. This option should not be enabled in production builds.
config CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK
bool "Debug the integrity check in FIPS 140 module"
depends on CRYPTO_FIPS140_MOD
help
This option makes the FIPS 140 module provide debugfs files containing
the text and rodata that were used for the integrity check, i.e. the
runtime text and rodata with relocations and code patches unapplied.
This option also makes the module load even if the integrity check
fails so that these files can be used to debug the failure. (A
possible failure mode is that the kernel has added a new type of code
patching and the module needs to be updated to disable or unapply it.)
This option must not be enabled in production builds.
Example commands for debugging an integrity check failure:
adb root
adb shell mount debugfs -t debugfs /sys/kernel/debug
adb shell cp /sys/kernel/debug/fips140/{text,rodata} /data/local/tmp/
adb pull /data/local/tmp/text text.checked
adb pull /data/local/tmp/rodata rodata.checked
llvm-objcopy -O binary --only-section=.text fips140.ko text.orig
llvm-objcopy -O binary --only-section=.rodata fips140.ko rodata.orig
for f in {text,rodata}.{orig,checked}; do xxd -g1 $f > $f.xxd; done
vimdiff text.{orig,checked}.xxd
vimdiff rodata.{orig,checked}.xxd
config CRYPTO_ALGAPI
tristate
select CRYPTO_ALGAPI2

View File

@ -23,6 +23,7 @@
#undef __DISABLE_EXPORTS
#include <linux/ctype.h>
#include <linux/debugfs.h>
#include <linux/module.h>
#include <crypto/aead.h>
#include <crypto/aes.h>
@ -357,6 +358,67 @@ static void __init unapply_rodata_relocations(void *section, int section_size,
}
}
#ifdef CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK
static struct {
const void *text;
int textsize;
const void *rodata;
int rodatasize;
} saved_integrity_check_info;
static ssize_t fips140_text_read(struct file *file, char __user *to,
size_t count, loff_t *ppos)
{
return simple_read_from_buffer(to, count, ppos,
saved_integrity_check_info.text,
saved_integrity_check_info.textsize);
}
static ssize_t fips140_rodata_read(struct file *file, char __user *to,
size_t count, loff_t *ppos)
{
return simple_read_from_buffer(to, count, ppos,
saved_integrity_check_info.rodata,
saved_integrity_check_info.rodatasize);
}
static const struct file_operations fips140_text_fops = {
.read = fips140_text_read,
};
static const struct file_operations fips140_rodata_fops = {
.read = fips140_rodata_read,
};
static void fips140_init_integrity_debug_files(const void *text, int textsize,
const void *rodata,
int rodatasize)
{
struct dentry *dir;
dir = debugfs_create_dir("fips140", NULL);
saved_integrity_check_info.text = kmemdup(text, textsize, GFP_KERNEL);
saved_integrity_check_info.textsize = textsize;
if (saved_integrity_check_info.text)
debugfs_create_file("text", 0400, dir, NULL,
&fips140_text_fops);
saved_integrity_check_info.rodata = kmemdup(rodata, rodatasize,
GFP_KERNEL);
saved_integrity_check_info.rodatasize = rodatasize;
if (saved_integrity_check_info.rodata)
debugfs_create_file("rodata", 0400, dir, NULL,
&fips140_rodata_fops);
}
#else /* CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK */
static void fips140_init_integrity_debug_files(const void *text, int textsize,
const void *rodata,
int rodatasize)
{
}
#endif /* !CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK */
extern struct {
u32 offset;
u32 count;
@ -398,6 +460,9 @@ static bool __init check_fips140_module_hmac(void)
offset_to_ptr(&fips140_rela_rodata.offset),
fips140_rela_rodata.count);
fips140_init_integrity_debug_files(textcopy, textsize,
rodatacopy, rodatasize);
fips140_inject_integrity_failure(textcopy);
tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
@ -538,10 +603,14 @@ fips140_init(void)
*/
if (!check_fips140_module_hmac()) {
pr_crit("integrity check failed -- giving up!\n");
goto panic;
if (!IS_ENABLED(CONFIG_CRYPTO_FIPS140_MOD_DEBUG_INTEGRITY_CHECK)) {
pr_crit("integrity check failed -- giving up!\n");
goto panic;
}
pr_crit("ignoring integrity check failure due to debug mode\n");
} else {
pr_info("integrity check passed\n");
}
pr_info("integrity check passed\n");
complete_all(&fips140_tests_done);