diff mbox series

[v2,2/2] Add test for CVE 2018-12896

Message ID 20200901134613.20839-2-mdoucha@suse.cz
State Accepted
Headers show
Series [v2,1/2] Add SAFE_TIMER_*() functions to tst_safe_clocks.h | expand

Commit Message

Martin Doucha Sept. 1, 2020, 1:46 p.m. UTC
Fixes #353

Signed-off-by: Martin Doucha <mdoucha@suse.cz>
---

Changes since v1:
- Fix running the test with -i parameter
- Move checks out of signal handler to make it async safe

 runtest/cve                                   |   1 +
 runtest/syscalls                              |   1 +
 .../kernel/syscalls/timer_settime/.gitignore  |   1 +
 .../syscalls/timer_settime/timer_settime03.c  | 123 ++++++++++++++++++
 4 files changed, 126 insertions(+)
 create mode 100644 testcases/kernel/syscalls/timer_settime/timer_settime03.c

Comments

Cyril Hrubis Sept. 2, 2020, 3:04 p.m. UTC | #1
Hi!
Pushed, thanks.
diff mbox series

Patch

diff --git a/runtest/cve b/runtest/cve
index a2ca8d27c..07c69e8ff 100644
--- a/runtest/cve
+++ b/runtest/cve
@@ -52,6 +52,7 @@  cve-2018-9568 connect02
 cve-2018-1000001 realpath01
 cve-2018-1000199 ptrace08
 cve-2018-1000204 ioctl_sg01
+cve-2018-12896 timer_settime03
 cve-2018-18445 bpf_prog04
 cve-2018-18559 bind06
 cve-2018-19854 crypto_user01
diff --git a/runtest/syscalls b/runtest/syscalls
index a6ab75ba7..398145f65 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -1543,6 +1543,7 @@  timer_gettime01 timer_gettime01
 
 timer_settime01 timer_settime01
 timer_settime02 timer_settime02
+timer_settime03 timer_settime03
 
 tkill01 tkill01
 tkill02 tkill02
diff --git a/testcases/kernel/syscalls/timer_settime/.gitignore b/testcases/kernel/syscalls/timer_settime/.gitignore
index e1ed3ef17..2541a5b57 100644
--- a/testcases/kernel/syscalls/timer_settime/.gitignore
+++ b/testcases/kernel/syscalls/timer_settime/.gitignore
@@ -1,2 +1,3 @@ 
 /timer_settime01
 /timer_settime02
+/timer_settime03
diff --git a/testcases/kernel/syscalls/timer_settime/timer_settime03.c b/testcases/kernel/syscalls/timer_settime/timer_settime03.c
new file mode 100644
index 000000000..4cc1f8bd8
--- /dev/null
+++ b/testcases/kernel/syscalls/timer_settime/timer_settime03.c
@@ -0,0 +1,123 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019 SUSE LLC <mdoucha@suse.cz>
+ */
+
+/*
+ * CVE 2018-12896
+ *
+ * Check for possible overflow of posix timer overrun counter. Create
+ * a CLOCK_REALTIME timer, set extremely low timer interval and expiration
+ * value just right to cause overrun overflow into negative values, start
+ * the timer with TIMER_ABSTIME flag to cause overruns immediately. Then just
+ * check the overrun counter in the timer signal handler. On a patched system,
+ * the value returned by timer_getoverrun() should be capped at INT_MAX and
+ * not allowed to overflow into negative range. Bug fixed in:
+ *
+ *  commit 78c9c4dfbf8c04883941445a195276bb4bb92c76
+ *  Author: Thomas Gleixner <tglx@linutronix.de>
+ *  Date:   Tue Jun 26 15:21:32 2018 +0200
+ *
+ *  posix-timers: Sanitize overrun handling
+ */
+
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+#include <limits.h>
+
+#include "tst_test.h"
+#include "tst_safe_clocks.h"
+
+static timer_t timer;
+static volatile int handler_called, overrun, saved_errno;
+
+static void sighandler(int sig LTP_ATTRIBUTE_UNUSED)
+{
+	struct itimerspec spec;
+
+	/*
+	 * Signal handler will be called twice in total because kernel will
+	 * schedule another pending signal before the timer gets disabled.
+	 */
+	if (handler_called)
+		return;
+
+	errno = 0;
+	overrun = timer_getoverrun(timer);
+	saved_errno = errno;
+	memset(&spec, 0, sizeof(struct itimerspec));
+	SAFE_TIMER_SETTIME(timer, 0, &spec, NULL);
+	handler_called = 1;
+}
+
+static void setup(void)
+{
+	struct sigevent sev;
+
+	memset(&sev, 0, sizeof(struct sigevent));
+	sev.sigev_notify = SIGEV_SIGNAL;
+	sev.sigev_signo = SIGUSR1;
+
+	SAFE_SIGNAL(SIGUSR1, sighandler);
+	SAFE_TIMER_CREATE(CLOCK_REALTIME, &sev, &timer);
+}
+
+static void run(void)
+{
+	int handler_delay = INT_MAX / 7;
+	long nsec;
+	struct itimerspec spec;
+
+	handler_called = 0;
+	memset(&spec, 0, sizeof(struct itimerspec));
+	SAFE_CLOCK_GETTIME(CLOCK_REALTIME, &spec.it_value);
+	nsec = (handler_delay % 100000000) * 10L;
+
+	if (nsec > spec.it_value.tv_nsec) {
+		spec.it_value.tv_sec -= 1;
+		spec.it_value.tv_nsec += 1000000000;
+	}
+
+	/* spec.it_value = now - 1.4 * max overrun value */
+	/* IOW, overflow will land right in the middle of negative range */
+	spec.it_value.tv_sec -= handler_delay / 100000000;
+	spec.it_value.tv_nsec -= nsec;
+	spec.it_interval.tv_nsec = 1;
+
+	SAFE_TIMER_SETTIME(timer, TIMER_ABSTIME, &spec, NULL);
+	while (!handler_called);
+	errno = saved_errno;
+
+	if (overrun == -1)
+		tst_brk(TBROK | TERRNO, "Error reading timer overrun count");
+
+	if (overrun == INT_MAX) {
+		tst_res(TPASS, "Timer overrun count is capped");
+		return;
+	}
+
+	if (overrun < 0) {
+		tst_res(TFAIL, "Timer overrun counter overflow");
+		return;
+	}
+
+	tst_res(TFAIL, "Timer overrun counter is wrong: %d; expected %d or "
+		"negative number", overrun, INT_MAX);
+}
+
+static void cleanup(void)
+{
+	SAFE_TIMER_DELETE(timer);
+}
+
+static struct tst_test test = {
+	.test_all = run,
+	.setup = setup,
+	.cleanup = cleanup,
+	.tags = (const struct tst_tag[]) {
+		{"linux-git", "78c9c4dfbf8c"},
+		{"CVE", "2018-12896"},
+		{}
+	}
+};