xref: /netbsd-src/external/bsd/ntp/dist/sntp/libevent/test/regress_finalize.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1 /*	$NetBSD: regress_finalize.c,v 1.7 2024/08/18 20:47:23 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 2013 Niels Provos and Nick Mathewson
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "event2/event-config.h"
30 #include "evconfig-private.h"
31 #include "tinytest.h"
32 #include "tinytest_macros.h"
33 #include <stdlib.h>
34 
35 #include "event2/event.h"
36 #include "event2/util.h"
37 #include "event-internal.h"
38 #include "defer-internal.h"
39 
40 #include "regress.h"
41 #include "regress_thread.h"
42 
43 static void
44 timer_callback(evutil_socket_t fd, short what, void *arg)
45 {
46 	int *int_arg = arg;
47 	*int_arg += 1;
48 	(void)fd;
49 	(void)what;
50 }
51 static void
52 simple_callback(struct event_callback *evcb, void *arg)
53 {
54 	int *int_arg = arg;
55         *int_arg += 1;
56 	(void)evcb;
57 }
58 static void
59 event_finalize_callback_1(struct event *ev, void *arg)
60 {
61 	int *int_arg = arg;
62         *int_arg += 100;
63 	(void)ev;
64 }
65 static void
66 callback_finalize_callback_1(struct event_callback *evcb, void *arg)
67 {
68 	int *int_arg = arg;
69         *int_arg += 100;
70 	(void)evcb;
71 }
72 
73 
74 static void
75 test_fin_cb_invoked(void *arg)
76 {
77 	struct basic_test_data *data = arg;
78 	struct event_base *base = data->base;
79 
80 	struct event *ev;
81 	struct event ev2;
82 	struct event_callback evcb;
83 	int cb_called = 0;
84 	int ev_called = 0;
85 
86 	const struct timeval ten_sec = {10,0};
87 
88 	event_deferred_cb_init_(&evcb, 0, simple_callback, &cb_called);
89 	ev = evtimer_new(base, timer_callback, &ev_called);
90 	/* Just finalize them; don't bother adding. */
91 	event_free_finalize(0, ev, event_finalize_callback_1);
92 	event_callback_finalize_(base, 0, &evcb, callback_finalize_callback_1);
93 
94 	event_base_dispatch(base);
95 
96 	tt_int_op(cb_called, ==, 100);
97 	tt_int_op(ev_called, ==, 100);
98 
99 	ev_called = cb_called = 0;
100 	event_base_assert_ok_(base);
101 
102 	/* Now try it when they're active. (actually, don't finalize: make
103 	 * sure activation can happen! */
104 	ev = evtimer_new(base, timer_callback, &ev_called);
105 	event_deferred_cb_init_(&evcb, 0, simple_callback, &cb_called);
106 
107 	event_active(ev, EV_TIMEOUT, 1);
108 	event_callback_activate_(base, &evcb);
109 
110 	event_base_dispatch(base);
111 	tt_int_op(cb_called, ==, 1);
112 	tt_int_op(ev_called, ==, 1);
113 
114 	ev_called = cb_called = 0;
115 	event_base_assert_ok_(base);
116 
117 	/* Great, it worked. Now activate and finalize and make sure only
118 	 * finalizing happens. */
119 	event_active(ev, EV_TIMEOUT, 1);
120 	event_callback_activate_(base, &evcb);
121 	event_free_finalize(0, ev, event_finalize_callback_1);
122 	event_callback_finalize_(base, 0, &evcb, callback_finalize_callback_1);
123 
124 	event_base_dispatch(base);
125 	tt_int_op(cb_called, ==, 100);
126 	tt_int_op(ev_called, ==, 100);
127 
128 	ev_called = 0;
129 
130 	event_base_assert_ok_(base);
131 
132 	/* Okay, now add but don't have it become active, and make sure *that*
133 	 * works. */
134 	ev = evtimer_new(base, timer_callback, &ev_called);
135 	event_add(ev, &ten_sec);
136 	event_free_finalize(0, ev, event_finalize_callback_1);
137 
138 	event_base_dispatch(base);
139 	tt_int_op(ev_called, ==, 100);
140 
141 	ev_called = 0;
142 	event_base_assert_ok_(base);
143 
144 	/* Now try adding and deleting after finalizing. */
145 	ev = evtimer_new(base, timer_callback, &ev_called);
146 	evtimer_assign(&ev2, base, timer_callback, &ev_called);
147 	event_add(ev, &ten_sec);
148 	event_free_finalize(0, ev, event_finalize_callback_1);
149 	event_finalize(0, &ev2, event_finalize_callback_1);
150 
151 	event_add(&ev2, &ten_sec);
152 	event_del(ev);
153 	event_active(&ev2, EV_TIMEOUT, 1);
154 
155 	event_base_dispatch(base);
156 	tt_int_op(ev_called, ==, 200);
157 
158 	event_base_assert_ok_(base);
159 
160 end:
161 	;
162 }
163 
164 #ifndef EVENT__DISABLE_MM_REPLACEMENT
165 static void *
166 tfff_malloc(size_t n)
167 {
168 	return malloc(n);
169 }
170 static void *tfff_p1=NULL, *tfff_p2=NULL;
171 static int tfff_p1_freed=0, tfff_p2_freed=0;
172 static void
173 tfff_free(void *p)
174 {
175 	if (! p)
176 		return;
177 	if (p == tfff_p1)
178 		++tfff_p1_freed;
179 	if (p == tfff_p2)
180 		++tfff_p2_freed;
181 	free(p);
182 }
183 static void *
184 tfff_realloc(void *p, size_t sz)
185 {
186 	return realloc(p,sz);
187 }
188 #endif
189 
190 static void
191 test_fin_free_finalize(void *arg)
192 {
193 #ifdef EVENT__DISABLE_MM_REPLACEMENT
194 	tinytest_set_test_skipped_();
195 #else
196 	struct event_base *base = NULL;
197 	struct event *ev, *ev2;
198 	int ev_called = 0;
199 	int ev2_called = 0;
200 
201 	(void)arg;
202 
203 	event_set_mem_functions(tfff_malloc, tfff_realloc, tfff_free);
204 
205 	base = event_base_new();
206 	tt_assert(base);
207 
208 	ev = evtimer_new(base, timer_callback, &ev_called);
209 	ev2 = evtimer_new(base, timer_callback, &ev2_called);
210 	tfff_p1 = ev;
211 	tfff_p2 = ev2;
212 	event_free_finalize(0, ev, event_finalize_callback_1);
213 	event_finalize(0, ev2, event_finalize_callback_1);
214 
215 	event_base_dispatch(base);
216 
217 	tt_int_op(ev_called, ==, 100);
218 	tt_int_op(ev2_called, ==, 100);
219 
220 	event_base_assert_ok_(base);
221 	tt_int_op(tfff_p1_freed, ==, 1);
222 	tt_int_op(tfff_p2_freed, ==, 0);
223 
224 	event_free(ev2);
225 
226 end:
227 	if (base)
228 		event_base_free(base);
229 #endif
230 }
231 
232 /* For test_fin_within_cb */
233 struct event_and_count {
234 	struct event *ev;
235 	struct event *ev2;
236 	int count;
237 };
238 static void
239 event_finalize_callback_2(struct event *ev, void *arg)
240 {
241 	struct event_and_count *evc = arg;
242 	evc->count += 100;
243 	event_free(ev);
244 }
245 static void
246 timer_callback_2(evutil_socket_t fd, short what, void *arg)
247 {
248 	struct event_and_count *evc = arg;
249 	event_finalize(0, evc->ev, event_finalize_callback_2);
250 	event_finalize(0, evc->ev2, event_finalize_callback_2);
251 	++ evc->count;
252 	(void)fd;
253 	(void)what;
254 }
255 
256 static void
257 test_fin_within_cb(void *arg)
258 {
259 	struct basic_test_data *data = arg;
260 	struct event_base *base = data->base;
261 
262 	struct event_and_count evc1, evc2;
263 	evc1.count = evc2.count = 0;
264 	evc2.ev2 = evc1.ev = evtimer_new(base, timer_callback_2, &evc1);
265 	evc1.ev2 = evc2.ev = evtimer_new(base, timer_callback_2, &evc2);
266 
267 	/* Activate both.  The first one will have its callback run, which
268 	 * will finalize both of them, preventing the second one's callback
269 	 * from running. */
270 	event_active(evc1.ev, EV_TIMEOUT, 1);
271 	event_active(evc2.ev, EV_TIMEOUT, 1);
272 
273 	event_base_dispatch(base);
274 	tt_int_op(evc1.count, ==, 101);
275 	tt_int_op(evc2.count, ==, 100);
276 
277 	event_base_assert_ok_(base);
278 	/* Now try with EV_PERSIST events. */
279 	evc1.count = evc2.count = 0;
280 	evc2.ev2 = evc1.ev = event_new(base, -1, EV_PERSIST, timer_callback_2, &evc1);
281 	evc1.ev2 = evc2.ev = event_new(base, -1, EV_PERSIST, timer_callback_2, &evc2);
282 
283 	event_active(evc1.ev, EV_TIMEOUT, 1);
284 	event_active(evc2.ev, EV_TIMEOUT, 1);
285 
286 	event_base_dispatch(base);
287 	tt_int_op(evc1.count, ==, 101);
288 	tt_int_op(evc2.count, ==, 100);
289 
290 	event_base_assert_ok_(base);
291 end:
292 	;
293 }
294 
295 static void
296 event_finalize_callback_free(struct event *ev, void *arg)
297 {
298 	struct event_base *base = arg;
299 	int err;
300 	if (base) {
301 		err = event_assign(ev, base, -1, EV_TIMEOUT, NULL, NULL);
302 		tt_int_op(err, ==, 0);
303 		test_ok += 1;
304 	} else {
305 		free(ev);
306 		test_ok += 1;
307 	}
308 
309 end:
310 	;
311 }
312 static void
313 test_fin_debug_use_after_free(void *arg)
314 {
315 	struct basic_test_data *data = arg;
316 	struct event_base *base = data->base;
317 	struct event *ev;
318 
319 	tt_ptr_op(ev = event_new(base, -1, EV_TIMEOUT, NULL, base), !=, NULL);
320 	tt_int_op(event_add(ev, NULL), ==, 0);
321 	tt_int_op(event_finalize(0, ev, event_finalize_callback_free), ==, 0);
322 
323 	// Dispatch base to trigger callbacks
324 	event_base_dispatch(base);
325 	event_base_assert_ok_(base);
326 	tt_int_op(test_ok, ==, 1);
327 
328 	// Now add again, since we did event_assign in event_finalize_callback_free
329 	// This used to fail in event_debug_assert_is_setup_
330 	tt_int_op(event_add(ev, NULL), ==, 0);
331 
332 	// Finalize and dispatch again
333 	tt_int_op(event_finalize(0, ev, event_finalize_callback_free), ==, 0);
334 	event_base_dispatch(base);
335 	event_base_assert_ok_(base);
336 	tt_int_op(test_ok, ==, 2);
337 
338 end:
339 	;
340 }
341 
342 #if 0
343 static void
344 timer_callback_3(evutil_socket_t *fd, short what, void *arg)
345 {
346 	(void)fd;
347 	(void)what;
348 
349 }
350 static void
351 test_fin_many(void *arg)
352 {
353 	struct basic_test_data *data = arg;
354 	struct event_base *base = data->base;
355 
356 	struct event *ev1, *ev2;
357 	struct event_callback evcb1, evcb2;
358 	int ev1_count = 0, ev2_count = 0;
359 	int evcb1_count = 0, evcb2_count = 0;
360 	struct event_callback *array[4];
361 
362 	int n;
363 
364 	/* First attempt: call finalize_many with no events running */
365 	ev1 = evtimer_new(base, timer_callback, &ev1_count);
366 	ev1 = evtimer_new(base, timer_callback, &ev2_count);
367 	event_deferred_cb_init_(&evcb1, 0, simple_callback, &evcb1_called);
368 	event_deferred_cb_init_(&evcb2, 0, simple_callback, &evcb2_called);
369 	array[0] = &ev1->ev_evcallback;
370 	array[1] = &ev2->ev_evcallback;
371 	array[2] = &evcb1;
372 	array[3] = &evcb2;
373 
374 
375 
376 	n = event_callback_finalize_many(base, 4, array,
377 	    callback_finalize_callback_1);
378 
379 }
380 #endif
381 
382 
383 #define TEST(name, flags)					\
384 	{ #name, test_fin_##name, (flags), &basic_setup, NULL }
385 
386 struct testcase_t finalize_testcases[] = {
387 
388 	TEST(cb_invoked, TT_FORK|TT_NEED_BASE),
389 	TEST(free_finalize, TT_FORK),
390 	TEST(within_cb, TT_FORK|TT_NEED_BASE),
391 	TEST(debug_use_after_free, TT_FORK|TT_NEED_BASE|TT_ENABLE_DEBUG_MODE),
392 //	TEST(many, TT_FORK|TT_NEED_BASE),
393 
394 
395 	END_OF_TESTCASES
396 };
397 
398