blob: 38059343d84533567f084250fd35ab84dc27fe3a [file] [log] [blame]
Gilles Peskine1061ec62021-01-29 21:17:11 +01001/** Mutex usage verification framework. */
2
3/*
4 * Copyright The Mbed TLS Contributors
Dave Rodgman16799db2023-11-02 19:47:20 +00005 * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
Gilles Peskine1061ec62021-01-29 21:17:11 +01006 */
7
8#include <test/helpers.h>
Paul Elliott17c119a2023-12-08 16:55:03 +00009#include <test/threading_helpers.h>
Gilles Peskine1061ec62021-01-29 21:17:11 +010010#include <test/macros.h>
11
12#if defined(MBEDTLS_TEST_MUTEX_USAGE)
13
14#include "mbedtls/threading.h"
15
Gilles Peskine2a4c5982021-01-29 21:18:09 +010016/** Mutex usage verification framework.
17 *
18 * The mutex usage verification code below aims to detect bad usage of
19 * Mbed TLS's mutex abstraction layer at runtime. Note that this is solely
20 * about the use of the mutex itself, not about checking whether the mutex
21 * correctly protects whatever it is supposed to protect.
22 *
23 * The normal usage of a mutex is:
24 * ```
25 * digraph mutex_states {
26 * "UNINITIALIZED"; // the initial state
27 * "IDLE";
28 * "FREED";
29 * "LOCKED";
30 * "UNINITIALIZED" -> "IDLE" [label="init"];
31 * "FREED" -> "IDLE" [label="init"];
32 * "IDLE" -> "LOCKED" [label="lock"];
33 * "LOCKED" -> "IDLE" [label="unlock"];
34 * "IDLE" -> "FREED" [label="free"];
35 * }
36 * ```
37 *
38 * All bad transitions that can be unambiguously detected are reported.
39 * An attempt to use an uninitialized mutex cannot be detected in general
40 * since the memory content may happen to denote a valid state. For the same
41 * reason, a double init cannot be detected.
42 * All-bits-zero is the state of a freed mutex, which is distinct from an
43 * initialized mutex, so attempting to use zero-initialized memory as a mutex
44 * without calling the init function is detected.
45 *
Gilles Peskinef96d3d82021-01-29 22:20:32 +010046 * The framework attempts to detect missing calls to init and free by counting
47 * calls to init and free. If there are more calls to init than free, this
48 * means that a mutex is not being freed somewhere, which is a memory leak
49 * on platforms where a mutex consumes resources other than the
50 * mbedtls_threading_mutex_t object itself. If there are more calls to free
51 * than init, this indicates a missing init, which is likely to be detected
52 * by an attempt to lock the mutex as well. A limitation of this framework is
53 * that it cannot detect scenarios where there is exactly the same number of
54 * calls to init and free but the calls don't match. A bug like this is
55 * unlikely to happen uniformly throughout the whole test suite though.
56 *
Gilles Peskine2a4c5982021-01-29 21:18:09 +010057 * If an error is detected, this framework will report what happened and the
58 * test case will be marked as failed. Unfortunately, the error report cannot
59 * indicate the exact location of the problematic call. To locate the error,
60 * use a debugger and set a breakpoint on mbedtls_test_mutex_usage_error().
61 */
Paul Elliott9e259362023-11-15 11:33:32 +000062enum value_of_mutex_state_field {
63 /* Potential values for the state field of mbedtls_threading_mutex_t.
Gilles Peskine39a1a262021-02-09 15:35:29 +010064 * Note that MUTEX_FREED must be 0 and MUTEX_IDLE must be 1 for
65 * compatibility with threading_mutex_init_pthread() and
66 * threading_mutex_free_pthread(). MUTEX_LOCKED could be any nonzero
67 * value. */
Paul Elliott5fa986c2023-11-10 14:05:09 +000068 MUTEX_FREED = 0, //! < Set by mbedtls_test_wrap_mutex_free
69 MUTEX_IDLE = 1, //! < Set by mbedtls_test_wrap_mutex_init and by mbedtls_test_wrap_mutex_unlock
70 MUTEX_LOCKED = 2, //! < Set by mbedtls_test_wrap_mutex_lock
Gilles Peskine2a4c5982021-01-29 21:18:09 +010071};
72
Gilles Peskine449bd832023-01-11 14:50:10 +010073typedef struct {
74 void (*init)(mbedtls_threading_mutex_t *);
75 void (*free)(mbedtls_threading_mutex_t *);
76 int (*lock)(mbedtls_threading_mutex_t *);
77 int (*unlock)(mbedtls_threading_mutex_t *);
Gilles Peskine1061ec62021-01-29 21:17:11 +010078} mutex_functions_t;
79static mutex_functions_t mutex_functions;
80
Paul Elliott392ed3f2023-11-24 15:48:28 +000081/**
82 * The mutex used to guard live_mutexes below and access to the status variable
83 * in every mbedtls_threading_mutex_t.
84 * Note that we are not reporting any errors when locking and unlocking this
85 * mutex. This is for a couple of reasons:
86 *
87 * 1. We have no real way of reporting any errors with this mutex - we cannot
88 * report it back to the caller, as the failure was not that of the mutex
89 * passed in. We could fail the test, but again this would indicate a problem
90 * with the test code that did not exist.
91 *
92 * 2. Any failure to lock is unlikely to be intermittent, and will thus not
93 * give false test results - the overall result would be to turn off the
94 * testing. This is not a situation that is likely to happen with normal
95 * testing and we still have TSan to fall back on should this happen.
96 */
Paul Elliott37746372023-11-12 19:05:57 +000097mbedtls_threading_mutex_t mbedtls_test_mutex_mutex;
98
Paul Elliott392ed3f2023-11-24 15:48:28 +000099/**
100 * The total number of calls to mbedtls_mutex_init(), minus the total number
101 * of calls to mbedtls_mutex_free().
Gilles Peskinef96d3d82021-01-29 22:20:32 +0100102 *
Paul Elliott392ed3f2023-11-24 15:48:28 +0000103 * Do not read or write without holding mbedtls_test_mutex_mutex (above). Reset
104 * to 0 after each test case.
Gilles Peskinef96d3d82021-01-29 22:20:32 +0100105 */
106static int live_mutexes;
107
Gilles Peskine449bd832023-01-11 14:50:10 +0100108static void mbedtls_test_mutex_usage_error(mbedtls_threading_mutex_t *mutex,
109 const char *msg)
Gilles Peskine2a4c5982021-01-29 21:18:09 +0100110{
111 (void) mutex;
Paul Elliott37746372023-11-12 19:05:57 +0000112
Gilles Peskine449bd832023-01-11 14:50:10 +0100113 if (mbedtls_test_info.mutex_usage_error == NULL) {
Gilles Peskine2a4c5982021-01-29 21:18:09 +0100114 mbedtls_test_info.mutex_usage_error = msg;
Gilles Peskine449bd832023-01-11 14:50:10 +0100115 }
116 mbedtls_fprintf(stdout, "[mutex: %s] ", msg);
Gilles Peskine2a4c5982021-01-29 21:18:09 +0100117 /* Don't mark the test as failed yet. This way, if the test fails later
118 * for a functional reason, the test framework will report the message
119 * and location for this functional reason. If the test passes,
120 * mbedtls_test_mutex_usage_check() will mark it as failed. */
121}
122
Gilles Peskine449bd832023-01-11 14:50:10 +0100123static void mbedtls_test_wrap_mutex_init(mbedtls_threading_mutex_t *mutex)
Gilles Peskine1061ec62021-01-29 21:17:11 +0100124{
Gilles Peskine449bd832023-01-11 14:50:10 +0100125 mutex_functions.init(mutex);
Paul Elliott5fa986c2023-11-10 14:05:09 +0000126
127 if (mutex_functions.lock(&mbedtls_test_mutex_mutex) == 0) {
128 mutex->state = MUTEX_IDLE;
Gilles Peskinef96d3d82021-01-29 22:20:32 +0100129 ++live_mutexes;
Paul Elliott5fa986c2023-11-10 14:05:09 +0000130
131 mutex_functions.unlock(&mbedtls_test_mutex_mutex);
Gilles Peskine449bd832023-01-11 14:50:10 +0100132 }
Gilles Peskine1061ec62021-01-29 21:17:11 +0100133}
134
Gilles Peskine449bd832023-01-11 14:50:10 +0100135static void mbedtls_test_wrap_mutex_free(mbedtls_threading_mutex_t *mutex)
Gilles Peskine1061ec62021-01-29 21:17:11 +0100136{
Paul Elliott37746372023-11-12 19:05:57 +0000137 if (mutex_functions.lock(&mbedtls_test_mutex_mutex) == 0) {
Paul Elliott5fa986c2023-11-10 14:05:09 +0000138
Paul Elliott9e259362023-11-15 11:33:32 +0000139 switch (mutex->state) {
Paul Elliott37746372023-11-12 19:05:57 +0000140 case MUTEX_FREED:
141 mbedtls_test_mutex_usage_error(mutex, "free without init or double free");
142 break;
143 case MUTEX_IDLE:
Paul Elliott9e259362023-11-15 11:33:32 +0000144 mutex->state = MUTEX_FREED;
Paul Elliott37746372023-11-12 19:05:57 +0000145 --live_mutexes;
146 break;
147 case MUTEX_LOCKED:
148 mbedtls_test_mutex_usage_error(mutex, "free without unlock");
149 break;
150 default:
151 mbedtls_test_mutex_usage_error(mutex, "corrupted state");
152 break;
153 }
154
155 mutex_functions.unlock(&mbedtls_test_mutex_mutex);
Gilles Peskine449bd832023-01-11 14:50:10 +0100156 }
157 mutex_functions.free(mutex);
Gilles Peskine1061ec62021-01-29 21:17:11 +0100158}
159
Gilles Peskine449bd832023-01-11 14:50:10 +0100160static int mbedtls_test_wrap_mutex_lock(mbedtls_threading_mutex_t *mutex)
Gilles Peskine1061ec62021-01-29 21:17:11 +0100161{
Paul Elliott37746372023-11-12 19:05:57 +0000162 /* Lock the passed in mutex first, so that the only way to change the state
163 * is to hold the passed in and internal mutex - otherwise we create a race
164 * condition. */
Gilles Peskine449bd832023-01-11 14:50:10 +0100165 int ret = mutex_functions.lock(mutex);
Paul Elliott37746372023-11-12 19:05:57 +0000166 if (mutex_functions.lock(&mbedtls_test_mutex_mutex) == 0) {
Paul Elliott9e259362023-11-15 11:33:32 +0000167 switch (mutex->state) {
Paul Elliott37746372023-11-12 19:05:57 +0000168 case MUTEX_FREED:
169 mbedtls_test_mutex_usage_error(mutex, "lock without init");
170 break;
171 case MUTEX_IDLE:
172 if (ret == 0) {
Paul Elliott9e259362023-11-15 11:33:32 +0000173 mutex->state = MUTEX_LOCKED;
Paul Elliott37746372023-11-12 19:05:57 +0000174 }
175 break;
176 case MUTEX_LOCKED:
177 mbedtls_test_mutex_usage_error(mutex, "double lock");
178 break;
179 default:
180 mbedtls_test_mutex_usage_error(mutex, "corrupted state");
181 break;
182 }
183
184 mutex_functions.unlock(&mbedtls_test_mutex_mutex);
Gilles Peskine2a4c5982021-01-29 21:18:09 +0100185 }
Gilles Peskine449bd832023-01-11 14:50:10 +0100186 return ret;
Gilles Peskine1061ec62021-01-29 21:17:11 +0100187}
188
Gilles Peskine449bd832023-01-11 14:50:10 +0100189static int mbedtls_test_wrap_mutex_unlock(mbedtls_threading_mutex_t *mutex)
Gilles Peskine1061ec62021-01-29 21:17:11 +0100190{
Paul Elliott37746372023-11-12 19:05:57 +0000191 /* Lock the internal mutex first and change state, so that the only way to
192 * change the state is to hold the passed in and internal mutex - otherwise
193 * we create a race condition. */
194 if (mutex_functions.lock(&mbedtls_test_mutex_mutex) == 0) {
Paul Elliott9e259362023-11-15 11:33:32 +0000195 switch (mutex->state) {
Paul Elliott37746372023-11-12 19:05:57 +0000196 case MUTEX_FREED:
197 mbedtls_test_mutex_usage_error(mutex, "unlock without init");
198 break;
199 case MUTEX_IDLE:
200 mbedtls_test_mutex_usage_error(mutex, "unlock without lock");
201 break;
202 case MUTEX_LOCKED:
Paul Elliott9e259362023-11-15 11:33:32 +0000203 mutex->state = MUTEX_IDLE;
Paul Elliott37746372023-11-12 19:05:57 +0000204 break;
205 default:
206 mbedtls_test_mutex_usage_error(mutex, "corrupted state");
207 break;
208 }
209 mutex_functions.unlock(&mbedtls_test_mutex_mutex);
Gilles Peskine2a4c5982021-01-29 21:18:09 +0100210 }
Paul Elliott37746372023-11-12 19:05:57 +0000211 return mutex_functions.unlock(mutex);
Gilles Peskine1061ec62021-01-29 21:17:11 +0100212}
213
Gilles Peskine449bd832023-01-11 14:50:10 +0100214void mbedtls_test_mutex_usage_init(void)
Gilles Peskine1061ec62021-01-29 21:17:11 +0100215{
216 mutex_functions.init = mbedtls_mutex_init;
217 mutex_functions.free = mbedtls_mutex_free;
218 mutex_functions.lock = mbedtls_mutex_lock;
219 mutex_functions.unlock = mbedtls_mutex_unlock;
220 mbedtls_mutex_init = &mbedtls_test_wrap_mutex_init;
221 mbedtls_mutex_free = &mbedtls_test_wrap_mutex_free;
222 mbedtls_mutex_lock = &mbedtls_test_wrap_mutex_lock;
223 mbedtls_mutex_unlock = &mbedtls_test_wrap_mutex_unlock;
Paul Elliott37746372023-11-12 19:05:57 +0000224
225 mutex_functions.init(&mbedtls_test_mutex_mutex);
Gilles Peskine1061ec62021-01-29 21:17:11 +0100226}
227
Gilles Peskine449bd832023-01-11 14:50:10 +0100228void mbedtls_test_mutex_usage_check(void)
Gilles Peskine2a4c5982021-01-29 21:18:09 +0100229{
Gilles Peskine449bd832023-01-11 14:50:10 +0100230 if (live_mutexes != 0) {
Gilles Peskinef96d3d82021-01-29 22:20:32 +0100231 /* A positive number (more init than free) means that a mutex resource
232 * is leaking (on platforms where a mutex consumes more than the
233 * mbedtls_threading_mutex_t object itself). The rare case of a
234 * negative number means a missing init somewhere. */
Gilles Peskine449bd832023-01-11 14:50:10 +0100235 mbedtls_fprintf(stdout, "[mutex: %d leaked] ", live_mutexes);
Gilles Peskinef96d3d82021-01-29 22:20:32 +0100236 live_mutexes = 0;
Gilles Peskine449bd832023-01-11 14:50:10 +0100237 if (mbedtls_test_info.mutex_usage_error == NULL) {
Gilles Peskinef96d3d82021-01-29 22:20:32 +0100238 mbedtls_test_info.mutex_usage_error = "missing free";
Gilles Peskine449bd832023-01-11 14:50:10 +0100239 }
Gilles Peskinef96d3d82021-01-29 22:20:32 +0100240 }
Gilles Peskine449bd832023-01-11 14:50:10 +0100241 if (mbedtls_test_info.mutex_usage_error != NULL &&
242 mbedtls_test_info.result != MBEDTLS_TEST_RESULT_FAILED) {
Gilles Peskine2a4c5982021-01-29 21:18:09 +0100243 /* Functionally, the test passed. But there was a mutex usage error,
244 * so mark the test as failed after all. */
Gilles Peskine449bd832023-01-11 14:50:10 +0100245 mbedtls_test_fail("Mutex usage error", __LINE__, __FILE__);
Gilles Peskine2a4c5982021-01-29 21:18:09 +0100246 }
247 mbedtls_test_info.mutex_usage_error = NULL;
248}
249
Paul Elliottf25d8312023-11-23 18:49:43 +0000250void mbedtls_test_mutex_usage_end(void)
251{
252 mbedtls_mutex_init = mutex_functions.init;
253 mbedtls_mutex_free = mutex_functions.free;
254 mbedtls_mutex_lock = mutex_functions.lock;
255 mbedtls_mutex_unlock = mutex_functions.unlock;
256
257 mutex_functions.free(&mbedtls_test_mutex_mutex);
258}
259
Gilles Peskine1061ec62021-01-29 21:17:11 +0100260#endif /* MBEDTLS_TEST_MUTEX_USAGE */