Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions _codeql_detected_source_root
110 changes: 107 additions & 3 deletions wled00/ota_update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,115 @@ void markOTAvalid() {
// Cache for bootloader SHA256 digest as hex string
static String bootloaderSHA256HexCache = "";

// Helper function to calculate actual bootloader size from flash
// Returns the actual size of the bootloader image, or 0 if invalid
static size_t getActualBootloaderSize() {
const size_t MIN_IMAGE_HEADER_SIZE = 24;
const size_t MAX_SEGMENT_SIZE = 0x20000; // 128KB maximum per segment

// We need to read enough data to parse all segment headers
// Typical bootloader has 4-6 segments, each header is 8 bytes
// Reading first 2KB should be sufficient for header parsing
const size_t PARSE_BUFFER_SIZE = 2048;

uint8_t* parseBuffer = (uint8_t*)malloc(PARSE_BUFFER_SIZE);
if (!parseBuffer) {
DEBUG_PRINTLN(F("Failed to allocate parse buffer"));
return 0;
}

// Read initial portion for parsing
if (esp_flash_read(NULL, parseBuffer, BOOTLOADER_OFFSET, PARSE_BUFFER_SIZE) != ESP_OK) {
DEBUG_PRINTLN(F("Failed to read bootloader header"));
free(parseBuffer);
return 0;
}

// Validate magic byte
if (parseBuffer[0] != 0xE9) {
DEBUG_PRINTF_P(PSTR("Invalid bootloader magic byte: 0x%02X\n"), parseBuffer[0]);
free(parseBuffer);
return 0;
}

// Get segment count
uint8_t segmentCount = parseBuffer[1];
if (segmentCount == 0 || segmentCount > 16) {
DEBUG_PRINTF_P(PSTR("Invalid segment count: %d\n"), segmentCount);
free(parseBuffer);
return 0;
}

// Parse segments to find actual bootloader size
size_t offset = MIN_IMAGE_HEADER_SIZE;

for (uint8_t i = 0; i < segmentCount; i++) {
// Check if segment header is within our parse buffer
if (offset + 8 > PARSE_BUFFER_SIZE) {
DEBUG_PRINTF_P(PSTR("Segment %d header at offset %d beyond parse buffer\n"), i, offset);
free(parseBuffer);
// Fall back to full size if we can't parse all segments
return BOOTLOADER_SIZE;
}

// Read segment size (little-endian uint32_t at offset+4)
uint32_t segmentSize = parseBuffer[offset + 4] |
(parseBuffer[offset + 5] << 8) |
(parseBuffer[offset + 6] << 16) |
(parseBuffer[offset + 7] << 24);

// Sanity check segment size
if (segmentSize > MAX_SEGMENT_SIZE) {
DEBUG_PRINTF_P(PSTR("Segment %d too large: %u bytes\n"), i, segmentSize);
free(parseBuffer);
return BOOTLOADER_SIZE; // Fall back to full size on error
}

offset += 8 + segmentSize; // Skip segment header (8 bytes) and data
}

size_t actualSize = offset;

// Check for appended SHA256 hash (byte 23 in header)
uint8_t hashAppended = parseBuffer[23];
if (hashAppended != 0) {
actualSize += 32;
DEBUG_PRINTLN(F("Bootloader has appended SHA256"));
}

// ESP32 bootloader images include a 1-byte checksum after segments/hash
// This is part of the standard ESP-IDF image format
actualSize += 1;

// Align to 16 bytes (ESP32 flash requirement)
// Use bitwise operation for efficient power-of-2 alignment
actualSize = (actualSize + 15) & ~15;

free(parseBuffer);

// Sanity check - actual size should not exceed allocated bootloader space
if (actualSize > BOOTLOADER_SIZE) {
DEBUG_PRINTF_P(PSTR("Calculated size %d exceeds max %d, using max\n"), actualSize, BOOTLOADER_SIZE);
return BOOTLOADER_SIZE;
}

DEBUG_PRINTF_P(PSTR("Actual bootloader size: %d bytes (from %d segments)\n"), actualSize, segmentCount);
return actualSize;
}

// Calculate and cache the bootloader SHA256 digest as hex string
void calculateBootloaderSHA256() {
if (!bootloaderSHA256HexCache.isEmpty()) return;

// Calculate SHA256
// First, determine the actual bootloader size
size_t actualBootloaderSize = getActualBootloaderSize();
if (actualBootloaderSize == 0) {
DEBUG_PRINTLN(F("Failed to determine bootloader size, using empty hash"));
bootloaderSHA256HexCache = "";
return;
}

// Calculate SHA256 only over the actual bootloader data
uint8_t sha256[32];
mbedtls_sha256_context ctx;
mbedtls_sha256_init(&ctx);
Expand All @@ -302,8 +406,8 @@ void calculateBootloaderSHA256() {
const size_t chunkSize = 256;
uint8_t buffer[chunkSize];

for (uint32_t offset = 0; offset < BOOTLOADER_SIZE; offset += chunkSize) {
size_t readSize = min((size_t)(BOOTLOADER_SIZE - offset), chunkSize);
for (uint32_t offset = 0; offset < actualBootloaderSize; offset += chunkSize) {
size_t readSize = min((size_t)(actualBootloaderSize - offset), chunkSize);
if (esp_flash_read(NULL, buffer, BOOTLOADER_OFFSET + offset, readSize) == ESP_OK) {
mbedtls_sha256_update(&ctx, buffer, readSize);
}
Expand Down
Loading