1 /* 2 * Copyright (C) 2011 Dan Carpenter. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 2 7 * of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt 16 */ 17 18 /* Does a search for Dan Rosenberg style info leaks */ 19 20 /* fixme: struct includes a struct with a hole in it */ 21 /* function is called that clears the struct */ 22 23 #include "scope.h" 24 #include "smatch.h" 25 #include "smatch_function_hashtable.h" 26 #include "smatch_slist.h" 27 #include "smatch_extra.h" 28 29 static int my_whole_id; 30 static int my_member_id; 31 32 STATE(cleared); 33 34 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state) 35 { 36 struct symbol *type; 37 38 type = get_real_base_type(sym); 39 if (!type || type->type != SYM_STRUCT) 40 return; 41 42 set_state(my_member_id, name, sym, state); 43 } 44 45 static void print_holey_warning(struct expression *data, const char *member) 46 { 47 char *name; 48 49 name = expr_to_str(data); 50 if (member) { 51 sm_warning("check that '%s' doesn't leak information (struct has a hole after '%s')", 52 name, member); 53 } else { 54 sm_warning("check that '%s' doesn't leak information (struct has holes)", 55 name); 56 } 57 free_string(name); 58 } 59 60 static int check_struct(struct expression *expr, struct symbol *type) 61 { 62 struct symbol *tmp, *base_type; 63 const char *prev = NULL; 64 int align; 65 66 if (type->ctype.alignment == 1) 67 return 0; 68 69 align = 0; 70 FOR_EACH_PTR(type->symbol_list, tmp) { 71 base_type = get_real_base_type(tmp); 72 if (base_type && base_type->type == SYM_STRUCT) { 73 if (check_struct(expr, base_type)) 74 return 1; 75 } 76 77 if (!tmp->ctype.alignment) { 78 sm_perror("cannot determine the alignment here"); 79 } else if (align % tmp->ctype.alignment) { 80 print_holey_warning(expr, prev); 81 return 1; 82 } 83 84 if (base_type == &bool_ctype) 85 align += 1; 86 else if (type_bits(tmp) <= 0) 87 align = 0; 88 else 89 align += type_bytes(tmp); 90 91 if (tmp->ident) 92 prev = tmp->ident->name; 93 else 94 prev = NULL; 95 } END_FOR_EACH_PTR(tmp); 96 97 if (align % type->ctype.alignment) { 98 print_holey_warning(expr, prev); 99 return 1; 100 } 101 102 return 0; 103 } 104 105 static int warn_on_holey_struct(struct expression *expr) 106 { 107 struct symbol *type; 108 type = get_type(expr); 109 if (!type || type->type != SYM_STRUCT) 110 return 0; 111 112 return check_struct(expr, type); 113 } 114 115 static int has_global_scope(struct expression *expr) 116 { 117 struct symbol *sym; 118 119 if (expr->type != EXPR_SYMBOL) 120 return FALSE; 121 sym = expr->symbol; 122 if (!sym) 123 return FALSE; 124 return toplevel(sym->scope); 125 } 126 127 static int was_initialized(struct expression *expr) 128 { 129 struct symbol *sym; 130 char *name; 131 132 name = expr_to_var_sym(expr, &sym); 133 if (!name) 134 return 0; 135 if (sym->initializer) 136 return 1; 137 return 0; 138 } 139 140 static void match_clear(const char *fn, struct expression *expr, void *_arg_no) 141 { 142 struct expression *ptr; 143 int arg_no = PTR_INT(_arg_no); 144 145 ptr = get_argument_from_call_expr(expr->args, arg_no); 146 if (!ptr) 147 return; 148 ptr = strip_expr(ptr); 149 if (ptr->type != EXPR_PREOP || ptr->op != '&') 150 return; 151 ptr = strip_expr(ptr->unop); 152 set_state_expr(my_whole_id, ptr, &cleared); 153 } 154 155 static int was_memset(struct expression *expr) 156 { 157 if (get_state_expr(my_whole_id, expr) == &cleared) 158 return 1; 159 return 0; 160 } 161 162 static int member_initialized(char *name, struct symbol *outer, struct symbol *member, int pointer) 163 { 164 char buf[256]; 165 struct symbol *base; 166 167 base = get_base_type(member); 168 if (!base || base->type != SYM_BASETYPE || !member->ident) 169 return FALSE; 170 171 if (pointer) 172 snprintf(buf, 256, "%s->%s", name, member->ident->name); 173 else 174 snprintf(buf, 256, "%s.%s", name, member->ident->name); 175 176 if (get_state(my_member_id, buf, outer)) 177 return TRUE; 178 179 return FALSE; 180 } 181 182 static int member_uninitialized(char *name, struct symbol *outer, struct symbol *member, int pointer) 183 { 184 char buf[256]; 185 struct symbol *base; 186 struct sm_state *sm; 187 188 base = get_base_type(member); 189 if (!base || base->type != SYM_BASETYPE || !member->ident) 190 return FALSE; 191 192 if (pointer) 193 snprintf(buf, 256, "%s->%s", name, member->ident->name); 194 else 195 snprintf(buf, 256, "%s.%s", name, member->ident->name); 196 197 sm = get_sm_state(my_member_id, buf, outer); 198 if (sm && !slist_has_state(sm->possible, &undefined)) 199 return FALSE; 200 201 sm_warning("check that '%s' doesn't leak information", buf); 202 return TRUE; 203 } 204 205 static int check_members_initialized(struct expression *expr) 206 { 207 char *name; 208 struct symbol *outer; 209 struct symbol *sym; 210 struct symbol *tmp; 211 int pointer = 0; 212 int printed = 0; 213 214 sym = get_type(expr); 215 if (sym && sym->type == SYM_PTR) { 216 pointer = 1; 217 sym = get_real_base_type(sym); 218 } 219 if (!sym) 220 return 0; 221 if (sym->type != SYM_STRUCT) 222 return 0; 223 224 name = expr_to_var_sym(expr, &outer); 225 226 /* 227 * check that at least one member was set. If all of them were not set 228 * it's more likely a problem in the check than a problem in the kernel 229 * code. 230 */ 231 FOR_EACH_PTR(sym->symbol_list, tmp) { 232 if (member_initialized(name, outer, tmp, pointer)) 233 goto check; 234 } END_FOR_EACH_PTR(tmp); 235 goto out; 236 237 check: 238 FOR_EACH_PTR(sym->symbol_list, tmp) { 239 if (member_uninitialized(name, outer, tmp, pointer)) { 240 printed = 1; 241 goto out; 242 } 243 } END_FOR_EACH_PTR(tmp); 244 out: 245 free_string(name); 246 return printed; 247 } 248 249 static void check_was_initialized(struct expression *data) 250 { 251 data = strip_expr(data); 252 if (!data) 253 return; 254 if (data->type == EXPR_PREOP && data->op == '&') 255 data = strip_expr(data->unop); 256 if (data->type != EXPR_SYMBOL) 257 return; 258 259 if (has_global_scope(data)) 260 return; 261 if (was_initialized(data)) 262 return; 263 if (was_memset(data)) 264 return; 265 if (warn_on_holey_struct(data)) 266 return; 267 check_members_initialized(data); 268 } 269 270 static void match_copy_to_user(const char *fn, struct expression *expr, void *_arg) 271 { 272 int arg = PTR_INT(_arg); 273 struct expression *data; 274 275 data = get_argument_from_call_expr(expr->args, arg); 276 data = strip_expr(data); 277 if (!data) 278 return; 279 if (data->type != EXPR_PREOP || data->op != '&') 280 return; 281 check_was_initialized(data); 282 } 283 284 static void db_param_cleared(struct expression *expr, int param, char *key, char *value) 285 { 286 while (expr->type == EXPR_ASSIGNMENT) 287 expr = strip_expr(expr->right); 288 if (expr->type != EXPR_CALL) 289 return; 290 291 match_clear(NULL, expr, INT_PTR(param)); 292 } 293 294 static void match_assign(struct expression *expr) 295 { 296 struct symbol *type; 297 298 type = get_type(expr->left); 299 if (!type || type->type != SYM_STRUCT) 300 return; 301 set_state_expr(my_whole_id, expr->left, &cleared); 302 } 303 304 static void register_clears_argument(void) 305 { 306 struct token *token; 307 const char *func; 308 int arg; 309 310 token = get_tokens_file("kernel.clears_argument"); 311 if (!token) 312 return; 313 if (token_type(token) != TOKEN_STREAMBEGIN) 314 return; 315 token = token->next; 316 while (token_type(token) != TOKEN_STREAMEND) { 317 if (token_type(token) != TOKEN_IDENT) 318 return; 319 func = show_ident(token->ident); 320 token = token->next; 321 if (token_type(token) != TOKEN_NUMBER) 322 return; 323 arg = atoi(token->number); 324 325 add_function_hook(func, &match_clear, INT_PTR(arg)); 326 token = token->next; 327 } 328 clear_token_alloc(); 329 } 330 331 static void register_copy_funcs_from_file(void) 332 { 333 struct token *token; 334 const char *func; 335 int arg; 336 337 token = get_tokens_file("kernel.rosenberg_funcs"); 338 if (!token) 339 return; 340 if (token_type(token) != TOKEN_STREAMBEGIN) 341 return; 342 token = token->next; 343 while (token_type(token) != TOKEN_STREAMEND) { 344 if (token_type(token) != TOKEN_IDENT) 345 return; 346 func = show_ident(token->ident); 347 token = token->next; 348 if (token_type(token) != TOKEN_NUMBER) 349 return; 350 arg = atoi(token->number); 351 add_function_hook(func, &match_copy_to_user, INT_PTR(arg)); 352 token = token->next; 353 } 354 clear_token_alloc(); 355 } 356 357 void check_rosenberg(int id) 358 { 359 if (option_project != PROJ_KERNEL) 360 return; 361 my_whole_id = id; 362 363 add_function_hook("memset", &match_clear, INT_PTR(0)); 364 add_function_hook("memcpy", &match_clear, INT_PTR(0)); 365 add_function_hook("memzero", &match_clear, INT_PTR(0)); 366 add_function_hook("__memset", &match_clear, INT_PTR(0)); 367 add_function_hook("__memcpy", &match_clear, INT_PTR(0)); 368 add_function_hook("__memzero", &match_clear, INT_PTR(0)); 369 add_function_hook("__builtin_memset", &match_clear, INT_PTR(0)); 370 add_function_hook("__builtin_memcpy", &match_clear, INT_PTR(0)); 371 372 add_hook(&match_assign, ASSIGNMENT_HOOK); 373 register_clears_argument(); 374 select_return_states_hook(PARAM_CLEARED, &db_param_cleared); 375 376 register_copy_funcs_from_file(); 377 } 378 379 void check_rosenberg2(int id) 380 { 381 if (option_project != PROJ_KERNEL) 382 return; 383 384 my_member_id = id; 385 set_dynamic_states(my_member_id); 386 add_extra_mod_hook(&extra_mod_hook); 387 } 388