1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2018 Google, Inc.
4 */
5
6 /*\
7 * [Description]
8 *
9 * Regression test for commit
10 * 3f05317d9889 (ipc/shm: fix use-after-free of shm file via remap_file_pages()).
11 *
12 * This bug allowed the remap_file_pages() syscall to use the file of a System
13 * V shared memory segment after its ID had been reallocated and the file
14 * freed. This test reproduces the bug as a NULL pointer dereference in
15 * touch_atime(), although it's a race condition so it's not guaranteed to
16 * work. This test is based on the reproducer provided in the fix's commit
17 * message.
18 */
19
20 #include "lapi/syscalls.h"
21 #include "tst_test.h"
22 #include "tst_fuzzy_sync.h"
23 #include "tst_safe_pthread.h"
24 #include "tst_safe_sysv_ipc.h"
25 #include "tst_timer.h"
26
27 static struct tst_fzsync_pair fzsync_pair;
28
29 /*
30 * Thread 2: repeatedly remove the shm ID and reallocate it again for a
31 * new shm segment.
32 */
thrproc(void * unused)33 static void *thrproc(void *unused)
34 {
35 int id = SAFE_SHMGET(0xF00F, 4096, IPC_CREAT|0700);
36
37 while (tst_fzsync_run_b(&fzsync_pair)) {
38 tst_fzsync_start_race_b(&fzsync_pair);
39 SAFE_SHMCTL(id, IPC_RMID, NULL);
40 id = SAFE_SHMGET(0xF00F, 4096, IPC_CREAT|0700);
41 tst_fzsync_end_race_b(&fzsync_pair);
42 }
43 return unused;
44 }
45
setup(void)46 static void setup(void)
47 {
48 /* Skip test if either remap_file_pages() or SysV IPC is unavailable */
49 tst_syscall(__NR_remap_file_pages, NULL, 0, 0, 0, 0);
50 tst_syscall(__NR_shmctl, 0xF00F, IPC_RMID, NULL);
51
52 tst_fzsync_pair_init(&fzsync_pair);
53 }
54
do_test(void)55 static void do_test(void)
56 {
57 /*
58 * Thread 1: repeatedly attach a shm segment, then remap it until the ID
59 * seems to have been removed by the other process.
60 */
61 tst_fzsync_pair_reset(&fzsync_pair, thrproc);
62 while (tst_fzsync_run_a(&fzsync_pair)) {
63 int id;
64 void *addr;
65
66 id = SAFE_SHMGET(0xF00F, 4096, IPC_CREAT|0700);
67 addr = SAFE_SHMAT(id, NULL, 0);
68 tst_fzsync_start_race_a(&fzsync_pair);
69 do {
70 /* This is the system call that crashed */
71 TEST(syscall(__NR_remap_file_pages, addr, 4096,
72 0, 0, 0));
73 } while (TST_RET == 0);
74 tst_fzsync_end_race_a(&fzsync_pair);
75
76 if (TST_ERR != EIDRM && TST_ERR != EINVAL) {
77 tst_brk(TBROK | TTERRNO,
78 "Unexpected remap_file_pages() error");
79 }
80
81 /*
82 * Ensure that a shm segment will actually be destroyed.
83 * This call may fail on recent kernels (v4.0+) because
84 * remap_file_pages() already unmapped the shm segment.
85 */
86 shmdt(addr);
87 }
88
89 tst_res(TPASS, "didn't crash");
90 }
91
cleanup(void)92 static void cleanup(void)
93 {
94 int id;
95
96 tst_fzsync_pair_cleanup(&fzsync_pair);
97
98 id = shmget(0xF00F, 4096, 0);
99 if (id == -1) {
100 if (errno != ENOENT)
101 tst_res(TWARN | TERRNO, "shmget()");
102 return;
103 }
104
105 SAFE_SHMCTL(id, IPC_RMID, NULL);
106 }
107
108 static struct tst_test test = {
109 .timeout = 20,
110 .setup = setup,
111 .test_all = do_test,
112 .cleanup = cleanup,
113 .tags = (const struct tst_tag[]) {
114 {"linux-git", "3f05317d9889"},
115 {}
116 }
117 };
118