Skip to content

Better error handling on potential name mismatch #184

@Memnoc

Description

@Memnoc

Issue: Segfault on parser name mismatch

Program crashes with segfault when mpca_lang() references a parser name that doesn't exist (in my case, due to case mismatch). A better quality-of-life improvement for the library users would be to handle this crash more gracefully.

Reproduce

mpc_parser_t *MyParser = mpc_new("myparser");  // lowercase

mpca_lang(MPCA_LANG_DEFAULT,
  "rule : <MyParser> ;",  // uppercase - won't match
  MyParser);
// Segfault at mpc.c:4087

Root Cause

Location: mpc.c line ~4105-4120 in mpca_grammar_find_parser()

When searching for a non-existent parser:

  1. Loop exhausts va_arg() looking for matching name
  2. va_arg() returns garbage when exhausted
  3. Code accesses p->name before NULL check → segfault
while (1) {
  p = va_arg(*st->va, mpc_parser_t *);
  st->parsers_num++;
  st->parsers = realloc(...);  // Using p before validation
  
  if (p == NULL || p->name == NULL) {  // Too late
    return mpc_failf("Unknown Parser '%s'!", x);
  }

In the book, it occurred while following this part: https://www.buildyourownlisp.com/chapter6_parsing
At the line:

mpc_parser_t* Lispy    = mpc_new("lispy");

I have made a typo there and passed "Lispy" instead of "lispy" which led to the segfault and took me for ride 💃🏻

Enhacement

Check pointer validity immediately after va_arg() and provide a better error message:

while (1) {
  p = va_arg(*st->va, mpc_parser_t *);
  
  if (p == NULL || p->name == NULL) {
    // Helpful message with available parser names
    char msg[1024] = "Available: ";
    for (int i = 0; i < st->parsers_num; i++) {
      if (st->parsers[i] && st->parsers[i]->name) {
        strcat(msg, "'"); strcat(msg, st->parsers[i]->name); strcat(msg, "' ");
      }
    }
    return mpc_failf("Parser '<%s>' not found. %s", x, msg);
  }
  
  st->parsers_num++;
  st->parsers = realloc(...);
  // ... rest
}

Happy to send a PR your way if you think it's worth it! 😄

Environment

  • mpc: Latest from GitHub (0.9.0)
  • OS: Linux (Arch)
  • Compiler: GCC

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions