Coverage Report

Created: 2025-11-16 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/lib/tevent/tevent_wrapper.c
Line
Count
Source
1
/*
2
   Infrastructure for event context wrappers
3
4
   Copyright (C) Stefan Metzmacher 2014
5
6
     ** NOTE! The following LGPL license applies to the tevent
7
     ** library. This does NOT imply that all of Samba is released
8
     ** under the LGPL
9
10
   This library is free software; you can redistribute it and/or
11
   modify it under the terms of the GNU Lesser General Public
12
   License as published by the Free Software Foundation; either
13
   version 3 of the License, or (at your option) any later version.
14
15
   This library is distributed in the hope that it will be useful,
16
   but WITHOUT ANY WARRANTY; without even the implied warranty of
17
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
   Lesser General Public License for more details.
19
20
   You should have received a copy of the GNU Lesser General Public
21
   License along with this library; if not, see <http://www.gnu.org/licenses/>.
22
*/
23
24
#include "replace.h"
25
#ifdef HAVE_PTHREAD
26
#include "system/threads.h"
27
#endif
28
#define TEVENT_DEPRECATED 1
29
#include "tevent.h"
30
#include "tevent_internal.h"
31
#include "tevent_util.h"
32
33
static int tevent_wrapper_glue_context_init(struct tevent_context *ev)
34
0
{
35
0
  tevent_abort(ev, "tevent_wrapper_glue_context_init() called");
36
0
  errno = ENOSYS;
37
0
  return -1;
38
0
}
39
40
static struct tevent_fd *tevent_wrapper_glue_add_fd(struct tevent_context *ev,
41
                TALLOC_CTX *mem_ctx,
42
                int fd, uint16_t flags,
43
                tevent_fd_handler_t handler,
44
                void *private_data,
45
                const char *handler_name,
46
                const char *location)
47
0
{
48
0
  struct tevent_wrapper_glue *glue = ev->wrapper.glue;
49
0
  struct tevent_fd *fde = NULL;
50
51
0
  if (glue->destroyed) {
52
0
    tevent_abort(ev, "add_fd wrapper use after free");
53
0
    return NULL;
54
0
  }
55
56
0
  if (glue->main_ev == NULL) {
57
0
    errno = EINVAL;
58
0
    return NULL;
59
0
  }
60
61
0
  fde = _tevent_add_fd(glue->main_ev, mem_ctx, fd, flags,
62
0
           handler, private_data,
63
0
           handler_name, location);
64
0
  if (fde == NULL) {
65
0
    return NULL;
66
0
  }
67
68
0
  fde->wrapper = glue;
69
70
0
  return fde;
71
0
}
72
73
static struct tevent_timer *tevent_wrapper_glue_add_timer(struct tevent_context *ev,
74
                TALLOC_CTX *mem_ctx,
75
                struct timeval next_event,
76
                tevent_timer_handler_t handler,
77
                void *private_data,
78
                const char *handler_name,
79
                const char *location)
80
0
{
81
0
  struct tevent_wrapper_glue *glue = ev->wrapper.glue;
82
0
  struct tevent_timer *te = NULL;
83
84
0
  if (glue->destroyed) {
85
0
    tevent_abort(ev, "add_timer wrapper use after free");
86
0
    return NULL;
87
0
  }
88
89
0
  if (glue->main_ev == NULL) {
90
0
    errno = EINVAL;
91
0
    return NULL;
92
0
  }
93
94
0
  te = _tevent_add_timer(glue->main_ev, mem_ctx, next_event,
95
0
             handler, private_data,
96
0
             handler_name, location);
97
0
  if (te == NULL) {
98
0
    return NULL;
99
0
  }
100
101
0
  te->wrapper = glue;
102
103
0
  return te;
104
0
}
105
106
static void tevent_wrapper_glue_schedule_immediate(struct tevent_immediate *im,
107
               struct tevent_context *ev,
108
               tevent_immediate_handler_t handler,
109
               void *private_data,
110
               const char *handler_name,
111
               const char *location)
