diff --git a/dbus/dbus-internals.c b/dbus/dbus-internals.c index ab498b15..ea0ffdd5 100644 --- a/dbus/dbus-internals.c +++ b/dbus/dbus-internals.c @@ -195,6 +195,13 @@ static dbus_bool_t warn_initted = FALSE; static dbus_bool_t fatal_warnings = FALSE; static dbus_bool_t fatal_warnings_on_check_failed = TRUE; +static int check_failed_count = 0; + +int _dbus_get_check_failed_count (void) +{ + return check_failed_count; +} + static void init_warnings(void) { @@ -221,6 +228,8 @@ init_warnings(void) } } + check_failed_count = 0; + warn_initted = TRUE; } } @@ -288,6 +297,8 @@ _dbus_warn_check_failed(const char *format, fflush (stderr); _dbus_abort (); } + else + check_failed_count++; } #ifdef DBUS_ENABLE_VERBOSE_MODE diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h index 1ca0065d..96df3ff7 100644 --- a/dbus/dbus-internals.h +++ b/dbus/dbus-internals.h @@ -49,6 +49,9 @@ void _dbus_warn_return_if_fail (const char *function, const char *file, int line); +DBUS_EMBEDDED_TESTS_EXPORT +int _dbus_get_check_failed_count (void); + #if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) #define _DBUS_FUNCTION_NAME __func__ #elif defined(__GNUC__) || defined(_MSC_VER) diff --git a/dbus/dbus-threads-internal.h b/dbus/dbus-threads-internal.h index aa7810ac..56d169b3 100644 --- a/dbus/dbus-threads-internal.h +++ b/dbus/dbus-threads-internal.h @@ -81,9 +81,13 @@ void _dbus_condvar_free_at_location (DBusCondVar **location_p); * * @return mutex instance or #NULL on OOM */ +DBUS_EMBEDDED_TESTS_EXPORT DBusRMutex *_dbus_platform_rmutex_new (void); +DBUS_EMBEDDED_TESTS_EXPORT void _dbus_platform_rmutex_free (DBusRMutex *mutex); +DBUS_EMBEDDED_TESTS_EXPORT void _dbus_platform_rmutex_lock (DBusRMutex *mutex); +DBUS_EMBEDDED_TESTS_EXPORT void _dbus_platform_rmutex_unlock (DBusRMutex *mutex); /** @@ -91,9 +95,13 @@ void _dbus_platform_rmutex_unlock (DBusRMutex *mutex); * * @return mutex instance or #NULL on OOM */ +DBUS_EMBEDDED_TESTS_EXPORT DBusCMutex *_dbus_platform_cmutex_new (void); +DBUS_EMBEDDED_TESTS_EXPORT void _dbus_platform_cmutex_free (DBusCMutex *mutex); +DBUS_EMBEDDED_TESTS_EXPORT void _dbus_platform_cmutex_lock (DBusCMutex *mutex); +DBUS_EMBEDDED_TESTS_EXPORT void _dbus_platform_cmutex_unlock (DBusCMutex *mutex); DBusCondVar* _dbus_platform_condvar_new (void); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4689c78b..864d078b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -144,6 +144,8 @@ if(DBUS_ENABLE_EMBEDDED_TESTS) add_test_executable(test-misc-internals "${SOURCES}" dbus-testutils) set_target_properties(test-misc-internals PROPERTIES COMPILE_FLAGS ${DBUS_INTERNAL_CLIENT_DEFINITIONS}) + add_test_executable(test-platform-mutex test-platform-mutex.c ${DBUS_INTERNAL_LIBRARIES} dbus-testutils) + set(SOURCES bus/main.c) add_test_executable(test-bus "${SOURCES}" dbus-daemon-internal dbus-testutils ${EXPAT_LIBRARIES}) set_target_properties(test-bus PROPERTIES COMPILE_FLAGS ${DBUS_INTERNAL_CLIENT_DEFINITIONS}) diff --git a/test/Makefile.am b/test/Makefile.am index d013dd87..e89d8ad4 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -85,6 +85,7 @@ TEST_BINARIES += test-spawn endif uninstallable_test_programs += \ + test-platform-mutex \ test-bus \ test-bus-dispatch \ test-bus-dispatch-sha1 \ @@ -256,6 +257,12 @@ test_misc_internals_SOURCES = \ $(NULL) test_misc_internals_LDADD = libdbus-testutils.la +test_platform_mutex_SOURCES = test-platform-mutex.c +test_platform_mutex_LDADD = \ + $(top_builddir)/dbus/libdbus-internal.la \ + libdbus-testutils.la \ + $(NULL) + EXTRA_DIST += dbus-test-runner testexecdir = $(libexecdir)/installed-tests/dbus diff --git a/test/test-platform-mutex.c b/test/test-platform-mutex.c new file mode 100644 index 00000000..ba2cb35f --- /dev/null +++ b/test/test-platform-mutex.c @@ -0,0 +1,279 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* test-platform-mutex.c + * + * Copyright © 2022 Ralf Habacker + * SPDX-License-Identifier: AFL-2.1 or GPL-2.0-or-later + + * Licensed under the Academic Free License version 2.1 + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#include + +#include "dbus/dbus-internals.h" +#include "dbus/dbus-spawn.h" +#include "dbus/dbus-sysdeps.h" +#include "dbus/dbus-test.h" +#include "test/test-utils.h" + +#define fail_if_mutex_is_null(a) if (a == NULL) _dbus_test_fatal ("Could not create cmutex") + + +static dbus_bool_t +_dbus_check_cmutex_new (const char *test_data_dir _DBUS_GNUC_UNUSED) +{ + DBusCMutex *mutex = NULL; + int count; + + count = _dbus_get_check_failed_count (); + mutex = _dbus_platform_cmutex_new (); + fail_if_mutex_is_null (mutex); + _dbus_platform_cmutex_free (mutex); + return _dbus_get_check_failed_count () == count; +} + +static dbus_bool_t +_dbus_check_cmutex_lock (const char *test_data_dir _DBUS_GNUC_UNUSED) +{ + DBusCMutex *mutex = NULL; + int count; + + count = _dbus_get_check_failed_count (); + mutex = _dbus_platform_cmutex_new (); + fail_if_mutex_is_null (mutex); + _dbus_platform_cmutex_lock (mutex); + _dbus_platform_cmutex_unlock (mutex); + _dbus_platform_cmutex_free (mutex); + return _dbus_get_check_failed_count () == count; +} + +#ifdef DBUS_WIN +static dbus_bool_t +_dbus_check_cmutex_lock_null_pointer (const char *test_data_dir _DBUS_GNUC_UNUSED) +{ + DBusCMutex *mutex = NULL; + int count; + + count = _dbus_get_check_failed_count (); + _dbus_platform_cmutex_lock (mutex); + return _dbus_get_check_failed_count () - count == 1; +} + +static dbus_bool_t +_dbus_check_cmutex_double_lock (const char *test_data_dir _DBUS_GNUC_UNUSED) +{ + DBusCMutex *mutex = NULL; + int count; + + count = _dbus_get_check_failed_count (); + mutex = _dbus_platform_cmutex_new (); + fail_if_mutex_is_null (mutex); + _dbus_platform_cmutex_lock (mutex); + _dbus_platform_cmutex_lock (mutex); + _dbus_platform_cmutex_unlock (mutex); + _dbus_platform_cmutex_unlock (mutex); + _dbus_platform_cmutex_free (mutex); + return _dbus_get_check_failed_count () == count; +} + +static dbus_bool_t +_dbus_check_cmutex_unlock_null_pointer (const char *test_data_dir _DBUS_GNUC_UNUSED) +{ + DBusCMutex *mutex = NULL; + int count; + + count = _dbus_get_check_failed_count (); + _dbus_platform_cmutex_unlock (mutex); + return _dbus_get_check_failed_count () - count == 1; +} + +static dbus_bool_t +_dbus_check_cmutex_null_free (const char *test_data_dir _DBUS_GNUC_UNUSED) +{ + DBusCMutex *mutex = NULL; + int count; + + count = _dbus_get_check_failed_count (); + _dbus_platform_cmutex_free (mutex); /* programming error (NULL isn't a mutex) */ + return _dbus_get_check_failed_count () - count == 1; +} + +static dbus_bool_t +_dbus_check_cmutex_double_free (const char *test_data_dir _DBUS_GNUC_UNUSED) +{ + DBusCMutex *mutex = NULL; + int count; + + mutex = _dbus_platform_cmutex_new (); + fail_if_mutex_is_null (mutex); + count = _dbus_get_check_failed_count (); + _dbus_platform_cmutex_free (mutex); + if (_dbus_get_check_failed_count () - count > 0) + { + _dbus_test_not_ok ("free'ing mutex failed"); + return FALSE; + } + _dbus_platform_cmutex_free (mutex); + return _dbus_get_check_failed_count () - count == 1; +} +#else +/* + * #NULL pointers of type DBusCMutex cannot be tested on unix-like + * operating systems, because they are pointing to a data structure + * and would cause a segment violation when accessed. + */ +#endif + +static dbus_bool_t +_dbus_check_rmutex_new (const char *test_data_dir _DBUS_GNUC_UNUSED) +{ + DBusRMutex *mutex = NULL; + int count; + + count = _dbus_get_check_failed_count (); + mutex = _dbus_platform_rmutex_new (); + fail_if_mutex_is_null (mutex); + _dbus_platform_rmutex_free (mutex); + return _dbus_get_check_failed_count () == count; +} + +static dbus_bool_t +_dbus_check_rmutex_lock (const char *test_data_dir _DBUS_GNUC_UNUSED) +{ + DBusRMutex *mutex = NULL; + int count; + + count = _dbus_get_check_failed_count (); + mutex = _dbus_platform_rmutex_new (); + fail_if_mutex_is_null (mutex); + _dbus_platform_rmutex_lock (mutex); + _dbus_platform_rmutex_unlock (mutex); + _dbus_platform_rmutex_free (mutex); + return _dbus_get_check_failed_count () == count; +} +#ifdef DBUS_WIN +static dbus_bool_t +_dbus_check_rmutex_lock_null_pointer (const char *test_data_dir _DBUS_GNUC_UNUSED) +{ + DBusRMutex *mutex = NULL; + int count; + + count = _dbus_get_check_failed_count (); + _dbus_platform_rmutex_lock (mutex); + return _dbus_get_check_failed_count () - count == 1; +} +#endif + +static dbus_bool_t +_dbus_check_rmutex_double_lock (const char *test_data_dir _DBUS_GNUC_UNUSED) +{ + DBusRMutex *mutex = NULL; + int count; + + count = _dbus_get_check_failed_count (); + mutex = _dbus_platform_rmutex_new (); + fail_if_mutex_is_null (mutex); + _dbus_platform_rmutex_lock (mutex); + _dbus_platform_rmutex_lock (mutex); + _dbus_platform_rmutex_unlock (mutex); + _dbus_platform_rmutex_unlock (mutex); + _dbus_platform_rmutex_free (mutex); + return _dbus_get_check_failed_count () == count; +} + +#ifdef DBUS_WIN +static dbus_bool_t +_dbus_check_rmutex_unlock_null_pointer (const char *test_data_dir _DBUS_GNUC_UNUSED) +{ + DBusRMutex *mutex = NULL; + int count; + + count = _dbus_get_check_failed_count (); + _dbus_platform_rmutex_unlock (mutex); + return _dbus_get_check_failed_count () - count == 1; +} + +static dbus_bool_t +_dbus_check_rmutex_null_free (const char *test_data_dir _DBUS_GNUC_UNUSED) +{ + DBusRMutex *mutex = NULL; + int count; + + count = _dbus_get_check_failed_count (); + _dbus_platform_rmutex_free (mutex); /* programming error (NULL isn't a mutex) */ + return _dbus_get_check_failed_count () - count == 1; +} + +static dbus_bool_t +_dbus_check_rmutex_double_free (const char *test_data_dir _DBUS_GNUC_UNUSED) +{ + DBusRMutex *mutex = NULL; + int count; + + mutex = _dbus_platform_rmutex_new (); + fail_if_mutex_is_null (mutex); + count = _dbus_get_check_failed_count (); + _dbus_platform_rmutex_free (mutex); + if (_dbus_get_check_failed_count () - count > 0) + { + _dbus_test_not_ok ("free'ing mutex failed"); + return FALSE; + } + _dbus_platform_rmutex_free (mutex); + return _dbus_get_check_failed_count () - count == 1; +} +#else +/* + * #NULL pointers of type DBusRMutex cannot be tested on unix-like + * operating systems, because they are pointing to a data structure + * and would cause a segment violation when accessed. + */ +#endif + +static DBusTestCase tests[] = +{ + { "cmutex_new", _dbus_check_cmutex_new}, + { "cmutex_lock", _dbus_check_cmutex_lock}, +#ifdef DBUS_WIN + { "cmutex_lock_null_pointer", _dbus_check_cmutex_lock_null_pointer}, + { "cmutex_double_lock", _dbus_check_cmutex_double_lock}, + { "cmutex_unlock_null_pointer", _dbus_check_cmutex_unlock_null_pointer}, + { "cmutex_null_free", _dbus_check_cmutex_null_free}, + { "cmutex_double_free", _dbus_check_cmutex_double_free}, +#endif + { "rmutex_new", _dbus_check_rmutex_new}, + { "rmutex_lock", _dbus_check_rmutex_lock}, +#ifdef DBUS_WIN + { "rmutex_lock_null_pointer", _dbus_check_rmutex_lock_null_pointer}, +#endif + { "rmutex_double_lock", _dbus_check_rmutex_double_lock}, +#ifdef DBUS_WIN + { "rmutex_unlock_null_pointer", _dbus_check_rmutex_unlock_null_pointer}, + { "rmutex_null_free", _dbus_check_rmutex_null_free}, + { "rmutex_double_free", _dbus_check_rmutex_double_free}, +#endif +}; + +int +main (int argc, + char **argv) +{ + dbus_setenv ("DBUS_FATAL_WARNINGS", "0"); + + return _dbus_test_main (argc, argv, _DBUS_N_ELEMENTS (tests), tests, + DBUS_TEST_FLAGS_CHECK_MEMORY_LEAKS, + NULL, NULL); +}