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