user_data: the pointer returned from container_of() is ok
[smatch.git] / check_rosenberg.c
blobc2f802e19b55cb23410b439c862fab9dfe63cc1c
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 *unused)
165 struct expression *data;
167 data = get_argument_from_call_expr(expr->args, 1);
168 if (!data)
169 return;
170 if (data->type != EXPR_PREOP || data->op != '&')
171 return;
173 data = strip_expr(data->unop);
174 if (data->type != EXPR_SYMBOL)
175 return;
177 if (has_global_scope(data))
178 return;
179 if (was_initialized(data))
180 return;
181 if (was_memset(data))
182 return;
183 if (holey_struct(data)) {
184 char *name;
186 name = expr_to_var(data);
187 sm_msg("warn: check that '%s' doesn't leak information (struct has holes)", name);
188 free_string(name);
189 return;
191 check_members_initialized(data);
194 static void register_holey_structs(void)
196 struct token *token;
197 const char *struct_type;
199 token = get_tokens_file("kernel.paholes");
200 if (!token)
201 return;
202 if (token_type(token) != TOKEN_STREAMBEGIN)
203 return;
204 token = token->next;
205 while (token_type(token) != TOKEN_STREAMEND) {
206 if (token_type(token) != TOKEN_IDENT)
207 return;
208 struct_type = show_ident(token->ident);
209 insert_struct(holey_structs, alloc_string(struct_type), INT_PTR(1));
210 token = token->next;
212 clear_token_alloc();
215 static void register_clears_argument(void)
217 struct token *token;
218 const char *func;
219 int arg;
221 token = get_tokens_file("kernel.clears_argument");
222 if (!token)
223 return;
224 if (token_type(token) != TOKEN_STREAMBEGIN)
225 return;
226 token = token->next;
227 while (token_type(token) != TOKEN_STREAMEND) {
228 if (token_type(token) != TOKEN_IDENT)
229 return;
230 func = show_ident(token->ident);
231 token = token->next;
232 if (token_type(token) != TOKEN_NUMBER)
233 return;
234 arg = atoi(token->number);
236 add_function_hook(func, &match_clear, INT_PTR(arg));
237 token = token->next;
239 clear_token_alloc();
242 void check_rosenberg(int id)
244 if (option_project != PROJ_KERNEL)
245 return;
246 my_id = id;
247 holey_structs = create_function_hashtable(10000);
249 register_holey_structs();
250 register_clears_argument();
252 add_function_hook("copy_to_user", &match_copy_to_user, NULL);