2 * sparse/check_rosenberg.c
4 * Copyright (C) 2011 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
10 /* Does a search for Dan Rosenberg style info leaks */
12 /* fixme: struct includes a struct with a hole in it */
13 /* function is called that clears the struct */
17 #include "smatch_function_hashtable.h"
18 #include "smatch_slist.h"
19 #include "smatch_extra.h"
21 static int my_whole_id
;
22 static int my_member_id
;
26 static void extra_mod_hook(const char *name
, struct symbol
*sym
, struct smatch_state
*state
)
28 set_state(my_member_id
, name
, sym
, state
);
31 static void print_holey_warning(struct expression
*data
, const char *member
)
35 name
= expr_to_str(data
);
37 sm_msg("warn: check that '%s' doesn't leak information (struct has a hole after '%s')",
40 sm_msg("warn: check that '%s' doesn't leak information (struct has holes)",
46 static int check_struct(struct expression
*expr
, struct symbol
*type
)
48 struct symbol
*tmp
, *base_type
;
49 const char *prev
= NULL
;
53 FOR_EACH_PTR(type
->symbol_list
, tmp
) {
54 base_type
= get_real_base_type(tmp
);
55 if (base_type
&& base_type
->type
== SYM_STRUCT
) {
56 if (check_struct(expr
, base_type
))
59 if (type
->ctype
.attribute
->is_packed
)
62 if (!tmp
->ctype
.alignment
) {
63 sm_msg("warn: cannot determine the alignment here\n");
64 } else if (align
% tmp
->ctype
.alignment
) {
65 print_holey_warning(expr
, prev
);
69 if (base_type
== &bool_ctype
)
71 else if (tmp
->bit_size
<= 0)
74 align
+= bits_to_bytes(tmp
->bit_size
);
77 prev
= tmp
->ident
->name
;
80 } END_FOR_EACH_PTR(tmp
);
85 static int warn_on_holey_struct(struct expression
*expr
)
88 type
= get_type(expr
);
89 if (!type
|| type
->type
!= SYM_STRUCT
)
92 return check_struct(expr
, type
);
95 static int has_global_scope(struct expression
*expr
)
99 if (expr
->type
!= EXPR_SYMBOL
)
102 return toplevel(sym
->scope
);
105 static int was_initialized(struct expression
*expr
)
110 name
= expr_to_var_sym(expr
, &sym
);
113 if (sym
->initializer
)
118 static void match_clear(const char *fn
, struct expression
*expr
, void *_arg_no
)
120 struct expression
*ptr
;
121 int arg_no
= PTR_INT(_arg_no
);
123 ptr
= get_argument_from_call_expr(expr
->args
, arg_no
);
126 if (ptr
->type
!= EXPR_PREOP
|| ptr
->op
!= '&')
128 ptr
= strip_expr(ptr
->unop
);
129 set_state_expr(my_whole_id
, ptr
, &cleared
);
132 static int was_memset(struct expression
*expr
)
134 if (get_state_expr(my_whole_id
, expr
) == &cleared
)
139 static int member_initialized(char *name
, struct symbol
*outer
, struct symbol
*member
)
144 base
= get_base_type(member
);
145 if (!base
|| base
->type
!= SYM_BASETYPE
|| !member
->ident
)
148 snprintf(buf
, 256, "%s.%s", name
, member
->ident
->name
);
149 if (get_state(my_member_id
, buf
, outer
))
155 static int member_uninitialized(char *name
, struct symbol
*outer
, struct symbol
*member
)
161 base
= get_base_type(member
);
162 if (!base
|| base
->type
!= SYM_BASETYPE
|| !member
->ident
)
165 snprintf(buf
, 256, "%s.%s", name
, member
->ident
->name
);
166 sm
= get_sm_state(my_member_id
, buf
, outer
);
167 if (sm
&& !slist_has_state(sm
->possible
, &undefined
))
170 sm_msg("warn: check that '%s' doesn't leak information", buf
);
174 static void check_members_initialized(struct expression
*expr
)
177 struct symbol
*outer
;
181 sym
= get_type(expr
);
182 if (!sym
|| sym
->type
!= SYM_STRUCT
)
185 name
= expr_to_var_sym(expr
, &outer
);
187 if (get_state(my_member_id
, name
, outer
))
191 * check that at least one member was set. If all of them were not set
192 * it's more likely a problem in the check than a problem in the kernel
195 FOR_EACH_PTR(sym
->symbol_list
, tmp
) {
196 if (member_initialized(name
, outer
, tmp
))
198 } END_FOR_EACH_PTR(tmp
);
202 FOR_EACH_PTR(sym
->symbol_list
, tmp
) {
203 if (member_uninitialized(name
, outer
, tmp
))
205 } END_FOR_EACH_PTR(tmp
);
210 static void match_copy_to_user(const char *fn
, struct expression
*expr
, void *_arg
)
212 int arg
= PTR_INT(_arg
);
213 struct expression
*data
;
215 data
= get_argument_from_call_expr(expr
->args
, arg
);
218 if (data
->type
!= EXPR_PREOP
|| data
->op
!= '&')
221 data
= strip_expr(data
->unop
);
222 if (data
->type
!= EXPR_SYMBOL
)
225 if (has_global_scope(data
))
227 if (was_initialized(data
))
229 if (was_memset(data
))
231 if (warn_on_holey_struct(data
))
233 check_members_initialized(data
);
236 static void register_clears_argument(void)
242 token
= get_tokens_file("kernel.clears_argument");
245 if (token_type(token
) != TOKEN_STREAMBEGIN
)
248 while (token_type(token
) != TOKEN_STREAMEND
) {
249 if (token_type(token
) != TOKEN_IDENT
)
251 func
= show_ident(token
->ident
);
253 if (token_type(token
) != TOKEN_NUMBER
)
255 arg
= atoi(token
->number
);
257 add_function_hook(func
, &match_clear
, INT_PTR(arg
));
263 void check_rosenberg(int id
)
265 if (option_project
!= PROJ_KERNEL
)
269 add_function_hook("memset", &match_clear
, INT_PTR(0));
270 add_function_hook("memcpy", &match_clear
, INT_PTR(0));
271 register_clears_argument();
273 add_function_hook("copy_to_user", &match_copy_to_user
, INT_PTR(1));
274 add_function_hook("nla_put", &match_copy_to_user
, INT_PTR(3));
277 void check_rosenberg2(int id
)
280 add_extra_mod_hook(&extra_mod_hook
);