From 0dac26b9f000a30265588d2523f17191ecb88bd4 Mon Sep 17 00:00:00 2001 From: Shrenik Mensinkai Date: Sun, 30 Aug 2026 01:22:03 +0530 Subject: [PATCH] fix(ota): reject oversized chunks without wrapping the length add eos_ota_write_chunk compared bytes_written + len to total_size, so a huge len could wrap and still be accepted. Check remaining space and cover overflow and wrap in the host tests. Co-authored-by: Cursor # Conflicts: # tests/test_ota.c --- services/ota/src/ota.c | 5 ++++- tests/test_ota.c | 44 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/services/ota/src/ota.c b/services/ota/src/ota.c index 9d880866..9260faf6 100644 --- a/services/ota/src/ota.c +++ b/services/ota/src/ota.c @@ -67,7 +67,10 @@ int eos_ota_begin(const eos_ota_source_t *source) { int eos_ota_write_chunk(const uint8_t *data, size_t len) { if (!g_ota.initialized || !g_ota.in_progress || !data) return -1; - if (g_ota.bytes_written + len > g_ota.total_size) return -1; + /* Compare against remaining space. Adding len can wrap and accept a huge chunk. */ + if (g_ota.bytes_written > g_ota.total_size || + len > (size_t)(g_ota.total_size - g_ota.bytes_written)) + return -1; eos_sha256_update(&g_ota.hash_ctx, data, len); g_ota.bytes_written += (uint32_t)len; if (g_ota.progress_cb && g_ota.total_size > 0) { diff --git a/tests/test_ota.c b/tests/test_ota.c index ba459758..0f7a0636 100644 --- a/tests/test_ota.c +++ b/tests/test_ota.c @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include "eos/ota.h" #include "eos/crypto.h" @@ -130,7 +132,6 @@ static void test_ota_status(void) { printf("[PASS] ota status\n"); } - /* expected_sha256 arrives with the update, so an attacker who swaps the image * swaps the digest too. Passing eos_ota_verify() must not be enough to install. */ static void test_ota_apply_refuses_without_authenticator(void) { @@ -200,6 +201,43 @@ static void test_ota_unapplied_slot_is_not_a_rollback_target(void) { printf("[PASS] ota unapplied slot is not a rollback target\n"); } +static void test_ota_write_chunk_overflow(void) { + eos_ota_init(); + uint8_t fw[64]; + memset(fw, 0xAA, sizeof(fw)); + eos_ota_source_t src; + memset(&src, 0, sizeof(src)); + src.expected_size = sizeof(fw); + assert(eos_ota_begin(&src) == 0); + assert(eos_ota_write_chunk(fw, 32) == 0); + assert(eos_ota_write_chunk(fw, 33) == -1); + + eos_ota_status_t st; + assert(eos_ota_get_status(&st) == 0); + assert(st.bytes_received == 32); + eos_ota_deinit(); + printf("[PASS] ota write chunk overflow\n"); +} + +static void test_ota_write_chunk_wrap(void) { + eos_ota_init(); + uint8_t fw[64]; + memset(fw, 0xAA, sizeof(fw)); + eos_ota_source_t src; + memset(&src, 0, sizeof(src)); + src.expected_size = sizeof(fw); + assert(eos_ota_begin(&src) == 0); + /* One accepted byte so bytes_written + SIZE_MAX wraps on the old add. */ + assert(eos_ota_write_chunk(fw, 1) == 0); + assert(eos_ota_write_chunk(fw, SIZE_MAX) == -1); + + eos_ota_status_t st; + assert(eos_ota_get_status(&st) == 0); + assert(st.bytes_received == 1); + eos_ota_deinit(); + printf("[PASS] ota write chunk wrap\n"); +} + int main(void) { printf("=== EoS OTA Tests ===\n"); test_ota_init(); @@ -211,6 +249,8 @@ int main(void) { test_ota_apply_honours_a_rejecting_authenticator(); test_ota_authenticator_sees_the_computed_digest(); test_ota_unapplied_slot_is_not_a_rollback_target(); - printf("=== ALL OTA TESTS PASSED (9/9) ===\n"); + test_ota_write_chunk_overflow(); + test_ota_write_chunk_wrap(); + printf("=== ALL OTA TESTS PASSED (11/11) ===\n"); return 0; } \ No newline at end of file