From 8d85aa44dabb772cf99360c41143486875f3e383 Mon Sep 17 00:00:00 2001 From: Emeric Brun Date: Thu, 29 Jun 2017 15:40:33 +0200 Subject: [PATCH] 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. --- include/types/applet.h | 2 +- include/types/pattern.h | 1 + src/map.c | 60 +++++++++++++++++++++++++++++++---------- src/pattern.c | 38 ++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 15 deletions(-) diff --git a/include/types/applet.h b/include/types/applet.h index 71976d88ce..5c1b074d3e 100644 --- a/include/types/applet.h +++ b/include/types/applet.h @@ -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; diff --git a/include/types/pattern.h b/include/types/pattern.h index 912e086b78..d7ce6136a4 100644 --- a/include/types/pattern.h +++ b/include/types/pattern.h @@ -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; diff --git a/src/map.c b/src/map.c index e8c6a2acbf..0c486838af 100644 --- 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; } diff --git a/src/pattern.c b/src/pattern.c index 60fe462a10..20593093e8 100644 --- a/src/pattern.c +++ b/src/pattern.c @@ -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); -- 2.39.5