From a580cc8cda50701e38c9db84ff9fd6fe46e390d0 Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Thu, 1 Aug 2024 09:55:05 -0400 Subject: [PATCH] ci: enable AddressSanitizer --- .github/workflows/spread.yml | 6 +- spread.yaml | 4 +- spread/build/ubuntu/task.yaml | 2 +- src/include/server/mir/scene/text_input_hub.h | 37 +++++- .../frontend_wayland/input_method_v1.cpp | 2 +- src/server/frontend_wayland/text_input_v1.cpp | 2 +- src/server/frontend_wayland/text_input_v2.cpp | 2 +- src/server/symbols.map | 23 ++-- .../mir_test_framework/stub_input_platform.h | 25 ++-- .../stub_input_platform_accessor.h | 40 +++++++ tests/mir_test_framework/CMakeLists.txt | 17 ++- .../fake_input_device_impl.cpp | 10 +- tests/mir_test_framework/stub_input.cpp | 4 +- .../stub_input_platform.cpp | 66 +++-------- .../stub_input_platform_accessor.cpp | 110 ++++++++++++++++++ tests/miral/runner.cpp | 5 +- tests/unit-tests/CMakeLists.txt | 14 ++- tests/unit-tests/test_udev_wrapper.cpp | 55 --------- .../test_udev_wrapper_asan_skip.cpp | 88 ++++++++++++++ 19 files changed, 368 insertions(+), 144 deletions(-) create mode 100644 tests/include/mir_test_framework/stub_input_platform_accessor.h create mode 100644 tests/mir_test_framework/stub_input_platform_accessor.cpp create mode 100644 tests/unit-tests/test_udev_wrapper_asan_skip.cpp diff --git a/.github/workflows/spread.yml b/.github/workflows/spread.yml index c453b986dcf..7cbbd717e78 100644 --- a/.github/workflows/spread.yml +++ b/.github/workflows/spread.yml @@ -30,9 +30,7 @@ jobs: set -euo pipefail if ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}; then - TASKS='"lxd:ubuntu-24.04:spread/build/ubuntu:asan" - "lxd:ubuntu-24.04:spread/build/ubuntu:tsan" - "lxd:ubuntu-24.04:spread/build/ubuntu:asan_clang" + TASKS='"lxd:ubuntu-24.04:spread/build/ubuntu:tsan" "lxd:ubuntu-24.04:spread/build/ubuntu:tsan_clang"' fi @@ -49,6 +47,8 @@ jobs: "lxd:fedora-rawhide:spread/build/fedora:amd64" "lxd:ubuntu-24.04:spread/build/sbuild:ubuntu_devel" "lxd:ubuntu-24.04:spread/build/sbuild:ubuntu_proposed" + "lxd:ubuntu-24.04:spread/build/ubuntu:asan" + "lxd:ubuntu-24.04:spread/build/ubuntu:asan_clang" "lxd:ubuntu-24.04:spread/build/ubuntu:ubsan" "lxd:ubuntu-24.04:spread/build/ubuntu:ubsan_clang"' diff --git a/spread.yaml b/spread.yaml index c489a9ef8e6..14540c62311 100644 --- a/spread.yaml +++ b/spread.yaml @@ -37,8 +37,8 @@ environment: GCC_VERSION: GCC_VERSION/ubuntu_devel: 14 DEB_BUILD_EXTRA: - DEB_BUILD_EXTRA/ubsan,ubsan_clang: nostrip - DEB_BUILD_EXTRA/asan,asan_clang,tsan,tsan_clang: nostrip nocheck + DEB_BUILD_EXTRA/asan,asan_clang,ubsan,ubsan_clang: nostrip optimize=-lto + DEB_BUILD_EXTRA/tsan,tsan_clang: nostrip nocheck DEBOOTSTRAP_OPTS: DEBOOTSTRAP_OPTS/debian_sid,ubuntu_devel: --no-merged-usr CTEST_OUTPUT_ON_FAILURE: 1 diff --git a/spread/build/ubuntu/task.yaml b/spread/build/ubuntu/task.yaml index bff7347e1be..88f18ea5fac 100644 --- a/spread/build/ubuntu/task.yaml +++ b/spread/build/ubuntu/task.yaml @@ -53,6 +53,6 @@ execute: | echo "OVERRIDE_CONFIGURE_OPTIONS += -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" >> debian/opts.mk # build and run tests - UBSAN_OPTIONS=halt_on_error=1 debian/rules build + UBSAN_OPTIONS=halt_on_error=1 ASAN_OPTIONS=detect_leaks=0,verify_asan_link_order=0,detect_odr_violation=0 debian/rules build ccache --show-stats --zero-stats > ${CCACHE_DIR}/ccache.stats diff --git a/src/include/server/mir/scene/text_input_hub.h b/src/include/server/mir/scene/text_input_hub.h index e8cf62224a6..f6a970b898b 100644 --- a/src/include/server/mir/scene/text_input_hub.h +++ b/src/include/server/mir/scene/text_input_hub.h @@ -23,6 +23,8 @@ #include #include #include +#include +#include struct wl_array; @@ -97,6 +99,39 @@ struct TextInputState std::optional content_purpose; }; +class CopyableWlArray +{ +public: + explicit CopyableWlArray(wl_array const* in_data) + { + wl_array_init(&data_); + wl_array_copy(&data_, const_cast(in_data)); + } + + CopyableWlArray(CopyableWlArray const& other) + : CopyableWlArray{&other.data_} + { + } + + CopyableWlArray& operator=(CopyableWlArray other) + { + std::swap(data_, other.data_); + return *this; + } + + ~CopyableWlArray() + { + wl_array_release(&data_); + } + + [[nodiscard]] wl_array* data() const + { + return const_cast(&data_); + } +private: + wl_array data_; +}; + /// See text-input-unstable-v3.xml for details struct TextInputChange { @@ -142,7 +177,7 @@ struct TextInputChange std::optional keysym; /// \remark Defined for text input v1 and v2, not v3. - std::optional modifier_map; + std::optional modifier_map; /// \remark Defined for text input v1 and v2, not v3. std::optional cursor_position; diff --git a/src/server/frontend_wayland/input_method_v1.cpp b/src/server/frontend_wayland/input_method_v1.cpp index b030d48f16d..579a282bcfe 100644 --- a/src/server/frontend_wayland/input_method_v1.cpp +++ b/src/server/frontend_wayland/input_method_v1.cpp @@ -327,7 +327,7 @@ class mf::InputMethodV1::Instance : wayland::InputMethodV1 void modifiers_map(struct wl_array *map) override { - change.pending_change.modifier_map = map; + change.pending_change.modifier_map = scene::CopyableWlArray(map); change.waiting_status = InputMethodV1ChangeWaitingStatus::none; } diff --git a/src/server/frontend_wayland/text_input_v1.cpp b/src/server/frontend_wayland/text_input_v1.cpp index 344b4f1877c..45b878fa8d0 100644 --- a/src/server/frontend_wayland/text_input_v1.cpp +++ b/src/server/frontend_wayland/text_input_v1.cpp @@ -289,7 +289,7 @@ void TextInputV1::send_text_change(ms::TextInputChange const& change) } if (change.modifier_map) { - send_modifiers_map_event(change.modifier_map.value()); + send_modifiers_map_event(change.modifier_map.value().data()); } if (change.direction) { diff --git a/src/server/frontend_wayland/text_input_v2.cpp b/src/server/frontend_wayland/text_input_v2.cpp index b169e8b2b23..d6f6f7bd447 100644 --- a/src/server/frontend_wayland/text_input_v2.cpp +++ b/src/server/frontend_wayland/text_input_v2.cpp @@ -303,7 +303,7 @@ void mf::TextInputV2::send_text_change(ms::TextInputChange const& change) } if (change.modifier_map) { - send_modifiers_map_event(change.modifier_map.value()); + send_modifiers_map_event(change.modifier_map.value().data()); } if (change.direction) { diff --git a/src/server/symbols.map b/src/server/symbols.map index 5b6e9aaf91a..fed4f30c50e 100644 --- a/src/server/symbols.map +++ b/src/server/symbols.map @@ -1344,12 +1344,12 @@ local: *; MIR_SERVER_INTERNAL_2.18 { global: extern "C++" { - mir::DefaultServerConfiguration::the_led_observer_registrar*; mir::DecorationStrategy::?DecorationStrategy*; mir::DecorationStrategy::DecorationStrategy*; mir::DecorationStrategy::operator*; mir::DefaultServerConfiguration::set_the_decoration_strategy*; mir::DefaultServerConfiguration::the_decoration_strategy*; + mir::DefaultServerConfiguration::the_led_observer_registrar*; mir::Server::set_the_decoration_strategy*; mir::Server::the_decoration_strategy*; mir::Server::the_idle_handler*; @@ -1367,8 +1367,17 @@ global: mir::input::receiver::XKBMapperRegistrar::set_keymap_for_device*; mir::input::receiver::XKBMapperRegistrar::unregister_interest*; mir::input::receiver::XKBMapperRegistrar::xkb_modifiers*; + mir::scene::CopyableWlArray::?CopyableWlArray*; + mir::scene::CopyableWlArray::CopyableWlArray*; + mir::scene::CopyableWlArray::data*; + mir::scene::CopyableWlArray::operator*; + mir::scene::TextInputChange::?TextInputChange*; + mir::scene::TextInputChange::operator*; mir::shell::IdleHandlerObserver::?IdleHandlerObserver*; mir::shell::IdleHandlerObserver::IdleHandlerObserver*; + non-virtual?thunk?to?mir::DecorationStrategy::?DecorationStrategy*; + non-virtual?thunk?to?mir::DefaultServerConfiguration::set_the_decoration_strategy*; + non-virtual?thunk?to?mir::DefaultServerConfiguration::the_decoration_strategy*; non-virtual?thunk?to?mir::DefaultServerConfiguration::the_led_observer_registrar*; non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::clear_all_keymaps*; non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::clear_keymap_for_device*; @@ -1382,19 +1391,17 @@ global: non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::unregister_interest*; non-virtual?thunk?to?mir::input::receiver::XKBMapperRegistrar::xkb_modifiers*; non-virtual?thunk?to?mir::shell::IdleHandlerObserver::?IdleHandlerObserver*; - typeinfo?for?mir::input::receiver::XKBMapperRegistrar; - typeinfo?for?mir::shell::IdleHandlerObserver; - vtable?for?mir::input::receiver::XKBMapperRegistrar; - vtable?for?mir::shell::IdleHandlerObserver; - non-virtual?thunk?to?mir::DecorationStrategy::?DecorationStrategy*; - non-virtual?thunk?to?mir::DefaultServerConfiguration::set_the_decoration_strategy*; - non-virtual?thunk?to?mir::DefaultServerConfiguration::the_decoration_strategy*; non-virtual?thunk?to?mir::shell::IdleHandlerObserver::?IdleHandlerObserver*; typeinfo?for?mir::DecorationStrategy; + typeinfo?for?mir::input::receiver::XKBMapperRegistrar; + typeinfo?for?mir::scene::CopyableWlArray; + typeinfo?for?mir::shell::IdleHandlerObserver; typeinfo?for?mir::shell::IdleHandlerObserver; virtual?thunk?to?mir::DefaultServerConfiguration::set_the_decoration_strategy*; virtual?thunk?to?mir::DefaultServerConfiguration::the_decoration_strategy*; vtable?for?mir::DecorationStrategy; + vtable?for?mir::input::receiver::XKBMapperRegistrar; + vtable?for?mir::shell::IdleHandlerObserver; vtable?for?mir::shell::IdleHandlerObserver; }; } MIR_SERVER_INTERNAL_2.17; diff --git a/tests/include/mir_test_framework/stub_input_platform.h b/tests/include/mir_test_framework/stub_input_platform.h index 82af68c6e0e..7abf07c91e8 100644 --- a/tests/include/mir_test_framework/stub_input_platform.h +++ b/tests/include/mir_test_framework/stub_input_platform.h @@ -36,11 +36,22 @@ class InputDevice; } namespace mir_test_framework { + +class DeviceStore +{ +public: + virtual ~DeviceStore() = default; + virtual void foreach_device(std::function const&)> const&) = 0; + virtual void clear() = 0; +}; + class FakeInputDevice; class StubInputPlatform : public mir::input::Platform { public: - explicit StubInputPlatform(std::shared_ptr const& input_device_registry); + explicit StubInputPlatform( + std::shared_ptr const& input_device_registry, + std::shared_ptr const& device_store); ~StubInputPlatform(); std::shared_ptr dispatchable() override; @@ -49,18 +60,16 @@ class StubInputPlatform : public mir::input::Platform void pause_for_config() override; void continue_after_config() override; - static void add(std::shared_ptr const& dev); - static void remove(std::shared_ptr const& dev); - static void register_dispatchable(std::shared_ptr const& queue); - static void unregister_dispatchable(std::shared_ptr const& queue); + void add(std::shared_ptr const& dev); + void remove(std::shared_ptr const& dev); + void register_dispatchable(std::shared_ptr const& queue); + void unregister_dispatchable(std::shared_ptr const& queue); private: std::shared_ptr const platform_dispatchable; std::shared_ptr const platform_queue; std::shared_ptr const registry; - static std::atomic stub_input_platform; - static std::vector> device_store; - static std::mutex device_store_guard; + std::shared_ptr const device_store; }; } diff --git a/tests/include/mir_test_framework/stub_input_platform_accessor.h b/tests/include/mir_test_framework/stub_input_platform_accessor.h new file mode 100644 index 00000000000..fb55033081d --- /dev/null +++ b/tests/include/mir_test_framework/stub_input_platform_accessor.h @@ -0,0 +1,40 @@ +/* + * Copyright © Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 or 3, + * as published by the Free Software Foundation. + * + * 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 . + */ + +#ifndef MIR_TEST_FRAMEWORK_STUB_INPUT_PLATFORM_ACCESSOR_H +#define MIR_TEST_FRAMEWORK_STUB_INPUT_PLATFORM_ACCESSOR_H + +#include "stub_input_platform.h" + +namespace mir_test_framework +{ +class StubInputPlatformAccessor +{ +public: + static mir::UniqueModulePtr get(std::shared_ptr const& input_device_registry); + static void add(std::shared_ptr const& dev); + static void remove(std::shared_ptr const& dev); + static void register_dispatchable(std::shared_ptr const& queue); + static void unregister_dispatchable(std::shared_ptr const& queue); + static void clear(); + +private: + static std::atomic stub_input_platform; + +}; +} + +#endif //MIR_TEST_FRAMEWORK_STUB_INPUT_PLATFORM_ACCESSOR_H diff --git a/tests/mir_test_framework/CMakeLists.txt b/tests/mir_test_framework/CMakeLists.txt index 64c58f6bd12..155619f2bfb 100644 --- a/tests/mir_test_framework/CMakeLists.txt +++ b/tests/mir_test_framework/CMakeLists.txt @@ -21,6 +21,17 @@ add_compile_definitions( string (REPLACE " -flto " " " CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) string (REPLACE " -flto " " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) +add_library(mir-stub-input-platform OBJECT + stub_input_platform.cpp +) + +target_link_libraries(mir-stub-input-platform + PUBLIC + mirplatform + mircommon + mircore +) + add_library(mir-public-test-framework OBJECT async_server_runner.cpp command_line_server_configuration.cpp @@ -93,6 +104,7 @@ target_link_libraries(mir-umock-test-framework add_library(mir-test-framework-static STATIC $ + $ $ $ ) @@ -115,8 +127,8 @@ add_library( mir-test-input-framework OBJECT stub_input.cpp + stub_input_platform_accessor.cpp fake_input_device_impl.cpp - stub_input_platform.cpp ) target_link_libraries(mir-test-input-framework @@ -129,7 +141,8 @@ target_link_libraries(mir-test-input-framework add_library( mirplatforminputstub MODULE $ - $) + $ + $) target_link_libraries(mirplatforminputstub mircommon) set_target_properties( diff --git a/tests/mir_test_framework/fake_input_device_impl.cpp b/tests/mir_test_framework/fake_input_device_impl.cpp index 3c087c81615..e9ae1b9cb60 100644 --- a/tests/mir_test_framework/fake_input_device_impl.cpp +++ b/tests/mir_test_framework/fake_input_device_impl.cpp @@ -15,7 +15,7 @@ */ #include "fake_input_device_impl.h" -#include "mir_test_framework/stub_input_platform.h" +#include "mir_test_framework/stub_input_platform_accessor.h" #include "mir/input/input_device.h" #include "mir/input/input_device_info.h" @@ -45,12 +45,12 @@ namespace synthesis = mir::input::synthesis; mtf::FakeInputDeviceImpl::FakeInputDeviceImpl(mi::InputDeviceInfo const& info) : queue{std::make_shared()}, device{std::make_shared(info, queue)} { - mtf::StubInputPlatform::add(device); + mtf::StubInputPlatformAccessor::add(device); } void mtf::FakeInputDeviceImpl::emit_device_removal() { - mtf::StubInputPlatform::remove(device); + mtf::StubInputPlatformAccessor::remove(device); } void mtf::FakeInputDeviceImpl::emit_runtime_error() @@ -328,14 +328,14 @@ void mtf::FakeInputDeviceImpl::InputDevice::start(mi::InputSink* destination, mi { sink = destination; builder = event_builder; - mtf::StubInputPlatform::register_dispatchable(queue); + mtf::StubInputPlatformAccessor::register_dispatchable(queue); } void mtf::FakeInputDeviceImpl::InputDevice::stop() { sink = nullptr; builder = nullptr; - mtf::StubInputPlatform::unregister_dispatchable(queue); + mtf::StubInputPlatformAccessor::unregister_dispatchable(queue); } mi::OutputInfo mtf::FakeInputDeviceImpl::InputDevice::get_output_info() const diff --git a/tests/mir_test_framework/stub_input.cpp b/tests/mir_test_framework/stub_input.cpp index a2ee65a80ee..2b6ae54ff2e 100644 --- a/tests/mir_test_framework/stub_input.cpp +++ b/tests/mir_test_framework/stub_input.cpp @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -#include "mir_test_framework/stub_input_platform.h" +#include "mir_test_framework/stub_input_platform_accessor.h" #include "fake_input_device_impl.h" #include "mir/module_properties.h" #include "mir/assert_module_entry_point.h" @@ -33,7 +33,7 @@ mir::UniqueModulePtr create_input_platform( std::shared_ptr const& /*report*/) { mir::assert_entry_point_signature(&create_input_platform); - return mir::make_module_ptr(input_device_registry); + return mtf::StubInputPlatformAccessor::get(input_device_registry); } void add_input_platform_options( diff --git a/tests/mir_test_framework/stub_input_platform.cpp b/tests/mir_test_framework/stub_input_platform.cpp index eb6245c7889..868fbe1448f 100644 --- a/tests/mir_test_framework/stub_input_platform.cpp +++ b/tests/mir_test_framework/stub_input_platform.cpp @@ -27,31 +27,29 @@ namespace mtf = mir_test_framework; namespace mi = mir::input; mtf::StubInputPlatform::StubInputPlatform( - std::shared_ptr const& input_device_registry) + std::shared_ptr const& input_device_registry, + std::shared_ptr const& device_store) : platform_dispatchable{std::make_shared()}, platform_queue{std::make_shared()}, - registry(input_device_registry) + registry(input_device_registry), + device_store(device_store) { - stub_input_platform = this; platform_dispatchable->add_watch(platform_queue); } mtf::StubInputPlatform::~StubInputPlatform() { - std::lock_guard lk{device_store_guard}; - device_store.clear(); - stub_input_platform = nullptr; + device_store->clear(); } void mtf::StubInputPlatform::start() { - std::lock_guard lk{device_store_guard}; - for (auto const& dev : device_store) + device_store->foreach_device([&](auto const& dev) { auto device = dev.lock(); if (device) registry->add_device(device); - } + }); } std::shared_ptr mtf::StubInputPlatform::dispatchable() @@ -61,13 +59,12 @@ std::shared_ptr mtf::StubInputPlatform::dispatchabl void mtf::StubInputPlatform::stop() { - std::lock_guard lk{device_store_guard}; - for (auto const& dev : device_store) + device_store->foreach_device([&](auto const& dev) { auto device = dev.lock(); if (device) registry->remove_device(device); - } + }); } void mtf::StubInputPlatform::pause_for_config() @@ -80,16 +77,8 @@ void mtf::StubInputPlatform::continue_after_config() void mtf::StubInputPlatform::add(std::shared_ptr const& dev) { - auto input_platform = stub_input_platform.load(); - if (!input_platform) - { - std::lock_guard lk{device_store_guard}; - device_store.push_back(dev); - return; - } - - input_platform->platform_queue->enqueue( - [registry=input_platform->registry,dev] + platform_queue->enqueue( + [registry=registry,dev] { registry->add_device(dev); }); @@ -97,21 +86,8 @@ void mtf::StubInputPlatform::add(std::shared_ptr const& void mtf::StubInputPlatform::remove(std::shared_ptr const& dev) { - auto input_platform = stub_input_platform.load(); - if (!input_platform) - BOOST_THROW_EXCEPTION(std::runtime_error("No stub input platform available")); - - std::lock_guard lk{device_store_guard}; - device_store.erase( - std::remove_if(begin(device_store), - end(device_store), - [dev](auto weak_dev) - { - return (weak_dev.lock() == dev); - }), - end(device_store)); - input_platform->platform_queue->enqueue( - [ registry = input_platform->registry, dev ] + platform_queue->enqueue( + [ registry = registry, dev ] { registry->remove_device(dev); }); @@ -119,22 +95,10 @@ void mtf::StubInputPlatform::remove(std::shared_ptr con void mtf::StubInputPlatform::register_dispatchable(std::shared_ptr const& queue) { - auto input_platform = stub_input_platform.load(); - if (!input_platform) - BOOST_THROW_EXCEPTION(std::runtime_error("No stub input platform available")); - - input_platform->platform_dispatchable->add_watch(queue); + platform_dispatchable->add_watch(queue); } void mtf::StubInputPlatform::unregister_dispatchable(std::shared_ptr const& queue) { - auto input_platform = stub_input_platform.load(); - if (!input_platform) - BOOST_THROW_EXCEPTION(std::runtime_error("No stub input platform available")); - - input_platform->platform_dispatchable->remove_watch(queue); + platform_dispatchable->remove_watch(queue); } - -std::atomic mtf::StubInputPlatform::stub_input_platform{nullptr}; -std::vector> mtf::StubInputPlatform::device_store; -std::mutex mtf::StubInputPlatform::device_store_guard; diff --git a/tests/mir_test_framework/stub_input_platform_accessor.cpp b/tests/mir_test_framework/stub_input_platform_accessor.cpp new file mode 100644 index 00000000000..c49f5a30187 --- /dev/null +++ b/tests/mir_test_framework/stub_input_platform_accessor.cpp @@ -0,0 +1,110 @@ +/* + * Copyright © Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 or 3, + * as published by the Free Software Foundation. + * + * 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 . + */ + +#include "mir_test_framework/stub_input_platform_accessor.h" + +namespace mir_test_framework +{ +class StaticDeviceStore : public mir_test_framework::DeviceStore +{ +public: + void foreach_device(std::function const&)> const& f) override + { + std::lock_guard lk{device_store_guard}; + for (auto const& dev : device_store) + f(dev); + } + + void clear() override + { + std::lock_guard lk{device_store_guard}; + device_store.clear(); + StubInputPlatformAccessor::clear(); + } + + static std::mutex device_store_guard; + static std::vector> device_store; +}; + +std::vector> StaticDeviceStore::device_store; +std::mutex StaticDeviceStore::device_store_guard; +} + +mir::UniqueModulePtr mir_test_framework::StubInputPlatformAccessor::get( + std::shared_ptr const& input_device_registry) +{ + auto ptr = mir::make_module_ptr( + input_device_registry, std::make_shared()); + stub_input_platform = ptr.get(); + return ptr; +} + + +void mir_test_framework::StubInputPlatformAccessor::add(std::shared_ptr const& dev) +{ + auto input_platform = stub_input_platform.load(); + if (!input_platform) + { + std::lock_guard lk{StaticDeviceStore::device_store_guard}; + StaticDeviceStore::device_store.push_back(dev); + return; + } + + input_platform->add(dev); +} + +void mir_test_framework::StubInputPlatformAccessor::remove(std::shared_ptr const& dev) +{ + auto input_platform = stub_input_platform.load(); + if (!input_platform) + BOOST_THROW_EXCEPTION(std::runtime_error("No stub input platform available")); + + std::lock_guard lk{StaticDeviceStore::device_store_guard}; + StaticDeviceStore::device_store.erase( + std::remove_if(begin(StaticDeviceStore::device_store), + end(StaticDeviceStore::device_store), + [dev](auto weak_dev) + { + return (weak_dev.lock() == dev); + }), + end(StaticDeviceStore::device_store)); + input_platform->remove(dev); +} + +void mir_test_framework::StubInputPlatformAccessor::register_dispatchable(std::shared_ptr const& queue) +{ + auto input_platform = stub_input_platform.load(); + if (!input_platform) + BOOST_THROW_EXCEPTION(std::runtime_error("No stub input platform available")); + + input_platform->register_dispatchable(queue); +} + +void mir_test_framework::StubInputPlatformAccessor::unregister_dispatchable(std::shared_ptr const& queue) +{ + auto input_platform = stub_input_platform.load(); + if (!input_platform) + BOOST_THROW_EXCEPTION(std::runtime_error("No stub input platform available")); + + input_platform->unregister_dispatchable(queue); +} + +void mir_test_framework::StubInputPlatformAccessor::clear() +{ + stub_input_platform = nullptr; +} + +std::atomic mir_test_framework::StubInputPlatformAccessor::stub_input_platform{nullptr}; \ No newline at end of file diff --git a/tests/miral/runner.cpp b/tests/miral/runner.cpp index 1814f511b0d..aa8a18a31d8 100644 --- a/tests/miral/runner.cpp +++ b/tests/miral/runner.cpp @@ -84,7 +84,10 @@ TEST_F(Runner, x11_socket_is_not_returned_by_default) TEST_F(Runner, register_signal_handler_before_setup_invokes_callback_after_setup) { auto signal = std::make_shared(); - register_signal_handler({signum}, [signal](int){ signal->raise(); }); + + // Storing this in a variable fixes an address sanitizer issue + std::initializer_list sigs = {signum}; + register_signal_handler(sigs, [signal](int){ signal->raise(); }); miral::TestServer::SetUp(); diff --git a/tests/unit-tests/CMakeLists.txt b/tests/unit-tests/CMakeLists.txt index 4509b601476..f297e1fb503 100644 --- a/tests/unit-tests/CMakeLists.txt +++ b/tests/unit-tests/CMakeLists.txt @@ -42,7 +42,11 @@ set_target_properties( # Umockdev uses glib, which uses the deprecated "register" allocation specifier set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dregister=") -set(UMOCK_UNIT_TEST_SOURCES test_udev_wrapper.cpp) +if(CMAKE_BUILD_TYPE STREQUAL "AddressSanitizer") + set(UMOCK_UNIT_TEST_SOURCES test_udev_wrapper.cpp) +else() + set(UMOCK_UNIT_TEST_SOURCES test_udev_wrapper.cpp test_udev_wrapper_asan_skip.cpp) +endif() set( UNIT_TEST_SOURCES @@ -133,8 +137,15 @@ set_property( SOURCE input/test_logind_console_services.cpp input/test_input_platform_probing.cpp SOURCE input/evdev/test_evdev_input_platform.cpp SOURCE graphics/test_platform_prober.cpp + SOURCE test_udev_wrapper_asan_skip.cpp PROPERTY COMPILE_OPTIONS -Wno-variadic-macros) +if(NOT CMAKE_BUILD_TYPE STREQUAL "AddressSanitizer") + set_property( + SOURCE test_udev_wrapper_asan_skip.cpp + PROPERTY COMPILE_OPTIONS -Wno-variadic-macros) +endif() + if ("test_touchspot_controller.cpp:array-bounds" IN_LIST MIR_COMPILER_QUIRKS) set_property( SOURCE input/test_touchspot_controller.cpp @@ -183,7 +194,6 @@ target_link_libraries( mir_add_wrapped_executable(mir_umock_unit_tests NOINSTALL ${UMOCK_UNIT_TEST_SOURCES} $ - $ ${MIR_SERVER_OBJECTS} ${MIR_PLATFORM_OBJECTS} ) diff --git a/tests/unit-tests/test_udev_wrapper.cpp b/tests/unit-tests/test_udev_wrapper.cpp index 8da062c49c5..4c1e8b5645d 100644 --- a/tests/unit-tests/test_udev_wrapper.cpp +++ b/tests/unit-tests/test_udev_wrapper.cpp @@ -30,22 +30,11 @@ namespace mtf=mir_test_framework; namespace { - -bool KilledByInvalidMemoryAccess(int exit_status) -{ - return testing::KilledBySignal(SIGSEGV)(exit_status) || - testing::KilledBySignal(SIGBUS)(exit_status) || - testing::KilledBySignal(SIGABRT)(exit_status) || - // It seems that valgrind kills us with SIGKILL - testing::KilledBySignal(SIGKILL)(exit_status); -} - class UdevWrapperTest : public ::testing::Test { public: mtf::UdevEnvironment udev_environment; }; - } TEST_F(UdevWrapperTest, IteratesOverCorrectNumberOfDevices) @@ -314,29 +303,6 @@ TEST_F(UdevWrapperTest, EnumeratorAddMatchSysnameIncludesCorrectDevices) } } -typedef UdevWrapperTest UdevWrapperDeathTest; - -TEST_F(UdevWrapperDeathTest, DereferencingEndReturnsInvalidObject) -{ - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - udev_environment.add_device("drm", "control64D", NULL, {}, {}); - udev_environment.add_device("drm", "card1", NULL, {}, {}); - - mir::udev::Enumerator devices(std::make_shared()); - - devices.scan_devices(); - - MIR_EXPECT_EXIT((*devices.end()).subsystem(), KilledByInvalidMemoryAccess, ""); - - auto iter = devices.begin(); - - while(iter != devices.end()) - { - iter++; - } - MIR_EXPECT_EXIT((*iter).subsystem(), KilledByInvalidMemoryAccess, ""); -} - TEST_F(UdevWrapperTest, MemberDereferenceWorks) { udev_environment.add_device("drm", "control64D", NULL, {}, {}); @@ -351,27 +317,6 @@ TEST_F(UdevWrapperTest, MemberDereferenceWorks) EXPECT_STREQ("drm", iter->subsystem()); } -TEST_F(UdevWrapperDeathTest, MemberDereferenceOfEndDies) -{ - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - udev_environment.add_device("drm", "control64D", NULL, {}, {}); - udev_environment.add_device("drm", "card1", NULL, {}, {}); - - mir::udev::Enumerator devices(std::make_shared()); - - devices.scan_devices(); - - MIR_EXPECT_EXIT(devices.end()->subsystem(), KilledByInvalidMemoryAccess, ""); - - auto iter = devices.begin(); - - while(iter != devices.end()) - { - iter++; - } - MIR_EXPECT_EXIT(iter->subsystem(), KilledByInvalidMemoryAccess, ""); -} - TEST_F(UdevWrapperTest, UdevMonitorDoesNotTriggerBeforeEnabling) { mir::udev::Monitor monitor{mir::udev::Context()}; diff --git a/tests/unit-tests/test_udev_wrapper_asan_skip.cpp b/tests/unit-tests/test_udev_wrapper_asan_skip.cpp new file mode 100644 index 00000000000..3a1c505bc3c --- /dev/null +++ b/tests/unit-tests/test_udev_wrapper_asan_skip.cpp @@ -0,0 +1,88 @@ +/* + * Copyright © Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or 3 as + * published by the Free Software Foundation. + * + * 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 . + */ + +#include "mir_test_framework/udev_environment.h" +#include "mir/udev/wrapper.h" +#include "mir/test/death.h" + +#include +#include +#include +#include + +namespace mtf=mir_test_framework; + +namespace +{ +bool KilledByInvalidMemoryAccess(int exit_status) +{ + return testing::KilledBySignal(SIGSEGV)(exit_status) || + testing::KilledBySignal(SIGBUS)(exit_status) || + testing::KilledBySignal(SIGABRT)(exit_status) || + // It seems that valgrind kills us with SIGKILL + testing::KilledBySignal(SIGKILL)(exit_status); +} + +/// Tests in this suite are skipped during the address sanitizer build, +/// as they purposefully do things that the address sanitizer would not like. +class UdevWrapperDeathTest : public ::testing::Test +{ +public: + mtf::UdevEnvironment udev_environment; +}; +} + +TEST_F(UdevWrapperDeathTest, DereferencingEndReturnsInvalidObject) +{ + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + udev_environment.add_device("drm", "control64D", NULL, {}, {}); + udev_environment.add_device("drm", "card1", NULL, {}, {}); + + mir::udev::Enumerator devices(std::make_shared()); + + devices.scan_devices(); + + MIR_EXPECT_EXIT((*devices.end()).subsystem(), KilledByInvalidMemoryAccess, ""); + + auto iter = devices.begin(); + + while(iter != devices.end()) + { + iter++; + } + MIR_EXPECT_EXIT((*iter).subsystem(), KilledByInvalidMemoryAccess, ""); +} + +TEST_F(UdevWrapperDeathTest, MemberDereferenceOfEndDies) +{ + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + udev_environment.add_device("drm", "control64D", NULL, {}, {}); + udev_environment.add_device("drm", "card1", NULL, {}, {}); + + mir::udev::Enumerator devices(std::make_shared()); + + devices.scan_devices(); + + MIR_EXPECT_EXIT(devices.end()->subsystem(), KilledByInvalidMemoryAccess, ""); + + auto iter = devices.begin(); + + while(iter != devices.end()) + { + iter++; + } + MIR_EXPECT_EXIT(iter->subsystem(), KilledByInvalidMemoryAccess, ""); +} \ No newline at end of file