Preserve multipart OTA MD5 after Update initialization
Arduino-ESP32 2.0.17 UpdateClass::begin() clears _target_md5 before initializing the digest builder (Updater.cpp:123-126). The installed pinned framework package is 3.20017.241212+sha.dcc1105b. Setting the multipart digest before begin therefore skipped the final comparison in end(), which only compares a nonempty _target_md5 (Updater.cpp:303-308). Set the supplied MD5 after Update.begin succeeds. If the digest is rejected, abort the begun update, release upload admission, and return HTTP 400 before assigning an owner or extending its receive timeout. Model the pinned begin-clears-digest behavior in the host fixture using independent digest vectors. Verify malformed and mismatched digests, actual byte corruption, disconnect cleanup, and later valid admission. Negative controls restore the original ordering and remove abort/busy cleanup. Validation: python3 -B test/test_async_web_lifecycle.py -v passed 4/4, including C++ ASan/UBSan execution and negative controls. No PlatformIO or hardware validation was run in this worktree.
This commit is contained in:
1 parent
be2ab346b0
commit
70bf4bdfeb
3 files changed
+127
-23
No files matched your search
@@ -67,6 +67,22 @@ static std::atomic<bool> ota_upload_busy{false};"""
|
||||
with self.assertRaises(AssertionError):
|
||||
self.compile_and_run(old,"-DESP32=1","-DTEST_OTA_BODY_TIMEOUT=1")
|
||||
|
||||
def test_md5_survives_begin_and_rejected_digest_releases_update(self):
|
||||
fixture=self.uploader_fixture()
|
||||
self.compile_and_run(fixture,"-DESP32=1")
|
||||
guard=extract_braced(fixture,"if(!Update.setMD5(")
|
||||
marker=" #if defined(ESP8266)\n int cmd"
|
||||
before_begin=fixture.replace(guard,"",1).replace(marker,guard+"\n"+marker,1)
|
||||
self.assertNotEqual(fixture,before_begin,"The negative control must set MD5 before begin")
|
||||
with self.assertRaises(AssertionError):
|
||||
self.compile_and_run(before_begin,"-DESP32=1")
|
||||
for cleanup in ("Update.abort();", "ota_upload_busy.store(false);"):
|
||||
with self.subTest(rejected_digest_cleanup=cleanup):
|
||||
missing=fixture.replace(guard,guard.replace(cleanup,"",1),1)
|
||||
self.assertNotEqual(fixture,missing,"The negative control must remove rejection cleanup")
|
||||
with self.assertRaises(AssertionError):
|
||||
self.compile_and_run(missing,"-DESP32=1")
|
||||
|
||||
def test_tcp_listener_can_rebind_with_completed_connections(self):
|
||||
cached=ROOT/".pio/libdeps/heltec_v4_repeater_observer_mqtt/AsyncTCP/src/AsyncTCP.cpp"
|
||||
source=(cached.read_text() if cached.exists() else urllib.request.urlopen(
|
||||
|
||||
Reference in new issue
Block a user