1 /*
   2  * Copyright (C) 2018 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 
  21 static int my_id;
  22 static int param_set_id;
  23 
  24 STATE(terminated);
  25 STATE(unterminated);
  26 STATE(set);
  27 
  28 static void set_terminated_var_sym(const char *name, struct symbol *sym, struct smatch_state *state)
  29 {
  30         if (get_param_num_from_sym(sym) >= 0)
  31                 set_state(param_set_id, name, sym, &set);
  32         set_state(my_id, name, sym, state);
  33 }
  34 
  35 static void set_terminated(struct expression *expr, struct smatch_state *state)
  36 {
  37         struct symbol *sym;
  38         char *name;
  39 
  40         name = expr_to_var_sym(expr, &sym);
  41         if (!name || !sym)
  42                 return;
  43         set_terminated_var_sym(name, sym, state);
  44         free_string(name);
  45 }
  46 
  47 static void match_nul_assign(struct expression *expr)
  48 {
  49         struct expression *array;
  50         struct symbol *type;
  51         sval_t sval;
  52 
  53         if (expr->op != '=')
  54                 return;
  55 
  56         if (!get_value(expr->right, &sval) || sval.value != 0)
  57                 return;
  58 
  59         array = get_array_base(expr->left);
  60         if (!array)
  61                 return;
  62 
  63         type = get_type(array);
  64         if (!type)
  65                 return;
  66         type = get_real_base_type(type);
  67         if (type != &char_ctype)
  68                 return;
  69         set_terminated(array, &terminated);
  70 }
  71 
  72 static struct smatch_state *get_terminated_state(struct expression *expr)
  73 {
  74         struct sm_state *sm, *tmp;
  75 
  76         if (!expr)
  77                 return NULL;
  78         if (expr->type == EXPR_STRING)
  79                 return &terminated;
  80         sm = get_sm_state_expr(my_id, expr);
  81         if (!sm)
  82                 return NULL;
  83         if (sm->state == &terminated || sm->state == &unterminated)
  84                 return sm->state;
  85 
  86         FOR_EACH_PTR(sm->possible, tmp) {
  87                 if (tmp->state == &unterminated)
  88                         return &unterminated;
  89         } END_FOR_EACH_PTR(tmp);
  90 
  91         return NULL;
  92 }
  93 
  94 static void match_string_assign(struct expression *expr)
  95 {
  96         struct smatch_state *state;
  97 
  98         if (expr->op != '=')
  99                 return;
 100         state = get_terminated_state(expr->right);
 101         if (!state)
 102                 return;
 103         set_terminated(expr->left, state);
 104 }
 105 
 106 static int sm_to_term(struct sm_state *sm)
 107 {
 108         struct sm_state *tmp;
 109 
 110         if (!sm)
 111                 return -1;
 112         if (sm->state == &terminated)
 113                 return 1;
 114 
 115         FOR_EACH_PTR(sm->possible, tmp) {
 116                 if (tmp->state == &unterminated)
 117                         return 0;
 118         } END_FOR_EACH_PTR(tmp);
 119 
 120         return -1;
 121 }
 122 
 123 static void struct_member_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
 124 {
 125         int term;
 126 
 127         term = sm_to_term(sm);
 128         if (term < 0)
 129                 return;
 130 
 131         sql_insert_caller_info(call, TERMINATED, param, printed_name, term ? "1" : "0");
 132 }
 133 
 134 static void match_call_info(struct expression *expr)
 135 {
 136         struct smatch_state *state;
 137         struct expression *arg;
 138         int i;
 139 
 140         i = -1;
 141         FOR_EACH_PTR(expr->args, arg) {
 142                 i++;
 143 
 144                 state = get_terminated_state(arg);
 145                 if (!state)
 146                         continue;
 147                 sql_insert_caller_info(expr, TERMINATED, i, "$",
 148                                        (state == &terminated) ? "1" : "0");
 149         } END_FOR_EACH_PTR(arg);
 150 }
 151 
 152 static void caller_info_terminated(const char *name, struct symbol *sym, char *key, char *value)
 153 {
 154         char fullname[256];
 155 
 156         if (strcmp(key, "*$") == 0)
 157                 snprintf(fullname, sizeof(fullname), "*%s", name);
 158         else if (strncmp(key, "$", 1) == 0)
 159                 snprintf(fullname, 256, "%s%s", name, key + 1);
 160         else
 161                 return;
 162 
 163         set_state(my_id, fullname, sym, (*value == '1') ? &terminated : &unterminated);
 164 }
 165 
 166 static void split_return_info(int return_id, char *return_ranges, struct expression *expr)
 167 {
 168         struct symbol *returned_sym;
 169         struct sm_state *tmp, *sm;
 170         const char *param_name;
 171         int param;
 172         int term;
 173 
 174         FOR_EACH_MY_SM(param_set_id, __get_cur_stree(), tmp) {
 175                 sm = get_sm_state(my_id, tmp->name, tmp->sym);
 176                 if (!sm)
 177                         continue;
 178                 term = sm_to_term(sm);
 179                 if (term < 0)
 180                         continue;
 181                 param = get_param_num_from_sym(tmp->sym);
 182                 if (param < 0)
 183                         continue;
 184 
 185                 param_name = get_param_name(sm);
 186                 if (!param_name)
 187                         continue;
 188                 if (strcmp(param_name, "$") == 0)
 189                         continue;
 190 
 191                 sql_insert_return_states(return_id, return_ranges, TERMINATED,
 192                                          param, param_name, term ? "1" : "0");
 193         } END_FOR_EACH_SM(tmp);
 194 
 195         returned_sym = expr_to_sym(expr);
 196         if (!returned_sym)
 197                 return;
 198 
 199         FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
 200                 if (sm->sym != returned_sym)
 201                         continue;
 202                 term = sm_to_term(sm);
 203                 if (term < 0)
 204                         continue;
 205                 param_name = get_param_name(sm);
 206                 if (!param_name)
 207                         continue;
 208                 sql_insert_return_states(return_id, return_ranges, TERMINATED,
 209                                          -1, param_name, term ? "1" : "0");
 210         } END_FOR_EACH_SM(sm);
 211 }
 212 
 213 static void return_info_terminated(struct expression *expr, int param, char *key, char *value)
 214 {
 215         struct expression *arg;
 216         char *name;
 217         struct symbol *sym;
 218 
 219         if (param == -1) {
 220                 arg = expr->left;
 221         } else {
 222                 struct expression *call = expr;
 223 
 224                 while (call->type == EXPR_ASSIGNMENT)
 225                         call = strip_expr(call->right);
 226                 if (call->type != EXPR_CALL)
 227                         return;
 228 
 229                 arg = get_argument_from_call_expr(call->args, param);
 230                 if (!arg)
 231                         return;
 232         }
 233 
 234         name = get_variable_from_key(arg, key, &sym);
 235         if (!name || !sym)
 236                 goto free;
 237 
 238         set_terminated_var_sym(name, sym, (*value == '1') ? &terminated : &unterminated);
 239 free:
 240         free_string(name);
 241 }
 242 
 243 bool is_nul_terminated(struct expression *expr)
 244 {
 245         if (get_terminated_state(expr) == &terminated)
 246                 return 1;
 247         return 0;
 248 }
 249 
 250 void register_nul_terminator(int id)
 251 {
 252         my_id = id;
 253 
 254         add_hook(&match_nul_assign, ASSIGNMENT_HOOK);
 255         add_hook(&match_string_assign, ASSIGNMENT_HOOK);
 256 
 257         add_hook(&match_call_info, FUNCTION_CALL_HOOK);
 258         add_member_info_callback(my_id, struct_member_callback);
 259         add_split_return_callback(&split_return_info);
 260 
 261         select_caller_info_hook(caller_info_terminated, TERMINATED);
 262         select_return_states_hook(TERMINATED, return_info_terminated);
 263 }
 264 
 265 void register_nul_terminator_param_set(int id)
 266 {
 267         param_set_id = id;
 268 }