yacc/bison 的分段错误

问题描述 投票:0回答:3

我正在尝试为学校作业编写一个简单的 HTTP 请求解析器,但我遇到了无法摆脱的分段错误。我认为我的生产规则没问题。我已经在启用跟踪的情况下执行了 bison,它总是在解析我的标头的部分产生段错误:

Reducing stack by rule 9 (line 59):
   $1 = token ID ()
   $2 = token COLON ()
   $3 = token STRING ()
[4]    36661 segmentation fault (core dumped)  ./problem1 < input.txt

这是我的 request.l 文件的内容:

%option noyywrap
%{
    #include<stdio.h>
    #include "request.tab.h"
    char *strclone(char *str);
%}

num                                     [0-9]+(\.[0-9]{1,2})?
letter                                  [a-zA-Z]
letternum                               [a-zA-Z0-9\-]
id                                      {letter}{letternum}*
string                                  \"[^"]*\"
fieldvalue                              {string}|{num}

%%

(GET|HEAD|POST|PUT|DELETE|OPTIONS)      { yylval = strclone(yytext); return METHOD; }
HTTP\/{num}                             { yylval = strclone(yytext); return VERSION; }
{id}                                    { yylval = strclone(yytext); return ID; }
"/"                                     { return SLASH; }
"\n"                                    { return NEWLINE; }
{string}                                { yylval = strclone(yytext); return STRING; }
":"                                     { return COLON; }
[ \t\n]+                                       ;
. {
    printf("Unexpected: %c\nExiting...\n", *yytext);
    exit(0);
}

%%

char *strclone(char *str) {
    int len = strlen(str);
    char *clone = (char *)malloc(sizeof(char)*(len+1));
    strcpy(clone,str);
    return clone;
}

和我的 request.y 文件:

%{
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define YYSTYPE char*

extern int yylex();
extern int yyparse();
extern FILE* yyin;

void yyerror(const char* s);
%}

%token METHOD
%token SLASH
%token VERSION
%token STRING
%token ID
%token COLON
%token NEWLINE

%%

REQUEST: METHOD URI VERSION NEWLINE HEADERS {
       printf("%s %s", $1, $2);
    }
;

URI: SLASH DIR {
        $$ = (char *)malloc(sizeof(char)*(1+strlen($2)+1));
        sprintf($$, "//%s", $2);
    }
;

DIR: ID SLASH {
        $$ = (char *)malloc(sizeof(char)*(strlen($1)+2));
        sprintf($$, "%s//", $1);
    }
    |ID {
        $$ = $1;
    }
    | {
        $$ = "";
    }
;

HEADERS: HEADER {
        $$ = $1;
    }
    |HEADER NEWLINE HEADERS {
        $$ = (char *)malloc(sizeof(char)*(strlen($1)+1+strlen($3)+1));
        sprintf($$, "%s\n%s", $1, $3);
    }
    |{
        $$ = "";
    }
;

HEADER: ID COLON STRING {
        $$ = (char *)malloc(sizeof(char)*(strlen($1)+1+strlen($2)+1));
        sprintf($$, "%s:%s", $1, $3);
    }
;

%%

void yyerror (char const *s) {
   fprintf(stderr, "Poruka nije tacna\n");
}

int main() {
    yydebug = 1;
    yyin = stdin;

    do {
        yyparse();
    } while(!feof(yyin));

    return 0;
}

这也是我作为输入传递的 input.txt 的内容:

GET / HTTP/1.1
Host: "developer.mozzila.org"
Accept-language: "fr"
c parsing bison flex-lexer yacc
3个回答
1
投票

request.y
中,您包含指令

#define YYSTYPE char*

因此,在 Bison 生成的解析器代码中,

yylval
的类型为
char*
。但该行并未插入
request.l
中。因此,在 Flex 生成的扫描代码中,
yylval
有其默认类型,
int

您可以通过将

YYSTYPE
的定义添加到
request.l
文件中来解决此问题,但随后您会在两个地方重复相同的设置,这将导致灾难。相反,使用 Bison 的声明语法:

%define api.value.type { char* }

(注意:这是一个 Bison 声明,而不是 C 预处理器定义,因此它与您的其他 Bison

%
指令配合使用。)

这个解决方案的优点是 Bison 还将声明添加到它生成的头文件中。由于该文件已

#include
保存在
request.l
中,因此无需对您的扫描仪进行任何修改。

不幸的是,C 允许将指针转换为整数类型,即使整数类型太窄而无法容纳整个地址,这是具有 8 字节指针和 4 字节的典型 64 位平台的情况

int

。因此,在扫描器中,将编译器认为的四字节 
int
 的值设置为八字节指针意味着该值将被截断。因此,当解析器尝试将其用作地址时,您将收到段错误。如果你幸运的话。

大多数 C 编译器都会警告您有关此截断的信息 - 但前提是您告诉编译器您希望看到警告(

-Wall

 对于 clang 和 gcc)。使用 
-Wall
 进行编译始终很重要,即使在编译代码生成器的输出时也是如此。

您还需要修复

@JakobStark指出的拼写错误。


1
投票
HEADER: ID COLON STRING { $$ = (char *)malloc(sizeof(char)*(strlen($1)+1+strlen($2)+1)); sprintf($$, "%s:%s", $1, $3); };
您不应该在表达式中使用 

strlen($3)

 来计算组合字符串的长度吗?您使用的 
strlen($2)
 只会返回冒号字符串的长度,该长度应该为 1。如果您然后 
sprintf
 到太短的缓冲区,您将访问其长度后面的缓冲区。


0
投票
过去几天我也遇到了这个错误。我试图模仿 /bison/examples/c/glr.我使用联合作为值类型。 我收到此错误是因为 lexer.l 中定义的 token 的返回类型不是 union.struct 类型之一,而 flex 不会警告该错误。

© www.soinside.com 2019 - 2024. All rights reserved.