2

Suppose I' have an interface class

class foo_if_t {

};

, a first library libfoo_orig.so with class foo_t which inherits from foo_if_t and has the following code:

#include "foo_if.h"

class foo_t : public foo_if_t{
private:
    int res;
public:
    foo_t(){}
    virtual ~foo_t(){}
};

and a second library libfoo_mod.so with class foo_t redefined as follows:

#include "foo_if.h"

class foo_t : public foo_if_t{
private:
    int res[100];
public:
    foo_t() {
         for (int i=0; i<100; i++)
         res[i] = i;
    }
    virtual ~foo_t(){}
};

I create a symlink libfoo.so --> libfoo_orig.so and compile the following application

#include "foo_orig.h"

int main(){
    foo_if_t *foo_if = new foo_t();
    delete foo_if;
}

with g++ -ggdb -O0 test.cpp -o test -L. -lfoo (hence linking to libfoo.so).

At this point I change the symlink to target libfoo_mod.so and rerun the code. This will result in the following error:

*** Error in `./test': free(): invalid next size (fast): 0x0000000001ec9010 ***
Aborted (core dumped)

What I believe it could be happening is that the constructor allocation of foo_t from library foo_orig.so eats a smaller chunk of heap than the one from foo_mod.so, so when foo_mod.so foo_t constructor is invoked, it dirties the heap memory beyond the allocation boundaries (hence corrupting the heap next chunk reference). This is telling me that the heap reservation is somehow pre-calculated at link time, whereas I thought it would be resolved dynamically at runtime, based on the actual constructor code invoked. Am I getting it wrong, and, if not, why is the produced output code behaving like this ?

As counter-proof test, I've wrapped new foo_t() invocation within an implementation of static foo_it_t * foo_if_t::new_instance() written for each library; invoking new_instance from the main code works right.

Riccardo Manfrin
  • 653
  • 1
  • 7
  • 15

1 Answers1

2

The problem here is that your library code and the main program code have a different idea of the layout structure of foo_t. Even though they both recognise the existence of the type, they have both been compiled with a different version of the header that defines it. This is always going to lead to big trouble.

In your case, the trouble is caused by the fact that the actual memory allocation, performed by new, is compiled from the main program and so has one idea of what the size of the created object is. The constructor meanwhile, is compiled with the library code and has an entirely different idea of the underlying object size. So the main program creates an object on the heap that is sizeof(foo_t) - which is sizeof(int) from its point of view. The constructor, not knowing this, happily trounces memory up to 100 ints, thus corrupting the heap.

Basically you cannot 'cheat' in this way. If you change header files you should always recompile libraries that depend on this header, or face unpredictable trouble like this (obviously you're doing it intentionally in this case, but this can easily be done by accident too).

Smeeheey
  • 9,906
  • 23
  • 39