fix: CRC im Settings-Record dort schreiben, wo der Leser sie erwartet
nl_settings_save() rechnete die CRC über [0, offsetof(rec_t, crc)) und legte sie dort ab, read_rec() liest sie bei sizeof(rec_hdr_t) + hdr.length. Beide Offsets stimmen nur überein, solange sizeof(nl_settings_t) ein Vielfaches von 4 ist (aktuell 204). Ein zusätzliches uint16_t - in Plan 4 (Migration) sehr wahrscheinlich - macht 206, und jeder gespeicherte Record fiele beim nächsten Boot still durch die CRC: WLAN, Token und Name wären auf allen Lampen weg. Geschrieben wird jetzt explizit bei NL_SETTINGS_CRC_OFF(length), also exakt an der Leseposition; dazu eine Übersetzungszeit-Zusicherung, dass die Nutzdaten direkt hinter dem Header beginnen (die CRC läuft auf beiden Seiten über einen zusammenhängenden Block). Test: test_crc_offset_matches_reader prüft die Symmetrie unabhängig von der Strukturgröße. Gegenprobe mit einem zusätzlichen uint16_t im Struct: mit der alten Schreibposition fallen vier Settings-Tests, mit der neuen keiner.
This commit is contained in:
@@ -24,9 +24,20 @@ typedef struct {
|
||||
typedef struct {
|
||||
rec_hdr_t hdr;
|
||||
nl_settings_t data;
|
||||
uint32_t crc;
|
||||
uint32_t crc; /* NB: only a size reserve - the CRC is written at NL_SETTINGS_CRC_OFF() */
|
||||
} rec_t;
|
||||
|
||||
/* Where read_rec() expects the CRC, and therefore where nl_settings_save() must put it: directly
|
||||
* behind the `length` payload bytes, not at offsetof(rec_t, crc). Both offsets are only equal
|
||||
* while sizeof(nl_settings_t) is a multiple of 4 (204 today). Adding a single uint16_t would move
|
||||
* offsetof(rec_t, crc) two bytes further than the reader looks and every stored record would
|
||||
* silently fail its CRC on the next boot - Wi-Fi, token and name gone on every lamp. */
|
||||
#define NL_SETTINGS_CRC_OFF(len) (sizeof(rec_hdr_t) + (uint32_t)(len))
|
||||
|
||||
/* The CRC runs over header and payload as one contiguous block on both sides, so the payload
|
||||
* really has to start right behind the header (no padding). Compile-time, gnu99-compatible. */
|
||||
typedef char nl_settings_layout_check[offsetof(rec_t, data) == sizeof(rec_hdr_t) ? 1 : -1];
|
||||
|
||||
static uint32_t s_addr[2];
|
||||
|
||||
void nl_settings_defaults(nl_settings_t *s)
|
||||
@@ -68,7 +79,7 @@ static bool read_rec(int slot, rec_t *r)
|
||||
nl_settings_defaults(&r->data);
|
||||
if (nl_flash_read(s_addr[slot] + sizeof hdr, &r->data, hdr.length) != 0)
|
||||
return false;
|
||||
if (nl_flash_read(s_addr[slot] + sizeof hdr + hdr.length, &stored_crc, sizeof stored_crc) != 0)
|
||||
if (nl_flash_read(s_addr[slot] + NL_SETTINGS_CRC_OFF(hdr.length), &stored_crc, sizeof stored_crc) != 0)
|
||||
return false;
|
||||
|
||||
crc = nl_crc32(0, &hdr, sizeof hdr);
|
||||
@@ -104,7 +115,7 @@ int nl_settings_save(const nl_settings_t *s)
|
||||
static rec_t a, b, r; /* keep ~700 bytes off the task stack */
|
||||
bool va = read_rec(0, &a);
|
||||
bool vb = read_rec(1, &b);
|
||||
uint32_t seq;
|
||||
uint32_t seq, crc, crc_off;
|
||||
int slot;
|
||||
|
||||
if (va && vb) {
|
||||
@@ -127,11 +138,14 @@ int nl_settings_save(const nl_settings_t *s)
|
||||
r.hdr.length = sizeof(nl_settings_t);
|
||||
r.hdr.seq = seq;
|
||||
r.data = *s;
|
||||
r.crc = nl_crc32(0, &r, offsetof(rec_t, crc));
|
||||
/* Write the CRC exactly where read_rec() reads it (see NL_SETTINGS_CRC_OFF). */
|
||||
crc_off = NL_SETTINGS_CRC_OFF(r.hdr.length);
|
||||
crc = nl_crc32(0, &r, crc_off);
|
||||
memcpy((uint8_t *)&r + crc_off, &crc, sizeof crc);
|
||||
|
||||
if (nl_flash_erase_sector(s_addr[slot]) != 0)
|
||||
return -1;
|
||||
return nl_flash_write(s_addr[slot], &r, sizeof r);
|
||||
return nl_flash_write(s_addr[slot], &r, crc_off + sizeof crc);
|
||||
}
|
||||
|
||||
int nl_settings_erase(void)
|
||||
|
||||
Reference in New Issue
Block a user