Weird issue with string_to_array

75 views
Skip to first unread message

Ilia Mirkin

unread,
Oct 6, 2012, 5:17:37 AM10/6/12
to prot...@googlegroups.com
Hello,

I've recently run into a problem with a (newly-written) custom
protobuf decoder, and I've narrowed down the issue to this simple
program:

"""
#include <iostream>
#include <string>

#include <string.h>

using namespace std;

inline void STLStringResizeUninitialized(string* s, size_t new_size) {
s->resize(new_size);
}

inline char* string_as_array(string* str) {
// DO NOT USE const_cast<char*>(str->data())! See the unittest for why.
return str->empty() ? NULL : &*str->begin();
}

int main(int argc, char** argv) {
string output;
STLStringResizeUninitialized(&output, 5);
string_as_array(&output);
cout << "output: " << output << endl;
return 0;
}
"""

Running this in a gdb session:

Breakpoint 1, main (argc=1, argv=0x7fffffffdf48) at simple_repro.cc:18
18 string output;
(gdb) n
19 STLStringResizeUninitialized(&output, 5);
(gdb)
20 string_as_array(&output);
(gdb) p *output._M_rep()
$1 = {<std::basic_string<char, std::char_traits<char>,
std::allocator<char> >::_Rep_base> = {_M_length = 5,
_M_capacity = 5, _M_refcount = 0}, static _S_max_size = <optimized
out>, static _S_terminal = <optimized out>,
static _S_empty_rep_storage = {0, 0, 0, 0}}
(gdb) n
21 cout << "output: " << output << endl;
(gdb) p *output._M_rep()
$2 = {<std::basic_string<char, std::char_traits<char>,
std::allocator<char> >::_Rep_base> = {_M_length = 5,
_M_capacity = 5, _M_refcount = -1}, static _S_max_size =
<optimized out>, static _S_terminal = <optimized out>,
static _S_empty_rep_storage = {0, 0, 0, 0}}


Note that the refcount went down to -1 after string_as_array is run.
I've reproduced this with gcc-4.5, 4.4, and 4.3 (for some reason I'm
having trouble building 4.1). Obviously I didn't start with this
program, the initial code was something like

func() {
string ret;
...
cis->ReadString(&ret, length);
...
return ret;
}

And I would get an abort() in ~basic_string (sometimes, not always) like:

*** glibc detected *** free(): invalid next size (fast): 0x00007fffdc083e90 ***

on the "return ret" line. The function didn't really do very much else
with ret in those cases. Changing the code to

output.resize(val32);
if (!cis_->ReadRaw((void *)output.c_str(), val32)) {

made everything work. I obviously realize that this is not a great way
to go, but I do think it ends up working in my specific situation.
However the effectively double-free (or at least ref-count-decrease)
seems quite worrying...

Any ideas? Should that string_as_array thing really work and I should
direct this at the gcc people?

Thanks,

-ilia

Ilia Mirkin

unread,
Oct 6, 2012, 7:41:55 AM10/6/12
to prot...@googlegroups.com
Actually after reading through basic_string.h and basic_string.tcc, it
seems that the -1 refcount is used to indicate unsharing of the
strings. So that is actually fine (and expected). However that still
leaves me with the original bug and less confidence that my "solution"
actually fixes anything vs just being a lucky workaround (one thing it
doesn't do is cause the un-sharing to happen, which I guess was the
purpose of having written string_to_Array that way). I guess it's time
for some more debugging...
Reply all
Reply to author
Forward
0 new messages