简体   繁体   English

Flex / Yacc程序在VC ++中导致自由指令断点

[英]Flex / Yacc program causing breakpoint on free instruction in VC++

I have Flex / Yacc program that is causing a breakpoint when it is run in the VC++ 2012 IDE. 我有Flex / Yacc程序,该程序在VC ++ 2012 IDE中运行时会导致断点。 The breakpoint occurs on the instruction (in pre_lxr.l below): 断点出现在指令上(在下面的pre_lxr.l中):

free(pre_fname);

The project contains the lexer (.l file), the yacc file (.y) and an interface file (which sits between the parser and the C++ part of the program). 该项目包含lexer(.l文件),yacc文件(.y)和接口文件(位于解析器和程序的C ++部分之间)。 The problem seems to be related to memory corruption / leak. 该问题似乎与内存损坏/泄漏有关。 See code below (I've included as much relevant code as I think is necessary). 请参见下面的代码(我认为必要的话,我已经包含了许多相关的代码)。 I'm also not sure about the yyterminate call. 我也不确定这个电话。

/* pre_prs_ifc.h */
#define MAX_PRE_ERR 120
#define MAX_BANKS 16
#define MAX_PRES 512

typedef struct
{
    char *bank;
    char *name;
} preStruct;

void initPrePrs();
void appBank(int line_num, const char *name);
void appPre(int line_num, const char *bank, const char *name);
void freePrePrs();

char pre_err[MAX_PRE_ERR];
int num_banks;
int num_pres;
char* pre_fname;
int pre_lnum;
char* bank[MAX_BANKS];
preStruct pre[MAX_PRES];

/* pre_prs_ifc.c */
#include "pre_prs_ifc.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void initPrePrs()
// initialises parser variables
{
    pre_err[0] = 0;
    num_banks = 0;
    num_pres = 0;
    pre_lnum = 1;
}

void appBank(int line_num, const char *name)
{
    if (num_banks < MAX_BANKS)
        bank[num_banks++] = _strdup(name);
    else
        sprintf(pre_err, "Error on line %d: maximum number of banks     
exceeded.", line_num);
}

void appPre(int line_num, const char *bank, const char *name)
{
    if (num_pres < MAX_PRES)
    {
        pre[num_pres].bank = _strdup(bank);
        pre[num_pres++].name = _strdup(name);
    }
    else
        sprintf(pre_err, "Error on line %d: maximum number of presets     
exceeded.", line_num);
}

void freePrePrs()
// frees parser variables
{
    int i;
    for (i = 0; i < num_banks; i++)
        free(bank[i]);
    for (i = 0; i < num_pres; i++)
    {
        free(pre[i].bank);
        free(pre[i].name);
    }
}

/* pre_lxr.l */
%{
    #include "pre_prs_ifc.h"
    #include "pre_prs.h"
    #include <stdio.h>
%}

%option outfile="pre_lxr.c" header-file="pre_lxr.h"
%option prefix="pre"
%option warn reentrant noyywrap never-interactive nounistd bison-bridge
%option debug

%%

"banks"|"BANKS" { return banks; }
"presets"|"PRESETS" { return presets; }

"{" { return l_bracket; }
"}" { return r_bracket; }
";" { return semicolon; }

\"([^\\\"\n]|\\.)+\" {
    yylval->str = (char*)malloc(strlen(yytext) - 1);
    strncpy(yylval->str, &yytext[1], strlen(yytext) - 2);
    return quoted_str;
}

[ \t] { }
[\r\n] { pre_lnum++; }

<<EOF>> {
    free(pre_fname);
    if (!YY_CURRENT_BUFFER)
        yyterminate();
}

%%
int preerror(const char *msg)
{
    sprintf(pre_err, "Error in %s on line %d.", pre_fname, pre_lnum);
    return 0;
}

/* pre_prs.y */
%{
    #include "pre_prs_ifc.h"
    #include "pre_prs.h"
    #include "pre_lxr.h"
    int preerror(yyscan_t scanner, const char *msg);
%}

%code requires
{
    #ifndef YYSCAN_T
        #define YYSCAN_T
        typedef void* yyscan_t;
    #endif
}

%output  "pre_prs.c"
%defines "pre_prs.h"
%name-prefix "pre"
%define api.pure
%lex-param   { yyscan_t scanner }
%parse-param { yyscan_t scanner }
%error-verbose

%union
{
    char ch;
    char* str;
}

%destructor { free($$); } <str>

%token<ch> l_bracket r_bracket semicolon
%token<str> quoted_str
%token<num> banks presets

%%
cmds:
    | cmds cmd
    ;

cmd:
    bank_list
    |
    pre_list
    ;

bank_list:
    banks l_bracket bank_decls r_bracket
    ;

bank_decls:
    | bank_decls bank_decl
    ;

bank_decl:
    quoted_str semicolon
    {
        fprintf(stderr, "append bank %s\n", $1);
        appBank(pre_lnum, $1);
        free($1);
    }
    ;

pre_list:
    presets l_bracket pre_decls r_bracket
    ;

pre_decls:
    | pre_decls pre_decl
    ;

pre_decl:
    quoted_str quoted_str semicolon
    {
        fprintf(stderr, "append preset, bank %s, name %s\n", $1, $2);
        appPre(pre_lnum, $1, $2);
        free($1);
        free($2);
    }
    ;

%%

/* C++ using the above code */
extern "C"
{
    #include "pre_prs_ifc.h"
    #include "pre_prs.h"
    #include "pre_lxr.h"
}
int yyparse(yyscan_t scanner);
...
bool CMainDlg::loadBankPre(std::string fname)
// load bank and preset lists
{
    int err, i;
    yyscan_t scanner;
    FILE *src;
    std::string name_str;
    CListBox* bank_lb;
    bool ret_val;
    pre_fname = _strdup(fname.c_str());
    if (prelex_init(&scanner))
    {
        m_err = "Error initialising scanner.";
        return false;
    }
    src = fopen(fname.c_str(), "r");
    if (src == NULL)
    {
        m_err = "Could not open file: " + fname;
        ret_val = false;
    }
    else
    {
        preset_in(src, scanner);
        initPrePrs();
        err = preparse(scanner);
        if (err)
        {
            m_err = pre_err;
            ret_val = false;
        }
        else
        {
            if (pre_err[0] == 0)
            {
                bank_lb = (CListBox*)GetDlgItem(IDC_LI_BANK);
                for (i = 0; i < num_banks; i++)
                    bank_lb->AddString(bank[i]);
                ret_val = true;
            }
            else
            {
                m_err = pre_err;
                ret_val = false;
            }
        }
        fclose(src);
    }
    pre_delete_buffer(0, scanner);
    prelex_destroy(scanner);
    return ret_val;
}
CMainDlg::~CMainDlg()
// destructor
{
    freePrePrs();
}

You should never define global variables in a header file. 永远不要在头文件中定义全局变量。 The header file must declare them as extern , and they need to be defined in precisely one translation unit. 头文件必须将它们声明extern ,并且它们需要精确地以一个翻译单元定义

As written, each translation unit has its own definition of the global variables, including pre_fname . 如所写,每个翻译单元都有自己的全局变量定义,包括pre_fname That's undefined behaviour and there is no guarantee that the names used in different translation units refer to the same storage locations. 这是未定义的行为,不能保证在不同翻译单元中使用的名称都引用相同的存储位置。 Even better than fixing the declarations would be to pass them through to the parser and scanner so as to avoid using globals. 比固定声明更好的方法是将它们传递给解析器和扫描器,以避免使用全局变量。

In any event, pre_fname is created (with strdup ) in CMainDlg::loadBankPre and it makes most sense to also free it in the same function, ideally using a smart pointer. 无论如何, pre_fname是在CMainDlg :: loadBankPre中创建的(使用strdup ),并且在同一个函数中最好使用智能指针free它也很有意义。 In you did that, you wouldn't need an <<EOF>> rule at all. 这样,您根本不需要<<EOF>>规则。

Also, strncpy really is not a good idea here. 另外, strncpy确实不是一个好主意。 As @PaulOgilvie points out in a comment, it leaves the copied string unterminated, with the consequence that the later strdup in appBank will copy an undetermined number of extra bytes, possibly referencing invalid memory. 正如@PaulOgilvie在评论中指出的那样,它使复制的字符串保持未appBank ,结果是appBank更高版本的strdup将复制不确定数量的额外字节,可能引用了无效的内存。 (By the way, you don't need to call strlen on yytext . Flex provides the variable yyleng for precisely this purpose, which saves an extra scan over the token.) (顺便说一句,你不需要调用strlenyytext ,Flex提供了变量yyleng正是这个目的,从而节省了令牌额外的扫描。)

On the other hand, when you are formatting into a fixed-length buffer ( sprintf(pre_err...) ), you should use snprintf instead. 另一方面,当您格式化为固定长度的缓冲区( sprintf(pre_err...) )时,应改用snprintf Otherwise you might overrun pre_err , which could end up overwriting pre_fname . 否则,你可能会溢出pre_err ,这最终可能会覆盖pre_fname

Also, your prototype for preerror does not agree with its definition. 同样,您的preerror原型也不符合其定义。 So if it is called, something bad will happen. 因此,如果调用它,将会发生一些不良情况。

And finally, your <<EOF>> rule (which is unnecessary, see above), does not return 0, so the lexer will keep on trying to scan. 最后,您的<<EOF>>规则(不必要,见上文)不会返回0,因此词法分析器将继续尝试进行扫描。 <<EOF>> rules must either return 0 or provide a new input buffer. <<EOF>>规则必须返回0或提供新的输入缓冲区。 (Or otherwise exit.) In this case, since the <<EOF>> rule calls free , the second (and subsequent) activations of the rule will free the same storage multiple times. (或退出。)在这种情况下,由于<<EOF>>规则调用free ,因此第二次(及后续)激活该规则将多次释放相同的存储。

I found out that the EOF section is called over and over resulting in an infinite loop, which can be stopped by including a call to yypop_buffer_state. 我发现一次又一次地调用EOF部分会导致无限循环,可以通过包含对yypop_buffer_state的调用来停止该循环。

<<EOF>> {
    yypop_buffer_state(yyscanner);
    free(pre_fname);
    if (!YY_CURRENT_BUFFER)
        yyterminate();
}

So the free was causing a crash on the second time around the EOF section. 因此免费版在EOF部分附近第二次导致崩溃。 As rici pointed out though (in this case) the EOF section is not needed at all if the free is moved to the function loadBankPre. 正如rici指出的(在这种情况下),如果将free移到了loadBankPre函数,则根本不需要EOF节。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM