Home | History | Annotate | Download | only in thp
      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 
     15 /* Description:
     16  *
     17  * This is a reproducer of CVE-2011-0999, which fixed by mainline commit
     18  * a7d6e4ecdb7648478ddec76d30d87d03d6e22b31:
     19  *
     20  * "Transparent hugepages can only be created if rmap is fully
     21  * functional. So we must prevent hugepages to be created while
     22  * is_vma_temporary_stack() is true."
     23  *
     24  * It will cause a panic something like this, if the patch didn't get
     25  * applied:
     26  *
     27  * kernel BUG at mm/huge_memory.c:1260!
     28  * invalid opcode: 0000 [#1] SMP
     29  * last sysfs file: /sys/devices/system/cpu/cpu23/cache/index2/shared_cpu_map
     30  * ....
     31  *
     32  * Due to commit da029c11e6b1 which reduced the stack size considerably, we
     33  * now perform a binary search to find the largest possible argument we can
     34  * use. Only the first iteration of the test performs the search; subsequent
     35  * iterations use the result of the search which is stored in some shared
     36  * memory.
     37  */
     38 
     39 #include <errno.h>
     40 #include <sys/types.h>
     41 #include <sys/resource.h>
     42 #include <sys/wait.h>
     43 #include <stdio.h>
     44 #include <string.h>
     45 #include <stdlib.h>
     46 #include <unistd.h>
     47 #include "tst_test.h"
     48 #include "mem.h"
     49 #include "tst_minmax.h"
     50 
     51 #define ARGS_SZ	(256 * 32)
     52 
     53 static struct bisection {
     54 	long left;
     55 	long right;
     56 	long mid;
     57 } *bst;
     58 
     59 static char *args[ARGS_SZ];
     60 static char *arg;
     61 
     62 static void thp_test(void)
     63 {
     64 	long prev_left;
     65 	int pid;
     66 
     67 	while (bst->right - bst->left > 1) {
     68 		pid_t pid = SAFE_FORK();
     69 
     70 		if (!pid) {
     71 			/* We set mid to left assuming exec will succeed. If
     72 			 * exec fails with E2BIG (and thus returns) then we
     73 			 * restore left and set right to mid instead.
     74 			 */
     75 			prev_left = bst->left;
     76 			bst->mid = (bst->left + bst->right) / 2;
     77 			bst->left = bst->mid;
     78 			args[bst->mid] = NULL;
     79 
     80 			TEST(execvp("true", args));
     81 			if (TEST_ERRNO != E2BIG)
     82 				tst_brk(TBROK | TTERRNO, "execvp(\"true\", ...)");
     83 			bst->left = prev_left;
     84 			bst->right = bst->mid;
     85 			exit(0);
     86 		}
     87 
     88 		tst_reap_children();
     89 		tst_res(TINFO, "left: %ld, right: %ld, mid: %ld",
     90 			bst->left, bst->right, bst->mid);
     91 	}
     92 
     93 	/* We end with mid == right or mid == left where right - left =
     94 	 * 1. Regardless we must use left because right is only set to values
     95 	 * which are too large.
     96 	 */
     97 	pid = SAFE_FORK();
     98 	if (pid == 0) {
     99 		args[bst->left] = NULL;
    100 		TEST(execvp("true", args));
    101 		if (TEST_ERRNO != E2BIG)
    102 			tst_brk(TBROK | TTERRNO, "execvp(\"true\", ...)");
    103 		exit(0);
    104 	}
    105 	tst_reap_children();
    106 
    107 	tst_res(TPASS, "system didn't crash.");
    108 }
    109 
    110 static void setup(void)
    111 {
    112 	struct rlimit rl = {
    113 		.rlim_cur = RLIM_INFINITY,
    114 		.rlim_max = RLIM_INFINITY,
    115 	};
    116 	int i;
    117 	long arg_len, arg_count;
    118 
    119 	bst = SAFE_MMAP(NULL, sizeof(*bst),
    120 			   PROT_READ | PROT_WRITE,
    121 			   MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    122 	bst->left = 0;
    123 	bst->right = ARGS_SZ;
    124 
    125 	arg_len = sysconf(_SC_PAGESIZE);
    126 	arg = SAFE_MALLOC(arg_len);
    127 	memset(arg, 'c', arg_len - 1);
    128 	arg[arg_len - 1] = '\0';
    129 
    130 	args[0] = "true";
    131 	arg_count = ARGS_SZ;
    132 	tst_res(TINFO, "Using %ld args of size %ld", arg_count, arg_len);
    133 	for (i = 1; i < arg_count; i++)
    134 		args[i] = arg;
    135 
    136 	SAFE_SETRLIMIT(RLIMIT_STACK, &rl);
    137 }
    138 
    139 static void cleanup(void)
    140 {
    141 	free(arg);
    142 }
    143 
    144 static struct tst_test test = {
    145 	.needs_root = 1,
    146 	.forks_child = 1,
    147 	.setup = setup,
    148 	.cleanup = cleanup,
    149 	.test_all = thp_test,
    150 };
    151