1 /*
   2  * Copyright (C) 2014 Oracle.
   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 #include "smatch.h"
  19 #include "smatch_slist.h"
  20 #include "smatch_extra.h"
  21 
  22 static int my_id;
  23 
  24 STATE(uninitialized);
  25 STATE(initialized);
  26 
  27 static void pre_merge_hook(struct sm_state *sm)
  28 {
  29         if (is_impossible_path())
  30                 set_state(my_id, sm->name, sm->sym, &initialized);
  31 }
  32 
  33 static void mark_members_uninitialized(struct symbol *sym)
  34 {
  35         struct symbol *struct_type, *tmp, *base_type;
  36         char buf[256];
  37 
  38         struct_type = get_real_base_type(sym);
  39         FOR_EACH_PTR(struct_type->symbol_list, tmp) {
  40                 if (!tmp->ident)
  41                         continue;
  42                 base_type = get_real_base_type(tmp);
  43                 if (!base_type ||
  44                     base_type->type == SYM_STRUCT ||
  45                     base_type->type == SYM_ARRAY ||
  46                     base_type->type == SYM_UNION)
  47                         continue;
  48                 snprintf(buf, sizeof(buf), "%s.%s", sym->ident->name, tmp->ident->name);
  49                 set_state(my_id, buf, sym, &uninitialized);
  50         } END_FOR_EACH_PTR(tmp);
  51 }
  52 
  53 static void match_declarations(struct symbol *sym)
  54 {
  55         struct symbol *type;
  56 
  57         if (sym->initializer)
  58                 return;
  59 
  60         type = get_real_base_type(sym);
  61         /* Smatch is crap at tracking arrays */
  62         if (type->type == SYM_ARRAY)
  63                 return;
  64         if (type->type == SYM_UNION)
  65                 return;
  66         if (sym->ctype.modifiers & MOD_STATIC)
  67                 return;
  68 
  69         if (!sym->ident)
  70                 return;
  71 
  72         if (type->type == SYM_STRUCT) {
  73                 mark_members_uninitialized(sym);
  74                 return;
  75         }
  76 
  77         set_state(my_id, sym->ident->name, sym, &uninitialized);
  78 }
  79 
  80 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
  81 {
  82         if (!sym || !sym->ident)
  83                 return;
  84         if (strcmp(name, sym->ident->name) != 0)
  85                 return;
  86         set_state(my_id, name, sym, &initialized);
  87 }
  88 
  89 static void match_assign(struct expression *expr)
  90 {
  91         struct expression *right;
  92 
  93         right = strip_expr(expr->right);
  94         if (right->type == EXPR_PREOP && right->op == '&')
  95                 set_state_expr(my_id, right->unop, &initialized);
  96 }
  97 
  98 static int is_initialized(struct expression *expr)
  99 {
 100         struct sm_state *sm;
 101 
 102         expr = strip_expr(expr);
 103         if (expr->type != EXPR_SYMBOL)
 104                 return 1;
 105         sm = get_sm_state_expr(my_id, expr);
 106         if (!sm)
 107                 return 1;
 108         if (!slist_has_state(sm->possible, &uninitialized))
 109                 return 1;
 110         return 0;
 111 }
 112 
 113 static void match_dereferences(struct expression *expr)
 114 {
 115         char *name;
 116 
 117         if (parse_error)
 118                 return;
 119 
 120         if (expr->type != EXPR_PREOP)
 121                 return;
 122         if (is_impossible_path())
 123                 return;
 124         if (is_initialized(expr->unop))
 125                 return;
 126 
 127         name = expr_to_str(expr->unop);
 128         sm_error("potentially dereferencing uninitialized '%s'.", name);
 129         free_string(name);
 130 
 131         set_state_expr(my_id, expr->unop, &initialized);
 132 }
 133 
 134 static void match_condition(struct expression *expr)
 135 {
 136         char *name;
 137 
 138         if (parse_error)
 139                 return;
 140 
 141         if (is_impossible_path())
 142                 return;
 143 
 144         if (is_initialized(expr))
 145                 return;
 146 
 147         name = expr_to_str(expr);
 148         sm_error("potentially using uninitialized '%s'.", name);
 149         free_string(name);
 150 
 151         set_state_expr(my_id, expr, &initialized);
 152 }
 153 
 154 static void match_call(struct expression *expr)
 155 {
 156         struct expression *arg;
 157         char *name;
 158 
 159         if (parse_error)
 160                 return;
 161 
 162         if (is_impossible_path())
 163                 return;
 164 
 165         FOR_EACH_PTR(expr->args, arg) {
 166                 if (is_initialized(arg))
 167                         continue;
 168 
 169                 name = expr_to_str(arg);
 170                 sm_warning("passing uninitialized '%s'", name);
 171                 free_string(name);
 172 
 173                 set_state_expr(my_id, arg, &initialized);
 174         } END_FOR_EACH_PTR(arg);
 175 }
 176 
 177 static int param_used_callback(void *found, int argc, char **argv, char **azColName)
 178 {
 179         *(int *)found = 1;
 180         return 0;
 181 }
 182 
 183 static int member_is_used(struct expression *call, int param, char *printed_name)
 184 {
 185         int found;
 186 
 187         /* for function pointers assume everything is used */
 188         if (call->fn->type != EXPR_SYMBOL)
 189                 return 0;
 190 
 191         found = 0;
 192         run_sql(&param_used_callback, &found,
 193                 "select * from return_implies where %s and type = %d and parameter = %d and key = '%s';",
 194                 get_static_filter(call->fn->symbol), PARAM_USED, param, printed_name);
 195         return found;
 196 }
 197 
 198 static void match_call_struct_members(struct expression *expr)
 199 {
 200         struct symbol *type, *sym;
 201         struct expression *arg;
 202         struct sm_state *sm;
 203         char *arg_name;
 204         char buf[256];
 205         int param;
 206 
 207         return;
 208 
 209         if (parse_error)
 210                 return;
 211 
 212         param = -1;
 213         FOR_EACH_PTR(expr->args, arg) {
 214                 param++;
 215                 if (arg->type != EXPR_PREOP || arg->op != '&')
 216                         continue;
 217                 type = get_type(arg->unop);
 218                 if (!type || type->type != SYM_STRUCT)
 219                         continue;
 220                 arg_name = expr_to_var_sym(arg->unop, &sym);
 221                 if (!arg_name || !sym)
 222                         goto free;
 223                 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
 224                         if (sm->sym != sym)
 225                                 continue;
 226                         if (!slist_has_state(sm->possible, &uninitialized))
 227                                 continue;
 228                         snprintf(buf, sizeof(buf), "$->%s", sm->name + strlen(arg_name) + 1);
 229                         if (!member_is_used(expr, param, buf))
 230                                 goto free;
 231                         sm_warning("struct member %s is uninitialized", sm->name);
 232                 } END_FOR_EACH_SM(sm);
 233 
 234 free:
 235                 free_string(arg_name);
 236         } END_FOR_EACH_PTR(arg);
 237 }
 238 
 239 static int is_being_modified(struct expression *expr)
 240 {
 241         struct expression *parent;
 242         struct statement *stmt;
 243 
 244         parent = expr_get_parent_expr(expr);
 245         if (!parent)
 246                 return 0;
 247         while (parent->type == EXPR_PREOP && parent->op == '(') {
 248                 parent = expr_get_parent_expr(parent);
 249                 if (!parent)
 250                         return 0;
 251         }
 252         if (parent->type == EXPR_PREOP && parent->op == '&')
 253                 return 1;
 254         if (parent->type == EXPR_ASSIGNMENT && expr_equiv(parent->left, expr))
 255                 return 1;
 256 
 257         stmt = last_ptr_list((struct ptr_list *)big_statement_stack);
 258         if (stmt && stmt->type == STMT_ASM)
 259                 return 1;
 260 
 261         return 0;
 262 }
 263 
 264 static void match_symbol(struct expression *expr)
 265 {
 266         char *name;
 267 
 268         if (parse_error)
 269                 return;
 270 
 271         if (is_impossible_path())
 272                 return;
 273 
 274         if (is_initialized(expr))
 275                 return;
 276 
 277         if (is_being_modified(expr))
 278                 return;
 279 
 280         name = expr_to_str(expr);
 281         sm_error("uninitialized symbol '%s'.", name);
 282         free_string(name);
 283 
 284         set_state_expr(my_id, expr, &initialized);
 285 }
 286 
 287 static void match_untracked(struct expression *call, int param)
 288 {
 289         struct expression *arg;
 290 
 291         arg = get_argument_from_call_expr(call->args, param);
 292         arg = strip_expr(arg);
 293         if (!arg || arg->type != EXPR_PREOP || arg->op != '&')
 294                 return;
 295         arg = strip_expr(arg->unop);
 296         set_state_expr(my_id, arg, &initialized);
 297 }
 298 
 299 static void match_ignore_param(const char *fn, struct expression *expr, void *_arg_nr)
 300 {
 301         int arg_nr = PTR_INT(_arg_nr);
 302         struct expression *arg;
 303 
 304         arg = get_argument_from_call_expr(expr->args, arg_nr);
 305         arg = strip_expr(arg);
 306         if (!arg)
 307                 return;
 308         if (arg->type != EXPR_PREOP || arg->op != '&')
 309                 return;
 310         arg = strip_expr(arg->unop);
 311         set_state_expr(my_id, arg, &initialized);
 312 }
 313 
 314 static void register_ignored_params_from_file(void)
 315 {
 316         char name[256];
 317         struct token *token;
 318         const char *func;
 319         char prev_func[256];
 320         int param;
 321 
 322         memset(prev_func, 0, sizeof(prev_func));
 323         snprintf(name, 256, "%s.ignore_uninitialized_param", option_project_str);
 324         name[255] = '\0';
 325         token = get_tokens_file(name);
 326         if (!token)
 327                 return;
 328         if (token_type(token) != TOKEN_STREAMBEGIN)
 329                 return;
 330         token = token->next;
 331         while (token_type(token) != TOKEN_STREAMEND) {
 332                 if (token_type(token) != TOKEN_IDENT)
 333                         return;
 334                 func = show_ident(token->ident);
 335 
 336                 token = token->next;
 337                 if (token_type(token) != TOKEN_NUMBER)
 338                         return;
 339                 param = atoi(token->number);
 340 
 341                 add_function_hook(func, &match_ignore_param, INT_PTR(param));
 342 
 343                 token = token->next;
 344         }
 345         clear_token_alloc();
 346 }
 347 
 348 void check_uninitialized(int id)
 349 {
 350         my_id = id;
 351 
 352         add_hook(&match_declarations, DECLARATION_HOOK);
 353         add_extra_mod_hook(&extra_mod_hook);
 354         add_hook(&match_assign, ASSIGNMENT_HOOK);
 355         add_untracked_param_hook(&match_untracked);
 356         add_pre_merge_hook(my_id, &pre_merge_hook);
 357 
 358         add_hook(&match_dereferences, DEREF_HOOK);
 359         add_hook(&match_condition, CONDITION_HOOK);
 360         add_hook(&match_call, FUNCTION_CALL_HOOK);
 361         add_hook(&match_call_struct_members, FUNCTION_CALL_HOOK);
 362         add_hook(&match_symbol, SYM_HOOK);
 363 
 364         register_ignored_params_from_file();
 365 }