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