Home:ALL Converter>Usage of leak sanitizer within dynamically loaded plugin

Usage of leak sanitizer within dynamically loaded plugin

Ask Time:2022-09-19T19:16:23         Author:Ivan Folbort

Json Formatter

Is there a way to properly run qmltestrunner against a test, that uses a custom qml module, that is compiled with address sanitizer flags (e.g. --fsanitize=address), and to be able to detect all leaks in this qmlmodule? The problem is that qmltestrunner is not linked against the address sanitizer, thus it complains regarding the linkage order when importing custom qmlmodule.

> qmltestrunner -import ../../../../build/qmlmodules/ -platform offscreen
==393140==ASan runtime does not come first in the initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.

When I am trying to run a qmltestrunner explicitly preloading sanitizer it detects qt internal leaks, but doesn't detect leaks in my qmlmodule:

> LD_PRELOAD=/lib/x86_64-linux-gnu/libasan.so.6 qmltestrunner -import ../../../../build/MyLib/qmlmodules/ -platform offscreen
is 0x12345********* Start testing of qmltestrunner *********
Config: Using QtTest library 5.15.2, Qt 5.15.2 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 10.2.1 20210110), debian 11
PASS   : qmltestrunner::TestIoauExample::initTestCase()
PASS   : qmltestrunner::TestIoauExample::test_ioau_example()
PASS   : qmltestrunner::TestIoauExample::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted, 2ms
********* Finished testing of qmltestrunner *********

=================================================================
==447337==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 500 byte(s) in 1 object(s) allocated from:
    #0 0x7f3f0f3417a7 in operator new[](unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:102
    #1 0x7f3f06f6950a in ioau::IoauTester::HeightmapGenerator::HeightmapGenerator(QObject*) (/home/ifolbort/localstorage/workspace/ioau-testbed2/ioau/build/monolith/build/ioau/qmlmodules/IoauTester/libioautester-qmlplugin.so+0x1550a)
    #2 0x7f3f06f62223 in void QQmlPrivate::createInto<ioau::IoauTester::HeightmapGenerator>(void*) (/home/ifolbort/localstorage/workspace/ioau-testbed2/ioau/build/monolith/build/ioau/qmlmodules/IoauTester/libioautester-qmlplugin.so+0xe223)
    #3 0x7f3f0d4e2c89 in QQmlType::create(QObject**, void**, unsigned long) const (/lib/x86_64-linux-gnu/libQt5Qml.so.5+0x291c89)
    #4 0x7f3f0d4c127c in QQmlComponent::QQmlComponent(QQmlEngine*, QObject*) (/lib/x86_64-linux-gnu/libQt5Qml.so.5+0x27027c)

Indirect leak of 168 byte(s) in 1 object(s) allocated from:
    #0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
    #1 0x7f3f0dd51b53 in QScreen::QScreen(QPlatformScreen*) (/lib/x86_64-linux-gnu/libQt5Gui.so.5+0x165b53)

Indirect leak of 88 byte(s) in 1 object(s) allocated from:
    #0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
    #1 0x7f3f0efb8d04 in QObject::QObject(QObject*) (/lib/x86_64-linux-gnu/libQt5Core.so.5+0x2e2d04)

Indirect leak of 40 byte(s) in 1 object(s) allocated from:
    #0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
    #1 0x7f3f076610a8  (/usr/lib/x86_64-linux-gnu/qt5/plugins/platforms/libqoffscreen.so+0x120a8)

Indirect leak of 24 byte(s) in 1 object(s) allocated from:
    #0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
    #1 0x7f3f07662167  (/usr/lib/x86_64-linux-gnu/qt5/plugins/platforms/libqoffscreen.so+0x13167)

Indirect leak of 16 byte(s) in 1 object(s) allocated from:
    #0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
    #1 0x7f3f0ede4bb0 in QtSharedPointer::ExternalRefCountData::getAndRef(QObject const*) (/lib/x86_64-linux-gnu/libQt5Core.so.5+0x10ebb0)

