Merge branch 'fix-and-run-tests-with-asan'

Fix issues found by address/leak sanitizers in the unit tests and add
Travis CI build running tests built with ASAN to ensure they don't
reappear in the future.

See https://github.com/wxWidgets/wxWidgets/pull/2086
This commit is contained in:
Vadim Zeitlin
2020-10-19 21:16:27 +02:00
14 changed files with 151 additions and 27 deletions

View File

@@ -36,8 +36,12 @@ jobs:
name: wxGTK 3 CMake Ubuntu 18.04 name: wxGTK 3 CMake Ubuntu 18.04
- dist: focal - dist: focal
compiler: gcc compiler: gcc
env: wxCONFIGURE_FLAGS="--disable-compat30 --disable-optimise --disable-unicode" wxSKIP_SAMPLES=1 env: wxGTK_VERSION=2 wxCONFIGURE_FLAGS="--disable-compat30 --disable-unicode" wxSKIP_SAMPLES=1
name: wxGTK ANSI Ubuntu 20.04 name: wxGTK ANSI Ubuntu 20.04
- dist: focal
compiler: gcc
env: wxGTK_VERSION=3 wxCONFIGURE_FLAGS="--disable-compat30 --disable-sys-libs" wxSKIP_SAMPLES=1 wxUSE_ASAN=1
name: wxGTK Ubuntu 20.04 with ASAN
- os: osx - os: osx
osx_image: xcode7.3 osx_image: xcode7.3
compiler: clang compiler: clang
@@ -78,7 +82,7 @@ jobs:
name: wxQt Ubuntu 18.04 name: wxQt Ubuntu 18.04
- os: linux - os: linux
arch: arm64 arch: arm64
env: wxCONFIGURE_FLAGS="--disable-sys-libs" wxLXC=1 env: wxGTK_VERSION=3 wxCONFIGURE_FLAGS="--disable-sys-libs" wxLXC=1
name: wxGTK ARM64 name: wxGTK ARM64
- os: linux - os: linux
arch: ppc64le arch: ppc64le
@@ -94,7 +98,7 @@ jobs:
allow_failures: allow_failures:
- os: linux - os: linux
arch: arm64 arch: arm64
env: wxCONFIGURE_FLAGS="--disable-sys-libs" wxLXC=1 env: wxGTK_VERSION=3 wxCONFIGURE_FLAGS="--disable-sys-libs" wxLXC=1
name: wxGTK ARM64 name: wxGTK ARM64
- os: linux - os: linux
arch: ppc64le arch: ppc64le

View File

@@ -22,9 +22,12 @@ case $(uname -s) in
3) libtoolkit_dev=libgtk-3-dev 3) libtoolkit_dev=libgtk-3-dev
extra_deps='libwebkit2gtk-4.0-dev libwebkitgtk-3.0-dev' extra_deps='libwebkit2gtk-4.0-dev libwebkitgtk-3.0-dev'
;; ;;
*) libtoolkit_dev=libgtk2.0-dev 2) libtoolkit_dev=libgtk2.0-dev
extra_deps='libwebkitgtk-dev' extra_deps='libwebkitgtk-dev'
;; ;;
*) echo 'Please specify wxGTK_VERSION explicitly.' >&2
exit 1
;;
esac esac
extra_deps="$extra_deps \ extra_deps="$extra_deps \
@@ -42,6 +45,24 @@ case $(uname -s) in
done done
$SUDO apt-get install -y $libtoolkit_dev $pkg_install $SUDO apt-get install -y $libtoolkit_dev $pkg_install
if [ "$wxUSE_ASAN" = 1 ]; then
codename=$(lsb_release --codename --short)
# Enable the `-dbgsym` repositories.
echo "deb http://ddebs.ubuntu.com ${codename} main restricted universe multiverse
deb http://ddebs.ubuntu.com ${codename}-updates main restricted universe multiverse
deb http://ddebs.ubuntu.com ${codename}-proposed main restricted universe multiverse" | \
$SUDO tee --append /etc/apt/sources.list.d/ddebs.list
# Import the debug symbol archive signing key from the Ubuntu server.
# Note that this command works only on Ubuntu 18.04 LTS and newer.
$SUDO apt-get install -y ubuntu-dbgsym-keyring
$SUDO apt-get update
# Install the symbols to allow LSAN suppression list to work.
$SUDO apt-get install -y libfontconfig1-dbgsym
fi
fi fi
;; ;;

