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
86 static struct tst_test test = {
87 .needs_root = 1,
88 .forks_child = 1,
89 .test_all = test_ksm,
90 .save_restore = (const struct tst_path_val[]) {
91 {"/sys/kernel/mm/ksm/run", "1", TST_SR_TBROK},
92 {}
93 },
94 .needs_kconfigs = (const char *const[]){
95 "CONFIG_KSM=y",
96 NULL
97 },
98 .tags = (const struct tst_tag[]) {
99 {"CVE", "2011-2183"},
100 {}
101 }
102 };
103
104 #else
105 TST_TEST_TCONF("no MADV_MERGEABLE found.");
106 #endif
107