-
Notifications
You must be signed in to change notification settings - Fork 14
Description
Summary
The serial transmit hook hookSerialTransmit takes a uint8_t * (non-const) buffer pointer, but note-c passes read-only flash-resident data through it. On ESP32, const string data resides in memory-mapped flash cache (0x3C000000–0x3E000000), which is read-only. A hook implementation that writes to the buffer (e.g., for fault injection during transport testing) will crash with a hardware exception.
Affected code
In n_serial.c:55, after transmitting the JSON request body:
_SerialTransmit((uint8_t *)c_newline, c_newline_len, true);Where c_newline is defined in n_const.c:26 as:
const char *c_newline = "\r\n";The cast from const char * to uint8_t * discards the const qualifier. The string literal "\r\n" is stored in .rodata (flash on embedded targets).
Hook signature
// n_hooks.c — current signature
void (*hookSerialTransmit)(uint8_t *text, size_t len, bool flush);
// _noteSerialTransmit passes the buffer straight through:
void _noteSerialTransmit(uint8_t *text, size_t len, bool flush) {
hookSerialTransmit(text, len, flush);
}The non-const uint8_t * parameter implies the hook may modify the buffer, but the actual data may be read-only.
Crash details
Platform: ESP32-S3 (Arduino/ESP-IDF, note-arduino 1.8.4)
Trigger: A custom serial transmit hook that injects bit flips into the buffer for transport integrity testing. When the hook XORs bits in the c_newline buffer, the ESP32-S3 raises a hardware exception:
Guru Meditation Error: Core 1 panic'ed (Cache error).
Dbus write to cache rejected, error address: 0x3c20d250
EXCCAUSE: 0x00000007
Backtrace:
counting_transmit() notecard_transport.cpp:77 ← buf[i] ^= ... on flash address
← _noteSerialTransmit() n_hooks.c:874
← _serialNoteTransaction() n_serial.c:55 ← _SerialTransmit((uint8_t *)c_newline, ...)
← _noteJSONTransaction() n_hooks.c:1043
← _noteTransactionShouldLock() n_request.c:590
← NoteBinaryStoreTransmit() n_helpers.c:653
Suggested fix
Avoid passing const/flash-resident data through the non-const transmit hook. Copy c_newline to a stack buffer before transmitting:
// n_serial.c:55 — copy to stack so hook can safely modify
uint8_t newline_buf[2] = {'\r', '\n'};
_SerialTransmit(newline_buf, c_newline_len, true);This preserves the non-const hook signature (keeping fault injection and other buffer-modifying hooks working) while avoiding the flash write crash.
Changing the hook to const uint8_t * would also fix the crash but would prevent legitimate use cases where the hook needs to modify the buffer before transmission, such as data fuzzing to simulate unreliable transport (useful for testing failure cases, md5 mismatches, or too high baud.)
Workaround
Hook implementations can check whether the buffer is in the flash address range before modifying it:
// ESP32-S3: SOC_DROM_LOW to SOC_DROM_HIGH
bool is_flash = ((uintptr_t)buf >= 0x3C000000 && (uintptr_t)buf < 0x3E000000);