11f5207b7SJohn Levon /* 21f5207b7SJohn Levon * Copyright (C) 2012 Oracle. 31f5207b7SJohn Levon * 41f5207b7SJohn Levon * This program is free software; you can redistribute it and/or 51f5207b7SJohn Levon * modify it under the terms of the GNU General Public License 61f5207b7SJohn Levon * as published by the Free Software Foundation; either version 2 71f5207b7SJohn Levon * of the License, or (at your option) any later version. 81f5207b7SJohn Levon * 91f5207b7SJohn Levon * This program is distributed in the hope that it will be useful, 101f5207b7SJohn Levon * but WITHOUT ANY WARRANTY; without even the implied warranty of 111f5207b7SJohn Levon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 121f5207b7SJohn Levon * GNU General Public License for more details. 131f5207b7SJohn Levon * 141f5207b7SJohn Levon * You should have received a copy of the GNU General Public License 151f5207b7SJohn Levon * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt 161f5207b7SJohn Levon */ 171f5207b7SJohn Levon 181f5207b7SJohn Levon /* 191f5207b7SJohn Levon * This is for functions like: 201f5207b7SJohn Levon * 211f5207b7SJohn Levon * int foo(int *x) 221f5207b7SJohn Levon * { 231f5207b7SJohn Levon * if (*x == 42) { 241f5207b7SJohn Levon * *x = 0; 251f5207b7SJohn Levon * return 1; 261f5207b7SJohn Levon * } 271f5207b7SJohn Levon * return 0; 281f5207b7SJohn Levon * } 291f5207b7SJohn Levon * 301f5207b7SJohn Levon * If we return 1 that means the value of *x has been set to 0. If we return 311f5207b7SJohn Levon * 0 then we have left *x alone. 321f5207b7SJohn Levon * 331f5207b7SJohn Levon */ 341f5207b7SJohn Levon 351f5207b7SJohn Levon #include "scope.h" 361f5207b7SJohn Levon #include "smatch.h" 371f5207b7SJohn Levon #include "smatch_slist.h" 381f5207b7SJohn Levon #include "smatch_extra.h" 391f5207b7SJohn Levon 401f5207b7SJohn Levon static int my_id; 411f5207b7SJohn Levon 421f5207b7SJohn Levon static struct smatch_state *unmatched_state(struct sm_state *sm) 431f5207b7SJohn Levon { 441f5207b7SJohn Levon return alloc_estate_empty(); 451f5207b7SJohn Levon } 461f5207b7SJohn Levon 471f5207b7SJohn Levon static int parent_is_set(const char *name, struct symbol *sym, struct smatch_state *state) 481f5207b7SJohn Levon { 491f5207b7SJohn Levon struct expression *faked; 501f5207b7SJohn Levon char *left_name; 511f5207b7SJohn Levon int ret = 0; 521f5207b7SJohn Levon int len; 531f5207b7SJohn Levon 541f5207b7SJohn Levon if (!__in_fake_assign) 551f5207b7SJohn Levon return 0; 561f5207b7SJohn Levon if (!is_whole_rl(estate_rl(state))) 571f5207b7SJohn Levon return 0; 581f5207b7SJohn Levon if (get_state(my_id, name, sym)) 591f5207b7SJohn Levon return 0; 601f5207b7SJohn Levon 611f5207b7SJohn Levon faked = get_faked_expression(); 621f5207b7SJohn Levon if (!faked) 631f5207b7SJohn Levon return 0; 641f5207b7SJohn Levon if ((faked->type == EXPR_PREOP || faked->type == EXPR_POSTOP) && 651f5207b7SJohn Levon (faked->op == SPECIAL_INCREMENT || faked->op == SPECIAL_DECREMENT)) { 661f5207b7SJohn Levon faked = strip_expr(faked->unop); 671f5207b7SJohn Levon if (faked->type == EXPR_SYMBOL) 681f5207b7SJohn Levon return 1; 691f5207b7SJohn Levon return 0; 701f5207b7SJohn Levon } 711f5207b7SJohn Levon if (faked->type != EXPR_ASSIGNMENT) 721f5207b7SJohn Levon return 0; 731f5207b7SJohn Levon 741f5207b7SJohn Levon left_name = expr_to_var(faked->left); 751f5207b7SJohn Levon if (!left_name) 761f5207b7SJohn Levon return 0; 771f5207b7SJohn Levon 781f5207b7SJohn Levon len = strlen(left_name); 791f5207b7SJohn Levon if (strncmp(name, left_name, len) == 0 && name[len] == '-') 801f5207b7SJohn Levon ret = 1; 811f5207b7SJohn Levon free_string(left_name); 821f5207b7SJohn Levon 831f5207b7SJohn Levon return ret; 841f5207b7SJohn Levon } 851f5207b7SJohn Levon 861f5207b7SJohn Levon static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state) 871f5207b7SJohn Levon { 881f5207b7SJohn Levon if (parent_is_set(name, sym, state)) 891f5207b7SJohn Levon return; 901f5207b7SJohn Levon if (get_param_num_from_sym(sym) < 0) 911f5207b7SJohn Levon return; 921f5207b7SJohn Levon set_state(my_id, name, sym, state); 931f5207b7SJohn Levon } 941f5207b7SJohn Levon 951f5207b7SJohn Levon /* 961f5207b7SJohn Levon * This function is is a dirty hack because extra_mod_hook is giving us a NULL 971f5207b7SJohn Levon * sym instead of a vsl. 981f5207b7SJohn Levon */ 991f5207b7SJohn Levon static void match_array_assignment(struct expression *expr) 1001f5207b7SJohn Levon { 1011f5207b7SJohn Levon struct expression *array, *offset; 1021f5207b7SJohn Levon char *name; 1031f5207b7SJohn Levon struct symbol *sym; 1041f5207b7SJohn Levon struct range_list *rl; 1051f5207b7SJohn Levon sval_t sval; 1061f5207b7SJohn Levon char buf[256]; 1071f5207b7SJohn Levon 1081f5207b7SJohn Levon if (__in_fake_assign) 1091f5207b7SJohn Levon return; 1101f5207b7SJohn Levon 1111f5207b7SJohn Levon if (!is_array(expr->left)) 1121f5207b7SJohn Levon return; 1131f5207b7SJohn Levon array = get_array_base(expr->left); 1141f5207b7SJohn Levon offset = get_array_offset(expr->left); 1151f5207b7SJohn Levon 1161f5207b7SJohn Levon /* These are handled by extra_mod_hook() */ 1171f5207b7SJohn Levon if (get_value(offset, &sval)) 1181f5207b7SJohn Levon return; 1191f5207b7SJohn Levon name = expr_to_var_sym(array, &sym); 1201f5207b7SJohn Levon if (!name || !sym) 1211f5207b7SJohn Levon goto free; 1221f5207b7SJohn Levon if (get_param_num_from_sym(sym) < 0) 1231f5207b7SJohn Levon goto free; 1241f5207b7SJohn Levon get_absolute_rl(expr->right, &rl); 1251f5207b7SJohn Levon rl = cast_rl(get_type(expr->left), rl); 1261f5207b7SJohn Levon 1271f5207b7SJohn Levon snprintf(buf, sizeof(buf), "*%s", name); 1281f5207b7SJohn Levon set_state(my_id, buf, sym, alloc_estate_rl(rl)); 1291f5207b7SJohn Levon free: 1301f5207b7SJohn Levon free_string(name); 1311f5207b7SJohn Levon } 1321f5207b7SJohn Levon 1331f5207b7SJohn Levon /* 1341f5207b7SJohn Levon * This relies on the fact that these states are stored so that 1351f5207b7SJohn Levon * foo->bar is before foo->bar->baz. 1361f5207b7SJohn Levon */ 1371f5207b7SJohn Levon static int parent_set(struct string_list *list, const char *name) 1381f5207b7SJohn Levon { 1391f5207b7SJohn Levon char *tmp; 1401f5207b7SJohn Levon int len; 1411f5207b7SJohn Levon int ret; 1421f5207b7SJohn Levon 1431f5207b7SJohn Levon FOR_EACH_PTR(list, tmp) { 1441f5207b7SJohn Levon len = strlen(tmp); 1451f5207b7SJohn Levon ret = strncmp(tmp, name, len); 1461f5207b7SJohn Levon if (ret < 0) 1471f5207b7SJohn Levon continue; 1481f5207b7SJohn Levon if (ret > 0) 1491f5207b7SJohn Levon return 0; 1501f5207b7SJohn Levon if (name[len] == '-') 1511f5207b7SJohn Levon return 1; 1521f5207b7SJohn Levon } END_FOR_EACH_PTR(tmp); 1531f5207b7SJohn Levon 1541f5207b7SJohn Levon return 0; 1551f5207b7SJohn Levon } 1561f5207b7SJohn Levon 1571f5207b7SJohn Levon static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr) 1581f5207b7SJohn Levon { 1591f5207b7SJohn Levon struct sm_state *sm; 1601f5207b7SJohn Levon struct smatch_state *extra; 1611f5207b7SJohn Levon int param; 1621f5207b7SJohn Levon struct range_list *rl; 1631f5207b7SJohn Levon const char *param_name; 1641f5207b7SJohn Levon struct string_list *set_list = NULL; 1651f5207b7SJohn Levon char *math_str; 1661f5207b7SJohn Levon char buf[256]; 1671f5207b7SJohn Levon 1681f5207b7SJohn Levon FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { 1691f5207b7SJohn Levon if (!estate_rl(sm->state)) 1701f5207b7SJohn Levon continue; 1711f5207b7SJohn Levon extra = get_state(SMATCH_EXTRA, sm->name, sm->sym); 1721f5207b7SJohn Levon if (extra) { 1731f5207b7SJohn Levon rl = rl_intersection(estate_rl(sm->state), estate_rl(extra)); 1741f5207b7SJohn Levon if (!rl) 1751f5207b7SJohn Levon continue; 1761f5207b7SJohn Levon } else { 1771f5207b7SJohn Levon rl = estate_rl(sm->state); 1781f5207b7SJohn Levon } 1791f5207b7SJohn Levon 1801f5207b7SJohn Levon param = get_param_num_from_sym(sm->sym); 1811f5207b7SJohn Levon if (param < 0) 1821f5207b7SJohn Levon continue; 1831f5207b7SJohn Levon param_name = get_param_name(sm); 1841f5207b7SJohn Levon if (!param_name) 1851f5207b7SJohn Levon continue; 1861f5207b7SJohn Levon if (strcmp(param_name, "$") == 0) { 1871f5207b7SJohn Levon insert_string(&set_list, (char *)sm->name); 1881f5207b7SJohn Levon continue; 1891f5207b7SJohn Levon } 190*efe51d0cSJohn Levon if (is_recursive_member(param_name)) { 191*efe51d0cSJohn Levon insert_string(&set_list, (char *)sm->name); 192*efe51d0cSJohn Levon continue; 193*efe51d0cSJohn Levon } 1941f5207b7SJohn Levon 195*efe51d0cSJohn Levon if (is_ignored_kernel_data(param_name)) { 1961f5207b7SJohn Levon insert_string(&set_list, (char *)sm->name); 1971f5207b7SJohn Levon continue; 1981f5207b7SJohn Levon } 1991f5207b7SJohn Levon 2001f5207b7SJohn Levon math_str = get_value_in_terms_of_parameter_math_var_sym(sm->name, sm->sym); 2011f5207b7SJohn Levon if (math_str) { 2021f5207b7SJohn Levon snprintf(buf, sizeof(buf), "%s[%s]", show_rl(rl), math_str); 2031f5207b7SJohn Levon insert_string(&set_list, (char *)sm->name); 2041f5207b7SJohn Levon sql_insert_return_states(return_id, return_ranges, 2051f5207b7SJohn Levon param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET, 2061f5207b7SJohn Levon param, param_name, buf); 2071f5207b7SJohn Levon continue; 2081f5207b7SJohn Levon } 2091f5207b7SJohn Levon 2101f5207b7SJohn Levon /* no useful information here. */ 2111f5207b7SJohn Levon if (is_whole_rl(rl) && parent_set(set_list, sm->name)) 2121f5207b7SJohn Levon continue; 2131f5207b7SJohn Levon insert_string(&set_list, (char *)sm->name); 2141f5207b7SJohn Levon 2151f5207b7SJohn Levon sql_insert_return_states(return_id, return_ranges, 2161f5207b7SJohn Levon param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET, 2171f5207b7SJohn Levon param, param_name, show_rl(rl)); 2181f5207b7SJohn Levon 2191f5207b7SJohn Levon } END_FOR_EACH_SM(sm); 2201f5207b7SJohn Levon 2211f5207b7SJohn Levon free_ptr_list((struct ptr_list **)&set_list); 2221f5207b7SJohn Levon } 2231f5207b7SJohn Levon 2241f5207b7SJohn Levon int param_was_set_var_sym(const char *name, struct symbol *sym) 2251f5207b7SJohn Levon { 2261f5207b7SJohn Levon struct sm_state *sm; 2271f5207b7SJohn Levon int len; 2281f5207b7SJohn Levon 2291f5207b7SJohn Levon FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { 2301f5207b7SJohn Levon if (sm->sym != sym) 2311f5207b7SJohn Levon continue; 2321f5207b7SJohn Levon len = strlen(sm->name); 2331f5207b7SJohn Levon if (strncmp(sm->name, name, len) != 0) 2341f5207b7SJohn Levon continue; 2351f5207b7SJohn Levon if (name[len] == '\0' || 2361f5207b7SJohn Levon name[len] == '-') 2371f5207b7SJohn Levon return 1; 2381f5207b7SJohn Levon } END_FOR_EACH_SM(sm); 2391f5207b7SJohn Levon 2401f5207b7SJohn Levon return 0; 2411f5207b7SJohn Levon } 2421f5207b7SJohn Levon 2431f5207b7SJohn Levon int param_was_set(struct expression *expr) 2441f5207b7SJohn Levon { 2451f5207b7SJohn Levon char *name; 2461f5207b7SJohn Levon struct symbol *sym; 2471f5207b7SJohn Levon int ret = 0; 2481f5207b7SJohn Levon 2491f5207b7SJohn Levon name = expr_to_var_sym(expr, &sym); 2501f5207b7SJohn Levon if (!name || !sym) 2511f5207b7SJohn Levon goto free; 2521f5207b7SJohn Levon 2531f5207b7SJohn Levon ret = param_was_set_var_sym(name, sym); 2541f5207b7SJohn Levon free: 2551f5207b7SJohn Levon free_string(name); 2561f5207b7SJohn Levon return ret; 2571f5207b7SJohn Levon } 2581f5207b7SJohn Levon 2591f5207b7SJohn Levon void register_param_set(int id) 2601f5207b7SJohn Levon { 2611f5207b7SJohn Levon my_id = id; 2621f5207b7SJohn Levon 263*efe51d0cSJohn Levon set_dynamic_states(my_id); 2641f5207b7SJohn Levon add_extra_mod_hook(&extra_mod_hook); 2651f5207b7SJohn Levon add_hook(match_array_assignment, ASSIGNMENT_HOOK); 2661f5207b7SJohn Levon add_unmatched_state_hook(my_id, &unmatched_state); 2671f5207b7SJohn Levon add_merge_hook(my_id, &merge_estates); 2681f5207b7SJohn Levon add_split_return_callback(&print_return_value_param); 2691f5207b7SJohn Levon } 2701f5207b7SJohn Levon 271