2 * Copyright (C) 2011 Dan Carpenter.
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.
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.
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
18 /* Does a search for Dan Rosenberg style info leaks */
20 /* fixme: struct includes a struct with a hole in it */
21 /* function is called that clears the struct */
25 #include "smatch_function_hashtable.h"
26 #include "smatch_slist.h"
27 #include "smatch_extra.h"
29 static int my_whole_id
;
30 static int my_member_id
;
34 static void extra_mod_hook(const char *name
, struct symbol
*sym
, struct smatch_state
*state
)
38 type
= get_real_base_type(sym
);
39 if (!type
|| type
->type
!= SYM_STRUCT
)
42 set_state(my_member_id
, name
, sym
, state
);
45 static void print_holey_warning(struct expression
*data
, const char *member
)
49 name
= expr_to_str(data
);
51 sm_msg("warn: check that '%s' doesn't leak information (struct has a hole after '%s')",
54 sm_msg("warn: check that '%s' doesn't leak information (struct has holes)",
60 static int check_struct(struct expression
*expr
, struct symbol
*type
)
62 struct symbol
*tmp
, *base_type
;
63 const char *prev
= NULL
;
67 FOR_EACH_PTR(type
->symbol_list
, tmp
) {
68 base_type
= get_real_base_type(tmp
);
69 if (base_type
&& base_type
->type
== SYM_STRUCT
) {
70 if (check_struct(expr
, base_type
))
73 if (type
->ctype
.attribute
->is_packed
)
76 if (!tmp
->ctype
.alignment
) {
77 sm_msg("warn: cannot determine the alignment here\n");
78 } else if (align
% tmp
->ctype
.alignment
) {
79 print_holey_warning(expr
, prev
);
83 if (base_type
== &bool_ctype
)
85 else if (type_bits(tmp
) <= 0)
88 align
+= type_bytes(tmp
);
91 prev
= tmp
->ident
->name
;
94 } END_FOR_EACH_PTR(tmp
);
96 if (align
% type
->ctype
.alignment
) {
97 print_holey_warning(expr
, prev
);
104 static int warn_on_holey_struct(struct expression
*expr
)
107 type
= get_type(expr
);
108 if (!type
|| type
->type
!= SYM_STRUCT
)
111 return check_struct(expr
, type
);
114 static int has_global_scope(struct expression
*expr
)
118 if (expr
->type
!= EXPR_SYMBOL
)
121 return toplevel(sym
->scope
);
124 static int was_initialized(struct expression
*expr
)
129 name
= expr_to_var_sym(expr
, &sym
);
132 if (sym
->initializer
)
137 static void match_clear(const char *fn
, struct expression
*expr
, void *_arg_no
)
139 struct expression
*ptr
;
140 int arg_no
= PTR_INT(_arg_no
);
142 ptr
= get_argument_from_call_expr(expr
->args
, arg_no
);
145 ptr
= strip_expr(ptr
);
146 if (ptr
->type
!= EXPR_PREOP
|| ptr
->op
!= '&')
148 ptr
= strip_expr(ptr
->unop
);
149 set_state_expr(my_whole_id
, ptr
, &cleared
);
152 static int was_memset(struct expression
*expr
)
154 if (get_state_expr(my_whole_id
, expr
) == &cleared
)
159 static int member_initialized(char *name
, struct symbol
*outer
, struct symbol
*member
, int pointer
)
164 base
= get_base_type(member
);
165 if (!base
|| base
->type
!= SYM_BASETYPE
|| !member
->ident
)
169 snprintf(buf
, 256, "%s->%s", name
, member
->ident
->name
);
171 snprintf(buf
, 256, "%s.%s", name
, member
->ident
->name
);
173 if (get_state(my_member_id
, buf
, outer
))
179 static int member_uninitialized(char *name
, struct symbol
*outer
, struct symbol
*member
, int pointer
)
185 base
= get_base_type(member
);
186 if (!base
|| base
->type
!= SYM_BASETYPE
|| !member
->ident
)
190 snprintf(buf
, 256, "%s->%s", name
, member
->ident
->name
);
192 snprintf(buf
, 256, "%s.%s", name
, member
->ident
->name
);
194 sm
= get_sm_state(my_member_id
, buf
, outer
);
195 if (sm
&& !slist_has_state(sm
->possible
, &undefined
))
198 sm_msg("warn: check that '%s' doesn't leak information", buf
);
202 static int check_members_initialized(struct expression
*expr
)
205 struct symbol
*outer
;
211 sym
= get_type(expr
);
212 if (sym
&& sym
->type
== SYM_PTR
) {
214 sym
= get_real_base_type(sym
);
218 if (sym
->type
!= SYM_STRUCT
)
221 name
= expr_to_var_sym(expr
, &outer
);
224 * check that at least one member was set. If all of them were not set
225 * it's more likely a problem in the check than a problem in the kernel
228 FOR_EACH_PTR(sym
->symbol_list
, tmp
) {
229 if (member_initialized(name
, outer
, tmp
, pointer
))
231 } END_FOR_EACH_PTR(tmp
);
235 FOR_EACH_PTR(sym
->symbol_list
, tmp
) {
236 if (member_uninitialized(name
, outer
, tmp
, pointer
)) {
240 } END_FOR_EACH_PTR(tmp
);
246 static void check_was_initialized(struct expression
*data
)
248 data
= strip_expr(data
);
251 if (data
->type
== EXPR_PREOP
&& data
->op
== '&')
252 data
= strip_expr(data
->unop
);
253 if (data
->type
!= EXPR_SYMBOL
)
256 if (has_global_scope(data
))
258 if (was_initialized(data
))
260 if (was_memset(data
))
262 if (warn_on_holey_struct(data
))
264 check_members_initialized(data
);
267 static void match_copy_to_user(const char *fn
, struct expression
*expr
, void *_arg
)
269 int arg
= PTR_INT(_arg
);
270 struct expression
*data
;
272 data
= get_argument_from_call_expr(expr
->args
, arg
);
273 data
= strip_expr(data
);
276 if (data
->type
!= EXPR_PREOP
|| data
->op
!= '&')
278 check_was_initialized(data
);
281 static void db_param_cleared(struct expression
*expr
, int param
, char *key
, char *value
)
283 while (expr
->type
== EXPR_ASSIGNMENT
)
284 expr
= strip_expr(expr
->right
);
285 if (expr
->type
!= EXPR_CALL
)
288 match_clear(NULL
, expr
, INT_PTR(param
));
291 static void match_assign(struct expression
*expr
)
295 type
= get_type(expr
->left
);
296 if (!type
|| type
->type
!= SYM_STRUCT
)
298 set_state_expr(my_whole_id
, expr
->left
, &cleared
);
301 static void register_clears_argument(void)
307 token
= get_tokens_file("kernel.clears_argument");
310 if (token_type(token
) != TOKEN_STREAMBEGIN
)
313 while (token_type(token
) != TOKEN_STREAMEND
) {
314 if (token_type(token
) != TOKEN_IDENT
)
316 func
= show_ident(token
->ident
);
318 if (token_type(token
) != TOKEN_NUMBER
)
320 arg
= atoi(token
->number
);
322 add_function_hook(func
, &match_clear
, INT_PTR(arg
));
328 static void register_copy_funcs_from_file(void)
334 token
= get_tokens_file("kernel.rosenberg_funcs");
337 if (token_type(token
) != TOKEN_STREAMBEGIN
)
340 while (token_type(token
) != TOKEN_STREAMEND
) {
341 if (token_type(token
) != TOKEN_IDENT
)
343 func
= show_ident(token
->ident
);
345 if (token_type(token
) != TOKEN_NUMBER
)
347 arg
= atoi(token
->number
);
348 add_function_hook(func
, &match_copy_to_user
, INT_PTR(arg
));
354 void check_rosenberg(int id
)
356 if (option_project
!= PROJ_KERNEL
)
360 add_function_hook("memset", &match_clear
, INT_PTR(0));
361 add_function_hook("memcpy", &match_clear
, INT_PTR(0));
362 add_function_hook("memzero", &match_clear
, INT_PTR(0));
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("__builtin_memset", &match_clear
, INT_PTR(0));
367 add_function_hook("__builtin_memcpy", &match_clear
, INT_PTR(0));
369 add_hook(&match_assign
, ASSIGNMENT_HOOK
);
370 register_clears_argument();
371 select_return_states_hook(PARAM_CLEARED
, &db_param_cleared
);
373 register_copy_funcs_from_file();
376 void check_rosenberg2(int id
)
378 if (option_project
!= PROJ_KERNEL
)
382 add_extra_mod_hook(&extra_mod_hook
);