param_limit/filter: ignore whole ranges
[smatch.git] / check_rosenberg.c
blob576c04a5ffbaf91aeb4bd174ca3c0d371fe48a9c
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)
83 return;
84 if (ptr->type != EXPR_PREOP || ptr->op != '&')
85 return;
86 ptr = strip_expr(ptr->unop);
87 set_state_expr(my_id, ptr, &cleared);
90 static int was_memset(struct expression *expr)
92 if (get_state_expr(my_id, expr) == &cleared)
93 return 1;
94 return 0;
97 static int member_initialized(char *name, struct symbol *outer, struct symbol *member)
99 char buf[256];
100 struct symbol *base;
102 base = get_base_type(member);
103 if (!base || base->type != SYM_BASETYPE || !member->ident)
104 return FALSE;
106 snprintf(buf, 256, "%s.%s", name, member->ident->name);
107 if (get_state(check_assigned_expr_id, buf, outer))
108 return TRUE;
110 return FALSE;
113 static int member_uninitialized(char *name, struct symbol *outer, struct symbol *member)
115 char buf[256];
116 struct symbol *base;
117 struct sm_state *sm;
119 base = get_base_type(member);
120 if (!base || base->type != SYM_BASETYPE || !member->ident)
121 return FALSE;
123 snprintf(buf, 256, "%s.%s", name, member->ident->name);
124 sm = get_sm_state(check_assigned_expr_id, buf, outer);
125 if (sm && !slist_has_state(sm->possible, &undefined))
126 return FALSE;
128 sm_msg("warn: check that '%s' doesn't leak information", buf);
129 return TRUE;
132 static void check_members_initialized(struct expression *expr)
134 char *name;
135 struct symbol *outer;
136 struct symbol *sym;
137 struct symbol *tmp;
139 sym = get_type(expr);
140 if (!sym || sym->type != SYM_STRUCT)
141 return;
143 name = expr_to_var_sym(expr, &outer);
145 if (get_state(check_assigned_expr_id, name, outer))
146 goto out;
148 FOR_EACH_PTR(sym->symbol_list, tmp) {
149 if (member_initialized(name, outer, tmp))
150 goto check;
151 } END_FOR_EACH_PTR(tmp);
152 goto out;
154 check:
155 FOR_EACH_PTR(sym->symbol_list, tmp) {
156 if (member_uninitialized(name, outer, tmp))
157 goto out;
158 } END_FOR_EACH_PTR(tmp);
159 out:
160 free_string(name);
163 static void match_copy_to_user(const char *fn, struct expression *expr, void *_arg)
165 int arg = PTR_INT(_arg);
166 struct expression *data;
168 data = get_argument_from_call_expr(expr->args, arg);
169 if (!data)
170 return;
171 if (data->type != EXPR_PREOP || data->op != '&')
172 return;
174 data = strip_expr(data->unop);
175 if (data->type != EXPR_SYMBOL)
176 return;
178 if (has_global_scope(data))
179 return;
180 if (was_initialized(data))
181 return;
182 if (was_memset(data))
183 return;
184 if (holey_struct(data)) {
185 char *name;
187 name = expr_to_var(data);
188 sm_msg("warn: check that '%s' doesn't leak information (struct has holes)", name);
189 free_string(name);
190 return;
192 check_members_initialized(data);
195 static void register_holey_structs(void)
197 struct token *token;
198 const char *struct_type;
200 token = get_tokens_file("kernel.paholes");
201 if (!token)
202 return;
203 if (token_type(token) != TOKEN_STREAMBEGIN)
204 return;
205 token = token->next;
206 while (token_type(token) != TOKEN_STREAMEND) {
207 if (token_type(token) != TOKEN_IDENT)
208 return;
209 struct_type = show_ident(token->ident);
210 insert_struct(holey_structs, alloc_string(struct_type), INT_PTR(1));
211 token = token->next;
213 clear_token_alloc();
216 static void register_clears_argument(void)
218 struct token *token;
219 const char *func;
220 int arg;
222 token = get_tokens_file("kernel.clears_argument");
223 if (!token)
224 return;
225 if (token_type(token) != TOKEN_STREAMBEGIN)
226 return;
227 token = token->next;
228 while (token_type(token) != TOKEN_STREAMEND) {
229 if (token_type(token) != TOKEN_IDENT)
230 return;
231 func = show_ident(token->ident);
232 token = token->next;
233 if (token_type(token) != TOKEN_NUMBER)
234 return;
235 arg = atoi(token->number);
237 add_function_hook(func, &match_clear, INT_PTR(arg));
238 token = token->next;
240 clear_token_alloc();
243 void check_rosenberg(int id)
245 if (option_project != PROJ_KERNEL)
246 return;
247 my_id = id;
248 holey_structs = create_function_hashtable(10000);
250 register_holey_structs();
251 register_clears_argument();
253 add_function_hook("copy_to_user", &match_copy_to_user, INT_PTR(1));
254 add_function_hook("nla_put", &match_copy_to_user, INT_PTR(3));