View File

@@ -8,6 +8,10 @@ wxPROC_COUNT=`getconf _NPROCESSORS_ONLN`
((wxPROC_COUNT++)) ((wxPROC_COUNT++))
wxBUILD_ARGS="-j$wxPROC_COUNT" wxBUILD_ARGS="-j$wxPROC_COUNT"
# Setting this variable suppresses "Error retrieving accessibility bus address"
# messages from WebKit tests that we're not interested in.
export NO_AT_BRIDGE=1
case $wxTOOLSET in case $wxTOOLSET in
cmake) cmake)
if [ -z $wxCMAKE_TESTS ]; then wxCMAKE_TESTS=CONSOLE_ONLY; fi if [ -z $wxCMAKE_TESTS ]; then wxCMAKE_TESTS=CONSOLE_ONLY; fi
@@ -17,6 +21,11 @@ case $wxTOOLSET in
fi fi
cmake --version cmake --version
if [ "$wxUSE_ASAN" = 1 ]; then
echo "ASAN currently isn't supported in CMake builds"
exit 1
fi
echo 'travis_fold:start:configure' echo 'travis_fold:start:configure'
echo 'Configuring...' echo 'Configuring...'
mkdir build_cmake mkdir build_cmake
@@ -57,7 +66,24 @@ case $wxTOOLSET in
*) *)
echo 'travis_fold:start:configure' echo 'travis_fold:start:configure'
echo 'Configuring...' echo 'Configuring...'
./configure --disable-optimise --disable-debug_info $wxCONFIGURE_FLAGS || rc=$?
wxCONFIGURE_OPTIONS="--disable-optimise $wxCONFIGURE_FLAGS"
if [ -n "$wxGTK_VERSION" ]; then
wxCONFIGURE_OPTIONS="--with-gtk=$wxGTK_VERSION $wxCONFIGURE_OPTIONS"
fi
if [ "$wxUSE_ASAN" = 1 ]; then
export LSAN_OPTIONS=suppressions=$(pwd)/misc/suppressions/lsan
wxASAN_CFLAGS="-fsanitize=address -fno-omit-frame-pointer"
wxASAN_CXXFLAGS=$wxASAN_CFLAGS
wxASAN_LDFLAGS="-fsanitize=address"
./configure $wxCONFIGURE_OPTIONS --enable-debug "CFLAGS=$wxASAN_CFLAGS" "CXXFLAGS=$wxASAN_CXXFLAGS" "LDFLAGS=$wxASAN_LDFLAGS" || rc=$?
else
./configure $wxCONFIGURE_OPTIONS --disable-debug_info || rc=$?
fi
if [ -n "$rc" ]; then if [ -n "$rc" ]; then
echo '*** Configuring failed, contents of config.log follows: ***' echo '*** Configuring failed, contents of config.log follows: ***'
echo '-----------------------------------------------------------' echo '-----------------------------------------------------------'

View File

@@ -29,7 +29,8 @@ public:
~wxWebKitJavascriptResult() ~wxWebKitJavascriptResult()
{ {
webkit_javascript_result_unref(m_jsresult); if ( m_jsresult != NULL )
webkit_javascript_result_unref(m_jsresult);
} }
operator WebKitJavascriptResult *() const { return m_jsresult; } operator WebKitJavascriptResult *() const { return m_jsresult; }

View File

@@ -321,6 +321,10 @@ public:
// Enable deleting the SizerItem without destroying the contained sizer. // Enable deleting the SizerItem without destroying the contained sizer.
void DetachSizer() { m_sizer = NULL; } void DetachSizer() { m_sizer = NULL; }
// Enable deleting the SizerItem without resetting the sizer in the
// contained window.
void DetachWindow() { m_window = NULL; m_kind = Item_None; }
virtual wxSize GetSize() const; virtual wxSize GetSize() const;
virtual wxSize CalcMin(); virtual wxSize CalcMin();
virtual void SetDimension( const wxPoint& pos, const wxSize& size ); virtual void SetDimension( const wxPoint& pos, const wxSize& size );

