1908a49
From bb102b392ff298ecd8a499f6ddc3904c59dcbba2 Mon Sep 17 00:00:00 2001
1908a49
From: Jakub Hrozek <jhrozek@redhat.com>
1908a49
Date: Wed, 18 Nov 2015 20:48:51 +0100
1908a49
Subject: [PATCH 104/104] FO: Use tevent_req_defer_callback() when notifying
1908a49
 callers
1908a49
MIME-Version: 1.0
1908a49
Content-Type: text/plain; charset=UTF-8
1908a49
Content-Transfer-Encoding: 8bit
1908a49
1908a49
If a fo_resolve_service callback would modify the server->common member
1908a49
in any way, for example by dereferencing the server and lowering the
1908a49
refcount to 0, which would free the common structure, then the next
1908a49
iteration of fo_resolve_service_done would access memory that was
1908a49
already gone.
1908a49
1908a49
Please see
1908a49
https://tevent.samba.org/group__tevent__request.html#ga09373077d0b39e321a196a86bfebf280
1908a49
for more details.
1908a49
1908a49
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
1908a49
(cherry picked from commit a92f68763a57b211a1bf6b80b6dd80c4a1aa2738)
1908a49
---
1908a49
 src/providers/fail_over.c      | 15 +++++++++++--
1908a49
 src/tests/cmocka/test_fo_srv.c | 49 +++++++++++++++++++++++++++++++++++++++---
1908a49
 2 files changed, 59 insertions(+), 5 deletions(-)
1908a49
1908a49
diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c
1908a49
index c60310d..0b99098 100644
1908a49
--- a/src/providers/fail_over.c
1908a49
+++ b/src/providers/fail_over.c
1908a49
@@ -131,6 +131,7 @@ struct resolve_service_request {
1908a49
 
1908a49
     struct server_common *server_common;
1908a49
     struct tevent_req *req;
1908a49
+    struct tevent_context *ev;
1908a49
 };
1908a49
 
1908a49
 struct status {
1908a49
@@ -940,7 +941,9 @@ resolve_service_request_destructor(struct resolve_service_request *request)
1908a49
 }
1908a49
 
1908a49
 static int
1908a49
-set_lookup_hook(struct fo_server *server, struct tevent_req *req)
1908a49
+set_lookup_hook(struct tevent_context *ev,
1908a49
+                struct fo_server *server,
1908a49
+                struct tevent_req *req)
1908a49
 {
1908a49
     struct resolve_service_request *request;
1908a49
 
1908a49
@@ -956,6 +959,7 @@ set_lookup_hook(struct fo_server *server, struct tevent_req *req)
1908a49
         talloc_free(request);
1908a49
         return ENOMEM;
1908a49
     }
1908a49
+    request->ev = ev;
1908a49
     request->req = req;
1908a49
     DLIST_ADD(server->common->request_list, request);
1908a49
     talloc_set_destructor(request, resolve_service_request_destructor);
1908a49
@@ -1142,7 +1146,7 @@ fo_resolve_service_server(struct tevent_req *req)
1908a49
     case SERVER_RESOLVING_NAME:
1908a49
         /* Name resolution is already under way. Just add ourselves into the
1908a49
          * waiting queue so we get notified after the operation is finished. */
