1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2024 SUSE LLC
4 * Author: Michal Hocko <[email protected]>
5 * LTP port: Andrea Cervesato <[email protected]>
6 */
7
8 /*\
9 * [Description]
10 *
11 * When debugging issues with a workload using SysV shmem, Michal Hocko has
12 * come up with a reproducer that shows how a series of mprotect()
13 * operations can result in an elevated shm_nattch and thus leak of the
14 * resource.
15 *
16 * The problem is caused by wrong assumptions in vma_merge() commit
17 * 714965ca8252 ("mm/mmap: start distinguishing if vma can be removed in
18 * mergeability test"). The shmem vmas have a vma_ops->close callback
19 * that decrements shm_nattch, and we remove the vma without calling it.
20 *
21 * Patch: https://lore.kernel.org/all/[email protected]/
22 */
23
24 #include "tst_test.h"
25 #include "tst_safe_sysv_ipc.h"
26 #include "libnewipc.h"
27
28 static int segment_id = -1;
29 static int key_id;
30 static int page_size;
31 static size_t segment_size;
32
run(void)33 static void run(void)
34 {
35 struct shmid_ds shmid_ds;
36 void *sh_mem;
37
38 segment_id = SAFE_SHMGET(
39 key_id,
40 segment_size,
41 IPC_CREAT | IPC_EXCL | 0600);
42
43 sh_mem = SAFE_SHMAT(segment_id, NULL, 0);
44
45 tst_res(TINFO, "Attached at %p. key: %d - size: %lu",
46 sh_mem, segment_id, segment_size);
47
48 SAFE_SHMCTL(segment_id, IPC_STAT, &shmid_ds);
49
50 tst_res(TINFO, "Number of attaches: %lu", shmid_ds.shm_nattch);
51
52 SAFE_MPROTECT(sh_mem + page_size, page_size, PROT_NONE);
53 SAFE_MPROTECT(sh_mem, 2 * page_size, PROT_WRITE);
54
55 SAFE_SHMCTL(segment_id, IPC_STAT, &shmid_ds);
56
57 tst_res(TINFO, "Number of attaches: %lu", shmid_ds.shm_nattch);
58 tst_res(TINFO, "Delete attached memory");
59
60 SAFE_SHMDT(sh_mem);
61 SAFE_SHMCTL(segment_id, IPC_STAT, &shmid_ds);
62
63 tst_res(TINFO, "Number of attaches: %lu", shmid_ds.shm_nattch);
64
65 SAFE_SHMCTL(segment_id, IPC_RMID, NULL);
66 segment_id = -1;
67
68 TST_EXP_EQ_LU(shmid_ds.shm_nattch, 0);
69 }
70
setup(void)71 static void setup(void)
72 {
73 key_id = GETIPCKEY() + 1;
74 page_size = getpagesize();
75
76 tst_res(TINFO, "Key id: %d", key_id);
77 tst_res(TINFO, "Page size: %d", page_size);
78
79 segment_size = 3 * page_size;
80 }
81
cleanup(void)82 static void cleanup(void)
83 {
84 if (segment_id != -1)
85 SAFE_SHMCTL(segment_id, IPC_RMID, NULL);
86 }
87
88 static struct tst_test test = {
89 .test_all = run,
90 .setup = setup,
91 .cleanup = cleanup,
92 .tags = (const struct tst_tag[]) {
93 {"linux-git", "fc0c8f9089c2"},
94 {}
95 }
96 };
97