Почему получается segmentation fault у программы на yacc?

Я пытаюсь запустить пример(4.3. Обрабатываем конфигурационный файл) из этого HOW-TO. Это код, который подан вход как язык L1:

zone "." {
        type hint;
        file "/etc/bind/db.root";
};

Это исходники, с которыми я работаю:

LEX-файл (example6.l)
%{
#include <stdio.h>
#include "y.tab.h"
%}

%%

zone            return ZONETOK;
file            return FILETOK;
[a-zA-Z][a-zA-Z0-9]*    yylval=strdup(yytext); return WORD;
[a-zA-Z0-9\/.-]+        yylval=strdup(yytext); return FILENAME;
\"                      return QUOTE;
\{                      return OBRACE;
\}                      return EBRACE;
;                       return SEMICOLON;
\n                      /* ignore EOL */;
[ \t]+                  /* ignore whitespace */;
%%

Ниже - файл (example6.y) с грамматикой (yacc)

%{
#include <stdio.h>
#include <string.h>

#define YYSTYPE char *

int yydebug=0;//пока без дебага

void yyerror(const char *str)
{
    fprintf(stderr,"error: %s\n",str);
}

int yywrap()
{
    return 1;
}

main()
{
    yyparse();
}

%}

%token WORD FILENAME QUOTE OBRACE EBRACE SEMICOLON ZONETOK FILETOK

%%

commands:
    |    
    commands command SEMICOLON
    ;


command:
    zone_set 
    ;

zone_set:
    ZONETOK quotedname zonecontent
    {
        printf("Complete zone for '%s' found\n",$2);
    }
    ;

zonecontent:
    OBRACE zonestatements EBRACE 

quotedname:
    QUOTE FILENAME QUOTE
    {
        $$=$2;
    }
    ;

zonestatements:
    |
    zonestatements zonestatement SEMICOLON
    ;

zonestatement:
    statements
    |
    FILETOK quotedname 
    {
        printf("A zonefile name '%s' was encountered\n", $2);
    }
    ;

block: 
    OBRACE zonestatements EBRACE SEMICOLON
    ;

statements:
    | statements statement
    ;

statement: WORD | block | quotedname

Я компилирую вышеуказанные файлы:

yacc -d example6.y
lex (example6.l)
cc lex.yy.c y.tab.c -o example6

И ловлю Segmentation fault

elvin@ubuntu:~/Documents/myfirstlex/conf_file$ ./example6
zone "." {
        type hint;
        file "/etc/bind/db.root";
};Segmentation fault (core dumped)
elvin@ubuntu:~/Documents/myfirstlex/conf_file$

Пытаясь разобраться с ошибкой, я запускался в режиме отладки

elvin@ubuntu:~/Documents/myfirstlex/conf_file$ ./example6
Starting parse
Entering state 0
Reducing stack by rule 1 (line 30):
-> $$ = nterm commands ()
Stack now 0
Entering state 1
Reading a token: zone "." {
        type hint;
        file "/etc/bind/db.root";
        type hint;
};Next token is token ZONETOK ()
Shifting token ZONETOK ()
.............................
Stack now 0 1 3 7 10 13 15
Entering state 18
Reducing stack by rule 10 (line 65):
$1 = token FILETOK ()
$2 = nterm quotedname ()
Segmentation fault (core dumped)

Можете объяснить, в чём может быть проблема? Если проблема с strdup, то как это можно избежать её использование?


Ответы (1 шт):

Автор решения: Fat-Zer

Нужно добавить #define YYSTYPE char * перед #include "y.tab.h" в LEX-файл.


Ошибка банальная и компилятор об этом должен был явно предупреждать:

example6.l: В функции «yylex»:
example6.l:12:7: предупреждение: assignment to «YYSTYPE» {aka «int»} from «char *» makes integer from pointer without a cast [-Wint-conversion]
   12 | [a-zA-Z][a-zA-Z0-9]*    yylval=strdup(yytext); return WORD;
      |       ^
example6.l:13:7: предупреждение: assignment to «YYSTYPE» {aka «int»} from «char *» makes integer from pointer without a cast [-Wint-conversion]
   13 | [a-zA-Z0-9\/.-]+        yylval=strdup(yytext); return FILENAME;

HOWTO'шка писалась в те ламповые времена, когда указатели вмещались в int и поэтому на этом особо не заостряли внимания. Сегодня же попытка передать в 32-х битном int 64-х битный char *, само собой, заканчивается UB и крахом...

→ Ссылка