All of lore.kernel.org
 help / color / mirror / Atom feed
From: Eric Biggers <ebiggers3@gmail.com>
To: keyrings@vger.kernel.org
Subject: [PATCH] syscalls/keyctl05: new test for key_update() crash
Date: Sat, 29 Jul 2017 01:07:18 +0000	[thread overview]
Message-ID: <20170729010718.42236-1-ebiggers3@gmail.com> (raw)

From: Eric Biggers <ebiggers@google.com>

Add a test for a kernel bug that caused a key type's ->free_preparse()
method to be called on an uninitialized key_preparsed_payload,
potentially causing a NULL pointer dereference or other kernel crash.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 runtest/syscalls                            |   1 +
 testcases/kernel/syscalls/.gitignore        |   1 +
 testcases/kernel/syscalls/keyctl/keyctl05.c | 239 ++++++++++++++++++++++++++++
 3 files changed, 241 insertions(+)
 create mode 100644 testcases/kernel/syscalls/keyctl/keyctl05.c

diff --git a/runtest/syscalls b/runtest/syscalls
index 5c7fd8e94..cfc1cbfa1 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -495,6 +495,7 @@ keyctl01 keyctl01
 keyctl02 keyctl02
 keyctl03 keyctl03
 keyctl04 keyctl04
+keyctl05 keyctl05
 
 kcmp01 kcmp01
 kcmp02 kcmp02
diff --git a/testcases/kernel/syscalls/.gitignore b/testcases/kernel/syscalls/.gitignore
index e311ba3f8..82dfb8293 100644
--- a/testcases/kernel/syscalls/.gitignore
+++ b/testcases/kernel/syscalls/.gitignore
@@ -458,6 +458,7 @@
 /keyctl/keyctl02
 /keyctl/keyctl03
 /keyctl/keyctl04
+/keyctl/keyctl05
 /kcmp/kcmp01
 /kcmp/kcmp02
 /kcmp/kcmp03
