1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2020 SUSE LLC <mdoucha@suse.cz> 4 */ 5 6/* 7 * CVE-2017-1000111 8 * 9 * Check for race condition between packet_set_ring() and tp_reserve. 10 * The race allows you to set tp_reserve bigger than ring buffer size. 11 * While this will cause truncation of all incoming packets to 0 bytes, 12 * sanity checks in tpacket_rcv() prevent any exploitable buffer overflows. 13 * Race fixed in: 14 * 15 * commit c27927e372f0785f3303e8fad94b85945e2c97b7 (HEAD) 16 * Author: Willem de Bruijn <willemb@google.com> 17 * Date: Thu Aug 10 12:41:58 2017 -0400 18 * 19 * packet: fix tp_reserve race in packet_set_ring 20 */ 21 22#include <unistd.h> 23#include <sys/types.h> 24#include <sys/socket.h> 25 26#include "tst_test.h" 27#include "tst_fuzzy_sync.h" 28#include "lapi/if_packet.h" 29#include "lapi/if_ether.h" 30 31static int sock = -1; 32static unsigned int pagesize; 33static struct tst_fzsync_pair fzsync_pair; 34 35static void setup(void) 36{ 37 pagesize = SAFE_SYSCONF(_SC_PAGESIZE); 38 tst_setup_netns(); 39 40 /* 41 * Reproducing the bug on unpatched system takes <15 loops. The test 42 * is slow and the bug is mostly harmless so don't waste too much 43 * time. 44 */ 45 fzsync_pair.exec_loops = 500; 46 tst_fzsync_pair_init(&fzsync_pair); 47} 48 49static void *thread_run(void *arg) 50{ 51 unsigned int val = 1 << 30; 52 53 while (tst_fzsync_run_b(&fzsync_pair)) { 54 tst_fzsync_start_race_b(&fzsync_pair); 55 setsockopt(sock, SOL_PACKET, PACKET_RESERVE, &val, sizeof(val)); 56 tst_fzsync_end_race_b(&fzsync_pair); 57 } 58 59 return arg; 60} 61 62static void run(void) 63{ 64 unsigned int val, version = TPACKET_V3; 65 socklen_t vsize = sizeof(val); 66 struct tpacket_req3 req = { 67 .tp_block_size = pagesize, 68 .tp_block_nr = 1, 69 .tp_frame_size = pagesize, 70 .tp_frame_nr = 1, 71 .tp_retire_blk_tov = 100 72 }; 73 74 tst_fzsync_pair_reset(&fzsync_pair, thread_run); 75 76 while (tst_fzsync_run_a(&fzsync_pair)) { 77 sock = SAFE_SOCKET(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 78 TEST(setsockopt(sock, SOL_PACKET, PACKET_VERSION, &version, 79 sizeof(version))); 80 81 if (TST_RET == -1 && TST_ERR == EINVAL) 82 tst_brk(TCONF | TTERRNO, "TPACKET_V3 not supported"); 83 84 if (TST_RET) { 85 tst_brk(TBROK | TTERRNO, 86 "setsockopt(PACKET_VERSION, TPACKET_V3"); 87 } 88 89 tst_fzsync_start_race_a(&fzsync_pair); 90 TEST(setsockopt(sock, SOL_PACKET, PACKET_RX_RING, &req, 91 sizeof(req))); 92 tst_fzsync_end_race_a(&fzsync_pair); 93 94 SAFE_GETSOCKOPT(sock, SOL_PACKET, PACKET_RESERVE, &val, &vsize); 95 SAFE_CLOSE(sock); 96 97 if (TST_RET == -1 && TST_ERR == EINVAL) { 98 tst_fzsync_pair_add_bias(&fzsync_pair, 1); 99 continue; 100 } 101 102 if (TST_RET) { 103 tst_brk(TBROK | TTERRNO, 104 "Invalid setsockopt() return value"); 105 } 106 107 if (val > req.tp_block_size) { 108 tst_res(TFAIL, "PACKET_RESERVE checks bypassed"); 109 return; 110 } 111 } 112 113 tst_res(TPASS, "Cannot reproduce bug"); 114} 115 116static void cleanup(void) 117{ 118 tst_fzsync_pair_cleanup(&fzsync_pair); 119 120 if (sock >= 0) 121 SAFE_CLOSE(sock); 122} 123 124static struct tst_test test = { 125 .test_all = run, 126 .setup = setup, 127 .cleanup = cleanup, 128 .max_runtime = 150, 129 .needs_kconfigs = (const char *[]) { 130 "CONFIG_USER_NS=y", 131 "CONFIG_NET_NS=y", 132 NULL 133 }, 134 .save_restore = (const struct tst_path_val[]) { 135 {"/proc/sys/user/max_user_namespaces", "1024", TST_SR_SKIP}, 136 {} 137 }, 138 .tags = (const struct tst_tag[]) { 139 {"linux-git", "c27927e372f0"}, 140 {"CVE", "2017-1000111"}, 141 {} 142 } 143}; 144