1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00
samba-mirror/source3/lib/poll_funcs/poll_funcs_tevent.c
Volker Lendecke c9cced0322 poll_funcs_tevent: Fix a valgrind error
The valgrind error happened in poll_funcs_tevent_handle_destructor in

  if (handle->ctx->refcount == 0)

handle->ctx was already gone at the time this destructor
was called. It happened because during messaging_init the
messaging_dgm subsystem was free'ed.  The unix_msg context and the
poll_funcs_tevent_context are children of messaging_dgm_context. How
was poll_funcs_tevent_handle_destructor still called? While working
on the new notify subsystem I've added some messaging_read_send
tevent_reqs, which register themselves with the dgm_context via
messaging_dgm_register_tevent_context. They were not gone yet. When
later these were also run down due to another talloc_free somewhere else,
this destructor referenced dead memory.

This code now protects the poll_funcs_tevent_handle against the
poll_funcs_tevent_context going away first with the loop

       for (h = ctx->handles; h != NULL; h = h->next) {
               h->ctx = NULL;
       }

in poll_funcs_tevent_context_destructor together with

       if (handle->ctx == NULL) {
               return 0;
       }

in poll_funcs_tevent_handle_destructor.

A side-effect of this code is that messaging_read_send request won't be
satisfied anymore after a reinit_after_fork kicked in. But I think this is the
right thing anyway: Every process should register its own message handlers
explicitly.

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
2014-10-24 01:40:16 +02:00

464 lines
11 KiB
C

