diff --git a/ChangeLog.d/fix_timing_alt.txt b/ChangeLog.d/fix_timing_alt.txt new file mode 100644 index 0000000000..86ec16d47e --- /dev/null +++ b/ChangeLog.d/fix_timing_alt.txt @@ -0,0 +1,5 @@ +Bugfix + * Fix a build issue when defining MBEDTLS_TIMING_ALT and MBEDTLS_SELF_TEST. + The library would not link if the user didn't provide an external self-test + function. The self-test is now provided regardless of the choice of + internal/alternative timing implementation. Fixes #6923. diff --git a/library/Makefile b/library/Makefile index 3b91e2556f..b011e88736 100644 --- a/library/Makefile +++ b/library/Makefile @@ -186,6 +186,13 @@ else all: shared static endif +ifdef TEST_TIMING_ALT_IMPL +OBJS_CRYPTO += external_timing_for_test.o +external_timing_for_test.o: ../tests/src/external_timing/external_timing_for_test.c + echo " CC $<" + $(CC) $(LOCAL_CFLAGS) $(CFLAGS) -o $@ -c $< +endif + static: libmbedcrypto.a libmbedx509.a libmbedtls.a cd ../tests && echo "This is a seedfile that contains 64 bytes (65 on Windows)......" > seedfile diff --git a/library/timing.c b/library/timing.c index d4f9554ee3..47e34f9227 100644 --- a/library/timing.c +++ b/library/timing.c @@ -352,9 +352,9 @@ int mbedtls_timing_get_delay(void *data) return 0; } +#endif /* !MBEDTLS_TIMING_ALT */ #if defined(MBEDTLS_SELF_TEST) - /* * Busy-waits for the given number of milliseconds. * Used for testing mbedtls_timing_hardclock. @@ -383,9 +383,8 @@ static void busy_msleep(unsigned long msec) mbedtls_printf(" cycles=%lu ratio=%lu millisecs=%lu secs=%lu hardfail=%d a=%lu b=%lu\n", \ cycles, ratio, millisecs, secs, hardfail, \ (unsigned long) a, (unsigned long) b); \ - mbedtls_printf(" elapsed(hires)=%lu elapsed(ctx)=%lu status(ctx)=%d\n", \ + mbedtls_printf(" elapsed(hires)=%lu status(ctx)=%d\n", \ mbedtls_timing_get_timer(&hires, 0), \ - mbedtls_timing_get_timer(&ctx.timer, 0), \ mbedtls_timing_get_delay(&ctx)); \ } \ return 1; \ @@ -526,5 +525,4 @@ hard_test_done: } #endif /* MBEDTLS_SELF_TEST */ -#endif /* !MBEDTLS_TIMING_ALT */ #endif /* MBEDTLS_TIMING_C */ diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh index 3266fea3ff..97d4296820 100755 --- a/tests/scripts/all.sh +++ b/tests/scripts/all.sh @@ -2728,6 +2728,19 @@ component_test_no_date_time () { make test } +component_test_alt_timing() { + msg "build: alternate timing implementation" + scripts/config.py set MBEDTLS_TIMING_ALT + make lib TEST_TIMING_ALT_IMPL=1 CFLAGS="-I../tests/src/external_timing" + + msg "test: MBEDTLS_TIMING_ALT - test suites" + make test TEST_TIMING_ALT_IMPL=1 CFLAGS="-I../tests/src/external_timing" + + msg "selftest - MBEDTLS-TIMING_ALT" + make programs TEST_TIMING_ALT_IMPL=1 CFLAGS="-I../../tests/src/external_timing -I../tests/src/external_timing" + programs/test/selftest +} + component_test_platform_calloc_macro () { msg "build: MBEDTLS_PLATFORM_{CALLOC/FREE}_MACRO enabled (ASan build)" scripts/config.py set MBEDTLS_PLATFORM_MEMORY diff --git a/tests/src/external_timing/external_timing_for_test.c b/tests/src/external_timing/external_timing_for_test.c new file mode 100644 index 0000000000..454ebbe10a --- /dev/null +++ b/tests/src/external_timing/external_timing_for_test.c @@ -0,0 +1,351 @@ +/** \file external_timing_for_test.c + * + * \brief Helper functions to test an alternate timing implementation. + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#if defined(MBEDTLS_TIMING_ALT) + +#if !defined(unix) && !defined(__unix__) && !defined(__unix) && \ + !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \ + !defined(__HAIKU__) && !defined(__midipix__) +#error "This module only works on Unix and Windows, see MBEDTLS_TIMING_C in config.h" +#endif + +/* *INDENT-OFF* */ +#ifndef asm +#define asm __asm +#endif +/* *INDENT-ON* */ + +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + +#include +#include + +struct _hr_time { + LARGE_INTEGER start; +}; + +#else + +#include +#include +#include +/* time.h should be included independently of MBEDTLS_HAVE_TIME. If the + * platform matches the ifdefs above, it will be used. */ +#include +#include +struct _hr_time { + struct timeval start; +}; +#endif /* _WIN32 && !EFIX64 && !EFI32 */ + +#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ + (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) + +#define HAVE_HARDCLOCK + +unsigned long mbedtls_timing_hardclock(void) +{ + unsigned long tsc; + __asm rdtsc + __asm mov[tsc], eax + return tsc; +} +#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && + ( _MSC_VER && _M_IX86 ) || __WATCOMC__ */ + +/* some versions of mingw-64 have 32-bit longs even on x84_64 */ +#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ + defined(__GNUC__) && (defined(__i386__) || ( \ + (defined(__amd64__) || defined(__x86_64__)) && __SIZEOF_LONG__ == 4)) + +#define HAVE_HARDCLOCK + +unsigned long mbedtls_timing_hardclock(void) +{ + unsigned long lo, hi; + asm volatile ("rdtsc" : "=a" (lo), "=d" (hi)); + return lo; +} +#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && + __GNUC__ && __i386__ */ + +#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ + defined(__GNUC__) && (defined(__amd64__) || defined(__x86_64__)) + +#define HAVE_HARDCLOCK + +unsigned long mbedtls_timing_hardclock(void) +{ + unsigned long lo, hi; + asm volatile ("rdtsc" : "=a" (lo), "=d" (hi)); + return lo | (hi << 32); +} +#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && + __GNUC__ && ( __amd64__ || __x86_64__ ) */ + +#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ + defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__)) + +#define HAVE_HARDCLOCK + +unsigned long mbedtls_timing_hardclock(void) +{ + unsigned long tbl, tbu0, tbu1; + + do { + asm volatile ("mftbu %0" : "=r" (tbu0)); + asm volatile ("mftb %0" : "=r" (tbl)); + asm volatile ("mftbu %0" : "=r" (tbu1)); + } while (tbu0 != tbu1); + + return tbl; +} +#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && + __GNUC__ && ( __powerpc__ || __ppc__ ) */ + +#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ + defined(__GNUC__) && defined(__sparc64__) + +#if defined(__OpenBSD__) +#warning OpenBSD does not allow access to tick register using software version instead +#else +#define HAVE_HARDCLOCK + +unsigned long mbedtls_timing_hardclock(void) +{ + unsigned long tick; + asm volatile ("rdpr %%tick, %0;" : "=&r" (tick)); + return tick; +} +#endif /* __OpenBSD__ */ +#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && + __GNUC__ && __sparc64__ */ + +#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ + defined(__GNUC__) && defined(__sparc__) && !defined(__sparc64__) + +#define HAVE_HARDCLOCK + +unsigned long mbedtls_timing_hardclock(void) +{ + unsigned long tick; + asm volatile (".byte 0x83, 0x41, 0x00, 0x00"); + asm volatile ("mov %%g1, %0" : "=r" (tick)); + return tick; +} +#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && + __GNUC__ && __sparc__ && !__sparc64__ */ + +#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ + defined(__GNUC__) && defined(__alpha__) + +#define HAVE_HARDCLOCK + +unsigned long mbedtls_timing_hardclock(void) +{ + unsigned long cc; + asm volatile ("rpcc %0" : "=r" (cc)); + return cc & 0xFFFFFFFF; +} +#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && + __GNUC__ && __alpha__ */ + +#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ + defined(__GNUC__) && defined(__ia64__) + +#define HAVE_HARDCLOCK + +unsigned long mbedtls_timing_hardclock(void) +{ + unsigned long itc; + asm volatile ("mov %0 = ar.itc" : "=r" (itc)); + return itc; +} +#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && + __GNUC__ && __ia64__ */ + +#if !defined(HAVE_HARDCLOCK) && defined(_MSC_VER) && \ + !defined(EFIX64) && !defined(EFI32) + +#define HAVE_HARDCLOCK + +unsigned long mbedtls_timing_hardclock(void) +{ + LARGE_INTEGER offset; + + QueryPerformanceCounter(&offset); + + return (unsigned long) (offset.QuadPart); +} +#endif /* !HAVE_HARDCLOCK && _MSC_VER && !EFIX64 && !EFI32 */ + +#if !defined(HAVE_HARDCLOCK) + +#define HAVE_HARDCLOCK + +static int hardclock_init = 0; +static struct timeval tv_init; + +unsigned long mbedtls_timing_hardclock(void) +{ + struct timeval tv_cur; + + if (hardclock_init == 0) { + gettimeofday(&tv_init, NULL); + hardclock_init = 1; + } + + gettimeofday(&tv_cur, NULL); + return (tv_cur.tv_sec - tv_init.tv_sec) * 1000000U + + (tv_cur.tv_usec - tv_init.tv_usec); +} +#endif /* !HAVE_HARDCLOCK */ + +volatile int mbedtls_timing_alarmed = 0; + +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + +unsigned long mbedtls_timing_get_timer(struct mbedtls_timing_hr_time *val, int reset) +{ + struct _hr_time *t = (struct _hr_time *) val; + + if (reset) { + QueryPerformanceCounter(&t->start); + return 0; + } else { + unsigned long delta; + LARGE_INTEGER now, hfreq; + QueryPerformanceCounter(&now); + QueryPerformanceFrequency(&hfreq); + delta = (unsigned long) ((now.QuadPart - t->start.QuadPart) * 1000ul + / hfreq.QuadPart); + return delta; + } +} + +/* It's OK to use a global because alarm() is supposed to be global anyway */ +static DWORD alarmMs; + +static void TimerProc(void *TimerContext) +{ + (void) TimerContext; + Sleep(alarmMs); + mbedtls_timing_alarmed = 1; + /* _endthread will be called implicitly on return + * That ensures execution of thread function's epilogue */ +} + +void mbedtls_set_alarm(int seconds) +{ + if (seconds == 0) { + /* No need to create a thread for this simple case. + * Also, this shorcut is more reliable at least on MinGW32 */ + mbedtls_timing_alarmed = 1; + return; + } + + mbedtls_timing_alarmed = 0; + alarmMs = seconds * 1000; + (void) _beginthread(TimerProc, 0, NULL); +} + +#else /* _WIN32 && !EFIX64 && !EFI32 */ + +unsigned long mbedtls_timing_get_timer(struct mbedtls_timing_hr_time *val, int reset) +{ + struct _hr_time *t = (struct _hr_time *) val; + + if (reset) { + gettimeofday(&t->start, NULL); + return 0; + } else { + unsigned long delta; + struct timeval now; + gettimeofday(&now, NULL); + delta = (now.tv_sec - t->start.tv_sec) * 1000ul + + (now.tv_usec - t->start.tv_usec) / 1000; + return delta; + } +} + +static void sighandler(int signum) +{ + mbedtls_timing_alarmed = 1; + signal(signum, sighandler); +} + +void mbedtls_set_alarm(int seconds) +{ + mbedtls_timing_alarmed = 0; + signal(SIGALRM, sighandler); + alarm(seconds); + if (seconds == 0) { + /* alarm(0) cancelled any previous pending alarm, but the + handler won't fire, so raise the flag straight away. */ + mbedtls_timing_alarmed = 1; + } +} + +#endif /* _WIN32 && !EFIX64 && !EFI32 */ + +/* + * Set delays to watch + */ +void mbedtls_timing_set_delay(void *data, uint32_t int_ms, uint32_t fin_ms) +{ + mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; + + ctx->int_ms = int_ms; + ctx->fin_ms = fin_ms; + + if (fin_ms != 0) { + (void) mbedtls_timing_get_timer(&ctx->timer, 1); + } +} + +/* + * Get number of delays expired + */ +int mbedtls_timing_get_delay(void *data) +{ + mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; + unsigned long elapsed_ms; + + if (ctx->fin_ms == 0) { + return -1; + } + + elapsed_ms = mbedtls_timing_get_timer(&ctx->timer, 0); + + if (elapsed_ms >= ctx->fin_ms) { + return 2; + } + + if (elapsed_ms >= ctx->int_ms) { + return 1; + } + + return 0; +} + +#endif /* MBEDTLS_TIMING_ALT */ diff --git a/tests/src/external_timing/timing_alt.h b/tests/src/external_timing/timing_alt.h new file mode 100644 index 0000000000..82e8c8b3d5 --- /dev/null +++ b/tests/src/external_timing/timing_alt.h @@ -0,0 +1,130 @@ +/* + * Copy of the internal MbedTLS timing implementation, to be used in tests. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EXTERNAL_TIMING_FOR_TEST_H +#define EXTERNAL_TIMING_FOR_TEST_H + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#include + +/** + * \brief timer structure + */ +struct mbedtls_timing_hr_time { + unsigned char opaque[32]; +}; + +/** + * \brief Context for mbedtls_timing_set/get_delay() + */ +typedef struct mbedtls_timing_delay_context { + struct mbedtls_timing_hr_time timer; + uint32_t int_ms; + uint32_t fin_ms; +} mbedtls_timing_delay_context; + +#else /* MBEDTLS_TIMING_ALT */ +#include "timing_alt.h" +#endif /* MBEDTLS_TIMING_ALT */ + +extern volatile int mbedtls_timing_alarmed; + +/** + * \brief Return the CPU cycle counter value + * + * \warning This is only a best effort! Do not rely on this! + * In particular, it is known to be unreliable on virtual + * machines. + * + * \note This value starts at an unspecified origin and + * may wrap around. + */ +unsigned long mbedtls_timing_hardclock(void); + +/** + * \brief Return the elapsed time in milliseconds + * + * \param val points to a timer structure + * \param reset If 0, query the elapsed time. Otherwise (re)start the timer. + * + * \return Elapsed time since the previous reset in ms. When + * restarting, this is always 0. + * + * \note To initialize a timer, call this function with reset=1. + * + * Determining the elapsed time and resetting the timer is not + * atomic on all platforms, so after the sequence + * `{ get_timer(1); ...; time1 = get_timer(1); ...; time2 = + * get_timer(0) }` the value time1+time2 is only approximately + * the delay since the first reset. + */ +unsigned long mbedtls_timing_get_timer(struct mbedtls_timing_hr_time *val, int reset); + +/** + * \brief Setup an alarm clock + * + * \param seconds delay before the "mbedtls_timing_alarmed" flag is set + * (must be >=0) + * + * \warning Only one alarm at a time is supported. In a threaded + * context, this means one for the whole process, not one per + * thread. + */ +void mbedtls_set_alarm(int seconds); + +/** + * \brief Set a pair of delays to watch + * (See \c mbedtls_timing_get_delay().) + * + * \param data Pointer to timing data. + * Must point to a valid \c mbedtls_timing_delay_context struct. + * \param int_ms First (intermediate) delay in milliseconds. + * The effect if int_ms > fin_ms is unspecified. + * \param fin_ms Second (final) delay in milliseconds. + * Pass 0 to cancel the current delay. + * + * \note To set a single delay, either use \c mbedtls_timing_set_timer + * directly or use this function with int_ms == fin_ms. + */ +void mbedtls_timing_set_delay(void *data, uint32_t int_ms, uint32_t fin_ms); + +/** + * \brief Get the status of delays + * (Memory helper: number of delays passed.) + * + * \param data Pointer to timing data + * Must point to a valid \c mbedtls_timing_delay_context struct. + * + * \return -1 if cancelled (fin_ms = 0), + * 0 if none of the delays are passed, + * 1 if only the intermediate delay is passed, + * 2 if the final delay is passed. + */ +int mbedtls_timing_get_delay(void *data); + +#ifdef __cplusplus +} + +#endif /* EXTERNAL_TIMING_FOR_TEST_H */