From a8cee309e74584d5c8ccecc675bbecafb061627e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Fri, 30 Aug 2013 10:56:04 +0200
Subject: [PATCH] SIGCHLD handler: do not call callback when pvt data was
 freed

https://fedorahosted.org/sssd/ticket/1992
---
 src/providers/ipa/ipa_dyndns.c          | 10 +++++++++-
 src/providers/krb5/krb5_child_handler.c |  2 +-
 src/providers/ldap/sdap_child_helpers.c |  2 +-
 src/util/child_common.c                 | 24 +++++++++++++++++++++++-
 src/util/child_common.h                 |  8 +++++++-
 5 files changed, 41 insertions(+), 5 deletions(-)

diff --git a/src/providers/ipa/ipa_dyndns.c b/src/providers/ipa/ipa_dyndns.c
index 22a16a11a0c67700784d64a3b8b83d210f2f54f2..a3d358ec227b2ff85aca99c73e22da48764e5c35 100644
--- a/src/providers/ipa/ipa_dyndns.c
+++ b/src/providers/ipa/ipa_dyndns.c
@@ -860,6 +860,7 @@ failed:
 
 struct nsupdate_send_ctx {
     struct ipa_nsupdate_ctx *nsupdate_ctx;
+    struct sss_child_ctx_old *child_ctx;
     int child_status;
 };
 
@@ -1105,7 +1106,8 @@ fork_nsupdate_send(struct ipa_nsupdate_ctx *ctx)
 
         /* Set up SIGCHLD handler */
         ret = child_handler_setup(ctx->dyndns_ctx->ipa_ctx->id_ctx->sdap_id_ctx->be->ev,
-                                  pid, ipa_dyndns_child_handler, req);
+                                  pid, ipa_dyndns_child_handler, req,
+                                  &state->child_ctx);
         if (ret != EOK) {
             return NULL;
         }
@@ -1135,9 +1137,15 @@ static void ipa_dyndns_timeout(struct tevent_context *ev,
 {
     struct tevent_req *req =
             talloc_get_type(pvt, struct tevent_req);
+    struct nsupdate_send_ctx *state =
+            tevent_req_data(req, struct nsupdate_send_ctx);
 
     DEBUG(1, ("Timeout reached for dynamic DNS update\n"));
 
+    child_handler_destroy(state->child_ctx);
+    state->child_ctx = NULL;
+    state->child_status = ETIMEDOUT;
+
     tevent_req_error(req, ETIMEDOUT);
 }
 
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index e792db3f7a6ebe7857b1ec699bc37d52ae3cad5d..89ed0487ed77f964b16e892bf8f8a44ae5629b01 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -275,7 +275,7 @@ static errno_t fork_child(struct tevent_req *req)
         fd_nonblocking(state->io->read_from_child_fd);
         fd_nonblocking(state->io->write_to_child_fd);
 
-        ret = child_handler_setup(state->ev, pid, NULL, NULL);
+        ret = child_handler_setup(state->ev, pid, NULL, NULL, NULL);
         if (ret != EOK) {
             DEBUG(1, ("Could not set up child signal handler\n"));
             return ret;
diff --git a/src/providers/ldap/sdap_child_helpers.c b/src/providers/ldap/sdap_child_helpers.c
index f2412f9e59a8ee3c6d1651b6b96ce2fd36c4fdd1..6db0dbf23ec42c80e8335dbd6f70c8763fd88421 100644
--- a/src/providers/ldap/sdap_child_helpers.c
+++ b/src/providers/ldap/sdap_child_helpers.c
@@ -119,7 +119,7 @@ static errno_t sdap_fork_child(struct tevent_context *ev,
         fd_nonblocking(child->read_from_child_fd);
         fd_nonblocking(child->write_to_child_fd);
 
-        ret = child_handler_setup(ev, pid, NULL, NULL);
+        ret = child_handler_setup(ev, pid, NULL, NULL, NULL);
         if (ret != EOK) {
             return ret;
         }
diff --git a/src/util/child_common.c b/src/util/child_common.c
index 5f0b2659571731a70211df310648a5cc8aee241c..fe4684fb0344d5b9b5ac32555467f24ad5b0e5a3 100644
--- a/src/util/child_common.c
+++ b/src/util/child_common.c
@@ -260,7 +260,8 @@ struct sss_child_ctx_old {
 };
 
 int child_handler_setup(struct tevent_context *ev, int pid,
-                        sss_child_callback_t cb, void *pvt)
+                        sss_child_callback_t cb, void *pvt,
+                        struct sss_child_ctx_old **_child_ctx)
 {
     struct sss_child_ctx_old *child_ctx;
 
@@ -284,9 +285,30 @@ int child_handler_setup(struct tevent_context *ev, int pid,
     child_ctx->pvt = pvt;
 
     DEBUG(8, ("Signal handler set up for pid [%d]\n", pid));
+
+    if (_child_ctx != NULL) {
+        *_child_ctx = child_ctx;
+    }
+
     return EOK;
 }
 
+void child_handler_destroy(struct sss_child_ctx_old *ctx)
+{
+    errno_t ret;
+
+    /* We still want to wait for the child to finish, but the caller is not
+     * interested in the result anymore (e.g. timeout was reached). */
+    ctx->cb = NULL;
+    ctx->pvt = NULL;
+
+    ret = kill(ctx->pid, SIGKILL);
+    if (ret == -1) {
+        ret = errno;
+        DEBUG(SSSDBG_MINOR_FAILURE, ("kill failed [%d][%s].\n", ret, strerror(ret)));
+    }
+}
+
 /* Async communication with the child process via a pipe */
 
 struct write_pipe_state {
diff --git a/src/util/child_common.h b/src/util/child_common.h
index 237969f2ffb46b7e903551c67db754315427f05b..95865bb529b6b282a57b138d7a85ce93649faa2e 100644
--- a/src/util/child_common.h
+++ b/src/util/child_common.h
@@ -78,9 +78,15 @@ typedef void (*sss_child_callback_t)(int child_status,
                                      struct tevent_signal *sige,
                                      void *pvt);
 
+struct sss_child_ctx_old;
+
 /* Set up child termination signal handler */
 int child_handler_setup(struct tevent_context *ev, int pid,
-                        sss_child_callback_t cb, void *pvt);
+                        sss_child_callback_t cb, void *pvt,
+                        struct sss_child_ctx_old **_child_ctx);
+
+/* Destroy child termination signal handler */
+void child_handler_destroy(struct sss_child_ctx_old *ctx);
 
 /* Async communication with the child process via a pipe */
 struct tevent_req *write_pipe_send(TALLOC_CTX *mem_ctx,
-- 
1.7.11.7

