LLVM Bugzilla is read-only and represents the historical archive of all LLVM issues filled before November 26, 2021. Use github to submit LLVM bugs

Bug 31454 - basic_string<T>::push_back() crashes if sizeof(T)>sizeof(long long)
Summary: basic_string<T>::push_back() crashes if sizeof(T)>sizeof(long long)
Status: RESOLVED FIXED
Alias: None
Product: libc++
Classification: Unclassified
Component: All Bugs (show other bugs)
Version: 3.9
Hardware: PC Linux
: P normal
Assignee: Marshall Clow (home)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2016-12-22 13:33 PST by Karen Arutyunov
Modified: 2018-02-07 13:30 PST (History)
5 users (show)

See Also:
Fixed By Commit(s):


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Karen Arutyunov 2016-12-22 13:33:39 PST
In build2 toolchain project we instantiate basic_string template with a char-like struct. Pushing back objects for such a string cases a program crash inside libc’s free() function.

The following code example reproduces the issue. In a real-life code such a char type requires a proper specialization of the char_traits template but for the current problem this is irrelevant.

The problem relates to the fact that after __set_long_cap(3) was called (inside the 2nd push_back()) the subsequent __get_long_cap() call (inside the 3rd push_back()) returns 2. After that (inside the 3rd push_back()) the expression (__sz == __cap) evaluates to false (as __sz == 2 and __cap == 1), this why __grow_by() is not called, and so traits_type::assign(*++__p, value_type()) writes out of the allocated memory block.

test.cxx:

#include <string>

struct bc
{
  long long a;
  char b; // If to remove this member the issue disappears.
};

typedef std::basic_string<bc> bstring;

int
main ()
{
  bc c;
  bstring s;
  s.push_back (c);
  s.push_back (c);
  s.push_back (c);
}

