pipewire/test/test-loop.c

449 lines
9.2 KiB
C
Raw Normal View History

/* PipeWire */
/* SPDX-FileCopyrightText: Copyright © 2022 Wim Taymans <wim.taymans@gmail.com> */
/* SPDX-License-Identifier: MIT */
2022-02-08 11:08:16 +01:00
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/eventfd.h>
#include "pwtest.h"
#include <pipewire/pipewire.h>
struct obj {
int x;
struct spa_source source;
};
struct data {
struct pw_main_loop *ml;
struct pw_loop *l;
struct obj *a, *b;
int count;
2022-02-08 11:08:16 +01:00
};
static inline void write_eventfd(int evfd)
{
uint64_t value = 1;
ssize_t r = write(evfd, &value, sizeof(value));
pwtest_errno_ok(r);
pwtest_int_eq(r, (ssize_t) sizeof(value));
}
static inline void read_eventfd(int evfd)
{
uint64_t value = 0;
ssize_t r = read(evfd, &value, sizeof(value));
pwtest_errno_ok(r);
pwtest_int_eq(r, (ssize_t) sizeof(value));
}
2022-02-08 11:08:16 +01:00
static void on_event(struct spa_source *source)
{
struct data *d = source->data;
pw_loop_remove_source(d->l, &d->a->source);
pw_loop_remove_source(d->l, &d->b->source);
close(d->a->source.fd);
close(d->b->source.fd);
free(d->a);
free(d->b);
pw_main_loop_quit(d->ml);
}
PWTEST(pwtest_loop_destroy2)
{
struct data data;
pw_init(0, NULL);
spa_zero(data);
2022-02-08 11:08:16 +01:00
data.ml = pw_main_loop_new(NULL);
pwtest_ptr_notnull(data.ml);
data.l = pw_main_loop_get_loop(data.ml);
pwtest_ptr_notnull(data.l);
data.a = calloc(1, sizeof(*data.a));
data.b = calloc(1, sizeof(*data.b));
data.a->source.func = on_event;
data.a->source.fd = eventfd(0, 0);
data.a->source.mask = SPA_IO_IN;
data.a->source.data = &data;
data.b->source.func = on_event;
data.b->source.fd = eventfd(0, 0);
data.b->source.mask = SPA_IO_IN;
data.b->source.data = &data;
pw_loop_add_source(data.l, &data.a->source);
pw_loop_add_source(data.l, &data.b->source);
write_eventfd(data.a->source.fd);
write_eventfd(data.b->source.fd);
2022-02-08 11:08:16 +01:00
pw_main_loop_run(data.ml);
pw_main_loop_destroy(data.ml);
pw_deinit();
return PWTEST_PASS;
}
static void
on_event_recurse1(struct spa_source *source)
{
static bool first = true;
struct data *d = source->data;
++d->count;
pwtest_int_lt(d->count, 3);
read_eventfd(source->fd);
if (first) {
first = false;
pw_loop_enter(d->l);
pw_loop_iterate(d->l, -1);
pw_loop_leave(d->l);
}
pw_main_loop_quit(d->ml);
}
PWTEST(pwtest_loop_recurse1)
{
struct data data;
pw_init(0, NULL);
spa_zero(data);
data.ml = pw_main_loop_new(NULL);
pwtest_ptr_notnull(data.ml);
data.l = pw_main_loop_get_loop(data.ml);
pwtest_ptr_notnull(data.l);
data.a = calloc(1, sizeof(*data.a));
data.b = calloc(1, sizeof(*data.b));
data.a->source.func = on_event_recurse1;
data.a->source.fd = eventfd(0, 0);
data.a->source.mask = SPA_IO_IN;
data.a->source.data = &data;
data.b->source.func = on_event_recurse1;
data.b->source.fd = eventfd(0, 0);
data.b->source.mask = SPA_IO_IN;
data.b->source.data = &data;
pw_loop_add_source(data.l, &data.a->source);
pw_loop_add_source(data.l, &data.b->source);
write_eventfd(data.a->source.fd);
write_eventfd(data.b->source.fd);
pw_main_loop_run(data.ml);
pw_main_loop_destroy(data.ml);
pw_deinit();
2022-02-08 19:04:08 +01:00
free(data.a);
free(data.b);
return PWTEST_PASS;
}
static void
on_event_recurse2(struct spa_source *source)
{
static bool first = true;
struct data *d = source->data;
++d->count;
pwtest_int_lt(d->count, 3);
read_eventfd(source->fd);
if (first) {
first = false;
pw_loop_enter(d->l);
pw_loop_iterate(d->l, -1);
pw_loop_leave(d->l);
} else {
pw_loop_remove_source(d->l, &d->a->source);
pw_loop_remove_source(d->l, &d->b->source);
close(d->a->source.fd);
close(d->b->source.fd);
free(d->a);
free(d->b);
}
pw_main_loop_quit(d->ml);
}
PWTEST(pwtest_loop_recurse2)
{
struct data data;
pw_init(0, NULL);
spa_zero(data);
data.ml = pw_main_loop_new(NULL);
pwtest_ptr_notnull(data.ml);
data.l = pw_main_loop_get_loop(data.ml);
pwtest_ptr_notnull(data.l);
data.a = calloc(1, sizeof(*data.a));
data.b = calloc(1, sizeof(*data.b));
data.a->source.func = on_event_recurse2;
data.a->source.fd = eventfd(0, 0);
data.a->source.mask = SPA_IO_IN;
data.a->source.data = &data;
data.b->source.func = on_event_recurse2;
data.b->source.fd = eventfd(0, 0);
data.b->source.mask = SPA_IO_IN;
data.b->source.data = &data;
pw_loop_add_source(data.l, &data.a->source);
pw_loop_add_source(data.l, &data.b->source);
write_eventfd(data.a->source.fd);
write_eventfd(data.b->source.fd);
pw_main_loop_run(data.ml);
pw_main_loop_destroy(data.ml);
pw_deinit();
return PWTEST_PASS;
}
static void
on_event_fail_if_called(void *data, int fd, uint32_t mask)
{
pwtest_fail_if_reached();
}
struct dmsbd_data {
struct pw_loop *l;
struct pw_main_loop *ml;
struct spa_source *source;
struct spa_hook hook;
};
static void dmsbd_before(void *data)
{
}
static void dmsbd_after(void *data)
{
struct dmsbd_data *d = data;
pw_loop_destroy_source(d->l, d->source);
pw_main_loop_quit(d->ml);
}
static const struct spa_loop_control_hooks dmsbd_hooks = {
SPA_VERSION_LOOP_CONTROL_HOOKS,
.before = dmsbd_before,
.after = dmsbd_after,
};
PWTEST(destroy_managed_source_before_dispatch)
{
pw_init(NULL, NULL);
struct dmsbd_data data = {0};
data.ml = pw_main_loop_new(NULL);
pwtest_ptr_notnull(data.ml);
data.l = pw_main_loop_get_loop(data.ml);
pwtest_ptr_notnull(data.l);
data.source = pw_loop_add_io(data.l, eventfd(0, 0), SPA_IO_IN, true, on_event_fail_if_called, NULL);
pwtest_ptr_notnull(data.source);
pw_loop_add_hook(data.l, &data.hook, &dmsbd_hooks, &data);
write_eventfd(data.source->fd);
pw_main_loop_run(data.ml);
pw_main_loop_destroy(data.ml);
pw_deinit();
return PWTEST_PASS;
}
test: loop: add test which destroys managed source before reentering 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
2022-02-24 16:30:00 +01:00
struct dmsbd_recurse_data {
struct pw_loop *l;
struct pw_main_loop *ml;
struct spa_source *a, *b;
struct spa_hook hook;
bool first;
};
static void dmsbd_recurse_on_event(void *data, int fd, uint32_t mask)
{
struct dmsbd_recurse_data *d = data;
read_eventfd(fd);
test: loop: add test which destroys managed source before reentering 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
2022-02-24 16:30:00 +01:00
pw_loop_enter(d->l);
pw_loop_iterate(d->l, 0);
pw_loop_leave(d->l);
pw_main_loop_quit(d->ml);
}
static void dmswp_recurse_before(void *data)
{
struct dmsbd_recurse_data *d = data;
if (d->first) {
write_eventfd(d->a->fd);
write_eventfd(d->b->fd);
test: loop: add test which destroys managed source before reentering 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
2022-02-24 16:30:00 +01:00
}
}
static void dmsbd_recurse_after(void *data)
{
struct dmsbd_recurse_data *d = data;
if (d->first) {
pw_loop_destroy_source(d->l, d->b);
d->first = false;
}
}
static const struct spa_loop_control_hooks dmsbd_recurse_hooks = {
SPA_VERSION_LOOP_CONTROL_HOOKS,
.before = dmswp_recurse_before,
.after = dmsbd_recurse_after,
};
PWTEST(destroy_managed_source_before_dispatch_recurse)
{
pw_init(NULL, NULL);
struct dmsbd_recurse_data data = {
.first = true,
};
data.ml = pw_main_loop_new(NULL);
pwtest_ptr_notnull(data.ml);
data.l = pw_main_loop_get_loop(data.ml);
pwtest_ptr_notnull(data.l);
data.l = pw_main_loop_get_loop(data.ml);
pwtest_ptr_notnull(data.l);
data.a = pw_loop_add_io(data.l, eventfd(0, 0), SPA_IO_IN, true, dmsbd_recurse_on_event, &data);
data.b = pw_loop_add_io(data.l, eventfd(0, 0), SPA_IO_IN, true, on_event_fail_if_called, NULL);
pwtest_ptr_notnull(data.a);
pwtest_ptr_notnull(data.b);
pw_loop_add_hook(data.l, &data.hook, &dmsbd_recurse_hooks, &data);
pw_main_loop_run(data.ml);
pw_main_loop_destroy(data.ml);
pw_deinit();
return PWTEST_PASS;
}
spa: support: loop: handle cancellation better Register a pthread cleanup handler to guarantee that `spa_source::{priv, rmask}` are cleared even if the thread is cancelled while the loop is dispatching. This is necessary, otherwise `spa_source::priv` could point to the stack of the cancelled thread, which will lead to problems like this later: Program terminated with signal SIGSEGV, Segmentation fault. #0 0x00007f846b025be2 in detach_source (source=0x7f845f435f60) at ../spa/plugins/support/loop.c:144 144 e->data = NULL; [Current thread is 1 (LWP 5274)] (gdb) p e $1 = (struct spa_poll_event *) 0x7f845e297820 (gdb) bt #0 0x00007f846b025be2 in detach_source (source=0x7f845f435f60) at ../spa/plugins/support/loop.c:144 #1 0x00007f846b0276ad in free_source (s=0x7f845f435f60) at ../spa/plugins/support/loop.c:359 #2 0x00007f846b02a453 in loop_destroy_source (object=0x7f845f3af478, source=0x7f845f435f60) at ../spa/plugins/support/loop.c:786 #3 0x00007f846b02a886 in impl_clear (handle=0x7f845f3af478) at ../spa/plugins/support/loop.c:859 #4 0x00007f846b172f40 in unref_handle (handle=0x7f845f3af450) at ../src/pipewire/pipewire.c:211 #5 0x00007f846b173579 in pw_unload_spa_handle (handle=0x7f845f3af478) at ../src/pipewire/pipewire.c:346 #6 0x00007f846b15a761 in pw_loop_destroy (loop=0x7f845f434e30) at ../src/pipewire/loop.c:159 #7 0x00007f846b135d8e in pw_data_loop_destroy (loop=0x7f845f434cb0) at ../src/pipewire/data-loop.c:166 #8 0x00007f846b12c31c in pw_context_destroy (context=0x7f845f41c690) at ../src/pipewire/context.c:485 #9 0x00007f846b3ddf9e in jack_client_close (client=0x7f845f3c1030) at ../pipewire-jack/src/pipewire-jack.c:3481 ...
2022-06-01 16:36:56 +02:00
struct ctwd_data {
struct spa_source source;
int handler_running_barrier;
};
static void ctwd_event_handler(struct spa_source *source)
{
struct ctwd_data *data = source->data;
write_eventfd(data->handler_running_barrier);
for (;;)
pause(); /* the purpose of this is to block the loop */
}
static int ctwd_add_source(struct spa_loop *loop, bool async, uint32_t seq,
const void *d, size_t size, void *user_data)
{
struct ctwd_data *data = user_data;
pwtest_neg_errno_ok(spa_loop_add_source(loop, &data->source));
return 0;
}
PWTEST(cancel_thread_while_dispatching)
{
static const struct spa_dict_item data_loop_props_items[] = {
{ "loop.cancel", "true" },
};
static const struct spa_dict data_loop_props = SPA_DICT_INIT_ARRAY(data_loop_props_items);
struct ctwd_data data = {
.source = {
.data = &data,
.func = ctwd_event_handler,
.mask = SPA_IO_IN,
.fd = eventfd(0, 0),
},
.handler_running_barrier = eventfd(0, 0),
};
pw_init(NULL, NULL);
struct pw_data_loop *dl = pw_data_loop_new(&data_loop_props);
pwtest_ptr_notnull(dl);
struct pw_loop *l = pw_data_loop_get_loop(dl);
pwtest_ptr_notnull(l);
pwtest_neg_errno_ok(pw_data_loop_start(dl));
pw_loop_invoke(l, ctwd_add_source, 0, NULL, 0, true, &data);
pwtest_ptr_notnull(data.source.loop);
write_eventfd(data.source.fd);
read_eventfd(data.handler_running_barrier);
pwtest_neg_errno_ok(pw_data_loop_stop(dl));
/* these are the important checks */
pwtest_ptr_null(data.source.priv);
pwtest_int_eq(data.source.rmask, UINT32_C(0));
pw_loop_remove_source(l, &data.source);
pw_data_loop_destroy(dl);
close(data.source.fd);
close(data.handler_running_barrier);
pw_deinit();
return PWTEST_PASS;
}
2022-02-08 11:08:16 +01:00
PWTEST_SUITE(support)
{
pwtest_add(pwtest_loop_destroy2, PWTEST_NOARG);
pwtest_add(pwtest_loop_recurse1, PWTEST_NOARG);
pwtest_add(pwtest_loop_recurse2, PWTEST_NOARG);
pwtest_add(destroy_managed_source_before_dispatch, PWTEST_NOARG);
test: loop: add test which destroys managed source before reentering 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
2022-02-24 16:30:00 +01:00
pwtest_add(destroy_managed_source_before_dispatch_recurse, PWTEST_NOARG);
spa: support: loop: handle cancellation better Register a pthread cleanup handler to guarantee that `spa_source::{priv, rmask}` are cleared even if the thread is cancelled while the loop is dispatching. This is necessary, otherwise `spa_source::priv` could point to the stack of the cancelled thread, which will lead to problems like this later: Program terminated with signal SIGSEGV, Segmentation fault. #0 0x00007f846b025be2 in detach_source (source=0x7f845f435f60) at ../spa/plugins/support/loop.c:144 144 e->data = NULL; [Current thread is 1 (LWP 5274)] (gdb) p e $1 = (struct spa_poll_event *) 0x7f845e297820 (gdb) bt #0 0x00007f846b025be2 in detach_source (source=0x7f845f435f60) at ../spa/plugins/support/loop.c:144 #1 0x00007f846b0276ad in free_source (s=0x7f845f435f60) at ../spa/plugins/support/loop.c:359 #2 0x00007f846b02a453 in loop_destroy_source (object=0x7f845f3af478, source=0x7f845f435f60) at ../spa/plugins/support/loop.c:786 #3 0x00007f846b02a886 in impl_clear (handle=0x7f845f3af478) at ../spa/plugins/support/loop.c:859 #4 0x00007f846b172f40 in unref_handle (handle=0x7f845f3af450) at ../src/pipewire/pipewire.c:211 #5 0x00007f846b173579 in pw_unload_spa_handle (handle=0x7f845f3af478) at ../src/pipewire/pipewire.c:346 #6 0x00007f846b15a761 in pw_loop_destroy (loop=0x7f845f434e30) at ../src/pipewire/loop.c:159 #7 0x00007f846b135d8e in pw_data_loop_destroy (loop=0x7f845f434cb0) at ../src/pipewire/data-loop.c:166 #8 0x00007f846b12c31c in pw_context_destroy (context=0x7f845f41c690) at ../src/pipewire/context.c:485 #9 0x00007f846b3ddf9e in jack_client_close (client=0x7f845f3c1030) at ../pipewire-jack/src/pipewire-jack.c:3481 ...
2022-06-01 16:36:56 +02:00
pwtest_add(cancel_thread_while_dispatching, PWTEST_NOARG);
2022-02-08 11:08:16 +01:00
return PWTEST_PASS;
}