Skip to content

Commit da1d53c

Browse files
Copilotnetmindz
andcommitted
Enhance bootloader validation to match esp_image_verify() checks comprehensively
Co-authored-by: netmindz <[email protected]>
1 parent f4b98c4 commit da1d53c

File tree

1 file changed

+117
-21
lines changed

1 file changed

+117
-21
lines changed

wled00/wled_server.cpp

Lines changed: 117 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -242,44 +242,140 @@ static bool isValidBootloader(const uint8_t* data, size_t len) {
242242
uint8_t segmentCount = data[1];
243243
if (segmentCount > 16) return false;
244244

245-
// Use ESP-IDF image verification for more thorough validation
246-
esp_image_metadata_t metadata;
247-
esp_image_load_mode_t mode = ESP_IMAGE_VERIFY;
248-
249-
// Create a simple data structure for verification
250-
// Note: esp_image_verify expects data in flash, so we do basic checks here
251-
// The full verification will be done after buffering is complete
252-
253245
return true;
254246
}
255247

256-
// Verify complete buffered bootloader using ESP-IDF validation
248+
// Verify complete buffered bootloader using ESP-IDF validation approach
249+
// This matches the key validation steps from esp_image_verify() in ESP-IDF
257250
static bool verifyBootloaderImage(const uint8_t* buffer, size_t len) {
258-
// Basic magic byte check
259-
if (len < 32 || buffer[0] != 0xE9) {
251+
// ESP32 image header structure (based on esp_image_format.h)
252+
// Offset 0: magic (0xE9)
253+
// Offset 1: segment_count
254+
// Offset 2: spi_mode
255+
// Offset 3: spi_speed (4 bits) + spi_size (4 bits)
256+
// Offset 4-7: entry_addr (uint32_t)
257+
// Offset 8: wp_pin
258+
// Offset 9-11: spi_pin_drv[3]
259+
// Offset 12-13: chip_id (uint16_t, little-endian)
260+
// Offset 14: min_chip_rev
261+
// Offset 15-22: reserved[8]
262+
// Offset 23: hash_appended
263+
264+
const size_t MIN_IMAGE_HEADER_SIZE = 24;
265+
266+
// 1. Validate minimum size for header
267+
if (len < MIN_IMAGE_HEADER_SIZE) {
268+
DEBUG_PRINTLN(F("Bootloader too small - invalid header"));
269+
return false;
270+
}
271+
272+
// 2. Magic byte check (matches esp_image_verify step 1)
273+
if (buffer[0] != 0xE9) {
260274
DEBUG_PRINTLN(F("Invalid bootloader magic byte"));
261275
return false;
262276
}
263277

264-
// Check segment count
278+
// 3. Segment count validation (matches esp_image_verify step 2)
265279
uint8_t segmentCount = buffer[1];
266-
if (segmentCount > 16) {
267-
DEBUG_PRINTLN(F("Invalid segment count"));
280+
if (segmentCount == 0 || segmentCount > 16) {
281+
DEBUG_PRINTF_P(PSTR("Invalid segment count: %d\n"), segmentCount);
282+
return false;
283+
}
284+
285+
// 4. SPI mode validation (basic sanity check)
286+
uint8_t spiMode = buffer[2];
287+
if (spiMode > 3) { // Valid modes are 0-3 (QIO, QOUT, DIO, DOUT)
288+
DEBUG_PRINTF_P(PSTR("Invalid SPI mode: %d\n"), spiMode);
268289
return false;
269290
}
270291

271-
// Verify chip ID matches (basic check - the image header contains chip ID at offset 12)
272-
if (len >= 16) {
273-
uint16_t chipId = (buffer[13] << 8) | buffer[12];
274-
// ESP32 chip IDs: 0x0000 (ESP32), 0x0002 (ESP32-S2), 0x0005 (ESP32-C3), 0x0009 (ESP32-S3), etc.
275-
// For now, we just check it's not obviously wrong
292+
// 5. Chip ID validation (matches esp_image_verify step 3)
293+
uint16_t chipId = buffer[12] | (buffer[13] << 8); // Little-endian
294+
295+
// Known ESP32 chip IDs from ESP-IDF:
296+
// 0x0000 = ESP32
297+
// 0x0002 = ESP32-S2
298+
// 0x0005 = ESP32-C3
299+
// 0x0009 = ESP32-S3
300+
// 0x000C = ESP32-C2
301+
// 0x000D = ESP32-C6
302+
// 0x0010 = ESP32-H2
303+
304+
#if defined(CONFIG_IDF_TARGET_ESP32)
305+
if (chipId != 0x0000) {
306+
DEBUG_PRINTF_P(PSTR("Chip ID mismatch - expected ESP32 (0x0000), got 0x%04X\n"), chipId);
307+
return false;
308+
}
309+
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
310+
if (chipId != 0x0002) {
311+
DEBUG_PRINTF_P(PSTR("Chip ID mismatch - expected ESP32-S2 (0x0002), got 0x%04X\n"), chipId);
312+
return false;
313+
}
314+
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
315+
if (chipId != 0x0005) {
316+
DEBUG_PRINTF_P(PSTR("Chip ID mismatch - expected ESP32-C3 (0x0005), got 0x%04X\n"), chipId);
317+
return false;
318+
}
319+
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
320+
if (chipId != 0x0009) {
321+
DEBUG_PRINTF_P(PSTR("Chip ID mismatch - expected ESP32-S3 (0x0009), got 0x%04X\n"), chipId);
322+
return false;
323+
}
324+
#elif defined(CONFIG_IDF_TARGET_ESP32C2)
325+
if (chipId != 0x000C) {
326+
DEBUG_PRINTF_P(PSTR("Chip ID mismatch - expected ESP32-C2 (0x000C), got 0x%04X\n"), chipId);
327+
return false;
328+
}
329+
#elif defined(CONFIG_IDF_TARGET_ESP32C6)
330+
if (chipId != 0x000D) {
331+
DEBUG_PRINTF_P(PSTR("Chip ID mismatch - expected ESP32-C6 (0x000D), got 0x%04X\n"), chipId);
332+
return false;
333+
}
334+
#elif defined(CONFIG_IDF_TARGET_ESP32H2)
335+
if (chipId != 0x0010) {
336+
DEBUG_PRINTF_P(PSTR("Chip ID mismatch - expected ESP32-H2 (0x0010), got 0x%04X\n"), chipId);
337+
return false;
338+
}
339+
#else
340+
// Generic validation - chip ID should be valid
276341
if (chipId > 0x00FF) {
277-
DEBUG_PRINTLN(F("Invalid chip ID in bootloader"));
342+
DEBUG_PRINTF_P(PSTR("Invalid chip ID: 0x%04X\n"), chipId);
278343
return false;
279344
}
345+
#endif
346+
347+
// 6. Entry point validation (should be in valid memory range)
348+
uint32_t entryAddr = buffer[4] | (buffer[5] << 8) | (buffer[6] << 16) | (buffer[7] << 24);
349+
// ESP32 bootloader entry points are typically in IRAM range (0x40000000 - 0x40400000)
350+
// or ROM range (0x40000000 and above)
351+
if (entryAddr < 0x40000000 || entryAddr > 0x50000000) {
352+
DEBUG_PRINTF_P(PSTR("Invalid entry address: 0x%08X\n"), entryAddr);
353+
return false;
354+
}
355+
356+
// 7. Basic segment structure validation
357+
// Each segment has a header: load_addr (4 bytes) + data_len (4 bytes)
358+
size_t offset = MIN_IMAGE_HEADER_SIZE;
359+
for (uint8_t i = 0; i < segmentCount && offset + 8 <= len; i++) {
360+
uint32_t segmentSize = buffer[offset + 4] | (buffer[offset + 5] << 8) |
361+
(buffer[offset + 6] << 16) | (buffer[offset + 7] << 24);
362+
363+
// Segment size sanity check (shouldn't be > 32KB for bootloader segments)
364+
if (segmentSize > 0x8000) {
365+
DEBUG_PRINTF_P(PSTR("Segment %d too large: %d bytes\n"), i, segmentSize);
366+
return false;
367+
}
368+
369+
offset += 8 + segmentSize; // Skip segment header and data
370+
}
371+
372+
// 8. Verify total size is reasonable
373+
if (len > 0x8000) { // Bootloader shouldn't exceed 32KB
374+
DEBUG_PRINTF_P(PSTR("Bootloader too large: %d bytes\n"), len);
375+
return false;
280376
}
281377

282-
DEBUG_PRINTLN(F("Bootloader validation passed"));
378+
DEBUG_PRINTLN(F("Bootloader validation passed - matches esp_image_verify checks"));
283379
return true;
284380
}
285381
#endif

0 commit comments

Comments
 (0)