This is the mail archive of the gcc-help@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Global variable in static library - double free or corruption error


Alexey -

Sorry to go to the top of the thread but this does not apply to any one comment.

Try running the attached program on the SOs in /usr/lib (or any
directory with a shared object). All it does is a load and unload. It
appears there are many victims of C++ shared objects and not knowing
all the rules of RTLD_GLOBAL, ABI and ODR. I have not been able to
make it through the list of libraries yet due to a crash from one
shared object or another.

Some of the libraries cannot even get through their constructors -
/usr/lib/codeblocks/plugins/libAutoVersioning.so comes to mid.

I'm actually quite surprised that a GTK module  -
libwx_gtk2u_core-2.8.so.0 - made an appearance. It seems the module
crashed in its destructor. Need I say more?

And I have not even begun to play dirty: if I fork/exec so that two
processes have a shared object open, I can guarantee a crash of the
second processunder the right circumstances (RTLD_GLOBAL and global
C++ objects) when the first process exits. What if the 'second
process' were a SELinux binary?

And one final note (keep in mind we *MUST* RTLD_GLOBAL to catch C++
exceptions across module boundaries (see the GCC FAQ)):

    ... usually there is no reason to use RTLD GLOBAL. For reasons
    explained later it is always highly advised ..." [1]

Jeff

[1] U. Dreeper, How To Write Shared Libraries,
http://people.redhat.com/drepper/dsohowto.pdf, p.10.

============================

jeffrey@bruno:~/dlopen-dlclose$ uname -a
Linux bruno 2.6.32-25-generic #45-Ubuntu SMP Sat Oct 16 19:52:42 UTC
2010 x86_64 GNU/Linux

============================

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff1f3ee80 in wxWindowBase::PushEventHandler(wxEvtHandler*) ()
   from /usr/lib/libwx_gtk2u_core-2.8.so.0
(gdb) where
#0  0x00007ffff1f3ee80 in wxWindowBase::PushEventHandler(wxEvtHandler*) ()
   from /usr/lib/libwx_gtk2u_core-2.8.so.0
#1  0x00007ffff33eecc7 in PluginManager (this=0x6c1a30) at pluginmanager.cpp:170
#2  0x00007ffff33e1e34 in Mgr<PluginManager>::Get (this=<value
optimized out>) at ./manager.h:183
#3  Manager::GetPluginManager (this=<value optimized out>) at manager.cpp:326
#4  0x00007ffff68190dd in PluginRegistrant (__priority=<value optimized out>,
    __initialize_p=<value optimized out>) at
../../../../src/include/cbplugin.h:593
#5  __static_initialization_and_destruction_0 (__priority=<value
optimized out>,
    __initialize_p=<value optimized out>) at AutoVersioning.cpp:58
#6  0x00007ffff6836c16 in __do_global_ctors_aux ()
   from /usr/lib/codeblocks/plugins/libAutoVersioning.so
#7  0x00007ffff6817f53 in _init () from
/usr/lib/codeblocks/plugins/libAutoVersioning.so
#8  0x00007fff00000000 in ?? ()
#9  0x00007ffff7dead65 in ?? () from /lib64/ld-linux-x86-64.so.2
#10 0x00007ffff7def841 in ?? () from /lib64/ld-linux-x86-64.so.2
#11 0x00007ffff7dea9c6 in ?? () from /lib64/ld-linux-x86-64.so.2
#12 0x00007ffff7deeffa in ?? () from /lib64/ld-linux-x86-64.so.2
#13 0x00007ffff7bd8f66 in ?? () from /lib/libdl.so.2
#14 0x00007ffff7dea9c6 in ?? () from /lib64/ld-linux-x86-64.so.2
#15 0x00007ffff7bd92ac in ?? () from /lib/libdl.so.2
#16 0x00007ffff7bd8ee1 in dlopen () from /lib/libdl.so.2
#17 0x000000000040229f in DoLoadUnload (files=..., errata=...) at
load-unload.cpp:204
#18 0x0000000000401fba in main (argc=1, argv=0x7fffffffe368) at
load-unload.cpp:163
(gdb)

============================

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff0311e8c in wxBaseArrayInt::Add(int, unsigned long) () from
/usr/lib/libwx_baseu-2.8.so.0
(gdb) where
#0  0x00007ffff0311e8c in wxBaseArrayInt::Add(int, unsigned long) ()
   from /usr/lib/libwx_baseu-2.8.so.0
#1  0x00007ffff282f593 in ~wxsRegisterItem (this=0x7ffff2b5a160,
__in_chrg=<value optimized out>)
    at ./../wxsitemfactory.h:108
#2  0x00007ffff6ec3630 in __cxa_finalize () from /lib/libc.so.6
#3  0x00007ffff2737c06 in __do_global_dtors_aux () from
/usr/lib/libwxsmithlib.so.0.0.1
#4  0x0000000000000009 in ?? ()
#5  0x00007fffffffdfb0 in ?? ()
#6  0x00007ffff28ae121 in _fini () from /usr/lib/libwxsmithlib.so.0.0.1
#7  0x00000000006898b0 in ?? ()
#8  0x00007ffff7df0972 in ?? () from /lib64/ld-linux-x86-64.so.2
#9  0x000000000064b7f0 in ?? ()
#10 0x00000000006720b0 in ?? ()
#11 0x00000000006648a0 in ?? ()
#12 0x00000000006787d0 in ?? ()
#13 0x0000000000658a80 in ?? ()
#14 0x00000000006643a0 in ?? ()
#15 0x0000000000641910 in ?? ()
#16 0x000000000066bb90 in ?? ()
#17 0x000000000066c3a0 in ?? ()
#18 0x0000000000663ef0 in ?? ()
#19 0x0000000000672560 in ?? ()
#20 0x000000000066dbf0 in ?? ()
#21 0x000000000066e0a0 in ?? ()
#22 0x000000000066b590 in ?? ()
#23 0x00000000006549e0 in ?? ()
#24 0x0000000000645c70 in ?? ()
#25 0x0000000000679140 in ?? ()
#26 0x0000000000646110 in ?? ()
#27 0x0000000000654e90 in ?? ()
#28 0x0000000000683f60 in ?? ()
#29 0x00000000006500b0 in ?? ()
#30 0x000000000064fc00 in ?? ()
#31 0x0000000000681a40 in ?? ()
#32 0x0000000000681ef0 in ?? ()
#33 0x000000000066cc90 in ?? ()
#34 0x000000000066d140 in ?? ()
#35 0x000000000065a820 in ?? ()
#36 0x0000000000678080 in ?? ()
#37 0x000000000065acc0 in ?? ()
#38 0x0000000000677be0 in ?? ()
#39 0x0000000000647930 in ?? ()
#40 0x000000000065cf40 in ?? ()
#41 0x000000000065d3e0 in ?? ()
#42 0x0000000000647dd0 in ?? ()
#43 0x00000000006795f0 in ?? ()
#44 0x0000000000650ae0 in ?? ()
#45 0x0000000000650f80 in ?? ()
#46 0x0000000000684400 in ?? ()
#47 0x0000000000683280 in ?? ()
#48 0x0000000000683720 in ?? ()
#49 0x0000000000644b90 in ?? ()
#50 0x0000000000645040 in ?? ()
#51 0x000000000067e3a0 in ?? ()
#52 0x000000000067e850 in ?? ()
#53 0x000000000065db90 in ?? ()
#54 0xe0eda5b1f212b194 in ?? ()
#55 0xfffffffffffffffe in ?? ()
#56 0x0101010101010101 in ?? ()
#57 0x0101010101010101 in ?? ()
#58 0x0101010101010101 in ?? ()
#59 0x0101010101010101 in ?? ()
#60 0x0000010101010101 in ?? ()
#61 0x0000000000000000 in ?? ()
(gdb)


On Thu, Oct 21, 2010 at 7:53 AM, Alexey Skidanov
<Alexey.Skidanov@orbotech.com> wrote:
> Hello,
> We use the gcc 4.1.1 in our project. After some code refactoring
> (creating new shared library) the application crashes at exit with
> following error:
>
> *** glibc detected *** : double free or corruption (fasttop) ***
>
> In order to simulate our application, I created the test one, with two
> shared libraries and one static library. The code is below:
>
> [SNIP]
//	load-unload.exe - load and unload shared objects to see which developers
//      and packagers did not pay attention to *ALL* the details of a C++ shared
//      object, RTLD_GLOBAL (so exceptions can be caught), the ABI ,and the ODR.
//
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//	This notice must not be removed from any files, and original
//	attribution must remain with the files.
//
//	Copyright (C) 2010, Jeffrey Walton (noloader@gmail.com)


