From f088353d37b433af7b979a17871233cccddf7aca Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Mon, 9 Mar 2009 18:04:38 -0400 Subject: Fix potential segfaults using freed memory. In some code paths ltdb_context was still referenced even after we were returned an error by one of the callbacks. Because the interface assumes that once an error is returned the ldb_request may be freed, and because the ltdb_context was allocated as a child of the request, this might cause access to freed memory. Allocate the ltdb_context on ldb, and keep track of what's going on with the request by adding a spy children on it. This way even if the request is freed before the ltdb_callback is called, we will safely free the ctx and just quietly return. --- source4/lib/ldb/ldb_tdb/ldb_index.c | 2 +- source4/lib/ldb/ldb_tdb/ldb_search.c | 6 ++--- source4/lib/ldb/ldb_tdb/ldb_tdb.c | 49 ++++++++++++++++++++++++++++++++---- source4/lib/ldb/ldb_tdb/ldb_tdb.h | 7 +++++- 4 files changed, 54 insertions(+), 10 deletions(-) (limited to 'source4/lib/ldb/ldb_tdb') diff --git a/source4/lib/ldb/ldb_tdb/ldb_index.c b/source4/lib/ldb/ldb_tdb/ldb_index.c index ad27c9a9a9..c99c2936d8 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_index.c +++ b/source4/lib/ldb/ldb_tdb/ldb_index.c @@ -1055,7 +1055,7 @@ static int ltdb_index_filter(const struct dn_list *dn_list, ret = ldb_module_send_entry(ac->req, msg, NULL); if (ret != LDB_SUCCESS) { - ac->callback_failed = true; + ac->request_terminated = true; return ret; } } diff --git a/source4/lib/ldb/ldb_tdb/ldb_search.c b/source4/lib/ldb/ldb_tdb/ldb_search.c index 0f595267fc..d395c28f28 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_search.c +++ b/source4/lib/ldb/ldb_tdb/ldb_search.c @@ -424,10 +424,10 @@ static int search_func(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, voi ret = ldb_module_send_entry(ac->req, msg, NULL); if (ret != LDB_SUCCESS) { - ac->callback_failed = true; + ac->request_terminated = true; /* the callback failed, abort the operation */ return -1; - } + } return 0; } @@ -544,7 +544,7 @@ int ltdb_search(struct ltdb_context *ctx) /* Check if we got just a normal error. * In that case proceed to a full search unless we got a * callback error */ - if ( ! ctx->callback_failed && ret != LDB_SUCCESS) { + if ( ! ctx->request_terminated && ret != LDB_SUCCESS) { /* Not indexed, so we need to do a full scan */ ret = ltdb_search_full(ctx); if (ret != LDB_SUCCESS) { diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.c b/source4/lib/ldb/ldb_tdb/ldb_tdb.c index 24ec06ea32..d38cb828bb 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_tdb.c +++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.c @@ -1019,7 +1019,15 @@ static void ltdb_timeout(struct tevent_context *ev, struct ltdb_context *ctx; ctx = talloc_get_type(private_data, struct ltdb_context); - ltdb_request_done(ctx, LDB_ERR_TIME_LIMIT_EXCEEDED); + if (!ctx->request_terminated) { + /* neutralize the spy */ + ctx->spy->ctx = NULL; + + /* request is done now */ + ltdb_request_done(ctx, LDB_ERR_TIME_LIMIT_EXCEEDED); + } + + talloc_free(ctx); } static void ltdb_request_extended_done(struct ltdb_context *ctx, @@ -1078,6 +1086,11 @@ static void ltdb_callback(struct tevent_context *ev, ctx = talloc_get_type(private_data, struct ltdb_context); + if (!ctx->request_terminated) { + /* neutralize the spy */ + ctx->spy->ctx = NULL; + } else goto done; + switch (ctx->req->operation) { case LDB_SEARCH: ret = ltdb_search(ctx); @@ -1102,11 +1115,24 @@ static void ltdb_callback(struct tevent_context *ev, ret = LDB_ERR_UNWILLING_TO_PERFORM; } - if (!ctx->callback_failed) { - /* Once we are done, we do not need timeout events */ - talloc_free(ctx->timeout_event); + if (!ctx->request_terminated) { + /* request is done now */ ltdb_request_done(ctx, ret); } + +done: + talloc_free(ctx); +} + +static int ltdb_request_destructor(void *ptr) +{ + struct ltdb_req_spy *spy = talloc_get_type(ptr, struct ltdb_req_spy); + + if (spy->ctx != NULL) { + spy->ctx->request_terminated = true; + } + + return 0; } static int ltdb_handle_request(struct ldb_module *module, @@ -1131,7 +1157,7 @@ static int ltdb_handle_request(struct ldb_module *module, ev = ldb_get_event_context(ldb); - ac = talloc_zero(req, struct ltdb_context); + ac = talloc_zero(ldb, struct ltdb_context); if (ac == NULL) { ldb_set_errstring(ldb, "Out of Memory"); return LDB_ERR_OPERATIONS_ERROR; @@ -1144,15 +1170,28 @@ static int ltdb_handle_request(struct ldb_module *module, tv.tv_usec = 0; te = tevent_add_timer(ev, ac, tv, ltdb_callback, ac); if (NULL == te) { + talloc_free(ac); return LDB_ERR_OPERATIONS_ERROR; } tv.tv_sec = req->starttime + req->timeout; ac->timeout_event = tevent_add_timer(ev, ac, tv, ltdb_timeout, ac); if (NULL == ac->timeout_event) { + talloc_free(ac); return LDB_ERR_OPERATIONS_ERROR; } + /* set a spy so that we do not try to use the request context + * if it is freed before ltdb_callback fires */ + ac->spy = talloc(req, struct ltdb_req_spy); + if (NULL == ac->spy) { + talloc_free(ac); + return LDB_ERR_OPERATIONS_ERROR; + } + ac->spy->ctx = ac; + + talloc_set_destructor((TALLOC_CTX *)ac->spy, ltdb_request_destructor); + return LDB_SUCCESS; } diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.h b/source4/lib/ldb/ldb_tdb/ldb_tdb.h index 0a06cdb1b0..5a1c8fee2d 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_tdb.h +++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.h @@ -36,11 +36,16 @@ struct ltdb_private { the async local context holds also internal search state during a full db search */ +struct ltdb_req_spy { + struct ltdb_context *ctx; +}; + struct ltdb_context { struct ldb_module *module; struct ldb_request *req; - bool callback_failed; + bool request_terminated; + struct ltdb_req_spy *spy; /* search stuff */ const struct ldb_parse_tree *tree; -- cgit