112
0
{
113
0
  struct tevent_wrapper_glue *glue = ev->wrapper.glue;
114
115
0
  if (glue->destroyed) {
116
0
    tevent_abort(ev, "scheduke_immediate wrapper use after free");
117
0
    return;
118
0
  }
119
120
0
  if (glue->main_ev == NULL) {
121
0
    tevent_abort(ev, location);
122
0
    errno = EINVAL;
123
0
    return;
124
0
  }
125
126
0
  _tevent_schedule_immediate(im, glue->main_ev,
127
0
           handler, private_data,
128
0
           handler_name, location);
129
130
0
  im->wrapper = glue;
131
132
0
  return;
133
0
}
134
135
static struct tevent_signal *tevent_wrapper_glue_add_signal(struct tevent_context *ev,
136
                  TALLOC_CTX *mem_ctx,
137
                  int signum, int sa_flags,
138
                  tevent_signal_handler_t handler,
139
                  void *private_data,
140
                  const char *handler_name,
141
                  const char *location)
142
0
{
143
0
  struct tevent_wrapper_glue *glue = ev->wrapper.glue;
144
0
  struct tevent_signal *se = NULL;
145
146
0
  if (glue->destroyed) {
147
0
    tevent_abort(ev, "add_signal wrapper use after free");
148
0
    return NULL;
149
0
  }
150
151
0
  if (glue->main_ev == NULL) {
152
0
    errno = EINVAL;
153
0
    return NULL;
154
0
  }
155
156
0
  se = _tevent_add_signal(glue->main_ev, mem_ctx,
157
0
        signum, sa_flags,
158
0
        handler, private_data,
159
0
        handler_name, location);
160
0
  if (se == NULL) {
161
0
    return NULL;
162
0
  }
163
164
0
  se->wrapper = glue;
165
166
0
  return se;
167
0
}
168
169
static int tevent_wrapper_glue_loop_once(struct tevent_context *ev, const char *location)
170
0
{
171
0
  tevent_abort(ev, "tevent_wrapper_glue_loop_once() called");
172
0
  errno = ENOSYS;
173
0
  return -1;
174
0
}
175
176
static int tevent_wrapper_glue_loop_wait(struct tevent_context *ev, const char *location)
177
0
{
178
0
  tevent_abort(ev, "tevent_wrapper_glue_loop_wait() called");
179
0
  errno = ENOSYS;
180
0
  return -1;
181
0
}
182
183
static const struct tevent_ops tevent_wrapper_glue_ops = {
184
  .context_init   = tevent_wrapper_glue_context_init,
185
  .add_fd     = tevent_wrapper_glue_add_fd,
186
  .set_fd_close_fn  = tevent_common_fd_set_close_fn,
187
  .get_fd_flags   = tevent_common_fd_get_flags,
188
  .set_fd_flags   = tevent_common_fd_set_flags,
189
  .add_timer    = tevent_wrapper_glue_add_timer,
190
  .schedule_immediate = tevent_wrapper_glue_schedule_immediate,
191
  .add_signal   = tevent_wrapper_glue_add_signal,
192
  .loop_once    = tevent_wrapper_glue_loop_once,
193
  .loop_wait    = tevent_wrapper_glue_loop_wait,
194
};
195
196
static int tevent_wrapper_context_destructor(struct tevent_context *wrap_ev)
197
0
{
198
0
  struct tevent_wrapper_glue *glue = wrap_ev->wrapper.glue;
199
0
  struct tevent_context *main_ev = NULL;
200
0
  struct tevent_fd *fd = NULL, *fn = NULL;
201
0
  struct tevent_timer *te = NULL, *tn = NULL;
202
0
  struct tevent_immediate *ie = NULL, *in = NULL;
203
0
  struct tevent_signal *se = NULL, *sn = NULL;
204
0
#ifdef HAVE_PTHREAD
205
0
  struct tevent_threaded_context *tctx = NULL, *tctxn = NULL;
206
0
#endif
207
208
0
  if (glue == NULL) {
209
0
    tevent_abort(wrap_ev,
210
0
      "tevent_wrapper_context_destructor() active on main");
211
    /* static checker support, return below is never reached */
212
0
    return -1;
213
0
  }
214
215
0
  if (glue->destroyed && glue->busy) {
216
0
    tevent_common_check_double_free(wrap_ev,
217
0
      "tevent_context wrapper double free");
218
0
  }
219
0
  glue->destroyed = true;
220
221
0
  if (glue->busy) {
222
0
    return -1;
223
0
  }
224
225
0
  main_ev = glue->main_ev;
226
0
  if (main_ev == NULL) {
227
0
    return 0;
228
0
  }
229
230
0
  TEVENT_DEBUG(wrap_ev, TEVENT_DEBUG_TRACE,
231
0
         "Destroying wrapper context %p \"%s\"\n",
232
0
         wrap_ev, talloc_get_name(glue->private_state));
233
234
0
  glue->main_ev = NULL;
235
0
  DLIST_REMOVE(main_ev->wrapper.list, glue);
236
237
0
#ifdef HAVE_PTHREAD
238
0
  for (tctx = main_ev->threaded_contexts; tctx != NULL; tctx = tctxn) {
239
0
    int ret;
240
241
0
    tctxn = tctx->next;
242
243
0
    if (tctx->event_ctx != glue->wrap_ev) {
244
0
      continue;
245
0
    }
246
247
0
    ret = pthread_mutex_lock(&tctx->event_ctx_mutex);
248
0
    if (ret != 0) {
249
0
      abort();
250
0
    }
251
252
    /*
253
     * Indicate to the thread that the tevent_context is
254
     * gone. The counterpart of this is in
255
     * _tevent_threaded_schedule_immediate, there we read
256
     * this under the threaded_context's mutex.
257
     */
258
259
0
    tctx->event_ctx = NULL;
260
261
0
    ret = pthread_mutex_unlock(&tctx->event_ctx_mutex);
262
0
    if (ret != 0) {
263
0
      abort();
264
0
    }
265
266
0
    DLIST_REMOVE(main_ev->threaded_contexts, tctx);
267
0
  }
268
0
#endif
269
270
0
  for (fd = main_ev->fd_events; fd; fd = fn) {
271
0
    fn = fd->next;
272
273
0
    if (fd->wrapper != glue) {
274
0
      continue;
275
0
    }
276
277
0
    tevent_fd_set_flags(fd, 0);
278
279
0
    tevent_common_fd_disarm(fd);
280
0
  }
281
282
0
  for (te = main_ev->timer_events; te; te = tn) {
283
0
    tn = te->next;
284
285
0
    if (te->wrapper != glue) {
286
0
      continue;
287
0
    }
288
289
0
    te->wrapper = NULL;
290
0
    te->event_ctx = NULL;
291
292
0
    if (main_ev->last_zero_timer == te) {
293
0
      main_ev->last_zero_timer = DLIST_PREV(te);
294
0
    }
295
0
    DLIST_REMOVE(main_ev->timer_events, te);
296
0
  }
297
298
0
  for (ie = main_ev->immediate_events; ie; ie = in) {
299
0
    in = ie->next;
300
301
0
    if (ie->wrapper != glue) {
302
0
      continue;
303
0
    }
304
305
0
    ie->wrapper = NULL;
306
0
    ie->event_ctx = NULL;
307
0
    ie->cancel_fn = NULL;
308
0
    DLIST_REMOVE(main_ev->immediate_events, ie);
309
0
  }
310
311
0
  for (se = main_ev->signal_events; se; se = sn) {
312
0
    sn = se->next;
313
314
0
    if (se->wrapper != glue) {
315
0
      continue;
316
0
    }
317
318
0
    se->wrapper = NULL;
319
0
    tevent_cleanup_pending_signal_handlers(se);
320
0
  }
321
322
0
  return 0;
323
0
}
324
325
struct tevent_context *_tevent_context_wrapper_create(struct tevent_context *main_ev,
326
            TALLOC_CTX *mem_ctx,
327
            const struct tevent_wrapper_ops *ops,
328
            void *pstate,
329
            size_t psize,
330
            const char *type,
331
            const char *location)
