1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2020 SUSE LLC <mdoucha@suse.cz>
4 *
5 * CVE-2017-10661
6 *
7 * Test for race condition vulnerability in timerfd_settime(). Multiple
8 * concurrent calls of timerfd_settime() clearing the CANCEL_ON_SET flag may
9 * cause memory corruption. Fixed in:
10 *
11 * commit 1e38da300e1e395a15048b0af1e5305bd91402f6
12 * Author: Thomas Gleixner <tglx@linutronix.de>
13 * Date: Tue Jan 31 15:24:03 2017 +0100
14 *
15 * timerfd: Protect the might cancel mechanism proper
16 */
17 #include <unistd.h>
18 #include "time64_variants.h"
19 #include "tst_timer.h"
20 #include "tst_safe_timerfd.h"
21 #include "tst_fuzzy_sync.h"
22
23 #define TIMERFD_FLAGS "timerfd_settime(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)"
24
25 #ifndef TFD_TIMER_CANCEL_ON_SET
26 #define TFD_TIMER_CANCEL_ON_SET (1<<1)
27 #endif
28
29 static int fd = -1;
30 static struct tst_its its;
31 static struct tst_fzsync_pair fzsync_pair;
32
33 static struct time64_variants variants[] = {
34 #if (__NR_timerfd_settime != __LTP__NR_INVALID_SYSCALL)
35 { .tfd_settime = sys_timerfd_settime, .ts_type = TST_KERN_OLD_TIMESPEC, .desc = "syscall with old kernel spec"},
36 #endif
37
38 #if (__NR_timerfd_settime64 != __LTP__NR_INVALID_SYSCALL)
39 { .tfd_settime = sys_timerfd_settime64, .ts_type = TST_KERN_TIMESPEC, .desc = "syscall time64 with kernel spec"},
40 #endif
41 };
42
setup(void)43 static void setup(void)
44 {
45 struct time64_variants *tv = &variants[tst_variant];
46
47 tst_res(TINFO, "Testing variant: %s", tv->desc);
48 its.type = tv->ts_type;
49
50 fd = SAFE_TIMERFD_CREATE(CLOCK_REALTIME, 0);
51
52 fzsync_pair.exec_loops = 1000000;
53 tst_fzsync_pair_init(&fzsync_pair);
54 }
55
cleanup(void)56 static void cleanup(void)
57 {
58 if (fd >= 0)
59 SAFE_CLOSE(fd);
60 tst_fzsync_pair_cleanup(&fzsync_pair);
61 }
62
punch_clock(int flags)63 static int punch_clock(int flags)
64 {
65 return variants[tst_variant].tfd_settime(fd, flags, tst_its_get(&its),
66 NULL);
67
68 }
69
thread_run(void * arg)70 static void *thread_run(void *arg)
71 {
72 while (tst_fzsync_run_b(&fzsync_pair)) {
73 tst_fzsync_start_race_b(&fzsync_pair);
74 punch_clock(0);
75 tst_fzsync_end_race_b(&fzsync_pair);
76 }
77
78 return arg;
79 }
80
run(void)81 static void run(void)
82 {
83 tst_fzsync_pair_reset(&fzsync_pair, thread_run);
84
85 while (tst_fzsync_run_a(&fzsync_pair)) {
86 TEST(punch_clock(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET));
87
88 if (TST_RET == -1)
89 tst_brk(TBROK | TTERRNO, TIMERFD_FLAGS " failed");
90
91 if (TST_RET != 0)
92 tst_brk(TBROK | TTERRNO, "Invalid " TIMERFD_FLAGS
93 " return value");
94
95 tst_fzsync_start_race_a(&fzsync_pair);
96 punch_clock(0);
97 tst_fzsync_end_race_a(&fzsync_pair);
98
99 if (tst_taint_check()) {
100 tst_res(TFAIL, "Kernel is vulnerable");
101 return;
102 }
103 }
104
105 tst_res(TPASS, "Nothing bad happened, probably");
106 }
107
108 static struct tst_test test = {
109 .test_all = run,
110 .test_variants = ARRAY_SIZE(variants),
111 .setup = setup,
112 .cleanup = cleanup,
113 .taint_check = TST_TAINT_W | TST_TAINT_D,
114 .max_runtime = 150,
115 .tags = (const struct tst_tag[]) {
116 {"linux-git", "1e38da300e1e"},
117 {"CVE", "2017-10661"},
118 {}
119 }
120 };
121