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