332
0
{
333
0
  void **ppstate = (void **)pstate;
334
0
  struct tevent_context *ev = NULL;
335
336
0
  if (main_ev->wrapper.glue != NULL) {
337
    /*
338
     * stacking of wrappers is not supported
339
     */
340
0
    tevent_debug(main_ev->wrapper.glue->main_ev, TEVENT_DEBUG_FATAL,
341
0
           "%s: %s() stacking not allowed\n",
342
0
           __func__, location);
343
0
    errno = EINVAL;
344
0
    return NULL;
345
0
  }
346
347
0
  if (main_ev->nesting.allowed) {
348
    /*
349
     * wrappers conflict with nesting
350
     */
351
0
    tevent_debug(main_ev, TEVENT_DEBUG_FATAL,
352
0
           "%s: %s() conflicts with nesting\n",
353
0
           __func__, location);
354
0
    errno = EINVAL;
355
0
    return NULL;
356
0
  }
357
358
0
  ev = talloc_zero(mem_ctx, struct tevent_context);
359
0
  if (ev == NULL) {
360
0
    return NULL;
361
0
  }
362
0
  ev->ops = &tevent_wrapper_glue_ops;
363
364
0
  ev->wrapper.glue = talloc_zero(ev, struct tevent_wrapper_glue);
365
0
  if (ev->wrapper.glue == NULL) {
366
0
    talloc_free(ev);
367
0
    return NULL;
368
0
  }
369
370
0
  talloc_set_destructor(ev, tevent_wrapper_context_destructor);
371
372
0
  ev->wrapper.glue->wrap_ev = ev;
373
0
  ev->wrapper.glue->main_ev = main_ev;
374
0
  ev->wrapper.glue->ops = ops;
375
0
  ev->wrapper.glue->private_state = talloc_zero_size(ev->wrapper.glue, psize);
376
0
  if (ev->wrapper.glue->private_state == NULL) {
377
0
    talloc_free(ev);
378
0
    return NULL;
379
0
  }
380
0
  talloc_set_name_const(ev->wrapper.glue->private_state, type);
381
382
0
  DLIST_ADD_END(main_ev->wrapper.list, ev->wrapper.glue);
383
384
0
  *ppstate = ev->wrapper.glue->private_state;
385
0
  return ev;
386
0
}
387
388
bool tevent_context_is_wrapper(struct tevent_context *ev)
389
0
{
390
0
  if (ev->wrapper.glue != NULL) {
391
0
    return true;
392
0
  }
393
394
0
  return false;
395
0
}
396
397
_PRIVATE_
398
struct tevent_context *tevent_wrapper_main_ev(struct tevent_context *ev)
399
0
{
400
0
  if (ev == NULL) {
401
0
    return NULL;
402
0
  }
403
404
0
  if (ev->wrapper.glue == NULL) {
405
0
    return ev;
406
0
  }
407
408
0
  return ev->wrapper.glue->main_ev;
409
0
}
410
411
/*
412
 * 32 stack elements should be more than enough
413
 *
414
 * e.g. Samba uses just 8 elements for [un]become_{root,user}()
415
 */
