db: caller info needs to record the -1 parameters
[smatch.git] / check_rosenberg.c
blob3ba76b2a92dc0a7e823616486d2b5b00c18de34e
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 = get_variable_from_expr(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;
138 sym = get_type(expr);
139 if (!sym || sym->type != SYM_STRUCT)
140 return;
142 name = get_variable_from_expr(expr, &outer);
144 if (get_state(check_assigned_expr_id, name, outer))
145 goto out;
147 FOR_EACH_PTR(sym->symbol_list, tmp) {
148 if (member_initialized(name, outer, tmp))
149 goto check;
150 } END_FOR_EACH_PTR(tmp);
151 goto out;
153 check:
154 FOR_EACH_PTR(sym->symbol_list, tmp) {
155 if (member_uninitialized(name, outer, tmp))
156 goto out;
157 } END_FOR_EACH_PTR(tmp);
158 out:
159 free_string(name);
162 static void match_copy_to_user(const char *fn, struct expression *expr, void *unused)
164 struct expression *data;
166 data = get_argument_from_call_expr(expr->args, 1);
167 if (!data)
168 return;
169 if (data->type != EXPR_PREOP || data->op != '&')
170 return;
172 data = strip_expr(data->unop);
173 if (data->type != EXPR_SYMBOL)
174 return;
176 if (has_global_scope(data))
177 return;
178 if (was_initialized(data))
179 return;
180 if (was_memset(data))
181 return;
182 if (holey_struct(data)) {
183 char *name;
185 name = get_variable_from_expr(data, NULL);
186 sm_msg("warn: check that '%s' doesn't leak information (struct has holes)", name);
187 free_string(name);
188 return;
190 check_members_initialized(data);
193 static void register_holey_structs(void)
195 struct token *token;
196 const char *struct_type;
198 token = get_tokens_file("kernel.paholes");
199 if (!token)
200 return;
201 if (token_type(token) != TOKEN_STREAMBEGIN)
202 return;
203 token = token->next;
204 while (token_type(token) != TOKEN_STREAMEND) {
205 if (token_type(token) != TOKEN_IDENT)
206 return;
207 struct_type = show_ident(token->ident);
208 insert_struct(holey_structs, alloc_string(struct_type), INT_PTR(1));
209 token = token->next;
211 clear_token_alloc();
214 static void register_clears_argument(void)
216 struct token *token;
217 const char *func;
218 int arg;
220 token = get_tokens_file("kernel.clears_argument");
221 if (!token)
222 return;
223 if (token_type(token) != TOKEN_STREAMBEGIN)
224 return;
225 token = token->next;
226 while (token_type(token) != TOKEN_STREAMEND) {
227 if (token_type(token) != TOKEN_IDENT)
228 return;
229 func = show_ident(token->ident);
230 token = token->next;
231 if (token_type(token) != TOKEN_NUMBER)
232 return;
233 arg = atoi(token->number);
235 add_function_hook(func, &match_clear, INT_PTR(arg));
236 token = token->next;
238 clear_token_alloc();
241 void check_rosenberg(int id)
243 if (option_project != PROJ_KERNEL)
244 return;
245 my_id = id;
246 holey_structs = create_function_hashtable(10000);
248 register_holey_structs();
249 register_clears_argument();
251 add_function_hook("copy_to_user", &match_copy_to_user, NULL);