/*
* Unix SMB/CIFS implementation.
* Copyright (C) Volker Lendecke 2013,2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "poll_funcs_tevent.h"
#include "tevent.h"
#include "system/select.h"
#include "dlinklist.h"
/*
* A poll_watch is asked for by the engine using this library via
* funcs->watch_new(). It represents interest in "fd" becoming readable or
* writable.
*/
struct poll_watch {
struct poll_funcs_state *state;
unsigned slot; /* index into state->watches[] */
int fd;
int events;
void (*callback)(struct poll_watch *w, int fd, short events,
void *private_data);
void *private_data;
};
struct poll_funcs_state {
/*
* "watches" is the array of all watches that we have handed out via
* funcs->watch_new(). The "watches" array can contain NULL pointers.
*/
unsigned num_watches;
struct poll_watch **watches;
/*
* "contexts is the array of tevent_contexts that serve
* "watches". "contexts" can contain NULL pointers.
*/
unsigned num_contexts;
struct poll_funcs_tevent_context **contexts;
};
struct poll_funcs_tevent_context {
struct poll_funcs_tevent_handle *handles;
struct poll_funcs_state *state;
unsigned slot; /* index into state->contexts[] */
struct tevent_context *ev;
struct tevent_fd **fdes; /* same indexes as state->watches[] */
};
/*
* poll_funcs_tevent_register() hands out a struct poll_funcs_tevent_handle as
* a void *. poll_funcs_tevent_register allows tevent_contexts to be
* registered multiple times, and we can't add a tevent_fd for the same fd's
* multiple times. So we have to share one poll_funcs_tevent_context.
*/
struct poll_funcs_tevent_handle {
struct poll_funcs_tevent_handle *prev, *next;
struct poll_funcs_tevent_context *ctx;
};
static uint16_t poll_events_to_tevent(short events)
{
uint16_t ret = 0;
if (events & POLLIN) {
ret |= TEVENT_FD_READ;
}
if (events & POLLOUT) {
ret |= TEVENT_FD_WRITE;
}
return ret;
}
static short tevent_to_poll_events(uint16_t flags)
{
short ret = 0;
if (flags & TEVENT_FD_READ) {
ret |= POLLIN;
}
if (flags & TEVENT_FD_WRITE) {
ret |= POLLOUT;
}
return ret;
}
/*
* Find or create a free slot in state->watches[]
*/
static bool poll_funcs_watch_find_slot(struct poll_funcs_state *state,
unsigned *slot)
{
struct poll_watch **watches;
unsigned i;
for (i=0; i<state->num_watches; i++) {
if (state->watches[i] == NULL) {
*slot = i;
return true;
}
}
watches = talloc_realloc(state, state->watches, struct poll_watch *,
state->num_watches + 1);
if (watches == NULL) {
return false;
}
watches[state->num_watches] = NULL;
state->watches = watches;
for (i=0; i<state->num_contexts; i++) {
struct tevent_fd **fdes;
struct poll_funcs_tevent_context *c = state->contexts[i];
if (c == NULL) {
continue;
}
fdes = talloc_realloc(c, c->fdes, struct tevent_fd *,
state->num_watches + 1);
if (fdes == NULL) {
return false;
}
c->fdes = fdes;
fdes[state->num_watches] = NULL;
}
*slot = state->num_watches;
state->num_watches += 1;
return true;
}
static void poll_funcs_fde_handler(struct tevent_context *ev,
struct tevent_fd *fde, uint16_t flags,
void *private_data);
static int poll_watch_destructor(struct poll_watch *w);
static struct poll_watch *tevent_watch_new(
const struct poll_funcs *funcs, int fd, short events,
void (*callback)(struct poll_watch *w, int fd, short events,
void *private_data),
void *private_data)
{
struct poll_funcs_state *state = talloc_get_type_abort(
funcs->private_data, struct poll_funcs_state);
struct poll_watch *w;
unsigned i, slot;
if (!poll_funcs_watch_find_slot(state, &slot)) {
return NULL;
}
w = talloc(state->watches, struct poll_watch);
if (w == NULL) {
return NULL;
}
w->state = state;
w->slot = slot;
w->fd = fd;
w->events = poll_events_to_tevent(events);
w->fd = fd;
w->callback = callback;
w->private_data = private_data;
state->watches[slot] = w;
talloc_set_destructor(w, poll_watch_destructor);
for (i=0; i<state->num_contexts; i++) {
struct poll_funcs_tevent_context *c = state->contexts[i];
if (c == NULL) {
continue;
}
c->fdes[slot] = tevent_add_fd(c->ev, c->fdes, w->fd, w->events,
poll_funcs_fde_handler, w);
if (c->fdes[slot] == NULL) {
goto fail;
}
}
return w;
fail:
TALLOC_FREE(w);
return NULL;
}
static int poll_watch_destructor(struct poll_watch *w)
{
struct poll_funcs_state *state = w->state;
unsigned slot = w->slot;
unsigned i;
TALLOC_FREE(state->watches[slot]);
for (i=0; i<state->num_contexts; i++) {
struct poll_funcs_tevent_context *c = state->contexts[i];
if (c == NULL) {
continue;
}
TALLOC_FREE(c->fdes[slot]);
}
return 0;
}
static void tevent_watch_update(struct poll_watch *w, short events)
{
struct poll_funcs_state *state = w->state;
unsigned slot = w->slot;
unsigned i;
w->events = poll_events_to_tevent(events);
for (i=0; i<state->num_contexts; i++) {
struct poll_funcs_tevent_context *c = state->contexts[i];
if (c == NULL) {
continue;
}
tevent_fd_set_flags(c->fdes[slot], w->events);
}
}
static short tevent_watch_get_events(struct poll_watch *w)
{
return tevent_to_poll_events(w->events);
}
static void tevent_watch_free(struct poll_watch *w)
{
TALLOC_FREE(w);
}
static struct poll_timeout *tevent_timeout_new(
const struct poll_funcs *funcs, const struct timeval *tv,
void (*callback)(struct poll_timeout *t, void *private_data),
void *private_data)
{
/* not implemented yet */
return NULL;
}
static void tevent_timeout_update(struct poll_timeout *t,
const struct timespec *ts)
{
return;
}
static void tevent_timeout_free(struct poll_timeout *t)
{
return;
}
static int poll_funcs_state_destructor(struct poll_funcs_state *state);
struct poll_funcs *poll_funcs_init_tevent(TALLOC_CTX *mem_ctx)
{
struct poll_funcs *f;
struct poll_funcs_state *state;
f = talloc(mem_ctx, struct poll_funcs);
if (f == NULL) {
return NULL;
}
state = talloc_zero(f, struct poll_funcs_state);
if (state == NULL) {
TALLOC_FREE(f);
return NULL;
}
talloc_set_destructor(state, poll_funcs_state_destructor);
f->watch_new = tevent_watch_new;
f->watch_update = tevent_watch_update;
f->watch_get_events = tevent_watch_get_events;
f->watch_free = tevent_watch_free;
f->timeout_new = tevent_timeout_new;
f->timeout_update = tevent_timeout_update;
f->timeout_free = tevent_timeout_free;
f->private_data = state;
return f;
}
static int poll_funcs_state_destructor(struct poll_funcs_state *state)
{
unsigned i;
/*
* Make sure the watches are cleared before the contexts. The watches
* have destructors attached to them that clean up the fde's
*/
for (i=0; i<state->num_watches; i++) {
TALLOC_FREE(state->watches[i]);
}
return 0;
}
/*
* Find or create a free slot in state->contexts[]
*/
static bool poll_funcs_context_slot_find(struct poll_funcs_state *state,
struct tevent_context *ev,
unsigned *slot)
{
struct poll_funcs_tevent_context **contexts;
unsigned i;
for (i=0; i<state->num_contexts; i++) {
struct poll_funcs_tevent_context *ctx = state->contexts[i];
if ((ctx == NULL) || (ctx->ev == ev)) {
*slot = i;
return true;
}
}
contexts = talloc_realloc(state, state->contexts,
struct poll_funcs_tevent_context *,
state->num_contexts + 1);
if (contexts == NULL) {
return false;
}
state->contexts = contexts;
state->contexts[state->num_contexts] = NULL;
*slot = state->num_contexts;
state->num_contexts += 1;
return true;
}
static int poll_funcs_tevent_context_destructor(
struct poll_funcs_tevent_context *ctx);
static struct poll_funcs_tevent_context *poll_funcs_tevent_context_new(
TALLOC_CTX *mem_ctx, struct poll_funcs_state *state,
struct tevent_context *ev, unsigned slot)
{
struct poll_funcs_tevent_context *ctx;
unsigned i;
ctx = talloc(mem_ctx, struct poll_funcs_tevent_context);
if (ctx == NULL) {
return NULL;
}
ctx->handles = NULL;
ctx->state = state;
ctx->ev = ev;
ctx->slot = slot;
ctx->fdes = talloc_array(ctx, struct tevent_fd *, state->num_watches);
if (ctx->fdes == NULL) {
goto fail;
}
for (i=0; i<state->num_watches; i++) {
struct poll_watch *w = state->watches[i];
if (w == NULL) {
ctx->fdes[i] = NULL;
continue;
}
ctx->fdes[i] = tevent_add_fd(ev, ctx->fdes, w->fd, w->events,
poll_funcs_fde_handler, w);
if (ctx->fdes[i] == NULL) {
goto fail;
}
}
talloc_set_destructor(ctx, poll_funcs_tevent_context_destructor);
return ctx;
fail:
TALLOC_FREE(ctx);
return NULL;
}
static int poll_funcs_tevent_context_destructor(
struct poll_funcs_tevent_context *ctx)
{
struct poll_funcs_tevent_handle *h;
ctx->state->contexts[ctx->slot] = NULL;
for (h = ctx->handles; h != NULL; h = h->next) {
h->ctx = NULL;
}
return 0;
}
static void poll_funcs_fde_handler(struct tevent_context *ev,
struct tevent_fd *fde, uint16_t flags,
void *private_data)
{
struct poll_watch *w = talloc_get_type_abort(
private_data, struct poll_watch);
short events = tevent_to_poll_events(flags);
w->callback(w, w->fd, events, w->private_data);
}
static int poll_funcs_tevent_handle_destructor(
struct poll_funcs_tevent_handle *handle);
void *poll_funcs_tevent_register(TALLOC_CTX *mem_ctx, struct poll_funcs *f,
struct tevent_context *ev)
{
struct poll_funcs_state *state = talloc_get_type_abort(
f->private_data, struct poll_funcs_state);
struct poll_funcs_tevent_handle *handle;
unsigned slot;
handle = talloc(mem_ctx, struct poll_funcs_tevent_handle);
if (handle == NULL) {
return NULL;
}
if (!poll_funcs_context_slot_find(state, ev, &slot)) {
goto fail;
}
if (state->contexts[slot] == NULL) {
state->contexts[slot] = poll_funcs_tevent_context_new(
state->contexts, state, ev, slot);
if (state->contexts[slot] == NULL) {
goto fail;
}
}
handle->ctx = state->contexts[slot];
DLIST_ADD(handle->ctx->handles, handle);
talloc_set_destructor(handle, poll_funcs_tevent_handle_destructor);
return handle;
fail:
TALLOC_FREE(handle);
return NULL;
}
static int poll_funcs_tevent_handle_destructor(
struct poll_funcs_tevent_handle *handle)
{
if (handle->ctx == NULL) {
return 0;
}
if (handle->ctx->handles == NULL) {
abort();
}
DLIST_REMOVE(handle->ctx->handles, handle);
if (handle->ctx->handles == NULL) {
TALLOC_FREE(handle->ctx);
}
return 0;
}