11 KiB
ESP32-C5 Toolkit Firmware Bug Review & Fixes
Date: 2024
Reviewer: AI Code Review
Firmware Version: 2.0
Executive Summary
Comprehensive review of ESP32-C5 Toolkit firmware identified 10 critical bugs, 5 medium-severity issues, and several improvements. The bugs range from memory leaks and race conditions to deadlock vulnerabilities and unsafe task management.
🔴 CRITICAL BUGS (Must Fix)
1. MUTEX DEADLOCK in frame_analyzer.c
File: main/frame_analyzer.c:57-59
Severity: CRITICAL
Issue: Deadlock when frame_analyzer_capture_start() calls frame_analyzer_reset() while already holding the mutex.
// BUG: frame_analyzer_capture_start takes mutex, then calls reset which tries to take it again
void frame_analyzer_capture_start(...) {
if (analyzer_mutex && xSemaphoreTake(analyzer_mutex, portMAX_DELAY)) {
frame_analyzer_reset(); // ❌ This tries to take the same mutex again!
// ...
}
}
Fix: Extract reset logic to a non-mutex-protected internal function, or remove mutex from reset when called internally.
2. UNSAFE TASK DELETION - Multiple Locations
Files:
main/wifi_sniffer.c:203main/deauth_engine.c:265main/handshake_capture.c:249, 431, 436
Severity: CRITICAL
Issue: vTaskDelete() called directly on potentially running tasks, which can corrupt task structures and cause crashes.
ESP-IDF Best Practice: Tasks should be gracefully stopped (set flag, wait for exit), or suspended before deletion.
Example Bug:
// wifi_sniffer.c:203
if (channel_hopper_task_handle != NULL) {
vTaskDelete(channel_hopper_task_handle); // ❌ Unsafe - task may be executing
channel_hopper_task_handle = NULL;
}
Fix: Suspend task first, then delete:
if (channel_hopper_task_handle != NULL) {
vTaskSuspend(channel_hopper_task_handle);
vTaskDelay(pdMS_TO_TICKS(10)); // Allow task to reach safe point
vTaskDelete(channel_hopper_task_handle);
channel_hopper_task_handle = NULL;
}
3. MEMORY LEAK in wifi_sniffer.c
File: main/wifi_sniffer.c:144-149
Severity: CRITICAL
Issue: Allocated channel_param memory leaked if xTaskCreate() fails.
uint8_t *channel_param = malloc(sizeof(uint8_t));
if (channel_param) {
*channel_param = channel;
xTaskCreate(single_channel_retry_task, "channel_retry", 2048, channel_param, 5, &channel_hopper_task_handle);
// ❌ If xTaskCreate fails, channel_param is leaked!
}
Fix: Check return value and free on failure:
uint8_t *channel_param = malloc(sizeof(uint8_t));
if (channel_param) {
*channel_param = channel;
BaseType_t ret = xTaskCreate(single_channel_retry_task, "channel_retry", 2048, channel_param, 5, &channel_hopper_task_handle);
if (ret != pdPASS) {
free(channel_param); // ✅ Free if task creation fails
}
}
4. RACE CONDITION in Task Handle Check
Files:
main/deauth_engine.c:258-265main/handshake_capture.c:430-437
Severity: CRITICAL
Issue: Task handle checked and then deleted in separate operations, allowing race condition.
// deauth_engine.c:258-265
while (attack_task_handle != NULL && wait_count < 50) {
vTaskDelay(pdMS_TO_TICKS(100));
wait_count++;
}
if (attack_task_handle != NULL) { // ❌ Race: handle could become NULL between check and delete
vTaskDelete(attack_task_handle);
attack_task_handle = NULL;
}
Fix: Store handle in local variable atomically:
TaskHandle_t task_to_delete = NULL;
if (xSemaphoreTake(attack_mutex, portMAX_DELAY) == pdTRUE) {
task_to_delete = attack_task_handle;
attack_task_handle = NULL; // Clear while holding mutex
xSemaphoreGive(attack_mutex);
}
if (task_to_delete != NULL) {
vTaskSuspend(task_to_delete);
vTaskDelay(pdMS_TO_TICKS(10));
vTaskDelete(task_to_delete);
}
5. PCAP BUFFER MEMORY EXHAUSTION
File: main/pcap_serializer.c:84-89
Severity: CRITICAL
Issue: No size limit on PCAP buffer growth - can exhaust all available heap.
// pcap_serializer.c:84
unsigned new_size = pcap_size + sizeof(pcap_record_header_t) + size;
uint8_t *new_buffer = realloc(pcap_buffer, new_size);
if (!new_buffer) {
ESP_LOGE(TAG, "Failed to reallocate PCAP buffer! PCAP may be incomplete.");
return; // ❌ No recovery, buffer left in inconsistent state
}
Fix: Add maximum size limit and better error handling:
#define MAX_PCAP_SIZE (512 * 1024) // 512KB limit
unsigned new_size = pcap_size + sizeof(pcap_record_header_t) + size;
if (new_size > MAX_PCAP_SIZE) {
ESP_LOGW(TAG, "PCAP buffer limit reached (%u bytes), dropping frame", MAX_PCAP_SIZE);
return;
}
uint8_t *new_buffer = realloc(pcap_buffer, new_size);
if (!new_buffer) {
ESP_LOGE(TAG, "Failed to reallocate PCAP buffer");
// Optionally: discard oldest packets to free space
return;
}
6. MISSING ERROR CHECK on Task Creation
File: main/deauth_engine.c:228
Severity: CRITICAL
Issue: xTaskCreate() return value not checked - task may not have been created.
xTaskCreate(dual_band_attack_task, "dual_attack", 8192, NULL, 5, &attack_task_handle);
// ❌ No error check - attack_running is true but task may not exist
Fix: Check return value:
BaseType_t ret = xTaskCreate(dual_band_attack_task, "dual_attack", 8192, NULL, 5, &attack_task_handle);
if (ret != pdPASS) {
ESP_LOGE(TAG, "Failed to create attack task");
attack_running = false;
xSemaphoreGive(attack_mutex);
return false;
}
7. WiFi MODE CHANGE CONFLICTS
Files: Multiple files change WiFi mode without coordination
Severity: HIGH
Issue: Multiple components (sniffer, deauth, handshake capture) change WiFi mode independently, causing conflicts.
Locations:
wifi_sniffer.c:98- SetsWIFI_MODE_APSTAdeauth_engine.c:95, 115- SetsWIFI_MODE_APSTA, thenWIFI_MODE_STAhandshake_capture.c:182, 268, 379, 451- Multiple mode changes
Fix: Implement WiFi mode manager with reference counting:
// Add to wifi_init.c or new wifi_manager.c
static int wifi_mode_ref_count = 0;
static SemaphoreHandle_t wifi_mode_mutex = NULL;
esp_err_t wifi_request_mode(wifi_mode_t requested_mode) {
if (wifi_mode_mutex == NULL) {
wifi_mode_mutex = xSemaphoreCreateMutex();
}
if (xSemaphoreTake(wifi_mode_mutex, portMAX_DELAY) != pdTRUE) {
return ESP_FAIL;
}
wifi_mode_ref_count++;
esp_err_t ret = esp_wifi_set_mode(requested_mode);
xSemaphoreGive(wifi_mode_mutex);
return ret;
}
void wifi_release_mode(void) {
if (xSemaphoreTake(wifi_mode_mutex, portMAX_DELAY) == pdTRUE) {
wifi_mode_ref_count--;
if (wifi_mode_ref_count <= 0) {
esp_wifi_set_mode(WIFI_MODE_APSTA); // Restore default
wifi_mode_ref_count = 0;
}
xSemaphoreGive(wifi_mode_mutex);
}
}
8. CHANNEL SWITCHING RACE CONDITIONS
File: main/wifi_sniffer.c, main/deauth_engine.c
Severity: HIGH
Issue: Multiple tasks/operations switch channels without coordination.
Example:
- Channel hopper task continuously changes channel
- Deauth engine changes channel for attacks
- Handshake capture sets specific channel
Fix: Implement channel lock mechanism or serialize channel operations.
9. BUFFER OVERFLOW RISK in Frame Analyzer
File: main/frame_analyzer.c:235
Severity: HIGH
Issue: EAPOL packet size not validated before memcpy.
uint16_t eapol_len = sizeof(eapol_packet_header_t) + ntohs(eapol->header.packet_body_length);
if (eapol_len <= sizeof(captured_handshake.eapol)) {
memcpy(captured_handshake.eapol, eapol, eapol_len); // ⚠️ Should also check against actual frame size
}
Fix: Add bounds checking against actual received frame size.
10. MISSING NULL CHECK before Memory Free
File: main/web_server.c:1759
Severity: MEDIUM
Issue: Packet freed without null check (though malloc failure is handled).
free(pkt); // pkt comes from malloc, should be checked
Fix: Add defensive check (though malloc failure is already handled, this is defensive):
if (pkt) {
free(pkt);
}
🟡 MEDIUM SEVERITY ISSUES
11. Inefficient RSSI Averaging
File: main/signal_analysis.c:46
Issue: Simple average overwrites previous value instead of maintaining running average.
ch->rssi = (ch->rssi + rssi) / 2; // Loses history
Fix: Use exponential moving average or maintain proper average with count.
12. Hardcoded Credentials in Code
File: Multiple files
Issue: WiFi AP credentials hardcoded ("ESP32-C5-Toolkit", "h4ck3rm4n").
Fix: Store in NVS or use configuration structure.
13. No Timeout on Mutex Operations
Files: Multiple
Issue: Many portMAX_DELAY timeouts - can hang forever if deadlock occurs.
Fix: Use reasonable timeouts and handle timeout errors gracefully.
14. Missing WiFi Error Handling
Files: Multiple
Issue: Several esp_wifi_set_channel() calls don't check return values.
Example: deauth_engine.c:68, handshake_capture.c:389
15. Stack Size May Be Insufficient
Files: Task creation calls
Issue: Some tasks use 2048-8192 bytes stack - may overflow with deep call stacks.
Fix: Monitor stack usage with uxTaskGetStackHighWaterMark() and adjust.
✅ RECOMMENDED IMPROVEMENTS
- Add Heap Monitoring: Use
esp_get_minimum_free_heap_size()to detect memory leaks - Add Watchdog: Implement task watchdog to detect hung tasks
- Improve Logging: Add more detailed error context to log messages
- Add Unit Tests: Critical path functions should have unit tests
- Documentation: Add function-level documentation for all public APIs
- Error Recovery: Implement better error recovery mechanisms (e.g., PCAP buffer full)
- Resource Limits: Add configurable limits for packet queues, PCAP size, etc.
🔧 PRIORITY FIX ORDER
- IMMEDIATE: Fix mutex deadlock (#1)
- IMMEDIATE: Fix unsafe task deletions (#2)
- HIGH: Fix memory leaks (#3)
- HIGH: Add PCAP size limits (#5)
- MEDIUM: Implement WiFi mode manager (#7)
- MEDIUM: Fix channel switching coordination (#8)
- LOW: Address other improvements
📝 NOTES
- All fixes should be tested on actual hardware
- Consider adding CONFIG options for buffer sizes
- Review ESP-IDF v6.0 migration guide for deprecated APIs
- Some "bugs" may be intentional for performance - verify before changing
REFERENCES
- ESP-IDF Programming Guide v6.0
- FreeRTOS Task Management Best Practices
- ESP32-C5 Technical Reference Manual