Skip to content

Protocol 0x05: partial or unaligned writes silently erase the rest of the touched page and report success #68

Closed

Description

@henols

Summary

On protocol 0x05 (flash 5V page-write) the firmware writes only the bytes it was given and then
commits the page. The device erases the whole physical page and programs only the loaded bytes, so
every byte of that page which was not part of the write is erased to 0xFF — in both
directions, before the start address as well as after the end. The write then reports
successful.

There is no read-modify-write anywhere on this path, and no warning.

This affects all 27 protocol-0x05 parts, including the validated ones (w29c020, w29c040,
sst39sf020, AE29F2008). It is independent of #67, which is about the page size being
wrong on 9 of them; this one bites even when the page size is right.

Bench evidence

Rig: W29C020 on a Leonardo, Rev 2.0-class shield, firmware 3.0.0b22, host 3.0.0b38.
Real page size 128 bytes — a part where the firmware's page size is correct.

Baseline — 256 bytes written as four distinct 64-byte quarters, read back clean:

bytes 0x000-0x03f  0x11    bytes 0x080-0x0bf  0x33
bytes 0x040-0x07f  0x22    bytes 0x0c0-0x0ff  0x44

Trailing loss — write w29c020 probe.bin -a 0x0 with a 64-byte file (first half of page 0):

bytes 0x000-0x03f  0x55  <- written
bytes 0x040-0x07f  0xff  <- ERASED (was 0x22), not part of the write
bytes 0x080-0x0bf  0x33  <- intact, next physical page
bytes 0x0c0-0x0ff  0x44  <- intact

Leading loss — then write w29c020 probe2.bin -a 0x40 with a 64-byte file (second half of the
same page):

bytes 0x000-0x03f  0xff  <- ERASED (was 0x55), BEFORE the write's start address
bytes 0x040-0x07f  0x66  <- written

Both writes printed Write to W29C020 successful. Damage is confined to the physical page
containing the written range, which also confirms the 128-byte page.

Impact

Any of these silently corrupts up to page_size - 1 neighbouring bytes:

  • firestarter write <chip> patch.bin -a <offset> with a file shorter than a page, or an offset
    that is not page-aligned — the normal way to patch part of a ROM image.
  • Any write whose total length is not a whole multiple of the page size: the final partial page
    loses its tail.

The dev test harness does not catch it because it writes a full-device, page-aligned region
for flash parts, so it never issues a partial page load. That is why #52, #48, #25 and #61 all
passed.

Suggested remedies

In rough order of preference:

  1. Read-modify-write on partial pages. Before committing a page that is only partly covered by
    the payload, read the existing page content, merge the payload over it, and write the whole
    page. This is what the device's page semantics require and it makes offset writes safe.
  2. Refuse and explain. If read-modify-write is not wanted in firmware, have the host detect a
    write whose start or end is not page-aligned on protocol 0x05 and refuse it with a message
    naming the bytes at risk, behind an explicit override flag.
  3. At minimum, stop reporting successful for a write that has demonstrably erased data
    outside the requested range.

Option 1 needs a real page size to be correct, which is #67.

Related

#62 ("Unable to erase AE29F2008") is the same family of behaviour seen from the other side:
FLAG_CAN_ERASE is deliberately cleared for protocol 0x05 because the page write auto-erases
per page, so erase correctly answers Not supported. That auto-erase is exactly the mechanism
documented here.

Activity

  1. added
    bugSomething isn't working
    cause:firmwareDefect in the Arduino firmware
    on Sep 11, 2026
  2. henols commented on Sep 16, 2026

    @henols
    OwnerAuthor

    Scope correction on the four parts named above, measured against the shipped chip database
    (firestarter_app/firestarter/data/chip_database.json):

    • SST39SF020 is recorded as algorithm: 6 (PROTO_FLASH_NOR_UNLOCK), not protocol 0x05.
      It is a sector-erase NOR part handled by a different firmware file (flash_nor_unlock.cpp) and
      never traverses flash_5v_page.cpp, the file this defect lives in. It is therefore outside this
      defect's blast radius entirely -- the measurement is the part's own row in the shipped database:
      SST SST39SF020,SST39SF020A alg=6 page_size=None raw=0 size=262144, against W29C020 /
      W29C040 / AE29F2008, all alg=5.
    • Of the other three, AE29F2008 and W29C020 are the same silicon under two names -- their
      database rows are identical on every field, including the chip id (0xDA45).

    This is a scope correction on which parts this defect can reach, not a statement that it has been
    fixed or released. Nothing here has shipped; the work referenced is on a milestone branch.

  3. henols commented on Sep 18, 2026

    @henols
    OwnerAuthor

    Fixed by refusing the write, not by adding a read-modify-write. A protocol 0x05 write whose start address or payload length is not a whole multiple of the chip's page size is now rejected before any page cycle runs, with a message naming the page size, the start address and the length. Both sides check: the host refuses before it opens the port, and the firmware refuses again on the write command. Whole-page writes are unchanged, so patching part of a page now means supplying the whole page.

    The fix is carried by firmware 3.0.0b33 and host 3.0.0b48:

    pip install --pre --upgrade firestarter
    firestarter fw --install --pre
    

    Verified on a W29C020 on a Leonardo and by the firmware and host test suites. No other protocol 0x05 part has been re-checked on hardware.

  4. added
    fix:releasedA released firmware or host version carries the fix; re-test to close
    on Sep 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcause:firmwareDefect in the Arduino firmwarefix:releasedA released firmware or host version carries the fix; re-test to close

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions