bc: fix infinite state growth for "yes 1 | bc" case

function                                             old     new   delta
zbc_vm_process                                       585     672     +87
bc_func_init                                          50      86     +36
zbc_program_num                                      990    1022     +32
bc_program_str                                        17      47     +30
bc_program_current_func                                -      22     +22
bc_parse_pushNUM                                      66      87     +21
bc_func_free                                          27      43     +16
zbc_num_binary                                       145     147      +2
bc_program_reset                                      64      61      -3
bc_parse_pushSTR                                      73      65      -8
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 7/2 up/down: 246/-11)           Total: 235 bytes
   text	   data	    bss	    dec	    hex	filename
 981393	    485	   7296	 989174	  f17f6	busybox_old
 981656	    485	   7296	 989437	  f18fd	busybox_unstripped

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2018-12-21 16:22:26 +01:00
parent 447dc02c27
commit 5d57bc442d

View File

@ -350,6 +350,8 @@ typedef struct BcFunc {
BcVec code;
IF_BC(BcVec labels;)
IF_BC(BcVec autos;)
IF_BC(BcVec strs;)
IF_BC(BcVec consts;)
IF_BC(size_t nparams;)
} BcFunc;
@ -694,8 +696,8 @@ typedef struct BcProgram {
BcVec arrs;
BcVec arr_map;
BcVec strs;
BcVec consts;
IF_DC(BcVec strs;)
IF_DC(BcVec consts;)
const char *file;
@ -1093,6 +1095,7 @@ static void bc_vec_pop_all(BcVec *v)
bc_vec_npop(v, v->len);
}
//TODO: return index of pushed data - many callers can use that
static void bc_vec_push(BcVec *v, const void *data)
{
if (v->len + 1 > v->cap) bc_vec_grow(v, 1);
@ -1160,18 +1163,6 @@ static void *bc_vec_item(const BcVec *v, size_t idx)
return v->v + v->size * idx;
}
static char** bc_program_str(size_t idx)
{
return bc_vec_item(&G.prog.strs, idx);
}
static BcFunc* bc_program_func(size_t idx)
{
return bc_vec_item(&G.prog.fns, idx);
}
// BC_PROG_MAIN is zeroth element, so:
#define bc_program_func_BC_PROG_MAIN() ((BcFunc*)(G.prog.fns.v))
static void *bc_vec_item_rev(const BcVec *v, size_t idx)
{
return v->v + v->size * (v->len - idx - 1);
@ -1189,6 +1180,46 @@ static FAST_FUNC void bc_vec_free(void *vec)
free(v->v);
}
static BcFunc* bc_program_func(size_t idx)
{
return bc_vec_item(&G.prog.fns, idx);
}
// BC_PROG_MAIN is zeroth element, so:
#define bc_program_func_BC_PROG_MAIN() ((BcFunc*)(G.prog.fns.v))
#if ENABLE_BC
static BcFunc* bc_program_current_func(void)
{
BcInstPtr *ip = bc_vec_top(&G.prog.exestack);
BcFunc *func = bc_program_func(ip->func);
return func;
}
#endif
static char** bc_program_str(size_t idx)
{
#if ENABLE_BC
if (IS_BC) {
BcFunc *func = bc_program_current_func();
return bc_vec_item(&func->strs, idx);
}
#endif
IF_DC(return bc_vec_item(&G.prog.strs, idx);)
}
static char** bc_program_const(size_t idx)
{
#if ENABLE_BC
if (IS_BC) {
BcFunc *func = bc_program_current_func();
return bc_vec_item(&func->consts, idx);
}
#endif
IF_DC(return bc_vec_item(&G.prog.consts, idx);)
}
static int bc_id_cmp(const void *e1, const void *e2)
{
return strcmp(((const BcId *) e1)->name, ((const BcId *) e2)->name);
@ -2522,11 +2553,18 @@ static BC_STATUS zbc_func_insert(BcFunc *f, char *name, bool var)
#define zbc_func_insert(...) (zbc_func_insert(__VA_ARGS__) COMMA_SUCCESS)
#endif
static FAST_FUNC void bc_string_free(void *string)
{
free(*(char**)string);
}
static void bc_func_init(BcFunc *f)
{
bc_char_vec_init(&f->code);
IF_BC(bc_vec_init(&f->labels, sizeof(size_t), NULL);)
IF_BC(bc_vec_init(&f->autos, sizeof(BcId), bc_id_free);)
IF_BC(bc_vec_init(&f->strs, sizeof(char *), bc_string_free);)
IF_BC(bc_vec_init(&f->consts, sizeof(char *), bc_string_free);)
IF_BC(f->nparams = 0;)
}
@ -2536,6 +2574,8 @@ static FAST_FUNC void bc_func_free(void *func)
bc_vec_free(&f->code);
IF_BC(bc_vec_free(&f->labels);)
IF_BC(bc_vec_free(&f->autos);)
IF_BC(bc_vec_free(&f->strs);)
IF_BC(bc_vec_free(&f->consts);)
}
static void bc_array_expand(BcVec *a, size_t len);
@ -2585,11 +2625,6 @@ static void bc_array_copy(BcVec *d, const BcVec *s)
}
}
static FAST_FUNC void bc_string_free(void *string)
{
free(*((char **) string));
}
#if ENABLE_DC
static void dc_result_copy(BcResult *d, BcResult *src)
{
@ -3501,8 +3536,8 @@ static BC_STATUS bc_parse_pushSTR(BcParse *p)
char *str = xstrdup(p->l.t.v.v);
bc_parse_push(p, BC_INST_STR);
bc_parse_pushIndex(p, G.prog.strs.len);
bc_vec_push(&G.prog.strs, &str);
bc_parse_pushIndex(p, p->func->strs.len);
bc_vec_push(&p->func->strs, &str);
RETURN_STATUS(zbc_lex_next(&p->l));
}
@ -3512,10 +3547,16 @@ static BC_STATUS bc_parse_pushSTR(BcParse *p)
static void bc_parse_pushNUM(BcParse *p)
{
char *num = xstrdup(p->l.t.v.v);
#if ENABLE_BC && ENABLE_DC
size_t idx = IS_BC ? p->func->consts.len : G.prog.consts.len;
bc_vec_push(IS_BC ? &p->func->consts : &G.prog.consts, &num);
#elif ENABLE_BC
size_t idx = p->func->consts.len;
bc_vec_push(&p->func->consts, &num);
#else // DC
size_t idx = G.prog.consts.len;
bc_vec_push(&G.prog.consts, &num);
#endif
bc_parse_push(p, BC_INST_NUM);
bc_parse_pushIndex(p, idx);
}
@ -3556,7 +3597,7 @@ static void bc_program_reset(void)
bc_vec_npop(&G.prog.exestack, G.prog.exestack.len - 1);
bc_vec_pop_all(&G.prog.results);
f = bc_program_func(0);
f = bc_program_func_BC_PROG_MAIN();
ip = bc_vec_top(&G.prog.exestack);
ip->idx = f->code.len;
}
@ -5027,9 +5068,12 @@ static BC_STATUS zbc_program_num(BcResult *r, BcNum **num, bool hex)
break;
case BC_RESULT_CONSTANT: {
BcStatus s;
char *str = *(char**)bc_vec_item(&G.prog.consts, r->d.id.idx);
char *str;
unsigned base_t;
size_t len = strlen(str);
size_t len;
str = *bc_program_const(r->d.id.idx);
len = strlen(str);
bc_num_init(&r->d.n, len);
@ -6667,6 +6711,43 @@ static BC_STATUS zbc_vm_process(const char *text)
bc_program_reset();
break;
}
// bc discards strings, constants and code after each
// top-level statement in the "main program".
// This prevents "yes 1 | bc" from growing its memory
// without bound. This can be done because data stack
// is empty and thus can't hold any references to
// strings or constants, there is no generated code
// which can hold references (after we discard one
// we just executed). Code of functions can have references,
// but bc stores function strings/constants in per-function
// storage.
if (IS_BC) {
BcFunc *f;
BcInstPtr *ip;
//FIXME: this does not clear up the stack
//for(i=1; i<3; i++) {
// i
// if(i==2) continue
// 77
//}
// if (G.prog.results.len != 0)
// bb_error_msg_and_die("data stack not empty: %d slots", G.prog.results.len);
if (G.prog.exestack.len != 1) // should be empty
bb_error_msg_and_die("BUG:call stack");
ip = (void*)G.prog.exestack.v;
if (ip->func != BC_PROG_MAIN)
bb_error_msg_and_die("BUG:not MAIN");
//bb_error_msg("ip->func:%d >idx:%d >len:%d", ip->func, ip->idx, ip->len);
f = bc_program_func_BC_PROG_MAIN();
//bb_error_msg("MAIN->code.len:%d >strs.len:%d >consts.len:%d", f->code.len, f->strs.len, f->consts.len); // labels, autos, nparams
bc_vec_pop_all(&f->code);
ip->idx = 0;
IF_BC(bc_vec_pop_all(&f->strs);)
IF_BC(bc_vec_pop_all(&f->consts);)
}
}
dbg_lex_done("%s:%d done", __func__, __LINE__);
@ -6999,8 +7080,8 @@ static void bc_program_free(void)
bc_vec_free(&G.prog.var_map);
bc_vec_free(&G.prog.arrs);
bc_vec_free(&G.prog.arr_map);
bc_vec_free(&G.prog.strs);
bc_vec_free(&G.prog.consts);
IF_DC(bc_vec_free(&G.prog.strs);)
IF_DC(bc_vec_free(&G.prog.consts);)
bc_vec_free(&G.prog.results);
bc_vec_free(&G.prog.exestack);
IF_BC(bc_num_free(&G.prog.last);)
@ -7057,8 +7138,8 @@ static void bc_program_init(void)
bc_vec_init(&G.prog.arrs, sizeof(BcVec), bc_vec_free);
bc_vec_init(&G.prog.arr_map, sizeof(BcId), bc_id_free);
bc_vec_init(&G.prog.strs, sizeof(char *), bc_string_free);
bc_vec_init(&G.prog.consts, sizeof(char *), bc_string_free);
IF_DC(bc_vec_init(&G.prog.strs, sizeof(char *), bc_string_free);)
IF_DC(bc_vec_init(&G.prog.consts, sizeof(char *), bc_string_free);)
bc_vec_init(&G.prog.results, sizeof(BcResult), bc_result_free);
bc_vec_init(&G.prog.exestack, sizeof(BcInstPtr), NULL);
bc_vec_push(&G.prog.exestack, &ip);