BUG/MAJOR: map: fix segfault during 'show map/acl' on cli.

The reference of the current map/acl element to dump could
be destroyed if map is updated from an 'http-request del-map'
configuration rule or throught a 'del map/acl' on CLI.

We use a 'back_refs' chaining element to fix this. As it
is done to dump sessions.

This patch needs also fix:
'BUG/MAJOR: cli: fix custom io_release was crushed by NULL.'

To clean the back_ref and avoid a crash on a further
del/clear map operation.

Those fixes should be backported on mainline branches 1.7 and 1.6.

This patch wont directly apply on 1.6.
diff --git a/include/types/applet.h b/include/types/applet.h
index 71976d8..5c1b074 100644
--- a/include/types/applet.h
+++ b/include/types/applet.h
@@ -141,7 +141,7 @@
 		struct {
 			unsigned int display_flags;
 			struct pat_ref *ref;
-			struct pat_ref_elt *elt;
+			struct bref bref;	/* back-reference from the pat_ref_elt being dumped */
 			struct pattern_expr *expr;
 			struct chunk chunk;
 		} map;
diff --git a/include/types/pattern.h b/include/types/pattern.h
index 912e086..d7ce613 100644
--- a/include/types/pattern.h
+++ b/include/types/pattern.h
@@ -114,6 +114,7 @@
  */
 struct pat_ref_elt {
 	struct list list; /* Used to chain elements. */
+	struct list back_refs; /* list of users tracking this pat ref */
 	char *pattern;
 	char *sample;
 	int line;
diff --git a/src/map.c b/src/map.c
index e8c6a2a..0c48683 100644
--- a/src/map.c
+++ b/src/map.c
@@ -299,44 +299,67 @@
 static int cli_io_handler_pat_list(struct appctx *appctx)
 {
 	struct stream_interface *si = appctx->owner;
+	struct pat_ref_elt *elt;
+
+	if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW))) {
+		/* If we're forced to shut down, we might have to remove our
+		 * reference to the last ref_elt being dumped.
+		 */
+		if (appctx->st2 == STAT_ST_LIST) {
+			if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users)) {
+				LIST_DEL(&appctx->ctx.sess.bref.users);
+				LIST_INIT(&appctx->ctx.sess.bref.users);
+			}
+		}
+		return 1;
+	}
 
 	switch (appctx->st2) {
 
 	case STAT_ST_INIT:
-		/* Init to the first entry. The list cannot be change */
-		appctx->ctx.map.elt = LIST_NEXT(&appctx->ctx.map.ref->head,
-		                                struct pat_ref_elt *, list);
-		if (&appctx->ctx.map.elt->list == &appctx->ctx.map.ref->head)
-			appctx->ctx.map.elt = NULL;
+		/* the function had not been called yet, let's prepare the
+		 * buffer for a response. We initialize the current stream
+		 * pointer to the first in the global list. When a target
+		 * stream is being destroyed, it is responsible for updating
+		 * this pointer. We know we have reached the end when this
+		 * pointer points back to the head of the streams list.
+		 */
+		LIST_INIT(&appctx->ctx.map.bref.users);
+		appctx->ctx.map.bref.ref = appctx->ctx.map.ref->head.n;
 		appctx->st2 = STAT_ST_LIST;
 		/* fall through */
 
 	case STAT_ST_LIST:
-		while (appctx->ctx.map.elt) {
+		if (!LIST_ISEMPTY(&appctx->ctx.map.bref.users)) {
+			LIST_DEL(&appctx->ctx.map.bref.users);
+			LIST_INIT(&appctx->ctx.map.bref.users);
+		}
+
+		while (appctx->ctx.map.bref.ref != &appctx->ctx.map.ref->head) {
 			chunk_reset(&trash);
 
+			elt = LIST_ELEM(appctx->ctx.map.bref.ref, struct pat_ref_elt *, list);
+
 			/* build messages */
-			if (appctx->ctx.map.elt->sample)
+			if (elt->sample)
 				chunk_appendf(&trash, "%p %s %s\n",
-				              appctx->ctx.map.elt, appctx->ctx.map.elt->pattern,
-				              appctx->ctx.map.elt->sample);
+				              elt, elt->pattern,
+				              elt->sample);
 			else
 				chunk_appendf(&trash, "%p %s\n",
-				              appctx->ctx.map.elt, appctx->ctx.map.elt->pattern);
+				              elt, elt->pattern);
 
 			if (bi_putchk(si_ic(si), &trash) == -1) {
 				/* let's try again later from this stream. We add ourselves into
 				 * this stream's users so that it can remove us upon termination.
 				 */
 				si_applet_cant_put(si);
+				LIST_ADDQ(&elt->back_refs, &appctx->ctx.map.bref.users);
 				return 0;
 			}
 
 			/* get next list entry and check the end of the list */
-			appctx->ctx.map.elt = LIST_NEXT(&appctx->ctx.map.elt->list,
-			                                struct pat_ref_elt *, list);
-			if (&appctx->ctx.map.elt->list == &appctx->ctx.map.ref->head)
-				break;
+			appctx->ctx.map.bref.ref = elt->list.n;
 		}
 
 		appctx->st2 = STAT_ST_FIN;