416
#define TEVENT_WRAPPER_STACK_SIZE 32
417
418
static struct tevent_wrapper_stack {
419
  const void *ev_ptr;
420
  const struct tevent_wrapper_glue *wrapper;
421
} wrapper_stack[TEVENT_WRAPPER_STACK_SIZE];
422
423
static size_t wrapper_stack_idx;
424
425
_PRIVATE_
426
void tevent_wrapper_push_use_internal(struct tevent_context *ev,
427
              struct tevent_wrapper_glue *wrapper)
428
0
{
429
  /*
430
   * ev and wrapper need to belong together!
431
   * It's also fine to only have a raw ev
432
   * without a wrapper.
433
   */
434
0
  if (unlikely(ev->wrapper.glue != wrapper)) {
435
0
    tevent_abort(ev, "tevent_wrapper_push_use_internal() invalid arguments");
436
0
    return;
437
0
  }
438
439
0
  if (wrapper != NULL) {
440
0
    if (unlikely(wrapper->busy)) {
441
0
      tevent_abort(ev, "wrapper already busy!");
442
0
      return;
443
0
    }
444
0
    wrapper->busy = true;
445
0
  }
446
447
0
  if (unlikely(wrapper_stack_idx >= TEVENT_WRAPPER_STACK_SIZE)) {
448
0
    tevent_abort(ev, "TEVENT_WRAPPER_STACK_SIZE overflow");
449
0
    return;
450
0
  }
451
452
0
  wrapper_stack[wrapper_stack_idx] = (struct tevent_wrapper_stack) {
453
0
    .ev_ptr = ev,
454
0
    .wrapper = wrapper,
455
0
  };
456
0
  wrapper_stack_idx++;
457
0
}
458
459
_PRIVATE_
460
void tevent_wrapper_pop_use_internal(const struct tevent_context *__ev_ptr,
461
             struct tevent_wrapper_glue *wrapper)