View File

@@ -84,6 +84,25 @@ public:
cannot be converted to a specific data type, wxAny will then cannot be converted to a specific data type, wxAny will then
hold and manage reference to wxVariantData* similar to how hold and manage reference to wxVariantData* similar to how
wxVariant does. wxVariant does.
Note that objects constructed from list-valued variants
require the list to be explicitly cleared using `WX_CLEAR_LIST`
to avoid leaking memory. This unfortunate behaviour will not
be changed to prevent breaking the existing code relying on it.
@code
wxVariant vList;
vList.NullList();
vList.Append(15);
vList.Append("abc");
// Create wxAny from the list variant.
wxAny any = wxAny(vList);
// Clear the list to avoid the memory leak.
wxAnyList anyList = any.As<wxAnyList>();
WX_CLEAR_LIST(wxAnyList, anyList);
@endcode
*/ */
wxAny(const wxVariant& variant); wxAny(const wxVariant& variant);

10
misc/suppressions/lsan Normal file
View File

@@ -0,0 +1,10 @@
# Leak sanitizer suppressions for wx, use it by setting
# LSAN_OPTIONS=suppressions=<path-to-this-file>
# Known leaks in libfontconfig.so.1: note that you must have its debug symbols
# installed for these suppressions to work.
leak:FcConfigValues
leak:FcLangSetCreate
leak:FcPatternObjectInsertElt
leak:FcValueListCreate
leak:FcValueSave

View File

@@ -32,6 +32,7 @@
#include "wx/vector.h" #include "wx/vector.h"
#include "wx/listimpl.cpp" #include "wx/listimpl.cpp"
#include "wx/private/window.h" #include "wx/private/window.h"
#include "wx/scopedptr.h"
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@@ -673,7 +674,35 @@ wxSizer::~wxSizer()
wxSizerItem* wxSizer::DoInsert( size_t index, wxSizerItem *item ) wxSizerItem* wxSizer::DoInsert( size_t index, wxSizerItem *item )
{ {
m_children.Insert( index, item ); // The helper class that solves two problems when
// wxWindowBase::SetContainingSizer() throws:
// 1. Avoid leaking memory using the scoped pointer to the sizer item.
// 2. Disassociate the window from the sizer item to not reset the
// containing sizer for the window in the items destructor.
class ContainingSizerGuard
{
public:
explicit ContainingSizerGuard( wxSizerItem *item )
: m_item(item)
{
}
~ContainingSizerGuard()
{
if ( m_item )
m_item->DetachWindow();
}
wxSizerItem* Release()
{
return m_item.release();
}
private:
wxScopedPtr<wxSizerItem> m_item;
};
ContainingSizerGuard guard( item );
if ( item->GetWindow() ) if ( item->GetWindow() )
item->GetWindow()->SetContainingSizer( this ); item->GetWindow()->SetContainingSizer( this );
@@ -681,7 +710,9 @@ wxSizerItem* wxSizer::DoInsert( size_t index, wxSizerItem *item )
if ( item->GetSizer() ) if ( item->GetSizer() )
item->GetSizer()->SetContainingWindow( m_containingWindow ); item->GetSizer()->SetContainingWindow( m_containingWindow );
return item; m_children.Insert( index, item );
return guard.Release();
} }
void wxSizer::SetContainingWindow(wxWindow *win) void wxSizer::SetContainingWindow(wxWindow *win)
@@ -1449,6 +1480,9 @@ wxGridSizer::wxGridSizer( int rows, int cols, const wxSize& gap )
wxSizerItem *wxGridSizer::DoInsert(size_t index, wxSizerItem *item) wxSizerItem *wxGridSizer::DoInsert(size_t index, wxSizerItem *item)
{ {
// Ensure that the item will be deleted in case of exception.
wxScopedPtr<wxSizerItem> scopedItem( item );
// if only the number of columns or the number of rows is specified for a // if only the number of columns or the number of rows is specified for a
// sizer, arbitrarily many items can be added to it but if both of them are // sizer, arbitrarily many items can be added to it but if both of them are
// fixed, then the sizer can't have more than that many items -- check for // fixed, then the sizer can't have more than that many items -- check for
@@ -1489,7 +1523,7 @@ wxSizerItem *wxGridSizer::DoInsert(size_t index, wxSizerItem *item)
); );
} }
return wxSizer::DoInsert(index, item); return wxSizer::DoInsert( index, scopedItem.release() );
} }
int wxGridSizer::CalcRowsCols(int& nrows, int& ncols) const int wxGridSizer::CalcRowsCols(int& nrows, int& ncols) const

