1 /* 2 * Copyright (C) 2012 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 /* 19 * This is for functions like: 20 * 21 * void foo(int *x) 22 * { 23 * if (*x == 42) 24 * *x = 0; 25 * } 26 * 27 * The final value of *x depends on the input to the function but with *x == 42 28 * filtered out. 29 * 30 */ 31 32 #include "smatch.h" 33 #include "smatch_extra.h" 34 #include "smatch_slist.h" 35 36 static int my_id; 37 38 static struct stree *start_states; 39 static struct stree_stack *saved_stack; 40 static void save_start_states(struct statement *stmt) 41 { 42 start_states = get_all_states_stree(SMATCH_EXTRA); 43 } 44 45 static void free_start_states(void) 46 { 47 free_stree(&start_states); 48 } 49 50 static void match_save_states(struct expression *expr) 51 { 52 push_stree(&saved_stack, start_states); 53 start_states = NULL; 54 } 55 56 static void match_restore_states(struct expression *expr) 57 { 58 free_stree(&start_states); 59 start_states = pop_stree(&saved_stack); 60 } 61 62 static struct smatch_state *unmatched_state(struct sm_state *sm) 63 { 64 struct smatch_state *state; 65 66 if (parent_is_gone_var_sym(sm->name, sm->sym)) 67 return alloc_estate_empty(); 68 69 state = __get_state(SMATCH_EXTRA, sm->name, sm->sym); 70 if (state) 71 return state; 72 return alloc_estate_whole(estate_type(sm->state)); 73 } 74 75 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other) 76 { 77 struct smatch_state *extra; 78 struct range_list *rl; 79 80 if (estate_rl(other->state)) 81 return; 82 83 extra = get_state(SMATCH_EXTRA, cur->name, cur->sym); 84 if (!extra) 85 return; 86 87 rl = rl_intersection(estate_rl(extra), estate_rl(cur->state)); 88 if (rl_equiv(rl, estate_rl(cur->state))) 89 return; 90 set_state(my_id, cur->name, cur->sym, alloc_estate_rl(clone_rl(rl))); 91 } 92 93 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state) 94 { 95 int param; 96 97 if (__in_fake_assign) 98 return; 99 100 param = get_param_num_from_sym(sym); 101 if (param < 0) 102 return; 103 104 /* on stack parameters are handled in smatch_param_limit.c */ 105 if (sym->ident && strcmp(sym->ident->name, name) == 0) 106 return; 107 108 set_state(my_id, name, sym, alloc_estate_empty()); 109 } 110 111 /* 112 * This relies on the fact that these states are stored so that 113 * foo->bar is before foo->bar->baz. 114 */ 115 static int parent_set(struct string_list *list, const char *name) 116 { 117 char *tmp; 118 int len; 119 int ret; 120 121 FOR_EACH_PTR(list, tmp) { 122 len = strlen(tmp); 123 ret = strncmp(tmp, name, len); 124 if (ret < 0) 125 continue; 126 if (ret > 0) 127 return 0; 128 if (name[len] == '-') 129 return 1; 130 } END_FOR_EACH_PTR(tmp); 131 132 return 0; 133 } 134 135 static void print_one_mod_param(int return_id, char *return_ranges, 136 int param, struct sm_state *sm, struct string_list **totally_filtered) 137 { 138 const char *param_name; 139 140 param_name = get_param_name(sm); 141 if (!param_name) 142 return; 143 if (is_whole_rl(estate_rl(sm->state))) 144 return; 145 if (!estate_rl(sm->state)) { 146 insert_string(totally_filtered, (char *)sm->name); 147 return; 148 } 149 150 if (is_ignored_kernel_data(param_name)) { 151 insert_string(totally_filtered, (char *)sm->name); 152 return; 153 } 154 155 sql_insert_return_states(return_id, return_ranges, PARAM_FILTER, param, 156 param_name, show_rl(estate_rl(sm->state))); 157 } 158 159 static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr) 160 { 161 struct sm_state *tmp; 162 struct sm_state *sm; 163 struct string_list *totally_filtered = NULL; 164 int param; 165 166 FOR_EACH_MY_SM(SMATCH_EXTRA, __get_cur_stree(), tmp) { 167 param = get_param_num_from_sym(tmp->sym); 168 if (param < 0) 169 continue; 170 171 /* on stack parameters are handled in smatch_param_limit.c */ 172 if (tmp->sym->ident && strcmp(tmp->sym->ident->name, tmp->name) == 0) 173 continue; 174 175 if (parent_set(totally_filtered, tmp->name)) 176 continue; 177 178 sm = get_sm_state(my_id, tmp->name, tmp->sym); 179 if (sm) 180 print_one_mod_param(return_id, return_ranges, param, sm, &totally_filtered); 181 } END_FOR_EACH_SM(tmp); 182 183 free_ptr_list((struct ptr_list **)&totally_filtered); 184 } 185 186 int param_has_filter_data(struct sm_state *sm) 187 { 188 struct smatch_state *state; 189 190 state = get_state(my_id, sm->name, sm->sym); 191 if (!state) { 192 if (get_assigned_expr_name_sym(sm->name, sm->sym)) 193 return 0; 194 return 1; 195 } 196 if (estate_rl(state)) 197 return 1; 198 return 0; 199 } 200 201 void register_param_filter(int id) 202 { 203 my_id = id; 204 205 set_dynamic_states(my_id); 206 add_hook(&save_start_states, AFTER_DEF_HOOK); 207 add_hook(&free_start_states, AFTER_FUNC_HOOK); 208 209 add_extra_mod_hook(&extra_mod_hook); 210 add_unmatched_state_hook(my_id, &unmatched_state); 211 add_pre_merge_hook(my_id, &pre_merge_hook); 212 add_merge_hook(my_id, &merge_estates); 213 214 add_hook(&match_save_states, INLINE_FN_START); 215 add_hook(&match_restore_states, INLINE_FN_END); 216 217 add_split_return_callback(&print_return_value_param); 218 } 219 220