1 /*
2  * Copyright (C) 2011 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 /* Does a search for Dan Rosenberg style info leaks */
19 
20 /* fixme: struct includes a struct with a hole in it */
21 /* function is called that clears the struct */
22 
23 #include "scope.h"
24 #include "smatch.h"
25 #include "smatch_function_hashtable.h"
26 #include "smatch_slist.h"
27 #include "smatch_extra.h"
28 
29 static int my_whole_id;
30 static int my_member_id;
31 
32 STATE(cleared);
33 
34 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
35 {
36 	struct symbol *type;
37 
38 	type = get_real_base_type(sym);
39 	if (!type || type->type != SYM_STRUCT)
40 		return;
41 
42 	set_state(my_member_id, name, sym, state);
43 }
44 
45 static void print_holey_warning(struct expression *data, const char *member)
46 {
47 	char *name;
48 
49 	name = expr_to_str(data);
50 	if (member) {
51 		sm_warning("check that '%s' doesn't leak information (struct has a hole after '%s')",
52 		       name, member);
53 	} else {
54 		sm_warning("check that '%s' doesn't leak information (struct has holes)",
55 		       name);
56 	}
57 	free_string(name);
58 }
59 
60 static int check_struct(struct expression *expr, struct symbol *type)
61 {
62 	struct symbol *tmp, *base_type;
63 	const char *prev = NULL;
64 	int align;
65 
66 	if (type->ctype.alignment == 1)
67 		return 0;
68 
69 	align = 0;
70 	FOR_EACH_PTR(type->symbol_list, tmp) {
71 		base_type = get_real_base_type(tmp);
72 		if (base_type && base_type->type == SYM_STRUCT) {
73 			if (check_struct(expr, base_type))
74 				return 1;
75 		}
76 
77 		if (!tmp->ctype.alignment) {
78 			sm_perror("cannot determine the alignment here");
79 		} else if (align % tmp->ctype.alignment) {
80 			print_holey_warning(expr, prev);
81 			return 1;
82 		}
83 
84 		if (base_type == &bool_ctype)
85 			align += 1;
86 		else if (type_bits(tmp) <= 0)
87 			align = 0;
88 		else
89 			align += type_bytes(tmp);
90 
91 		if (tmp->ident)
92 			prev = tmp->ident->name;
93 		else
94 			prev = NULL;
95 	} END_FOR_EACH_PTR(tmp);
96 
97 	if (align % type->ctype.alignment) {
98 		print_holey_warning(expr, prev);
99 		return 1;
100 	}
101 
102 	return 0;
103 }
104 
105 static int warn_on_holey_struct(struct expression *expr)
106 {
107 	struct symbol *type;
108 	type = get_type(expr);
109 	if (!type || type->type != SYM_STRUCT)
110 		return 0;
111 
112 	return check_struct(expr, type);
113 }
114 
115 static int has_global_scope(struct expression *expr)
116 {
117 	struct symbol *sym;
118 
119 	if (expr->type != EXPR_SYMBOL)
120 		return FALSE;
121 	sym = expr->symbol;
122 	if (!sym)
123 		return FALSE;
124 	return toplevel(sym->scope);
125 }
126 
127 static int was_initialized(struct expression *expr)
128 {
129 	struct symbol *sym;
130 	char *name;
131 
132 	name = expr_to_var_sym(expr, &sym);
133 	if (!name)
134 		return 0;
135 	if (sym->initializer)
136 		return 1;
137 	return 0;
138 }
139 
140 static void match_clear(const char *fn, struct expression *expr, void *_arg_no)
141 {
142 	struct expression *ptr;
143 	int arg_no = PTR_INT(_arg_no);
144 
145 	ptr = get_argument_from_call_expr(expr->args, arg_no);
146 	if (!ptr)
147 		return;
148 	ptr = strip_expr(ptr);
149 	if (ptr->type != EXPR_PREOP || ptr->op != '&')
150 		return;
151 	ptr = strip_expr(ptr->unop);
152 	set_state_expr(my_whole_id, ptr, &cleared);
153 }
154 
155 static int was_memset(struct expression *expr)
156 {
157 	if (get_state_expr(my_whole_id, expr) == &cleared)
158 		return 1;
159 	return 0;
160 }
161 
162 static int member_initialized(char *name, struct symbol *outer, struct symbol *member, int pointer)
163 {
164 	char buf[256];
165 	struct symbol *base;
166 
167 	base = get_base_type(member);
168 	if (!base || base->type != SYM_BASETYPE || !member->ident)
169 		return FALSE;
170 
171 	if (pointer)
172 		snprintf(buf, 256, "%s->%s", name, member->ident->name);
173 	else
174 		snprintf(buf, 256, "%s.%s", name, member->ident->name);
175 
176 	if (get_state(my_member_id, buf, outer))
177 		return TRUE;
178 
179 	return FALSE;
180 }
181 
182 static int member_uninitialized(char *name, struct symbol *outer, struct symbol *member, int pointer)
183 {
184 	char buf[256];
185 	struct symbol *base;
186 	struct sm_state *sm;
187 
188 	base = get_base_type(member);
189 	if (!base || base->type != SYM_BASETYPE || !member->ident)
190 		return FALSE;
191 
192 	if (pointer)
193 		snprintf(buf, 256, "%s->%s", name, member->ident->name);
194 	else
195 		snprintf(buf, 256, "%s.%s", name, member->ident->name);
196 
197 	sm = get_sm_state(my_member_id, buf, outer);
198 	if (sm && !slist_has_state(sm->possible, &undefined))
199 		return FALSE;
200 
201 	sm_warning("check that '%s' doesn't leak information", buf);
202 	return TRUE;
203 }
204 
205 static int check_members_initialized(struct expression *expr)
206 {
207 	char *name;
208 	struct symbol *outer;
209 	struct symbol *sym;
210 	struct symbol *tmp;
211 	int pointer = 0;
212 	int printed = 0;
213 
214 	sym = get_type(expr);
215 	if (sym && sym->type == SYM_PTR) {
216 		pointer = 1;
217 		sym = get_real_base_type(sym);
218 	}
219 	if (!sym)
220 		return 0;
221 	if (sym->type != SYM_STRUCT)
222 		return 0;
223 
224 	name = expr_to_var_sym(expr, &outer);
225 
226 	/*
227 	 * check that at least one member was set.  If all of them were not set
228 	 * it's more likely a problem in the check than a problem in the kernel
229 	 * code.
230 	 */
231 	FOR_EACH_PTR(sym->symbol_list, tmp) {
232 		if (member_initialized(name, outer, tmp, pointer))
233 			goto check;
234 	} END_FOR_EACH_PTR(tmp);
235 	goto out;
236 
237 check:
238 	FOR_EACH_PTR(sym->symbol_list, tmp) {
239 		if (member_uninitialized(name, outer, tmp, pointer)) {
240 			printed = 1;
241 			goto out;
242 		}
243 	} END_FOR_EACH_PTR(tmp);
244 out:
245 	free_string(name);
246 	return printed;
247 }
248 
249 static void check_was_initialized(struct expression *data)
250 {
251 	data = strip_expr(data);
252 	if (!data)
253 		return;
254 	if (data->type == EXPR_PREOP && data->op == '&')
255 		data = strip_expr(data->unop);
256 	if (data->type != EXPR_SYMBOL)
257 		return;
258 
259 	if (has_global_scope(data))
260 		return;
261 	if (was_initialized(data))
262 		return;
263 	if (was_memset(data))
264 		return;
265 	if (warn_on_holey_struct(data))
266 		return;
267 	check_members_initialized(data);
268 }
269 
270 static void match_copy_to_user(const char *fn, struct expression *expr, void *_arg)
271 {
272 	int arg = PTR_INT(_arg);
273 	struct expression *data;
274 
275 	data = get_argument_from_call_expr(expr->args, arg);
276 	data = strip_expr(data);
277 	if (!data)
278 		return;
279 	if (data->type != EXPR_PREOP || data->op != '&')
280 		return;
281 	check_was_initialized(data);
282 }
283 
284 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
285 {
286 	while (expr->type == EXPR_ASSIGNMENT)
287 		expr = strip_expr(expr->right);
288 	if (expr->type != EXPR_CALL)
289 		return;
290 
291 	match_clear(NULL, expr, INT_PTR(param));
292 }
293 
294 static void match_assign(struct expression *expr)
295 {
296 	struct symbol *type;
297 
298 	type = get_type(expr->left);
299 	if (!type || type->type != SYM_STRUCT)
300 		return;
301 	set_state_expr(my_whole_id, expr->left, &cleared);
302 }
303 
304 static void register_clears_argument(void)
305 {
306 	struct token *token;
307 	const char *func;
308 	int arg;
309 
310 	token = get_tokens_file("kernel.clears_argument");
311 	if (!token)
312 		return;
313 	if (token_type(token) != TOKEN_STREAMBEGIN)
314 		return;
315 	token = token->next;
316 	while (token_type(token) != TOKEN_STREAMEND) {
317 		if (token_type(token) != TOKEN_IDENT)
318 			return;
319 		func = show_ident(token->ident);
320 		token = token->next;
321 		if (token_type(token) != TOKEN_NUMBER)
322 			return;
323 		arg = atoi(token->number);
324 
325 		add_function_hook(func, &match_clear, INT_PTR(arg));
326 		token = token->next;
327 	}
328 	clear_token_alloc();
329 }
330 
331 static void register_copy_funcs_from_file(void)
332 {
333 	struct token *token;
334 	const char *func;
335 	int arg;
336 
337 	token = get_tokens_file("kernel.rosenberg_funcs");
338 	if (!token)
339 		return;
340 	if (token_type(token) != TOKEN_STREAMBEGIN)
341 		return;
342 	token = token->next;
343 	while (token_type(token) != TOKEN_STREAMEND) {
344 		if (token_type(token) != TOKEN_IDENT)
345 			return;
346 		func = show_ident(token->ident);
347 		token = token->next;
348 		if (token_type(token) != TOKEN_NUMBER)
349 			return;
350 		arg = atoi(token->number);
351 		add_function_hook(func, &match_copy_to_user, INT_PTR(arg));
352 		token = token->next;
353 	}
354 	clear_token_alloc();
355 }
356 
357 void check_rosenberg(int id)
358 {
359 	if (option_project != PROJ_KERNEL)
360 		return;
361 	my_whole_id = id;
362 
363 	add_function_hook("memset", &match_clear, INT_PTR(0));
364 	add_function_hook("memcpy", &match_clear, INT_PTR(0));
365 	add_function_hook("memzero", &match_clear, INT_PTR(0));
366 	add_function_hook("__memset", &match_clear, INT_PTR(0));
367 	add_function_hook("__memcpy", &match_clear, INT_PTR(0));
368 	add_function_hook("__memzero", &match_clear, INT_PTR(0));
369 	add_function_hook("__builtin_memset", &match_clear, INT_PTR(0));
370 	add_function_hook("__builtin_memcpy", &match_clear, INT_PTR(0));
371 
372 	add_hook(&match_assign, ASSIGNMENT_HOOK);
373 	register_clears_argument();
374 	select_return_states_hook(PARAM_CLEARED, &db_param_cleared);
375 
376 	register_copy_funcs_from_file();
377 }
378 
379 void check_rosenberg2(int id)
380 {
381 	if (option_project != PROJ_KERNEL)
382 		return;
383 
384 	my_member_id = id;
385 	set_dynamic_states(my_member_id);
386 	add_extra_mod_hook(&extra_mod_hook);
387 }
388 
389