1

My application is suspending on a line of code that appears to have nothing wrong with it, however my IDE appears to be suspending on that line with the error:

gdb/mi (24/03/09 13:36) (Exited. Signal 'SIGSEGV' received. Description: Segmentation fault.)

The line of code simply calls a method which has no code in it. Isn't a segmentation fault when you have a null reference? If so, how can an empty method have a null reference?

This piece of code, seems to be causing the issue:

#include <sys/socket.h>

#define BUFFER_SIZE 256

char *buffer;

buffer = (char*)GetSomePointer()->SomeStackMemoryString.c_str();
int writeResult = write(socketFD, buffer, BUFFER_SIZE);

bzero(buffer, BUFFER_SIZE);
int readResult = read(socketFD, buffer, BUFFER_SIZE);

When the line using the read(...) method is commented out, the problem goes away.

Update:

I have changed the question to point toward the actual problem, and I have removed all the irrelevant code - and I also answered my own question so that people reading this know specifically what the issue is, please read my answer before saying "you're a moron!".

Community
  • 1
  • 1
Nick Bolton
  • 34,516
  • 66
  • 162
  • 230
  • Can you post some piece of code ? – sameer karjatkar Mar 24 '09 at 13:49
  • Hmm not really, its rather complex. – Nick Bolton Mar 24 '09 at 13:56
  • From the edits you've added here, I don't think the marked answer is likely to be the problem. That behavour should be reproducible with/without your "long-running" code. The same behaviour as outlined in sharptooth's answer will occur if the vtable is getting corrupted too. – Richard Corden Mar 24 '09 at 14:44
  • Hmm, seems to be memory corruption. I agree, although sharptooth's answer was applicable before, now with my edits the answer by user9876 seems more likley... What should I do? – Nick Bolton Mar 24 '09 at 16:00
  • If you suspect heap allocation problems it's likely that some memory allocation call returns null because of memory shortage or because you try to allocate an unreasonably big block. Then you add some offset to that null and overwrite service data like vtables. – sharptooth Mar 24 '09 at 16:06
  • Try adding checks to the memory allocation routine calls to check what they return. – sharptooth Mar 24 '09 at 16:10
  • The code you posted is suspicious. How is the class hierarchy organized? – sharptooth Mar 24 '09 at 16:15
  • Also how is glutHandleReshape() called? A snippet might be helpful. – sharptooth Mar 24 '09 at 16:18
  • @sharptooth: How do I add checks to the memory allocation routine calls? – Nick Bolton Mar 24 '09 at 16:54
  • @sharptooth: Added code as requested. – Nick Bolton Mar 24 '09 at 17:02
  • After you've created the singleton, dump its address. Then, each time when the static callback is invoked dump the pointer returned by GetGlutInstance(). Is it always the same as the address of the created object? – sharptooth Mar 24 '09 at 17:17
  • What do you mean when you say "dump its address"? I haven't herd of this term before... I assume you don't mean delete the pointer. Also, are you saying that I shouldn't be using GetGlutInstance() directly? – Nick Bolton Mar 24 '09 at 17:47
  • Updated question and answered it. The issue is indeed caused because of a memory leak. – Nick Bolton Mar 24 '09 at 19:42
  • This is not a memory leak. Buffer points to a staticly allocated string. You do not own this memory. Passing it to bzero or read results in undefined behavior. In your case, you get SEGFAULT. Either change buffer to an array or allocate it on the heap. – Aaron Saarela Mar 24 '09 at 20:08
  • Ah, but the SEGFAULT isn't caused by bzero or read because of the line of code before that... buffer = (char*)GetSomePointer()->SomeStackMemoryString.c_str(); ... This causes bzero to see that the memory is allocated, and so not getting the segfault. Does this sound correct? – Nick Bolton Mar 24 '09 at 20:11
  • By the way, the SEGFAULT is occuring well after this code in a completely different function, when accessing a virtual method (i.e. the memory has been overwritten in the sample code in my question). – Nick Bolton Mar 24 '09 at 20:12

7 Answers7

10

First, calling a method through a null pointer or reference is strictly speaking undefined behaviour. But it may succeed unless the call is virtual.

Calling virtual methods virtually (through a pointer/reference, not from the derived class with Class::Method() way of invokation) always fails if the reference/pointer is null because virtual calls require access to vtable and accessing the vtable through a null pointer/reference is impossible. So you can't call an empty virtual method through a reference/pointer.

To understand this you need to know more about how code is organized. For every non-inlined method there's a section of code segment containing the machine code implementing the method.

When a call is done non-virtually (either from a derived class or a non-virtual method through a reference/pointer) the compiler knows exactly which method to call (no polymorphism). So it just inserts a call to an exact portion of code and passes this pointer as the first parameter there. In case of calling through null pointer this will be null too, but you don't care if your method is empty.

