1

I am currently developing a shared library in c++ for Android devices.

While writing tests I stumbled over strange behaviour which causes a segfault (dlfree), when calling the function in the example code.

First of all:

  • The test which calls the library function links dynamically against the library.
  • I also compiled the library and the test for linux and windows desktops. There they run without causing a segfault.
  • Linking statically, the segfault does not appear on android.

Example code

typedef unsigned int DBRuleID;
typedef std::string DBRuleTarget;

struct DBRule {
  DBRuleID id; //int
  DBRuleTarget target; //std::string
};


//segfault variant
bool getRule(DBRuleID id, DBRule& rule) {
  rule.target = "I am causing segfault!";
  return true;
}


//working variant
bool getRule(DBRuleID id, DBRule& rule) {
  //nothing is set
  return true;
}

Segmentation Fault

Build fingerprint: 'generic/sdk/generic:3.0/HONEYCOMB/104254:eng/test-keys'
pid: 525, tid: 525  >>> /data/local/TestRulesDB <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadbaad
r0 deadbaad  r1 0000000c  r2 00000027  r3 00000000
 r4 00000080  r5 aff46658  r6 00013000  r7 00000004
 r8 00000004  r9 00013d3c  10 00000000  fp bec61a14
 ip ffffffff  sp bec61950  lr aff193e9  pc aff15f58  cpsr 00000030
         #00  pc 00015f58  /system/lib/libc.so
         #01  pc 00012d2a  /system/lib/libc.so (dlfree)

EDIT - New findings

If the DBRule struct, which is passed to the function, is initialized with values everything works fine, otherwise it results in a segmentation fault.

//works
DBRule rule_1 = { 0, "target"};

//works not
DBRule rule_1 = { 0, ""};

//works not
DBRule rule_1;

Could someone please explain that to me? And what will be the best way to initialize it by default?

The questions are

  • What am I doing wrong, what am I missing?
  • Is there a mechanism which tries to delete allocated memory on the heap more than once?

I fired up valgrind on the desktop already, but there are no errors shown.

Thanks in advance!

Johannes
  • 53
  • 5
  • Have you allocated memory to hold string value? `strdup` ftw... – t0mm13b Feb 06 '13 at 02:22
  • Do I have to allocate memory 'extra'/manually for type string within the struct? Could you please provide me with a short example to better understanding what you exactly mean? Thanks! – Johannes Feb 06 '13 at 09:26
  • See [this](http://stackoverflow.com/questions/252782/strdup-what-does-it-do-in-c) – t0mm13b Feb 06 '13 at 15:32
  • Neither strdup() nor strdupa() fix the problem. I am getting a segfault instantly when trying to "write" to rule.target - no matter how. (.resize fails too) – Johannes Feb 06 '13 at 22:11
  • Whats the struct declaration of `DBRuleTarget`? It *will* help to re-edit your question to include that vital information. – t0mm13b Feb 06 '13 at 23:26
  • Sorry, I thought naming the types as comments was obvious enough. Added typedefs now. – Johannes Feb 06 '13 at 23:49
  • Right, that is a C++ string, have a look at [this](http://www.cplusplus.com/reference/string/string/assign/) – t0mm13b Feb 07 '13 at 16:54

1 Answers1

1

Your issue is that all empty std::string objects use the same location for their internal storage: the internal storage of any empty std::string is the same static member. And the STL uses this location to determine whether or not it should deallocate the std::string's internal storage.

This works well for statically compiled code, or when you are not passing empty strings across dynamic library boundaries; but when you are dealing with dynamic libraries, issues start to crop up: both the executable and each dynamic library will have different storage locations for the empty std::string.

So here is what is going on: when this code executes:

rule.target = "I am causing segfault!";

The first thing that happens is that the internal storage of rule.target is deallocated. If rule.target was the empty std::string (regardless of how it got that way), then it will point to the global empty std::string storage of the code where it was initialized. If this did not happen inside your library, then your library will conclude it was not the empty string and will try to deallocate the storage. But since this was statically allocated by the client code, you get the segfault.

One way to fix this is to link your library statically (as you discovered); another is to use a shared version of the C++ runtime (and require clients to do the same) which exports the internal symbol for the empty std::string. The final solution is to avoid using std::string on your library's interface, directly or indirectly, if there is the chance that you will receive or send an empty std::string.

flamewing
  • 89
  • 7