#include "smatch.h"
#include "smatch_slist.h"
#include "smatch_extra.h"
#include <md5.h>
static int my_id;
mtag_t str_to_mtag(const char *str)
{
unsigned char c[MD5_DIGEST_LENGTH];
unsigned long long *tag = (unsigned long long *)&c;
int len;
len = strlen(str);
md5_calc(c, str, len);
*tag &= ~MTAG_ALIAS_BIT;
*tag &= ~MTAG_OFFSET_MASK;
return *tag;
}
static int save_allocator(void *_allocator, int argc, char **argv, char **azColName)
{
char **allocator = _allocator;
if (*allocator) {
if (strcmp(*allocator, argv[0]) == 0)
return 0;
free_string(*allocator);
*allocator = alloc_string("unknown");
return 0;
}
*allocator = alloc_string(argv[0]);
return 0;
}
char *get_allocator_info_from_tag(mtag_t tag)
{
char *allocator = NULL;
run_sql(save_allocator, &allocator,
"select value from mtag_info where tag = %lld and type = %d;",
tag, ALLOCATOR);
return allocator;
}
static char *get_allocator_info(struct expression *expr, struct smatch_state *state)
{
sval_t sval;
if (expr->type != EXPR_ASSIGNMENT)
return NULL;
if (estate_get_single_value(state, &sval))
return get_allocator_info_from_tag(sval.value);
expr = strip_expr(expr->right);
if (expr->type != EXPR_CALL ||
!expr->fn ||
expr->fn->type != EXPR_SYMBOL)
return NULL;
return expr_to_str(expr->fn);
}
static void update_mtag_info(struct expression *expr, mtag_t tag,
const char *left_name, const char *tag_info,
struct smatch_state *state)
{
char *allocator;
sql_insert_mtag_about(tag, left_name, tag_info);
allocator = get_allocator_info(expr, state);
if (allocator)
sql_insert_mtag_info(tag, ALLOCATOR, allocator);
}
struct smatch_state *get_mtag_return(struct expression *expr, struct smatch_state *state)
{
struct expression *left, *right;
char *left_name, *right_name;
struct symbol *left_sym;
struct range_list *rl;
char buf[256];
mtag_t tag;
sval_t tag_sval;
if (!expr || expr->type != EXPR_ASSIGNMENT || expr->op != '=')
return NULL;
if (!is_fresh_alloc(expr->right))
return NULL;
if (!rl_intersection(estate_rl(state), valid_ptr_rl))
return NULL;
left = strip_expr(expr->left);
right = strip_expr(expr->right);
left_name = expr_to_str_sym(left, &left_sym);
if (!left_name || !left_sym)
return NULL;
right_name = expr_to_str(right);
snprintf(buf, sizeof(buf), "%s %s %s %s", get_filename(), get_function(),
left_name, right_name);
tag = str_to_mtag(buf);
tag_sval.type = estate_type(state);
tag_sval.uvalue = tag;
rl = rl_filter(estate_rl(state), valid_ptr_rl);
rl = clone_rl(rl);
add_range(&rl, tag_sval, tag_sval);
update_mtag_info(expr, tag, left_name, buf, state);
free_string(left_name);
free_string(right_name);
return alloc_estate_rl(rl);
}
int get_string_mtag(struct expression *expr, mtag_t *tag)
{
mtag_t xor;
if (expr->type != EXPR_STRING || !expr->string)
return 0;
xor = str_to_mtag("__smatch string");
*tag = str_to_mtag(expr->string->data);
*tag = *tag ^ xor;
return 1;
}
int get_toplevel_mtag(struct symbol *sym, mtag_t *tag)
{
char buf[256];
if (!sym)
return 0;
if (!sym->ident ||
!(sym->ctype.modifiers & MOD_TOPLEVEL))
return 0;
snprintf(buf, sizeof(buf), "%s %s",
(sym->ctype.modifiers & MOD_STATIC) ? get_filename() : "extern",
sym->ident->name);
*tag = str_to_mtag(buf);
return 1;
}
bool get_symbol_mtag(struct symbol *sym, mtag_t *tag)
{
char buf[256];
if (!sym || !sym->ident)
return false;
if (get_toplevel_mtag(sym, tag))
return true;
if (get_param_num_from_sym(sym) >= 0)
return false;
snprintf(buf, sizeof(buf), "%s %s %s",
get_filename(), get_function(), sym->ident->name);
*tag = str_to_mtag(buf);
return true;
}
static void global_variable(struct symbol *sym)
{
mtag_t tag;
if (!get_toplevel_mtag(sym, &tag))
return;
sql_insert_mtag_about(tag,
sym->ident->name,
(sym->ctype.modifiers & MOD_STATIC) ? get_filename() : "extern");
}
static int get_array_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
{
struct expression *array, *offset_expr;
struct symbol *type;
sval_t sval;
int start_offset;
if (!is_array(expr))
return 0;
array = get_array_base(expr);
if (!array)
return 0;
type = get_type(array);
if (!type || type->type != SYM_ARRAY)
return 0;
type = get_real_base_type(type);
if (!type_bytes(type))
return 0;
if (!expr_to_mtag_offset(array, tag, &start_offset))
return 0;
offset_expr = get_array_offset(expr);
if (!get_value(offset_expr, &sval))
return 0;
*offset = start_offset + sval.value * type_bytes(type);
return 1;
}
struct range_list *swap_mtag_seed(struct expression *expr, struct range_list *rl)
{
char buf[256];
char *name;
sval_t sval;
mtag_t tag;
if (!rl_to_sval(rl, &sval))
return rl;
if (sval.type->type != SYM_PTR || sval.uvalue != MTAG_SEED)
return rl;
name = expr_to_str(expr);
snprintf(buf, sizeof(buf), "%s %s %s", get_filename(), get_function(), name);
free_string(name);
tag = str_to_mtag(buf);
sval.value = tag;
return alloc_rl(sval, sval);
}
int create_mtag_alias(mtag_t tag, struct expression *expr, mtag_t *new)
{
char buf[256];
int lines_from_start;
char *str;
if (!cur_func_sym)
return 0;
lines_from_start = expr->pos.line - cur_func_sym->pos.line;
str = expr_to_str(expr);
snprintf(buf, sizeof(buf), "%lld %d %s", tag, lines_from_start, str);
free_string(str);
*new = str_to_mtag(buf);
sql_insert_mtag_alias(tag, *new);
return 1;
}
static int get_implied_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
{
struct smatch_state *state;
struct symbol *type;
sval_t sval;
type = get_type(expr);
if (!type_is_ptr(type))
return 0;
state = get_extra_state(expr);
if (!state || !estate_get_single_value(state, &sval) || sval.value == 0)
return 0;
*tag = sval.uvalue & ~MTAG_OFFSET_MASK;
*offset = sval.uvalue & MTAG_OFFSET_MASK;
return 1;
}
int expr_to_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
{
*tag = 0;
*offset = 0;
if (bits_in_pointer != 64)
return 0;
expr = strip_expr(expr);
if (!expr)
return 0;
if (is_array(expr))
return get_array_mtag_offset(expr, tag, offset);
if (expr->type == EXPR_PREOP && expr->op == '*') {
expr = strip_expr(expr->unop);
return get_implied_mtag_offset(expr, tag, offset);
} else if (expr->type == EXPR_DEREF) {
int tmp, tmp_offset = 0;
while (expr->type == EXPR_DEREF) {
tmp = get_member_offset_from_deref(expr);
if (tmp < 0)
return 0;
tmp_offset += tmp;
expr = strip_expr(expr->deref);
}
*offset = tmp_offset;
if (expr->type == EXPR_PREOP && expr->op == '*') {
expr = strip_expr(expr->unop);
if (get_implied_mtag_offset(expr, tag, &tmp_offset)) {
if (tmp_offset)
return 0;
return 1;
}
return 0;
} else if (expr->type == EXPR_SYMBOL) {
return get_symbol_mtag(expr->symbol, tag);
}
return 0;
} else if (expr->type == EXPR_SYMBOL) {
return get_symbol_mtag(expr->symbol, tag);
}
return 0;
}
int get_mtag_sval(struct expression *expr, sval_t *sval)
{
struct symbol *type;
mtag_t tag;
int offset = 0;
if (bits_in_pointer != 64)
return 0;
expr = strip_expr(expr);
type = get_type(expr);
if (!type_is_ptr(type))
return 0;
if (expr->type == EXPR_STRING && get_string_mtag(expr, &tag))
goto found;
if (expr->type == EXPR_SYMBOL &&
(type->type == SYM_ARRAY || type->type == SYM_FN) &&
get_toplevel_mtag(expr->symbol, &tag))
goto found;
if (expr->type == EXPR_PREOP && expr->op == '&') {
expr = strip_expr(expr->unop);
if (expr_to_mtag_offset(expr, &tag, &offset))
goto found;
return 0;
}
if (get_implied_mtag_offset(expr, &tag, &offset))
goto found;
return 0;
found:
if (offset >= MTAG_OFFSET_MASK)
return 0;
sval->type = type;
sval->uvalue = tag | offset;
return 1;
}
void register_mtag(int id)
{
my_id = id;
add_hook(&global_variable, BASE_HOOK);
}