// Need pthreads so gdb can follow the call to dlopen(2)
// g++ -g -ggdb -O0 -Wall -Wextra load-unload.cpp -o load-unload.exe -ldl -lpthread

#include <iostream>
using std::cout;
using std::cerr;
using std::endl;

#include <fstream>
using std::ifstream;

#include <string>
using std::string;

#include <vector>
using std::vector;

#include <stdexcept>
using std::runtime_error;

#include <cstdlib>
using std::exit;

#include <cctype>
using std::isdigit;

#include <cstring>
using std::strerror;

#include <algorithm>
using std::sort;
using std::unique;

// Include after the standard c/c++ stuff
#include <errno.h>
#include <dirent.h>
#include <dlfcn.h>
#include <link.h>
#include <elf.h>

#include <sys/types.h>
#include <sys/stat.h>

typedef vector<string> FileList;
typedef vector<string> DirList;
typedef vector<string> ErrList;

typedef size_t MaxDepth;
MaxDepth Unlimited = static_cast<MaxDepth>(-1);
MaxDepth RootOnly = static_cast<MaxDepth>(1);

// See APPLICATION BINARY INTERFACE, Chapter 4.
#if !defined(ELFMAG0)
# define ELFMAG0 0x7f
#endif

#if !defined(ELFMAG1)
# define ELFMAG1 'E'
#endif

#if !defined(ELFMAG2)
# define ELFMAG2 'L'
#endif

#if !defined(ELFMAG3)
# define ELFMAG3 'F'
#endif

class ProgramError : public runtime_error
{
public:
	ProgramError(const char* msg, int err = -1)
	: runtime_error(msg), m_err(err)
	{
	}

	ProgramError(const string& msg, int err = -1)
	: runtime_error(msg.c_str()), m_err(err)
	{
	}

	int error() const
	{
		return m_err;
	}

private:
	int m_err;
};

class AutoDir
{
public:
	AutoDir(DIR*& dir) : m_dir(dir)
	{
	}

	virtual ~AutoDir()
	{
		if(m_dir)
		{
			closedir(m_dir);
			m_dir = NULL;
		}			
	}

private:
	DIR*& m_dir;
};

class AutoLib
{
public:
	AutoLib(void*& lib) : m_lib(lib)
	{
	}

	virtual ~AutoLib()
	{
		if(m_lib)
		{
			dlclose(m_lib);
			m_lib = NULL;
		}			
	}

private:
	void*& m_lib;
};

void DoDirectoryWalk(const string& entry, FileList& files, ErrList errata, MaxDepth = Unlimited);
void DoLoadUnload(const FileList& files, ErrList errata);

bool IsSharedObject(const string& file);
bool IsElfFormat(const string& file);

int main(int argc, char* argv[])
{
	ErrList errata; // non fatal, such as access denied
	string libdir = "/usr/lib"; // assume /usr/lib

	if(argc >= 2)
		libdir = argv[1];

	cout << "Testing shared objects in " << libdir << endl;
	cout << "Press CTLT-C to early exit" << endl;
	cout << endl;

	size_t processed = 0;

	try
	{
		FileList files;

		MaxDepth depth = RootOnly;
		if(depth == Unlimited)
		{
			cout << "Recursion depth = unlimited" << endl;
		}
		else if(depth == RootOnly)
		{
			cout << "Recursion depth = root directory only" << endl;
		}
		else
		{
			cout << "Recursion depth = " << depth << endl;
		}

		// Errata is non-fatal errors, such as EACCES.
		// Recursion depth is RootOnly, Unlimited, or any number in between.
		DoDirectoryWalk(libdir, files, errata, depth /*recursion*/);

		char* ldpath = getenv("LD_PATH");
		if(ldpath)
		{
			// Since LD_PATH was set, recurse all directories
			// to test all SOs on the alternate path
			DoDirectoryWalk(ldpath, files, errata, Unlimited /*recursion depth*/);
		}

		// Sort for sane viewing
		processed = files.size();
		sort(files.begin(), files.end());
	
		// Remove any duplicates (should there really be any?)
		FileList::iterator last = unique(files.begin(), files.end());
		files.erase(last, files.end());

		if(processed)
		{
			cout << "Attempting to process " << processed << " files" << endl;
			DoLoadUnload(files, errata);
		}					
	}
	
	catch(const ProgramError& err)
	{
		cerr << err.what() << endl;
		exit(err.error());
	}

	if(errata.size())
	{
		cout << endl;
		cout << "Errata:" << endl;
		
		for(size_t i = 0; i < errata.size(); i++)
		{
			cout << errata[i] << endl;
		}
	}

	if(processed)
	{
		cout << endl;
		cout << "Processed " << processed << " files" << endl;
	}

	return 0;
}

