/*
* Copyright (C) 2011-2017 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*/
/* Description:
*
* This is a reproducer of CVE-2011-0999, which fixed by mainline commit
* a7d6e4ecdb7648478ddec76d30d87d03d6e22b31:
*
* "Transparent hugepages can only be created if rmap is fully
* functional. So we must prevent hugepages to be created while
* is_vma_temporary_stack() is true."
*
* It will cause a panic something like this, if the patch didn't get
* applied:
*
* kernel BUG at mm/huge_memory.c:1260!
* invalid opcode: 0000 [#1] SMP
* last sysfs file: /sys/devices/system/cpu/cpu23/cache/index2/shared_cpu_map
* ....
*
* Due to commit da029c11e6b1 which reduced the stack size considerably, we
* now perform a binary search to find the largest possible argument we can
* use. Only the first iteration of the test performs the search; subsequent
* iterations use the result of the search which is stored in some shared
* memory.
*/
#include <errno.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "tst_test.h"
#include "mem.h"
#include "tst_minmax.h"
#define ARGS_SZ (256 * 32)
static struct bisection {
long left;
long right;
long mid;
} *bst;
static char *args[ARGS_SZ];
static char *arg;
static void thp_test(void)
{
long prev_left;
int pid;
while (bst->right - bst->left > 1) {
pid_t pid = SAFE_FORK();
if (!pid) {
/* We set mid to left assuming exec will succeed. If
* exec fails with E2BIG (and thus returns) then we
* restore left and set right to mid instead.
*/
prev_left = bst->left;
bst->mid = (bst->left + bst->right) / 2;
bst->left = bst->mid;
args[bst->mid] = NULL;
TEST(execvp("true", args));
if (TEST_ERRNO != E2BIG)
tst_brk(TBROK | TTERRNO, "execvp(\"true\", ...)");
bst->left = prev_left;
bst->right = bst->mid;
exit(0);
}
tst_reap_children();
tst_res(TINFO, "left: %ld, right: %ld, mid: %ld",
bst->left, bst->right, bst->mid);
}
/* We end with mid == right or mid == left where right - left =
* 1. Regardless we must use left because right is only set to values
* which are too large.
*/
pid = SAFE_FORK();
if (pid == 0) {
args[bst->left] = NULL;
TEST(execvp("true", args));
if (TEST_ERRNO != E2BIG)
tst_brk(TBROK | TTERRNO, "execvp(\"true\", ...)");
exit(0);
}
tst_reap_children();
tst_res(TPASS, "system didn't crash.");
}
static void setup(void)
{
struct rlimit rl = {
.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY,
};
int i;
long arg_len, arg_count;
bst = SAFE_MMAP(NULL, sizeof(*bst),
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
bst->left = 0;
bst->right = ARGS_SZ;
arg_len = sysconf(_SC_PAGESIZE);
arg = SAFE_MALLOC(arg_len);
memset(arg, 'c', arg_len - 1);
arg[arg_len - 1] = '\0';
args[0] = "true";
arg_count = ARGS_SZ;
tst_res(TINFO, "Using %ld args of size %ld", arg_count, arg_len);
for (i = 1; i < arg_count; i++)
args[i] = arg;
SAFE_SETRLIMIT(RLIMIT_STACK, &rl);
}
static void cleanup(void)
{
free(arg);
}
static struct tst_test test = {
.needs_root = 1,
.forks_child = 1,
.setup = setup,
.cleanup = cleanup,
.test_all = thp_test,
};