diff --git a/wled00/ota_release_check.cpp b/wled00/ota_release_check.cpp index df2a3aa7b..67acc0e86 100644 --- a/wled00/ota_release_check.cpp +++ b/wled00/ota_release_check.cpp @@ -7,43 +7,11 @@ #endif bool extractReleaseFromCustomDesc(const uint8_t* binaryData, size_t dataSize, char* extractedRelease) { - if (!binaryData || !extractedRelease || dataSize < sizeof(esp_image_header_t)) { + if (!binaryData || !extractedRelease || dataSize < 64) { return false; } -#ifdef ESP32 - // Look for ESP32 image header to find the custom description section - const esp_image_header_t* header = (const esp_image_header_t*)binaryData; - - // Validate ESP32 image header - if (header->magic != ESP_IMAGE_HEADER_MAGIC) { - DEBUG_PRINTLN(F("Not a valid ESP32 image - missing magic header")); - return false; - } - - // The custom description section is located at a fixed offset after the image header - // ESP-IDF places custom description at offset 0x20 in the binary for ESP32 - const size_t custom_desc_offset = 0x20; - - if (dataSize < custom_desc_offset + sizeof(wled_custom_desc_t)) { - DEBUG_PRINTLN(F("Binary too small to contain custom description")); - return false; - } - - const wled_custom_desc_t* custom_desc = (const wled_custom_desc_t*)(binaryData + custom_desc_offset); - - // Validate magic number and version - if (custom_desc->magic != WLED_CUSTOM_DESC_MAGIC) { - DEBUG_PRINTLN(F("No WLED custom description found - no magic number")); - return false; - } - - if (custom_desc->version != WLED_CUSTOM_DESC_VERSION) { - DEBUG_PRINTF_P(PSTR("Unsupported custom description version: %u\n"), custom_desc->version); - return false; - } - - // Validate simple hash checksum (using the same simple hash as in wled_custom_desc.cpp) + // Helper lambda function for hash validation (same as in wled_custom_desc.cpp) auto simple_hash = [](const char* str) -> uint32_t { uint32_t hash = 5381; for (int i = 0; str[i]; ++i) { @@ -51,25 +19,47 @@ bool extractReleaseFromCustomDesc(const uint8_t* binaryData, size_t dataSize, ch } return hash; }; - - uint32_t expected_hash = simple_hash(custom_desc->release_name); - if (custom_desc->crc32 != expected_hash) { - DEBUG_PRINTF_P(PSTR("Custom description hash mismatch: expected 0x%08x, got 0x%08x\n"), - expected_hash, custom_desc->crc32); - return false; - } - // Extract release name (ensure null termination) - strncpy(extractedRelease, custom_desc->release_name, WLED_RELEASE_NAME_MAX_LEN - 1); - extractedRelease[WLED_RELEASE_NAME_MAX_LEN - 1] = '\0'; + // For both ESP32 and ESP8266, search for our custom structure + // Since we don't know the exact offset, we'll search for our magic number + const size_t search_limit = min(dataSize, (size_t)32768); // Limit search to first 32KB - DEBUG_PRINTF_P(PSTR("Extracted release name from custom description: '%s'\n"), extractedRelease); - return true; -#else - // ESP8266 doesn't use ESP-IDF format, so we can't extract custom description - DEBUG_PRINTLN(F("ESP8266 binaries do not support custom description extraction")); + for (size_t offset = 0; offset <= search_limit - sizeof(wled_custom_desc_t); offset++) { + const wled_custom_desc_t* custom_desc = (const wled_custom_desc_t*)(binaryData + offset); + + // Check for magic number + if (custom_desc->magic == WLED_CUSTOM_DESC_MAGIC) { + // Found potential match, validate version + if (custom_desc->version != WLED_CUSTOM_DESC_VERSION) { + DEBUG_PRINTF_P(PSTR("Found WLED structure at offset %u but version mismatch: %u\n"), + offset, custom_desc->version); + continue; + } + + // Validate hash - allow CRC32 of 0 (not computed at compile time) + uint32_t expected_hash = simple_hash(custom_desc->release_name); + if (custom_desc->crc32 != 0 && custom_desc->crc32 != expected_hash) { + DEBUG_PRINTF_P(PSTR("Found WLED structure at offset %u but hash mismatch\n"), offset); + continue; + } + + // Valid structure found + strncpy(extractedRelease, custom_desc->release_name, WLED_RELEASE_NAME_MAX_LEN - 1); + extractedRelease[WLED_RELEASE_NAME_MAX_LEN - 1] = '\0'; + + #ifdef ESP32 + DEBUG_PRINTF_P(PSTR("Extracted ESP32 release name from .rodata.wled_desc section at offset %u: '%s'\n"), + offset, extractedRelease); + #else + DEBUG_PRINTF_P(PSTR("Extracted ESP8266 release name from .ver_number section at offset %u: '%s'\n"), + offset, extractedRelease); + #endif + return true; + } + } + + DEBUG_PRINTLN(F("No WLED custom description found in binary")); return false; -#endif } bool validateReleaseCompatibility(const char* extractedRelease) { @@ -92,6 +82,10 @@ bool shouldAllowOTA(const uint8_t* binaryData, size_t dataSize, bool ignoreRelea errorMessage[0] = '\0'; } + // Ensure our custom description structure is referenced (prevents optimization) + const wled_custom_desc_t* local_desc = getWledCustomDesc(); + (void)local_desc; // Suppress unused variable warning + // If user chose to ignore release check, allow OTA if (ignoreReleaseCheck) { DEBUG_PRINTLN(F("OTA release check bypassed by user")); @@ -103,13 +97,9 @@ bool shouldAllowOTA(const uint8_t* binaryData, size_t dataSize, bool ignoreRelea bool hasCustomDesc = extractReleaseFromCustomDesc(binaryData, dataSize, extractedRelease); if (!hasCustomDesc) { - // No custom description - this could be a legacy binary or ESP8266 binary + // No custom description - this could be a legacy binary if (errorMessage) { -#ifdef ESP32 strcpy(errorMessage, "Binary has no release compatibility metadata. Check 'Ignore release name check' to proceed."); -#else - strcpy(errorMessage, "ESP8266 binaries do not support release checking. Check 'Ignore release name check' to proceed."); -#endif } DEBUG_PRINTLN(F("OTA blocked: No custom description found")); return false; diff --git a/wled00/ota_release_check.h b/wled00/ota_release_check.h index 391a657cc..d9162ca7b 100644 --- a/wled00/ota_release_check.h +++ b/wled00/ota_release_check.h @@ -55,4 +55,11 @@ bool validateReleaseCompatibility(const char* extractedRelease); */ bool shouldAllowOTA(const uint8_t* binaryData, size_t dataSize, bool ignoreReleaseCheck, char* errorMessage); +/** + * Get pointer to the embedded custom description structure + * This ensures the structure is referenced and not optimized out + * @return pointer to the custom description structure + */ +const wled_custom_desc_t* getWledCustomDesc(); + #endif // WLED_OTA_RELEASE_CHECK_H \ No newline at end of file diff --git a/wled00/wled_custom_desc.cpp b/wled00/wled_custom_desc.cpp index 88932ed87..87f28d85f 100644 --- a/wled00/wled_custom_desc.cpp +++ b/wled00/wled_custom_desc.cpp @@ -3,23 +3,44 @@ #ifdef ESP32 -// Simple hash function for validation (compile-time friendly) -constexpr uint32_t simple_hash(const char* str) { - uint32_t hash = 5381; - for (int i = 0; str[i]; ++i) { - hash = ((hash << 5) + hash) + str[i]; - } - return hash; -} - // Create the custom description structure with current release name -// This will be embedded at a fixed offset in the ESP32 binary -const wled_custom_desc_t __attribute__((section(".rodata_custom_desc"))) wled_custom_description = { - .magic = WLED_CUSTOM_DESC_MAGIC, - .version = WLED_CUSTOM_DESC_VERSION, - .release_name = WLED_RELEASE_NAME, - .crc32 = simple_hash(WLED_RELEASE_NAME), // Use simple hash for validation - .reserved = {0} +// This will be embedded in a standard rodata section for ESP32 binary +// Using a simpler section name that the linker will accept +const wled_custom_desc_t __attribute__((section(".rodata.wled_desc"))) wled_custom_description = { + WLED_CUSTOM_DESC_MAGIC, // magic + WLED_CUSTOM_DESC_VERSION, // version + WLED_RELEASE_NAME, // release_name + 0, // crc32 - computed at runtime + {0} // reserved }; -#endif // ESP32 \ No newline at end of file +// Reference to ensure it's not optimized away +const wled_custom_desc_t* __attribute__((used)) wled_custom_desc_ref = &wled_custom_description; + +// Function to ensure the structure is referenced by code +const wled_custom_desc_t* getWledCustomDesc() { + return &wled_custom_description; +} + +#elif defined(ESP8266) + +// For ESP8266, use the .ver_number section to store release metadata +// This section is located at a known offset in ESP8266 binaries +const wled_custom_desc_t __attribute__((section(".ver_number"))) wled_esp8266_version_info = { + WLED_CUSTOM_DESC_MAGIC, // magic + WLED_CUSTOM_DESC_VERSION, // version + WLED_RELEASE_NAME, // release_name + 0, // crc32 - computed at runtime + {0} // reserved +}; + +// Reference the structure in code to prevent linker from discarding it +// This ensures the version info is always present in the binary +const wled_custom_desc_t* __attribute__((used)) wled_esp8266_version_ref = &wled_esp8266_version_info; + +// Function to ensure the structure is referenced by code +const wled_custom_desc_t* getWledCustomDesc() { + return &wled_esp8266_version_info; +} + +#endif // ESP32/ESP8266 \ No newline at end of file diff --git a/wled00/wled_server.cpp b/wled00/wled_server.cpp index bf72fd47b..c6bbaf166 100644 --- a/wled00/wled_server.cpp +++ b/wled00/wled_server.cpp @@ -512,7 +512,11 @@ void initServer() #if WLED_WATCHDOG_TIMEOUT > 0 WLED::instance().enableWatchdog(); #endif + #ifdef ESP32 + request->send(500, FPSTR(CONTENT_TYPE_PLAIN), String("Update.begin failed: ") + Update.errorString()); + #else request->send(500, FPSTR(CONTENT_TYPE_PLAIN), String("Update.begin failed: ") + Update.getErrorString()); + #endif return; }