void DoLoadUnload(const FileList& files, ErrList errata)
{
	for(size_t i = 0; i < files.size(); i++)
	{
		cout << "Loading/unloading " << files[i] << endl;

		void* lib = NULL;
		AutoLib cleanup(lib);

		lib = dlopen(files[i].c_str(), RTLD_GLOBAL);

		if(!lib)
			lib = dlopen(files[i].c_str(), RTLD_GLOBAL | RTLD_LAZY);

		if(!lib)
		{
			errata.push_back(dlerror());
		}
	}
}

// Adapted from W. Richard Stevens, Advanced Programming in the Unix Environment
void DoDirectoryWalk(const string& entry, FileList& files, ErrList errata, MaxDepth maxDepth)
{
	if(!entry.length())
	{
		throw ProgramError("Entry is not valid", EINVAL);
	}

	DirList dirs;

	struct stat    sbuf;
	struct dirent* dbuf;

	int ret = 0;

	// http://linux.die.net/man/2/lstat
	ret = lstat(entry.c_str(), &sbuf);
	if(ret < 0)
	{
		string err;

		if(EACCES == errno)
		{
			err = string("lstat failed for ") + entry + " (EACCES)";
			errata.push_back(err);
			return;
		}

		err = string("lstat failed for ") + entry + ": ";
		throw ProgramError(err + strerror(ret), errno);
	}

	if(S_ISREG(sbuf.st_mode))
	{
		// if(IsSharedObject(entry))
		//	files.push_back(entry);

		if(IsElfFormat(entry))
			files.push_back(entry);

		return;
	}

	if(!S_ISDIR(sbuf.st_mode))
		return;

	string path = entry;
	if(path[path.size()-1] != '/')
		path += '/';

	DIR* dp = NULL;
	AutoDir cleanup(dp);
	
	dp = opendir(path.c_str());
	if(!dp)
		throw ProgramError(strerror(ret), errno);

	if(maxDepth--)
	{
		// Only add directories if we are still recursing
		while(NULL != (dbuf = readdir(dp)))
		{
			if(0 == strcmp(".", dbuf->d_name))
				continue;

			if(0 == strcmp("..", dbuf->d_name))
				continue;

			dirs.push_back(path + dbuf->d_name);
		}

		for(size_t i = 0; i < dirs.size(); i++)
		{
			DoDirectoryWalk(dirs[i], files, errata, maxDepth);
		}
	}
}

// Cheap, but keeps us from opening each file looking for an elf signature and other attributes.
bool IsSharedObject(const string& file)
{
	static const string ext = "so";

	string::size_type pos = file.rfind(ext);
	if(string::npos == pos)
		return false;

	// This catches all files that end with '.so'
	if(file.substr(pos) == ext)
		return true;

	// This catches all files that end with '.so.W',, '.so.W.X', ..., '.so.W.X.Y.Z'. It will
	// probably miss some interesting SOs like OpenSSL since a letter is used in versioning.
	pos += ext.length();
	while(pos < file.length())
	{
		const char c = file[pos];

		if(c != '.' && !isdigit(c))
			return false;

		pos++;
	}

	return true;
}

bool IsElfFormat(const string& file)
{
	try
	{
		// We are only interested in the first four bytes of Elf32_Ehdr.
		// ELFMAG0 can take on a few values. See APPLICATION BINARY INTERFACE, Chapter 4.
		static const char elf1[] = { 0x7F, ELFMAG1, ELFMAG2, ELFMAG3 };
		static const char elf2[] = { 0xB1, ELFMAG1, ELFMAG2, ELFMAG3 };

		std::ifstream stream;
		stream.open(file.c_str());

		char ident[4] = {0,0,0,0};
		stream.read(ident, sizeof(ident));

		if(0 == memcmp(elf1, ident, 4) || 0 == memcmp(elf2, ident, 4))
			return true;
		
	}

	catch(std::exception& e)
	{
	}

	return false;
}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]