Indirect leak of 16 byte(s) in 1 object(s) allocated from:
    #0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
    #1 0x7f3f0dd01422 in QWindowSystemInterface::handleScreenAdded(QPlatformScreen*, bool) (/lib/x86_64-linux-gnu/libQt5Gui.so.5+0x115422)
    #2 0x7773c9c2beee67ff  (<unknown module>)

Indirect leak of 16 byte(s) in 1 object(s) allocated from:
    #0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
    #1 0x7f3f0dd0cadb in QPlatformScreen::QPlatformScreen() (/lib/x86_64-linux-gnu/libQt5Gui.so.5+0x120adb)

SUMMARY: AddressSanitizer: 868 byte(s) leaked in 8 allocation(s).

When I disable Asan linkage order verification test qmltestrunner passes tests, but my leak is not detected:

> ASAN_OPTIONS=verify_asan_link_order=false  qmltestrunner -import ../../../../build/MyLib/qmlmodules/ -platform offscreen
is 0x12345********* Start testing of qmltestrunner *********
Config: Using QtTest library 5.15.2, Qt 5.15.2 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 10.2.1 20210110), debian 11
PASS   : qmltestrunner::TestIoauExample::initTestCase()
PASS   : qmltestrunner::TestIoauExample::test_ioau_example()
PASS   : qmltestrunner::TestIoauExample::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted, 2ms
********* Finished testing of qmltestrunner *********

qml source file with TestCase i am trying to run:

import QtQuick 2.0
import QtTest 1.2
import MyLibTester 0.1

Item {
    TestCase {
    
        name: "TestMyLibExample"
        when: heightmapGenerator.ready
        function test_mylib_example() {
            verify(heightmapGenerator.ready)
        }
    }
    resources: HeightmapGenerator {
        id: heightmapGenerator
        property bool loaded: false
        property bool ready: false
        Component.onCompleted: {
            ready = true
            loaded = true
        }
    }
}

a leak that I am trying to introduce and which is not detected when running qmltestrunner

--- a/mylib/qmlmodules/MyLibTester/src/HeightmapGenerator.cpp
+++ b/mylib/qmlmodules/MyLibTester/src/HeightmapGenerator.cpp
@@ -9,6 +9,8 @@
+#include "../tests/leak_tst.h"

 namespace mylib
 {
 namespace MyLibTester
@@ -35,10 +37,13 @@ static float cubicSpline(float x)
 

 }
-
 HeightmapGenerator::HeightmapGenerator(QObject* parent)
        : QObject(parent)
 {
+       auto test_leak = leak_test(500);
+       std::cout << "is " 
+                 << (test_leak == reinterpret_cast<bool*>(0x12345) ? "NOT" : "") 
+                 << "0x12345";
 }

However, when I compile a simple app with the same leak introduced sanitizer is able to detect the leak:

//leak_tst.h
#ifndef leak_tst
#define leak_tst

bool* leak_test(int n)
{
    return new bool[n];
}

#endif
///leak_tst.cpp
#include "leak_tst.h"
#include <iostream>
int main()
{
    auto test_leak = leak_test(500);
    std::cout << "is " 
          << (test_leak == reinterpret_cast<bool*>(0x12345) ? "NOT" : "") 
          << "0x12345";
    return 0;
}
> leak_tst
is 0x12345
=================================================================
==419360==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 500 byte(s) in 1 object(s) allocated from:
    #0 0x7fb68db4b7a7 in operator new[](unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:102
    #1 0x55b8687e3132 in main (/home/ifolbort/localstorage/workspace/ioau-testbed2/ioau/build/monolith/build/ioau/qmlmodules/IoauTester/tests/test-leak+0x1132)
    #2 0x7fb68d734d09 in __libc_start_main ../csu/libc-start.c:308

SUMMARY: AddressSanitizer: 500 byte(s) leaked in 1 allocation(s).

Author:Ivan Folbort,eproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/73772527/usage-of-leak-sanitizer-within-dynamically-loaded-plugin
yy