@@ -583,6 +606,14 @@
 	return 1;
 }
 
+static void cli_release_show_map(struct appctx *appctx)
+{
+	if (appctx->st2 == STAT_ST_LIST) {
+		if (!LIST_ISEMPTY(&appctx->ctx.map.bref.users))
+			LIST_DEL(&appctx->ctx.map.bref.users);
+	}
+}
+
 static int cli_parse_show_map(char **args, struct appctx *appctx, void *private)
 {
 	if (strcmp(args[1], "map") == 0 ||
@@ -612,6 +643,7 @@
 			return 1;
 		}
 		appctx->io_handler = cli_io_handler_pat_list;
+		appctx->io_release = cli_release_show_map;
 		return 0;
 	}
 
diff --git a/src/pattern.c b/src/pattern.c
index 60fe462..2059309 100644
--- a/src/pattern.c
+++ b/src/pattern.c
@@ -1581,10 +1581,22 @@
 {
 	struct pattern_expr *expr;
 	struct pat_ref_elt *elt, *safe;
+	struct bref *bref, *back;
 
 	/* delete pattern from reference */
 	list_for_each_entry_safe(elt, safe, &ref->head, list) {
 		if (elt == refelt) {
+			list_for_each_entry_safe(bref, back, &elt->back_refs, users) {
+				/*
+				 * we have to unlink all watchers. We must not relink them if
+				 * this elt  was the last one in the list.
+				 */
+				LIST_DEL(&bref->users);
+				LIST_INIT(&bref->users);
+				if (elt->list.n != &ref->head)
+					LIST_ADDQ(&LIST_ELEM(elt->list.n, struct stream *, list)->back_refs, &bref->users);
+				bref->ref = elt->list.n;
+			}
 			list_for_each_entry(expr, &ref->pat, list)
 				pattern_delete(expr, elt);
 
@@ -1606,11 +1618,23 @@
 {
 	struct pattern_expr *expr;
 	struct pat_ref_elt *elt, *safe;
+	struct bref *bref, *back;
 	int found = 0;
 
 	/* delete pattern from reference */
 	list_for_each_entry_safe(elt, safe, &ref->head, list) {
 		if (strcmp(key, elt->pattern) == 0) {
+			list_for_each_entry_safe(bref, back, &elt->back_refs, users) {
+				/*
+				 * we have to unlink all watchers. We must not relink them if
+				 * this elt was the last one in the list.
+				 */
+				LIST_DEL(&bref->users);
+				LIST_INIT(&bref->users);
+				if (elt->list.n != &ref->head)
+					LIST_ADDQ(&LIST_ELEM(elt->list.n, struct stream *, list)->back_refs, &bref->users);
+				bref->ref = elt->list.n;
+			}
 			list_for_each_entry(expr, &ref->pat, list)
 				pattern_delete(expr, elt);
 
@@ -1853,6 +1877,7 @@
 	else
 		elt->sample = NULL;
 
+	LIST_INIT(&elt->back_refs);
 	LIST_ADDQ(&ref->head, &elt->list);
 
 	return 1;
@@ -1948,6 +1973,7 @@
 	else
 		elt->sample = NULL;
 
+	LIST_INIT(&elt->back_refs);
 	LIST_ADDQ(&ref->head, &elt->list);
 
 	list_for_each_entry(expr, &ref->pat, list) {
@@ -1995,8 +2021,20 @@
 {
 	struct pat_ref_elt *elt, *safe;
 	struct pattern_expr *expr;
+	struct bref *bref, *back;
 
 	list_for_each_entry_safe(elt, safe, &ref->head, list) {
+		list_for_each_entry_safe(bref, back, &elt->back_refs, users) {
+			/*
+			 * we have to unlink all watchers. We must not relink them if
+			 * this elt  was the last one in the list.
+			 */
+			LIST_DEL(&bref->users);
+			LIST_INIT(&bref->users);
+			if (elt->list.n != &ref->head)
+				LIST_ADDQ(&LIST_ELEM(elt->list.n, struct stream *, list)->back_refs, &bref->users);
+			bref->ref = elt->list.n;
+		}
 		LIST_DEL(&elt->list);
 		free(elt->pattern);
 		free(elt->sample);