Commit Graph
4 Commits
Author SHA1 Message Date
yi chen fde03cee88 fix(spiffs): fix off-by-one in spiffsgen.py obj name length check
SpiffsFS.create_file() rejected names only when strictly longer than
obj_name_len, but CONFIG_SPIFFS_OBJ_NAME_LEN's documented semantics
(see components/spiffs/Kconfig) are that the length includes the
zero-termination character, so the maximum number of actual name
characters is obj_name_len - 1.

With the old check, a name exactly obj_name_len characters long was
accepted. SpiffsObjIndexPage.to_binary() then computes the NUL padding
after the name as (obj_name_len - len(name)), which is 0 in that case,
so the generated image's fixed-size name field ends up with no NUL
terminator anywhere in its reserved region.

Fix the boundary so the generator enforces the same maximum length
that the Kconfig help text documents.
2026-07-17 13:03:30 +02:00
yi chen d057757a20 fix(esp_partition): prevent size_t overflow bypassing bounds checks on linux target
esp_partition_write/read/erase_range/mmap in partition_linux.c (the
`linux` target backend used by --preview set-target linux / host_test)
validated the requested range with `offset + size > partition->size`.
When `size` is close to SIZE_MAX, this addition wraps around size_t and
can evaluate to a small value, so the check passes even though the
request is far out of bounds. A caller passing e.g.
esp_partition_write(partition, 1, src, SIZE_MAX) sails through both
bounds checks and reaches the byte-copy loop with new_size == SIZE_MAX,
causing out-of-bounds reads/writes far past both the caller's buffer
and the mmap'd emulated-flash file.

Replace all four instances with the overflow-safe form already used by
the other esp_partition backends (partition_target.c,
partition_bootloader.c, partition_tee.c):
`size > partition->size - offset`, which is safe because the preceding
check already guarantees offset <= partition->size.

Signed-off-by: yi chen <94xhn1@gmail.com>
2026-07-17 12:48:48 +02:00
yi chen 8ffb62127b fix(wear_levelling): guard WL_Flash::write()/read() against size==0 underflow
WL_Flash::write() and WL_Flash::read() computed:

    uint32_t count = (size - 1) / this->cfg.wl_page_size;

`size` is `size_t` (unsigned). Neither the public wl_write()/wl_read() API
(wear_levelling.cpp), nor the newer wl_bdl_write()/wl_bdl_read() block-device
path (wl_blockdev.cpp), reject size == 0 before calling into WL_Flash, and
wear_levelling.h does not document size == 0 as invalid (a 0-byte
write/read is a reasonable no-op, mirroring POSIX write()/read() with
count == 0).

When size == 0, `size - 1` wraps around to SIZE_MAX, so `count` becomes an
enormous page count instead of 0. The functions then loop that many times,
reading (write()) or writing (read()) `wl_page_size` bytes per iteration
through the flash partition, immediately walking past the caller-supplied
buffer on the very first iteration:

  - write(): out-of-bounds *read* from the caller's `src` buffer.
  - read():  out-of-bounds *write* into the caller's `dest` buffer -- the
             more severe case, since it corrupts caller memory with flash
             content instead of merely over-reading.

Verified with a standalone reproduction that compiles the unmodified
WL_Flash.cpp against a mock Flash_Access partition: calling
`wl.write(0, an_8_byte_buffer, 0)` with no other change immediately
segfaults (confirmed count == 0xFFFFFFFF for wl_page_size == 4096); with
this fix applied the same call returns ESP_OK without touching memory
outside the buffer, and normal non-zero-size read/write is unaffected.

Add an early `size == 0` return (mirroring the existing `!initialized`
guard) to both functions, and a host_test regression case exercising
wl_write()/wl_read() with size == 0 through the public API.

Disclosure: this fix was prepared with AI assistance (Claude) and reviewed
by me before submission.

Signed-off-by: yi chen <94xhn1@gmail.com>
2026-07-14 12:23:13 +02:00
yi chen 550f1bf2c4 fix(vfs): use MAX_FDS instead of VFS_MAX_COUNT when clearing fd table on unregister
esp_vfs_unregister_with_id() scanned only the first VFS_MAX_COUNT
(default 8, max 20) slots of s_fd_table[MAX_FDS] (MAX_FDS = FD_SETSIZE,
64 on non-Cygwin targets) when clearing stale references to the
unregistered VFS. Every other loop over s_fd_table in this file
(and in vfs_calls.c) correctly bounds on MAX_FDS.

Any global fd >= VFS_MAX_COUNT that was still open against the VFS
being unregistered was left with a stale vfs_index pointing at a slot
that esp_get_free_index() can immediately hand out to the next
esp_vfs_register*() call, causing later operations on that fd to be
routed into an unrelated filesystem's context.

Signed-off-by: yi chen <94xhn1@gmail.com>
2026-07-13 15:58:48 +02:00