mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-03-11 05:48:41 +00:00
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.
This commit is contained in:
parent
d6871f785f
commit
8d85aa44da
@ -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;
|
||||
|
@ -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;
|
||||
|
60
src/map.c
60
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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user