#define _GNU_SOURCE
#include <string.h>
#include "smatch.h"
#include "smatch_slist.h"
static int my_id;
STATE(checked);
STATE(modified);
struct stree *to_check;
static struct statement *get_cur_stmt(void)
{
return last_ptr_list((struct ptr_list *)big_statement_stack);
}
static void set_modified(struct sm_state *sm, struct expression *mod_expr)
{
set_state(my_id, sm->name, sm->sym, &modified);
}
static struct expression *strip_condition(struct expression *expr)
{
expr = strip_expr(expr);
if (expr->type == EXPR_PREOP && expr->op == '!')
return strip_condition(expr->unop);
if (expr->type == EXPR_COMPARE &&
(expr->op == SPECIAL_EQUAL ||
expr->op == SPECIAL_NOTEQUAL)) {
if (expr_is_zero(expr->left))
return strip_condition(expr->right);
if (expr_is_zero(expr->right))
return strip_condition(expr->left);
}
return expr;
}
static int conditions_match(struct expression *cond, struct expression *prev)
{
prev = strip_condition(prev);
if (prev == cond)
return 1;
if (prev->type == EXPR_LOGICAL) {
if (conditions_match(cond, prev->left) ||
conditions_match(cond, prev->right))
return 1;
}
return 0;
}
static int is_obvious_else(struct expression *cond)
{
struct statement *parent;
struct expression *prev;
if (!get_cur_stmt())
return 0;
parent = get_cur_stmt()->parent;
if (!parent)
return 0;
if (parent->type != STMT_IF)
return 0;
if (!parent->if_false)
return 0;
if (parent->if_false != get_cur_stmt())
return 0;
prev = strip_condition(parent->if_conditional);
return conditions_match(cond, prev);
}
static int name_means_synchronize(const char *name)
{
if (!name)
return 0;
if (strcasestr(name, "wait"))
return 1;
if (strcasestr(name, "down"))
return 1;
if (strcasestr(name, "lock") && !strcasestr(name, "unlock"))
return 1;
if (strcasestr(name, "delay"))
return 1;
if (strcasestr(name, "schedule"))
return 1;
if (strcmp(name, "smp_rmb") == 0)
return 1;
if (strcmp(name, "mb") == 0)
return 1;
if (strcmp(name, "barrier") == 0)
return 1;
return 0;
}
static int previous_statement_was_synchronize(void)
{
struct statement *stmt;
struct position pos;
struct position prev_pos;
char *ident;
if (!__cur_stmt)
return 0;
if (__prev_stmt) {
prev_pos = __prev_stmt->pos;
prev_pos.line -= 3;
} else {
prev_pos = __cur_stmt->pos;
prev_pos.line -= 5;
}
FOR_EACH_PTR_REVERSE(big_statement_stack, stmt) {
if (stmt->pos.line < prev_pos.line)
return 0;
pos = stmt->pos;
ident = get_macro_name(pos);
if (name_means_synchronize(ident))
return 1;
ident = pos_ident(pos);
if (!ident)
continue;
if (strcmp(ident, "if") == 0) {
pos.pos += 4;
ident = pos_ident(pos);
if (!ident)
continue;
}
if (name_means_synchronize(ident))
return 1;
} END_FOR_EACH_PTR_REVERSE(stmt);
return 0;
}
static void match_condition(struct expression *expr)
{
struct smatch_state *state;
sval_t dummy;
char *name;
if (inside_loop())
return;
if (get_value(expr, &dummy))
return;
if (get_macro_name(expr->pos))
return;
state = get_stored_condition(expr);
if (!state || !state->data)
return;
if (get_macro_name(((struct expression *)state->data)->pos))
return;
if (is_pointer(expr) && implied_condition_true(expr))
return;
if (definitely_inside_loop()) {
struct symbol *sym;
if (__inline_fn)
return;
name = expr_to_var_sym(expr, &sym);
if (!name)
return;
set_state_expr(my_id, expr, &checked);
set_state_stree(&to_check, my_id, name, sym, &checked);
free_string(name);
return;
}
if (is_obvious_else(state->data))
return;
if (previous_statement_was_synchronize())
return;
name = expr_to_str(expr);
sm_warning("we tested '%s' before and it was '%s'", name, state->name);
free_string(name);
}
int get_check_line(struct sm_state *sm)
{
struct sm_state *tmp;
FOR_EACH_PTR(sm->possible, tmp) {
if (tmp->state == &checked)
return tmp->line;
} END_FOR_EACH_PTR(tmp);
return get_lineno();
}
static void after_loop(struct statement *stmt)
{
struct sm_state *check, *sm;
if (!stmt || stmt->type != STMT_ITERATOR)
return;
if (definitely_inside_loop())
return;
if (__inline_fn)
return;
FOR_EACH_SM(to_check, check) {
continue;
sm = get_sm_state(my_id, check->name, check->sym);
continue;
if (!sm)
continue;
if (slist_has_state(sm->possible, &modified))
continue;
sm_printf("%s:%d %s() ", get_filename(), get_check_line(sm), get_function());
sm_printf("warn: we tested '%s' already\n", check->name);
} END_FOR_EACH_SM(check);
free_stree(&to_check);
}
static void match_func_end(struct symbol *sym)
{
if (__inline_fn)
return;
if (to_check)
sm_msg("debug: odd... found an function without an end.");
free_stree(&to_check);
}
void check_double_checking(int id)
{
my_id = id;
if (!option_spammy)
return;
add_hook(&match_condition, CONDITION_HOOK);
add_modification_hook(my_id, &set_modified);
add_hook(after_loop, STMT_HOOK_AFTER);
add_hook(&match_func_end, AFTER_FUNC_HOOK);
}