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:
Ondrej Zajicek (work) 2015-11-09 00:42:02 +01:00
parent 3aed0a6ff7
commit 9b9a7143c4
7 changed files with 90 additions and 53 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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 *);

View file

@ -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 */

View file

@ -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 */

View file

@ -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);

View file

@ -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;