/* $NetBSD: regress_finalize.c,v 1.1.1.2 2021/04/07 02:43:15 christos Exp $ */ /* * Copyright (c) 2013 Niels Provos and Nick Mathewson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "event2/event-config.h" #include __RCSID("$NetBSD: regress_finalize.c,v 1.1.1.2 2021/04/07 02:43:15 christos Exp $"); #include "evconfig-private.h" #include "tinytest.h" #include "tinytest_macros.h" #include #include "event2/event.h" #include "event2/util.h" #include "event-internal.h" #include "defer-internal.h" #include "regress.h" #include "regress_thread.h" static void timer_callback(evutil_socket_t fd, short what, void *arg) { int *int_arg = arg; *int_arg += 1; (void)fd; (void)what; } static void simple_callback(struct event_callback *evcb, void *arg) { int *int_arg = arg; *int_arg += 1; (void)evcb; } static void event_finalize_callback_1(struct event *ev, void *arg) { int *int_arg = arg; *int_arg += 100; (void)ev; } static void callback_finalize_callback_1(struct event_callback *evcb, void *arg) { int *int_arg = arg; *int_arg += 100; (void)evcb; } static void test_fin_cb_invoked(void *arg) { struct basic_test_data *data = arg; struct event_base *base = data->base; struct event *ev; struct event ev2; struct event_callback evcb; int cb_called = 0; int ev_called = 0; const struct timeval ten_sec = {10,0}; event_deferred_cb_init_(&evcb, 0, simple_callback, &cb_called); ev = evtimer_new(base, timer_callback, &ev_called); /* Just finalize them; don't bother adding. */ event_free_finalize(0, ev, event_finalize_callback_1); event_callback_finalize_(base, 0, &evcb, callback_finalize_callback_1); event_base_dispatch(base); tt_int_op(cb_called, ==, 100); tt_int_op(ev_called, ==, 100); ev_called = cb_called = 0; event_base_assert_ok_(base); /* Now try it when they're active. (actually, don't finalize: make * sure activation can happen! */ ev = evtimer_new(base, timer_callback, &ev_called); event_deferred_cb_init_(&evcb, 0, simple_callback, &cb_called); event_active(ev, EV_TIMEOUT, 1); event_callback_activate_(base, &evcb); event_base_dispatch(base); tt_int_op(cb_called, ==, 1); tt_int_op(ev_called, ==, 1); ev_called = cb_called = 0; event_base_assert_ok_(base); /* Great, it worked. Now activate and finalize and make sure only * finalizing happens. */ event_active(ev, EV_TIMEOUT, 1); event_callback_activate_(base, &evcb); event_free_finalize(0, ev, event_finalize_callback_1); event_callback_finalize_(base, 0, &evcb, callback_finalize_callback_1); event_base_dispatch(base); tt_int_op(cb_called, ==, 100); tt_int_op(ev_called, ==, 100); ev_called = 0; event_base_assert_ok_(base); /* Okay, now add but don't have it become active, and make sure *that* * works. */ ev = evtimer_new(base, timer_callback, &ev_called); event_add(ev, &ten_sec); event_free_finalize(0, ev, event_finalize_callback_1); event_base_dispatch(base); tt_int_op(ev_called, ==, 100); ev_called = 0; event_base_assert_ok_(base); /* Now try adding and deleting after finalizing. */ ev = evtimer_new(base, timer_callback, &ev_called); evtimer_assign(&ev2, base, timer_callback, &ev_called); event_add(ev, &ten_sec); event_free_finalize(0, ev, event_finalize_callback_1); event_finalize(0, &ev2, event_finalize_callback_1); event_add(&ev2, &ten_sec); event_del(ev); event_active(&ev2, EV_TIMEOUT, 1); event_base_dispatch(base); tt_int_op(ev_called, ==, 200); event_base_assert_ok_(base); end: ; } #ifndef EVENT__DISABLE_MM_REPLACEMENT static void * tfff_malloc(size_t n) { return malloc(n); } static void *tfff_p1=NULL, *tfff_p2=NULL; static int tfff_p1_freed=0, tfff_p2_freed=0; static void tfff_free(void *p) { if (! p) return; if (p == tfff_p1) ++tfff_p1_freed; if (p == tfff_p2) ++tfff_p2_freed; free(p); } static void * tfff_realloc(void *p, size_t sz) { return realloc(p,sz); } #endif static void test_fin_free_finalize(void *arg) { #ifdef EVENT__DISABLE_MM_REPLACEMENT tinytest_set_test_skipped_(); #else struct event_base *base = NULL; struct event *ev, *ev2; int ev_called = 0; int ev2_called = 0; (void)arg; event_set_mem_functions(tfff_malloc, tfff_realloc, tfff_free); base = event_base_new(); tt_assert(base); ev = evtimer_new(base, timer_callback, &ev_called); ev2 = evtimer_new(base, timer_callback, &ev2_called); tfff_p1 = ev; tfff_p2 = ev2; event_free_finalize(0, ev, event_finalize_callback_1); event_finalize(0, ev2, event_finalize_callback_1); event_base_dispatch(base); tt_int_op(ev_called, ==, 100); tt_int_op(ev2_called, ==, 100); event_base_assert_ok_(base); tt_int_op(tfff_p1_freed, ==, 1); tt_int_op(tfff_p2_freed, ==, 0); event_free(ev2); end: if (base) event_base_free(base); #endif } /* For test_fin_within_cb */ struct event_and_count { struct event *ev; struct event *ev2; int count; }; static void event_finalize_callback_2(struct event *ev, void *arg) { struct event_and_count *evc = arg; evc->count += 100; event_free(ev); } static void timer_callback_2(evutil_socket_t fd, short what, void *arg) { struct event_and_count *evc = arg; event_finalize(0, evc->ev, event_finalize_callback_2); event_finalize(0, evc->ev2, event_finalize_callback_2); ++ evc->count; (void)fd; (void)what; } static void test_fin_within_cb(void *arg) { struct basic_test_data *data = arg; struct event_base *base = data->base; struct event_and_count evc1, evc2; evc1.count = evc2.count = 0; evc2.ev2 = evc1.ev = evtimer_new(base, timer_callback_2, &evc1); evc1.ev2 = evc2.ev = evtimer_new(base, timer_callback_2, &evc2); /* Activate both. The first one will have its callback run, which * will finalize both of them, preventing the second one's callback * from running. */ event_active(evc1.ev, EV_TIMEOUT, 1); event_active(evc2.ev, EV_TIMEOUT, 1); event_base_dispatch(base); tt_int_op(evc1.count, ==, 101); tt_int_op(evc2.count, ==, 100); event_base_assert_ok_(base); /* Now try with EV_PERSIST events. */ evc1.count = evc2.count = 0; evc2.ev2 = evc1.ev = event_new(base, -1, EV_PERSIST, timer_callback_2, &evc1); evc1.ev2 = evc2.ev = event_new(base, -1, EV_PERSIST, timer_callback_2, &evc2); event_active(evc1.ev, EV_TIMEOUT, 1); event_active(evc2.ev, EV_TIMEOUT, 1); event_base_dispatch(base); tt_int_op(evc1.count, ==, 101); tt_int_op(evc2.count, ==, 100); event_base_assert_ok_(base); end: ; } static void event_finalize_callback_free(struct event *ev, void *arg) { struct event_base *base = arg; int err; if (base) { err = event_assign(ev, base, -1, EV_TIMEOUT, NULL, NULL); tt_int_op(err, ==, 0); test_ok += 1; } else { free(ev); test_ok += 1; } end: ; } static void test_fin_debug_use_after_free(void *arg) { struct basic_test_data *data = arg; struct event_base *base = data->base; struct event *ev; tt_ptr_op(ev = event_new(base, -1, EV_TIMEOUT, NULL, base), !=, NULL); tt_int_op(event_add(ev, NULL), ==, 0); tt_int_op(event_finalize(0, ev, event_finalize_callback_free), ==, 0); // Dispatch base to trigger callbacks event_base_dispatch(base); event_base_assert_ok_(base); tt_int_op(test_ok, ==, 1); // Now add again, since we did event_assign in event_finalize_callback_free // This used to fail in event_debug_assert_is_setup_ tt_int_op(event_add(ev, NULL), ==, 0); // Finalize and dispatch again tt_int_op(event_finalize(0, ev, event_finalize_callback_free), ==, 0); event_base_dispatch(base); event_base_assert_ok_(base); tt_int_op(test_ok, ==, 2); end: ; } #if 0 static void timer_callback_3(evutil_socket_t *fd, short what, void *arg) { (void)fd; (void)what; } static void test_fin_many(void *arg) { struct basic_test_data *data = arg; struct event_base *base = data->base; struct event *ev1, *ev2; struct event_callback evcb1, evcb2; int ev1_count = 0, ev2_count = 0; int evcb1_count = 0, evcb2_count = 0; struct event_callback *array[4]; int n; /* First attempt: call finalize_many with no events running */ ev1 = evtimer_new(base, timer_callback, &ev1_count); ev1 = evtimer_new(base, timer_callback, &ev2_count); event_deferred_cb_init_(&evcb1, 0, simple_callback, &evcb1_called); event_deferred_cb_init_(&evcb2, 0, simple_callback, &evcb2_called); array[0] = &ev1->ev_evcallback; array[1] = &ev2->ev_evcallback; array[2] = &evcb1; array[3] = &evcb2; n = event_callback_finalize_many(base, 4, array, callback_finalize_callback_1); } #endif #define TEST(name, flags) \ { #name, test_fin_##name, (flags), &basic_setup, NULL } struct testcase_t finalize_testcases[] = { TEST(cb_invoked, TT_FORK|TT_NEED_BASE), TEST(free_finalize, TT_FORK), TEST(within_cb, TT_FORK|TT_NEED_BASE), TEST(debug_use_after_free, TT_FORK|TT_NEED_BASE|TT_ENABLE_DEBUG_MODE), // TEST(many, TT_FORK|TT_NEED_BASE), END_OF_TESTCASES };