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
35 static int devfd = -1;
36 static char buffer[BUF_SIZE];
37 static unsigned char command[CMD_SIZE];
38 static struct sg_io_hdr query;
39
40 /* TODO: split this off to a separate SCSI library? */
find_generic_scsi_device(int access_flags)41 static 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
setup(void)74 static 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
cleanup(void)95 static void cleanup(void)
96 {
97 if (devfd >= 0)
98 SAFE_CLOSE(devfd);
99 }
100
run(void)101 static 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
125 static 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