1/*
2 * Copyright (C) 2012 Oracle.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
16 */
17
18#include "smatch.h"
19#include "smatch_function_hashtable.h"
20
21static int my_id;
22
23DEFINE_STRING_HASHTABLE_STATIC(unconstant_macros);
24
25static int does_inc_dec(struct expression *expr)
26{
27	if (expr->type == EXPR_PREOP || expr->type == EXPR_POSTOP) {
28		if (expr->op == SPECIAL_INCREMENT || expr->op == SPECIAL_DECREMENT)
29			return 1;
30		return does_inc_dec(expr->unop);
31	}
32	return 0;
33}
34
35static int expr_equiv_no_inc_dec(struct expression *one, struct expression *two)
36{
37	if (does_inc_dec(one) || does_inc_dec(two))
38		return 0;
39	return expr_equiv(one, two);
40}
41
42static int inconsistent_check(struct expression *left, struct expression *right)
43{
44	sval_t sval;
45
46	if (get_value(left->left, &sval)) {
47		if (get_value(right->left, &sval))
48			return expr_equiv_no_inc_dec(left->right, right->right);
49		if (get_value(right->right, &sval))
50			return expr_equiv_no_inc_dec(left->right, right->left);
51		return 0;
52	}
53	if (get_value(left->right, &sval)) {
54		if (get_value(right->left, &sval))
55			return expr_equiv_no_inc_dec(left->left, right->right);
56		if (get_value(right->right, &sval))
57			return expr_equiv_no_inc_dec(left->left, right->left);
58		return 0;
59	}
60
61	return 0;
62}
63
64static void check_or(struct expression *expr)
65{
66	struct expression *left, *right;
67
68	left = strip_expr(expr->left);
69	right = strip_expr(expr->right);
70
71	if (left->type != EXPR_COMPARE || left->op != SPECIAL_NOTEQUAL)
72		return;
73	if (right->type != EXPR_COMPARE || right->op != SPECIAL_NOTEQUAL)
74		return;
75	if (!inconsistent_check(left, right))
76		return;
77
78	sm_warning("was && intended here instead of ||?");
79}
80
81static int is_kernel_min_macro(struct expression *expr)
82{
83	char *macro;
84
85	if (option_project != PROJ_KERNEL)
86		return 0;
87	macro = get_macro_name(expr->pos);
88	if (!macro)
89		return 0;
90	if (strcmp(macro, "min") == 0 ||
91	    strcmp(macro, "min_t") == 0 ||
92	    strcmp(macro, "max") == 0 ||
93	    strcmp(macro, "max_t") == 0)
94		return 1;
95	return 0;
96}
97
98static void check_and(struct expression *expr)
99{
100	struct expression *left, *right;
101
102	if (is_kernel_min_macro(expr))
103		return;
104
105	left = strip_expr(expr->left);
106	right = strip_expr(expr->right);
107
108	if (left->type != EXPR_COMPARE || left->op != SPECIAL_EQUAL)
109		return;
110	if (right->type != EXPR_COMPARE || right->op != SPECIAL_EQUAL)
111		return;
112	if (!inconsistent_check(left, right))
113		return;
114
115	sm_warning("was || intended here instead of &&?");
116}
117
118static void match_logic(struct expression *expr)
119{
120	if (expr->type != EXPR_LOGICAL)
121		return;
122
123	if (expr->op == SPECIAL_LOGICAL_OR)
124		check_or(expr);
125	if (expr->op == SPECIAL_LOGICAL_AND)
126		check_and(expr);
127}
128
129static int is_unconstant_macro(struct expression *expr)
130{
131	char *macro;
132
133	macro = get_macro_name(expr->pos);
134	if (!macro)
135		return 0;
136	if (search_unconstant_macros(unconstant_macros, macro))
137		return 1;
138	return 0;
139}
140
141static void match_condition(struct expression *expr)
142{
143	sval_t sval;
144
145	if (expr->type != EXPR_BINOP)
146		return;
147	if (expr->op == '|') {
148		if (get_value(expr->left, &sval) || get_value(expr->right, &sval))
149			sm_warning("suspicious bitop condition");
150		return;
151	}
152
153	if (expr->op != '&')
154		return;
155
156	if (get_macro_name(expr->pos))
157		return;
158	if (is_unconstant_macro(expr->left) || is_unconstant_macro(expr->right))
159		return;
160
161	if ((get_value(expr->left, &sval) && sval.value == 0) ||
162	    (get_value(expr->right, &sval) && sval.value == 0))
163		sm_warning("bitwise AND condition is false here");
164}
165
166static void match_binop(struct expression *expr)
167{
168	sval_t left, right, sval;
169
170	if (expr->op != '&')
171		return;
172	if (!get_value(expr, &sval) || sval.value != 0)
173		return;
174	if (get_macro_name(expr->pos))
175		return;
176	if (!get_value(expr->left, &left) || !get_value(expr->right, &right))
177		return;
178	sm_warning("odd binop '0x%llx & 0x%llx'", left.uvalue, right.uvalue);
179}
180
181void check_or_vs_and(int id)
182{
183	my_id = id;
184
185	unconstant_macros = create_function_hashtable(100);
186	load_strings("unconstant_macros", unconstant_macros);
187
188	add_hook(&match_logic, LOGIC_HOOK);
189	add_hook(&match_condition, CONDITION_HOOK);
190	if (option_spammy)
191		add_hook(&match_binop, BINOP_HOOK);
192}
193