diff --git a/testcases/kernel/syscalls/keyctl/keyctl05.c b/testcases/kernel/syscalls/keyctl/keyctl05.c
new file mode 100644
index 000000000..502cea5a4
--- /dev/null
+++ b/testcases/kernel/syscalls/keyctl/keyctl05.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2017 Google, Inc.
+ *
+ * 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 commit 63a0b0509e70 ("KEYS: fix freeing uninitialized
+ * memory in key_update()").  Try to reproduce the crash in two different ways:
+ *
+ * 1. Try to update a key of a type that has a ->preparse() method but not an
+ *    ->update() method.  Examples are the "asymmetric" and "dns_resolver" key
+ *    types.  It crashes reliably for the "asymmetric" key type, since the
+ *    "asymmetric" key type's ->free_preparse() method will dereference a
+ *    pointer in the uninitialized memory, whereas other key types often just
+ *    free a pointer which tends be NULL in practice, depending on how the stack
+ *    is laid out.  However, to actually be able to add an "asymmetric" key, we
+ *    need a specially-formatted payload and several kernel config options.  We
+ *    do try it, but for completeness we also try the "dns_resolver" key type
+ *    (though that's not guaranteed to be available either).
+ *
+ * 2. Race keyctl_update() with another task removing write permission from the
+ *    key using keyctl_setperm().  This can cause a crash with almost any key
+ *    type.  "user" is a good one to try, since it's always available if
+ *    keyrings are supported at all.  However, depending on how the stack is
+ *    laid out the crash may not actually occur.
+ */
+
+#include "config.h"
+#ifdef HAVE_LINUX_KEYCTL_H
+# include <linux/keyctl.h>
+#endif
+#include <stdlib.h>
+#include <sys/wait.h>
+#include "tst_test.h"
+#include "linux_syscall_numbers.h"
+
+#define KEY_POS_WRITE   0x04000000
+#define KEY_POS_ALL     0x3f000000
+
+#ifdef HAVE_LINUX_KEYCTL_H
+
+/*
+ * A valid payload for the "asymmetric" key type.  This is an x509 certificate
+ * in DER format, generated using:
+ *
+ *	openssl req -x509 -newkey rsa:512 -batch -nodes -outform der \
+ *		| ~/linux/scripts/bin2c
+ */
+static const char x509_cert[] +	"\x30\x82\x01\xd3\x30\x82\x01\x7d\xa0\x03\x02\x01\x02\x02\x09\x00"
+	"\x92\x2a\x76\xff\x0c\x00\xfb\x9a\x30\x0d\x06\x09\x2a\x86\x48\x86"
+	"\xf7\x0d\x01\x01\x0b\x05\x00\x30\x45\x31\x0b\x30\x09\x06\x03\x55"
+	"\x04\x06\x13\x02\x41\x55\x31\x13\x30\x11\x06\x03\x55\x04\x08\x0c"
+	"\x0a\x53\x6f\x6d\x65\x2d\x53\x74\x61\x74\x65\x31\x21\x30\x1f\x06"
+	"\x03\x55\x04\x0a\x0c\x18\x49\x6e\x74\x65\x72\x6e\x65\x74\x20\x57"
+	"\x69\x64\x67\x69\x74\x73\x20\x50\x74\x79\x20\x4c\x74\x64\x30\x1e"
+	"\x17\x0d\x31\x37\x30\x37\x32\x38\x32\x31\x34\x31\x33\x34\x5a\x17"
+	"\x0d\x31\x37\x30\x38\x32\x37\x32\x31\x34\x31\x33\x34\x5a\x30\x45"
+	"\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x41\x55\x31\x13\x30"
+	"\x11\x06\x03\x55\x04\x08\x0c\x0a\x53\x6f\x6d\x65\x2d\x53\x74\x61"
+	"\x74\x65\x31\x21\x30\x1f\x06\x03\x55\x04\x0a\x0c\x18\x49\x6e\x74"
+	"\x65\x72\x6e\x65\x74\x20\x57\x69\x64\x67\x69\x74\x73\x20\x50\x74"
+	"\x79\x20\x4c\x74\x64\x30\x5c\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7"
+	"\x0d\x01\x01\x01\x05\x00\x03\x4b\x00\x30\x48\x02\x41\x00\xde\x0b"
+	"\x1c\x24\xe2\x0d\xf8\x17\xf2\xc3\x6f\xc9\x72\x3e\x9d\xb0\x2d\x47"
+	"\xe4\xc4\x85\x87\xed\xde\x06\xe3\xf3\xe9\x4c\x35\x6c\xe4\xcb\x0e"
+	"\x44\x28\x23\x66\x76\xec\x4e\xdf\x10\x93\x92\x1e\x52\xfb\xdf\x5c"
+	"\x08\xe7\x24\x04\x66\xe3\x06\x05\x27\x56\xfb\x3e\x91\x31\x02\x03"
+	"\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16"
+	"\x04\x14\x6f\x39\x3a\x46\xdf\x29\x63\xde\x54\x7b\x6c\x31\x06\xd0"
+	"\x9f\x36\x16\xfb\x9c\xbf\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30"
+	"\x16\x80\x14\x6f\x39\x3a\x46\xdf\x29\x63\xde\x54\x7b\x6c\x31\x06"
+	"\xd0\x9f\x36\x16\xfb\x9c\xbf\x30\x0c\x06\x03\x55\x1d\x13\x04\x05"
+	"\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01"
+	"\x01\x0b\x05\x00\x03\x41\x00\x73\xf0\x4b\x62\x56\xed\xf0\x8b\x7e"
+	"\xc4\x75\x78\x98\xa2\x7a\x6e\x75\x1f\xde\x9b\xa0\xbe\x1a\x1f\x86"
+	"\x44\x13\xcd\x45\x06\x7f\x86\xde\xf6\x36\x4e\xb6\x15\xfa\xf5\xb0"
+	"\x34\xd2\x5e\x0b\xb3\x2c\x03\x5a\x5a\x28\x97\x5e\x7b\xdf\x63\x75"
+	"\x83\x8d\x69\xda\xd6\x59\xbd"
+	;
+
+static void new_session_keyring(void)
+{
+	TEST(tst_syscall(__NR_keyctl, KEYCTL_JOIN_SESSION_KEYRING, NULL));
+	if (TEST_RETURN < 0)
+		tst_brk(TBROK | TTERRNO, "failed to join new session keyring");
+}
+
+static void test_update_nonupdatable(const char *type,
+				     const void *payload, size_t plen)
+{
+	int keyid;
+
+	new_session_keyring();
+
+	TEST(tst_syscall(__NR_add_key, type, "desc", payload, plen,
+			 KEY_SPEC_SESSION_KEYRING));
+	if (TEST_RETURN < 0) {
+		if (TEST_ERRNO = ENODEV) {
+			tst_res(TCONF, "kernel doesn't support key type '%s'",
+				type);
+			return;
+		}
+		if (TEST_ERRNO = EBADMSG && !strcmp(type, "asymmetric")) {
+			tst_res(TCONF, "kernel is missing x509 cert parser "
+				"(CONFIG_X509_CERTIFICATE_PARSER)");
+			return;
+		}
+		if (TEST_ERRNO = ENOENT && !strcmp(type, "asymmetric")) {
+			tst_res(TCONF, "kernel is missing crypto algorithms "
+				"needed to parse x509 cert (CONFIG_CRYPTO_RSA "
+				"and/or CONFIG_CRYPTO_SHA256)");
+			return;
+		}
+		tst_res(TFAIL | TTERRNO, "unexpected error adding '%s' key",
+			type);
+		return;
+	}
+	keyid = TEST_RETURN;
+
+	/*
+	 * Non-updatable keys don't start with write permission, so we must
+	 * explicitly grant it.
+	 */
+	TEST(tst_syscall(__NR_keyctl, KEYCTL_SETPERM, keyid, KEY_POS_ALL));
+	if (TEST_RETURN < 0) {
+		tst_res(TFAIL | TTERRNO,
+			"failed to grant write permission to '%s' key", type);
+		return;
+	}
+
+	/* Try to update the key.  This may crash on buggy kernels. */
+	TEST(tst_syscall(__NR_keyctl, KEYCTL_UPDATE, keyid, payload, plen));
+	if (TEST_RETURN < 0) {
+		if (TEST_ERRNO = EOPNOTSUPP) {
+			tst_res(TPASS,
+				"updating '%s' key expectedly failed with EOPNOTSUPP",
+				type);
+			return;
+		}
+		tst_res(TFAIL | TTERRNO, "updating '%s' key failed", type);
+		return;
+	}
+	tst_res(TFAIL, "updating '%s' key unexpectedly succeeded", type);
+}
+
+static void test_update_setperm_race(void)
+{
+	static const char payload[] = "payload";
+	int keyid;
+	int i;
+	int status;
+
+	new_session_keyring();
+
+	TEST(tst_syscall(__NR_add_key, "user", "desc", payload, sizeof(payload),
+			 KEY_SPEC_SESSION_KEYRING));
+	if (TEST_RETURN < 0) {
+		tst_res(TFAIL | TTERRNO, "failed to add 'user' key");
+		return;
+	}
+	keyid = TEST_RETURN;
+
+	TEST(fork());
+	if (TEST_RETURN < 0) {
+		tst_res(TFAIL | TTERRNO, "failed to fork!");
+		return;
+	}
+
+	if (TEST_RETURN = 0) {
+		uint32_t perm = KEY_POS_ALL;
+
+		/*
+		 * Repeatedly add and remove write permission from the key.
+		 * All calls should succeed.
+		 */
+		status = 0;
+		for (i = 0; i < 10000; i++) {
+			perm ^= KEY_POS_WRITE;
+			status |= syscall(__NR_keyctl, KEYCTL_SETPERM, keyid,
+					  perm);
+		}
+		exit(status ? 1 : 0);
+	}
+
+	/*
+	 * Try to update the key, racing with removing write permission.
+	 * This may crash on buggy kernels.
+	 */
+	for (i = 0; i < 10000; i++) {
+		TEST(tst_syscall(__NR_keyctl, KEYCTL_UPDATE, keyid,
+				 payload, sizeof(payload)));
+		if (TEST_RETURN < 0 && TEST_ERRNO != EACCES) {
+			tst_res(TFAIL | TTERRNO, "failed to update 'user' key");
+			return;
+		}
+	}
+	TEST(wait(&status));
+	if (TEST_RETURN < 0) {
+		tst_res(TFAIL | TTERRNO, "wait() error!");
+		return;
+	}
+	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+		tst_res(TFAIL, "setperm task encountered error");
+		return;
+	}
+	tst_res(TPASS, "didn't crash while racing to update 'user' key");
+}
+
+static void do_test(void)
+{
+	/* two 0 bytes is accepted as a dns_resolver key payload */
+	static char zeroes[2];
+
+	test_update_nonupdatable("asymmetric", x509_cert, sizeof(x509_cert));
+	test_update_nonupdatable("dns_resolver", zeroes, sizeof(zeroes));
+
+	test_update_setperm_race();
+}
+
+static struct tst_test test = {
+	.test_all = do_test,
+};
+
+#else
+	TST_TEST_TCONF("linux/keyctl.h was missing upon compilation.");
+#endif /* HAVE_LINUX_KEYCTL_H */
-- 
2.14.0.rc0.400.g1c36432dff-goog


WARNING: multiple messages have this Message-ID (diff)
From: Eric Biggers <ebiggers3@gmail.com>
To: ltp@lists.linux.it
Subject: [LTP] [PATCH] syscalls/keyctl05: new test for key_update() crash
Date: Fri, 28 Jul 2017 18:07:18 -0700	[thread overview]
Message-ID: <20170729010718.42236-1-ebiggers3@gmail.com> (raw)

From: Eric Biggers <ebiggers@google.com>

Add a test for a kernel bug that caused a key type's ->free_preparse()
method to be called on an uninitialized key_preparsed_payload,
potentially causing a NULL pointer dereference or other kernel crash.

Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 runtest/syscalls                            |   1 +
 testcases/kernel/syscalls/.gitignore        |   1 +
 testcases/kernel/syscalls/keyctl/keyctl05.c | 239 ++++++++++++++++++++++++++++
 3 files changed, 241 insertions(+)
 create mode 100644 testcases/kernel/syscalls/keyctl/keyctl05.c

diff --git a/runtest/syscalls b/runtest/syscalls
index 5c7fd8e94..cfc1cbfa1 100644
--- a/runtest/syscalls
+++ b/runtest/syscalls
@@ -495,6 +495,7 @@ keyctl01 keyctl01
 keyctl02 keyctl02
 keyctl03 keyctl03
 keyctl04 keyctl04
+keyctl05 keyctl05
 
 kcmp01 kcmp01
 kcmp02 kcmp02
diff --git a/testcases/kernel/syscalls/.gitignore b/testcases/kernel/syscalls/.gitignore
index e311ba3f8..82dfb8293 100644
--- a/testcases/kernel/syscalls/.gitignore
+++ b/testcases/kernel/syscalls/.gitignore
@@ -458,6 +458,7 @@
 /keyctl/keyctl02
 /keyctl/keyctl03
 /keyctl/keyctl04
+/keyctl/keyctl05
 /kcmp/kcmp01
 /kcmp/kcmp02
 /kcmp/kcmp03
diff --git a/testcases/kernel/syscalls/keyctl/keyctl05.c b/testcases/kernel/syscalls/keyctl/keyctl05.c
new file mode 100644
index 000000000..502cea5a4
--- /dev/null
+++ b/testcases/kernel/syscalls/keyctl/keyctl05.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2017 Google, Inc.
+ *
+ * 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 commit 63a0b0509e70 ("KEYS: fix freeing uninitialized
+ * memory in key_update()").  Try to reproduce the crash in two different ways:
+ *
+ * 1. Try to update a key of a type that has a ->preparse() method but not an
+ *    ->update() method.  Examples are the "asymmetric" and "dns_resolver" key
+ *    types.  It crashes reliably for the "asymmetric" key type, since the
+ *    "asymmetric" key type's ->free_preparse() method will dereference a
+ *    pointer in the uninitialized memory, whereas other key types often just
+ *    free a pointer which tends be NULL in practice, depending on how the stack
+ *    is laid out.  However, to actually be able to add an "asymmetric" key, we
+ *    need a specially-formatted payload and several kernel config options.  We
+ *    do try it, but for completeness we also try the "dns_resolver" key type
+ *    (though that's not guaranteed to be available either).
+ *
+ * 2. Race keyctl_update() with another task removing write permission from the
+ *    key using keyctl_setperm().  This can cause a crash with almost any key
+ *    type.  "user" is a good one to try, since it's always available if
+ *    keyrings are supported at all.  However, depending on how the stack is
+ *    laid out the crash may not actually occur.
+ */
+
+#include "config.h"
+#ifdef HAVE_LINUX_KEYCTL_H
+# include <linux/keyctl.h>
+#endif
+#include <stdlib.h>
+#include <sys/wait.h>
+#include "tst_test.h"
+#include "linux_syscall_numbers.h"
+
+#define KEY_POS_WRITE   0x04000000
+#define KEY_POS_ALL     0x3f000000
+
+#ifdef HAVE_LINUX_KEYCTL_H
+
+/*
+ * A valid payload for the "asymmetric" key type.  This is an x509 certificate
+ * in DER format, generated using:
+ *
+ *	openssl req -x509 -newkey rsa:512 -batch -nodes -outform der \
+ *		| ~/linux/scripts/bin2c
+ */
+static const char x509_cert[] =
+	"\x30\x82\x01\xd3\x30\x82\x01\x7d\xa0\x03\x02\x01\x02\x02\x09\x00"
+	"\x92\x2a\x76\xff\x0c\x00\xfb\x9a\x30\x0d\x06\x09\x2a\x86\x48\x86"
+	"\xf7\x0d\x01\x01\x0b\x05\x00\x30\x45\x31\x0b\x30\x09\x06\x03\x55"
+	"\x04\x06\x13\x02\x41\x55\x31\x13\x30\x11\x06\x03\x55\x04\x08\x0c"
+	"\x0a\x53\x6f\x6d\x65\x2d\x53\x74\x61\x74\x65\x31\x21\x30\x1f\x06"
+	"\x03\x55\x04\x0a\x0c\x18\x49\x6e\x74\x65\x72\x6e\x65\x74\x20\x57"
+	"\x69\x64\x67\x69\x74\x73\x20\x50\x74\x79\x20\x4c\x74\x64\x30\x1e"
+	"\x17\x0d\x31\x37\x30\x37\x32\x38\x32\x31\x34\x31\x33\x34\x5a\x17"
+	"\x0d\x31\x37\x30\x38\x32\x37\x32\x31\x34\x31\x33\x34\x5a\x30\x45"
+	"\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x41\x55\x31\x13\x30"
+	"\x11\x06\x03\x55\x04\x08\x0c\x0a\x53\x6f\x6d\x65\x2d\x53\x74\x61"
+	"\x74\x65\x31\x21\x30\x1f\x06\x03\x55\x04\x0a\x0c\x18\x49\x6e\x74"
+	"\x65\x72\x6e\x65\x74\x20\x57\x69\x64\x67\x69\x74\x73\x20\x50\x74"
+	"\x79\x20\x4c\x74\x64\x30\x5c\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7"
+	"\x0d\x01\x01\x01\x05\x00\x03\x4b\x00\x30\x48\x02\x41\x00\xde\x0b"
+	"\x1c\x24\xe2\x0d\xf8\x17\xf2\xc3\x6f\xc9\x72\x3e\x9d\xb0\x2d\x47"
+	"\xe4\xc4\x85\x87\xed\xde\x06\xe3\xf3\xe9\x4c\x35\x6c\xe4\xcb\x0e"
+	"\x44\x28\x23\x66\x76\xec\x4e\xdf\x10\x93\x92\x1e\x52\xfb\xdf\x5c"
+	"\x08\xe7\x24\x04\x66\xe3\x06\x05\x27\x56\xfb\x3e\x91\x31\x02\x03"
+	"\x01\x00\x01\xa3\x50\x30\x4e\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16"
+	"\x04\x14\x6f\x39\x3a\x46\xdf\x29\x63\xde\x54\x7b\x6c\x31\x06\xd0"
+	"\x9f\x36\x16\xfb\x9c\xbf\x30\x1f\x06\x03\x55\x1d\x23\x04\x18\x30"
+	"\x16\x80\x14\x6f\x39\x3a\x46\xdf\x29\x63\xde\x54\x7b\x6c\x31\x06"
+	"\xd0\x9f\x36\x16\xfb\x9c\xbf\x30\x0c\x06\x03\x55\x1d\x13\x04\x05"
+	"\x30\x03\x01\x01\xff\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01"
+	"\x01\x0b\x05\x00\x03\x41\x00\x73\xf0\x4b\x62\x56\xed\xf0\x8b\x7e"
+	"\xc4\x75\x78\x98\xa2\x7a\x6e\x75\x1f\xde\x9b\xa0\xbe\x1a\x1f\x86"
+	"\x44\x13\xcd\x45\x06\x7f\x86\xde\xf6\x36\x4e\xb6\x15\xfa\xf5\xb0"
+	"\x34\xd2\x5e\x0b\xb3\x2c\x03\x5a\x5a\x28\x97\x5e\x7b\xdf\x63\x75"
+	"\x83\x8d\x69\xda\xd6\x59\xbd"
+	;
+
+static void new_session_keyring(void)
+{
+	TEST(tst_syscall(__NR_keyctl, KEYCTL_JOIN_SESSION_KEYRING, NULL));
+	if (TEST_RETURN < 0)
+		tst_brk(TBROK | TTERRNO, "failed to join new session keyring");
+}
+
+static void test_update_nonupdatable(const char *type,
+				     const void *payload, size_t plen)
+{
+	int keyid;
+
+	new_session_keyring();
+
+	TEST(tst_syscall(__NR_add_key, type, "desc", payload, plen,
+			 KEY_SPEC_SESSION_KEYRING));
+	if (TEST_RETURN < 0) {
+		if (TEST_ERRNO == ENODEV) {
+			tst_res(TCONF, "kernel doesn't support key type '%s'",
+				type);
+			return;
+		}
+		if (TEST_ERRNO == EBADMSG && !strcmp(type, "asymmetric")) {
+			tst_res(TCONF, "kernel is missing x509 cert parser "
+				"(CONFIG_X509_CERTIFICATE_PARSER)");
+			return;
+		}
+		if (TEST_ERRNO == ENOENT && !strcmp(type, "asymmetric")) {
+			tst_res(TCONF, "kernel is missing crypto algorithms "
+				"needed to parse x509 cert (CONFIG_CRYPTO_RSA "
+				"and/or CONFIG_CRYPTO_SHA256)");
+			return;
+		}
+		tst_res(TFAIL | TTERRNO, "unexpected error adding '%s' key",
+			type);
+		return;
+	}
+	keyid = TEST_RETURN;
+
+	/*
+	 * Non-updatable keys don't start with write permission, so we must
+	 * explicitly grant it.
+	 */
+	TEST(tst_syscall(__NR_keyctl, KEYCTL_SETPERM, keyid, KEY_POS_ALL));
+	if (TEST_RETURN < 0) {
+		tst_res(TFAIL | TTERRNO,
+			"failed to grant write permission to '%s' key", type);
+		return;
+	}
+
+	/* Try to update the key.  This may crash on buggy kernels. */
+	TEST(tst_syscall(__NR_keyctl, KEYCTL_UPDATE, keyid, payload, plen));
+	if (TEST_RETURN < 0) {
+		if (TEST_ERRNO == EOPNOTSUPP) {
+			tst_res(TPASS,
+				"updating '%s' key expectedly failed with EOPNOTSUPP",
+				type);
+			return;
+		}
+		tst_res(TFAIL | TTERRNO, "updating '%s' key failed", type);
+		return;
+	}
+	tst_res(TFAIL, "updating '%s' key unexpectedly succeeded", type);
+}
+
+static void test_update_setperm_race(void)
+{
+	static const char payload[] = "payload";
+	int keyid;
+	int i;
+	int status;
+
+	new_session_keyring();
+
+	TEST(tst_syscall(__NR_add_key, "user", "desc", payload, sizeof(payload),
+			 KEY_SPEC_SESSION_KEYRING));
+	if (TEST_RETURN < 0) {
+		tst_res(TFAIL | TTERRNO, "failed to add 'user' key");
+		return;
+	}
+	keyid = TEST_RETURN;
+
+	TEST(fork());
+	if (TEST_RETURN < 0) {
+		tst_res(TFAIL | TTERRNO, "failed to fork!");
+		return;
+	}
+
+	if (TEST_RETURN == 0) {
+		uint32_t perm = KEY_POS_ALL;
+
+		/*
+		 * Repeatedly add and remove write permission from the key.
+		 * All calls should succeed.
+		 */
+		status = 0;
+		for (i = 0; i < 10000; i++) {
+			perm ^= KEY_POS_WRITE;
+			status |= syscall(__NR_keyctl, KEYCTL_SETPERM, keyid,
+					  perm);
+		}
+		exit(status ? 1 : 0);
+	}
+
+	/*
+	 * Try to update the key, racing with removing write permission.
+	 * This may crash on buggy kernels.
+	 */
+	for (i = 0; i < 10000; i++) {
+		TEST(tst_syscall(__NR_keyctl, KEYCTL_UPDATE, keyid,
+				 payload, sizeof(payload)));
+		if (TEST_RETURN < 0 && TEST_ERRNO != EACCES) {
+			tst_res(TFAIL | TTERRNO, "failed to update 'user' key");
+			return;
+		}
+	}
+	TEST(wait(&status));
+	if (TEST_RETURN < 0) {
+		tst_res(TFAIL | TTERRNO, "wait() error!");
+		return;
+	}
+	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+		tst_res(TFAIL, "setperm task encountered error");
+		return;
+	}
+	tst_res(TPASS, "didn't crash while racing to update 'user' key");
+}
+
+static void do_test(void)
+{
+	/* two 0 bytes is accepted as a dns_resolver key payload */
+	static char zeroes[2];
+
+	test_update_nonupdatable("asymmetric", x509_cert, sizeof(x509_cert));
+	test_update_nonupdatable("dns_resolver", zeroes, sizeof(zeroes));
+
+	test_update_setperm_race();
+}
+
+static struct tst_test test = {
+	.test_all = do_test,
+};
+
+#else
+	TST_TEST_TCONF("linux/keyctl.h was missing upon compilation.");
+#endif /* HAVE_LINUX_KEYCTL_H */
-- 
2.14.0.rc0.400.g1c36432dff-goog


             reply	other threads:[~2017-07-29  1:07 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-07-29  1:07 Eric Biggers [this message]
2017-07-29  1:07 ` [LTP] [PATCH] syscalls/keyctl05: new test for key_update() crash Eric Biggers
2017-07-31  8:57 ` Richard Palethorpe
2017-07-31 21:09   ` Eric Biggers
2017-08-01 14:32     ` Richard Palethorpe

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170729010718.42236-1-ebiggers3@gmail.com \
    --to=ebiggers3@gmail.com \
    --cc=keyrings@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.