]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MAJOR: map: fix segfault during 'show map/acl' on cli.
authorEmeric Brun <ebrun@haproxy.com>
Thu, 29 Jun 2017 13:40:33 +0000 (15:40 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 30 Jun 2017 04:49:42 +0000 (06:49 +0200)
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.

include/types/applet.h
include/types/pattern.h
src/map.c
src/pattern.c

index 71976d88cedee09178a418111d357f9a87aba98c..5c1b074d3ea72ac72583b3fa0759d7c1acfe1251 100644 (file)
@@ -141,7 +141,7 @@ struct appctx {
                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;
index 912e086b78a1505daab0fdb4a47607314ad9612d..d7ce6136a43e613361be12975a0e05cfa5f4e6e7 100644 (file)
@@ -114,6 +114,7 @@ struct pat_ref {
  */
 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;
index e8c6a2acbf05c36e9edca717ab60fc34a43d7472..0c486838afb2a62a2528bb3f3989e76d02fa5a02 100644 (file)
--- a/src/map.c
+++ b/src/map.c
@@ -299,44 +299,67 @@ struct pattern_expr *pat_expr_get_next(struct pattern_expr *getnext, struct list
 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 @@ static int cli_parse_get_map(char **args, struct appctx *appctx, void *private)
        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 @@ static int cli_parse_show_map(char **args, struct appctx *appctx, void *private)
                        return 1;
                }
                appctx->io_handler = cli_io_handler_pat_list;
+               appctx->io_release = cli_release_show_map;
                return 0;
        }
 
index 60fe462a105b1ecc212d0ce18966c0f2e21282bc..20593093e876d46d8e1bb47304838c5404072611 100644 (file)
@@ -1581,10 +1581,22 @@ int pat_ref_delete_by_id(struct pat_ref *ref, struct pat_ref_elt *refelt)
 {
        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 @@ int pat_ref_delete(struct pat_ref *ref, const char *key)
 {
        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 @@ int pat_ref_append(struct pat_ref *ref, char *pattern, char *sample, int line)
        else
                elt->sample = NULL;
 
+       LIST_INIT(&elt->back_refs);
        LIST_ADDQ(&ref->head, &elt->list);
 
        return 1;
@@ -1948,6 +1973,7 @@ int pat_ref_add(struct pat_ref *ref,
        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 @@ void pat_ref_prune(struct pat_ref *ref)
 {
        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);