|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
| 2 | +/* |
| 3 | + * Copyright (c) 2020 SUSE LLC <[email protected]> |
| 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 in: |
| 19 | + * |
| 20 | + * commit 9060cb719e61b685ec0102574e10337fa5f445ea |
| 21 | + * Author: Mao Wenan <[email protected]> |
| 22 | + * Date: Mon Feb 18 10:44:44 2019 +0800 |
| 23 | + * |
| 24 | + * net: crypto set sk to NULL when af_alg_release. |
| 25 | + */ |
| 26 | + |
| 27 | +#include <sys/types.h> |
| 28 | +#include <sys/stat.h> |
| 29 | +#include <unistd.h> |
| 30 | +#include <pwd.h> |
| 31 | + |
| 32 | +#include "tst_test.h" |
| 33 | +#include "tst_af_alg.h" |
| 34 | +#include "tst_fuzzy_sync.h" |
| 35 | +#include "tst_taint.h" |
| 36 | + |
| 37 | +static int fd = -1, sock = -1; |
| 38 | +static int uid, gid; |
| 39 | +static struct tst_fzsync_pair fzsync_pair; |
| 40 | + |
| 41 | +static void setup(void) |
| 42 | +{ |
| 43 | + uid = getuid(); |
| 44 | + gid = getgid(); |
| 45 | + tst_taint_init(TST_TAINT_W | TST_TAINT_D); |
| 46 | + |
| 47 | + fd = SAFE_OPEN("tmpfile", O_RDWR | O_CREAT, 0644); |
| 48 | + |
| 49 | + tst_fzsync_pair_init(&fzsync_pair); |
| 50 | +} |
| 51 | + |
| 52 | +static void *thread_run(void *arg) |
| 53 | +{ |
| 54 | + while (tst_fzsync_run_b(&fzsync_pair)) { |
| 55 | + tst_fzsync_start_race_b(&fzsync_pair); |
| 56 | + dup2(fd, sock); |
| 57 | + tst_fzsync_end_race_b(&fzsync_pair); |
| 58 | + } |
| 59 | + |
| 60 | + return arg; |
| 61 | +} |
| 62 | + |
| 63 | +static void run(void) |
| 64 | +{ |
| 65 | + tst_fzsync_pair_reset(&fzsync_pair, thread_run); |
| 66 | + |
| 67 | + while (tst_fzsync_run_a(&fzsync_pair)) { |
| 68 | + sock = tst_alg_setup_reqfd("hash", "sha1", NULL, 0); |
| 69 | + tst_fzsync_start_race_a(&fzsync_pair); |
| 70 | + TEST(fchownat(sock, "", uid, gid, AT_EMPTY_PATH)); |
| 71 | + tst_fzsync_end_race_a(&fzsync_pair); |
| 72 | + SAFE_CLOSE(sock); |
| 73 | + |
| 74 | + if (tst_taint_check()) { |
| 75 | + tst_res(TFAIL, "Kernel is vulnerable"); |
| 76 | + return; |
| 77 | + } |
| 78 | + |
| 79 | + if (TST_RET == -1 && TST_ERR == ENOENT) { |
| 80 | + tst_res(TPASS | TTERRNO, |
| 81 | + "fchownat() failed successfully"); |
| 82 | + return; |
| 83 | + } |
| 84 | + |
| 85 | + if (TST_RET == -1) { |
| 86 | + tst_brk(TBROK | TTERRNO, |
| 87 | + "fchownat() failed unexpectedly"); |
| 88 | + } |
| 89 | + |
| 90 | + if (TST_RET) { |
| 91 | + tst_brk(TBROK | TTERRNO, |
| 92 | + "Invalid fchownat() return value"); |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + tst_res(TFAIL, "fchownat() failed to fail, kernel may be vulnerable"); |
| 97 | +} |
| 98 | + |
| 99 | +static void cleanup(void) |
| 100 | +{ |
| 101 | + tst_fzsync_pair_cleanup(&fzsync_pair); |
| 102 | + |
| 103 | + if (fd >= 0) |
| 104 | + SAFE_CLOSE(fd); |
| 105 | +} |
| 106 | + |
| 107 | +static struct tst_test test = { |
| 108 | + .needs_tmpdir = 1, |
| 109 | + .test_all = run, |
| 110 | + .setup = setup, |
| 111 | + .cleanup = cleanup, |
| 112 | + .tags = (const struct tst_tag[]) { |
| 113 | + {"linux-git", "9060cb719e61"}, |
| 114 | + {"CVE", "2019-8912"}, |
| 115 | + {} |
| 116 | + } |
| 117 | +}; |
0 commit comments