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