I was trying to use an STL list in C++ and I arrived into a strange exception that I'm not able to understand.
The list is defined as list<ASTNode*> m_stats; and ASTNode* is a class. When I try to add elements by calling
ASTNode *node = new ASTNode();
m_stats.push_back(node);
it throws the following exception:
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00000004
0x91c20fe7 in std::_List_node_base::hook()
I tried to debug with gdb and inspected value inserted, is it not null and it is exactly what it should be..
the backtrace is:
#0 0x91c20fe7 in std::_List_node_base::hook ()
#1 0x0000a9fb in std::list<ASTNode*, std::allocator<ASTNode*> >::_M_insert (this=0x180344, __position={_M_node = 0x0}, __x=#0xbffff094) at stl_list.h:1152
#2 0x0000aa27 in std::list<ASTNode*, std::allocator<ASTNode*> >::push_front (this=0x180344, __x=#0xbffff094) at stl_list.h:743
#3 0x0000aa45 in ASTStatements::addStatement (this=0x180340, stat=0x180410) at ast.h:277
Am I missing something?
EDIT: added class source
class ASTStatements : public ASTNode
{
list<ASTNode*> m_stats;
public:
ASTStatements() {}
ASTStatements(list<ASTNode*> stats)
{
std::copy(stats.begin(), stats.end(), m_stats.begin());
}
ASTStatements(const ASTStatements &other)
{
std::copy(other.m_stats.begin(), other.m_stats.end(), m_stats.begin());
}
ASTStatements &operator= (const ASTStatements &other)
{
if (&other != this)
{
std::copy(other.m_stats.begin(), other.m_stats.end(), m_stats.begin());
}
}
ASTStatements *clone()
{
return new ASTStatements(*this);
}
u8 type()
{
return 0;
}
const char *generateASM()
{
list<ASTNode*>::iterator it = m_stats.begin();
while (it != m_stats.end())
{
((ASTNode*)*it)->generateASM();
++it;
}
}
void addStatement(ASTNode *stat)
{
m_stats.push_front(stat);
}
u8 typeCheck()
{
return 0;
}
};
I used it in a bison grammar file to handle multiple statements (didn't find a better way to handle a generic list of items in a non terminal) in this way:
statements:
statement { if ($$ == null) $$ = new ASTStatements(); ((ASTStatements*)$$)->addStatement($1); } statements { $$->generateASM(); }
;
Thanks in advance
Your constructors and assignment statements are wrong. When you call std::copy, there must already be enough space at the destination iterator to accommodate everything you're copying into it. The list won't grow by itself. The error message you're getting suggests you're overwriting some memory, and that's likely exactly what happens when you try to copy into a list that isn't big enough. (Formally, the behavior is undefined.)
You could use a std::back_insert_iterator, which is an iterator adapter that appends items to the underlying container instead of overwriting the current location. Create one using the std::back_inserter helper function from the <algorithm> header:
std::copy(stats.begin(), stats.end(), std::back_inserter(m_stats));
Better yet, though, is to skip all the copying and let the list's own constructors and assignment operators take care of it for you, as they're designed to:
ASTStatements(list<ASTNode*> stats)
: m_stats(stats)
{ }
ASTStatements& operator=(const ASTStatements& other)
{
m_stats = other.m_stats;
}
There was most likely something you did to your list that occurred before the lines of code you posted. I'd suggest looking at all places m_stats is used and double check your use of it. You could post your uses of m_stats here and we could try helping you some more.
Related
One error that I often see is a container being cleared whilst iterating through it. I have attempted to put together a small example program demonstrating this happening. One thing to note is that this can often happen many function calls deep so is quite hard to detect.
Note: This example deliberately shows some poorly designed code. I am trying to find a solution to detect the errors caused by writing code such as this without having to meticulously examine an entire codebase (~500 C++ units)
#include <iostream>
#include <string>
#include <vector>
class Bomb;
std::vector<Bomb> bombs;
class Bomb
{
std::string name;
public:
Bomb(std::string name)
{
this->name = name;
}
void touch()
{
if(rand() % 100 > 30)
{
/* Simulate everything being exploded! */
bombs.clear();
/* An error: "this" is no longer valid */
std::cout << "Crickey! The bomb was set off by " << name << std::endl;
}
}
};
int main()
{
bombs.push_back(Bomb("Freddy"));
bombs.push_back(Bomb("Charlie"));
bombs.push_back(Bomb("Teddy"));
bombs.push_back(Bomb("Trudy"));
for(size_t i = 0; i < bombs.size(); i++)
{
bombs.at(i).touch();
}
return 0;
}
Can anyone suggest a way of guaranteeing this cannot happen?
The only way I can currently detect this kind of thing is replacing the global new and delete with mmap / mprotect and detecting use after free memory accesses. This and Valgrind however sometimes fail to pick it up if the vector does not need to reallocate (i.e only some elements removed or the new size is not yet the reserve size). Ideally I don't want to have to clone much of the STL to make a version of std::vector that always reallocates every insertion/deletion during debug / testing.
One way that almost works is if the std::vector instead contains std::weak_ptr, then the usage of .lock() to create a temporary reference prevents its deletion whilst execution is within the classes method. However this cannot work with std::shared_ptr because you do not need lock() and same with plain objects. Creating a container of weak pointers just for this would be wasteful.
Can anyone else think of a way to protect ourselves from this.
Easiest way is to run your unit tests with Clang MemorySanitizer linked in.
Let some continuous-integration Linux box to do it automatically on each push
into repo.
MemorySanitizer has "Use-after-destruction detection" (flag -fsanitize-memory-use-after-dtor + environment variable MSAN_OPTIONS=poison_in_dtor=1) and so it will blow the test up that executes the code and that turns your continuous-integration red.
If you have neither unit tests nor continuous integration in place then you can also just manually debug your code with MemorySanitizer but that is hard way compared with the easiest. So better start to use continuous integration and write unit tests.
Note that there may be legitimate reasons of memory reads and writes after destructor has been ran but memory hasn't yet been freed. For example std::variant<std::string,double>. It lets us to assign it std::string then double and so its implementation might destroy the string and reuse same storage for double. Filtering such cases out is unfortunately manual work at the moment, but tools evolve.
In your particular example the misery boils down to no less than two design flaws:
Your vector is a global variable. Limit the scope of all of your objects as much as possible and issues like this are less likely to occur.
Having the single responsibility principle in mind, I can hardly imagine how one could come up with a class that needs to have some method that either directly or indirectly (maybe through 100 layers of call stack) deletes objects that could happen to be this.
I am aware that your example is artificial and intentionally bad, so please don't get me wrong here: I'm sure that in your actual case it is not so obvious how sticking to some basic design rules can prevent you from doing this. But as I said, I strongly believe that good design will reduce the likelyhood of such bugs coming up. And in fact, I cannot remember that I was ever facing such an issue, but maybe I am just not experienced enough :)
However, if this really keeps being an issue despite sticking with some design rules, then I have this idea how to detect it:
Create a member int recursionDepth in your class and initialize it with 0
At the beginning of each non-private method increment it.
Use RAII to make sure that at the end of each method it is decremented again
In the destructor check it to be 0, otherwise it means that the destructor is directly or indirectly called by some method of this.
You may want to #ifdef all of this and enable it only in debug build. This would essentially make it a debug assertion, some people like them :)
Note, that this does not work in a multi threaded environment.
In the end I went with a custom iterator that if the owner std::vector resizes whilst the iterator is still in scope, it will log an error or abort (giving me a stacktrace of the program). This example is a bit convoluted but I have tried to simplify it as much as possible and removed unused functionality from the iterator.
This system has flagged up about 50 errors of this nature. Some may be repeats. However Valgrind and ElecricFence at this point came up clean which is disappointing (In total they flagged up around 10 which I have already fixed since the start of the code cleanup).
In this example I use clear() which Valgrind does flag as an error. However in the actual codebase it is random access erases (i.e vec.erase(vec.begin() + 9)) which I need to check and Valgrind unfortunately misses quite a few.
main.cpp
#include "sstd_vector.h"
#include <iostream>
#include <string>
#include <memory>
class Bomb;
sstd::vector<std::shared_ptr<Bomb> > bombs;
class Bomb
{
std::string name;
public:
Bomb(std::string name)
{
this->name = name;
}
void touch()
{
if(rand() % 100 > 30)
{
/* Simulate everything being exploded! */
bombs.clear(); // Causes an ABORT
std::cout << "Crickey! The bomb was set off by " << name << std::endl;
}
}
};
int main()
{
bombs.push_back(std::make_shared<Bomb>("Freddy"));
bombs.push_back(std::make_shared<Bomb>("Charlie"));
bombs.push_back(std::make_shared<Bomb>("Teddy"));
bombs.push_back(std::make_shared<Bomb>("Trudy"));
/* The key part is the lifetime of the iterator. If the vector
* changes during the lifetime of the iterator, even if it did
* not reallocate, an error will be logged */
for(sstd::vector<std::shared_ptr<Bomb> >::iterator it = bombs.begin(); it != bombs.end(); it++)
{
it->get()->touch();
}
return 0;
}
sstd_vector.h
#include <vector>
#include <stdlib.h>
namespace sstd
{
template <typename T>
class vector
{
std::vector<T> data;
size_t refs;
void check_valid()
{
if(refs > 0)
{
/* Report an error or abort */
abort();
}
}
public:
vector() : refs(0) { }
~vector()
{
check_valid();
}
vector& operator=(vector const& other)
{
check_valid();
data = other.data;
return *this;
}
void push_back(T val)
{
check_valid();
data.push_back(val);
}
void clear()
{
check_valid();
data.clear();
}
class iterator
{
friend class vector;
typename std::vector<T>::iterator it;
vector<T>* parent;
iterator() { }
iterator& operator=(iterator const&) { abort(); }
public:
iterator(iterator const& other)
{
it = other.it;
parent = other.parent;
parent->refs++;
}
~iterator()
{
parent->refs--;
}
bool operator !=(iterator const& other)
{
if(it != other.it) return true;
if(parent != other.parent) return true;
return false;
}
iterator operator ++(int val)
{
iterator rtn = *this;
it ++;
return rtn;
}
T* operator ->()
{
return &(*it);
}
T& operator *()
{
return *it;
}
};
iterator begin()
{
iterator rtn;
rtn.it = data.begin();
rtn.parent = this;
refs++;
return rtn;
}
iterator end()
{
iterator rtn;
rtn.it = data.end();
rtn.parent = this;
refs++;
return rtn;
}
};
}
The disadvantages of this system is that I must use an iterator rather than .at(idx) or [idx]. I personally don't mind this one so much. I can still use .begin() + idx if random access is needed.
It is a little bit slower (nothing compared to Valgrind though). When I am done, I can do a search / replace of sstd::vector with std::vector and there should be no performance drop.
I keep getting this error and I'm not sure how to correct it as I am given no errors in my code editors. I have looked up similar issues, but I am still having trouble to understand how to apply the solutions here. I've tried altering my code for several hours now, but to no avail. Any help would be appreciated. I have provided my .h and .cpp files below.
ErrorMessage.h
#ifndef SICT_ERRORMESSAGE_H
#define SICT_ERRORMESSAGE_H
#include <iostream>
namespace sict {
class ErrorMessage {
char* message_; //pointer that holds the address of the message stored in current object
public:
explicit ErrorMessage(const char* errorMessage = nullptr); //receive address of a C-style nullterminate string holding an error message
ErrorMessage(const ErrorMessage& em) = delete; //deleted copy constructor that prevents copying of an ErrorMessage object
ErrorMessage& operator=(const ErrorMessage& em) = delete; //deleted assignment operator that prevents assignment of ErrorMessage object to current object
virtual ~ErrorMessage(); //deallocates any memory that has been dynamically allocated by the current object
void clear(); //clears any message stored by current object and initialize object to safe, empty state
bool isClear() const; //return true if object is in a safe, empty state
void message(const char* str); //stores a copy of the C-style string pointed to by str
const char* message() const; //return address of the message stored in current object
};
//helper operator
std::ostream& operator<<(std::ostream& os, const ErrorMessage& err);
}
#endif
ErrorMessage.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
#include "ErrorMessage.h"
namespace sict {
ErrorMessage::ErrorMessage(const char* errorMessage) {
if(errorMessage == nullptr) {
message_ = nullptr;
}
else {
message(errorMessage);
}
}
ErrorMessage::~ErrorMessage() {
delete [] message_;
}
void ErrorMessage::clear() {
delete [] message_;
message_ = nullptr;
}
bool ErrorMessage::isClear() const {
if(message_ == nullptr) {
return true;
}
else {
return false;
}
}
void ErrorMessage::message(const char* str) {
delete [] message_;
message_ = new char[strlen(str) + 1];
strcpy(message_, str);
}
const char* ErrorMessage::message() const {
return message_;
}
std::ostream& operator<<(std::ostream& os, const ErrorMessage& err) {
if(!err.isClear()) {
os << err.message();
}
return os;
}
}
It's not surprising your code made it through editor syntax checks and compilation - it's valid code. It's just got an incorrect pointer somewhere.
This may mean that your'e accidentally dereferencing something , or perhaps passing a value somewhere you should be passing a pointer. You should get a compile time warning about that kind of stuff.
Another possibility is that you're failing to initialize some pointer, and its value happens to be 0xb75....
Clearly, neither you nor I are not likely to guess from whence this error originates. As Sam Varshavchik pointed out in a comment, you don't even know if the code you posted is the source of the error. Even if you guess your way through this one ( or perhaps keenly observe, Sam ), it's just plain silly to try to write C++ that way.
What you need is a debugger. A debugger is a program you run your program within, and it keeps track of the program's state so that when you have a memory violation, the debugger can produce a backtrace showing where in your source code the error occurred. You also have to compile your program with debugging support, so that the debugger has markers it can use to refer back to the source code.
It's a process far beyond the scope of your question, but one that's easy to learn about once you know what you're going for. Look for one that integrates with your IDE, if possible, as you're leveraging your development environment heavily. It's not unlikely that you already have it set up- you might just need to use it. Search for C++ debugging in the context of your editor first - if it turns up nothing, consider searching under your compiler suite, whatever that may be ( if your'e using open source, you're probably using gcc, and the matching debugger is gdb ).
You're about to gain a far more accurate understanding of what it is to program C / C++. Good luck.
I have a C++ structure defined as follows:
typedef struct event{
int id;
string name;
//int arg0;
QByteArray data;
bool operator<(const event& e) const
{
return id < e.id;
}
bool operator==(const event& e) const
{
return id == e.id;
}
}Event;
I also have a map defined as follows:
map<string, set<Event>> mapOfEventsByString;
When I want to see if an Event is associated to a given string I use this line of code:
if(mapOfEventsByString.find(aString)->second.count(event)==1)
{
//do stuff
}
Problem: Sometimes (and I mean that 9/10 times I can run the entire application with the exact same data set without any problems), I get a segmentation fault here:
bool operator<(const event& e) const
{
return id < e.id; <------- GIVES SEGMENTATION FAULT SOMETIMES
}
After many attempts at reproducing the error while debugging, I managed to pinpoint the segfault to that line. In that scenario e.id is filled with data and id says: "no such value".
Help?
Thank you
Without a backtrace we are only guessing, but this is a strong indication that the member id doesn't exist, so you're accessing memory you shouldn't.
If the member id doesn't exist, then your operator< call is broken. Given that it is invoked by the below highlighted part of your code:
if(mapOfEventsByString.find(aString)->second.count(event)==1)
// ^^^^^^^^^^^^^
that suggests to me that the below highlighted expression does not assuredly refer to a valid object:
if(mapOfEventsByString.find(aString)->second.count(event)==1)
// ^^^^^^
The only way that can happen is if the below highlighted dereference operation is invalid:
if(mapOfEventsByString.find(aString)->second.count(event)==1)
// ^^
which occurs when the find fails, returning mapOfEventsByString.end() (which cannot be dereferenced):
if(mapOfEventsByString.find(aString)->second.count(event)==1)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I think if you actually check for find success you'll see that, 1 out of 10 times, aString was not found in mapOfEventsByString.
Let's do this, instead:
const auto it = mapOfEventsByString.find(aString);
if (it != mapOfEventsByString.end() && it->second.count(event) == 1) {
// do stuff
}
Now you can put a breakpoint in for when it == mapOfEventsByString.end() and investigate why the find is failing. Good luck!
I have a following class:
class Op
{
private:
std::vector<std::string> m_operands;
std::string m_op;
public:
Op(std::string op = "") : m_op(op){}
std::string GenCode();
void AddOperand(std::string& operand) ;
std::vector<std::string> GetOperands() const { return m_operands; }
std::string GetOp() const { return m_op; }
};
The instances of the class are saved to std::vector<Op> m_movOpV;
I.e. the following method adds a new op:
Op* AddMovOp()
{
Op op("MovOp");
m_movOpV.push_back(op);
return &m_movOpV.back();
}
In addition I have defined :
typedef std::pair<std::string,Op*> Assignment;
std::vector<Assignment> m_proceduralAssign;
Thus,after adding the Op,I can generate an Assignment using the following function :
void AddAssignment(ModuleVCodeGen::Op* op,const std::string& lExp)
{
Assignment assignment = (Assignment)std::make_pair(lExp,op);
m_proceduralAssign.push_back(assignment);
}
The belowfollowing scenario generates a problem:
1.Op* op1 = AddMovOp();
2.op1->AddOperand("operand1");
3.AddAssign(op,"1");
4.Op* op2 = AddMovOp();
After performing the step 4. m_movOpV is updated correctly with new Op.But m_proceduralAssign has a junk data : The Assignment that already exists in m_proceduralAssign turns to have empty m_operands and bad pointer to m_op.
And no new Assignment (which I tried to add) is added.
Can you please advise where the problem happens?And how to solve it?
You should revisit your design. A big problem I see with your code right now is that, references and pointers to vector objects are invalidated quite often (during push_backs and other operations), so by returning a pointer to the newly inserted item you are asking for trouble. If I were you I'd look into saving the index instead.
I've got a program where I have a std::vector as a member of a class:
class Blackboard
{
public:
inline std::vector<Vector2<int> > GetPath()
{ return m_path; }
inline void SetPath(std::vector<Vector2<int> > path)
{ m_path = path; }
inline void ClearPath()
{ if(m_path.size() > 0) m_path.clear(); }
private:
std::vector<Vector2<int>> m_path;
};
Where the Vector2 class is defined as:
template <class T>
class Vector2
{
private:
T m_x;
T m_y;
public:
Vector2(void)
{ m_x = 0; m_y = 0;}
Vector2(T x, T y)
{ m_x = x; m_y = y;}
~Vector2(void)
{ }
inline T x() const
{ return m_x; }
inline T y() const
{ return m_y; }
// ...
};
And at some point I call:
m_blackboard.ClearPath();
This works fine in debug, but crashes in release with the "Microsoft Visual Studio C Runtime Library has detected a fatal error in Test2.exe." message.
The call stack, at the last point where I can still see shows that:
Test2.exe!std::vector<RBT::Vector2<int>,
std::allocator<RBT::Vector2<int> > >::erase
(std::_Vector_const_iterator<RBT::Vector2<int>,
std::allocator<RBT::Vector2<int> > >
_First_arg={m_x=15 m_y=7 },
std::_Vector_const_iterator<RBT::Vector2<int>,
std::allocator<RBT::Vector2<int> > >
_Last_arg={m_x=15 m_y=8 }) Line 1037 + 0xe bytes C++
Here is where I'm calling the code that ends up crashing:
BTNode::Status GoToDestBehavior::Update()
{
BTEntityData::Node* node = m_dataRef->m_bTree.GetNode(m_index);
if(node->m_state == BTNode::STATE_READY)
{
BehaviorTree::RequestDeferredAction(Batch::PATHFIND, m_dataRef->m_entityID);
return BTNode::STATE_RUNNING;
}
else if(node->m_state == BTNode::STATE_RUNNING)
{
std::vector<Vector2<int>> path = m_dataRef->m_blackboard.GetPath();
EntitySystem::Entity* entity = EntitySystem::GetEntity(m_dataRef->m_entityID);
Assert(entity != NULL, "Invalid entity\n");
Assert(entity->HasComponent(Component::PHYSICS_COMP), "Associated entity must have physics component to move\n");
int phyIndex = entity->GetComponentIndex(Component::PHYSICS_COMP);
PhysicsSystem::PhysicsData * physicsData = PhysicsSystem::GetComponent(phyIndex);
Assert(physicsData != NULL, "Invalid physics data\n");
// Path is empty, so finish
if(path.size() == 0)
{
physicsData->m_dir = Direction::NONE; // Stop because we are here
return BTNode::STATE_SUCCESS;
}
// Remove last element if we are at it
//LogFmt("Size of vector %d\n", path.size());
Vector2<int> last = path.back();
if(last.x() == physicsData->m_posX && last.y() == physicsData->m_posY)
{
path.pop_back();
}
// Last node of the path has been transversed
if(path.size() == 0)
{
physicsData->m_dir = Direction::NONE; // Stop because we are here
m_dataRef->m_blackboard.ClearPath();
return BTNode::STATE_SUCCESS;
}
Vector2<int> step = path.back();
physicsData->m_dir = Direction::VectorToDirection(physicsData->m_posX, physicsData->m_posY, step.x(), step.y());
if(physicsData->m_dir == Direction::NONE)
{
m_dataRef->m_blackboard.SetPath(path);
return BTNode::STATE_FAIL;
}
m_dataRef->m_blackboard.SetPath(path);
return BTNode::STATE_RUNNING;
}
return BTNode::STATE_ERROR;
}
I don't know why it's behaving like this. Most similar issues I've found online have the problem of calling clear on an empty array, but I have a guard against that, so it shouldn't be the issue.
The other thing I can think of is my Vector2 class requiring some kind of copy constructor or something for when I add elements to the vector, but in the end it's just 2 ints, so I don't know why that might be failing.
I've been over this code too much and might be missing something obvious.
It's perfectly fine to call clear on an empty container of any sort.
Using my psychic debugging skills, I have determined that in code you aren't showing us you're accessing elements of the vector that don't actually exist (possibly before you inserted them, and probably with operator[]). Usually element creation is done through resize, push_back, or insert.
The other possibility is that you have another memory corruption somewhere in your program.
I found an issue I had due to a change in data format. The std::list I was using changed from a pointer to a list to directly the list. This started causing all sorts of errors that checking for the size of the list did not solve and were caused by a ZeroMemory()/memset() call that wiped out all of the tracking data of the list, since it was now part of the class instead of a pointer to the list.
If you have an empty list and call .clear() on it with a crash, chances are you have messed up the internal tracking memory as mentioned by Mark in his answer. Look for a place where you are doing memory clearing on containing classes and the like as the most likely culprits.
I know it's been 8 years, but I thought too I had this problem when I was destroying an empty bst into which my code was sending a nullptr value to the __p variable in the implementation of "new_allocator.h". This __p is needed to never be null, as mentioned in the file itself!
// __p is not permitted to be a null pointer.
The solution is not sending anything if you don't have something to send, basically.