View File

@@ -652,6 +652,8 @@ void wxAnyTestCase::wxVariantConversions()
CPPUNIT_ASSERT(variant.GetCount() == 2); CPPUNIT_ASSERT(variant.GetCount() == 2);
CPPUNIT_ASSERT(variant[0].GetLong() == 15); CPPUNIT_ASSERT(variant[0].GetLong() == 15);
CPPUNIT_ASSERT(variant[1].GetString() == "abc"); CPPUNIT_ASSERT(variant[1].GetString() == "abc");
// Avoid the memory leak.
WX_CLEAR_LIST(wxAnyList, anyList);
any = wxAny(vCustomType); any = wxAny(vCustomType);
CPPUNIT_ASSERT(wxANY_CHECK_TYPE(any, wxVariantData*)); CPPUNIT_ASSERT(wxANY_CHECK_TYPE(any, wxVariantData*));

View File

@@ -22,6 +22,7 @@
#if wxUSE_FILESYSTEM #if wxUSE_FILESYSTEM
#include "wx/fs_mem.h" #include "wx/fs_mem.h"
#include "wx/scopedptr.h"
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// helpers // helpers
@@ -186,16 +187,16 @@ TEST_CASE("wxFileSystem::MemoryFSHandler", "[filesys][memoryfshandler][find]")
AutoMemoryFSHandler() AutoMemoryFSHandler()
: m_handler(new wxMemoryFSHandler()) : m_handler(new wxMemoryFSHandler())
{ {
wxFileSystem::AddHandler(m_handler); wxFileSystem::AddHandler(m_handler.get());
} }
~AutoMemoryFSHandler() ~AutoMemoryFSHandler()
{ {
wxFileSystem::RemoveHandler(m_handler); wxFileSystem::RemoveHandler(m_handler.get());
} }
private: private:
wxMemoryFSHandler* const m_handler; wxScopedPtr<wxMemoryFSHandler> const m_handler;
} autoMemoryFSHandler; } autoMemoryFSHandler;
wxMemoryFSHandler::AddFile("foo.txt", "foo contents"); wxMemoryFSHandler::AddFile("foo.txt", "foo contents");

View File

