AddressSanitizer: invalid-pointer-pair from libc++ string __addr_in_range - c++

The following simple program makes AddressSanitizer to report "invalid-pointer-pair" with latest master build of Clang (doesn't happen with the latest official release 15):
#include <filesystem>
#include <iostream>
constexpr std::string_view str = "/usr";
const std::filesystem::path path{str};
int main() {
std::cout << "Path: " << path << std::endl;
}
What happens:
$ clang++ -fsanitize=address,pointer-compare,pointer-subtract -g -O0 -std=c++20 repro-asan.cpp -stdlib=libc++ -o repro-asan
$ export ASAN_OPTIONS="detect_invalid_pointer_pairs=2"
$ ./repro-asan
=================================================================
==3183863==ERROR: AddressSanitizer: invalid-pointer-pair: 0x563a8315eb41 0x563a82764600
error: failed to decompress '.debug_aranges', zlib is not available
error: failed to decompress '.debug_info', zlib is not available
error: failed to decompress '.debug_abbrev', zlib is not available
error: failed to decompress '.debug_line', zlib is not available
error: failed to decompress '.debug_str', zlib is not available
error: failed to decompress '.debug_line_str', zlib is not available
error: failed to decompress '.debug_loclists', zlib is not available
error: failed to decompress '.debug_rnglists', zlib is not available
#0 0x563a8274d818 in bool std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__addr_in_range[abi:v170000]<char const&>(char const&) const /home/me/dev/llvm/build/bin/../include/c++/v1/string:1979:23
#1 0x563a8274d418 in std::__1::enable_if<__is_cpp17_forward_iterator<char const*>::value, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&>::type std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::append[abi:v170000]<char const*>(char const*, char const*) /home/me/dev/llvm/build/bin/../include/c++/v1/string:2805:14
#2 0x563a8274d264 in std::__1::enable_if<__is_cpp17_forward_iterator<char const*>::value, void>::type std::__1::__fs::filesystem::_PathCVT<char>::__append_range[abi:v170000]<char const*>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&, char const*, char const*) /home/me/dev/llvm/build/bin/../include/c++/v1/__filesystem/path.h:316:12
#3 0x563a8274cfcd in void std::__1::__fs::filesystem::_PathCVT<char>::__append_source[abi:v170000]<std::__1::basic_string_view<char, std::__1::char_traits<char>>>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&, std::__1::basic_string_view<char, std::__1::char_traits<char>> const&) /home/me/dev/llvm/build/bin/../include/c++/v1/__filesystem/path.h:331:5
#4 0x563a8274cb2e in std::__1::__fs::filesystem::path::path[abi:v170000]<std::__1::basic_string_view<char, std::__1::char_traits<char>>, void>(std::__1::basic_string_view<char, std::__1::char_traits<char>> const&, std::__1::__fs::filesystem::path::format) /home/me/dev/llvm/build/bin/../include/c++/v1/__filesystem/path.h:483:5
#5 0x563a82676438 in __cxx_global_var_init /home/me/dev/tmp/repro-asan.cpp:6:29
#6 0x563a82676474 in _GLOBAL__sub_I_repro_asan.cpp /home/me/dev/tmp/repro-asan.cpp
#7 0x7fca55896eba in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29eba) (BuildId: 69389d485a9793dbe873f0ea2c93e02efaa9aa3d)
#8 0x563a826764a4 in _start (/home/me/dev/tmp/repro-asan+0x1f4a4)
0x563a8315eb41 is located 1 bytes inside of global variable 'path' defined in '/home/me/dev/tmp/repro-asan.cpp:6' (0x563a8315eb40) of size 24
0x563a82764600 is located 32 bytes before global variable '.str.2' defined in '/home/me/dev/llvm/build/bin/../include/c++/v1/string:1984' (0x563a82764620) of size 13
'.str.2' is ascii string 'basic_string'
0x563a82764600 is located 0 bytes inside of global variable '.str.1' defined in '/home/me/dev/tmp/repro-asan.cpp:4' (0x563a82764600) of size 5
'.str.1' is ascii string '/usr'
SUMMARY: AddressSanitizer: invalid-pointer-pair /home/me/dev/llvm/build/bin/../include/c++/v1/string:1979:23 in bool std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::__addr_in_range[abi:v170000]<char const&>(char const&) const
==3183863==ABORTING
Looking at the source code of the library, there's indeed a comparison between two pointers: https://github.com/llvm/llvm-project/blob/7f12efa88e17548d98f3e7425687f4afe0df34ed/libcxx/include/string#L1979
My understanding is that these pointers belong to different objects, meaning their comparison is undefined in C++. Thus, I'm wondering whether it's me doing something wrong (using the standard library wrong), or is it a problem with the library itself?
System: Ubuntu 22.04 LTS

The problematic function is testing whether an element lies in a range with the following (from https://github.com/llvm/llvm-project/blob/main/libcxx/include/string#L1979):
return data() <= __p && __p <= data() + size();
where __p is the pointer under consideration and [data(), data()+size()) the range.
My understanding is that these pointers belong to different objects, meaning their comparison is undefined in C++.
It is not undefined, just unspecified. The comparison doesn't have to be consistent, but the operation must result in true or false and the rest of the execution must be consistent with either.
While the standard only says that the comparison is unspecified, on common architectures, such as libc++ is targeting, it is guaranteed that memory has a linear layout and that the comparison of addresses forms a total order, so that the check is actually fine (except in constant evaluation, where it is forbidden, which is why the linked function has a specific branch for that). In principle compilers could also arbitrarily choose the result of these operations e.g. for optimization purposes, but no compiler I am aware of does that. And libc++ may rely on such guarantees, even if they are not made explicitly.
What ASAN's invalid-pointer-pair is checking here is not a check for undefined behavior. It only checks for unspecified behavior which is very likely unintentional, and at least non-portable. In this case it seems to me that the use is however intentional, with the assumption that < forms a strict total order over pointer addresses on the given compiler/architecture.
Libc++ could have used std::less instead of the relational operators directly. std::less is specified to actually guarantee a total order of all pointer addresses. However, the standard library also has to make the same assumptions to implement std::less anyway and on a usual platform it will simply be implemented with the < operator itself, so I don't think this is a big improvement and I don't know whether ASAN would recognize that the diagnostic must be suppressed in this case either.
In the end, I guess there is simply a suppression missing here somewhere.

Related

How do I get an error from `pop_back()` if `size()` is 0?

I was explaining to a coworker why we have small test with sanitizers on them. He asked about popping a vector too many times and if it was an exception, assert, UB and which sanitizer catches it
It appears NONE catches them. Address and memory will if you call back() after popping too many times but if you pop and do size() you get a large invalid value due to wrapping
Is there a way I can get an assert or exception or runtime termination when I pop too many times? I really thought a debug build without sanitizers would have caught that (with an assert or exception)
I use clang sanitizer but build options with gcc will also be helpful
Both libstdc++ and libc++ have a "debug mode" with assertions, that can be enabled using:
-D_GLIBCXX_DEBUG for libstdc++
-D_LIBCPP_DEBUG for libc++
Also -fsanitize=undefined appears to catch it, but the error message is much more cryptic.
The sanitizers may need some annotations to do a good job on library types. g++ -fsanitize=address -D_GLIBCXX_SANITIZE_VECTOR (documentation), clang++ -stdlib=libc++ -fsanitize=address or clang++ -stdlib=libstdc++ -fsanitize=address -D_GLIBCXX_SANITIZE_VECTOR -D__SANITIZE_ADDRESS__ (the need for this last macro is a bug) all detect the issue and print a message
=================================================================
==12312==ERROR: AddressSanitizer: bad parameters to __sanitizer_annotate_contiguous_container:
beg : 0x602000000010
end : 0x602000000014
old_mid : 0x602000000010
new_mid : 0x60200000000c
#0 0x7f6a9db3b707 in __sanitizer_annotate_contiguous_container ../../../../src/libsanitizer/asan/asan_poisoning.cpp:362
#1 0x55b9dcd1fc41 in std::_Vector_base<int, std::allocator<int> >::_Vector_impl::_Asan<std::allocator<int> >::_S_adjust(std::_Vector_base<int, std::allocator<int> >::_Vector_impl&, int*, int*) (/tmp/a.out+0x1c41)
#2 0x55b9dcd1fb66 in std::_Vector_base<int, std::allocator<int> >::_Vector_impl::_Asan<std::allocator<int> >::_S_shrink(std::_Vector_base<int, std::allocator<int> >::_Vector_impl&, unsigned long) (/tmp/a.out+0x1b66)
#3 0x55b9dcd1f6c4 in std::vector<int, std::allocator<int> >::pop_back() (/tmp/a.out+0x16c4)
#4 0x55b9dcd1f36d in main (/tmp/a.out+0x136d)
#5 0x7f6a9d57fe49 in __libc_start_main ../csu/libc-start.c:314
#6 0x55b9dcd1f199 in _start (/tmp/a.out+0x1199)
SUMMARY: AddressSanitizer: bad-__sanitizer_annotate_contiguous_container ../../../../src/libsanitizer/asan/asan_poisoning.cpp:362 in __sanitizer_annotate_contiguous_container
==12312==ABORTING
Note that there is no need for something as complicated as the sanitizers for this case, as mentioned in the other answer, libraries often have debug modes. For instance with libstdc++, defining _GLIBCXX_DEBUG enables the full debug mode (ABI-incompatible with normal mode)
/usr/include/c++/11/debug/vector:523:
In function:
void std::__debug::vector<_Tp, _Allocator>::pop_back() [with _Tp = int;
_Allocator = std::allocator<int>]
Error: attempt to access an element in an empty container.
Objects involved in the operation:
sequence "this" # 0x0x7ffe20bf5f80 {
type = std::__debug::vector<int, std::allocator<int> >;
}
while defining _GLIBCXX_ASSERTIONS enables much lighter assertions and preserves the ABI, but gives less information about the error
/usr/include/c++/11/bits/stl_vector.h:1227: void std::vector<_Tp, _Alloc>::pop_back() [with _Tp = int; _Alloc = std::allocator<int>]: Assertion '!this->empty()' failed.
and defining _LIBCPP_DEBUG with libc++ prints
/usr/lib/llvm-11/bin/../include/c++/v1/vector:1703: _LIBCPP_ASSERT '!empty()' failed. vector::pop_back called for empty vector

What are symbols with (.cold.n) in object files built for iOS?

I am working on a multiplatform library that is compiled as static library for iOS. After a seemingly unrelated change the binary for iOS became significantly larger. When I looked at contents of the library to see what had changed, I saw a large number symbols with demangled names like this:
unsigned short boost::basic_format<char, std::__1::char_traits<char>, std::__1::allocator<char> >::make_or_reuse_data(unsigned long) (.cold.1)
I understand that the duplication is because the Boost formatter is included in many object files, but what is interesting is the (.cold.1) part. This particular function does not have any other versions, but there are some that come both without the part and .cold. versions with numbers up to 8.
What does the .cold. and number mean, and does the increased library size transfer to final app binary?
I have the same question, then I google it.
I guess that __attribute__((cold)) is the cause of .cold.n appending to the method name.
Reference
[llvm-dev] [RFC] Add "hot" function attribute to LLVM IR and use
hot/cold attribute in function section prefix
6.30 Declaring Attributes of Functions
Line64 in attributes.h

"free(): invalid pointer" when calling "libucis.so" on release build (O3), where my code was never reached

I'm working on a large project and made a relative small contribution: a new class in C++ called by a C code. After adding my contribution to the project, I found that the debugging build works flawlessly, while the release build only fail on ~30 testcases (from a total of 20k+), where some functions of "libucis.so" was called. "valgrind" on the debug and release build of the same code base surprisingly shows a difference: no invalid access was found in the debug build, while it is the case for the release build. Some typical valgrind errors are like below:
==220335== Invalid free() / delete / delete[] / realloc()
==220335== at 0x508D17D: operator delete(void*) (vg_replace_malloc.c:576)
==220335== by 0x5588587: _M_dispose (basic_string.h:2765)
==220335== by 0x5588587: std::string::_M_mutate(unsigned long, unsigned long, unsigned long) (basic_string.tcc:927)
==220335== by 0x5588A4D: std::string::_M_replace_safe(unsigned long, unsigned long, char const*, unsigned long) (basic_string.tcc:1124)
*****some functions of libucsi.so not shown*****
==220335== Invalid free() / delete / delete[] / realloc()
==220335== at 0x508D17D: operator delete(void*) (vg_replace_malloc.c:576)
==220335== by 0x5589004: _M_dispose (basic_string.h:2765)
==220335== by 0x5589004: std::string::reserve(unsigned long) (basic_string.tcc:951)
*****some functions of libucsi.so not shown*****
What's more, I confirmed that my code (class methods in C wrapper) was never called in all failed testcases that reports the similar error above. And all the valgrind invaid access errors happens on the basic_string class in STL.
Regarding my class: a SQLite database writer. Includes vector and string headers.
I have done an extensive search, either online or offline... Some hint would be greatly appreciated.
Problem finally resolved. Here is a summary of the investigation and solution.
After going back and forth for nothing, I decided to test my class with nothing: keep only function members with empty definitions and necessary returns, and comment all "redundant" things. Successfully passed all the remaining ~30 testcases. Then I gradually uncomment my code, eventually I found that the error occur whenever I use std::string, even with a single declaration like "std::string s;". Based on a further search, I believe the problem is due to that "libucis.so" is compiled with a different version stdc++. And I knew that libucis.so is not compiled by us but is supplied by a third party. Then I went back to my makefile, and discarded all unnecessary library/header search paths. The result is that the character count to compile my code is reduced from ~5000 to only 281. Incrementally rebuild the whole project, problem resolved. Phew...
Update: The root cause may be somehow different from the discussion above. Eventually I found that compile my C++11 code with -fPIC solved the problem. But the Q is that the other C codes included in the target are not compiled with -fPIC... So why my C++11 code is the only guy needs to be compiled with -fPIC option, not the remaining (20+ source code files) C codes?

How to debug confusingly big code?

I am not a c++ programmer but try to debug some complex code. Not the best preconditions, I know...
So I have an openfoam solver which uses (includes) lots of code and I am struggling to really find the error. I compile with
SOURCE=mySolver.C ; g++ -m64 -Dlinux64 -DWM_DP -Wall -Wextra -Wno-unused-parameter -Wold-style-cast -O3 -DNoRepository -ftemplate-depth-100 -I/opt/software/openfoam/OpenFOAM-2.0.5/src/dynamicMesh/lnInclude {more linking} -I. -fPIC -c $SOURCE -o Make/linux64Gcc46DPOpt/mySolver.o
and after running the solver with the appropriate options, it crashes at the end after (or while) my return statement:
BEFORE return 0
*** glibc detected *** /opt/software/openfoam/myLibs/applications/bin/linux64Gcc46DPOpt/mySolver: double free or corruption (!prev): 0x000000000d3b7c30 ***
======= Backtrace: =========
/lib64/libc.so.6[0x31c307230f]
/lib64/libc.so.6(cfree+0x4b)[0x31c307276b]
/opt/software/openfoam/ThirdParty-2.0.5/platforms/linux64/gcc-4.5.3/lib64/libstdc++.so.6(_ZNSsD1Ev+0x39)[0x2b34781ffff9]
/opt/software/openfoam/myLibs/applications/bin/linux64Gcc46DPOpt/mySolver(_ZN4Foam6stringD1Ev+0x18)[0x441e2e]
/opt/software/openfoam/myLibs/applications/bin/linux64Gcc46DPOpt/mySolver(_ZN4Foam4wordD2Ev+0x18)[0x442216]
/lib64/libc.so.6(__cxa_finalize+0x8e)[0x31c303368e]
/opt/software/openfoam/myLibs/lib/linux64Gcc46DPOpt/libTMP.so[0x2b347a17f866]
======= Memory map: ========
...
My solver looks like (sorry, I can't post all parts):
#include "stuff1.H"
#include "stuff2.H"
int main(int argc, char *argv[])
{
#include "stuff3.H"
#include "stuffn.H"
while (runTime.run())
{
...
}
Info<< "BEFORE return 0\n" << endl;
return(0);
}
Running the solver with gdb with setting set environment MALLOC_CHECK_ 2 yields to:
BEFORE return 0
Program received signal SIGABRT, Aborted.
0x00000031c3030265 in raise () from /lib64/libc.so.6
(gdb) bt
#0 0x00000031c3030265 in raise () from /lib64/libc.so.6
#1 0x00000031c3031d10 in abort () from /lib64/libc.so.6
#2 0x00000031c3075ebc in free_check () from /lib64/libc.so.6
#3 0x00000031c30727f1 in free () from /lib64/libc.so.6
#4 0x00002aaab0496ff9 in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() ()
from /opt/software/openfoam/ThirdParty-2.0.5/platforms/linux64/gcc-4.5.3/lib64/libstdc++.so.6
#5 0x0000000000441e2e in Foam::string::~string (this=0x2aaaac0bd3c8, __in_chrg=<value optimized out>) at /opt/software/openfoam/OpenFOAM-2.0.5/src/OpenFOAM/lnInclude/string.H:78
#6 0x0000000000442216 in Foam::word::~word (this=0x2aaaac0bd3c8, __in_chrg=<value optimized out>) at /opt/software/openfoam/OpenFOAM-2.0.5/src/OpenFOAM/lnInclude/word.H:63
#7 0x00000031c303368e in __cxa_finalize () from /lib64/libc.so.6
#8 0x00002aaab2416866 in __do_global_dtors_aux () from /opt/software/openfoam/myLibs/lib/linux64Gcc46DPOpt/libTMP.so
#9 0x0000000000000000 in ?? ()
(gdb)
How should I proceed to find the real source of my error?
Btw. I saw this and this which is similar but not solving my issue. Also valgrind isn't working correctly for me. I know it has to do with some wrong (de-)allocation but I don't know how to really find the problem.
/Edit
I wasn't able to locate my problem yet...
I think the backtrace which I posted above (position #8) shows the problem is in the code which compiles to libTMP.so. In the Make/options file I added the option -DFULLDEBUG -g -O0. I thought it's possible to track the bug then but I don't know how.
Any help is highly appreciated!
If you have dealt with all compiler warnings and valgrind errors but the problem remains, then Divide and conquer.
Cut out half of the code (use #if directives, remove files from Makefile, or delete lines and restore later using source control).
If the problem goes away then it's likely that it was caused by something you just removed. Or if the problem remains then it's certainly in the code that still remains.
Repeat procedure recursively until you hone in on the problem location.
This doesn't always work because undefined behaviour can manifest itself at a later time than the line which caused it.
However you can work towards producing a minimal program that still has the problem. Eventually you must either produce an actual minimal example that you cannot reduce further, or uncover the true cause.
If you havn't got anything concrete after using gdb and valgrind I think what you can try is disassemble your so libraray using objdump, as you can see in backtrace it has given you the address of the errors, I had tried this kind of approach a long back in my project while debugging a problem. After disassemble you match the address of error to the address of statement in your library, it might give you an idea about the error location.
The command for disassembling objdump -dR <library.so>
You can find more information about objdump here
valgrind
Ok, I risk being shot down for a one-word answer, but bear with me. Try valgrind. Build the most debug version you have that still has issues and simply issue:
valgrind path/to/program
Chances are, the first reported issue will be your problem source. You can even get valgrind to launch a gdb server and let you attach to debug the code leading to the first memory issue. See:
http://tromey.com/blog/?s=valgrind
Some other options that were not listed yet are:
You can try gdb execution flow recording capability:
$ gdb target_executable
(gdb) b main
(gdb) run
(gdb) target record-full
(gdb) set record full insn-number-max unlimited
Then when the program crashes, you will be able to execute flow backward with reverse-next and reverse-step commands. Note that program runs really slow in this mode.
Another possible approach is to try clang static analyzer or clang-check tools on your code. Sometimes analyzer can give a good hint where problem in code might be.
Also, you can link your code with jemalloc and use it's debugging capabilities. Options "opt.junk", "opt.quarantine", "opt.valgrind" and "opt.redzone" can be usefull. In general, it makes malloc allocate some additional memory that is used to monitor writes and reads after the end of buffers, reads of deallocated memory and so on. See man page. This options can be enabled with mallctl function.
One more way to find a bug is to build your code with gcc's or clang's sanitizers enabled. You can turn them on with -fsanitize="sanitizer", where "sanitizer" can be one of: address, thread, leak, undefined. Compiler will instrument application with some additional code that will do additional checks and will print the report. For example:
#include <vector>
#include <iostream>
int main() {
std::vector<int> vect;
vect.resize(5);
std::cout << vect[10] << std::endl; // access the element after the end of vector internal buffer
}
Compile it with sanitizer turned on and run:
$ clang++ -fsanitize=address test.cpp
$ ./a.out
Gives the output:
==29920==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60400000dff8 at pc 0x0000004bad10 bp 0x7fff16d63e10 sp 0x7fff16d63e08
READ of size 4 at 0x60400000dff8 thread T0
#0 0x4bad0f in main (/home/pablo/a.out+0x4bad0f)
#1 0x7f0b6ce43fdf in __libc_start_main (/lib64/libc.so.6+0x1ffdf)
#2 0x4baaac in _start (/home/pablo/a.out+0x4baaac)
0x60400000dff8 is located 0 bytes to the right of 40-byte region [0x60400000dfd0,0x60400000dff8)
allocated by thread T0 here:
#0 0x435b9b in operator new(unsigned long) (/home/pablo/a.out+0x435b9b)
#1 0x4c1f49 in __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*) (/home/pablo/a.out+0x4c1f49)
#2 0x4c1d05 in __gnu_cxx::__alloc_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) (/home/pablo/a.out+0x4c1d05)
#3 0x4bfd51 in std::_Vector_base<int, std::allocator<int> >::_M_allocate(unsigned long) (/home/pablo/a.out+0x4bfd51)
#4 0x4bdb2a in std::vector<int, std::allocator<int> >::_M_fill_insert(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, unsigned long, int const&) (/home/pablo/a.out+0x4bdb2a)
#5 0x4bbe49 in std::vector<int, std::allocator<int> >::insert(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, unsigned long, int const&) (/home/pablo/a.out+0x4bbe49)
#6 0x4bb358 in std::vector<int, std::allocator<int> >::resize(unsigned long, int) (/home/pablo/a.out+0x4bb358)
#7 0x4bacaa in main (/home/pablo/a.out+0x4bacaa)
#8 0x7f0b6ce43fdf in __libc_start_main (/lib64/libc.so.6+0x1ffdf)
I partially agree with Matt: divide et impera is the way. But I partially agree 'cause I partially disagree: modifiyng the code you are trying to debug can lead you to hunt on the wrong track, even more if you are trying to debug a huge and complex code not yours in a language that you don't master.
Instead, follow a divide et impera method coupled with a top to bottom strategy: start by adding a few breakpoints in code at a higher level, let's say in the main, then launch the program and see which breackpoints get hitten and which not before crashing. Now you have a general idea of where the bug is; remove all breakpoints and add new ones a little bit deeper, in the area you just found, and repeat until you hit the routine that cause the crash.
It can be tedious, I know, but it works and, moreover, while doing so it will give you a much much better understanding of how the entire system works. I've fixed bugs in unknown applications made of tens of thousands lines of code in this way, and it always works; maybe it can take an entire day, but it works.

"corrupted double-linked list" on boost::function free() [duplicate]

This question already has an answer here:
What is a glibc free/malloc/realloc invalid next size/invalid pointer error and how to fix it?
(1 answer)
Closed 8 years ago.
I am going to try to ask this question without supplying too much source code because all the relevant bits add up to a bunch. The key (I think?) objects involved are
using namespace o2scl;
typedef MSMTModel<TASensor,PosModel,target2d,ovector,ovector_const_subvector> TA_MSMTModel;
typedef MPC_funct_mfptr<MSMT_InitialState,TA_MSMTModel,MSMTFormation> MPC_TAFormation_mfptr;
typedef boost::function<int (size_t, const ovector_base&, double&, TA_MSMTModel&)> TA_mfunct;
TA_mfunct mf1 = boost::bind(&MPC_TAFormation_mfptr::mfn, f1, _1, _2, _3, _4);
the boost::function mf1 is used as a callback function for a minimisation routine (o2scl::ool_mmin_spg) but I don't think the problem I am having is specific to that. The code runs with the calls to the ool_mmin_spg.mmin() function which makes use of mf1 as a callback and seems to run without errors. Then I get this lovely message
* glibc detected * ./test: corrupted double-linked list: 0x0000000001e9fb20 ***
followed by a backtrace and memory map. The relevant line of the backtrace seems to be
#7 0x000000000041d32a in boost::detail::function::functor_manager, o2scl::ovector_const_subvector_tlate >, MSMTFormation>, unsigned long, o2scl::ovector_base_tlate const&, double&, dmect::MSMTModel, o2scl::ovector_const_subvector_tlate >&>, boost::_bi::list5, o2scl::ovector_const_subvector_tlate >, MSMTFormation> >, boost::arg<1>, boost::arg<2>, boost::arg<3>, boost::arg<4> > > >::manage (in_buffer=, out_buffer=warning: (Internal error: pc 0x41d270 in read in psymtab, but not in symtab.)
I deduce that there is a problem freeing up memory from the boost::function, but other than that I am lost. Are there any pointers on trying to debug glibc "corrupted double-linked list" errors? I've found a few references on google but all have seemed to address very specific problems. Please let me know if more detailed code snips are required and thanks for your time!
Run the program through valgrind. That'll give you a stack trace when the memory gets corrupted (as well as a stack trace corresponding to the history of that piece of memory eg. where it was created or, if it was deleted, where it was destroyed).
I'm the O2scl writer and didn't see this post until now. I looked over the spg minimizer a bit just to make sure, and I didn't see anything clearly wrong, but I admit I've never tried using boost::function and o2scl together in this way. I'll continue to look things over a bit, but let me know if you figure things out.