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 void sighandler(int sig);
47
test_ksm(void)48 static void test_ksm(void)
49 {
50 int status;
51 long ps;
52 pid_t pid;
53 void *ptr;
54 struct sigaction sa;
55
56 memset (&sa, '\0', sizeof(sa));
57 sa.sa_handler = sighandler;
58 sa.sa_flags = 0;
59 TEST(sigaction(SIGSEGV, &sa, NULL));
60 if (TST_RET == -1)
61 tst_brk(TBROK | TRERRNO,
62 "SIGSEGV signal setup failed");
63
64 ps = sysconf(_SC_PAGESIZE);
65
66 pid = SAFE_FORK();
67 if (pid == 0) {
68 ptr = SAFE_MEMALIGN(ps, ps);
69 if (madvise(ptr, ps, MADV_MERGEABLE) < 0)
70 tst_brk(TBROK | TERRNO, "madvise");
71 *(volatile char *)NULL = 0; /* SIGSEGV occurs as expected. */
72 }
73 SAFE_WAITPID(pid, &status, WUNTRACED | WCONTINUED);
74 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
75 tst_brk(TBROK, "invalid signal received: %d", status);
76
77 tst_res(TPASS, "still alive.");
78 }
79
sighandler(int sig)80 static void sighandler(int sig)
81 {
82 _exit((sig == SIGSEGV) ? 0 : sig);
83 }
84
85 static struct tst_test test = {
86 .needs_root = 1,
87 .forks_child = 1,
88 .test_all = test_ksm,
89 .save_restore = (const struct tst_path_val[]) {
90 {"/sys/kernel/mm/ksm/run", "1", TST_SR_TBROK},
91 {"/sys/kernel/mm/ksm/smart_scan", "0",
92 TST_SR_SKIP_MISSING | TST_SR_TBROK_RO},
93 {}
94 },
95 .needs_kconfigs = (const char *const[]){
96 "CONFIG_KSM=y",
97 NULL
98 },
99 .tags = (const struct tst_tag[]) {
100 {"CVE", "2011-2183"},
101 {}
102 }
103 };
104
105 #else
106 TST_TEST_TCONF("no MADV_MERGEABLE found.");
107 #endif
108