std::vector<std::string> Causes Segfault

So as many of you might know, a massive blizzard hit much of the Northeast a few days ago. Being trapped inside I decided I would write a script interpreter for a little scripting language that I'm going to use in one of my games. All was going well, when towards the end of its development, it began crashing for seemingly no reason.

Running GDB under Windows proved little use, revealing a backtrace of random system kernel calls. As a sanity check I copied the project into my Backtrack partition and tried again. Under Backtrack 5 the program crashes in the destructor (which is empty) for a very basic Value class that I have, which consists only of a string, int and enum. I ran it under valgrind to try and find heap corruption, but to no avail. I am using only STL containers and absolutely no dynamic memory (in my own code). The crash happens (consistently) when I call a function Value runFunction(string name, vector<Value> args) from within the Value runTokens(vector<Token> tkns) function. Just for shits and giggles I decided to write my own string class to use in my Value class. My string class only allocates memory, and never frees it. The program runs fine when I use it (disregarding the blatant memory leak). This leads me to believe that either the stack is being corrupted (which I highly doubt because all of my memory is managed by STL containers) or that somehow the string destructors are getting called multiple times. I cannot fathom as to why though.

I attempted to reproduce the problem on a smaller scale, but was unable to (further justifying my theory of heap corruption or multiple destructor calls). I was also unable to find any solutions through Google, only people telling the askers to do what I tried above. If anyone can provide any insight as to why this might be happening, it would be greatly appreciated.

If anyone wants to see the actual code (roughly 1200 lines), here are the files as well as the CodeBlocks projects. I'm not asking you guys to look through it, that would be crazy! But if you could run it on your systems in your setups and tell me if you observe anything differently that would be really nice.

Original: http://filecloud.io/euqxa6ld

Leaking string class: http://filecloud.io/mt6qwr8l

And a simple script to test it:

include.txt
1
2
3
4
5
6
int function(int $arg1, string $arg2, int $const)
{
    int $tmp = $arg1+ $const;
    print($arg2);
    return $tmp;
}


script.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include "include.txt"

int $var = 5;
string $str = "dog";  //this is a comment

/*
    Block Comment
    Wooo!
*/

beg:
print("Beg\n");
pause();

$var = ($var + 5) * 2 - 90 / 3;
print("Var = ", $var, "\n");

if ($var < 15)
{
    $str = $str + "gy";
}

if ($str == "doggy")
    $var = $var - 15;

print("Var = ", $var, "\n");
while ($var<=5)
{
    $var = $var + 1;
    print("Var = ", $var, "\n");
}

print(function($var, $str, 78));


goto beg;
Last edited on
Download links aren't working for me.
Crap :/ I'll fix those.

Edit: Fixed
Last edited on
Is there no main?
As naraku9333 pointed out, there doesn't seem to be a main in your functions.

May I ask why you always pass vectors by value?

It looks like it would be very easy in Script::runEquation to access outside of the vector.

Value::val is only assigned a value in Value::operator= (and the defaulted copy constructor,) meaning it's actual value will be unspecified everywhere. I have to assume it's inclusion in Value::nonZero will lead to incorrect results.
Last edited on
Line 63 in Script Interpreter.cpp could be the issue.

Edit: Yeah, remove the semicolon on 63.

Also, your readFile function wouldn't work for me in VS2013 so I used this
1
2
3
4
5
6
7
8
9
string Script::readFile(string file)
{
    //TODO - read from directory of script file, then from other search directories if not there
    ifstream input(file, ios::binary);
    string data;
    copy(istream_iterator<char>(input), istream_iterator<char>(), back_inserter(data));
    
    return data;
}
Last edited on
Script Interpreter.cpp: In member function ‘Value Script::runFunction(std::string, std::vector<Value>)’:
Script Interpreter.cpp:672:1: warning: control reaches end of non-void function [-Wreturn-type]



> Line 63 in Script Interpreter.cpp could be the issue.
> Edit: Yeah, remove the semicolon on 63.
1
2
3
4
5
6
7
//original
for (; data[i]!='\n'; i++);
i--;

//with `the' semicolon removed
for (; data[i]!='\n'; i++)
   i--; //infinite loop 
Last edited on
@Cire: I do have a main function, perhaps I forgot to zip main.cpp, I'll retype it below (I'm at school right now). I suppose I could convert to references, I just wasn't thinking of it when I wrote all the code, I was just trying to get all of the design down before I forgot it. Also, thanks for pointing out the problem with Value::val, it was a last minute addition I made but never really thought through. As for reading past the end(s) of the vectors, the interpreter somewhat assumes syntactical correctness of the script it's running. I figured since it was only for my personal use it wasn't a huge deal, although it's still a good idea to put checks in all of my loops.

@ne555: I like your readFile function better :) Line 63 just "reads" through the script data, ignoring characters until it reaches '\n' (this is for line comments). The semi colon is supposed to be there. The i--; afterwards is to essentially cancel the effect of the i++ in the for loop to ensure that all characters are read.

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "Script Interpreter.hpp"
#include <iostream>
using namespace std;