$ clang++ -stdlib=libc++ test.cxx 
$ ./a.out 
*** Error in `./a.out': free(): invalid next size (fast): 0x0000000000809010 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x77d75)[0x7f7603aafd75]
/lib64/libc.so.6(+0x801ca)[0x7f7603ab81ca]
/lib64/libc.so.6(cfree+0x4c)[0x7f7603abb72c]
./a.out[0x401134]
./a.out[0x400cb3]
/lib64/libc.so.6(__libc_start_main+0xf0)[0x7f7603a58580]
./a.out[0x400ab9]
======= Memory map: ========
00400000-00402000 r-xp 00000000 fd:00 33893461                           /home/karen/projects/build2/a.out
00601000-00602000 r--p 00001000 fd:00 33893461                           /home/karen/projects/build2/a.out
00602000-00603000 rw-p 00002000 fd:00 33893461                           /home/karen/projects/build2/a.out
00809000-0082a000 rw-p 00000000 00:00 0                                  [heap]
7f75fc000000-7f75fc021000 rw-p 00000000 00:00 0 
7f75fc021000-7f7600000000 ---p 00000000 00:00 0 
7f7603612000-7f7603619000 r-xp 00000000 fd:00 34797155                   /usr/lib64/librt-2.22.so
7f7603619000-7f7603818000 ---p 00007000 fd:00 34797155                   /usr/lib64/librt-2.22.so
7f7603818000-7f7603819000 r--p 00006000 fd:00 34797155                   /usr/lib64/librt-2.22.so
7f7603819000-7f760381a000 rw-p 00007000 fd:00 34797155                   /usr/lib64/librt-2.22.so
7f760381a000-7f7603832000 r-xp 00000000 fd:00 33555771                   /usr/lib64/libpthread-2.22.so
7f7603832000-7f7603a31000 ---p 00018000 fd:00 33555771                   /usr/lib64/libpthread-2.22.so
7f7603a31000-7f7603a33000 r--p 00017000 fd:00 33555771                   /usr/lib64/libpthread-2.22.so
7f7603a33000-7f7603a34000 rw-p 00019000 fd:00 33555771                   /usr/lib64/libpthread-2.22.so
7f7603a34000-7f7603a38000 rw-p 00000000 00:00 0 
7f7603a38000-7f7603bef000 r-xp 00000000 fd:00 33555743                   /usr/lib64/libc-2.22.so
7f7603bef000-7f7603def000 ---p 001b7000 fd:00 33555743                   /usr/lib64/libc-2.22.so
7f7603def000-7f7603df3000 r--p 001b7000 fd:00 33555743                   /usr/lib64/libc-2.22.so
7f7603df3000-7f7603df5000 rw-p 001bb000 fd:00 33555743                   /usr/lib64/libc-2.22.so
7f7603df5000-7f7603df9000 rw-p 00000000 00:00 0 
7f7603df9000-7f7603e0f000 r-xp 00000000 fd:00 35543539                   /usr/lib64/libgcc_s-5.3.1-20160406.so.1
7f7603e0f000-7f760400e000 ---p 00016000 fd:00 35543539                   /usr/lib64/libgcc_s-5.3.1-20160406.so.1
7f760400e000-7f760400f000 r--p 00015000 fd:00 35543539                   /usr/lib64/libgcc_s-5.3.1-20160406.so.1
7f760400f000-7f7604010000 rw-p 00016000 fd:00 35543539                   /usr/lib64/libgcc_s-5.3.1-20160406.so.1
7f7604010000-7f7604111000 r-xp 00000000 fd:00 34797143                   /usr/lib64/libm-2.22.so
7f7604111000-7f7604310000 ---p 00101000 fd:00 34797143                   /usr/lib64/libm-2.22.so
7f7604310000-7f7604311000 r--p 00100000 fd:00 34797143                   /usr/lib64/libm-2.22.so
7f7604311000-7f7604312000 rw-p 00101000 fd:00 34797143                   /usr/lib64/libm-2.22.so
7f7604312000-7f7604357000 r-xp 00000000 fd:00 35206817                   /usr/local/lib/libc++abi.so.1.0
7f7604357000-7f7604556000 ---p 00045000 fd:00 35206817                   /usr/local/lib/libc++abi.so.1.0
7f7604556000-7f7604559000 r--p 00044000 fd:00 35206817                   /usr/local/lib/libc++abi.so.1.0
7f7604559000-7f760455a000 rw-p 00047000 fd:00 35206817                   /usr/local/lib/libc++abi.so.1.0
7f760455a000-7f760460c000 r-xp 00000000 fd:00 34909176                   /usr/local/lib/libc++.so.1.0
7f760460c000-7f760480b000 ---p 000b2000 fd:00 34909176                   /usr/local/lib/libc++.so.1.0
7f760480b000-7f7604810000 r--p 000b1000 fd:00 34909176                   /usr/local/lib/libc++.so.1.0
7f7604810000-7f7604811000 rw-p 000b6000 fd:00 34909176                   /usr/local/lib/libc++.so.1.0
7f7604811000-7f7604814000 rw-p 00000000 00:00 0 
7f7604814000-7f7604835000 r-xp 00000000 fd:00 33555725                   /usr/lib64/ld-2.22.so
7f7604a1b000-7f7604a20000 rw-p 00000000 00:00 0 
7f7604a31000-7f7604a34000 rw-p 00000000 00:00 0 
7f7604a34000-7f7604a35000 r--p 00020000 fd:00 33555725                   /usr/lib64/ld-2.22.so
7f7604a35000-7f7604a36000 rw-p 00021000 fd:00 33555725                   /usr/lib64/ld-2.22.so
7f7604a36000-7f7604a37000 rw-p 00000000 00:00 0 
7ffc3d4e9000-7ffc3d50a000 rw-p 00000000 00:00 0                          [stack]
7ffc3d5c3000-7ffc3d5c5000 r--p 00000000 00:00 0                          [vvar]
7ffc3d5c5000-7ffc3d5c7000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted (core dumped)
Comment 1 Marshall Clow (home) 2017-02-14 07:35:50 PST
I've done some investigation here.  The problem is that the arithmetic here for large "character type" is incorrect. For this example, there should be no short strings, except for the empty string - but libc++ gets this wrong.

There's also the case for even larger "character types" where there are no short strings at all, which we don't handle either.

The problem is in the calculation for __min_cap, where it assumes that we'll always have room for at least two value of type CharT.
Comment 2 Marshall Clow (home) 2017-02-14 13:14:00 PST
Turns out that's not the problem - but it's something we should do - when we're contemplating an ABI change (because it certainly would be).

BTW, you can reproduce this w/o a struct - basic_string<__uint128_t> does it also.
Comment 3 Marshall Clow (home) 2017-02-21 06:29:59 PST
Changing __recommend() so that it never returns __min_cap makes the problem go away, but I don't understand why.

    size_type __recommend(size_type __s) _NOEXCEPT
        {
        if (__s < __min_cap) return static_cast<size_type>(__min_cap) - 1;
        size_type __guess = __align_it<sizeof(value_type) < __alignment ?
                     __alignment/sizeof(value_type) : 1 > (__s+1) - 1;
        if (__guess == __min_cap) ++__guess;
        return __guess;
        }

More investigation coming, but if you need a quick fix...
Comment 4 Marshall Clow (home) 2018-02-07 13:30:40 PST
revision 324531 fixes this.