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
55static int ksm_run_orig;
56static void sighandler(int sig);
57
58static 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 (TEST_RETURN == -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		*(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
90static void sighandler(int sig)
91{
92	_exit((sig == SIGSEGV) ? 0 : sig);
93}
94
95static 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
107static void cleanup(void)
108{
109	/* restore /sys/kernel/mm/ksm/run value */
110	FILE_PRINTF(PATH_KSM "run", "%d", ksm_run_orig);
111}
112
113static struct tst_test test = {
114	.needs_root = 1,
115	.forks_child = 1,
116	.setup = setup,
117	.cleanup = cleanup,
118	.test_all = test_ksm,
119	.min_kver = "2.6.32",
120};
121
122#else
123	TST_TEST_TCONF("no MADV_MERGEABLE found.");
124#endif
125