Home | History | Annotate | Download | only in ksm
      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 
     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 
     90 static void sighandler(int sig)
     91 {
     92 	_exit((sig == SIGSEGV) ? 0 : sig);
     93 }
     94 
     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 
    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