From 29fdb302e19fa76ef14ea5f24c9e84e8a960d6c6 Mon Sep 17 00:00:00 2001 From: SangYoun Kwak Date: Thu, 28 Apr 2022 17:29:07 +0900 Subject: [PATCH] Detect dm-verity corruption of rootfs using udev and do action in conf file Read StorageRootfsRecovery section from storage.conf StorageRootfsRecovery contains DmverityCorruptedAction, DmverityCorruptedCount, DmverityCorruptedTimeout Use CRITICAL_LOG when rootfs(dm-verity) corrupted Change-Id: Id5a01d89e50acd9f487f8e77c157915cce83700a Signed-off-by: SangYoun Kwak --- CMakeLists.txt | 3 + conf/storage.conf | 5 + packaging/storaged.spec | 1 + src/shared/log.h | 8 ++ src/storage/CMakeLists.txt | 3 + src/storage/storage.c | 253 +++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 254 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74162b2..6577dd9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,9 @@ SET(PKG_MODULES ADD_DEFINITIONS("-DLIBPATH=\"${LIB_INSTALL_DIR}\"") ADD_DEFINITIONS("-D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64") +IF(CRITICAL_LOG_MODULE STREQUAL on) + ADD_DEFINITIONS("-DCRITICAL_LOG_ON") +ENDIF() INCLUDE(FindPkgConfig) pkg_check_modules(${PROJECT_NAME}_pkgs REQUIRED ${PKG_MODULES}) diff --git a/conf/storage.conf b/conf/storage.conf index 188f5e8..6ccb09c 100644 --- a/conf/storage.conf +++ b/conf/storage.conf @@ -5,3 +5,8 @@ WarningLevel=5 CriticalLevel=0.1 #0.0% FullLevel=0 + +[StorageRootfsRecovery] +#DmverityCorruptedAction=reboot,recovery +#DmverityCorruptedCount=3 +#DmverityCorruptedTimeout=10s diff --git a/packaging/storaged.spec b/packaging/storaged.spec index 0b2a5c8..6c6a3ae 100644 --- a/packaging/storaged.spec +++ b/packaging/storaged.spec @@ -110,6 +110,7 @@ This package can be installed optional for auto dbus test. -DBLOCK_TMPFS=on \ -DSTORAGE_MODULE=on \ -DEXTENDED_STORAGE=%{extended_storage} \ + -DCRITICAL_LOG_MODULE=on \ #eol %build diff --git a/src/shared/log.h b/src/shared/log.h index d9da05d..77cb86d 100644 --- a/src/shared/log.h +++ b/src/shared/log.h @@ -25,6 +25,14 @@ #undef LOG_TAG #define LOG_TAG "STORAGED" +/* define CRITICAL_LOG */ +#ifdef CRITICAL_LOG_ON +#define CRITICAL_LOG(fmt, arg...) \ + do { CRITICAL_LOG_(LOG_ID_SYSTEM, DLOG_INFO, LOG_TAG, fmt, ##arg); } while (0) +#else +#define CRITICAL_LOG(fmt, arg...) _I(fmt, ##arg) +#endif + #define _D(fmt, arg...) \ do { SLOGD(fmt, ##arg); } while (0) #define _I(fmt, arg...) \ diff --git a/src/storage/CMakeLists.txt b/src/storage/CMakeLists.txt index 4e8c38b..736a9a7 100644 --- a/src/storage/CMakeLists.txt +++ b/src/storage/CMakeLists.txt @@ -10,6 +10,8 @@ pkg_check_modules(${PROJECT_NAME}_pkgs REQUIRED storage vconf libsyscommon + libudev + capi-system-device ) FOREACH(flag ${${PROJECT_NAME}_pkgs_CFLAGS}) @@ -28,6 +30,7 @@ SET(SRCS ${ALL_SRCS}) SET(SHARED_SRCS ../shared/common.c ../shared/fd_handler.c + ../shared/udev.c ) ADD_LIBRARY(${PROJECT_NAME} ${SRCS} ${SHARED_SRCS}) diff --git a/src/storage/storage.c b/src/storage/storage.c index 7d21b7f..8c8bb7a 100644 --- a/src/storage/storage.c +++ b/src/storage/storage.c @@ -34,11 +34,14 @@ #include #include #include +#include +#include #include "log.h" #include "module-intf.h" #include "storaged_common.h" #include "cleanup.h" +#include "udev.h" #define MEMORY_STATUS_TMP_PATH "/tmp" #define MEMORY_STATUS_OPT_PATH "/opt" @@ -60,6 +63,8 @@ #define NEED_CLEANUP_DIR_PATH "/run/storaged/needcleanup" #define NEED_CLEANUP_FILE_PATH "/run/storaged/needcleanup/trigger" +#define DM_ROOT_NODE_NAME "rootfs" + #define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0])) #define LOW_STORAGE_WARNING "lowstorage_warning" @@ -70,6 +75,11 @@ #define INTERNAL_STORAGE_NOTION "InternalStorageNotiOn" #define INTERNAL_STORAGE_NOTIOFF "InternalStorageNotiOff" +#define RECOVERY_ACTION_IGNORE_STR "ignore" +#define RECOVERY_ACTION_REBOOT_RECOVERY_STR "reboot,recovery" +#define ROOTFS_DMVERITY_CORRUPTED_COUNT_DEFUALT (1) +#define ROOTFS_DMVERITY_CORRUPTED_TIMEOUT_DEFUALT (0) + enum memnoti_level { MEMNOTI_LEVEL_FULL = 0, MEMNOTI_LEVEL_CRITICAL, @@ -96,6 +106,17 @@ struct storage_config_info { double full_level; }; +enum recovery_action { + RECOVERY_ACTION_IGNORE = 0, + RECOVERY_ACTION_REBOOT_RECOVERY, +}; + +struct storage_config_rootfs_recovery { + enum recovery_action action; + int dm_verity_corrupted_count; + int dm_verity_corrupted_timeout; +}; + static guint memnoti_timer; static int noti_id; @@ -125,7 +146,24 @@ static struct storage_config_info storage_opt_info = { .full_level = MEMNOTI_FULL_VALUE, }; -static void write_file() +static char *recovery_action_str[] = { + [RECOVERY_ACTION_IGNORE] = RECOVERY_ACTION_IGNORE_STR, + [RECOVERY_ACTION_REBOOT_RECOVERY] = RECOVERY_ACTION_REBOOT_RECOVERY_STR, +}; +static struct storage_config_rootfs_recovery storage_rootfs_recovery_info = { + .action = RECOVERY_ACTION_IGNORE, + .dm_verity_corrupted_count = ROOTFS_DMVERITY_CORRUPTED_COUNT_DEFUALT, + .dm_verity_corrupted_timeout = ROOTFS_DMVERITY_CORRUPTED_TIMEOUT_DEFUALT, +}; +static guint poweroff_g_timeout_event_source_id = 0; + +static void dm_verity_uevent_block_handler(struct udev_device *dev); +static struct uevent_handler dm_verity_uh = { + .subsystem = BLOCK_SUBSYSTEM, + .uevent_func = dm_verity_uevent_block_handler, +}; + +static void write_file(void) { FILE *fp; @@ -634,12 +672,19 @@ static dbus_interface_u storage_interface = { static void booting_done(void) { static int done; + int ret_val; if (done > 0) return; done = 1; _I("Booting done."); + /* UDEV register_udev_uevent_control */ + ret_val = register_udev_uevent_control(&dm_verity_uh); + if (ret_val < 0) { + _E("Failed to register dm_verity uevent: %d", ret_val); + } + if (init_storage_config_info_all() == -1) _E("Failed to remain mem noti control fd init."); } @@ -656,40 +701,197 @@ static void storage_poweroff(GDBusConnection *conn, g_source_remove(memnoti_timer); memnoti_timer = 0; } + + /* UDEV unregister_udev_uevent_control */ + unregister_udev_uevent_control(&dm_verity_uh); } -static int load_config(struct parse_result *result, void *user_data) +static gboolean reboot_recovery_callback(void *data) { - struct storage_config_info *info = (struct storage_config_info *)user_data; - char *name; - char *value; + int ret_dbus; + gint poweroff_retval; + + GVariant *poweroff_param = g_variant_new("(ss)", "reboot", "recovery"); + + ret_dbus = gdbus_call_sync_with_reply_int("org.tizen.system.deviced", + "/Org/Tizen/System/DeviceD/PowerOff", + "org.tizen.system.deviced.PowerOff", + "PowerOffWithOption", + poweroff_param, &poweroff_retval); + g_variant_unref(poweroff_param); + + if (ret_dbus < 0) { + CRITICAL_LOG("Failed to call PowerOffWithOption(reboot recovery): (%d)", ret_dbus); + return G_SOURCE_CONTINUE; + } + + CRITICAL_LOG("org.tizen.system.deviced.PowerOff.PowerOffWithOption reboot recovery: (%d)", + poweroff_retval); + + return G_SOURCE_REMOVE; +} + +static void dm_verity_uevent_block_handler(struct udev_device *dev) +{ + static int dm_verity_corrupted_cnt = 0; + const char *dm_name = NULL; + const char *dm_verity_err_block_nr = NULL; + int ret_board_api = 0; + + /* if dm_name is rootfs and DM_VERITY_ERR_BLOCK_NR exist, then reboot recovery */ + dm_name = udev_device_get_property_value(dev, "DM_NAME"); + dm_verity_err_block_nr = udev_device_get_property_value(dev, "DM_VERITY_ERR_BLOCK_NR"); + + if (!dm_name || !dm_verity_err_block_nr) { + return; + } + + if(strncmp(DM_ROOT_NODE_NAME, dm_name, strlen(DM_ROOT_NODE_NAME))) { + return; + } + + ++dm_verity_corrupted_cnt; + if (dm_verity_corrupted_cnt < storage_rootfs_recovery_info.dm_verity_corrupted_count) { + CRITICAL_LOG("dm_verity_corrupted_cnt: %d(<%d)", + dm_verity_corrupted_cnt, + storage_rootfs_recovery_info.dm_verity_corrupted_count); + return; + } + /* prevent overflow */ + dm_verity_corrupted_cnt = storage_rootfs_recovery_info.dm_verity_corrupted_count; + + CRITICAL_LOG("Dmverity corrupted: action=(%s), count=(%d), timeout=(%d)ms", + recovery_action_str[storage_rootfs_recovery_info.action], + storage_rootfs_recovery_info.dm_verity_corrupted_count, + storage_rootfs_recovery_info.dm_verity_corrupted_timeout); - if (!info) + /* action: ignore */ + if (storage_rootfs_recovery_info.action == RECOVERY_ACTION_IGNORE) { + /* ignore, do nothing */ + return; + } + /* action: reboot,recovery */ + if (storage_rootfs_recovery_info.action == RECOVERY_ACTION_REBOOT_RECOVERY && + poweroff_g_timeout_event_source_id <= 0) { + /* set current partition status as "corrupted" */ + ret_board_api = device_board_set_partition_status('\0', "corrupted"); + CRITICAL_LOG("device_board_set_partition_status: (%d)", ret_board_api); + /* clear partition ab cloned */ + ret_board_api = device_board_clear_partition_ab_cloned(); + CRITICAL_LOG("device_board_clear_partition_ab_cloned: (%d)", ret_board_api); + /* toggle partition */ + ret_board_api = device_board_switch_partition('\0'); + CRITICAL_LOG("device_board_switch_partition: (%d)", ret_board_api); + + poweroff_g_timeout_event_source_id = + g_timeout_add(storage_rootfs_recovery_info.dm_verity_corrupted_timeout, + reboot_recovery_callback, NULL); + _I("g_timeout_add event source id: (%u)", + poweroff_g_timeout_event_source_id); + } +} + +static int config_parse_time_ms(const char *value, int *parsed_ms) +{ + char prefix; + char *unit_ptr = strchr(value, 's'); + if (unit_ptr == NULL) { + _E("Cannot find 's' in the string (%s)", value); return -EINVAL; + } - if (!MATCH(result->section, "StorageUsageLevelThreshold")) + if (value > (unit_ptr - 1)) { + _E("Size of string should be larger than 1"); return -EINVAL; + } + + prefix = *(unit_ptr - 1); + + if (isdigit(prefix)) { + *parsed_ms = atoi(value) * 1000; + return 0; + } + + *(unit_ptr - 1) = '\0'; + if (prefix == 'm') { + *parsed_ms = atoi(value); + return 0; + } - _D("%s,%s,%s", result->section, result->name, result->value); + _E("Unknown unit of time"); + return -EINVAL; +} - name = result->name; - value = result->value; +static int load_config(struct parse_result *result, void *user_data) +{ + char *name; + char *value; + + if (MATCH(result->section, "StorageUsageLevelThreshold")) { + _D("%s,%s,%s", result->section, result->name, result->value); + + name = result->name; + value = result->value; + + if (MATCH(name, "WarningLevel")) { + storage_internal_info.warning_level = (double)atof(value); + _I("WarningLevel: (%f)", storage_internal_info.warning_level); + } else if (MATCH(name, "CriticalLevel")) { + storage_internal_info.critical_level = (double)atof(value); + _I("CriticalLevel: (%f)", storage_internal_info.critical_level); + } else if (MATCH(name, "FullLevel")) { + storage_internal_info.full_level = (double)atof(value); + _I("FullLevel: (%f)", storage_internal_info.full_level); + } + + } else if (MATCH(result->section, "StorageRootfsRecovery")) { + _D("%s,%s,%s", result->section, result->name, result->value); + + name = result->name; + value = result->value; + + if (MATCH(name, "DmverityCorruptedAction")) { + if (!value || !(*value)) { + return -EINVAL; + } + + if (!strncmp(value, RECOVERY_ACTION_IGNORE_STR, strlen(RECOVERY_ACTION_IGNORE_STR))) { + storage_rootfs_recovery_info.action = RECOVERY_ACTION_IGNORE; + } else if (!strncmp(value, RECOVERY_ACTION_REBOOT_RECOVERY_STR, strlen(RECOVERY_ACTION_REBOOT_RECOVERY_STR))) { + storage_rootfs_recovery_info.action = RECOVERY_ACTION_REBOOT_RECOVERY; + } else { + _E("Unknown action: (%s)", value); + /* do nothing, use previous value */ + } + + _I("Dmverity Corrupted Action: (%s)", recovery_action_str[storage_rootfs_recovery_info.action]); + + } else if (MATCH(name, "DmverityCorruptedCount")) { + storage_rootfs_recovery_info.dm_verity_corrupted_count = atoi(value); + _I("DmverityCorruptedCount: (%d)", + storage_rootfs_recovery_info.dm_verity_corrupted_count); + + } else if (MATCH(name, "DmverityCorruptedTimeout")) { + int ms = 0; + if (!config_parse_time_ms(value, &ms)) { + storage_rootfs_recovery_info.dm_verity_corrupted_timeout = ms; + } + _I("DmverityCorruptedTimeout: (%d)ms", + storage_rootfs_recovery_info.dm_verity_corrupted_timeout); + } - if (MATCH(name, "WarningLevel")) - info->warning_level = (double)atof(value); - else if (MATCH(name, "CriticalLevel")) - info->critical_level = (double)atof(value); - else if (MATCH(name, "FullLevel")) - info->full_level = (double)atof(value); + } else { + return -EINVAL; + } return 0; } -static void storage_config_load(struct storage_config_info *info) +static void storage_config_load(void) { int ret_val; - ret_val = config_parse(STORAGE_CONF_FILE, load_config, info); + ret_val = config_parse(STORAGE_CONF_FILE, load_config, NULL); if (ret_val < 0) _E("Failed to load %s, %d Use default value.", STORAGE_CONF_FILE, ret_val); } @@ -698,7 +900,10 @@ static void storage_init(void *data) { int ret_val; - storage_config_load(&storage_internal_info); + /* UDEV udev_init */ + udev_init(NULL); + + storage_config_load(); ret_val = gdbus_register_object(NULL, STORAGED_PATH_STORAGE, &storage_interface); @@ -724,9 +929,19 @@ static void storage_init(void *data) static void storage_exit(void *data) { + int ret_val; + + udev_exit(NULL); + free_cleanup_storage(); /* unregister notifier for below each event */ gdbus_signal_unsubscribe(NULL, id_storage_poweroff); + + /* UDEV unregister_udev_uevent_control */ + ret_val = unregister_udev_uevent_control(&dm_verity_uh); + if (ret_val < 0) { + _E("Failed to unregister block uevent: %d", ret_val); + } } static storaged_module_interface storage_module = { -- 2.7.4