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