extra: type bug handling empty states in reset_struct_members()
[smatch.git] / check_rosenberg.c
blob1c58aa47fe7d8c27e9e61f213f4cc612fd8419b7
1 /*
2 * sparse/check_rosenberg.c
4 * Copyright (C) 2011 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
8 */
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 */
15 #include "scope.h"
16 #include "smatch.h"
17 #include "smatch_function_hashtable.h"
18 #include "smatch_slist.h"
20 static int my_id;
21 extern int check_assigned_expr_id;
23 STATE(cleared);
25 static DEFINE_HASHTABLE_INSERT(insert_struct, char, int);
26 static DEFINE_HASHTABLE_SEARCH(search_struct, char, int);
27 static struct hashtable *holey_structs;
29 static char *get_struct_type(struct expression *expr)
31 struct symbol *type;
33 type = get_type(expr);
34 if (!type || type->type != SYM_STRUCT || !type->ident)
35 return NULL;
36 return alloc_string(type->ident->name);
39 static int holey_struct(struct expression *expr)
41 char *struct_type = 0;
42 int ret = 0;
44 struct_type = get_struct_type(expr);
45 if (!struct_type)
46 return 0;
47 if (search_struct(holey_structs, struct_type))
48 ret = 1;
49 free_string(struct_type);
50 return ret;
53 static int has_global_scope(struct expression *expr)
55 struct symbol *sym;
57 if (expr->type != EXPR_SYMBOL)
58 return FALSE;
59 sym = expr->symbol;
60 return toplevel(sym->scope);
63 static int was_initialized(struct expression *expr)
65 struct symbol *sym;
66 char *name;
68 name = expr_to_var_sym(expr, &sym);
69 if (!name)
70 return 0;
71 if (sym->initializer)
72 return 1;
73 return 0;
76 static void match_clear(const char *fn, struct expression *expr, void *_arg_no)
78 struct expression *ptr;
79 int arg_no = PTR_INT(_arg_no);
81 ptr = get_argument_from_call_expr(expr->args, arg_no);
82 if (ptr->type != EXPR_PREOP || ptr->op != '&')
83 return;
84 ptr = strip_expr(ptr->unop);
85 set_state_expr(my_id, ptr, &cleared);
88 static int was_memset(struct expression *expr)
90 if (get_state_expr(my_id, expr) == &cleared)
91 return 1;
92 return 0;
95 static int member_initialized(char *name, struct symbol *outer, struct symbol *member)
97 char buf[256];
98 struct symbol *base;
100 base = get_base_type(member);
101 if (!base || base->type != SYM_BASETYPE || !member->ident)
102 return FALSE;
104 snprintf(buf, 256, "%s.%s", name, member->ident->name);
105 if (get_state(check_assigned_expr_id, buf, outer))
106 return TRUE;
108 return FALSE;
111 static int member_uninitialized(char *name, struct symbol *outer, struct symbol *member)
113 char buf[256];
114 struct symbol *base;
115 struct sm_state *sm;
117 base = get_base_type(member);
118 if (!base || base->type != SYM_BASETYPE || !member->ident)
119 return FALSE;
121 snprintf(buf, 256, "%s.%s", name, member->ident->name);
122 sm = get_sm_state(check_assigned_expr_id, buf, outer);
123 if (sm && !slist_has_state(sm->possible, &undefined))
124 return FALSE;
126 sm_msg("warn: check that '%s' doesn't leak information", buf);
127 return TRUE;
130 static void check_members_initialized(struct expression *expr)
132 char *name;
133 struct symbol *outer;
134 struct symbol *sym;
135 struct symbol *tmp;
137 sym = get_type(expr);
138 if (!sym || sym->type != SYM_STRUCT)
139 return;
141 name = expr_to_var_sym(expr, &outer);
143 if (get_state(check_assigned_expr_id, name, outer))
144 goto out;
146 FOR_EACH_PTR(sym->symbol_list, tmp) {
147 if (member_initialized(name, outer, tmp))
148 goto check;
149 } END_FOR_EACH_PTR(tmp);
150 goto out;
152 check:
153 FOR_EACH_PTR(sym->symbol_list, tmp) {
154 if (member_uninitialized(name, outer, tmp))
155 goto out;
156 } END_FOR_EACH_PTR(tmp);
157 out:
158 free_string(name);
161 static void match_copy_to_user(const char *fn, struct expression *expr, void *unused)
163 struct expression *data;
165 data = get_argument_from_call_expr(expr->args, 1);
166 if (!data)
167 return;
168 if (data->type != EXPR_PREOP || data->op != '&')
169 return;
171 data = strip_expr(data->unop);
172 if (data->type != EXPR_SYMBOL)
173 return;
175 if (has_global_scope(data))
176 return;
177 if (was_initialized(data))
178 return;
179 if (was_memset(data))
180 return;
181 if (holey_struct(data)) {
182 char *name;
184 name = expr_to_var(data);
185 sm_msg("warn: check that '%s' doesn't leak information (struct has holes)", name);
186 free_string(name);
187 return;
189 check_members_initialized(data);
192 static void register_holey_structs(void)
194 struct token *token;
195 const char *struct_type;
197 token = get_tokens_file("kernel.paholes");
198 if (!token)
199 return;
200 if (token_type(token) != TOKEN_STREAMBEGIN)
201 return;
202 token = token->next;
203 while (token_type(token) != TOKEN_STREAMEND) {
204 if (token_type(token) != TOKEN_IDENT)
205 return;
206 struct_type = show_ident(token->ident);
207 insert_struct(holey_structs, alloc_string(struct_type), INT_PTR(1));
208 token = token->next;
210 clear_token_alloc();
213 static void register_clears_argument(void)
215 struct token *token;
216 const char *func;
217 int arg;
219 token = get_tokens_file("kernel.clears_argument");
220 if (!token)
221 return;
222 if (token_type(token) != TOKEN_STREAMBEGIN)
223 return;
224 token = token->next;
225 while (token_type(token) != TOKEN_STREAMEND) {
226 if (token_type(token) != TOKEN_IDENT)
227 return;
228 func = show_ident(token->ident);
229 token = token->next;
230 if (token_type(token) != TOKEN_NUMBER)
231 return;
232 arg = atoi(token->number);
234 add_function_hook(func, &match_clear, INT_PTR(arg));
235 token = token->next;
237 clear_token_alloc();
240 void check_rosenberg(int id)
242 if (option_project != PROJ_KERNEL)
243 return;
244 my_id = id;
245 holey_structs = create_function_hashtable(10000);
247 register_holey_structs();
248 register_clears_argument();
250 add_function_hook("copy_to_user", &match_copy_to_user, NULL);