From 815920c87f9bda6b3fb7ec24686154210c9e8774 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 14 Oct 2022 15:49:52 +1100 Subject: [PATCH] extmod/utime_mphal: Make ticks_add check for overflow of delta. Work done in collaboration with @jimmo. Signed-off-by: Damien George --- extmod/utime_mphal.c | 13 +++++++++++ tests/extmod/ticks_add.py | 42 +++++++++++++++++++++++++++++++++++ tests/extmod/ticks_add.py.exp | 36 ++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 tests/extmod/ticks_add.py create mode 100644 tests/extmod/ticks_add.py.exp diff --git a/extmod/utime_mphal.c b/extmod/utime_mphal.c index 3d1cdfd820..cd91c95530 100644 --- a/extmod/utime_mphal.c +++ b/extmod/utime_mphal.c @@ -95,6 +95,19 @@ STATIC mp_obj_t time_ticks_add(mp_obj_t ticks_in, mp_obj_t delta_in) { // we assume that first argument come from ticks_xx so is small int mp_uint_t ticks = MP_OBJ_SMALL_INT_VALUE(ticks_in); mp_uint_t delta = mp_obj_get_int(delta_in); + + // Check that delta does not overflow the range that ticks_diff can handle. + // This ensures the following: + // - ticks_diff(ticks_add(T, delta), T) == delta + // - ticks_diff(T, ticks_add(T, delta)) == -delta + // The latter requires excluding delta=-TICKS_PERIOD/2. + // + // This unsigned comparison is equivalent to a signed comparison of: + // delta <= TICKS_PERIOD/2 || delta >= TICKS_PERIOD/2 + if (delta + MICROPY_PY_UTIME_TICKS_PERIOD / 2 - 1 >= MICROPY_PY_UTIME_TICKS_PERIOD - 1) { + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("ticks interval overflow")); + } + return MP_OBJ_NEW_SMALL_INT((ticks + delta) & (MICROPY_PY_UTIME_TICKS_PERIOD - 1)); } MP_DEFINE_CONST_FUN_OBJ_2(mp_utime_ticks_add_obj, time_ticks_add); diff --git a/tests/extmod/ticks_add.py b/tests/extmod/ticks_add.py new file mode 100644 index 0000000000..2f1ba6c810 --- /dev/null +++ b/tests/extmod/ticks_add.py @@ -0,0 +1,42 @@ +try: + from utime import ticks_diff, ticks_add +except ImportError: + print("SKIP") + raise SystemExit + +# Maximum value returned from ticks_add, ticks_ms, etc. +TICKS_MAX = ticks_add(0, -1) +# Maximum value returned from ticks_diff. +TICKS_INTERVAL_MAX = TICKS_MAX // 2 + +# Invariants: +# - ticks_diff(ticks_add(T, delta), T) == delta +# - ticks_diff(T, ticks_add(T, delta)) == -delta + +# Check actual values of ticks_add. +print(ticks_add(20, 12)) +print(ticks_add(20, -12)) + +# Check invariant. +print(ticks_diff(ticks_add(100, 123), 100)) +print(ticks_diff(ticks_add(100, -123), 100)) +print(ticks_diff(100, ticks_add(100, 123))) +print(ticks_diff(100, ticks_add(100, -123))) + +# Check limits. +for T in (0, 10, TICKS_MAX): + for delta in ( + -TICKS_INTERVAL_MAX - 1, + -TICKS_INTERVAL_MAX, + 0, + TICKS_INTERVAL_MAX, + TICKS_INTERVAL_MAX + 1, + ): + try: + print(ticks_diff(ticks_add(T, delta), T) == delta) + except OverflowError: + print("OverflowError") + try: + print(ticks_diff(T, ticks_add(T, delta)) == -delta) + except OverflowError: + print("OverflowError") diff --git a/tests/extmod/ticks_add.py.exp b/tests/extmod/ticks_add.py.exp new file mode 100644 index 0000000000..60dc6f5afd --- /dev/null +++ b/tests/extmod/ticks_add.py.exp @@ -0,0 +1,36 @@ +32 +8 +123 +-123 +-123 +123 +OverflowError +OverflowError +True +True +True +True +True +True +OverflowError +OverflowError +OverflowError +OverflowError +True +True +True +True +True +True +OverflowError +OverflowError +OverflowError +OverflowError +True +True +True +True +True +True +OverflowError +OverflowError