When a call is done virtually (through a reference/pointer) the compiler doesn't know which exactly method to call, it only knows that there's a table of virtual methods and the address of the table is stored in the object. In order to find what method to call it's necessary to first dereference the pointer/reference, get to the table, get the address of method from it and only then call the method. Reading the table is done in runtime, not during compilation. If the pointer/reference is null you get segmentation fault at this point.

This also explains why virtual calls can't be inlined. The compiler simply has no idea what code to inline when it's looking at the source during compilation.

sharptooth
  • 159,303
  • 82
  • 478
  • 911
  • It sounds like the answer I'm looking for, but I'm not sure I understand the theory behind what you're explaining. Is there a tutorial on this? – Nick Bolton Mar 24 '09 at 13:59
  • Calling a static method through a null pointer is supported and in the standard. – Torlack Mar 24 '09 at 17:01
3

Without code, the best I can do is a wild guess. But here goes:

Your "long-running code" is writing to an invalid pointer. (Either a totally random pointer, or going past the beginning/start of a buffer or array). This happens to be clobbering the virtual function table for your object - either it's overwriting the pointer to the object, or the vptr member of the object, or it's overwriting the actual global virtual function table for that class.

Some things to try:

  • Put a sentinel member in your class. E.g. an int which is initialised to a known pattern (0xdeadbeef or 0xcafebabe are common) in your constructor, and never changed. Before you make the virtual function call, check (assert()) that it still has the right value.
  • Try using a memory debugger. On Linux, options include Electric Fence (efence) or Valgrind.
  • Run your program under a debugger (gdb is fine) and poke around to see what's wrong - either post-mortem after the segfault happens, or by setting a breakpoint just before the place it's going to segfault.
user9876
  • 10,362
  • 6
  • 38
  • 64
3

Your code is bogus: buffer points to some random piece of memory. I'm not sure why the line with bzero is not failing.

The correct code is:

   char buffer[BUFFER_SIZE];

   bzero(buffer, BUFFER_SIZE);
   int readResult = read(socketFD, buffer, BUFFER_SIZE);

or you can use calloc(1, BUFFER_SIZE) to get some memory allocated (and zeroed out).

florin
  • 13,150
  • 6
  • 41
  • 47
  • Yep, exactly right! The pointer was there since the start, and the code in question was added after (hence why stack allocation wasn't used in the first place). – Nick Bolton Mar 24 '09 at 19:40
  • Also, bzero was not failing because of the GetSomePointer()->SomeStackMemoryString line (this was similar to what was in my original code, just added it to my question now). This was the crucial line of code that was stopping me from seeing the more than obvious problem. – Nick Bolton Mar 24 '09 at 20:07
2

I cannot think of any reason why an empty method on its own would cause such a problem. Without any other context, my first though would be that a problem elsewhere is corrupting your memory and it just so happens to manifest itself in this way here.

We had that kind of a problem before, and I wrote about it in this answer here. That same question has a lot of other good advice in it too which might help you.

Community
  • 1
  • 1
Richard Corden
  • 20,589
  • 6
  • 55
  • 83
2

Isn't a segmentation fault when you have a null reference?

Possibly, but not necessarily. What causes a segfault is somewhat platform-specific, but it basically means that your program is accessing memory that it shouldn't be. You might want to read the wikipedia article to get a better idea of what it is.

One thing you might check on, does the empty method have a return type? I could be wrong on this, but if it returns an object, I could see how a copy constructor can get called on garbage if the method isn't actually returning an object. This could cause all sorts of wonky behavior.

Do you get the same result if you change its return type to void or you return a value?

Jason Baker
  • 171,942
  • 122
  • 354
  • 501
1

The issue is because the buffer variable is using unassigned memory, which causes memory corruption when the read(...) function puts data in buffer.

Normally, bzero would actually cause the segmentation fault, but because a string is being assigned to the memory location, the read function was allowed to write past the allocated memory (causing the leak).

/* this causes *some* memory to be allocated, 
 * tricking bzero(...) to not SIGSEGV */
buffer = (char*)GetSomePointer()->SomeStackMemoryString.c_str();

int writeResult = write(socketFD, buffer, BUFFER_SIZE);

This change solves the memory leak:

#define BUFFER_SIZE 256

// Use memory on the stack, for auto allocation and release.
char buffer[BUFFER_SIZE];

// Don't write to the buffer, just pass in the chars on their own.
string writeString = GetSomePointer()->SomeStackMemoryString;
int writeResult = write(socketFD, writeString.c_str(), writeString.length());

// It's now safe to use the buffer, as stack memory is used.
bzero(buffer, BUFFER_SIZE);
int readResult = read(socketFD, buffer, BUFFER_SIZE);
Nick Bolton
  • 34,516
  • 66
  • 162
  • 230
0

Are you calling the virtual method from the constructor of a base class? That could be the problem: If you're calling a pure virtual method from class Base in Base's constructor, and it is only actually defined in class Derived, you might end up accessing a vtable record that has not yet been set, because Derived's constructor has not been executed at that point.

Carl Seleborg
  • 12,771
  • 11
  • 51
  • 70