1 /*
2 * Copyright (c) 2017 Richard Palethorpe <rpalethorpe@suse.com>
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17 /*
18 * Fuzzy Synchronisation - abreviated to fzsync
19 *
20 * This library is intended to help reproduce race conditions by providing two
21 * thread synchronisation mechanisms. The first is a 'checkpoint' system where
22 * each thread will wait indefinitely for the other to enter a checkpoint
23 * before continuing. This is acheived by calling tst_fzsync_wait() and/or
24 * tst_fzsync_wait_update() at the points you want to synchronise in each
25 * thread. This is functionally very similar to using pthread_barrier_wait()
26 * with two threads. However tst_fzsync_wait() can be inlined and is
27 * guaranteed not to call sleep or futex.
28 *
29 * The second takes the form a of a delay which is calculated by measuring the
30 * time difference between two points in each thread and comparing it to the
31 * desired difference (default is zero). Using a delay allows you to
32 * synchronise the threads at a point outside of your direct control
33 * (e.g. inside the kernel) or just increase the accuracy for the first
34 * mechanism. It is acheived using tst_fzsync_delay_{a,b}(),
35 * tst_fzsync_time_{a,b}() and tst_fzsync[_wait_]update().
36 *
37 * For a usage example see testcases/cve/cve-2016-7117.c or just run
38 * 'git grep tst_fuzzy_sync.h'
39 */
40
41 #include <sys/time.h>
42 #include <time.h>
43 #include <math.h>
44 #include "tst_atomic.h"
45
46 #ifndef CLOCK_MONOTONIC_RAW
47 # define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
48 #endif
49
50 /**
51 * struct tst_fzsync_pair - the state of a two way synchronisation
52 * @avg_diff: Internal; the average time difference over multiple iterations.
53 * @avg_diff_trgt: The desired average time difference, defaults to 0.
54 * @avg_alpha: The rate at which old diff samples are forgotten,
55 * defaults to 0.25.
56 * @avg_dev: Internal; Absolute average deviation.
57 * @a: Internal; The time at which call site A was last passed.
58 * @b: Internal; The time at which call site B was last passed.
59 * @delay: Internal; The size of the delay, positive to delay A,
60 * negative to delay B.
61 * @delay_inc: The step size of a delay increment, defaults to 1.
62 * @update_gap: The number of iterations between recalculating the delay.
63 * Defaults to 0xF and must be of the form $2^n - 1$
64 * @info_gap: The number of iterations between printing some statistics.
65 * Defaults to 0x7FFFF and must also be one less than a power of 2.
66 * @a_cntr: Internal; Atomic counter used by fzsync_pair_wait()
67 * @b_cntr: Internal; Atomic counter used by fzsync_pair_wait()
68 * @exit: Internal; Used by tst_fzsync_pair_exit() and fzsync_pair_wait()
69 *
70 * This contains all the necessary state for synchronising two points A and
71 * B. Where A is the time of an event in one process and B is the time of an
72 * event in another process.
73 *
74 * Internal fields should only be accessed by library functions.
75 */
76 struct tst_fzsync_pair {
77 float avg_diff;
78 float avg_diff_trgt;
79 float avg_alpha;
80 float avg_dev;
81 struct timespec a;
82 struct timespec b;
83 long delay;
84 long delay_inc;
85 int update_gap;
86 int info_gap;
87 int a_cntr;
88 int b_cntr;
89 int exit;
90 };
91
92 /**
93 * TST_FZSYNC_PAIR_INIT - Default values for struct tst_fzysnc_pair
94 */
95 #define TST_FZSYNC_PAIR_INIT { \
96 .avg_alpha = 0.25, \
97 .delay_inc = 1, \
98 .update_gap = 0xF, \
99 .info_gap = 0x7FFFF \
100 }
101
102 /**
103 * tst_fzsync_pair_info - Print some synchronisation statistics
104 */
tst_fzsync_pair_info(struct tst_fzsync_pair * pair)105 static void tst_fzsync_pair_info(struct tst_fzsync_pair *pair)
106 {
107 tst_res(TINFO,
108 "avg_diff = %.0fns, avg_dev = %.0fns, delay = %05ld loops",
109 pair->avg_diff, pair->avg_dev, pair->delay);
110 }
111
112 /**
113 * tst_fzsync_delay_a - Perform spin delay for A, if needed
114 *
115 * Usually called just before the point you want to synchronise.
116 */
tst_fzsync_delay_a(struct tst_fzsync_pair * pair)117 static inline void tst_fzsync_delay_a(struct tst_fzsync_pair *pair)
118 {
119 volatile long spin_delay = pair->delay;
120
121 while (spin_delay > 0)
122 spin_delay--;
123 }
124
125 /**
126 * tst_fzsync_delay_b - Perform spin delay for B, if needed
127 *
128 * Usually called just before the point you want to synchronise.
129 */
tst_fzsync_delay_b(struct tst_fzsync_pair * pair)130 static inline void tst_fzsync_delay_b(struct tst_fzsync_pair *pair)
131 {
132 volatile long spin_delay = pair->delay;
133
134 while (spin_delay < 0)
135 spin_delay++;
136 }
137
tst_fzsync_time(struct timespec * t)138 static inline void tst_fzsync_time(struct timespec *t)
139 {
140 clock_gettime(CLOCK_MONOTONIC_RAW, t);
141 }
142
143 /**
144 * tst_fzsync_time_a - Set A's time to now.
145 *
146 * Called at the point you want to synchronise.
147 */
tst_fzsync_time_a(struct tst_fzsync_pair * pair)148 static inline void tst_fzsync_time_a(struct tst_fzsync_pair *pair)
149 {
150 tst_fzsync_time(&pair->a);
151 }
152
153 /**
154 * tst_fzsync_time_b - Set B's call time to now.
155 *
156 * Called at the point you want to synchronise.
157 */
tst_fzsync_time_b(struct tst_fzsync_pair * pair)158 static inline void tst_fzsync_time_b(struct tst_fzsync_pair *pair)
159 {
160 tst_fzsync_time(&pair->b);
161 }
162
163 /**
164 * tst_exp_moving_avg - Exponential moving average
165 * @alpha: The preference for recent samples over old ones.
166 * @sample: The current sample
167 * @prev_avg: The average of the all the previous samples
168 *
169 * Returns average including the current sample.
170 */
tst_exp_moving_avg(float alpha,float sample,float prev_avg)171 static inline float tst_exp_moving_avg(float alpha,
172 float sample,
173 float prev_avg)
174 {
175 return alpha * sample + (1.0 - alpha) * prev_avg;
176 }
177
178 /**
179 * tst_fzsync_pair_update - Recalculate the delay
180 * @loop_index: The i in "for(i = 0;..." or zero to ignore update_gap
181 * @pair: The state necessary for calculating the delay
182 *
183 * This should be called at the end of each loop to update the average
184 * measured time difference (between A and B) and update the delay. It is
185 * assumed that A and B are less than a second apart.
186 *
187 * The values of update_gap, avg_alpha and delay_inc decide the speed at which
188 * the algorithm approaches the optimum delay value and whether it is
189 * stable. If your test is behaving strangely, it could be because this
190 * algorithm is behaving chaotically and flip-flopping between large positve
191 * and negative delay values. You can call tst_fzysync_pair_info every few
192 * loops to check whether the average difference and delay values are stable.
193 */
tst_fzsync_pair_update(int loop_index,struct tst_fzsync_pair * pair)194 static void tst_fzsync_pair_update(int loop_index, struct tst_fzsync_pair *pair)
195 {
196 long diff;
197 long inc = pair->delay_inc;
198 float target = pair->avg_diff_trgt;
199 float avg = pair->avg_diff;
200
201 diff = pair->a.tv_nsec - pair->b.tv_nsec
202 + 1000000000 * (pair->a.tv_sec - pair->b.tv_sec);
203 avg = tst_exp_moving_avg(pair->avg_alpha, diff, avg);
204 pair->avg_dev = tst_exp_moving_avg(pair->avg_alpha,
205 fabs(diff - avg),
206 pair->avg_dev);
207
208 if (!(loop_index & pair->update_gap)) {
209 if (avg > target)
210 pair->delay -= inc;
211 else if (avg < target)
212 pair->delay += inc;
213 }
214
215 if (!(loop_index & pair->info_gap))
216 tst_fzsync_pair_info(pair);
217
218 pair->avg_diff = avg;
219 }
220
221 /**
222 * tst_fzsync_pair_wait - Wait for the other thread
223 * @our_cntr: The counter for the thread we are on
224 * @other_cntr: The counter for the thread we are synchronising with
225 *
226 * Use this (through tst_fzsync_pair_wait_a() and tst_fzsync_pair_wait_b()) if
227 * you need an additional synchronisation point in a thread or you do not want
228 * to use the delay facility (not recommended). See
229 * tst_fzsync_pair_wait_update().
230 *
231 * Returns a non-zero value if the thread should continue otherwise the
232 * calling thread should exit.
233 */
tst_fzsync_pair_wait(struct tst_fzsync_pair * pair,int * our_cntr,int * other_cntr)234 static inline int tst_fzsync_pair_wait(struct tst_fzsync_pair *pair,
235 int *our_cntr, int *other_cntr)
236 {
237 if (tst_atomic_inc(other_cntr) == INT_MAX) {
238 /*
239 * We are about to break the invariant that the thread with
240 * the lowest count is in front of the other. So we must wait
241 * here to ensure the other thread has atleast reached the
242 * line above before doing that. If we are in rear position
243 * then our counter may already have been set to zero.
244 */
245 while (tst_atomic_load(our_cntr) > 0
246 && tst_atomic_load(our_cntr) < INT_MAX
247 && !tst_atomic_load(&pair->exit))
248 ;
249
250 tst_atomic_store(0, other_cntr);
251 /*
252 * Once both counters have been set to zero the invariant
253 * is restored and we can continue.
254 */
255 while (tst_atomic_load(our_cntr) > 1
256 && !tst_atomic_load(&pair->exit))
257 ;
258 } else {
259 /*
260 * If our counter is less than the other thread's we are ahead
261 * of it and need to wait.
262 */
263 while (tst_atomic_load(our_cntr) < tst_atomic_load(other_cntr)
264 && !tst_atomic_load(&pair->exit))
265 ;
266 }
267
268 /* Only exit if we have done the same amount of work as the other thread */
269 return !tst_atomic_load(&pair->exit) ||
270 tst_atomic_load(other_cntr) <= tst_atomic_load(our_cntr);
271 }
272
tst_fzsync_wait_a(struct tst_fzsync_pair * pair)273 static inline int tst_fzsync_wait_a(struct tst_fzsync_pair *pair)
274 {
275 return tst_fzsync_pair_wait(pair, &pair->a_cntr, &pair->b_cntr);
276 }
277
tst_fzsync_wait_b(struct tst_fzsync_pair * pair)278 static inline int tst_fzsync_wait_b(struct tst_fzsync_pair *pair)
279 {
280 return tst_fzsync_pair_wait(pair, &pair->b_cntr, &pair->a_cntr);
281 }
282
283 /**
284 * tst_fzsync_pair_wait_update_{a,b} - Wait and then recalculate
285 *
286 * This allows you to have two long running threads which wait for each other
287 * every iteration. So each thread will exit this function at approximately
288 * the same time. It also updates the delay values in a thread safe manner.
289 *
290 * You must call this function in both threads the same number of times each
291 * iteration. So a call in one thread must match with a call in the
292 * other. Make sure that calls to tst_fzsync_pair_wait() and
293 * tst_fzsync_pair_wait_update() happen in the same order in each thread. That
294 * is, make sure that a call to tst_fzsync_pair_wait_update_a() in one thread
295 * corresponds to a call to tst_fzsync_pair_wait_update_b() in the other.
296 *
297 * Returns a non-zero value if the calling thread should continue to loop. If
298 * it returns zero then tst_fzsync_exit() has been called and you must exit
299 * the thread.
300 */
tst_fzsync_wait_update_a(struct tst_fzsync_pair * pair)301 static inline int tst_fzsync_wait_update_a(struct tst_fzsync_pair *pair)
302 {
303 static int loop_index;
304
305 tst_fzsync_pair_wait(pair, &pair->a_cntr, &pair->b_cntr);
306 loop_index++;
307 tst_fzsync_pair_update(loop_index, pair);
308 return tst_fzsync_pair_wait(pair, &pair->a_cntr, &pair->b_cntr);
309 }
310
tst_fzsync_wait_update_b(struct tst_fzsync_pair * pair)311 static inline int tst_fzsync_wait_update_b(struct tst_fzsync_pair *pair)
312 {
313 tst_fzsync_pair_wait(pair, &pair->b_cntr, &pair->a_cntr);
314 return tst_fzsync_pair_wait(pair, &pair->b_cntr, &pair->a_cntr);
315 }
316
317 /**
318 * tst_fzsync_pair_exit - Signal that the other thread should exit
319 *
320 * Causes tst_fzsync_pair_wait() and tst_fzsync_pair_wait_update() to return
321 * 0.
322 */
tst_fzsync_pair_exit(struct tst_fzsync_pair * pair)323 static inline void tst_fzsync_pair_exit(struct tst_fzsync_pair *pair)
324 {
325 tst_atomic_store(1, &pair->exit);
326 }
327