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 }