1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2011-2017 Red Hat, Inc.
4 *
5 * KSM - NULL pointer dereference in ksm_do_scan() (CVE-2011-2183)
6 *
7 * This is a testcase from upstream commit:
8 * 2b472611a32a72f4a118c069c2d62a1a3f087afd.
9 *
10 * an exiting task can race against ksmd::scan_get_next_rmap_item
11 * (http://lkml.org/lkml/2011/6/1/742) easily triggering a NULL pointer
12 * dereference in ksmd.
13 * ksm_scan.mm_slot == &ksm_mm_head with only one registered mm
14 *
15 * CPU 1 (__ksm_exit) CPU 2 (scan_get_next_rmap_item)
16 * list_empty() is false
17 * lock slot == &ksm_mm_head
18 * list_del(slot->mm_list)
19 * (list now empty)
20 * unlock
21 * lock
22 * slot = list_entry(slot->mm_list.next)
23 * (list is empty, so slot is still ksm_mm_head)
24 * unlock
25 * slot->mm == NULL ... Oops
26 *
27 * Close this race by revalidating that the new slot is not simply the list
28 * head again.
29 *
30 * Test Prerequisites:
31 *
32 * *) ksm and ksmtuned daemons need to be disabled. Otherwise, it could
33 * distrub the testing as they also change some ksm tunables depends
34 * on current workloads.
35 */
36
37 #include <sys/wait.h>
38 #include <signal.h>
39 #include <stdlib.h>
40 #include <errno.h>
41 #include "tst_test.h"
42 #include "mem.h"
43
44 #ifdef HAVE_DECL_MADV_MERGEABLE
45
46 static int ksm_run_orig = -1;
47 static void sighandler(int sig);
48
test_ksm(void)49 static void test_ksm(void)
50 {
51 int status;
52 long ps;
53 pid_t pid;
54 void *ptr;
55 struct sigaction sa;
56
57 memset (&sa, '\0', sizeof(sa));
58 sa.sa_handler = sighandler;
59 sa.sa_flags = 0;
60 TEST(sigaction(SIGSEGV, &sa, NULL));
61 if (TST_RET == -1)
62 tst_brk(TBROK | TRERRNO,
63 "SIGSEGV signal setup failed");
64
65 ps = sysconf(_SC_PAGESIZE);
66
67 pid = SAFE_FORK();
68 if (pid == 0) {
69 ptr = SAFE_MEMALIGN(ps, ps);
70 if (madvise(ptr, ps, MADV_MERGEABLE) < 0)
71 tst_brk(TBROK | TERRNO, "madvise");
72 *(volatile char *)NULL = 0; /* SIGSEGV occurs as expected. */
73 }
74 SAFE_WAITPID(pid, &status, WUNTRACED | WCONTINUED);
75 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
76 tst_brk(TBROK, "invalid signal received: %d", status);
77
78 tst_res(TPASS, "still alive.");
79 }
80
sighandler(int sig)81 static void sighandler(int sig)
82 {
83 _exit((sig == SIGSEGV) ? 0 : sig);
84 }
85
setup(void)86 static void setup(void)
87 {
88 if (access(PATH_KSM, F_OK) == -1)
89 tst_brk(TCONF, "KSM configuration is not enabled");
90
91 /* save original /sys/kernel/mm/ksm/run value */
92 SAFE_FILE_SCANF(PATH_KSM "run", "%d", &ksm_run_orig);
93
94 /* echo 1 > /sys/kernel/mm/ksm/run */
95 SAFE_FILE_PRINTF(PATH_KSM "run", "1");
96 }
97
cleanup(void)98 static void cleanup(void)
99 {
100 /* restore /sys/kernel/mm/ksm/run value */
101 if (ksm_run_orig > 0)
102 FILE_PRINTF(PATH_KSM "run", "%d", ksm_run_orig);
103 }
104
105 static struct tst_test test = {
106 .needs_root = 1,
107 .forks_child = 1,
108 .setup = setup,
109 .cleanup = cleanup,
110 .test_all = test_ksm,
111 .min_kver = "2.6.32",
112 };
113
114 #else
115 TST_TEST_TCONF("no MADV_MERGEABLE found.");
116 #endif
117