11f5207bJohn Levon/*
21f5207bJohn Levon * Copyright (C) 2014 Oracle.
31f5207bJohn Levon *
41f5207bJohn Levon * This program is free software; you can redistribute it and/or
51f5207bJohn Levon * modify it under the terms of the GNU General Public License
61f5207bJohn Levon * as published by the Free Software Foundation; either version 2
71f5207bJohn Levon * of the License, or (at your option) any later version.
81f5207bJohn Levon *
91f5207bJohn Levon * This program is distributed in the hope that it will be useful,
101f5207bJohn Levon * but WITHOUT ANY WARRANTY; without even the implied warranty of
121f5207bJohn Levon * GNU General Public License for more details.
131f5207bJohn Levon *
141f5207bJohn Levon * You should have received a copy of the GNU General Public License
151f5207bJohn Levon * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
161f5207bJohn Levon */
171f5207bJohn Levon
181f5207bJohn Levon#define _GNU_SOURCE
191f5207bJohn Levon#include <string.h>
201f5207bJohn Levon#include "smatch.h"
211f5207bJohn Levon#include "smatch_slist.h"
221f5207bJohn Levon
231f5207bJohn Levonstatic int my_id;
241f5207bJohn Levon
251f5207bJohn LevonSTATE(checked);
261f5207bJohn LevonSTATE(modified);
271f5207bJohn Levon
281f5207bJohn Levonstruct stree *to_check;
291f5207bJohn Levon
301f5207bJohn Levonstatic struct statement *get_cur_stmt(void)
311f5207bJohn Levon{
321f5207bJohn Levon	return last_ptr_list((struct ptr_list *)big_statement_stack);
331f5207bJohn Levon}
341f5207bJohn Levon
351f5207bJohn Levonstatic void set_modified(struct sm_state *sm, struct expression *mod_expr)
361f5207bJohn Levon{
371f5207bJohn Levon	set_state(my_id, sm->name, sm->sym, &modified);
381f5207bJohn Levon}
391f5207bJohn Levon
401f5207bJohn Levonstatic struct expression *strip_condition(struct expression *expr)
411f5207bJohn Levon{
421f5207bJohn Levon	expr = strip_expr(expr);
431f5207bJohn Levon
441f5207bJohn Levon	if (expr->type == EXPR_PREOP && expr->op == '!')
451f5207bJohn Levon		return strip_condition(expr->unop);
461f5207bJohn Levon
471f5207bJohn Levon	if (expr->type == EXPR_COMPARE &&
481f5207bJohn Levon	    (expr->op == SPECIAL_EQUAL ||
491f5207bJohn Levon	     expr->op == SPECIAL_NOTEQUAL)) {
50c85f09cJohn Levon		if (expr_is_zero(expr->left))
511f5207bJohn Levon			return strip_condition(expr->right);
52c85f09cJohn Levon		if (expr_is_zero(expr->right))
531f5207bJohn Levon			return strip_condition(expr->left);
541f5207bJohn Levon	}
551f5207bJohn Levon
561f5207bJohn Levon	return expr;
571f5207bJohn Levon}
581f5207bJohn Levon
591f5207bJohn Levonstatic int conditions_match(struct expression *cond, struct expression *prev)
601f5207bJohn Levon{
611f5207bJohn Levon	prev = strip_condition(prev);
621f5207bJohn Levon
631f5207bJohn Levon	if (prev == cond)
641f5207bJohn Levon		return 1;
651f5207bJohn Levon
661f5207bJohn Levon	if (prev->type == EXPR_LOGICAL) {
671f5207bJohn Levon		if (conditions_match(cond, prev->left) ||
681f5207bJohn Levon		    conditions_match(cond, prev->right))
691f5207bJohn Levon			return 1;
701f5207bJohn Levon	}
711f5207bJohn Levon
721f5207bJohn Levon	return 0;
731f5207bJohn Levon}
741f5207bJohn Levon
751f5207bJohn Levon/*
761f5207bJohn Levon * People like to do "if (foo) { ... } else if (!foo) { ... }".  Don't
771f5207bJohn Levon * complain when they do that even though it is nonsense.
781f5207bJohn Levon */
791f5207bJohn Levonstatic int is_obvious_else(struct expression *cond)
801f5207bJohn Levon{
811f5207bJohn Levon	struct statement *parent;
821f5207bJohn Levon	struct expression *prev;
831f5207bJohn Levon
841f5207bJohn Levon	if (!get_cur_stmt())
851f5207bJohn Levon		return 0;
861f5207bJohn Levon	parent = get_cur_stmt()->parent;
871f5207bJohn Levon	if (!parent)
881f5207bJohn Levon		return 0;
891f5207bJohn Levon
901f5207bJohn Levon	if (parent->type != STMT_IF)
911f5207bJohn Levon		return 0;
921f5207bJohn Levon
931f5207bJohn Levon	if (!parent->if_false)
941f5207bJohn Levon		return 0;
951f5207bJohn Levon	if (parent->if_false != get_cur_stmt())
961f5207bJohn Levon		return 0;
971f5207bJohn Levon
981f5207bJohn Levon	prev = strip_condition(parent->if_conditional);
991f5207bJohn Levon
1001f5207bJohn Levon	return conditions_match(cond, prev);
1011f5207bJohn Levon}
1021f5207bJohn Levon
1031f5207bJohn Levonstatic int name_means_synchronize(const char *name)
1041f5207bJohn Levon{
1051f5207bJohn Levon	if (!name)
1061f5207bJohn Levon		return 0;
1071f5207bJohn Levon
1081f5207bJohn Levon	if (strcasestr(name, "wait"))
1091f5207bJohn Levon		return 1;
1101f5207bJohn Levon	if (strcasestr(name, "down"))
1111f5207bJohn Levon		return 1;
1121f5207bJohn Levon	if (strcasestr(name, "lock") && !strcasestr(name, "unlock"))
1131f5207bJohn Levon		return 1;
1141f5207bJohn Levon	if (strcasestr(name, "delay"))
1151f5207bJohn Levon		return 1;
1161f5207bJohn Levon	if (strcasestr(name, "schedule"))
1171f5207bJohn Levon		return 1;
1181f5207bJohn Levon	if (strcmp(name, "smp_rmb") == 0)
1191f5207bJohn Levon		return 1;
1201f5207bJohn Levon	if (strcmp(name, "mb") == 0)
1211f5207bJohn Levon		return 1;
1221f5207bJohn Levon	if (strcmp(name, "barrier") == 0)
1231f5207bJohn Levon		return 1;
1241f5207bJohn Levon	return 0;
1251f5207bJohn Levon}
1261f5207bJohn Levon
1271f5207bJohn Levonstatic int previous_statement_was_synchronize(void)
1281f5207bJohn Levon{
1291f5207bJohn Levon	struct statement *stmt;
1301f5207bJohn Levon	struct position pos;
1311f5207bJohn Levon	struct position prev_pos;
1321f5207bJohn Levon	char *ident;
1331f5207bJohn Levon
134c85f09cJohn Levon	if (!__cur_stmt)
135c85f09cJohn Levon		return 0;
136c85f09cJohn Levon
1371f5207bJohn Levon	if (__prev_stmt) {
1381f5207bJohn Levon		prev_pos = __prev_stmt->pos;
1391f5207bJohn Levon		prev_pos.line -= 3;
1401f5207bJohn Levon	} else {
1411f5207bJohn Levon		prev_pos = __cur_stmt->pos;
1421f5207bJohn Levon		prev_pos.line -= 5;
1431f5207bJohn Levon	}
1441f5207bJohn Levon
1451f5207bJohn Levon	FOR_EACH_PTR_REVERSE(big_statement_stack, stmt) {
1461f5207bJohn Levon		if (stmt->pos.line < prev_pos.line)
1471f5207bJohn Levon			return 0;
1481f5207bJohn Levon		pos = stmt->pos;
1491f5207bJohn Levon		ident = get_macro_name(pos);
1501f5207bJohn Levon		if (name_means_synchronize(ident))
1511f5207bJohn Levon			return 1;
1521f5207bJohn Levon		ident = pos_ident(pos);
1531f5207bJohn Levon		if (!ident)
1541f5207bJohn Levon			continue;
1551f5207bJohn Levon		if (strcmp(ident, "if") == 0) {
1561f5207bJohn Levon			pos.pos += 4;
1571f5207bJohn Levon			ident = pos_ident(pos);
1581f5207bJohn Levon			if (!ident)
1591f5207bJohn Levon				continue;
1601f5207bJohn Levon		}
1611f5207bJohn Levon		if (name_means_synchronize(ident))
1621f5207bJohn Levon			return 1;
1631f5207bJohn Levon	} END_FOR_EACH_PTR_REVERSE(stmt);
1641f5207bJohn Levon	return 0;
1651f5207bJohn Levon}
1661f5207bJohn Levon
1671f5207bJohn Levonstatic void match_condition(struct expression *expr)
1681f5207bJohn Levon{
1691f5207bJohn Levon	struct smatch_state *state;
1701f5207bJohn Levon	sval_t dummy;
1711f5207bJohn Levon	char *name;
1721f5207bJohn Levon
1731f5207bJohn Levon	if (inside_loop())
1741f5207bJohn Levon		return;
1751f5207bJohn Levon
1761f5207bJohn Levon	if (get_value(expr, &dummy))
1771f5207bJohn Levon		return;
1781f5207bJohn Levon
1791f5207bJohn Levon	if (get_macro_name(expr->pos))
1801f5207bJohn Levon		return;
1811f5207bJohn Levon
1821f5207bJohn Levon	state = get_stored_condition(expr);
1831f5207bJohn Levon	if (!state || !state->data)
1841f5207bJohn Levon		return;
1851f5207bJohn Levon	if (get_macro_name(((struct expression *)state->data)->pos))
1861f5207bJohn Levon		return;
1871f5207bJohn Levon
1881f5207bJohn Levon	/*
1891f5207bJohn Levon	 * we allow double checking for NULL because people do this all the time
1901f5207bJohn Levon	 * and trying to stop them is a losers' battle.
1911f5207bJohn Levon	 */
1921f5207bJohn Levon	if (is_pointer(expr) && implied_condition_true(expr))
1931f5207bJohn Levon		return;
1941f5207bJohn Levon
1951f5207bJohn Levon	if (definitely_inside_loop()) {
1961f5207bJohn Levon		struct symbol *sym;
1971f5207bJohn Levon
1981f5207bJohn Levon		if (__inline_fn)
1991f5207bJohn Levon			return;
2001f5207bJohn Levon
2011f5207bJohn Levon		name = expr_to_var_sym(expr, &sym);
2021f5207bJohn Levon		if (!name)
2031f5207bJohn Levon			return;
2041f5207bJohn Levon		set_state_expr(my_id, expr, &checked);
2051f5207bJohn Levon		set_state_stree(&to_check, my_id, name, sym, &checked);
2061f5207bJohn Levon		free_string(name);
2071f5207bJohn Levon		return;
2081f5207bJohn Levon	}
2091f5207bJohn Levon
2101f5207bJohn Levon	if (is_obvious_else(state->data))
2111f5207bJohn Levon		return;
2121f5207bJohn Levon
2131f5207bJohn Levon	/*
2141f5207bJohn Levon	 * It's common to test something, then take a lock and test if it is
2151f5207bJohn Levon	 * still true.
2161f5207bJohn Levon	 */
2171f5207bJohn Levon	if (previous_statement_was_synchronize())
2181f5207bJohn Levon		return;
2191f5207bJohn Levon
2201f5207bJohn Levon	name = expr_to_str(expr);
2211f5207bJohn Levon	sm_warning("we tested '%s' before and it was '%s'", name, state->name);
2221f5207bJohn Levon	free_string(name);
2231f5207bJohn Levon}
2241f5207bJohn Levon
2251f5207bJohn Levonint get_check_line(struct sm_state *sm)
2261f5207bJohn Levon{
2271f5207bJohn Levon	struct sm_state *tmp;
2281f5207bJohn Levon
2291f5207bJohn Levon	FOR_EACH_PTR(sm->possible, tmp) {
2301f5207bJohn Levon		if (tmp->state == &checked)
2311f5207bJohn Levon			return tmp->line;
2321f5207bJohn Levon	} END_FOR_EACH_PTR(tmp);
2331f5207bJohn Levon
2341f5207bJohn Levon	return get_lineno();
2351f5207bJohn Levon}
2361f5207bJohn Levon
2371f5207bJohn Levonstatic void after_loop(struct statement *stmt)
2381f5207bJohn Levon{
2391f5207bJohn Levon	struct sm_state *check, *sm;
2401f5207bJohn Levon
2411f5207bJohn Levon	if (!stmt || stmt->type != STMT_ITERATOR)
2421f5207bJohn Levon		return;
2431f5207bJohn Levon	if (definitely_inside_loop())
2441f5207bJohn Levon		return;
2451f5207bJohn Levon	if (__inline_fn)
2461f5207bJohn Levon		return;
2471f5207bJohn Levon
2481f5207bJohn Levon	FOR_EACH_SM(to_check, check) {
2491f5207bJohn Levon		continue;
2501f5207bJohn Levon		sm = get_sm_state(my_id, check->name, check->sym);
2511f5207bJohn Levon		continue;
2521f5207bJohn Levon		if (!sm)
2531f5207bJohn Levon			continue;
2541f5207bJohn Levon		if (slist_has_state(sm->possible, &modified))
2551f5207bJohn Levon			continue;
2561f5207bJohn Levon
2571f5207bJohn Levon		sm_printf("%s:%d %s() ", get_filename(), get_check_line(sm), get_function());
2581f5207bJohn Levon		sm_printf("warn: we tested '%s' already\n", check->name);
2591f5207bJohn Levon	} END_FOR_EACH_SM(check);
2601f5207bJohn Levon
2611f5207bJohn Levon	free_stree(&to_check);
2621f5207bJohn Levon}
2631f5207bJohn Levon
2641f5207bJohn Levonstatic void match_func_end(struct symbol *sym)
2651f5207bJohn Levon{
2661f5207bJohn Levon	if (__inline_fn)
2671f5207bJohn Levon		return;
2681f5207bJohn Levon	if (to_check)
2691f5207bJohn Levon		sm_msg("debug: odd...  found an function without an end.");
2701f5207bJohn Levon	free_stree(&to_check);
2711f5207bJohn Levon}
2721f5207bJohn Levon
2731f5207bJohn Levonvoid check_double_checking(int id)
2741f5207bJohn Levon{
2751f5207bJohn Levon	my_id = id;
2761f5207bJohn Levon
2771f5207bJohn Levon	if (!option_spammy)
2781f5207bJohn Levon		return;
2791f5207bJohn Levon
2801f5207bJohn Levon	add_hook(&match_condition, CONDITION_HOOK);
2811f5207bJohn Levon	add_modification_hook(my_id, &set_modified);
2821f5207bJohn Levon	add_hook(after_loop, STMT_HOOK_AFTER);
2831f5207bJohn Levon	add_hook(&match_func_end, AFTER_FUNC_HOOK);
2841f5207bJohn Levon}