From 4f9e257c77c2ab1b8f6f76859bc07cafa95a7595 Mon Sep 17 00:00:00 2001 From: Chen Chen Date: Thu, 30 Jul 2026 17:04:17 +0800 Subject: [PATCH] fix(hal): prevent overflow in fractional clock division Co-authored-by: Cursor --- components/hal/hal_utils.c | 11 ++++++----- .../hal/test_apps/hal_utils/main/test_calc_clk_div.c | 10 +++++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/components/hal/hal_utils.c b/components/hal/hal_utils.c index c1e153532b6..bd1d68d3799 100644 --- a/components/hal/hal_utils.c +++ b/components/hal/hal_utils.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,7 +16,7 @@ #endif __attribute__((always_inline)) -static inline uint32_t _sub_abs(uint32_t a, uint32_t b) +static inline uint64_t _sub_abs(uint64_t a, uint64_t b) { return a > b ? a - b : b - a; } @@ -78,9 +78,10 @@ uint32_t hal_utils_calc_clk_div_frac_accurate(const hal_utils_clk_info_t *clk_in // Carry bit if the decimal is greater than 1.0 - 1.0 / ((max_fract - 1) * 2) if (freq_error < clk_info->exp_freq_hz - clk_info->exp_freq_hz / (clk_info->max_fract - 1) * 2) { // Search the closest fraction, time complexity O(n) - for (uint32_t sub = 0, a = 2, b = 0, min = UINT32_MAX; min && a < clk_info->max_fract; a++) { - b = (a * freq_error + clk_info->exp_freq_hz / 2) / clk_info->exp_freq_hz; - sub = _sub_abs(clk_info->exp_freq_hz * b, freq_error * a); + uint64_t min = UINT64_MAX; + for (uint32_t a = 2, b = 0; min && a < clk_info->max_fract; a++) { + b = ((uint64_t)a * freq_error + clk_info->exp_freq_hz / 2) / clk_info->exp_freq_hz; + uint64_t sub = _sub_abs((uint64_t)clk_info->exp_freq_hz * b, (uint64_t)freq_error * a); if (sub < min) { div_denom = a; div_numer = b; diff --git a/components/hal/test_apps/hal_utils/main/test_calc_clk_div.c b/components/hal/test_apps/hal_utils/main/test_calc_clk_div.c index c1ab968b69f..30c8b60087e 100644 --- a/components/hal/test_apps/hal_utils/main/test_calc_clk_div.c +++ b/components/hal/test_apps/hal_utils/main/test_calc_clk_div.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -80,6 +80,14 @@ TEST_CASE("test_fractal_division", "[clk_div]") TEST_ASSERT_EQUAL_UINT32(9, clk_div.integer); TEST_ASSERT_UINT32_WITHIN(clk_info.exp_freq_hz * 0.0001, clk_info.exp_freq_hz, real_freq); + // Fractional division whose intermediate products exceed UINT32_MAX + clk_info.exp_freq_hz = 42 * 1000 * 1000 + 336 * 1000; + real_freq = hal_utils_calc_clk_div_frac_accurate(&clk_info, &clk_div); + TEST_ASSERT_EQUAL_UINT32(3, clk_div.integer); + TEST_ASSERT_EQUAL_UINT32(286, clk_div.numerator); + TEST_ASSERT_EQUAL_UINT32(367, clk_div.denominator); + TEST_ASSERT_EQUAL_UINT32(42335977, real_freq); + // Fractal division with no error clk_info.exp_freq_hz = 50 * 1000 * 1000; real_freq = hal_utils_calc_clk_div_frac_fast(&clk_info, &clk_div);