yacc/bison 的分段错误
Segmentation fault with yacc/bison
我正在尝试为学校作业编写一个简单的 HTTP 请求解析器,但我遇到了无法摆脱的分段错误。我认为我的生产规则没问题。我在启用跟踪的情况下执行了 bison,它总是在解析我的 header:
的部分产生段错误
Reducing stack by rule 9 (line 59):
= token ID ()
= token COLON ()
= 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", , );
}
;
URI: SLASH DIR {
$$ = (char *)malloc(sizeof(char)*(1+strlen()+1));
sprintf($$, "//%s", );
}
;
DIR: ID SLASH {
$$ = (char *)malloc(sizeof(char)*(strlen()+2));
sprintf($$, "%s//", );
}
|ID {
$$ = ;
}
| {
$$ = "";
}
;
HEADERS: HEADER {
$$ = ;
}
|HEADER NEWLINE HEADERS {
$$ = (char *)malloc(sizeof(char)*(strlen()+1+strlen()+1));
sprintf($$, "%s\n%s", , );
}
|{
$$ = "";
}
;
HEADER: ID COLON STRING {
$$ = (char *)malloc(sizeof(char)*(strlen()+1+strlen()+1));
sprintf($$, "%s:%s", , );
}
;
%%
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"
HEADER: ID COLON STRING {
$$ = (char *)malloc(sizeof(char)*(strlen()+1+strlen()+1));
sprintf($$, "%s:%s", , );
};
你不应该在计算组合字符串长度的表达式中使用 strlen()
吗? strlen()
因为你使用只会 return 冒号字符串的长度应该是 1。如果你然后 sprintf
到太短的缓冲区,你访问它的长度后面的缓冲区。
在 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还将声明添加到它生成的头文件中。由于该文件在 request.l
中 #include
d,因此无需对您的扫描仪进行任何修改。
不幸的是,C 允许将指针转换为整数类型,即使整数类型太窄而无法容纳整个地址,典型的 64 位平台和 8 字节指针就是这种情况, 4 字节 int
。因此,在您的扫描器中,将编译器认为是四字节 int
的值设置为八字节指针意味着该值将被截断。因此,当解析器尝试将其用作地址时,您将遇到段错误。运气好的话。
大多数 C 编译器会就此截断向您发出警告——但前提是您告诉编译器您希望看到警告(-Wall
用于 clang 和 gcc)。使用 -Wall
进行编译始终很重要,即使在编译代码生成器的输出时也是如此。
您还需要更正 @JakobStark 指出的拼写错误。
我正在尝试为学校作业编写一个简单的 HTTP 请求解析器,但我遇到了无法摆脱的分段错误。我认为我的生产规则没问题。我在启用跟踪的情况下执行了 bison,它总是在解析我的 header:
的部分产生段错误Reducing stack by rule 9 (line 59):
= token ID ()
= token COLON ()
= 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", , );
}
;
URI: SLASH DIR {
$$ = (char *)malloc(sizeof(char)*(1+strlen()+1));
sprintf($$, "//%s", );
}
;
DIR: ID SLASH {
$$ = (char *)malloc(sizeof(char)*(strlen()+2));
sprintf($$, "%s//", );
}
|ID {
$$ = ;
}
| {
$$ = "";
}
;
HEADERS: HEADER {
$$ = ;
}
|HEADER NEWLINE HEADERS {
$$ = (char *)malloc(sizeof(char)*(strlen()+1+strlen()+1));
sprintf($$, "%s\n%s", , );
}
|{
$$ = "";
}
;
HEADER: ID COLON STRING {
$$ = (char *)malloc(sizeof(char)*(strlen()+1+strlen()+1));
sprintf($$, "%s:%s", , );
}
;
%%
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"
HEADER: ID COLON STRING {
$$ = (char *)malloc(sizeof(char)*(strlen()+1+strlen()+1));
sprintf($$, "%s:%s", , );
};
你不应该在计算组合字符串长度的表达式中使用 strlen()
吗? strlen()
因为你使用只会 return 冒号字符串的长度应该是 1。如果你然后 sprintf
到太短的缓冲区,你访问它的长度后面的缓冲区。
在 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还将声明添加到它生成的头文件中。由于该文件在 request.l
中 #include
d,因此无需对您的扫描仪进行任何修改。
C 允许将指针转换为整数类型,即使整数类型太窄而无法容纳整个地址,典型的 64 位平台和 8 字节指针就是这种情况, 4 字节 int
。因此,在您的扫描器中,将编译器认为是四字节 int
的值设置为八字节指针意味着该值将被截断。因此,当解析器尝试将其用作地址时,您将遇到段错误。运气好的话。
大多数 C 编译器会就此截断向您发出警告——但前提是您告诉编译器您希望看到警告(-Wall
用于 clang 和 gcc)。使用 -Wall
进行编译始终很重要,即使在编译代码生成器的输出时也是如此。
您还需要更正 @JakobStark 指出的拼写错误。