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