mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 01:31:55 +00:00
Conf: Fixes bug in symbol lookup during reconfiguration
Symbol lookup by cf_find_symbol() not only did the lookup but also added new void symbols allocated from cfg_mem linpool, which gets broken when lookups are done outside of config parsing, which may lead to crashes during reconfiguration. The patch separates lookup-only cf_find_symbol() and config-modifying cf_get_symbol(), while the later is called only during parsing. Also new_config and cfg_mem global variables are NULLed outside of parsing.
This commit is contained in:
parent
3aed0a6ff7
commit
9b9a7143c4
@ -70,7 +70,7 @@ struct sym_scope {
|
|||||||
static struct sym_scope *conf_this_scope;
|
static struct sym_scope *conf_this_scope;
|
||||||
|
|
||||||
static int cf_hash(byte *c);
|
static int cf_hash(byte *c);
|
||||||
static struct symbol *cf_find_sym(byte *c, unsigned int h0);
|
static inline struct symbol * cf_get_sym(byte *c, uint h0);
|
||||||
|
|
||||||
linpool *cfg_mem;
|
linpool *cfg_mem;
|
||||||
|
|
||||||
@ -194,7 +194,7 @@ else: {
|
|||||||
}
|
}
|
||||||
k=k->next;
|
k=k->next;
|
||||||
}
|
}
|
||||||
cf_lval.s = cf_find_sym(yytext, h);
|
cf_lval.s = cf_get_sym(yytext, h);
|
||||||
return SYM;
|
return SYM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,8 +426,9 @@ check_eof(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct symbol *
|
static struct symbol *
|
||||||
cf_new_sym(byte *c, unsigned int h)
|
cf_new_sym(byte *c, uint h0)
|
||||||
{
|
{
|
||||||
|
uint h = h0 & (SYM_HASH_SIZE-1);
|
||||||
struct symbol *s, **ht;
|
struct symbol *s, **ht;
|
||||||
int l;
|
int l;
|
||||||
|
|
||||||
@ -449,56 +450,77 @@ cf_new_sym(byte *c, unsigned int h)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct symbol *
|
static struct symbol *
|
||||||
cf_find_sym(byte *c, unsigned int h0)
|
cf_find_sym(struct config *cfg, byte *c, uint h0)
|
||||||
{
|
{
|
||||||
unsigned int h = h0 & (SYM_HASH_SIZE-1);
|
uint h = h0 & (SYM_HASH_SIZE-1);
|
||||||
struct symbol *s, **ht;
|
struct symbol *s, **ht;
|
||||||
|
|
||||||
if (ht = new_config->sym_hash)
|
if (ht = cfg->sym_hash)
|
||||||
{
|
{
|
||||||
for(s = ht[h]; s; s=s->next)
|
for(s = ht[h]; s; s=s->next)
|
||||||
if (!strcmp(s->name, c) && s->scope->active)
|
if (!strcmp(s->name, c) && s->scope->active)
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
if (new_config->sym_fallback)
|
if (ht = cfg->sym_fallback)
|
||||||
{
|
{
|
||||||
/* We know only top-level scope is active */
|
/* We know only top-level scope is active */
|
||||||
for(s = new_config->sym_fallback[h]; s; s=s->next)
|
for(s = ht[h]; s; s=s->next)
|
||||||
if (!strcmp(s->name, c) && s->scope->active)
|
if (!strcmp(s->name, c) && s->scope->active)
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
return cf_new_sym(c, h);
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct symbol *
|
||||||
|
cf_get_sym(byte *c, uint h0)
|
||||||
|
{
|
||||||
|
return cf_find_sym(new_config, c, h0) ?: cf_new_sym(c, h0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cf_find_symbol - find a symbol by name
|
* cf_find_symbol - find a symbol by name
|
||||||
|
* @cfg: specificed config
|
||||||
* @c: symbol name
|
* @c: symbol name
|
||||||
*
|
*
|
||||||
* This functions searches the symbol table for a symbol of given
|
* This functions searches the symbol table in the config @cfg for a symbol of
|
||||||
* name. First it examines the current scope, then the second recent
|
* given name. First it examines the current scope, then the second recent one
|
||||||
* one and so on until it either finds the symbol and returns a pointer
|
* and so on until it either finds the symbol and returns a pointer to its
|
||||||
* to its &symbol structure or reaches the end of the scope chain
|
* &symbol structure or reaches the end of the scope chain and returns %NULL to
|
||||||
* and returns %NULL to signify no match.
|
* signify no match.
|
||||||
*/
|
*/
|
||||||
struct symbol *
|
struct symbol *
|
||||||
cf_find_symbol(byte *c)
|
cf_find_symbol(struct config *cfg, byte *c)
|
||||||
{
|
{
|
||||||
return cf_find_sym(c, cf_hash(c));
|
return cf_find_sym(cfg, c, cf_hash(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cf_get_symbol - get a symbol by name
|
||||||
|
* @c: symbol name
|
||||||
|
*
|
||||||
|
* This functions searches the symbol table of the currently parsed config
|
||||||
|
* (@new_config) for a symbol of given name. It returns either the already
|
||||||
|
* existing symbol or a newly allocated undefined (%SYM_VOID) symbol if no
|
||||||
|
* existing symbol is found.
|
||||||
|
*/
|
||||||
|
struct symbol *
|
||||||
|
cf_get_symbol(byte *c)
|
||||||
|
{
|
||||||
|
return cf_get_sym(c, cf_hash(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct symbol *
|
struct symbol *
|
||||||
cf_default_name(char *template, int *counter)
|
cf_default_name(char *template, int *counter)
|
||||||
{
|
{
|
||||||
char buf[32];
|
char buf[SYM_MAX_LEN];
|
||||||
struct symbol *s;
|
struct symbol *s;
|
||||||
char *perc = strchr(template, '%');
|
char *perc = strchr(template, '%');
|
||||||
|
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
bsprintf(buf, template, ++(*counter));
|
bsprintf(buf, template, ++(*counter));
|
||||||
s = cf_find_sym(buf, cf_hash(buf));
|
s = cf_get_sym(buf, cf_hash(buf));
|
||||||
if (!s)
|
|
||||||
break;
|
|
||||||
if (s->class == SYM_VOID)
|
if (s->class == SYM_VOID)
|
||||||
return s;
|
return s;
|
||||||
if (!perc)
|
if (!perc)
|
||||||
@ -529,7 +551,7 @@ cf_define_symbol(struct symbol *sym, int type, void *def)
|
|||||||
{
|
{
|
||||||
if (sym->scope == conf_this_scope)
|
if (sym->scope == conf_this_scope)
|
||||||
cf_error("Symbol already defined");
|
cf_error("Symbol already defined");
|
||||||
sym = cf_new_sym(sym->name, cf_hash(sym->name) & (SYM_HASH_SIZE-1));
|
sym = cf_new_sym(sym->name, cf_hash(sym->name));
|
||||||
}
|
}
|
||||||
sym->class = type;
|
sym->class = type;
|
||||||
sym->def = def;
|
sym->def = def;
|
||||||
|
65
conf/conf.c
65
conf/conf.c
@ -20,19 +20,19 @@
|
|||||||
*
|
*
|
||||||
* There can exist up to four different configurations at one time: an active
|
* There can exist up to four different configurations at one time: an active
|
||||||
* one (pointed to by @config), configuration we are just switching from
|
* one (pointed to by @config), configuration we are just switching from
|
||||||
* (@old_config), one queued for the next reconfiguration (@future_config;
|
* (@old_config), one queued for the next reconfiguration (@future_config; if
|
||||||
* if there is one and the user wants to reconfigure once again, we just
|
* there is one and the user wants to reconfigure once again, we just free the
|
||||||
* free the previous queued config and replace it with the new one) and
|
* previous queued config and replace it with the new one) and finally a config
|
||||||
* finally a config being parsed (@new_config). The stored @old_config
|
* being parsed (@new_config). The stored @old_config is also used for undo
|
||||||
* is also used for undo reconfiguration, which works in a similar way.
|
* reconfiguration, which works in a similar way. Reconfiguration could also
|
||||||
* Reconfiguration could also have timeout (using @config_timer) and undo
|
* have timeout (using @config_timer) and undo is automatically called if the
|
||||||
* is automatically called if the new configuration is not confirmed later.
|
* new configuration is not confirmed later. The new config (@new_config) and
|
||||||
|
* associated linear pool (@cfg_mem) is non-NULL only during parsing.
|
||||||
*
|
*
|
||||||
* Loading of new configuration is very simple: just call config_alloc()
|
* Loading of new configuration is very simple: just call config_alloc() to get
|
||||||
* to get a new &config structure, then use config_parse() to parse a
|
* a new &config structure, then use config_parse() to parse a configuration
|
||||||
* configuration file and fill all fields of the structure
|
* file and fill all fields of the structure and finally ask the config manager
|
||||||
* and finally ask the config manager to switch to the new
|
* to switch to the new config by calling config_commit().
|
||||||
* config by calling config_commit().
|
|
||||||
*
|
*
|
||||||
* CLI commands are parsed in a very similar way -- there is also a stripped-down
|
* CLI commands are parsed in a very similar way -- there is also a stripped-down
|
||||||
* &config structure associated with them and they are lex-ed and parsed by the
|
* &config structure associated with them and they are lex-ed and parsed by the
|
||||||
@ -91,10 +91,15 @@ config_alloc(byte *name)
|
|||||||
linpool *l = lp_new(p, 4080);
|
linpool *l = lp_new(p, 4080);
|
||||||
struct config *c = lp_allocz(l, sizeof(struct config));
|
struct config *c = lp_allocz(l, sizeof(struct config));
|
||||||
|
|
||||||
|
/* Duplication of name string in local linear pool */
|
||||||
|
uint nlen = strlen(name) + 1;
|
||||||
|
char *ndup = lp_allocu(l, nlen);
|
||||||
|
memcpy(ndup, name, nlen);
|
||||||
|
|
||||||
c->mrtdump_file = -1; /* Hack, this should be sysdep-specific */
|
c->mrtdump_file = -1; /* Hack, this should be sysdep-specific */
|
||||||
c->pool = p;
|
c->pool = p;
|
||||||
cfg_mem = c->mem = l;
|
c->mem = l;
|
||||||
c->file_name = cfg_strdup(name);
|
c->file_name = ndup;
|
||||||
c->load_time = now;
|
c->load_time = now;
|
||||||
c->tf_route = c->tf_proto = (struct timeformat){"%T", "%F", 20*3600};
|
c->tf_route = c->tf_proto = (struct timeformat){"%T", "%F", 20*3600};
|
||||||
c->tf_base = c->tf_log = (struct timeformat){"%F %T", NULL, 0};
|
c->tf_base = c->tf_log = (struct timeformat){"%F %T", NULL, 0};
|
||||||
@ -119,11 +124,13 @@ config_alloc(byte *name)
|
|||||||
int
|
int
|
||||||
config_parse(struct config *c)
|
config_parse(struct config *c)
|
||||||
{
|
{
|
||||||
|
int done = 0;
|
||||||
DBG("Parsing configuration file `%s'\n", c->file_name);
|
DBG("Parsing configuration file `%s'\n", c->file_name);
|
||||||
new_config = c;
|
new_config = c;
|
||||||
cfg_mem = c->mem;
|
cfg_mem = c->mem;
|
||||||
if (setjmp(conf_jmpbuf))
|
if (setjmp(conf_jmpbuf))
|
||||||
return 0;
|
goto cleanup;
|
||||||
|
|
||||||
cf_lex_init(0, c);
|
cf_lex_init(0, c);
|
||||||
sysdep_preconfig(c);
|
sysdep_preconfig(c);
|
||||||
protos_preconfig(c);
|
protos_preconfig(c);
|
||||||
@ -137,7 +144,12 @@ config_parse(struct config *c)
|
|||||||
if (!c->router_id)
|
if (!c->router_id)
|
||||||
cf_error("Router ID must be configured manually on IPv6 routers");
|
cf_error("Router ID must be configured manually on IPv6 routers");
|
||||||
#endif
|
#endif
|
||||||
return 1;
|
done = 1;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
new_config = NULL;
|
||||||
|
cfg_mem = NULL;
|
||||||
|
return done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -150,14 +162,22 @@ config_parse(struct config *c)
|
|||||||
int
|
int
|
||||||
cli_parse(struct config *c)
|
cli_parse(struct config *c)
|
||||||
{
|
{
|
||||||
new_config = c;
|
int done = 0;
|
||||||
c->sym_fallback = config->sym_hash;
|
c->sym_fallback = config->sym_hash;
|
||||||
|
new_config = c;
|
||||||
cfg_mem = c->mem;
|
cfg_mem = c->mem;
|
||||||
if (setjmp(conf_jmpbuf))
|
if (setjmp(conf_jmpbuf))
|
||||||
return 0;
|
goto cleanup;
|
||||||
|
|
||||||
cf_lex_init(1, c);
|
cf_lex_init(1, c);
|
||||||
cf_parse();
|
cf_parse();
|
||||||
return 1;
|
done = 1;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
c->sym_fallback = NULL;
|
||||||
|
new_config = NULL;
|
||||||
|
cfg_mem = NULL;
|
||||||
|
return done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -237,10 +257,6 @@ config_do_commit(struct config *c, int type)
|
|||||||
if (old_config && !config->shutdown)
|
if (old_config && !config->shutdown)
|
||||||
log(L_INFO "Reconfiguring");
|
log(L_INFO "Reconfiguring");
|
||||||
|
|
||||||
/* This should not be necessary, but it seems there are some
|
|
||||||
functions that access new_config instead of config */
|
|
||||||
new_config = config;
|
|
||||||
|
|
||||||
if (old_config)
|
if (old_config)
|
||||||
old_config->obstacle_count++;
|
old_config->obstacle_count++;
|
||||||
|
|
||||||
@ -254,9 +270,6 @@ config_do_commit(struct config *c, int type)
|
|||||||
DBG("protos_commit\n");
|
DBG("protos_commit\n");
|
||||||
protos_commit(c, old_config, force_restart, type);
|
protos_commit(c, old_config, force_restart, type);
|
||||||
|
|
||||||
/* Just to be sure nobody uses that now */
|
|
||||||
new_config = NULL;
|
|
||||||
|
|
||||||
int obs = 0;
|
int obs = 0;
|
||||||
if (old_config)
|
if (old_config)
|
||||||
obs = --old_config->obstacle_count;
|
obs = --old_config->obstacle_count;
|
||||||
|
@ -147,7 +147,9 @@ int cf_lex(void);
|
|||||||
void cf_lex_init(int is_cli, struct config *c);
|
void cf_lex_init(int is_cli, struct config *c);
|
||||||
void cf_lex_unwind(void);
|
void cf_lex_unwind(void);
|
||||||
|
|
||||||
struct symbol *cf_find_symbol(byte *c);
|
struct symbol *cf_find_symbol(struct config *cfg, byte *c);
|
||||||
|
|
||||||
|
struct symbol *cf_get_symbol(byte *c);
|
||||||
struct symbol *cf_default_name(char *template, int *counter);
|
struct symbol *cf_default_name(char *template, int *counter);
|
||||||
struct symbol *cf_define_symbol(struct symbol *symbol, int type, void *def);
|
struct symbol *cf_define_symbol(struct symbol *symbol, int type, void *def);
|
||||||
void cf_push_scope(struct symbol *);
|
void cf_push_scope(struct symbol *);
|
||||||
|
@ -521,7 +521,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
|
|||||||
WALK_LIST(oc, old->protos)
|
WALK_LIST(oc, old->protos)
|
||||||
{
|
{
|
||||||
p = oc->proto;
|
p = oc->proto;
|
||||||
sym = cf_find_symbol(oc->name);
|
sym = cf_find_symbol(new, oc->name);
|
||||||
if (sym && sym->class == SYM_PROTO && !new->shutdown)
|
if (sym && sym->class == SYM_PROTO && !new->shutdown)
|
||||||
{
|
{
|
||||||
/* Found match, let's check if we can smoothly switch to new configuration */
|
/* Found match, let's check if we can smoothly switch to new configuration */
|
||||||
|
@ -311,7 +311,7 @@ roa_commit(struct config *new, struct config *old)
|
|||||||
if (old)
|
if (old)
|
||||||
WALK_LIST(t, roa_table_list)
|
WALK_LIST(t, roa_table_list)
|
||||||
{
|
{
|
||||||
struct symbol *sym = cf_find_symbol(t->name);
|
struct symbol *sym = cf_find_symbol(new, t->name);
|
||||||
if (sym && sym->class == SYM_ROA)
|
if (sym && sym->class == SYM_ROA)
|
||||||
{
|
{
|
||||||
/* Found old table in new config */
|
/* Found old table in new config */
|
||||||
|
@ -1663,7 +1663,7 @@ rt_prune_loop(void)
|
|||||||
void
|
void
|
||||||
rt_preconfig(struct config *c)
|
rt_preconfig(struct config *c)
|
||||||
{
|
{
|
||||||
struct symbol *s = cf_find_symbol("master");
|
struct symbol *s = cf_get_symbol("master");
|
||||||
|
|
||||||
init_list(&c->tables);
|
init_list(&c->tables);
|
||||||
c->master_rtc = rt_new_table(s);
|
c->master_rtc = rt_new_table(s);
|
||||||
@ -1903,7 +1903,7 @@ rt_commit(struct config *new, struct config *old)
|
|||||||
rtable *ot = o->table;
|
rtable *ot = o->table;
|
||||||
if (!ot->deleted)
|
if (!ot->deleted)
|
||||||
{
|
{
|
||||||
struct symbol *sym = cf_find_symbol(o->name);
|
struct symbol *sym = cf_find_symbol(new, o->name);
|
||||||
if (sym && sym->class == SYM_TABLE && !new->shutdown)
|
if (sym && sym->class == SYM_TABLE && !new->shutdown)
|
||||||
{
|
{
|
||||||
DBG("\t%s: same\n", o->name);
|
DBG("\t%s: same\n", o->name);
|
||||||
|
@ -96,7 +96,7 @@ drop_gid(gid_t gid)
|
|||||||
static inline void
|
static inline void
|
||||||
add_num_const(char *name, int val)
|
add_num_const(char *name, int val)
|
||||||
{
|
{
|
||||||
struct symbol *s = cf_find_symbol(name);
|
struct symbol *s = cf_get_symbol(name);
|
||||||
s->class = SYM_CONSTANT | T_INT;
|
s->class = SYM_CONSTANT | T_INT;
|
||||||
s->def = cfg_allocz(sizeof(struct f_val));
|
s->def = cfg_allocz(sizeof(struct f_val));
|
||||||
SYM_TYPE(s) = T_INT;
|
SYM_TYPE(s) = T_INT;
|
||||||
|
Loading…
Reference in New Issue
Block a user