int main()
{
    string file;
    cout << "File: ";
    getline(cin, file);

    Script scr(file);
    try
    {
        scr.run();
    }
    catch (runtime_error err)
    {
        cout << err.what << endl;
    }

    return 0;
}


This may or may not be correct, but that's the jist of it. I don't have access to my files or environment at the moment. I'll definitely make your guys' recommended changes when I get home and I'll update this thread then.
@ne555
You're absolutely correct. Taking out the semicolon appeared to fix an issue I created with the change in readFile, the istream_iterator doesn't seem to be extracting newline chars from the stream which was causing the for loop to crash.
618
619
620
621
bool Script::isFunction(string name)
{
    return (functions.find(name)!=functions.end() && ( name=="print" || name=="pause")); //TODO - add other function names here
}
Last edited on
¿what's the point of the `functions' map then?
The functions map is for user defined functions, and the other checks are going to be for functions that I hard code right into the interpreter.

After making the recommended changes, the original problem still persists. In GDB under Windows the segmentation fault occurs in Value::operator= on the line where the std::string data member is copied. I'll play with it more in the next few days, I'm just too tired to think right now. I feel like it's going to end up being something that's stupidly simple, or that I'll just never figure it out.
With only changes to Value.hpp
1
2
3
4
5
6
Value()
    {
        val = 0;
        data = "";
        type = None;
    }

and Script Interpreter.cpp here
1
2
3
4
5
6
7
8
9
10
11
12
string Script::readFile(string file)
{
    //TODO - read from directory of script file, then from other search directories if not there
    ifstream input(file, ios::binary);
    string data, tmp;
    
    while(getline(input, tmp))
    {
        data += tmp + "\n";
    }
    return data;
}


and here
1
2
3
4
bool Script::isFunction(string name)
{
    return (functions.find(name)!=functions.end() && name=="print" || name=="pause"); //TODO - add other function names here
}

It runs with no exceptions for me in VS2013 and MinGW-4.8.1, also ran through GDB with no issues.
Output:
Data type: int
Function: function
Start args
Data type: int
Variable: arg1
Next arg
Data type: string
Variable: arg2
Next arg
Data type: int
Variable: const
End args
Start block
Data type: int
Variable: tmp
Operator: =
Variable: arg1
Operator: +
Variable: const
End statement
Function: print
Start args
Variable: arg2
End args
End statement
Return
Variable: tmp
End statement
End block
Data type: int
Variable: var
Operator: =
Int constant: 5
End statement
Data type: string
Variable: str
Operator: =
String constant: dog
End statement
JumpPoint: beg
Function: print
Start args
String constant: Beg

End args
End statement
Function: pause
Start args
End args
End statement
Variable: var
Operator: =
Operator: (
Variable: var
Operator: +
Int constant: 5
End args
Operator: *
Int constant: 2
Operator: -
Int constant: 90
Operator: /
Int constant: 3
End statement
Function: print
Start args
String constant: Var =
Next arg
Variable: var
Next arg
String constant:

End args
End statement
Conditional
Start args
Variable: var
Operator: <
Int constant: 15
End args
Start block
Variable: str
Operator: =
Variable: str
Operator: +
String constant: gy
End statement
End block
Conditional
Start args
Variable: str
Operator: ==
String constant: doggy
End args
Variable: var
Operator: =
Variable: var
Operator: -
Int constant: 15
End statement
Function: print
Start args
String constant: Var =
Next arg
Variable: var
Next arg
String constant:

End args
End statement
Loop
Start args
Variable: var
Operator: <=
Int constant: 5
End args
Start block
Variable: var
Operator: =
Variable: var
Operator: +
Int constant: 1
End statement
Function: print
Start args
String constant: Var =
Next arg
Variable: var
Next arg
String constant:

End args
End statement
End block
Function: print
Start args
Function: function
Start args
Variable: var
Next arg
Variable: str
Next arg
Int constant: 78
End args
End args
End statement
Goto: beg
End statement
Run tokens
Undefined function: print
> Undefined function: print
but that's one of the hard-coded functions.
According to the description in http://www.cplusplus.com/forum/general/121435/#msg661436 the original `Script::isFunction(string name)' was correct (it could be better implemented thought)

@ModShop: ¿what are you waiting to upload the updated code?
http://www.cplusplus.com/forum/general/112111/
Last edited on
@naraku9333: I added the default constructor to Value, as well as modified the readFile function, but I still get the segfault. I believe you don't get it because the program throws and exception because isFunction() will always return false in your version.

@ne555: Sorry, I haven't been on my computer much at all this weekend. Here is the updated code: http://www.filedropper.com/scriptinterpreter

EDIT: I changed the evaluate() function and runTokens() function to take references rather than values, but it made no difference. Is it at all possible that somehow std::vector is calling the same destructors twice?
Last edited on
You've got an invalid string object whose internal buffer points to garbage. So when its destructor is invoked you've got a bad delete.

That invalid object is constructed in the `Script::runFunction()' method, as you promise to return a `Value' but didn't do it (look at the path `TODO - hard code functions here')
That's why it says `warning: control reaches end of non-void function'
Now don't I feel stupid. Thank you so much for helping me! I probably never would have figured that one out, I was way over thinking it. Thanks again for your help :)
Topic archived. No new replies allowed.