Skip to content

Commit bc21d17

Browse files
committed
Merge pull request #1556 from pguyot/w10/fix-stack-traces-filenames
Fix handling of filenames in stack traces - Fix a crash that happened if a stack element was in another file - Optimize memory usage by not building lists of line references and of filenames - Fix parsing of files with no filename at all. This fixes most occurrences of 'unsupported line_ref tag: 0' (fixes #1388) These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents 33af056 + 59a54b8 commit bc21d17

File tree

6 files changed

+323
-130
lines changed

6 files changed

+323
-130
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ and a race condition in otp_socket code
5757
memory error
5858
- Fixed possible concurrency problems in ESP32 UART driver
5959
- Fixed concurrency and memory leak related to links and monitors
60+
- Fixed issues with parsing of line references for stack traces
6061

6162
### Changed
6263

src/libAtomVM/module.c

Lines changed: 161 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
static struct LiteralEntry *module_build_literals_table(const void *literalsBuf);
6363
static void module_add_label(Module *mod, int index, const uint8_t *ptr);
6464
static enum ModuleLoadResult module_build_imported_functions_table(Module *this_module, uint8_t *table_data, GlobalContext *glb);
65-
static void parse_line_table(uint16_t **line_refs, struct ModuleFilename **filenames, uint8_t *data, size_t len);
65+
static void module_parse_line_table(Module *mod, const uint8_t *data, size_t len);
6666

6767
#define IMPL_CODE_LOADER 1
6868
#include "opcodesswitch.h"
@@ -281,7 +281,7 @@ Module *module_new_from_iff_binary(GlobalContext *global, const void *iff_binary
281281
return NULL;
282282
}
283283

284-
parse_line_table(&mod->line_refs, &mod->filenames, beam_file + offsets[LINT] + 8, sizes[LINT]);
284+
module_parse_line_table(mod, beam_file + offsets[LINT] + 8, sizes[LINT]);
285285
list_init(&mod->line_ref_offsets);
286286

287287
if (offsets[LITT]) {
@@ -437,117 +437,187 @@ const struct ExportedFunction *module_resolve_function0(Module *mod, int import_
437437
}
438438
}
439439

440-
static uint16_t *parse_line_refs(uint8_t **data, size_t num_refs, size_t len)
440+
static bool module_check_line_refs(Module *mod, const uint8_t **data, size_t len)
441441
{
442-
uint16_t *ref_table = malloc((num_refs + 1) * sizeof(uint16_t));
443-
if (IS_NULL_PTR(ref_table)) {
444-
return NULL;
445-
}
446-
447442
// assert pos >= *data
448-
uint8_t *pos = *data;
449-
for (size_t i = 0; i < num_refs + 1; ++i) {
443+
const uint8_t *pos = *data;
444+
size_t i = 0;
445+
while (i < mod->line_refs_count) {
450446
if ((size_t) (pos - *data) > len) {
451447
fprintf(stderr, "Invalid line_ref: expected tag.\n");
452-
free(ref_table);
453-
return NULL;
448+
return false;
454449
}
455450
uint8_t tag = *pos;
456451
switch (tag & 0x0F) {
457452
case TAG_COMPACT_INT: {
458-
uint16_t line_idx = ((tag & 0xF0) >> 4);
459-
ref_table[i] = line_idx;
460-
++pos;
461-
break;
462-
}
463-
case TAG_COMPACT_ATOM: {
464-
uint16_t line_idx = ((tag & 0xF0) >> 4);
465-
ref_table[i] = line_idx;
453+
++i;
466454
++pos;
467455
break;
468456
}
469457
case TAG_EXTENDED_INT: {
470-
uint16_t high_order_3_bits = (tag & 0xE0);
471458
++pos;
472459
if ((size_t) (pos - *data) > len) {
473460
fprintf(stderr, "Invalid line_ref: expected extended int.\n");
474-
free(ref_table);
475-
return NULL;
461+
return false;
462+
}
463+
++i;
464+
++pos;
465+
break;
466+
}
467+
case TAG_COMPACT_ATOM: {
468+
uint16_t location_ix = ((tag & 0xF0) >> 4);
469+
if (location_ix > mod->locations_count) {
470+
fprintf(stderr, "Invalid line_ref: location_ix = %d is greater than locations_count = %d.\n", (int) location_ix, (int) mod->locations_count);
471+
return false;
476472
}
477-
uint8_t next_byte = *pos;
478-
uint16_t line_idx = ((high_order_3_bits << 3) | next_byte);
479473
++pos;
480-
ref_table[i] = line_idx;
481474
break;
482475
}
483476
case TAG_EXTENDED_ATOM: {
484-
uint16_t file_idx = ((tag & 0xF0) >> 4);
477+
uint16_t high_order_3_bits = (tag & 0xE0);
485478
++pos;
486479
if ((size_t) (pos - *data) > len) {
487-
fprintf(stderr, "Invalid line_ref: expected extended atom.\n");
488-
free(ref_table);
489-
return NULL;
480+
fprintf(stderr, "Invalid line_ref: expected extended int.\n");
481+
return false;
490482
}
491483
uint8_t next_byte = *pos;
492-
uint16_t line_idx = ((next_byte & 0xF0) >> 4);
484+
uint16_t location_ix = ((high_order_3_bits << 3) | next_byte);
485+
if (location_ix > mod->locations_count) {
486+
fprintf(stderr, "Invalid line_ref: location_ix = %d is greater than locations_count = %d.\n", (int) location_ix, (int) mod->locations_count);
487+
return false;
488+
}
493489
++pos;
494-
ref_table[file_idx - 1] = line_idx;
495490
break;
496491
}
497492
default:
498493
// TODO handle integer compact encodings > 2048
499494
fprintf(stderr, "Unsupported line_ref tag: %u\n", tag);
500-
free(ref_table);
501-
return NULL;
495+
return false;
502496
}
503497
}
504498

505499
*data = pos;
506-
return ref_table;
500+
return true;
507501
}
508502

509-
struct ModuleFilename *parse_filename_table(uint8_t **data, size_t num_filenames, size_t len)
503+
static bool module_check_locations(Module *mod, const uint8_t *data, size_t len)
510504
{
511-
struct ModuleFilename *filenames = malloc(num_filenames * sizeof(struct ModuleFilename));
512-
if (IS_NULL_PTR(filenames)) {
513-
return NULL;
514-
}
515-
516-
// assert pos >= *data
517-
uint8_t *pos = *data;
518-
for (size_t i = 0; i < num_filenames; ++i) {
519-
if ((size_t) ((pos + 2) - *data) > len) {
505+
const uint8_t *pos = data;
506+
for (size_t i = 1; i <= mod->locations_count; i++) {
507+
if ((size_t) ((pos + 2) - data) > len) {
520508
fprintf(stderr, "Invalid filename: expected 16-bit size.\n");
521-
free(filenames);
522-
return NULL;
509+
return false;
523510
}
524511
uint16_t size = READ_16_UNALIGNED(pos);
525-
pos +=2;
526-
if ((size_t) ((pos + size) - *data) > len) {
512+
pos += 2;
513+
if ((size_t) ((pos + size) - data) > len) {
527514
fprintf(stderr, "Invalid filename: expected filename data (%u bytes).\n", size);
528-
free(filenames);
529-
return NULL;
515+
return false;
530516
}
531-
filenames[i].len = size;
532-
filenames[i].data = pos;
533517
pos += size;
534518
}
535519

536-
*data = pos;
537-
return filenames;
520+
return true;
521+
}
522+
523+
static bool module_get_line_ref(Module *mod, uint16_t line_ref, uint16_t *out_line, uint16_t *out_location)
524+
{
525+
// First is undefined
526+
if (line_ref == 0) {
527+
*out_line = 0;
528+
*out_location = 0;
529+
return true;
530+
}
531+
532+
const uint8_t *pos = mod->line_refs_table;
533+
uint16_t location_ix = 0;
534+
size_t i = 1;
535+
while (i <= mod->line_refs_count) {
536+
uint8_t tag = *pos;
537+
switch (tag & 0x0F) {
538+
case TAG_COMPACT_INT: {
539+
if (i == line_ref) {
540+
uint16_t line_idx = ((tag & 0xF0) >> 4);
541+
*out_line = line_idx;
542+
*out_location = location_ix;
543+
return true;
544+
}
545+
++i;
546+
++pos;
547+
break;
548+
}
549+
case TAG_EXTENDED_INT: {
550+
if (i == line_ref) {
551+
uint16_t high_order_3_bits = (tag & 0xE0);
552+
uint16_t line_idx = ((high_order_3_bits << 3) | pos[1]);
553+
*out_line = line_idx;
554+
*out_location = location_ix;
555+
return true;
556+
}
557+
pos += 2;
558+
++i;
559+
break;
560+
}
561+
case TAG_COMPACT_ATOM: {
562+
location_ix = ((tag & 0xF0) >> 4);
563+
++pos;
564+
break;
565+
}
566+
case TAG_EXTENDED_ATOM: {
567+
uint16_t high_order_3_bits = (tag & 0xE0);
568+
location_ix = ((high_order_3_bits << 3) | pos[1]);
569+
pos += 2;
570+
break;
571+
}
572+
default:
573+
// TODO handle integer compact encodings > 2048
574+
return false;
575+
}
576+
}
577+
578+
return false;
538579
}
539580

540-
static void parse_line_table(uint16_t **line_refs, struct ModuleFilename **filenames, uint8_t *data, size_t len)
581+
582+
static bool module_get_location(Module *mod, uint16_t location_ix, size_t *filename_len, const uint8_t **filename)
541583
{
584+
// 0 is module.erl
585+
if (location_ix == 0) {
586+
*filename_len = 0;
587+
if (filename) {
588+
*filename = NULL;
589+
}
590+
return true;
591+
}
592+
593+
const uint8_t *pos = mod->locations_table;
594+
for (size_t i = 1; i <= mod->locations_count; i++) {
595+
uint16_t size = READ_16_UNALIGNED(pos);
596+
pos +=2;
597+
if (i == location_ix) {
598+
*filename_len = size;
599+
if (filename) {
600+
*filename = pos;
601+
}
602+
return true;
603+
}
604+
pos += size;
605+
}
542606

543-
*line_refs = NULL;
544-
*filenames = NULL;
607+
return false;
608+
}
545609

610+
static void module_parse_line_table(Module *mod, const uint8_t *data, size_t len)
611+
{
546612
if (len == 0) {
613+
mod->line_refs_count = 0;
614+
mod->line_refs_table = NULL;
615+
mod->locations_count = 0;
616+
mod->locations_table = NULL;
547617
return;
548618
}
549619

550-
uint8_t *pos = data;
620+
const uint8_t *pos = data;
551621

552622
CHECK_FREE_SPACE(4, "Error reading Line chunk: version\n");
553623
uint32_t version = READ_32_UNALIGNED(pos);
@@ -568,28 +638,36 @@ static void parse_line_table(uint16_t **line_refs, struct ModuleFilename **filen
568638
pos += 4;
569639

570640
CHECK_FREE_SPACE(4, "Error reading Line chunk: num_refs\n");
571-
uint32_t num_refs = READ_32_UNALIGNED(pos);
641+
mod->line_refs_count = READ_32_UNALIGNED(pos);
572642
pos += 4;
573643

574644
CHECK_FREE_SPACE(4, "Error reading Line chunk: num_filenames\n");
575-
uint32_t num_filenames = READ_32_UNALIGNED(pos);
645+
mod->locations_count = READ_32_UNALIGNED(pos);
576646
pos += 4;
577647

578-
*line_refs = parse_line_refs(&pos, num_refs, len - (pos - data));
579-
if (IS_NULL_PTR(*line_refs)) {
648+
mod->line_refs_table = pos;
649+
650+
if (UNLIKELY(!module_check_line_refs(mod, &pos, len - (pos - data)))) {
651+
mod->line_refs_count = 0;
652+
mod->line_refs_table = NULL;
653+
mod->locations_count = 0;
654+
mod->locations_table = NULL;
580655
return;
581656
}
582657

583-
*filenames = parse_filename_table(&pos, num_filenames, len - (pos - data));
584-
if (IS_NULL_PTR(*filenames)) {
585-
free(*line_refs);
586-
return;
658+
mod->locations_table = pos;
659+
660+
if (UNLIKELY(!module_check_locations(mod, pos, len - (pos - data)))) {
661+
mod->line_refs_count = 0;
662+
mod->line_refs_table = NULL;
663+
mod->locations_count = 0;
664+
mod->locations_table = NULL;
587665
}
588666
}
589667

590668
void module_insert_line_ref_offset(Module *mod, int line_ref, int offset)
591669
{
592-
if (IS_NULL_PTR(mod->line_refs) || line_ref == 0) {
670+
if (IS_NULL_PTR(mod->line_refs_table) || line_ref == 0) {
593671
return;
594672
}
595673
struct LineRefOffset *ref_offset = malloc(sizeof(struct LineRefOffset));
@@ -602,7 +680,16 @@ void module_insert_line_ref_offset(Module *mod, int line_ref, int offset)
602680
list_append(&mod->line_ref_offsets, &ref_offset->head);
603681
}
604682

605-
int module_find_line(Module *mod, unsigned int offset)
683+
static bool module_find_line_ref(Module *mod, uint16_t line_ref, uint16_t *line, size_t *filename_len, const uint8_t **filename)
684+
{
685+
uint16_t location_ix;
686+
if (UNLIKELY(!module_get_line_ref(mod, line_ref, line, &location_ix))) {
687+
return false;
688+
}
689+
return module_get_location(mod, location_ix, filename_len, filename);
690+
}
691+
692+
bool module_find_line(Module *mod, unsigned int offset, uint16_t *line, size_t *filename_len, const uint8_t **filename)
606693
{
607694
int i = 0;
608695
struct LineRefOffset *head = GET_LIST_ENTRY(&mod->line_ref_offsets, struct LineRefOffset, head);
@@ -611,25 +698,23 @@ int module_find_line(Module *mod, unsigned int offset)
611698
struct LineRefOffset *ref_offset = GET_LIST_ENTRY(item, struct LineRefOffset, head);
612699

613700
if (offset == ref_offset->offset) {
614-
return mod->line_refs[ref_offset->line_ref];
701+
return module_find_line_ref(mod, ref_offset->line_ref, line, filename_len, filename);
615702
} else if (i == 0 && offset < ref_offset->offset) {
616-
return -1;
703+
return false;
617704
} else {
618705

619706
struct LineRefOffset *prev_ref_offset = GET_LIST_ENTRY(ref_offset->head.prev, struct LineRefOffset, head);
620707
if (prev_ref_offset->offset <= offset && offset < ref_offset->offset) {
621-
return mod->line_refs[prev_ref_offset->line_ref];
708+
return module_find_line_ref(mod, prev_ref_offset->line_ref, line, filename_len, filename);
622709
}
623710

624711
struct LineRefOffset *next_ref_offset = GET_LIST_ENTRY(ref_offset->head.next, struct LineRefOffset, head);
625712
if (next_ref_offset == head && ref_offset->offset <= offset) {
626-
return mod->line_refs[ref_offset->line_ref];
713+
return module_find_line_ref(mod, ref_offset->line_ref, line, filename_len, filename);
627714
}
628715
}
629716

630717
++i;
631718
}
632-
// should never occur, but return is needed to squelch compiler warnings
633-
AVM_ABORT();
634-
return -1;
719+
return false;
635720
}

0 commit comments

Comments
 (0)