rtos/riot: fix out-of-bounds read of optional symbols array
[openocd.git] / 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 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)