Blame SOURCES/bz1473695-dont-crash-on-shm-truncate.patch

b5ede7
diff --git a/include/qb/qbipcs.h b/include/qb/qbipcs.h
b5ede7
index 55c0f81..7b4daa7 100644
b5ede7
--- a/include/qb/qbipcs.h
b5ede7
+++ b/include/qb/qbipcs.h
b5ede7
@@ -142,6 +142,10 @@ typedef void (*qb_ipcs_connection_created_fn) (qb_ipcs_connection_t *c);
b5ede7
  * successfully created.
b5ede7
  * @note if you return anything but 0 this function will be
b5ede7
  * repeativily called (until 0 is returned).
b5ede7
+ *
b5ede7
+ * With SHM connections libqb will briefly trap SIGBUS during the
b5ede7
+ * disconnect process to guard against server crashes if the mapped
b5ede7
+ * file is truncated. The signal will be restored afterwards.
b5ede7
  */
b5ede7
 typedef int32_t (*qb_ipcs_connection_closed_fn) (qb_ipcs_connection_t *c);
b5ede7
 
b5ede7
diff --git a/lib/ipc_int.h b/lib/ipc_int.h
b5ede7
index 67fc444..9cd06cf 100644
b5ede7
--- a/lib/ipc_int.h
b5ede7
+++ b/lib/ipc_int.h
b5ede7
@@ -92,6 +92,7 @@ struct qb_ipcc_connection {
b5ede7
 	char name[NAME_MAX];
b5ede7
 	int32_t needs_sock_for_poll;
b5ede7
 	gid_t egid;
b5ede7
+	pid_t server_pid;
b5ede7
 	struct qb_ipc_one_way setup;
b5ede7
 	struct qb_ipc_one_way request;
b5ede7
 	struct qb_ipc_one_way response;
b5ede7
diff --git a/lib/ipc_setup.c b/lib/ipc_setup.c
b5ede7
index 57d755b..0e16964 100644
b5ede7
--- a/lib/ipc_setup.c
b5ede7
+++ b/lib/ipc_setup.c
b5ede7
@@ -494,6 +494,7 @@ qb_ipcc_us_setup_connect(struct qb_ipcc_connection *c,
b5ede7
 
b5ede7
 	qb_ipc_auth_creds(data);
b5ede7
 	c->egid = data->ugp.gid;
b5ede7
+	c->server_pid = data->ugp.pid;
b5ede7
 
b5ede7
 	destroy_ipc_auth_data(data);
b5ede7
 	return r->hdr.error;
b5ede7
diff --git a/lib/ipc_shm.c b/lib/ipc_shm.c
b5ede7
index 699f4e4..9f237b6 100644
b5ede7
--- a/lib/ipc_shm.c
b5ede7
+++ b/lib/ipc_shm.c
b5ede7
@@ -20,6 +20,8 @@
b5ede7
  */
b5ede7
 #include "os_base.h"
b5ede7
 #include <poll.h>
b5ede7
+#include <signal.h>
b5ede7
+#include <setjmp.h>
b5ede7
 
b5ede7
 #include "ipc_int.h"
b5ede7
 #include "util_int.h"
b5ede7
@@ -36,9 +38,12 @@
b5ede7
 static void
b5ede7
 qb_ipcc_shm_disconnect(struct qb_ipcc_connection *c)
b5ede7
 {
b5ede7
-	void (*rb_destructor)(struct qb_ringbuffer_s *) = c->is_connected
b5ede7
-		? qb_rb_close
b5ede7
-		: qb_rb_force_close;
b5ede7
+	void (*rb_destructor)(struct qb_ringbuffer_s *);
b5ede7
+
b5ede7
+	rb_destructor = qb_rb_close;
b5ede7
+	if (!c->is_connected && (!c->server_pid || (kill(c->server_pid, 0) == -1 && errno == ESRCH))) {
b5ede7
+		rb_destructor = qb_rb_force_close;
b5ede7
+	}
b5ede7
 
b5ede7
 	qb_ipcc_us_sock_close(c->setup.u.us.sock);
b5ede7
 
b5ede7
@@ -215,18 +220,30 @@ return_error:
b5ede7
  * service functions
b5ede7
  * --------------------------------------------------------
b5ede7
  */
b5ede7
+static jmp_buf sigbus_jmpbuf;
b5ede7
+static void catch_sigbus(int signal)
b5ede7
+{
b5ede7
+	longjmp(sigbus_jmpbuf, 1);
b5ede7
+}
b5ede7
 
b5ede7
 static void
b5ede7
 qb_ipcs_shm_disconnect(struct qb_ipcs_connection *c)
b5ede7
 {
b5ede7
-	if (c->state == QB_IPCS_CONNECTION_ESTABLISHED ||
b5ede7
-	    c->state == QB_IPCS_CONNECTION_ACTIVE) {
b5ede7
-		if (c->setup.u.us.sock > 0) {
b5ede7
-			(void)c->service->poll_fns.dispatch_del(c->setup.u.us.sock);
b5ede7
-			qb_ipcc_us_sock_close(c->setup.u.us.sock);
b5ede7
-			c->setup.u.us.sock = -1;
b5ede7
-		}
b5ede7
+	struct sigaction sa;
b5ede7
+	struct sigaction old_sa;
b5ede7
+
b5ede7
+	/* Don't die if the client has truncated the SHM under us */
b5ede7
+	memset(&old_sa, 0, sizeof(old_sa));
b5ede7
+	memset(&sa, 0, sizeof(sa));
b5ede7
+	sa.sa_handler = catch_sigbus;
b5ede7
+	sigemptyset(&sa.sa_mask);
b5ede7
+	sa.sa_flags = 0;
b5ede7
+	sigaction(SIGBUS, &sa, &old_sa);
b5ede7
+
b5ede7
+	if (setjmp(sigbus_jmpbuf) == 1) {
b5ede7
+		goto end_disconnect;
b5ede7
 	}
b5ede7
+
b5ede7
 	if (c->state == QB_IPCS_CONNECTION_SHUTTING_DOWN ||
b5ede7
 	    c->state == QB_IPCS_CONNECTION_ACTIVE) {
b5ede7
 		if (c->response.u.shm.rb) {
b5ede7
@@ -239,6 +256,17 @@ qb_ipcs_shm_disconnect(struct qb_ipcs_connection *c)
b5ede7
 			qb_rb_close(qb_rb_lastref_and_ret(&c->request.u.shm.rb));
b5ede7
 		}
b5ede7
 	}
b5ede7
+
b5ede7
+	if (c->state == QB_IPCS_CONNECTION_ESTABLISHED ||
b5ede7
+	    c->state == QB_IPCS_CONNECTION_ACTIVE) {
b5ede7
+		if (c->setup.u.us.sock > 0) {
b5ede7
+			(void)c->service->poll_fns.dispatch_del(c->setup.u.us.sock);
b5ede7
+			qb_ipcc_us_sock_close(c->setup.u.us.sock);
b5ede7
+			c->setup.u.us.sock = -1;
b5ede7
+		}
b5ede7
+	}
b5ede7
+end_disconnect:
b5ede7
+	sigaction(SIGBUS, &old_sa, NULL);
b5ede7
 }
b5ede7
 
b5ede7
 static int32_t
b5ede7
diff --git a/tests/check_ipc.c b/tests/check_ipc.c
b5ede7
index f8af2c5..46c3b40 100644
b5ede7
--- a/tests/check_ipc.c
b5ede7
+++ b/tests/check_ipc.c
b5ede7
@@ -444,18 +444,30 @@ run_ipc_server(void)
b5ede7
 static int32_t
b5ede7
 run_function_in_new_process(void (*run_ipc_server_fn)(void))
b5ede7
 {
b5ede7
-	pid_t pid = fork ();
b5ede7
+	pid_t pid1 = fork ();
b5ede7
+	pid_t pid2;
b5ede7
 
b5ede7
-	if (pid == -1) {
b5ede7
+	if (pid1 == -1) {
b5ede7
 		fprintf (stderr, "Can't fork\n");
b5ede7
 		return -1;
b5ede7
 	}
b5ede7
 
b5ede7
-	if (pid == 0) {
b5ede7
-		run_ipc_server_fn();
b5ede7
-		exit(0);
b5ede7
+	/* Double-fork so the servers can be reaped in a timely manner */
b5ede7
+	if (pid1 == 0) {
b5ede7
+		pid2 = fork();
b5ede7
+		if (pid2 == -1) {
b5ede7
+			fprintf (stderr, "Can't fork twice\n");
b5ede7
+			exit(0);
b5ede7
+		}
b5ede7
+		if (pid2 == 0) {
b5ede7
+			run_ipc_server_fn();
b5ede7
+			exit(0);
b5ede7
+		} else {
b5ede7
+			waitpid(pid2, NULL, 0);
b5ede7
+			exit(0);
b5ede7
+		}
b5ede7
 	}
b5ede7
-	return pid;
b5ede7
+	return pid1;
b5ede7
 }
b5ede7
 
b5ede7
 static void
b5ede7
--- a/lib/loop_poll.cg	2018-04-20 11:23:05.255007538 +0100
b5ede7
+++ b/lib/loop_poll.c	2018-04-20 11:23:36.000113203 +0100
b5ede7
@@ -723,7 +723,7 @@ qb_loop_signal_mod(qb_loop_t * lp,
b5ede7
 	sig->p = p;
b5ede7
 
b5ede7
 	if (sig->signal != the_sig) {
b5ede7
-		signal(sig->signal, SIG_DFL);
b5ede7
+		(void)signal(sig->signal, SIG_DFL);
b5ede7
 		sig->signal = the_sig;
b5ede7
 		_adjust_sigactions_(s);
b5ede7
 	}
b5ede7
@@ -774,7 +774,7 @@ qb_loop_signal_del(qb_loop_t * lp, qb_lo
b5ede7
 	}
b5ede7
 
b5ede7
 	qb_list_del(&sig->item.list);
b5ede7
-	signal(sig->signal, SIG_DFL);
b5ede7
+	(void)signal(sig->signal, SIG_DFL);
b5ede7
 	free(sig);
b5ede7
 	_adjust_sigactions_(s);
b5ede7
 	return 0;