我正在尝试为学校作业编写一个简单的 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"
在
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指出的拼写错误。
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
到太短的缓冲区,您将访问其长度后面的缓冲区。