From e8d9f9186841f3dcb2a49de7f82f5c3e1b2da0f7 Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Mon, 13 Oct 2025 18:09:27 -0500 Subject: [PATCH] linuxkm/module_hooks.c and linuxkm/linuxkm_wc_port.h: finish implementation of FIPS_OPTEST glue code, including /sys/module/libwolfssl/FIPS_optest_run_code (FIPS_optest_trig_handler(), plus my_kallsyms_lookup_name() helper). --- .wolfssl_known_macro_extras | 2 +- linuxkm/linuxkm_wc_port.h | 9 ++- linuxkm/module_hooks.c | 151 +++++++++++++++++++++++++++++++++++- 3 files changed, 155 insertions(+), 7 deletions(-) diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index 5e3808fe0..8f4d07af6 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -221,6 +221,7 @@ ETHERNET_AVAILABLE ETHERNET_H EV_TRIGGER EXTERNAL_LOADER_APP +FIPS_OPTEST_FULL_RUN_AT_MODULE_INIT FORCE_FAILURE_GETRANDOM FP_ECC_CONTROL FREERTOS_TCP_WINSIM @@ -833,7 +834,6 @@ WOLFSSL_PSK_IDENTITY_ALERT WOLFSSL_PSK_ID_PROTECTION WOLFSSL_PSK_MULTI_ID_PER_CS WOLFSSL_PSK_TLS13_CB -WOLFSSL_PSOC6_CRYPTO WOLFSSL_PYTHON WOLFSSL_RENESAS_FSPSM_CRYPT_ONLY WOLFSSL_RENESAS_RA6M3 diff --git a/linuxkm/linuxkm_wc_port.h b/linuxkm/linuxkm_wc_port.h index fd571afb9..de2d7fa82 100644 --- a/linuxkm/linuxkm_wc_port.h +++ b/linuxkm/linuxkm_wc_port.h @@ -432,6 +432,12 @@ #endif #include + #ifndef __PIE__ + #if defined(WOLFSSL_LINUXKM_USE_GET_RANDOM_KPROBES) || defined(FIPS_OPTEST) + #include + #endif + #endif + #ifdef LINUXKM_LKCAPI_REGISTER /* the LKCAPI assumes that expanded encrypt and decrypt keys will stay * loaded simultaneously, and the Linux in-tree implementations have two @@ -474,9 +480,6 @@ #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0) #include #endif /* linux ver >= 6.13 */ - #ifdef WOLFSSL_LINUXKM_USE_GET_RANDOM_KPROBES - #include - #endif #if defined(_LINUX_REFCOUNT_H) || defined(_LINUX_REFCOUNT_TYPES_H) #define WC_LKM_REFCOUNT_TO_INT(refcount) (atomic_read(&(refcount.refs))) diff --git a/linuxkm/module_hooks.c b/linuxkm/module_hooks.c index c7d939ab0..721168f9b 100644 --- a/linuxkm/module_hooks.c +++ b/linuxkm/module_hooks.c @@ -372,7 +372,21 @@ int wc_linuxkm_GenerateSeed_IntelRD(struct OS_Seed* os, byte* output, word32 sz) #endif #ifdef FIPS_OPTEST -extern int linuxkm_op_test_wrapper(void); + #ifndef HAVE_FIPS + #error FIPS_OPTEST requires HAVE_FIPS. + #endif + #ifdef LINUXKM_LKCAPI_REGISTER + #error FIPS_OPTEST is not allowed with LINUXKM_LKCAPI_REGISTER. + #endif + extern int linuxkm_op_test_1(int argc, const char* argv[]); + extern int linuxkm_op_test_wrapper(void); + static void *my_kallsyms_lookup_name(const char *name); + static enum FipsModeId *fipsMode_ptr = NULL; + static wolfSSL_Atomic_Int *conTestFailure_ptr = NULL; + static ssize_t FIPS_optest_trig_handler(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count); + static struct kobj_attribute FIPS_optest_trig_attr = __ATTR(FIPS_optest_run_code, 0220, NULL, FIPS_optest_trig_handler); + static int installed_sysfs_FIPS_optest_trig_files = 0; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) @@ -592,8 +606,40 @@ static int wolfssl_init(void) #endif /* HAVE_FIPS && FIPS_VERSION3_GT(5,2,0) */ #ifdef FIPS_OPTEST + fipsMode_ptr = (enum FipsModeId *)my_kallsyms_lookup_name("fipsMode"); + if (fipsMode_ptr == NULL) { + pr_err("ERROR: couldn't obtain fipsMode_ptr.\n"); + return -ECANCELED; + } + + conTestFailure_ptr = (wolfSSL_Atomic_Int *)my_kallsyms_lookup_name("conTestFailure"); + if (conTestFailure_ptr == NULL) { + pr_err("ERROR: couldn't obtain conTestFailure_ptr.\n"); + return -ECANCELED; + } + + ret = linuxkm_lkcapi_sysfs_install_node(&FIPS_optest_trig_attr, &installed_sysfs_FIPS_optest_trig_files); + if (ret != 0) { + pr_err("ERROR: linuxkm_lkcapi_sysfs_install_node() failed for %s (code %d).\n", FIPS_optest_trig_attr.attr.name, ret); + return -ECANCELED; + } + +#ifdef FIPS_OPTEST_FULL_RUN_AT_MODULE_INIT (void)linuxkm_op_test_wrapper(); + *fipsMode_ptr = FIPS_MODE_INIT; + WOLFSSL_ATOMIC_STORE(*conTestFailure_ptr, 0); + /* note, must call fipsEntry() here, not wolfCrypt_IntegrityTest_fips(), + * because wc_GetCastStatus_fips(FIPS_CAST_HMAC_SHA2_256) may be degraded + * after the op_test() run. + */ + fipsEntry(); + ret = wolfCrypt_GetStatus_fips(); + if (ret != 0) { + pr_err("ERROR: wolfCrypt_GetStatus_fips() after reset failed with code %d: %s\n", ret, wc_GetErrorString(ret)); + return -ECANCELED; + } #endif +#endif /* FIPS_OPTEST */ #ifndef NO_CRYPT_TEST ret = wolfcrypt_test(NULL); @@ -679,6 +725,9 @@ static void wolfssl_exit(void) int ret; (void)linuxkm_lkcapi_sysfs_deinstall_node(&FIPS_rerun_self_test_attr, &installed_sysfs_FIPS_files); +#ifdef FIPS_OPTEST + (void)linuxkm_lkcapi_sysfs_deinstall_node(&FIPS_optest_trig_attr, &installed_sysfs_FIPS_optest_trig_files); +#endif #endif #ifdef LINUXKM_LKCAPI_REGISTER @@ -1525,14 +1574,18 @@ static int updateFipsHash(void) static ssize_t FIPS_rerun_self_test_handler(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { - int arg; int ret; (void)kobj; (void)attr; - if (kstrtoint(buf, 10, &arg) || arg != 1) + /* only recognize "1" and "1\n". */ + if ((count < 1) || (count > 2) || + (buf[0] != '1') || + ((count == 2) && (buf[1] != '\n'))) + { return -EINVAL; + } pr_info("wolfCrypt: rerunning FIPS self-test on command."); @@ -1562,4 +1615,96 @@ static ssize_t FIPS_rerun_self_test_handler(struct kobject *kobj, struct kobj_at return count; } +#ifdef FIPS_OPTEST + +static void *my_kallsyms_lookup_name(const char *name) { + static typeof(kallsyms_lookup_name) *kallsyms_lookup_name_ptr = NULL; + static struct kprobe kallsyms_lookup_name_kp = { + .symbol_name = "kallsyms_lookup_name" + }; + unsigned long a; + + if (! kallsyms_lookup_name_ptr) { + int ret; + kallsyms_lookup_name_kp.addr = NULL; + if ((ret = register_kprobe(&kallsyms_lookup_name_kp)) != 0) { + pr_err_once("ERROR: register_kprobe(&kallsyms_lookup_name_kp) failed: %d", ret); + return 0; + } + kallsyms_lookup_name_ptr = (typeof(kallsyms_lookup_name_ptr))kallsyms_lookup_name_kp.addr; + unregister_kprobe(&kallsyms_lookup_name_kp); + if (! kallsyms_lookup_name_ptr) { + pr_err_once("ERROR: kallsyms_lookup_name_kp.addr is null."); + return 0; + } + } + + a = kallsyms_lookup_name_ptr(name); + return (void *)a; +} + +typedef struct test_func_args { + int return_code; +} test_func_args; + +static ssize_t FIPS_optest_trig_handler(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, const size_t count) +{ + int ret; + int argc; + const char *argv[2]; + char code_buf[5]; + size_t corrected_count; + + (void)kobj; + (void)attr; + + /* buf may or may not have an LF at end -- tolerate both. there is no + * terminating null in either case. + */ + if (count < 1) + return -EINVAL; + if (buf[count-1] == '\n') + corrected_count = count - 1; + else + corrected_count = count; + if ((corrected_count < 1) || (corrected_count > 4)) + return -EINVAL; + memcpy(code_buf, buf, corrected_count); + code_buf[corrected_count] = 0; + + if (strspn(buf, "-0123456789") != corrected_count) + return -EINVAL; + + argv[0] = "./optest"; + argv[1] = code_buf; + argc = 2; + + printf("OK, testing code %s\n", buf); + + ret = linuxkm_op_test_1(argc, &argv[0]); + + printf("ret of op_test = %d\n", ret); + + /* reload the library in memory and re-init state */ + printf("Reloading the module in memory (equivalent to power " + "cycle)\n"); + *fipsMode_ptr = FIPS_MODE_INIT; + WOLFSSL_ATOMIC_STORE(*conTestFailure_ptr, 0); + /* note, must call fipsEntry() here, not wolfCrypt_IntegrityTest_fips(), + * because wc_GetCastStatus_fips(FIPS_CAST_HMAC_SHA2_256) may be degraded + * after the op_test() run. + */ + fipsEntry(); + ret = wolfCrypt_GetStatus_fips(); + printf("Status indicator of library reload/powercycle: %d\n", + ret); + printf("Module status is: %d\n", wolfCrypt_GetStatus_fips()); + printf("Module mode is: %d\n", wolfCrypt_GetMode_fips()); + + return count; +} + +#endif /* FIPS_OPTEST */ + #endif /* HAVE_FIPS */