pyOCD fast_program=True sector/page data-loss issue
Title
fast_program=True causes silent data loss on targets with sector_size > page_size (erased sectors only partially re-programmed)
Environment
pyocd 0.44.1, also present at current git HEAD; Linux x86-64; STLINK-V3; STM32F427 with target override stm32f429xi.
Steps
- Flash image A.
- Modify one 4 KB page within a 16 KB sector.
- Program with
-O smart_flash=true -O fast_program=true -O chip_erase=sector.
Expected
Dirty sectors are erased and fully re-programmed; unchanged sectors are skipped.
Actual
Dirty sectors are erased, but only the changed 4 KB page is programmed. The other 12 KB of each dirty 16 KB sector reads back as 0xFF.
Observed log line:
Erased 32768 bytes (2 sectors), programmed 8192 bytes (2 pages), skipped 57344 bytes (14 pages)
The data loss was independently confirmed by read-back with st-flash.
Cause
With assume_estimate_correct=True, _analyze_pages_with_crc32() resolves matching pages to same=True. Then _finalize_smart_flash() early-returns because no pages are left with same is None, and _scan_pages_for_same(), the only place that calls sector.mark_all_pages_not_same() for dirty sectors, never runs.
The sector-erase programmer then erases sectors with any not-same page but programs only the not-same pages. This affects any region where sector_size > page_size and a dirty sector contains a CRC-matching page. This is not the documented CRC-collision caveat; the digests were correct.
Suggested Fix
Run the mark-all-pages-in-dirty-sectors loop unconditionally in _finalize_smart_flash() after analysis.
def _finalize_smart_flash(self, progress_cb, fast_verify: bool) -> None:
if any(page.same is None for page in self.page_list):
self._compute_sector_erase_pages_and_weight(fast_verify)
if any(page.same is None for page in self.page_list):
self._scan_pages_for_same(progress_cb)
for sector in self.sector_list:
if sector.are_any_pages_not_same():
sector.mark_all_pages_not_same()
The duplicate marking inside _scan_pages_for_same() is idempotent and can be left or removed.
Notes
- Verified on real STM32F427 silicon with STLINK-V3 serial
004D00373033510135393935.
- Default pyOCD mode,
fast_program=False, passes the same incremental flash proof.
- Trigger conditions:
fast_program=True; sector_size > page_size; partial-sector dirty data where at least one page in an erased sector matches the existing target contents.
- Re-run an upstream issue search immediately before filing; the original search found no existing report.
pyOCD
fast_program=Truesector/page data-loss issueTitle
fast_program=Truecauses silent data loss on targets with sector_size > page_size (erased sectors only partially re-programmed)Environment
pyocd 0.44.1, also present at current git HEAD; Linux x86-64; STLINK-V3; STM32F427 with target override
stm32f429xi.Steps
-O smart_flash=true -O fast_program=true -O chip_erase=sector.Expected
Dirty sectors are erased and fully re-programmed; unchanged sectors are skipped.
Actual
Dirty sectors are erased, but only the changed 4 KB page is programmed. The other 12 KB of each dirty 16 KB sector reads back as
0xFF.Observed log line:
The data loss was independently confirmed by read-back with
st-flash.Cause
With
assume_estimate_correct=True,_analyze_pages_with_crc32()resolves matching pages tosame=True. Then_finalize_smart_flash()early-returns because no pages are left withsame is None, and_scan_pages_for_same(), the only place that callssector.mark_all_pages_not_same()for dirty sectors, never runs.The sector-erase programmer then erases sectors with any not-same page but programs only the not-same pages. This affects any region where
sector_size > page_sizeand a dirty sector contains a CRC-matching page. This is not the documented CRC-collision caveat; the digests were correct.Suggested Fix
Run the mark-all-pages-in-dirty-sectors loop unconditionally in
_finalize_smart_flash()after analysis.The duplicate marking inside
_scan_pages_for_same()is idempotent and can be left or removed.Notes
004D00373033510135393935.fast_program=False, passes the same incremental flash proof.fast_program=True;sector_size > page_size; partial-sector dirty data where at least one page in an erased sector matches the existing target contents.