1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2019 SUSE LLC <mdoucha@suse.cz> 4 */ 5 6/* 7 * CVE-2018-1000204 8 * 9 * Test ioctl(SG_IO) and check that kernel doesn't leak data. Requires 10 * a read-accessible generic SCSI device (e.g. a DVD drive). 11 * 12 * Leak fixed in: 13 * 14 * commit a45b599ad808c3c982fdcdc12b0b8611c2f92824 15 * Author: Alexander Potapenko <glider@google.com> 16 * Date: Fri May 18 16:23:18 2018 +0200 17 * 18 * scsi: sg: allocate with __GFP_ZERO in sg_build_indirect() 19 */ 20 21#include <sys/types.h> 22#include <dirent.h> 23#include <fcntl.h> 24#include <unistd.h> 25#include <ctype.h> 26#include <scsi/sg.h> 27#include <sys/ioctl.h> 28#include <stdio.h> 29#include "tst_test.h" 30#include "tst_memutils.h" 31 32#define BUF_SIZE (128 * 4096) 33#define CMD_SIZE 6 34 35static int devfd = -1; 36static char buffer[BUF_SIZE]; 37static unsigned char command[CMD_SIZE]; 38static struct sg_io_hdr query; 39 40/* TODO: split this off to a separate SCSI library? */ 41static const char *find_generic_scsi_device(int access_flags) 42{ 43 DIR *devdir; 44 struct dirent *ent; 45 int tmpfd; 46 static char devpath[PATH_MAX]; 47 48 errno = 0; 49 devdir = opendir("/dev"); 50 51 if (!devdir) 52 return NULL; 53 54 while ((ent = SAFE_READDIR(devdir))) { 55 /* The bug is most likely reproducible only on /dev/sg* */ 56 if (strncmp(ent->d_name, "sg", 2) || !isdigit(ent->d_name[2])) 57 continue; 58 59 snprintf(devpath, PATH_MAX, "/dev/%s", ent->d_name); 60 /* access() makes incorrect assumptions about block devices */ 61 tmpfd = open(devpath, access_flags); 62 63 if (tmpfd >= 0) { 64 SAFE_CLOSE(tmpfd); 65 SAFE_CLOSEDIR(devdir); 66 return devpath; 67 } 68 } 69 70 SAFE_CLOSEDIR(devdir); 71 return NULL; 72} 73 74static void setup(void) 75{ 76 const char *devpath = find_generic_scsi_device(O_RDONLY); 77 78 if (!devpath) 79 tst_brk(TCONF, "Could not find any usable SCSI device"); 80 81 tst_res(TINFO, "Found SCSI device %s", devpath); 82 83 /* Pollute some memory to avoid false negatives */ 84 tst_pollute_memory(0, 0x42); 85 86 devfd = SAFE_OPEN(devpath, O_RDONLY); 87 query.interface_id = 'S'; 88 query.dxfer_direction = SG_DXFER_FROM_DEV; 89 query.cmd_len = CMD_SIZE; 90 query.dxfer_len = BUF_SIZE; 91 query.dxferp = buffer; 92 query.cmdp = command; 93} 94 95static void cleanup(void) 96{ 97 if (devfd >= 0) 98 SAFE_CLOSE(devfd); 99} 100 101static void run(void) 102{ 103 size_t i, j; 104 105 memset(buffer, 0, BUF_SIZE); 106 107 for (i = 0; i < 100; i++) { 108 TEST(ioctl(devfd, SG_IO, &query)); 109 110 if (TST_RET != 0 && TST_RET != -1) 111 tst_brk(TBROK|TTERRNO, "Invalid ioctl() return value"); 112 113 /* Check the buffer even if ioctl() failed, just in case. */ 114 for (j = 0; j < BUF_SIZE; j++) { 115 if (buffer[j]) { 116 tst_res(TFAIL, "Kernel memory leaked"); 117 return; 118 } 119 } 120 } 121 122 tst_res(TPASS, "Output buffer is empty, no data leaked"); 123} 124 125static struct tst_test test = { 126 .test_all = run, 127 .setup = setup, 128 .cleanup = cleanup, 129 .max_runtime = 3600, 130 .tags = (const struct tst_tag[]) { 131 {"linux-git", "a45b599ad808"}, 132 {"CVE", "2018-1000204"}, 133 {} 134 } 135}; 136