1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 04:34:43 +04:00

ELF, Flipper application: do not crash on "out of memory" (#3664)

* ELF, Flipper application: do not crash on "out of memory"
* loader: better error messages
* typo
* fix position
* Loader: QR code for common errors
* NFC: error message
* Loader: error descriptions
This commit is contained in:
Sergei Gavrilov
2024-06-18 01:40:47 +10:00
committed by GitHub
parent 729db7fa98
commit d8ef0991fb
15 changed files with 324 additions and 121 deletions

View File

@@ -435,7 +435,6 @@ static bool elf_relocate(ELFFile* elf, ELFSection* s) {
/************************************ Internal FAP interfaces *************************************/
/**************************************************************************************************/
typedef enum {
SectionTypeERROR = 0,
SectionTypeUnused = 1 << 0,
SectionTypeData = 1 << 1,
SectionTypeRelData = 1 << 2,
@@ -443,8 +442,6 @@ typedef enum {
SectionTypeStrTab = 1 << 4,
SectionTypeDebugLink = 1 << 5,
SectionTypeFastRelData = 1 << 6,
SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab,
} SectionType;
static bool elf_load_debug_link(ELFFile* elf, Elf32_Shdr* section_header) {
@@ -460,37 +457,62 @@ static bool str_prefix(const char* str, const char* prefix) {
return strncmp(prefix, str, strlen(prefix)) == 0;
}
static bool elf_load_section_data(ELFFile* elf, ELFSection* section, Elf32_Shdr* section_header) {
typedef enum {
ELFLoadSectionResultSuccess,
ELFLoadSectionResultNoMemory,
ELFLoadSectionResultError,
} ELFLoadSectionResult;
typedef struct {
SectionType type;
ELFLoadSectionResult result;
} SectionTypeInfo;
static ELFLoadSectionResult
elf_load_section_data(ELFFile* elf, ELFSection* section, Elf32_Shdr* section_header) {
if(section_header->sh_size == 0) {
FURI_LOG_D(TAG, "No data for section");
return true;
return ELFLoadSectionResultSuccess;
}
size_t safe_size = section_header->sh_size + 1024;
furi_kernel_lock();
if(memmgr_heap_get_max_free_block() < safe_size) {
furi_kernel_unlock();
FURI_LOG_E(TAG, "Not enough memory to load section data");
return ELFLoadSectionResultNoMemory;
}
section->data = aligned_malloc(section_header->sh_size, section_header->sh_addralign);
section->size = section_header->sh_size;
furi_kernel_unlock();
if(section_header->sh_type == SHT_NOBITS) {
// BSS section, no data to load
return true;
return ELFLoadSectionResultSuccess;
}
if((!storage_file_seek(elf->fd, section_header->sh_offset, true)) ||
(storage_file_read(elf->fd, section->data, section_header->sh_size) !=
section_header->sh_size)) {
FURI_LOG_E(TAG, " seek/read fail");
return false;
return ELFLoadSectionResultError;
}
FURI_LOG_D(TAG, "0x%p", section->data);
return true;
return ELFLoadSectionResultSuccess;
}
static SectionType elf_preload_section(
static SectionTypeInfo elf_preload_section(
ELFFile* elf,
size_t section_idx,
Elf32_Shdr* section_header,
FuriString* name_string) {
const char* name = furi_string_get_cstr(name_string);
SectionTypeInfo info;
#ifdef ELF_DEBUG_LOG
// log section name, type and flags
@@ -527,7 +549,10 @@ static SectionType elf_preload_section(
if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.") ||
str_prefix(name, ".fast.rel.ARM.")) {
FURI_LOG_D(TAG, "Ignoring ARM section");
return SectionTypeUnused;
info.type = SectionTypeUnused;
info.result = ELFLoadSectionResultSuccess;
return info;
}
// Load allocable section
@@ -546,26 +571,32 @@ static SectionType elf_preload_section(
elf->fini_array = section_p;
}
if(!elf_load_section_data(elf, section_p, section_header)) {
info.type = SectionTypeData;
info.result = elf_load_section_data(elf, section_p, section_header);
if(info.result != ELFLoadSectionResultSuccess) {
FURI_LOG_E(TAG, "Error loading section '%s'", name);
return SectionTypeERROR;
} else {
return SectionTypeData;
}
return info;
}
// Load link info section
if(section_header->sh_flags & SHF_INFO_LINK) {
info.type = SectionTypeRelData;
if(str_prefix(name, ".rel")) {
name = name + strlen(".rel");
ELFSection* section_p = elf_file_get_or_put_section(elf, name);
section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel);
section_p->rel_offset = section_header->sh_offset;
return SectionTypeRelData;
info.result = ELFLoadSectionResultSuccess;
} else {
FURI_LOG_E(TAG, "Unknown link info section '%s'", name);
return SectionTypeERROR;
info.result = ELFLoadSectionResultError;
}
return info;
}
// Load fast rel section
@@ -574,13 +605,16 @@ static SectionType elf_preload_section(
ELFSection* section_p = elf_file_get_or_put_section(elf, name);
section_p->fast_rel = malloc(sizeof(ELFSection));
if(!elf_load_section_data(elf, section_p->fast_rel, section_header)) {
info.type = SectionTypeFastRelData;
info.result = elf_load_section_data(elf, section_p->fast_rel, section_header);
if(info.result != ELFLoadSectionResultSuccess) {
FURI_LOG_E(TAG, "Error loading section '%s'", name);
return SectionTypeERROR;
} else {
FURI_LOG_D(TAG, "Loaded fast rel section for '%s'", name);
}
FURI_LOG_D(TAG, "Loaded fast rel section for '%s'", name);
return SectionTypeFastRelData;
return info;
}
// Load symbol table
@@ -588,27 +622,39 @@ static SectionType elf_preload_section(
FURI_LOG_D(TAG, "Found .symtab section");
elf->symbol_table = section_header->sh_offset;
elf->symbol_count = section_header->sh_size / sizeof(Elf32_Sym);
return SectionTypeSymTab;
info.type = SectionTypeSymTab;
info.result = ELFLoadSectionResultSuccess;
return info;
}
// Load string table
if(strcmp(name, ".strtab") == 0) {
FURI_LOG_D(TAG, "Found .strtab section");
elf->symbol_table_strings = section_header->sh_offset;
return SectionTypeStrTab;
info.type = SectionTypeStrTab;
info.result = ELFLoadSectionResultSuccess;
return info;
}
// Load debug link section
if(strcmp(name, ".gnu_debuglink") == 0) {
FURI_LOG_D(TAG, "Found .gnu_debuglink section");
info.type = SectionTypeDebugLink;
if(elf_load_debug_link(elf, section_header)) {
return SectionTypeDebugLink;
info.result = ELFLoadSectionResultSuccess;
return info;
} else {
return SectionTypeERROR;
info.result = ELFLoadSectionResultError;
return info;
}
}
return SectionTypeUnused;
info.type = SectionTypeUnused;
info.result = ELFLoadSectionResultSuccess;
return info;
}
static Elf32_Addr elf_address_of_by_hash(ELFFile* elf, uint32_t hash) {
@@ -836,35 +882,57 @@ bool elf_file_open(ELFFile* elf, const char* path) {
return true;
}
bool elf_file_load_section_table(ELFFile* elf) {
SectionType loaded_sections = SectionTypeERROR;
ElfLoadSectionTableResult elf_file_load_section_table(ELFFile* elf) {
SectionType loaded_sections = 0;
FuriString* name = furi_string_alloc();
ElfLoadSectionTableResult result = ElfLoadSectionTableResultSuccess;
FURI_LOG_D(TAG, "Scan ELF indexs...");
// TODO FL-3526: why we start from 1?
for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) {
Elf32_Shdr section_header;
furi_string_reset(name);
if(!elf_read_section(elf, section_idx, &section_header, name)) {
loaded_sections = SectionTypeERROR;
loaded_sections = 0;
break;
}
FURI_LOG_D(
TAG, "Preloading data for section #%d %s", section_idx, furi_string_get_cstr(name));
SectionType section_type = elf_preload_section(elf, section_idx, &section_header, name);
loaded_sections |= section_type;
SectionTypeInfo section_type_info =
elf_preload_section(elf, section_idx, &section_header, name);
loaded_sections |= section_type_info.type;
if(section_type == SectionTypeERROR) {
loaded_sections = SectionTypeERROR;
if(section_type_info.result != ELFLoadSectionResultSuccess) {
if(section_type_info.result == ELFLoadSectionResultNoMemory) {
FURI_LOG_E(TAG, "Not enough memory");
result = ElfLoadSectionTableResultNoMemory;
} else if(section_type_info.result == ELFLoadSectionResultError) {
FURI_LOG_E(TAG, "Error loading section");
result = ElfLoadSectionTableResultError;
}
loaded_sections = 0;
break;
}
}
furi_string_free(name);
return IS_FLAGS_SET(loaded_sections, SectionTypeValid);
if(result != ElfLoadSectionTableResultSuccess) {
return result;
} else {
bool sections_valid =
IS_FLAGS_SET(loaded_sections, SectionTypeSymTab | SectionTypeStrTab) |
IS_FLAGS_SET(loaded_sections, SectionTypeFastRelData);
if(sections_valid) {
return ElfLoadSectionTableResultSuccess;
} else {
FURI_LOG_E(TAG, "No valid sections found");
return ElfLoadSectionTableResultError;
}
}
}
ElfProcessSectionResult elf_process_section(
@@ -877,7 +945,6 @@ ElfProcessSectionResult elf_process_section(
Elf32_Shdr section_header;
// find section
// TODO FL-3526: why we start from 1?
for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) {
furi_string_reset(section_name);
if(!elf_read_section(elf, section_idx, &section_header, section_name)) {

View File

@@ -33,7 +33,6 @@ typedef struct {
typedef enum {
ELFFileLoadStatusSuccess = 0,
ELFFileLoadStatusUnspecifiedError,
ELFFileLoadStatusNoFreeMemory,
ELFFileLoadStatusMissingImports,
} ELFFileLoadStatus;
@@ -43,6 +42,12 @@ typedef enum {
ElfProcessSectionResultSuccess,
} ElfProcessSectionResult;
typedef enum {
ElfLoadSectionTableResultError,
ElfLoadSectionTableResultNoMemory,
ElfLoadSectionTableResultSuccess,
} ElfLoadSectionTableResult;
typedef bool(ElfProcessSection)(File* file, size_t offset, size_t size, void* context);
/**
@@ -70,9 +75,9 @@ bool elf_file_open(ELFFile* elf_file, const char* path);
/**
* @brief Load ELF file section table (load stage #1)
* @param elf_file
* @return bool
* @return ElfLoadSectionTableResult
*/
bool elf_file_load_section_table(ELFFile* elf_file);
ElfLoadSectionTableResult elf_file_load_section_table(ELFFile* elf_file);
/**
* @brief Load and relocate ELF file sections (load stage #2)

View File

@@ -161,8 +161,11 @@ static FlipperApplicationPreloadStatus
// if we are loading full file
if(load_full) {
// load section table
if(!elf_file_load_section_table(app->elf)) {
ElfLoadSectionTableResult load_result = elf_file_load_section_table(app->elf);
if(load_result == ElfLoadSectionTableResultError) {
return FlipperApplicationPreloadStatusInvalidFile;
} else if(load_result == ElfLoadSectionTableResultNoMemory) {
return FlipperApplicationPreloadStatusNotEnoughMemory;
}
// load assets section
@@ -219,8 +222,6 @@ FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplicatio
elf_file_init_debug_info(app->elf, &app->state);
flipper_application_list_add_app(app);
return FlipperApplicationLoadStatusSuccess;
case ELFFileLoadStatusNoFreeMemory:
return FlipperApplicationLoadStatusNoFreeMemory;
case ELFFileLoadStatusMissingImports:
return FlipperApplicationLoadStatusMissingImports;
default:
@@ -272,38 +273,38 @@ FuriThread* flipper_application_alloc_thread(FlipperApplication* app, const char
return app->thread;
}
static const char* preload_status_strings[] = {
[FlipperApplicationPreloadStatusSuccess] = "Success",
[FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error",
[FlipperApplicationPreloadStatusInvalidFile] = "Invalid file",
[FlipperApplicationPreloadStatusInvalidManifest] = "Invalid file manifest",
[FlipperApplicationPreloadStatusApiTooOld] =
"Update Application to use with this Firmware (ApiTooOld)",
[FlipperApplicationPreloadStatusApiTooNew] =
"Update Firmware to use with this Application (ApiTooNew)",
[FlipperApplicationPreloadStatusTargetMismatch] = "Hardware target mismatch",
};
static const char* load_status_strings[] = {
[FlipperApplicationLoadStatusSuccess] = "Success",
[FlipperApplicationLoadStatusUnspecifiedError] = "Unknown error",
[FlipperApplicationLoadStatusNoFreeMemory] = "Out of memory",
[FlipperApplicationLoadStatusMissingImports] =
"Update Firmware to use with this Application (MissingImports)",
};
const char* flipper_application_preload_status_to_string(FlipperApplicationPreloadStatus status) {
if(status >= COUNT_OF(preload_status_strings) || preload_status_strings[status] == NULL) {
return "Unknown error";
switch(status) {
case FlipperApplicationPreloadStatusSuccess:
return "Success";
case FlipperApplicationPreloadStatusInvalidFile:
return "Invalid file";
case FlipperApplicationPreloadStatusNotEnoughMemory:
return "Not enough memory";
case FlipperApplicationPreloadStatusInvalidManifest:
return "Invalid file manifest";
case FlipperApplicationPreloadStatusApiTooOld:
return "Update Application to use with this Firmware (ApiTooOld)";
case FlipperApplicationPreloadStatusApiTooNew:
return "Update Firmware to use with this Application (ApiTooNew)";
case FlipperApplicationPreloadStatusTargetMismatch:
return "Hardware target mismatch";
}
return preload_status_strings[status];
return "Unknown error";
}
const char* flipper_application_load_status_to_string(FlipperApplicationLoadStatus status) {
if(status >= COUNT_OF(load_status_strings) || load_status_strings[status] == NULL) {
switch(status) {
case FlipperApplicationLoadStatusSuccess:
return "Success";
case FlipperApplicationLoadStatusUnspecifiedError:
return "Unknown error";
case FlipperApplicationLoadStatusMissingImports:
return "Update Firmware to use with this Application (MissingImports)";
}
return load_status_strings[status];
return "Unknown error";
}
const FlipperAppPluginDescriptor*

View File

@@ -18,8 +18,8 @@ extern "C" {
typedef enum {
FlipperApplicationPreloadStatusSuccess = 0,
FlipperApplicationPreloadStatusUnspecifiedError,
FlipperApplicationPreloadStatusInvalidFile,
FlipperApplicationPreloadStatusNotEnoughMemory,
FlipperApplicationPreloadStatusInvalidManifest,
FlipperApplicationPreloadStatusApiTooOld,
FlipperApplicationPreloadStatusApiTooNew,
@@ -29,7 +29,6 @@ typedef enum {
typedef enum {
FlipperApplicationLoadStatusSuccess = 0,
FlipperApplicationLoadStatusUnspecifiedError,
FlipperApplicationLoadStatusNoFreeMemory,
FlipperApplicationLoadStatusMissingImports,
} FlipperApplicationLoadStatus;