• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2020 SUSE LLC <mdoucha@suse.cz>
4  */
5 
6 /*
7  * CVE-2019-8912
8  *
9  * Check for possible use-after-free in sockfs_setattr() on AF_ALG socket
10  * closed by dup2() or dup3(). Unlike regular close(), dup*() syscalls don't
11  * set sock->sk = NULL after closing the socket. Racing fchownat() against
12  * dup2() may then result in sockfs_setattr() using the stale pointer and
13  * writing into a block of released memory that may have been reused in the
14  * mean time.
15  *
16  * The race window is small and it's hard to trigger a kernel crash but
17  * fchownat() will return ENOENT as it should only when the bug is not
18  * present. Race fixed specifically for af_alg in:
19  *
20  *  commit 9060cb719e61b685ec0102574e10337fa5f445ea
21  *  Author: Mao Wenan <maowenan@huawei.com>
22  *  Date:   Mon Feb 18 10:44:44 2019 +0800
23  *
24  *  net: crypto set sk to NULL when af_alg_release.
25  *
26  * It was observed that the same bug is present on many other
27  * protocols. A more general fix is in:
28  *
29  *  commit ff7b11aa481f682e0e9711abfeb7d03f5cd612bf
30  *  Author: Eric Biggers <ebiggers@google.com>
31  *  Date:   Thu Feb 21 14:13:56 2019 -0800
32  *
33  *  net: socket: set sock->sk to NULL after calling proto_ops::release()
34  */
35 
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include <pwd.h>
40 #include "lapi/fcntl.h"
41 
42 #include "tst_test.h"
43 #include "tst_af_alg.h"
44 #include "tst_fuzzy_sync.h"
45 #include "tst_taint.h"
46 
47 static int fd = -1, sock = -1;
48 static int uid, gid;
49 static struct tst_fzsync_pair fzsync_pair;
50 
setup(void)51 static void setup(void)
52 {
53 	uid = getuid();
54 	gid = getgid();
55 
56 	fd = SAFE_OPEN("tmpfile", O_RDWR | O_CREAT, 0644);
57 
58 	tst_fzsync_pair_init(&fzsync_pair);
59 }
60 
thread_run(void * arg)61 static void *thread_run(void *arg)
62 {
63 	while (tst_fzsync_run_b(&fzsync_pair)) {
64 		tst_fzsync_start_race_b(&fzsync_pair);
65 		dup2(fd, sock);
66 		tst_fzsync_end_race_b(&fzsync_pair);
67 	}
68 
69 	return arg;
70 }
71 
run(void)72 static void run(void)
73 {
74 	tst_fzsync_pair_reset(&fzsync_pair, thread_run);
75 
76 	while (tst_fzsync_run_a(&fzsync_pair)) {
77 		sock = tst_alg_setup_reqfd("hash", "sha1", NULL, 0);
78 		tst_fzsync_start_race_a(&fzsync_pair);
79 		TEST(fchownat(sock, "", uid, gid, AT_EMPTY_PATH));
80 		tst_fzsync_end_race_a(&fzsync_pair);
81 		SAFE_CLOSE(sock);
82 
83 		if (tst_taint_check()) {
84 			tst_res(TFAIL, "Kernel is vulnerable");
85 			return;
86 		}
87 
88 		if (TST_RET == -1 && TST_ERR == EBADF) {
89 			tst_fzsync_pair_add_bias(&fzsync_pair, 1);
90 			continue;
91 		}
92 
93 		if (TST_RET == -1 && TST_ERR == ENOENT) {
94 			tst_res(TPASS | TTERRNO,
95 				"fchownat() failed successfully");
96 			return;
97 		}
98 
99 		if (TST_RET == -1) {
100 			tst_brk(TBROK | TTERRNO,
101 				"fchownat() failed unexpectedly");
102 		}
103 
104 		if (TST_RET) {
105 			tst_brk(TBROK | TTERRNO,
106 				"Invalid fchownat() return value");
107 		}
108 	}
109 
110 	tst_res(TFAIL, "fchownat() failed to fail, kernel may be vulnerable");
111 }
112 
cleanup(void)113 static void cleanup(void)
114 {
115 	tst_fzsync_pair_cleanup(&fzsync_pair);
116 
117 	if (fd >= 0)
118 		SAFE_CLOSE(fd);
119 }
120 
121 static struct tst_test test = {
122 	.needs_tmpdir = 1,
123 	.test_all = run,
124 	.setup = setup,
125 	.cleanup = cleanup,
126 	.min_kver = "4.10.0",
127 	.min_cpus = 2,
128 	.taint_check = TST_TAINT_W | TST_TAINT_D,
129 	.tags = (const struct tst_tag[]) {
130 		{"linux-git", "ff7b11aa481f"},
131 		{"linux-git", "9060cb719e61"},
132 		{"CVE", "2019-8912"},
133 		{}
134 	}
135 };
136