1908a49
-        ret = set_lookup_hook(state->server, req);
1908a49
+        ret = set_lookup_hook(state->ev, state->server, req);
1908a49
         if (ret != EOK) {
1908a49
             tevent_req_error(req, ret);
1908a49
             return true;
1908a49
@@ -1194,6 +1198,13 @@ fo_resolve_service_done(struct tevent_req *subreq)
1908a49
     /* Take care of all requests for this server. */
1908a49
     while ((request = common->request_list) != NULL) {
1908a49
         DLIST_REMOVE(common->request_list, request);
1908a49
+
1908a49
+        /* If the request callback decresed refcount on the returned
1908a49
+         * server, we would have crashed as common would not be valid
1908a49
+         * anymore. Rather schedule the notify for next tev iteration
1908a49
+         */
1908a49
+        tevent_req_defer_callback(request->req, request->ev);
1908a49
+
1908a49
         if (ret) {
1908a49
             tevent_req_error(request->req, ret);
1908a49
         } else {
1908a49
diff --git a/src/tests/cmocka/test_fo_srv.c b/src/tests/cmocka/test_fo_srv.c
1908a49
index 67f86fb..a84ce43 100644
1908a49
--- a/src/tests/cmocka/test_fo_srv.c
1908a49
+++ b/src/tests/cmocka/test_fo_srv.c
1908a49
@@ -575,10 +575,10 @@ static void test_fo_srv_before(struct tevent_req *req)
1908a49
     fo_set_server_status(test_ctx->srv, SERVER_WORKING);
1908a49
 
1908a49
     /* Simulate changing the DNS environment. Change the host names */
1908a49
-    s1 = mock_ares_reply(test_ctx, "ldap2.sssd.com", 100, 2, 389);
1908a49
+    s1 = mock_ares_reply(test_ctx, "ldap1.sssd.com", 100, 2, 389);
1908a49
     assert_non_null(s1);
1908a49
 
1908a49
-    s2 = mock_ares_reply(test_ctx, "ldap3.sssd.com", 100, 1, 389);
1908a49
+    s2 = mock_ares_reply(test_ctx, "ldap2.sssd.com", 100, 1, 389);
1908a49
     assert_non_null(s2);
1908a49
 
1908a49
     s1->next = s2;
1908a49
@@ -596,12 +596,17 @@ static void test_fo_srv_before(struct tevent_req *req)
1908a49
     tevent_req_set_callback(req, test_fo_srv_after, test_ctx);
1908a49
 }
1908a49
 
1908a49
+static void test_fo_srv_after2(struct tevent_req *req);
1908a49
+
1908a49
 static void test_fo_srv_after(struct tevent_req *req)
1908a49
 {
1908a49
     struct test_fo_ctx *test_ctx = \
1908a49
         tevent_req_callback_data(req, struct test_fo_ctx);
1908a49
     struct fo_server *srv;
1908a49
     errno_t ret;
1908a49
+    struct ares_srv_reply *s1;
1908a49
+    struct ares_srv_reply *s2;
1908a49
+    char *dns_domain;
1908a49
 
1908a49
     ret = fo_resolve_service_recv(req, req, &srv;;
1908a49
     talloc_zfree(req);
1908a49
@@ -612,8 +617,46 @@ static void test_fo_srv_after(struct tevent_req *req)
1908a49
      */
1908a49
     fo_set_server_status(test_ctx->srv, SERVER_WORKING);
1908a49
 
1908a49
+    sleep(test_ctx->ttl + 1);
1908a49
+
1908a49
     /* Must be a different server now */
1908a49
-    check_server(test_ctx, srv, 389, "ldap3.sssd.com");
1908a49
+    check_server(test_ctx, srv, 389, "ldap2.sssd.com");
1908a49
+
1908a49
+    /* Simulate changing the DNS environment. Change the host names */
1908a49
+    s1 = mock_ares_reply(test_ctx, "ldap1.sssd.com", 100, 1, 389);
1908a49
+    assert_non_null(s1);
1908a49
+
1908a49
+    s2 = mock_ares_reply(test_ctx, "ldap2.sssd.com", 100, 2, 389);
1908a49
+    assert_non_null(s2);
1908a49
+
1908a49
+    s1->next = s2;
1908a49
+
1908a49
+    dns_domain = talloc_strdup(test_ctx, "sssd.com");
1908a49
+    assert_non_null(dns_domain);
1908a49
+
1908a49
+    mock_srv_results(s1, test_ctx->ttl, dns_domain);
1908a49
+    sleep(test_ctx->ttl + 1);
1908a49
+
1908a49
+    req = fo_resolve_service_send(test_ctx, test_ctx->ctx->ev,
1908a49
+                                  test_ctx->resolv, test_ctx->fo_ctx,
1908a49
+                                  test_ctx->fo_svc);
1908a49
+    assert_non_null(req);
1908a49
+    tevent_req_set_callback(req, test_fo_srv_after2, test_ctx);
1908a49
+}
1908a49
+
1908a49
+static void test_fo_srv_after2(struct tevent_req *req)
1908a49
+{
1908a49
+    struct test_fo_ctx *test_ctx = \
1908a49
+        tevent_req_callback_data(req, struct test_fo_ctx);
1908a49
+    struct fo_server *srv;
1908a49
+    errno_t ret;
1908a49
+
1908a49
+    ret = fo_resolve_service_recv(req, req, &srv;;
1908a49
+    talloc_zfree(req);
1908a49
+    assert_int_equal(ret, ERR_OK);
1908a49
+
1908a49
+    /* Must be a different server now */
1908a49
+    check_server(test_ctx, srv, 389, "ldap1.sssd.com");
1908a49
 
1908a49
     test_ctx->ctx->error = ERR_OK;
1908a49
     test_ctx->ctx->done = true;
1908a49
-- 
1908a49
2.5.0
1908a49