462
0
{
463
0
  struct tevent_context *main_ev = NULL;
464
465
  /*
466
   * Note that __ev_ptr might a a stale pointer and should not
467
   * be touched, we just compare the pointer value in order
468
   * to enforce the stack order.
469
   */
470
471
0
  if (wrapper != NULL) {
472
0
    main_ev = wrapper->main_ev;
473
0
  }
474
475
0
  if (unlikely(wrapper_stack_idx == 0)) {
476
0
    tevent_abort(main_ev, "tevent_wrapper stack already empty");
477
0
    return;
478
0
  }
479
0
  wrapper_stack_idx--;
480
481
0
  if (wrapper != NULL) {
482
0
    wrapper->busy = false;
483
0
  }
484
485
0
  if (wrapper_stack[wrapper_stack_idx].ev_ptr != __ev_ptr) {
486
0
    tevent_abort(main_ev, "tevent_wrapper_pop_use mismatch ev!");
487
0
    return;
488
0
  }
489
0
  if (wrapper_stack[wrapper_stack_idx].wrapper != wrapper) {
490
0
    tevent_abort(main_ev, "tevent_wrapper_pop_use mismatch wrap!");
491
0
    return;
492
0
  }
493
494
0
  if (wrapper == NULL) {
495
0
    return;
496
0
  }
497
498
0
  if (wrapper->destroyed) {
499
    /*
500
     * Notice that we can't use TALLOC_FREE()
501
     * here because wrapper is a talloc child
502
     * of wrapper->wrap_ev.
503
     */
504
0
    talloc_free(wrapper->wrap_ev);
505
0
  }
506
0
}
507
508
bool _tevent_context_push_use(struct tevent_context *ev,
509
            const char *location)
510
0
{
511
0
  bool ok;
512
513
0
  if (ev->wrapper.glue == NULL) {
514
0
    tevent_wrapper_push_use_internal(ev, NULL);
515
0
    return true;
516
0
  }
517
518
0
  if (ev->wrapper.glue->main_ev == NULL) {
519
0
    return false;
520
0
  }
521
522
0
  tevent_wrapper_push_use_internal(ev, ev->wrapper.glue);
523
0
  ok = ev->wrapper.glue->ops->before_use(ev->wrapper.glue->wrap_ev,
524
0
                 ev->wrapper.glue->private_state,
525
0
                 ev->wrapper.glue->main_ev,
526
0
                 location);
527
0
  if (!ok) {
528
0
    tevent_wrapper_pop_use_internal(ev, ev->wrapper.glue);
529
0
    return false;
530
0
  }
531
532
0
  return true;
533
0
}
534
535
void _tevent_context_pop_use(struct tevent_context *ev,
536
           const char *location)
537
0
{
538
0
  tevent_wrapper_pop_use_internal(ev, ev->wrapper.glue);
539
540
0
  if (ev->wrapper.glue == NULL) {
541
0
    return;
542
0
  }
543
544
0
  if (ev->wrapper.glue->main_ev == NULL) {
545
0
    return;
546
0
  }
547
548
0
  ev->wrapper.glue->ops->after_use(ev->wrapper.glue->wrap_ev,
549
0
           ev->wrapper.glue->private_state,
550
0
           ev->wrapper.glue->main_ev,
551
0
           location);
552
0
}
553
554
bool tevent_context_same_loop(struct tevent_context *ev1,
555
            struct tevent_context *ev2)
556
0
{
557
0
  struct tevent_context *main_ev1 = tevent_wrapper_main_ev(ev1);
558
0
  struct tevent_context *main_ev2 = tevent_wrapper_main_ev(ev2);
559
560
0
  if (main_ev1 == NULL) {
561
0
    return false;
562
0
  }
563
564
0
  if (main_ev1 == main_ev2) {
565
0
    return true;
566
0
  }
567
568
0
  return false;
569
0
}