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_var_sym(const char *name, struct symbol *sym) 73 { 74 struct sm_state *sm, *tmp; 75 76 sm = get_sm_state(my_id, name, sym); 77 if (!sm) 78 return NULL; 79 if (sm->state == &terminated || sm->state == &unterminated) 80 return sm->state; 81 82 FOR_EACH_PTR(sm->possible, tmp) { 83 if (tmp->state == &unterminated) 84 return &unterminated; 85 } END_FOR_EACH_PTR(tmp); 86 87 return NULL; 88 } 89 90 static struct smatch_state *get_terminated_state(struct expression *expr) 91 { 92 struct sm_state *sm, *tmp; 93 94 if (!expr) 95 return NULL; 96 if (expr->type == EXPR_STRING) 97 return &terminated; 98 sm = get_sm_state_expr(my_id, expr); 99 if (!sm) 100 return NULL; 101 if (sm->state == &terminated || sm->state == &unterminated) 102 return sm->state; 103 104 FOR_EACH_PTR(sm->possible, tmp) { 105 if (tmp->state == &unterminated) 106 return &unterminated; 107 } END_FOR_EACH_PTR(tmp); 108 109 return NULL; 110 } 111 112 static void match_string_assign(struct expression *expr) 113 { 114 struct smatch_state *state; 115 116 if (expr->op != '=') 117 return; 118 state = get_terminated_state(expr->right); 119 if (!state) 120 return; 121 set_terminated(expr->left, state); 122 } 123 124 static int sm_to_term(struct sm_state *sm) 125 { 126 struct sm_state *tmp; 127 128 if (!sm) 129 return -1; 130 if (sm->state == &terminated) 131 return 1; 132 133 FOR_EACH_PTR(sm->possible, tmp) { 134 if (tmp->state == &unterminated) 135 return 0; 136 } END_FOR_EACH_PTR(tmp); 137 138 return -1; 139 } 140 141 static void struct_member_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm) 142 { 143 int term; 144 145 term = sm_to_term(sm); 146 if (term < 0) 147 return; 148 149 sql_insert_caller_info(call, TERMINATED, param, printed_name, term ? "1" : "0"); 150 } 151 152 static void match_call_info(struct expression *expr) 153 { 154 struct smatch_state *state; 155 struct expression *arg; 156 int i; 157 158 i = -1; 159 FOR_EACH_PTR(expr->args, arg) { 160 i++; 161 162 state = get_terminated_state(arg); 163 if (!state) 164 continue; 165 sql_insert_caller_info(expr, TERMINATED, i, "$", 166 (state == &terminated) ? "1" : "0"); 167 } END_FOR_EACH_PTR(arg); 168 } 169 170 static void caller_info_terminated(const char *name, struct symbol *sym, char *key, char *value) 171 { 172 char fullname[256]; 173 174 if (strcmp(key, "*$") == 0) 175 snprintf(fullname, sizeof(fullname), "*%s", name); 176 else if (strncmp(key, "$", 1) == 0) 177 snprintf(fullname, 256, "%s%s", name, key + 1); 178 else 179 return; 180 181 set_state(my_id, fullname, sym, (*value == '1') ? &terminated : &unterminated); 182 } 183 184 static void split_return_info(int return_id, char *return_ranges, struct expression *expr) 185 { 186 struct symbol *returned_sym; 187 struct sm_state *tmp, *sm; 188 const char *param_name; 189 int param; 190 int term; 191 192 FOR_EACH_MY_SM(param_set_id, __get_cur_stree(), tmp) { 193 sm = get_sm_state(my_id, tmp->name, tmp->sym); 194 if (!sm) 195 continue; 196 term = sm_to_term(sm); 197 if (term < 0) 198 continue; 199 param = get_param_num_from_sym(tmp->sym); 200 if (param < 0) 201 continue; 202 203 param_name = get_param_name(sm); 204 if (!param_name) 205 continue; 206 if (strcmp(param_name, "$") == 0) 207 continue; 208 209 sql_insert_return_states(return_id, return_ranges, TERMINATED, 210 param, param_name, term ? "1" : "0"); 211 } END_FOR_EACH_SM(tmp); 212 213 returned_sym = expr_to_sym(expr); 214 if (!returned_sym) 215 return; 216 217 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { 218 if (sm->sym != returned_sym) 219 continue; 220 term = sm_to_term(sm); 221 if (term < 0) 222 continue; 223 param_name = get_param_name(sm); 224 if (!param_name) 225 continue; 226 sql_insert_return_states(return_id, return_ranges, TERMINATED, 227 -1, param_name, term ? "1" : "0"); 228 } END_FOR_EACH_SM(sm); 229 } 230 231 static void return_info_terminated(struct expression *expr, int param, char *key, char *value) 232 { 233 struct expression *arg; 234 char *name; 235 struct symbol *sym; 236 237 if (param == -1) { 238 arg = expr->left; 239 } else { 240 struct expression *call = expr; 241 242 while (call->type == EXPR_ASSIGNMENT) 243 call = strip_expr(call->right); 244 if (call->type != EXPR_CALL) 245 return; 246 247 arg = get_argument_from_call_expr(call->args, param); 248 if (!arg) 249 return; 250 } 251 252 name = get_variable_from_key(arg, key, &sym); 253 if (!name || !sym) 254 goto free; 255 256 set_terminated_var_sym(name, sym, (*value == '1') ? &terminated : &unterminated); 257 free: 258 free_string(name); 259 } 260 261 bool is_nul_terminated_var_sym(const char *name, struct symbol *sym) 262 { 263 if (get_terminated_state_var_sym(name, sym) == &terminated) 264 return 1; 265 return 0; 266 } 267 268 bool is_nul_terminated(struct expression *expr) 269 { 270 if (get_terminated_state(expr) == &terminated) 271 return 1; 272 return 0; 273 } 274 275 static void match_strnlen_test(struct expression *expr) 276 { 277 struct expression *left, *tmp, *arg; 278 int cnt; 279 280 if (expr->type != EXPR_COMPARE) 281 return; 282 if (expr->op != SPECIAL_EQUAL && expr->op != SPECIAL_NOTEQUAL) 283 return; 284 285 left = strip_expr(expr->left); 286 cnt = 0; 287 while ((tmp = get_assigned_expr(left))) { 288 if (cnt++ > 3) 289 break; 290 left = tmp; 291 } 292 293 if (left->type != EXPR_CALL) 294 return; 295 if (!sym_name_is("strnlen", left->fn)) 296 return; 297 arg = get_argument_from_call_expr(left->args, 0); 298 set_true_false_states_expr(my_id, arg, 299 (expr->op == SPECIAL_EQUAL) ? &terminated : NULL, 300 (expr->op == SPECIAL_NOTEQUAL) ? &terminated : NULL); 301 if (get_param_num(arg) >= 0) 302 set_true_false_states_expr(param_set_id, arg, 303 (expr->op == SPECIAL_EQUAL) ? &terminated : NULL, 304 (expr->op == SPECIAL_NOTEQUAL) ? &terminated : NULL); 305 } 306 307 void register_nul_terminator(int id) 308 { 309 my_id = id; 310 311 add_hook(&match_nul_assign, ASSIGNMENT_HOOK); 312 add_hook(&match_string_assign, ASSIGNMENT_HOOK); 313 314 add_hook(&match_call_info, FUNCTION_CALL_HOOK); 315 add_member_info_callback(my_id, struct_member_callback); 316 add_split_return_callback(&split_return_info); 317 318 select_caller_info_hook(caller_info_terminated, TERMINATED); 319 select_return_states_hook(TERMINATED, return_info_terminated); 320 321 add_hook(&match_strnlen_test, CONDITION_HOOK); 322 } 323 324 void register_nul_terminator_param_set(int id) 325 { 326 param_set_id = id; 327 }