@@ -20,15 +20,13 @@
#endif // WX_PRECOMP #endif // WX_PRECOMP
#include "wx/html/winpars.h" #include "wx/html/winpars.h"
#include "wx/scopedptr.h"
// Test that parsing invalid HTML simply fails but doesn't crash for example. // Test that parsing invalid HTML simply fails but doesn't crash for example.
TEST_CASE("wxHtmlParser::ParseInvalid", "[html][parser][error]") TEST_CASE("wxHtmlParser::ParseInvalid", "[html][parser][error]")
{ {
class NullParser : public wxHtmlWinParser class NullParser : public wxHtmlWinParser
{ {
public:
virtual wxObject *GetProduct() wxOVERRIDE { return NULL; }
protected: protected:
virtual void AddText(const wxString& WXUNUSED(txt)) wxOVERRIDE { } virtual void AddText(const wxString& WXUNUSED(txt)) wxOVERRIDE { }
}; };
@@ -37,17 +35,17 @@ TEST_CASE("wxHtmlParser::ParseInvalid", "[html][parser][error]")
wxMemoryDC dc; wxMemoryDC dc;
p.SetDC(&dc); p.SetDC(&dc);
p.Parse("<"); delete p.Parse("<");
p.Parse("<foo"); delete p.Parse("<foo");
p.Parse("<!--"); delete p.Parse("<!--");
p.Parse("<!---"); delete p.Parse("<!---");
} }
TEST_CASE("wxHtmlCell::Detach", "[html][cell]") TEST_CASE("wxHtmlCell::Detach", "[html][cell]")
{ {
wxMemoryDC dc; wxMemoryDC dc;
wxHtmlContainerCell* const top = new wxHtmlContainerCell(NULL); wxScopedPtr<wxHtmlContainerCell> const top(new wxHtmlContainerCell(NULL));
wxHtmlContainerCell* const cont = new wxHtmlContainerCell(NULL); wxHtmlContainerCell* const cont = new wxHtmlContainerCell(NULL);
wxHtmlCell* const cell1 = new wxHtmlWordCell("Hello", dc); wxHtmlCell* const cell1 = new wxHtmlWordCell("Hello", dc);
wxHtmlCell* const cell2 = new wxHtmlColourCell(*wxRED); wxHtmlCell* const cell2 = new wxHtmlColourCell(*wxRED);

View File

@@ -1147,7 +1147,7 @@ void MBConvTestCase::TestDecoder(
// make sure the correct output length was calculated // make sure the correct output length was calculated
WX_ASSERT_EQUAL_MESSAGE WX_ASSERT_EQUAL_MESSAGE
( (
("while converting \"%s\"", multiBuffer), ("while converting \"%s\"", inputCopy),
wideChars, wideChars,
outputWritten outputWritten
); );

View File

@@ -17,6 +17,7 @@
#endif // WX_PRECOMP #endif // WX_PRECOMP
#include "wx/accel.h" #include "wx/accel.h"
#include "wx/scopedptr.h"
namespace namespace
{ {
@@ -35,11 +36,11 @@ void CheckAccelEntry(const wxAcceleratorEntry& accel, int keycode, int flags)
*/ */
TEST_CASE( "wxAcceleratorEntry::Create", "[accelentry]" ) TEST_CASE( "wxAcceleratorEntry::Create", "[accelentry]" )
{ {
wxAcceleratorEntry* pa; wxScopedPtr<wxAcceleratorEntry> pa;
SECTION( "Correct behavior" ) SECTION( "Correct behavior" )
{ {
pa = wxAcceleratorEntry::Create("Foo\tCtrl+Z"); pa.reset( wxAcceleratorEntry::Create("Foo\tCtrl+Z") );
CHECK( pa ); CHECK( pa );
CHECK( pa->IsOk() ); CHECK( pa->IsOk() );
@@ -48,21 +49,21 @@ TEST_CASE( "wxAcceleratorEntry::Create", "[accelentry]" )
SECTION( "Tab missing" ) SECTION( "Tab missing" )
{ {
pa = wxAcceleratorEntry::Create("Shift-Q"); pa.reset( wxAcceleratorEntry::Create("Shift-Q") );
CHECK( !pa ); CHECK( !pa );
} }
SECTION( "No accelerator key specified" ) SECTION( "No accelerator key specified" )
{ {
pa = wxAcceleratorEntry::Create("bloordyblop"); pa.reset( wxAcceleratorEntry::Create("bloordyblop") );
CHECK( !pa ); CHECK( !pa );
} }
SECTION( "Display name parsing" ) SECTION( "Display name parsing" )
{ {
pa = wxAcceleratorEntry::Create("Test\tBackSpace"); pa.reset( wxAcceleratorEntry::Create("Test\tBackSpace") );
CHECK( pa ); CHECK( pa );
CHECK( pa->IsOk() ); CHECK( pa->IsOk() );

View File

@@ -20,6 +20,7 @@
#endif // WX_PRECOMP #endif // WX_PRECOMP
#include "wx/menu.h" #include "wx/menu.h"
#include "wx/scopedptr.h"
#include "wx/translation.h" #include "wx/translation.h"
#include "wx/uiaction.h" #include "wx/uiaction.h"
@@ -624,7 +625,9 @@ namespace
void VerifyAccelAssigned( wxString labelText, int keycode ) void VerifyAccelAssigned( wxString labelText, int keycode )
{ {
wxAcceleratorEntry* entry = wxAcceleratorEntry::Create( labelText ); const wxScopedPtr<wxAcceleratorEntry> entry(
wxAcceleratorEntry::Create( labelText )
);
CHECK( entry ); CHECK( entry );
CHECK( entry->GetKeyCode() == keycode ); CHECK( entry->GetKeyCode() == keycode );