Add a test which triggers two event sources in the loop's "before"
control hook, and destroys the second source in the loop's "after"
control hook, and then reenters the loop in the event handler of
the first source. At the moment, this test triggers a use-after-free.
==2973914==ERROR: AddressSanitizer: heap-use-after-free on address 0x608000000440 [...]
READ of size 4 at 0x608000000440 thread T0
#0 0x7fa97f60c6b7 in loop_iterate ../spa/plugins/support/loop.c:376
#1 0x7fa98472c1eb in pw_main_loop_run ../src/pipewire/main-loop.c:148
#2 0x559995af7a76 in destroy_managed_source_before_dispatch_recurse ../test/test-loop.c:355
#3 0x559995b02678 in start_test_nofork ../test/pwtest.c:882
#4 0x559995b06191 in run_test ../test/pwtest.c:1087
#5 0x559995b0948a in run_tests ../test/pwtest.c:1283
#6 0x559995b0aea4 in main ../test/pwtest.c:1482
#7 0x7fa98360130f in __libc_start_call_main (/usr/lib/libc.so.6+0x2d30f)
#8 0x7fa9836013c0 in __libc_start_main@GLIBC_2.2.5 (/usr/lib/libc.so.6+0x2d3c0)
#9 0x559995aed754 in _start (/home/pb/temp/src/pipewire/build/test/test-loop+0x26754)
0x608000000440 is located 32 bytes inside of 96-byte region [0x608000000420,0x608000000480)
freed by thread T0 here:
#0 0x7fa984ffda79 in __interceptor_free /usr/src/debug/gcc/libsanitizer/asan/asan_malloc_linux.cpp:127
#1 0x7fa97f60b03a in process_destroy ../spa/plugins/support/loop.c:344
#2 0x7fa97f60cbf8 in loop_iterate ../spa/plugins/support/loop.c:387
#3 0x559995af5b62 in dmsbd_recurse_on_event ../test/test-loop.c:298
#4 0x7fa97f60d826 in source_io_func ../spa/plugins/support/loop.c:396
#5 0x7fa97f60c7e7 in loop_iterate ../spa/plugins/support/loop.c:377
#6 0x7fa98472c1eb in pw_main_loop_run ../src/pipewire/main-loop.c:148
#7 0x559995af7a76 in destroy_managed_source_before_dispatch_recurse ../test/test-loop.c:355
#8 0x559995b02678 in start_test_nofork ../test/pwtest.c:882
#9 0x559995b06191 in run_test ../test/pwtest.c:1087
#10 0x559995b0948a in run_tests ../test/pwtest.c:1283
#11 0x559995b0aea4 in main ../test/pwtest.c:1482
#12 0x7fa98360130f in __libc_start_call_main (/usr/lib/libc.so.6+0x2d30f)
previously allocated by thread T0 here:
#0 0x7fa984ffdfb9 in __interceptor_calloc /usr/src/debug/gcc/libsanitizer/asan/asan_malloc_linux.cpp:154
#1 0x7fa97f60d883 in loop_add_io ../spa/plugins/support/loop.c:408
#2 0x559995af75de in destroy_managed_source_before_dispatch_recurse ../test/test-loop.c:349
#3 0x559995b02678 in start_test_nofork ../test/pwtest.c:882
#4 0x559995b06191 in run_test ../test/pwtest.c:1087
#5 0x559995b0948a in run_tests ../test/pwtest.c:1283
#6 0x559995b0aea4 in main ../test/pwtest.c:1482
#7 0x7fa98360130f in __libc_start_call_main (/usr/lib/libc.so.6+0x2d30f)
SUMMARY: AddressSanitizer: heap-use-after-free ../spa/plugins/support/loop.c:376 in loop_iterate
Rewrite the test that destroys an active managed source
right after polling is done. There is no need to use a
thread loop because the same thing can be simulated using
the before/after loop control hooks in a more controlled
manner.
Catch SIGTERM/SIGINT in test runner, and clean up any spawned processes.
Fixes printing test output on termination by signal (e.g. meson
timeout), and doesn't leave spawned processes running.
Add an extra private field to the source to store the pollevent of
the current iteration. This changes ABI but it seems an embedded source
is not used outside of our own plugins and the unit test doesn't test
this ABI case.
Whenever a source is removed, we can set the data field of the
pollevent to NULL so that it won't be handled in any iteration anymore.
Avoid dispatching the same event multiple times when doing recursive
iterations.
Add some more unit tests for this.
Fixes#2114
Call spa_handle_clear() on the handle before freeing.
Actually store the loaded dll in the right array.
First clear the handle, then close the dll.
Fix iteration of the dlls.
Most feature checks already use #ifdef, and do not care about
the value of the macro. Convert all feature checks to do that,
and simplify the meson build scripts by replacing
if cond
cdata.set('X', 1)
endif
with
cdata.set('X', cond)
Use `meson.project_{build,source}_root()` instead of
`meson.{build,source}_root()` because those functions
do not work as expected when used inside a subproject,
and they have been deprecated in meson 0.56.0.
Previously, the standard i/o stream and daemon logs
were not properly null terminated, which could cause
issues in `print_lines()` inside `log_test_result()`.
See #1710
When we get to this code, we already updated the environment variables
so we need to use the usual error path to restore them.
And where chdir does fail, print an error to the log. 256 chars should
be enough here, if your tmpdir exceeds that you just have the error
message cut off.
You are only supposed to use _insert_new()/_remove() or _insert_at()
on the map, If we detect a _insert_at() to a removed item,
return an error because else we might corrupt the free list.
Update unit test accordingly.
The previous code exposed a race condition, it assumed that our MARK
message was in the next lot of messages by the journal. If something
else would write to the journal in the meantime and our message took
time to arrive, the test would fail.
Fixes#1710
Valgrind tests are running under different conditions than normal tests,
specifically they all run in the same process rather than running one
fork per test. This means one test can affect another test's result if
the test doesn't clean up propertly.
This makes them harder to debug from the logs, so let's give us a
fighting chance by having debug logging enabled.
When running meson test without a preceding ninja build, we end up with
missing libraries. This somehow happened to work until recently but now
it triggers an issue with libspa-dbus.so not being available during the
valgrind test run, causing a test case to fail.
The docs are slightly ambiguous here but it appears a call to
sd_journal_next() is required at least once before the actual data
message.
journalctl positions the cursor with sd_journal_seek_tail() followed by
sd_journal_seek_previous(). The actual enty is then fetched with
sd_journal_next().
Let's do the same here and let's hope this fixes the current race
condition with the test sometimes failing.
Debugging is easier if we can explicitly run a single iteration only.
This filter could be a range but for now it's sufficient to be able to
run a single test only.
Re-using an id after removing it is a bug in the caller but there are
two cases where we corrupt the free list without warning:
Removing an object twice:
id = pw_map_insert_new(object);
pw_map_remove(map, id);
pw_map_remove(map, id);
And inserting an element at an index previously removed:
id = pw_map_insert_new(object);
pw_map_remove(map, id);
pw_map_insert_at(map, id, new_object);
The latter is arguably valid code, or at least it'll look like it's
valid code.
For both cases, check if the id to remove/insert at is a free item and
handle that accordingly.
The Doxygen "Modules" page is not very illuminative, as different parts
of the API are mixed together and not all parts are included.
Try to address this:
Put all parts of the public API to some Doxygen group, usually one group
per header file. Use short, systematic names.
Make these groups sub-groups of a few top-level groups, roughly
corresponding to the different logical parts of the API (core, impl,
stream, filter, spa, utilities).