rtos/riot: fix out-of-bounds read of optional symbols array
[fw/openocd] / src / rtos / riot.c
1 /***************************************************************************
2  *   Copyright (C) 2015 by Daniel Krebs                                    *
3  *   Daniel Krebs - github@daniel-krebs.net                                *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
17  ***************************************************************************/
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include <helper/time_support.h>
24 #include <jtag/jtag.h>
25 #include "target/target.h"
26 #include "target/target_type.h"
27 #include "rtos.h"
28 #include "helper/log.h"
29 #include "helper/types.h"
30 #include "target/armv7m.h"
31 #include "rtos_riot_stackings.h"
32
33 static bool riot_detect_rtos(struct target *target);
34 static int riot_create(struct target *target);
35 static int riot_update_threads(struct rtos *rtos);
36 static int riot_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
37         struct rtos_reg **reg_list, int *num_regs);
38 static int riot_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]);
39
40 struct riot_thread_state {
41         int value;
42         const char *desc;
43 };
44
45 /* refer RIOT sched.h */
46 static const struct riot_thread_state riot_thread_states[] = {
47         { 0, "Stopped" },
48         { 1, "Zombie" },
49         { 2, "Sleeping" },
50         { 3, "Blocked mutex" },
51         { 4, "Blocked receive" },
52         { 5, "Blocked send" },
53         { 6, "Blocked reply" },
54         { 7, "Blocked any flag" },
55         { 8, "Blocked all flags" },
56         { 9, "Blocked mbox" },
57         { 10, "Blocked condition" },
58         { 11, "Running" },
59         { 12, "Pending" },
60 };
61 #define RIOT_NUM_STATES ARRAY_SIZE(riot_thread_states)
62
63 struct riot_params {
64         const char *target_name;
65         unsigned char thread_sp_offset;
66         unsigned char thread_status_offset;
67 };
68
69 static const struct riot_params riot_params_list[] = {
70         {
71                 "cortex_m",             /* target_name */
72                 0x00,                                   /* thread_sp_offset */
73                 0x04,                                   /* thread_status_offset */
74         },
75         {       /* STLink */
76                 "hla_target",           /* target_name */
77                 0x00,                   /* thread_sp_offset */
78                 0x04,                   /* thread_status_offset */
79         }
80 };
81 #define RIOT_NUM_PARAMS ARRAY_SIZE(riot_params_list)
82
83 /* Initialize in riot_create() depending on architecture */
84 static const struct rtos_register_stacking *stacking_info;
85
86 enum riot_symbol_values {
87         RIOT_THREADS_BASE = 0,
88         RIOT_NUM_THREADS,
89         RIOT_ACTIVE_PID,
90         RIOT_MAX_THREADS,
91         RIOT_NAME_OFFSET,
92 };
93
94 struct riot_symbol {
95         const char *const name;
96         bool optional;
97 };
98
99 /* refer RIOT core/sched.c */
100 static struct riot_symbol const riot_symbol_list[] = {
101         {"sched_threads", false},
102         {"sched_num_threads", false},
103         {"sched_active_pid", false},
104         {"max_threads", false},
105         {"_tcb_name_offset", true},
106         {NULL, false}
107 };
108
109 const struct rtos_type riot_rtos = {
110         .name = "RIOT",
111         .detect_rtos = riot_detect_rtos,
112         .create = riot_create,
113         .update_threads = riot_update_threads,
114         .get_thread_reg_list = riot_get_thread_reg_list,
115         .get_symbol_list_to_lookup = riot_get_symbol_list_to_lookup,
116 };
117
118 static int riot_update_threads(struct rtos *rtos)
119 {
120         int retval;
121         unsigned int tasks_found = 0;
122         const struct riot_params *param;
123
124         if (!rtos)
125                 return ERROR_FAIL;
126
127         if (!rtos->rtos_specific_params)
128                 return ERROR_FAIL;
129
130         param = (const struct riot_params *)rtos->rtos_specific_params;
131
132         if (!rtos->symbols) {
133                 LOG_ERROR("No symbols for RIOT");
134                 return ERROR_FAIL;
135         }
136
137         if (rtos->symbols[RIOT_THREADS_BASE].address == 0) {
138                 LOG_ERROR("Can't find symbol `%s`",
139                         riot_symbol_list[RIOT_THREADS_BASE].name);
140                 return ERROR_FAIL;
141         }
142
143         /* wipe out previous thread details if any */
144         rtos_free_threadlist(rtos);
145
146         /* Reset values */
147         rtos->current_thread = 0;
148         rtos->thread_count = 0;
149
150         /* read the current thread id */
151         int16_t active_pid = 0;
152         retval = target_read_u16(rtos->target,
153                         rtos->symbols[RIOT_ACTIVE_PID].address,
154                         (uint16_t *)&active_pid);
155         if (retval != ERROR_OK) {
156                 LOG_ERROR("Can't read symbol `%s`",
157                         riot_symbol_list[RIOT_ACTIVE_PID].name);
158                 return retval;
159         }
160         rtos->current_thread = active_pid;
161
162         /* read the current thread count
163          * It's `int` in RIOT, but this is Cortex M* only anyway */
164         int32_t thread_count = 0;
165         retval = target_read_u16(rtos->target,
166                         rtos->symbols[RIOT_NUM_THREADS].address,
167                         (uint16_t *)&thread_count);
168         if (retval != ERROR_OK) {
169                 LOG_ERROR("Can't read symbol `%s`",
170                         riot_symbol_list[RIOT_NUM_THREADS].name);
171                 return retval;
172         }
173         rtos->thread_count = thread_count;
174
175         /* read the maximum number of threads */
176         uint8_t max_threads = 0;
177         retval = target_read_u8(rtos->target,
178                         rtos->symbols[RIOT_MAX_THREADS].address,
179                         &max_threads);
180         if (retval != ERROR_OK) {
181                 LOG_ERROR("Can't read symbol `%s`",
182                         riot_symbol_list[RIOT_MAX_THREADS].name);
183                 return retval;
184         }
185
186         /* Base address of thread array */
187         uint32_t threads_base = rtos->symbols[RIOT_THREADS_BASE].address;
188
189         /* Try to get the offset of tcb_t::name, if absent RIOT wasn't compiled
190          * with DEVELHELP, so there are no thread names */
191         uint8_t name_offset = 0;
192         if (rtos->symbols[RIOT_NAME_OFFSET].address != 0) {
193                 retval = target_read_u8(rtos->target,
194                                 rtos->symbols[RIOT_NAME_OFFSET].address,
195                                 &name_offset);
196                 if (retval != ERROR_OK) {
197                         LOG_ERROR("Can't read symbol `%s`",
198                                 riot_symbol_list[RIOT_NAME_OFFSET].name);
199                         return retval;
200                 }
201         }
202
203         /* Allocate memory for thread description */
204         rtos->thread_details = calloc(thread_count, sizeof(struct thread_detail));
205         if (!rtos->thread_details) {
206                 LOG_ERROR("RIOT: out of memory");
207                 return ERROR_FAIL;
208         }
209
210         /* Buffer for thread names, maximum to display is 32 */
211         char buffer[32];
212
213         for (unsigned int i = 0; i < max_threads; i++) {
214                 /* get pointer to tcb_t */
215                 uint32_t tcb_pointer = 0;
216                 retval = target_read_u32(rtos->target,
217                                 threads_base + (i * 4),
218                                 &tcb_pointer);
219                 if (retval != ERROR_OK) {
220                         LOG_ERROR("Can't parse `%s`",
221                                 riot_symbol_list[RIOT_THREADS_BASE].name);
222                         goto error;
223                 }
224
225                 if (tcb_pointer == 0) {
226                         /* PID unused */
227                         continue;
228                 }
229
230                 /* Index is PID */
231                 rtos->thread_details[tasks_found].threadid = i;
232
233                 /* read thread state */
234                 uint8_t status = 0;
235                 retval = target_read_u8(rtos->target,
236                                 tcb_pointer + param->thread_status_offset,
237                                 &status);
238                 if (retval != ERROR_OK) {
239                         LOG_ERROR("Can't parse `%s`",
240                                 riot_symbol_list[RIOT_THREADS_BASE].name);
241                         goto error;
242                 }
243
244                 /* Search for state */
245                 unsigned int k;
246                 for (k = 0; k < RIOT_NUM_STATES; k++) {
247                         if (riot_thread_states[k].value == status)
248                                 break;
249                 }
250
251                 /* Copy state string */
252                 if (k >= RIOT_NUM_STATES) {
253                         rtos->thread_details[tasks_found].extra_info_str =
254                         strdup("unknown state");
255                 } else {
256                         rtos->thread_details[tasks_found].extra_info_str =
257                         strdup(riot_thread_states[k].desc);
258                 }
259
260                 if (!rtos->thread_details[tasks_found].extra_info_str) {
261                         LOG_ERROR("RIOT: out of memory");
262                         retval = ERROR_FAIL;
263                         goto error;
264                 }
265
266                 /* Thread names are only available if compiled with DEVELHELP */
267                 if (name_offset != 0) {
268                         uint32_t name_pointer = 0;
269                         retval = target_read_u32(rtos->target,
270                                         tcb_pointer + name_offset,
271                                         &name_pointer);
272                         if (retval != ERROR_OK) {
273                                 LOG_ERROR("Can't parse `%s`",
274                                         riot_symbol_list[RIOT_THREADS_BASE].name);
275                                 goto error;
276                         }
277
278                         /* read thread name */
279                         retval = target_read_buffer(rtos->target,
280                                         name_pointer,
281                                         sizeof(buffer),
282                                         (uint8_t *)&buffer);
283                         if (retval != ERROR_OK) {
284                                 LOG_ERROR("Can't parse `%s`",
285                                         riot_symbol_list[RIOT_THREADS_BASE].name);
286                                 goto error;
287                         }
288
289                         /* Make sure the string in the buffer terminates */
290                         if (buffer[sizeof(buffer) - 1] != 0)
291                                 buffer[sizeof(buffer) - 1] = 0;
292
293                         /* Copy thread name */
294                         rtos->thread_details[tasks_found].thread_name_str =
295                         strdup(buffer);
296
297                 } else {
298                         rtos->thread_details[tasks_found].thread_name_str =
299                         strdup("Enable DEVELHELP to see task names");
300                 }
301
302                 if (!rtos->thread_details[tasks_found].thread_name_str) {
303                         LOG_ERROR("RIOT: out of memory");
304                         retval = ERROR_FAIL;
305                         goto error;
306                 }
307
308                 rtos->thread_details[tasks_found].exists = true;
309
310                 tasks_found++;
311         }
312
313         return ERROR_OK;
314
315 error:
316         rtos_free_threadlist(rtos);
317         return retval;
318 }
319
320 static int riot_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
321                 struct rtos_reg **reg_list, int *num_regs)
322 {
323         int retval;
324         const struct riot_params *param;
325
326         if (!rtos)
327                 return ERROR_FAIL;
328
329         if (thread_id == 0)
330                 return ERROR_FAIL;
331
332         if (!rtos->rtos_specific_params)
333                 return ERROR_FAIL;
334
335         param = (const struct riot_params *)rtos->rtos_specific_params;
336
337         /* find the thread with given thread id */
338         uint32_t threads_base = rtos->symbols[RIOT_THREADS_BASE].address;
339         uint32_t tcb_pointer = 0;
340         retval = target_read_u32(rtos->target,
341                         threads_base + (thread_id * 4),
342                         &tcb_pointer);
343         if (retval != ERROR_OK) {
344                 LOG_ERROR("Can't parse `%s`", riot_symbol_list[RIOT_THREADS_BASE].name);
345                 return retval;
346         }
347
348         /* read stack pointer for that thread */
349         uint32_t stackptr = 0;
350         retval = target_read_u32(rtos->target,
351                         tcb_pointer + param->thread_sp_offset,
352                         &stackptr);
353         if (retval != ERROR_OK) {
354                 LOG_ERROR("Can't parse `%s`", riot_symbol_list[RIOT_THREADS_BASE].name);
355                 return retval;
356         }
357
358         return rtos_generic_stack_read(rtos->target,
359                         stacking_info,
360                         stackptr,
361                         reg_list,
362                         num_regs);
363 }
364
365 static int riot_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[])
366 {
367         *symbol_list = calloc(ARRAY_SIZE(riot_symbol_list), sizeof(struct symbol_table_elem));
368
369         if (!*symbol_list) {
370                 LOG_ERROR("RIOT: out of memory");
371                 return ERROR_FAIL;
372         }
373
374         for (unsigned int i = 0; i < ARRAY_SIZE(riot_symbol_list); i++) {
375                 (*symbol_list)[i].symbol_name = riot_symbol_list[i].name;
376                 (*symbol_list)[i].optional = riot_symbol_list[i].optional;
377         }
378
379         return ERROR_OK;
380 }
381
382 static bool riot_detect_rtos(struct target *target)
383 {
384         if ((target->rtos->symbols) &&
385                 (target->rtos->symbols[RIOT_THREADS_BASE].address != 0)) {
386                 /* looks like RIOT */
387                 return true;
388         }
389         return false;
390 }
391
392 static int riot_create(struct target *target)
393 {
394         unsigned int i = 0;
395
396         /* lookup if target is supported by RIOT */
397         while ((i < RIOT_NUM_PARAMS) &&
398                 (strcmp(riot_params_list[i].target_name, target->type->name) != 0)) {
399                 i++;
400         }
401         if (i >= RIOT_NUM_PARAMS) {
402                 LOG_ERROR("Could not find target in RIOT compatibility list");
403                 return ERROR_FAIL;
404         }
405
406         target->rtos->rtos_specific_params = (void *)&riot_params_list[i];
407         target->rtos->current_thread = 0;
408         target->rtos->thread_details = NULL;
409
410         /* Stacking is different depending on architecture */
411         struct armv7m_common *armv7m_target = target_to_armv7m(target);
412
413         if (armv7m_target->arm.arch == ARM_ARCH_V6M)
414                 stacking_info = &rtos_riot_cortex_m0_stacking;
415         else if (is_armv7m(armv7m_target))
416                 stacking_info = &rtos_riot_cortex_m34_stacking;
417         else {
418                 LOG_ERROR("No stacking info for architecture");
419                 return ERROR_FAIL;
420         }
421         return ERROR_OK;
422 }