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 static int skb_put_id; 32 33 STATE(cleared); 34 35 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state) 36 { 37 struct symbol *type; 38 39 type = get_real_base_type(sym); 40 if (!type || type->type != SYM_STRUCT) 41 return; 42 43 set_state(my_member_id, name, sym, state); 44 } 45 46 static void print_holey_warning(struct expression *data, const char *member) 47 { 48 char *name; 49 50 name = expr_to_str(data); 51 if (member) { 52 sm_warning("check that '%s' doesn't leak information (struct has a hole after '%s')", 53 name, member); 54 } else { 55 sm_warning("check that '%s' doesn't leak information (struct has holes)", 56 name); 57 } 58 free_string(name); 59 } 60 61 static int check_struct(struct expression *expr, struct symbol *type) 62 { 63 struct symbol *tmp, *base_type; 64 const char *prev = NULL; 65 int align; 66 67 if (type->ctype.alignment == 1) 68 return 0; 69 70 align = 0; 71 FOR_EACH_PTR(type->symbol_list, tmp) { 72 base_type = get_real_base_type(tmp); 73 if (base_type && base_type->type == SYM_STRUCT) { 74 if (check_struct(expr, base_type)) 75 return 1; 76 } 77 78 if (!tmp->ctype.alignment) { 79 sm_perror("cannot determine the alignment here"); 80 } else if (align % tmp->ctype.alignment) { 81 print_holey_warning(expr, prev); 82 return 1; 83 } 84 85 if (base_type == &bool_ctype) 86 align += 1; 87 else if (type_bits(tmp) <= 0) 88 align = 0; 89 else 90 align += type_bytes(tmp); 91 92 if (tmp->ident) 93 prev = tmp->ident->name; 94 else 95 prev = NULL; 96 } END_FOR_EACH_PTR(tmp); 97 98 if (align % type->ctype.alignment) { 99 print_holey_warning(expr, prev); 100 return 1; 101 } 102 103 return 0; 104 } 105 106 static int warn_on_holey_struct(struct expression *expr) 107 { 108 struct symbol *type; 109 type = get_type(expr); 110 if (!type || type->type != SYM_STRUCT) 111 return 0; 112 113 return check_struct(expr, type); 114 } 115 116 static int has_global_scope(struct expression *expr) 117 { 118 struct symbol *sym; 119 120 if (expr->type != EXPR_SYMBOL) 121 return FALSE; 122 sym = expr->symbol; 123 if (!sym) 124 return FALSE; 125 return toplevel(sym->scope); 126 } 127 128 static void match_clear(const char *fn, struct expression *expr, void *_arg_no) 129 { 130 struct expression *ptr; 131 int arg_no = PTR_INT(_arg_no); 132 133 ptr = get_argument_from_call_expr(expr->args, arg_no); 134 if (!ptr) 135 return; 136 ptr = strip_expr(ptr); 137 if (ptr->type != EXPR_PREOP || ptr->op != '&') 138 return; 139 ptr = strip_expr(ptr->unop); 140 set_state_expr(my_whole_id, ptr, &cleared); 141 } 142 143 static int was_memset(struct expression *expr) 144 { 145 if (get_state_expr(my_whole_id, expr) == &cleared) 146 return 1; 147 return 0; 148 } 149 150 static int member_initialized(char *name, struct symbol *outer, struct symbol *member, int pointer) 151 { 152 char buf[256]; 153 struct symbol *base; 154 155 base = get_base_type(member); 156 if (!base || base->type != SYM_BASETYPE || !member->ident) 157 return FALSE; 158 159 if (pointer) 160 snprintf(buf, 256, "%s->%s", name, member->ident->name); 161 else 162 snprintf(buf, 256, "%s.%s", name, member->ident->name); 163 164 if (get_state(my_member_id, buf, outer)) 165 return TRUE; 166 167 return FALSE; 168 } 169 170 static int member_uninitialized(char *name, struct symbol *outer, struct symbol *member, int pointer) 171 { 172 char buf[256]; 173 struct symbol *base; 174 struct sm_state *sm; 175 176 base = get_base_type(member); 177 if (!base || base->type != SYM_BASETYPE || !member->ident) 178 return FALSE; 179 180 if (pointer) 181 snprintf(buf, 256, "%s->%s", name, member->ident->name); 182 else 183 snprintf(buf, 256, "%s.%s", name, member->ident->name); 184 185 sm = get_sm_state(my_member_id, buf, outer); 186 if (sm && !slist_has_state(sm->possible, &undefined)) 187 return FALSE; 188 189 sm_warning("check that '%s' doesn't leak information", buf); 190 return TRUE; 191 } 192 193 static int check_members_initialized(struct expression *expr) 194 { 195 char *name; 196 struct symbol *outer; 197 struct symbol *sym; 198 struct symbol *tmp; 199 int pointer = 0; 200 int printed = 0; 201 202 sym = get_type(expr); 203 if (sym && sym->type == SYM_PTR) { 204 pointer = 1; 205 sym = get_real_base_type(sym); 206 } 207 if (!sym) 208 return 0; 209 if (sym->type != SYM_STRUCT) 210 return 0; 211 212 name = expr_to_var_sym(expr, &outer); 213 214 /* 215 * check that at least one member was set. If all of them were not set 216 * it's more likely a problem in the check than a problem in the kernel 217 * code. 218 */ 219 FOR_EACH_PTR(sym->symbol_list, tmp) { 220 if (member_initialized(name, outer, tmp, pointer)) 221 goto check; 222 } END_FOR_EACH_PTR(tmp); 223 goto out; 224 225 check: 226 FOR_EACH_PTR(sym->symbol_list, tmp) { 227 if (member_uninitialized(name, outer, tmp, pointer)) { 228 printed = 1; 229 goto out; 230 } 231 } END_FOR_EACH_PTR(tmp); 232 out: 233 free_string(name); 234 return printed; 235 } 236 237 static void check_was_initialized(struct expression *data) 238 { 239 data = strip_expr(data); 240 if (!data) 241 return; 242 if (data->type == EXPR_PREOP && data->op == '&') 243 data = strip_expr(data->unop); 244 if (data->type != EXPR_SYMBOL) 245 return; 246 247 if (has_global_scope(data)) 248 return; 249 if (was_memset(data)) 250 return; 251 if (warn_on_holey_struct(data)) 252 return; 253 check_members_initialized(data); 254 } 255 256 static void check_skb_put(struct expression *data) 257 { 258 data = strip_expr(data); 259 if (!data) 260 return; 261 if (data->type == EXPR_PREOP && data->op == '&') 262 data = strip_expr(data->unop); 263 264 if (was_memset(data)) 265 return; 266 if (warn_on_holey_struct(data)) 267 return; 268 check_members_initialized(data); 269 } 270 271 static void match_copy_to_user(const char *fn, struct expression *expr, void *_arg) 272 { 273 int arg = PTR_INT(_arg); 274 struct expression *data; 275 276 data = get_argument_from_call_expr(expr->args, arg); 277 data = strip_expr(data); 278 if (!data) 279 return; 280 if (data->type != EXPR_PREOP || data->op != '&') 281 return; 282 check_was_initialized(data); 283 } 284 285 static void db_param_cleared(struct expression *expr, int param, char *key, char *value) 286 { 287 while (expr->type == EXPR_ASSIGNMENT) 288 expr = strip_expr(expr->right); 289 if (expr->type != EXPR_CALL) 290 return; 291 292 match_clear(NULL, expr, INT_PTR(param)); 293 } 294 295 static struct smatch_state *alloc_expr_state(struct expression *expr) 296 { 297 struct smatch_state *state; 298 char *name; 299 300 name = expr_to_str(expr); 301 if (!name) 302 return NULL; 303 304 state = __alloc_smatch_state(0); 305 expr = strip_expr(expr); 306 state->name = alloc_sname(name); 307 free_string(name); 308 state->data = expr; 309 return state; 310 } 311 312 static void match_skb_put(const char *fn, struct expression *expr, void *unused) 313 { 314 struct symbol *type; 315 struct smatch_state *state; 316 317 type = get_type(expr->left); 318 type = get_real_base_type(type); 319 if (!type || type->type != SYM_STRUCT) 320 return; 321 state = alloc_expr_state(expr->left); 322 set_state_expr(skb_put_id, expr->left, state); 323 } 324 325 static void match_return_skb_put(struct expression *expr) 326 { 327 struct sm_state *sm; 328 struct stree *stree; 329 330 if (is_error_return(expr)) 331 return; 332 333 stree = __get_cur_stree(); 334 335 FOR_EACH_MY_SM(skb_put_id, stree, sm) { 336 check_skb_put(sm->state->data); 337 } END_FOR_EACH_SM(sm); 338 } 339 340 static void register_clears_argument(void) 341 { 342 struct token *token; 343 const char *func; 344 int arg; 345 346 token = get_tokens_file("kernel.clears_argument"); 347 if (!token) 348 return; 349 if (token_type(token) != TOKEN_STREAMBEGIN) 350 return; 351 token = token->next; 352 while (token_type(token) != TOKEN_STREAMEND) { 353 if (token_type(token) != TOKEN_IDENT) 354 return; 355 func = show_ident(token->ident); 356 token = token->next; 357 if (token_type(token) != TOKEN_NUMBER) 358 return; 359 arg = atoi(token->number); 360 361 add_function_hook(func, &match_clear, INT_PTR(arg)); 362 token = token->next; 363 } 364 clear_token_alloc(); 365 } 366 367 static void register_copy_funcs_from_file(void) 368 { 369 struct token *token; 370 const char *func; 371 int arg; 372 373 token = get_tokens_file("kernel.rosenberg_funcs"); 374 if (!token) 375 return; 376 if (token_type(token) != TOKEN_STREAMBEGIN) 377 return; 378 token = token->next; 379 while (token_type(token) != TOKEN_STREAMEND) { 380 if (token_type(token) != TOKEN_IDENT) 381 return; 382 func = show_ident(token->ident); 383 token = token->next; 384 if (token_type(token) != TOKEN_NUMBER) 385 return; 386 arg = atoi(token->number); 387 add_function_hook(func, &match_copy_to_user, INT_PTR(arg)); 388 token = token->next; 389 } 390 clear_token_alloc(); 391 } 392 393 void check_rosenberg(int id) 394 { 395 if (option_project != PROJ_KERNEL) 396 return; 397 my_whole_id = id; 398 399 add_function_hook("memset", &match_clear, INT_PTR(0)); 400 add_function_hook("memcpy", &match_clear, INT_PTR(0)); 401 add_function_hook("memzero", &match_clear, INT_PTR(0)); 402 add_function_hook("__memset", &match_clear, INT_PTR(0)); 403 add_function_hook("__memcpy", &match_clear, INT_PTR(0)); 404 add_function_hook("__memzero", &match_clear, INT_PTR(0)); 405 add_function_hook("__builtin_memset", &match_clear, INT_PTR(0)); 406 add_function_hook("__builtin_memcpy", &match_clear, INT_PTR(0)); 407 408 register_clears_argument(); 409 select_return_states_hook(PARAM_CLEARED, &db_param_cleared); 410 411 register_copy_funcs_from_file(); 412 } 413 414 void check_rosenberg2(int id) 415 { 416 if (option_project != PROJ_KERNEL) 417 return; 418 419 my_member_id = id; 420 set_dynamic_states(my_member_id); 421 add_extra_mod_hook(&extra_mod_hook); 422 } 423 424 void check_rosenberg3(int id) 425 { 426 if (option_project != PROJ_KERNEL) 427 return; 428 429 skb_put_id = id; 430 set_dynamic_states(skb_put_id); 431 add_function_assign_hook("skb_put", &match_skb_put, NULL); 432 add_hook(&match_return_skb_put, RETURN_HOOK); 433 } 434