#include <stdlib.h>
#include <stdio.h>
#include "smatch.h"
#include "smatch_slist.h"
#undef CHECKORDER
ALLOCATOR(smatch_state, "smatch state");
ALLOCATOR(sm_state, "sm state");
ALLOCATOR(named_stree, "named slist");
__DO_ALLOCATOR(char, 1, 4, "state names", sname);
int sm_state_counter;
static struct stree_stack *all_pools;
const char *show_sm(struct sm_state *sm)
{
static char buf[256];
struct sm_state *tmp;
int pos;
int i;
if (!sm)
return "<none>";
pos = snprintf(buf, sizeof(buf), "[%s] %s = '%s'%s",
check_name(sm->owner), sm->name, show_state(sm->state),
sm->merged ? " [merged]" : "");
if (pos > sizeof(buf))
goto truncate;
if (ptr_list_size((struct ptr_list *)sm->possible) == 1)
return buf;
pos += snprintf(buf + pos, sizeof(buf) - pos, " (");
if (pos > sizeof(buf))
goto truncate;
i = 0;
FOR_EACH_PTR(sm->possible, tmp) {
if (i++)
pos += snprintf(buf + pos, sizeof(buf) - pos, ", ");
if (pos > sizeof(buf))
goto truncate;
pos += snprintf(buf + pos, sizeof(buf) - pos, "%s",
show_state(tmp->state));
if (pos > sizeof(buf))
goto truncate;
} END_FOR_EACH_PTR(tmp);
snprintf(buf + pos, sizeof(buf) - pos, ")");
return buf;
truncate:
for (i = 0; i < 3; i++)
buf[sizeof(buf) - 2 - i] = '.';
return buf;
}
void __print_stree(struct stree *stree)
{
struct sm_state *sm;
option_debug++;
sm_msg("dumping stree [%ld states]", stree_count(stree));
FOR_EACH_SM(stree, sm) {
sm_printf("%s\n", show_sm(sm));
} END_FOR_EACH_SM(sm);
sm_printf("---\n");
option_debug--;
}
int cmp_tracker(const struct sm_state *a, const struct sm_state *b)
{
int ret;
if (a == b)
return 0;
if (!b)
return -1;
if (!a)
return 1;
if (a->owner < b->owner)
return -1;
if (a->owner > b->owner)
return 1;
ret = strcmp(a->name, b->name);
if (ret < 0)
return -1;
if (ret > 0)
return 1;
if (!b->sym && a->sym)
return -1;
if (!a->sym && b->sym)
return 1;
if (a->sym < b->sym)
return -1;
if (a->sym > b->sym)
return 1;
return 0;
}
int *dynamic_states;
void allocate_dynamic_states_array(int num_checks)
{
dynamic_states = calloc(num_checks + 1, sizeof(int));
}
void set_dynamic_states(unsigned short owner)
{
dynamic_states[owner] = true;
}
bool has_dynamic_states(unsigned short owner)
{
if (owner >= num_checks)
return false;
return dynamic_states[owner];
}
static int cmp_possible_sm(const struct sm_state *a, const struct sm_state *b, int preserve)
{
int ret;
if (a == b)
return 0;
if (!has_dynamic_states(a->owner)) {
if (a->state > b->state)
return -1;
if (a->state < b->state)
return 1;
return 0;
}
if (a->owner == SMATCH_EXTRA) {
ret = cmp_tracker(a, b);
if (ret)
return ret;
if (preserve) {
if (a->merged && !b->merged)
return -1;
if (!a->merged)
return 1;
}
}
if (!a->state->name || !b->state->name)
return 0;
return strcmp(a->state->name, b->state->name);
}
struct sm_state *alloc_sm_state(int owner, const char *name,
struct symbol *sym, struct smatch_state *state)
{
struct sm_state *sm_state = __alloc_sm_state(0);
sm_state_counter++;
sm_state->name = alloc_sname(name);
sm_state->owner = owner;
sm_state->sym = sym;
sm_state->state = state;
sm_state->line = get_lineno();
sm_state->merged = 0;
sm_state->pool = NULL;
sm_state->left = NULL;
sm_state->right = NULL;
sm_state->possible = NULL;
add_ptr_list(&sm_state->possible, sm_state);
return sm_state;
}
static struct sm_state *alloc_state_no_name(int owner, const char *name,
struct symbol *sym,
struct smatch_state *state)
{
struct sm_state *tmp;
tmp = alloc_sm_state(owner, NULL, sym, state);
tmp->name = name;
return tmp;
}
int too_many_possible(struct sm_state *sm)
{
if (ptr_list_size((struct ptr_list *)sm->possible) >= 100)
return 1;
return 0;
}
void add_possible_sm(struct sm_state *to, struct sm_state *new)
{
struct sm_state *tmp;
int preserve = 1;
int cmp;
if (too_many_possible(to))
preserve = 0;
FOR_EACH_PTR(to->possible, tmp) {
cmp = cmp_possible_sm(tmp, new, preserve);
if (cmp < 0)
continue;
else if (cmp == 0) {
return;
} else {
INSERT_CURRENT(new, tmp);
return;
}
} END_FOR_EACH_PTR(tmp);
add_ptr_list(&to->possible, new);
}
static void copy_possibles(struct sm_state *to, struct sm_state *one, struct sm_state *two)
{
struct sm_state *large = one;
struct sm_state *small = two;
struct sm_state *tmp;
if (ptr_list_size((struct ptr_list *)two->possible) >
ptr_list_size((struct ptr_list *)one->possible)) {
large = two;
small = one;
}
to->possible = clone_slist(large->possible);
add_possible_sm(to, to);
FOR_EACH_PTR(small->possible, tmp) {
add_possible_sm(to, tmp);
} END_FOR_EACH_PTR(tmp);
}
char *alloc_sname(const char *str)
{
char *tmp;
if (!str)
return NULL;
tmp = __alloc_sname(strlen(str) + 1);
strcpy(tmp, str);
return tmp;
}
static struct symbol *oom_func;
static int oom_limit = 3000000;
int out_of_memory(void)
{
if (oom_func)
return 1;
if (sm_state_counter * sizeof(struct sm_state) >= 100000000)
return 1;
if (get_mem_kb() > oom_limit) {
oom_func = cur_func_sym;
final_pass++;
sm_perror("OOM: %luKb sm_state_count = %d", get_mem_kb(), sm_state_counter);
final_pass--;
return 1;
}
return 0;
}
int low_on_memory(void)
{
if (sm_state_counter * sizeof(struct sm_state) >= 25000000)
return 1;
return 0;
}
static void free_sm_state(struct sm_state *sm)
{
free_slist(&sm->possible);
}
static void free_all_sm_states(struct allocation_blob *blob)
{
unsigned int size = sizeof(struct sm_state);
unsigned int offset = 0;
while (offset < blob->offset) {
free_sm_state((struct sm_state *)(blob->data + offset));
offset += size;
}
}
void free_every_single_sm_state(void)
{
struct allocator_struct *desc = &sm_state_allocator;
struct allocation_blob *blob = desc->blobs;
desc->blobs = NULL;
desc->allocations = 0;
desc->total_bytes = 0;
desc->useful_bytes = 0;
desc->freelist = NULL;
while (blob) {
struct allocation_blob *next = blob->next;
free_all_sm_states(blob);
blob_free(blob, desc->chunking);
blob = next;
}
clear_sname_alloc();
clear_smatch_state_alloc();
free_stack_and_strees(&all_pools);
sm_state_counter = 0;
if (oom_func) {
oom_limit += 100000;
oom_func = NULL;
}
}
unsigned long get_pool_count(void)
{
return ptr_list_size((struct ptr_list *)all_pools);
}
struct sm_state *clone_sm(struct sm_state *s)
{
struct sm_state *ret;
ret = alloc_state_no_name(s->owner, s->name, s->sym, s->state);
ret->merged = s->merged;
ret->line = s->line;
ret->possible = clone_slist(s->possible);
ret->left = s->left;
ret->right = s->right;
return ret;
}
int is_merged(struct sm_state *sm)
{
return sm->merged;
}
int is_leaf(struct sm_state *sm)
{
return !sm->merged;
}
int slist_has_state(struct state_list *slist, struct smatch_state *state)
{
struct sm_state *tmp;
FOR_EACH_PTR(slist, tmp) {
if (tmp->state == state)
return 1;
} END_FOR_EACH_PTR(tmp);
return 0;
}
struct state_list *clone_slist(struct state_list *from_slist)
{
struct sm_state *sm;
struct state_list *to_slist = NULL;
FOR_EACH_PTR(from_slist, sm) {
add_ptr_list(&to_slist, sm);
} END_FOR_EACH_PTR(sm);
return to_slist;
}
static struct smatch_state *merge_states(int owner, const char *name,
struct symbol *sym,
struct smatch_state *state1,
struct smatch_state *state2)
{
struct smatch_state *ret;
if (state1 == state2)
ret = state1;
else if (__has_merge_function(owner))
ret = __client_merge_function(owner, state1, state2);
else if (state1 == &ghost)
ret = state2;
else if (state2 == &ghost)
ret = state1;
else if (!state1 || !state2)
ret = &undefined;
else
ret = &merged;
return ret;
}
struct sm_state *merge_sm_states(struct sm_state *one, struct sm_state *two)
{
struct smatch_state *s;
struct sm_state *result;
static int warned;
if (one->state->data && !has_dynamic_states(one->owner))
sm_msg("dynamic state: %s", show_sm(one));
if (one == two)
return one;
if (out_of_memory()) {
if (!warned)
sm_warning("Function too hairy. No more merges.");
warned = 1;
return one;
}
warned = 0;
s = merge_states(one->owner, one->name, one->sym, one->state, two->state);
result = alloc_state_no_name(one->owner, one->name, one->sym, s);
result->merged = 1;
result->left = one;
result->right = two;
copy_possibles(result, one, two);
if (result->state == one->state)
result->line = one->line;
if (result->state == two->state)
result->line = two->line;
if (option_debug ||
strcmp(check_name(one->owner), option_debug_check) == 0) {
struct sm_state *tmp;
int i = 0;
printf("%s:%d %s() merge [%s] '%s' %s(L %d) + %s(L %d) => %s (",
get_filename(), get_lineno(), get_function(),
check_name(one->owner), one->name,
show_state(one->state), one->line,
show_state(two->state), two->line,
show_state(s));
FOR_EACH_PTR(result->possible, tmp) {
if (i++)
printf(", ");
printf("%s", show_state(tmp->state));
} END_FOR_EACH_PTR(tmp);
printf(")\n");
}
return result;
}
struct sm_state *get_sm_state_stree(struct stree *stree, int owner, const char *name,
struct symbol *sym)
{
struct tracker tracker = {
.owner = owner,
.name = (char *)name,
.sym = sym,
};
if (!name)
return NULL;
return avl_lookup(stree, (struct sm_state *)&tracker);
}
struct smatch_state *get_state_stree(struct stree *stree,
int owner, const char *name,
struct symbol *sym)
{
struct sm_state *sm;
sm = get_sm_state_stree(stree, owner, name, sym);
if (sm)
return sm->state;
return NULL;
}
void overwrite_sm_state_stree(struct stree **stree, struct sm_state *new)
{
avl_insert(stree, new);
}
void overwrite_sm_state_stree_stack(struct stree_stack **stack,
struct sm_state *sm)
{
struct stree *stree;
stree = pop_stree(stack);
overwrite_sm_state_stree(&stree, sm);
push_stree(stack, stree);
}
struct sm_state *set_state_stree(struct stree **stree, int owner, const char *name,
struct symbol *sym, struct smatch_state *state)
{
struct sm_state *new = alloc_sm_state(owner, name, sym, state);
avl_insert(stree, new);
return new;
}
void set_state_stree_perm(struct stree **stree, int owner, const char *name,
struct symbol *sym, struct smatch_state *state)
{
struct sm_state *sm;
sm = malloc(sizeof(*sm) + strlen(name) + 1);
memset(sm, 0, sizeof(*sm));
sm->owner = owner;
sm->name = (char *)(sm + 1);
strcpy((char *)sm->name, name);
sm->sym = sym;
sm->state = state;
overwrite_sm_state_stree(stree, sm);
}
void delete_state_stree(struct stree **stree, int owner, const char *name,
struct symbol *sym)
{
struct tracker tracker = {
.owner = owner,
.name = (char *)name,
.sym = sym,
};
avl_remove(stree, (struct sm_state *)&tracker);
}
void delete_state_stree_stack(struct stree_stack **stack, int owner, const char *name,
struct symbol *sym)
{
struct stree *stree;
stree = pop_stree(stack);
delete_state_stree(&stree, owner, name, sym);
push_stree(stack, stree);
}
void push_stree(struct stree_stack **stack, struct stree *stree)
{
add_ptr_list(stack, stree);
}
struct stree *pop_stree(struct stree_stack **stack)
{
struct stree *stree;
stree = last_ptr_list((struct ptr_list *)*stack);
delete_ptr_list_last((struct ptr_list **)stack);
return stree;
}
struct stree *top_stree(struct stree_stack *stack)
{
return last_ptr_list((struct ptr_list *)stack);
}
void free_slist(struct state_list **slist)
{
__free_ptr_list((struct ptr_list **)slist);
}
void free_stree_stack(struct stree_stack **stack)
{
__free_ptr_list((struct ptr_list **)stack);
}
void free_stack_and_strees(struct stree_stack **stree_stack)
{
struct stree *stree;
FOR_EACH_PTR(*stree_stack, stree) {
free_stree(&stree);
} END_FOR_EACH_PTR(stree);
free_stree_stack(stree_stack);
}
struct sm_state *set_state_stree_stack(struct stree_stack **stack, int owner, const char *name,
struct symbol *sym, struct smatch_state *state)
{
struct stree *stree;
struct sm_state *sm;
stree = pop_stree(stack);
sm = set_state_stree(&stree, owner, name, sym, state);
push_stree(stack, stree);
return sm;
}
struct sm_state *get_sm_state_stree_stack(struct stree_stack *stack,
int owner, const char *name,
struct symbol *sym)
{
struct stree *stree;
struct sm_state *ret;
stree = pop_stree(&stack);
ret = get_sm_state_stree(stree, owner, name, sym);
push_stree(&stack, stree);
return ret;
}
struct smatch_state *get_state_stree_stack(struct stree_stack *stack,
int owner, const char *name,
struct symbol *sym)
{
struct sm_state *sm;
sm = get_sm_state_stree_stack(stack, owner, name, sym);
if (sm)
return sm->state;
return NULL;
}
static void match_states_stree(struct stree **one, struct stree **two)
{
struct smatch_state *tmp_state;
struct sm_state *sm;
struct state_list *add_to_one = NULL;
struct state_list *add_to_two = NULL;
AvlIter one_iter;
AvlIter two_iter;
__set_cur_stree_readonly();
avl_iter_begin(&one_iter, *one, FORWARD);
avl_iter_begin(&two_iter, *two, FORWARD);
for (;;) {
if (!one_iter.sm && !two_iter.sm)
break;
if (cmp_tracker(one_iter.sm, two_iter.sm) < 0) {
__set_fake_cur_stree_fast(*two);
__in_unmatched_hook++;
tmp_state = __client_unmatched_state_function(one_iter.sm);
__in_unmatched_hook--;
__pop_fake_cur_stree_fast();
sm = alloc_state_no_name(one_iter.sm->owner, one_iter.sm->name,
one_iter.sm->sym, tmp_state);
add_ptr_list(&add_to_two, sm);
avl_iter_next(&one_iter);
} else if (cmp_tracker(one_iter.sm, two_iter.sm) == 0) {
avl_iter_next(&one_iter);
avl_iter_next(&two_iter);
} else {
__set_fake_cur_stree_fast(*one);
__in_unmatched_hook++;
tmp_state = __client_unmatched_state_function(two_iter.sm);
__in_unmatched_hook--;
__pop_fake_cur_stree_fast();
sm = alloc_state_no_name(two_iter.sm->owner, two_iter.sm->name,
two_iter.sm->sym, tmp_state);
add_ptr_list(&add_to_one, sm);
avl_iter_next(&two_iter);
}
}
__set_cur_stree_writable();
FOR_EACH_PTR(add_to_one, sm) {
avl_insert(one, sm);
} END_FOR_EACH_PTR(sm);
FOR_EACH_PTR(add_to_two, sm) {
avl_insert(two, sm);
} END_FOR_EACH_PTR(sm);
free_slist(&add_to_one);
free_slist(&add_to_two);
}
static void call_pre_merge_hooks(struct stree **one, struct stree **two)
{
struct sm_state *sm, *cur;
struct stree *new;
__in_unmatched_hook++;
__set_fake_cur_stree_fast(*one);
__push_fake_cur_stree();
FOR_EACH_SM(*two, sm) {
cur = get_sm_state(sm->owner, sm->name, sm->sym);
if (cur == sm)
continue;
call_pre_merge_hook(cur, sm);
} END_FOR_EACH_SM(sm);
new = __pop_fake_cur_stree();
overwrite_stree(new, one);
free_stree(&new);
__pop_fake_cur_stree_fast();
__set_fake_cur_stree_fast(*two);
__push_fake_cur_stree();
FOR_EACH_SM(*one, sm) {
cur = get_sm_state(sm->owner, sm->name, sm->sym);
if (cur == sm)
continue;
call_pre_merge_hook(cur, sm);
} END_FOR_EACH_SM(sm);
new = __pop_fake_cur_stree();
overwrite_stree(new, two);
free_stree(&new);
__pop_fake_cur_stree_fast();
__in_unmatched_hook--;
}
static void clone_pool_havers_stree(struct stree **stree)
{
struct sm_state *sm, *tmp;
struct state_list *slist = NULL;
FOR_EACH_SM(*stree, sm) {
if (sm->pool) {
tmp = clone_sm(sm);
add_ptr_list(&slist, tmp);
}
} END_FOR_EACH_SM(sm);
FOR_EACH_PTR(slist, sm) {
avl_insert(stree, sm);
} END_FOR_EACH_PTR(sm);
free_slist(&slist);
}
int __stree_id;
static void __merge_stree(struct stree **to, struct stree *stree, int add_pool)
{
struct stree *results = NULL;
struct stree *implied_one = NULL;
struct stree *implied_two = NULL;
AvlIter one_iter;
AvlIter two_iter;
struct sm_state *one, *two, *res;
if (out_of_memory())
return;
if (!stree)
return;
if (*to == stree)
return;
if (!*to) {
*to = clone_stree(stree);
return;
}
implied_one = clone_stree(*to);
implied_two = clone_stree(stree);
match_states_stree(&implied_one, &implied_two);
call_pre_merge_hooks(&implied_one, &implied_two);
if (add_pool) {
clone_pool_havers_stree(&implied_one);
clone_pool_havers_stree(&implied_two);
set_stree_id(&implied_one, ++__stree_id);
set_stree_id(&implied_two, ++__stree_id);
if (implied_one->base_stree)
set_stree_id(&implied_one->base_stree, ++__stree_id);
if (implied_two->base_stree)
set_stree_id(&implied_two->base_stree, ++__stree_id);
}
push_stree(&all_pools, implied_one);
push_stree(&all_pools, implied_two);
avl_iter_begin(&one_iter, implied_one, FORWARD);
avl_iter_begin(&two_iter, implied_two, FORWARD);
for (;;) {
if (!one_iter.sm || !two_iter.sm)
break;
one = one_iter.sm;
two = two_iter.sm;
if (one == two) {
avl_insert(&results, one);
goto next;
}
if (add_pool) {
one->pool = implied_one;
if (implied_one->base_stree)
one->pool = implied_one->base_stree;
two->pool = implied_two;
if (implied_two->base_stree)
two->pool = implied_two->base_stree;
}
res = merge_sm_states(one, two);
add_possible_sm(res, one);
add_possible_sm(res, two);
avl_insert(&results, res);
next:
avl_iter_next(&one_iter);
avl_iter_next(&two_iter);
}
free_stree(to);
*to = results;
}
void merge_stree(struct stree **to, struct stree *stree)
{
__merge_stree(to, stree, 1);
}
void merge_stree_no_pools(struct stree **to, struct stree *stree)
{
__merge_stree(to, stree, 0);
}
void merge_fake_stree(struct stree **to, struct stree *stree)
{
struct stree *one = *to;
struct stree *two = stree;
struct sm_state *sm;
struct state_list *add_to_one = NULL;
struct state_list *add_to_two = NULL;
AvlIter one_iter;
AvlIter two_iter;
if (!stree)
return;
if (*to == stree)
return;
if (!*to) {
*to = clone_stree(stree);
return;
}
avl_iter_begin(&one_iter, one, FORWARD);
avl_iter_begin(&two_iter, two, FORWARD);
for (;;) {
if (!one_iter.sm && !two_iter.sm)
break;
if (cmp_tracker(one_iter.sm, two_iter.sm) < 0) {
sm = get_sm_state(one_iter.sm->owner, one_iter.sm->name,
one_iter.sm->sym);
if (sm)
add_ptr_list(&add_to_two, sm);
avl_iter_next(&one_iter);
} else if (cmp_tracker(one_iter.sm, two_iter.sm) == 0) {
avl_iter_next(&one_iter);
avl_iter_next(&two_iter);
} else {
sm = get_sm_state(two_iter.sm->owner, two_iter.sm->name,
two_iter.sm->sym);
if (sm)
add_ptr_list(&add_to_one, sm);
avl_iter_next(&two_iter);
}
}
FOR_EACH_PTR(add_to_one, sm) {
avl_insert(&one, sm);
} END_FOR_EACH_PTR(sm);
FOR_EACH_PTR(add_to_two, sm) {
avl_insert(&two, sm);
} END_FOR_EACH_PTR(sm);
one->base_stree = clone_stree(__get_cur_stree());
FOR_EACH_SM(one, sm) {
avl_insert(&one->base_stree, sm);
} END_FOR_EACH_SM(sm);
two->base_stree = clone_stree(__get_cur_stree());
FOR_EACH_SM(two, sm) {
avl_insert(&two->base_stree, sm);
} END_FOR_EACH_SM(sm);
free_slist(&add_to_one);
free_slist(&add_to_two);
__merge_stree(&one, two, 1);
*to = one;
}
void filter_stree(struct stree **stree, struct stree *filter)
{
struct stree *results = NULL;
AvlIter one_iter;
AvlIter two_iter;
avl_iter_begin(&one_iter, *stree, FORWARD);
avl_iter_begin(&two_iter, filter, FORWARD);
for (;;) {
if (!one_iter.sm && !two_iter.sm)
break;
if (cmp_tracker(one_iter.sm, two_iter.sm) < 0) {
avl_insert(&results, one_iter.sm);
avl_iter_next(&one_iter);
} else if (cmp_tracker(one_iter.sm, two_iter.sm) == 0) {
if (one_iter.sm != two_iter.sm)
avl_insert(&results, one_iter.sm);
avl_iter_next(&one_iter);
avl_iter_next(&two_iter);
} else {
avl_iter_next(&two_iter);
}
}
free_stree(stree);
*stree = results;
}
void and_stree_stack(struct stree_stack **stack)
{
struct sm_state *tmp;
struct stree *right_stree = pop_stree(stack);
FOR_EACH_SM(right_stree, tmp) {
overwrite_sm_state_stree_stack(stack, tmp);
} END_FOR_EACH_SM(tmp);
free_stree(&right_stree);
}
void or_stree_stack(struct stree_stack **pre_conds,
struct stree *cur_stree,
struct stree_stack **stack)
{
struct stree *new;
struct stree *old;
struct stree *pre_stree;
struct stree *res;
struct stree *tmp_stree;
new = pop_stree(stack);
old = pop_stree(stack);
pre_stree = pop_stree(pre_conds);
push_stree(pre_conds, clone_stree(pre_stree));
res = clone_stree(pre_stree);
overwrite_stree(old, &res);
tmp_stree = clone_stree(cur_stree);
overwrite_stree(new, &tmp_stree);
merge_stree(&res, tmp_stree);
filter_stree(&res, pre_stree);
push_stree(stack, res);
free_stree(&tmp_stree);
free_stree(&pre_stree);
free_stree(&new);
free_stree(&old);
}
struct stree **get_named_stree(struct named_stree_stack *stack,
const char *name,
struct symbol *sym)
{
struct named_stree *tmp;
FOR_EACH_PTR(stack, tmp) {
if (tmp->sym == sym &&
strcmp(tmp->name, name) == 0)
return &tmp->stree;
} END_FOR_EACH_PTR(tmp);
return NULL;
}
void overwrite_stree(struct stree *from, struct stree **to)
{
struct sm_state *tmp;
FOR_EACH_SM(from, tmp) {
overwrite_sm_state_stree(to, tmp);
} END_FOR_EACH_SM(tmp);
}