1 /*
2  * Copyright (C) 2009 Dan Carpenter.
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 /*
19  * This check is supposed to find places like this:
20  * err = foo();
21  * err = bar();
22  * if (err)
23  *         return err;
24  * (the first assignment isn't used)
25  *
26  * How the check works is that every assignment gets an ID.
27  * We store that assignment ID in a list of assignments that
28  * haven't been used.  We also set the state of 'err' from
29  * the example above to be.  Then when we use 'err' we remove
30  * it from the list.  At the end of the function we print
31  * a list of assignments that still haven't been used.
32  *
33  * Note that this check only works for assignments to
34  * EXPR_SYMBOL.  Maybe it could be modified to cover other
35  * assignments later but then you would have to deal with
36  * scope issues.
37  *
38  * Also this state is quite tied to the order the callbacks
39  * are called in smatch_flow.c.  (If the order changed it
40  * would break).
41  *
42  */
43 
44 #include "smatch.h"
45 #include "smatch_slist.h"
46 #include "smatch_function_hashtable.h"
47 
48 static int my_id;
49 
50 struct assignment {
51 	int assign_id;
52 	char *name;
53 	char *function;
54 	int line;
55 };
56 ALLOCATOR(assignment, "assignment id");
57 DECLARE_PTR_LIST(assignment_list, struct assignment);
58 static struct assignment_list *assignment_list;
59 
60 static struct expression *skip_this;
61 static int assign_id;
62 
63 static DEFINE_HASHTABLE_INSERT(insert_func, char, int);
64 static DEFINE_HASHTABLE_SEARCH(search_func, char, int);
65 static struct hashtable *ignored_funcs;
66 
67 static const char *kernel_ignored[] = {
68 	"inb",
69 	"inl",
70 	"inw",
71 	"readb",
72 	"readl",
73 	"readw",
74 };
75 
get_fn_name(struct expression * expr)76 static char *get_fn_name(struct expression *expr)
77 {
78 	if (expr->type != EXPR_CALL)
79 		return NULL;
80 	if (expr->fn->type != EXPR_SYMBOL)
81 		return NULL;
82 	return expr_to_var(expr->fn);
83 }
84 
ignored_function(struct expression * expr)85 static int ignored_function(struct expression *expr)
86 {
87 	char *func;
88 	int ret = 0;
89 
90 	func = get_fn_name(expr);
91 	if (!func)
92 		return 0;
93 	if (search_func(ignored_funcs, func))
94 		ret = 1;
95 	free_string(func);
96 	return ret;
97 }
98 
match_assign_call(struct expression * expr)99 static void match_assign_call(struct expression *expr)
100 {
101 	struct expression *left;
102 	struct assignment *assign;
103 
104 	if (final_pass)
105 		return;
106 	if (in_condition())
107 		return;
108 	if (expr->op != '=')
109 		return;
110 	if (unreachable())
111 		return;
112 	if (ignored_function(expr->right))
113 		return;
114 	left = strip_expr(expr->left);
115 	if (!left || left->type != EXPR_SYMBOL)
116 		return;
117 	if (left->symbol->ctype.modifiers & (MOD_TOPLEVEL | MOD_EXTERN | MOD_STATIC))
118 		return;
119 
120 	skip_this = left;
121 
122 	set_state_expr(my_id, left, alloc_state_num(assign_id));
123 
124 	assign = __alloc_assignment(0);
125 	assign->assign_id = assign_id++;
126 	assign->name = expr_to_var(left);
127 	assign->function = get_fn_name(expr->right);
128 	assign->line = get_lineno();
129 	add_ptr_list(&assignment_list, assign);
130 }
131 
match_assign(struct expression * expr)132 static void match_assign(struct expression *expr)
133 {
134 	struct expression *left;
135 
136 	if (expr->op != '=')
137 		return;
138 	left = strip_expr(expr->left);
139 	if (!left || left->type != EXPR_SYMBOL)
140 		return;
141 	set_state_expr(my_id, left, &undefined);
142 }
143 
delete_used(int assign_id)144 static void delete_used(int assign_id)
145 {
146 	struct assignment *tmp;
147 
148  	FOR_EACH_PTR(assignment_list, tmp) {
149 		if (tmp->assign_id == assign_id) {
150 			DELETE_CURRENT_PTR(tmp);
151 			return;
152 		}
153 	} END_FOR_EACH_PTR(tmp);
154 }
155 
delete_used_symbols(struct state_list * possible)156 static void delete_used_symbols(struct state_list *possible)
157 {
158 	struct sm_state *tmp;
159 
160  	FOR_EACH_PTR(possible, tmp) {
161 		delete_used(PTR_INT(tmp->state->data));
162 	} END_FOR_EACH_PTR(tmp);
163 }
164 
match_symbol(struct expression * expr)165 static void match_symbol(struct expression *expr)
166 {
167 	struct sm_state *sm;
168 
169 	expr = strip_expr(expr);
170 	if (expr == skip_this)
171 		return;
172 	sm = get_sm_state_expr(my_id, expr);
173 	if (!sm)
174 		return;
175 	delete_used_symbols(sm->possible);
176 	set_state_expr(my_id, expr, &undefined);
177 }
178 
match_end_func(struct symbol * sym)179 static void match_end_func(struct symbol *sym)
180 {
181 	struct assignment *tmp;
182 
183 	if (__inline_fn)
184 		return;
185  	FOR_EACH_PTR(assignment_list, tmp) {
186 		sm_printf("%s:%d %s() ", get_filename(), tmp->line, get_function());
187 		sm_printf("warn: unused return: %s = %s()\n",
188 			tmp->name, tmp->function);
189 	} END_FOR_EACH_PTR(tmp);
190 }
191 
match_after_func(struct symbol * sym)192 static void match_after_func(struct symbol *sym)
193 {
194 	if (__inline_fn)
195 		return;
196 	clear_assignment_alloc();
197 	__free_ptr_list((struct ptr_list **)&assignment_list);
198 }
199 
check_unused_ret(int id)200 void check_unused_ret(int id)
201 {
202 	my_id = id;
203 
204 	/* It turns out that this test is worthless unless you use --two-passes.  */
205 	if (!option_two_passes)
206 		return;
207 	add_hook(&match_assign_call, CALL_ASSIGNMENT_HOOK);
208 	add_hook(&match_assign, ASSIGNMENT_HOOK);
209 	add_hook(&match_symbol, SYM_HOOK);
210 	add_hook(&match_end_func, END_FUNC_HOOK);
211 	add_hook(&match_after_func, AFTER_FUNC_HOOK);
212 	ignored_funcs = create_function_hashtable(100);
213 	if (option_project == PROJ_KERNEL) {
214 		int i;
215 
216 		for (i = 0; i < ARRAY_SIZE(kernel_ignored); i++)
217 			insert_func(ignored_funcs, (char *)kernel_ignored[i], (int *)1);
218 	}
219 }
220