1*1f5207b7SJohn Levon /* 2*1f5207b7SJohn Levon * Copyright (C) 2017 Oracle. 3*1f5207b7SJohn Levon * 4*1f5207b7SJohn Levon * This program is free software; you can redistribute it and/or 5*1f5207b7SJohn Levon * modify it under the terms of the GNU General Public License 6*1f5207b7SJohn Levon * as published by the Free Software Foundation; either version 2 7*1f5207b7SJohn Levon * of the License, or (at your option) any later version. 8*1f5207b7SJohn Levon * 9*1f5207b7SJohn Levon * This program is distributed in the hope that it will be useful, 10*1f5207b7SJohn Levon * but WITHOUT ANY WARRANTY; without even the implied warranty of 11*1f5207b7SJohn Levon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12*1f5207b7SJohn Levon * GNU General Public License for more details. 13*1f5207b7SJohn Levon * 14*1f5207b7SJohn Levon * You should have received a copy of the GNU General Public License 15*1f5207b7SJohn Levon * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt 16*1f5207b7SJohn Levon */ 17*1f5207b7SJohn Levon 18*1f5207b7SJohn Levon /* 19*1f5207b7SJohn Levon * This is for smatch_extra.c to use. It sort of like check_assigned_expr.c but 20*1f5207b7SJohn Levon * more limited. Say a function returns "64min-s64max[$0->data]" and the caller 21*1f5207b7SJohn Levon * does "struct whatever *p = get_data(dev);" then we want to record that p is 22*1f5207b7SJohn Levon * now the same as "dev->data". Then if we update "p->foo" it means we can 23*1f5207b7SJohn Levon * update "dev->data->foo" as well. 24*1f5207b7SJohn Levon * 25*1f5207b7SJohn Levon */ 26*1f5207b7SJohn Levon 27*1f5207b7SJohn Levon #include "smatch.h" 28*1f5207b7SJohn Levon #include "smatch_slist.h" 29*1f5207b7SJohn Levon #include "smatch_extra.h" 30*1f5207b7SJohn Levon 31*1f5207b7SJohn Levon extern int check_assigned_expr_id; 32*1f5207b7SJohn Levon static int my_id; 33*1f5207b7SJohn Levon static int link_id; 34*1f5207b7SJohn Levon 35*1f5207b7SJohn Levon static struct smatch_state *alloc_my_state(const char *name, struct symbol *sym) 36*1f5207b7SJohn Levon { 37*1f5207b7SJohn Levon struct smatch_state *state; 38*1f5207b7SJohn Levon 39*1f5207b7SJohn Levon state = __alloc_smatch_state(0); 40*1f5207b7SJohn Levon state->name = alloc_sname(name); 41*1f5207b7SJohn Levon state->data = sym; 42*1f5207b7SJohn Levon return state; 43*1f5207b7SJohn Levon } 44*1f5207b7SJohn Levon 45*1f5207b7SJohn Levon static void undef(struct sm_state *sm, struct expression *mod_expr) 46*1f5207b7SJohn Levon { 47*1f5207b7SJohn Levon if (__in_fake_parameter_assign) 48*1f5207b7SJohn Levon return; 49*1f5207b7SJohn Levon set_state(my_id, sm->name, sm->sym, &undefined); 50*1f5207b7SJohn Levon } 51*1f5207b7SJohn Levon 52*1f5207b7SJohn Levon char *map_call_to_other_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym) 53*1f5207b7SJohn Levon { 54*1f5207b7SJohn Levon struct smatch_state *state; 55*1f5207b7SJohn Levon int skip; 56*1f5207b7SJohn Levon char buf[256]; 57*1f5207b7SJohn Levon 58*1f5207b7SJohn Levon /* skip 'foo->'. This was checked in the caller. */ 59*1f5207b7SJohn Levon skip = strlen(sym->ident->name) + 2; 60*1f5207b7SJohn Levon 61*1f5207b7SJohn Levon state = get_state(my_id, sym->ident->name, sym); 62*1f5207b7SJohn Levon if (!state || !state->data) 63*1f5207b7SJohn Levon return NULL; 64*1f5207b7SJohn Levon 65*1f5207b7SJohn Levon snprintf(buf, sizeof(buf), "%s->%s", state->name, name + skip); 66*1f5207b7SJohn Levon *new_sym = state->data; 67*1f5207b7SJohn Levon return alloc_string(buf); 68*1f5207b7SJohn Levon } 69*1f5207b7SJohn Levon 70*1f5207b7SJohn Levon static char *map_my_state_long_to_short(struct sm_state *sm, const char *name, struct symbol *sym, struct symbol **new_sym, bool stack) 71*1f5207b7SJohn Levon { 72*1f5207b7SJohn Levon int len; 73*1f5207b7SJohn Levon char buf[256]; 74*1f5207b7SJohn Levon 75*1f5207b7SJohn Levon if (sm->state->data != sym) 76*1f5207b7SJohn Levon return NULL; 77*1f5207b7SJohn Levon len = strlen(sm->state->name); 78*1f5207b7SJohn Levon if (strncmp(name, sm->state->name, len) != 0) 79*1f5207b7SJohn Levon return NULL; 80*1f5207b7SJohn Levon 81*1f5207b7SJohn Levon if (name[len] == '.') 82*1f5207b7SJohn Levon return NULL; 83*1f5207b7SJohn Levon if (!stack && name[len] != '-') 84*1f5207b7SJohn Levon return NULL; 85*1f5207b7SJohn Levon snprintf(buf, sizeof(buf), "%s%s", sm->name, name + len); 86*1f5207b7SJohn Levon *new_sym = sm->sym; 87*1f5207b7SJohn Levon return alloc_string(buf); 88*1f5207b7SJohn Levon } 89*1f5207b7SJohn Levon 90*1f5207b7SJohn Levon static char *map_assignment_long_to_short(struct sm_state *sm, const char *name, struct symbol *sym, struct symbol **new_sym, bool stack) 91*1f5207b7SJohn Levon { 92*1f5207b7SJohn Levon struct expression *orig_expr; 93*1f5207b7SJohn Levon struct symbol *orig_sym; 94*1f5207b7SJohn Levon int len; 95*1f5207b7SJohn Levon char buf[256]; 96*1f5207b7SJohn Levon 97*1f5207b7SJohn Levon orig_expr = sm->state->data; 98*1f5207b7SJohn Levon if (!orig_expr) 99*1f5207b7SJohn Levon return NULL; 100*1f5207b7SJohn Levon 101*1f5207b7SJohn Levon /* 102*1f5207b7SJohn Levon * Say we have an assignment like: 103*1f5207b7SJohn Levon * foo->bar->my_ptr = my_ptr; 104*1f5207b7SJohn Levon * We still expect the function to carry on using "my_ptr" as the 105*1f5207b7SJohn Levon * shorter name. That's not a long to short mapping. 106*1f5207b7SJohn Levon * 107*1f5207b7SJohn Levon */ 108*1f5207b7SJohn Levon if (orig_expr->type == EXPR_SYMBOL) 109*1f5207b7SJohn Levon return NULL; 110*1f5207b7SJohn Levon 111*1f5207b7SJohn Levon orig_sym = expr_to_sym(orig_expr); 112*1f5207b7SJohn Levon if (!orig_sym) 113*1f5207b7SJohn Levon return NULL; 114*1f5207b7SJohn Levon if (sym != orig_sym) 115*1f5207b7SJohn Levon return NULL; 116*1f5207b7SJohn Levon 117*1f5207b7SJohn Levon len = strlen(sm->state->name); 118*1f5207b7SJohn Levon if (strncmp(name, sm->state->name, len) != 0) 119*1f5207b7SJohn Levon return NULL; 120*1f5207b7SJohn Levon 121*1f5207b7SJohn Levon if (name[len] == '.') 122*1f5207b7SJohn Levon return NULL; 123*1f5207b7SJohn Levon if (!stack && name[len] != '-') 124*1f5207b7SJohn Levon return NULL; 125*1f5207b7SJohn Levon snprintf(buf, sizeof(buf), "%s%s", sm->name, name + len); 126*1f5207b7SJohn Levon *new_sym = sm->sym; 127*1f5207b7SJohn Levon return alloc_string(buf); 128*1f5207b7SJohn Levon } 129*1f5207b7SJohn Levon 130*1f5207b7SJohn Levon /* 131*1f5207b7SJohn Levon * Normally, we expect people to consistently refer to variables by the shortest 132*1f5207b7SJohn Levon * name. So they use "b->a" instead of "foo->bar.a" when both point to the 133*1f5207b7SJohn Levon * same memory location. However, when we're dealing across function boundaries 134*1f5207b7SJohn Levon * then sometimes we pass frob(foo) which sets foo->bar.a. In that case, we 135*1f5207b7SJohn Levon * translate it to the shorter name. Smatch extra updates the shorter name, 136*1f5207b7SJohn Levon * which in turn updates the longer name. 137*1f5207b7SJohn Levon * 138*1f5207b7SJohn Levon */ 139*1f5207b7SJohn Levon static char *map_long_to_short_name_sym_helper(const char *name, struct symbol *sym, struct symbol **new_sym, bool stack) 140*1f5207b7SJohn Levon { 141*1f5207b7SJohn Levon char *ret; 142*1f5207b7SJohn Levon struct sm_state *sm; 143*1f5207b7SJohn Levon 144*1f5207b7SJohn Levon *new_sym = NULL; 145*1f5207b7SJohn Levon 146*1f5207b7SJohn Levon FOR_EACH_SM(__get_cur_stree(), sm) { 147*1f5207b7SJohn Levon if (sm->owner == my_id) { 148*1f5207b7SJohn Levon ret = map_my_state_long_to_short(sm, name, sym, new_sym, stack); 149*1f5207b7SJohn Levon if (ret) 150*1f5207b7SJohn Levon return ret; 151*1f5207b7SJohn Levon continue; 152*1f5207b7SJohn Levon } 153*1f5207b7SJohn Levon if (sm->owner == check_assigned_expr_id) { 154*1f5207b7SJohn Levon ret = map_assignment_long_to_short(sm, name, sym, new_sym, stack); 155*1f5207b7SJohn Levon if (ret) 156*1f5207b7SJohn Levon return ret; 157*1f5207b7SJohn Levon continue; 158*1f5207b7SJohn Levon } 159*1f5207b7SJohn Levon } END_FOR_EACH_SM(sm); 160*1f5207b7SJohn Levon 161*1f5207b7SJohn Levon return NULL; 162*1f5207b7SJohn Levon } 163*1f5207b7SJohn Levon 164*1f5207b7SJohn Levon char *map_long_to_short_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym) 165*1f5207b7SJohn Levon { 166*1f5207b7SJohn Levon return map_long_to_short_name_sym_helper(name, sym, new_sym, 1); 167*1f5207b7SJohn Levon } 168*1f5207b7SJohn Levon 169*1f5207b7SJohn Levon char *map_long_to_short_name_sym_nostack(const char *name, struct symbol *sym, struct symbol **new_sym) 170*1f5207b7SJohn Levon { 171*1f5207b7SJohn Levon return map_long_to_short_name_sym_helper(name, sym, new_sym, 0); 172*1f5207b7SJohn Levon } 173*1f5207b7SJohn Levon 174*1f5207b7SJohn Levon char *map_call_to_param_name_sym(struct expression *expr, struct symbol **sym) 175*1f5207b7SJohn Levon { 176*1f5207b7SJohn Levon char *name; 177*1f5207b7SJohn Levon struct symbol *start_sym; 178*1f5207b7SJohn Levon struct smatch_state *state; 179*1f5207b7SJohn Levon 180*1f5207b7SJohn Levon *sym = NULL; 181*1f5207b7SJohn Levon 182*1f5207b7SJohn Levon name = expr_to_str_sym(expr, &start_sym); 183*1f5207b7SJohn Levon if (!name) 184*1f5207b7SJohn Levon return NULL; 185*1f5207b7SJohn Levon if (expr->type == EXPR_CALL) 186*1f5207b7SJohn Levon start_sym = expr_to_sym(expr->fn); 187*1f5207b7SJohn Levon 188*1f5207b7SJohn Levon state = get_state(my_id, name, start_sym); 189*1f5207b7SJohn Levon free_string(name); 190*1f5207b7SJohn Levon if (!state || !state->data) 191*1f5207b7SJohn Levon return NULL; 192*1f5207b7SJohn Levon 193*1f5207b7SJohn Levon *sym = state->data; 194*1f5207b7SJohn Levon return alloc_string(state->name); 195*1f5207b7SJohn Levon } 196*1f5207b7SJohn Levon 197*1f5207b7SJohn Levon static void store_mapping_helper(char *left_name, struct symbol *left_sym, struct expression *call, const char *return_string) 198*1f5207b7SJohn Levon { 199*1f5207b7SJohn Levon const char *p = return_string; 200*1f5207b7SJohn Levon char *close; 201*1f5207b7SJohn Levon int param; 202*1f5207b7SJohn Levon struct expression *arg, *new; 203*1f5207b7SJohn Levon char *right_name; 204*1f5207b7SJohn Levon struct symbol *right_sym; 205*1f5207b7SJohn Levon char buf[256]; 206*1f5207b7SJohn Levon 207*1f5207b7SJohn Levon while (*p && *p != '[') 208*1f5207b7SJohn Levon p++; 209*1f5207b7SJohn Levon if (!*p) 210*1f5207b7SJohn Levon return; 211*1f5207b7SJohn Levon p++; 212*1f5207b7SJohn Levon if (*p != '$') 213*1f5207b7SJohn Levon return; 214*1f5207b7SJohn Levon 215*1f5207b7SJohn Levon snprintf(buf, sizeof(buf), "%s", p); 216*1f5207b7SJohn Levon close = strchr(buf, ']'); 217*1f5207b7SJohn Levon if (!close) 218*1f5207b7SJohn Levon return; 219*1f5207b7SJohn Levon *close = '\0'; 220*1f5207b7SJohn Levon 221*1f5207b7SJohn Levon param = atoi(buf + 1); 222*1f5207b7SJohn Levon arg = get_argument_from_call_expr(call->args, param); 223*1f5207b7SJohn Levon if (!arg) 224*1f5207b7SJohn Levon return; 225*1f5207b7SJohn Levon 226*1f5207b7SJohn Levon new = gen_expression_from_key(arg, buf); 227*1f5207b7SJohn Levon if (!new) 228*1f5207b7SJohn Levon return; 229*1f5207b7SJohn Levon 230*1f5207b7SJohn Levon right_name = expr_to_var_sym(new, &right_sym); 231*1f5207b7SJohn Levon if (!right_name || !right_sym) 232*1f5207b7SJohn Levon goto free; 233*1f5207b7SJohn Levon 234*1f5207b7SJohn Levon set_state(my_id, left_name, left_sym, alloc_my_state(right_name, right_sym)); 235*1f5207b7SJohn Levon store_link(link_id, right_name, right_sym, left_name, left_sym); 236*1f5207b7SJohn Levon 237*1f5207b7SJohn Levon free: 238*1f5207b7SJohn Levon free_string(right_name); 239*1f5207b7SJohn Levon } 240*1f5207b7SJohn Levon 241*1f5207b7SJohn Levon void __add_return_to_param_mapping(struct expression *expr, const char *return_string) 242*1f5207b7SJohn Levon { 243*1f5207b7SJohn Levon struct expression *call; 244*1f5207b7SJohn Levon char *left_name = NULL; 245*1f5207b7SJohn Levon struct symbol *left_sym; 246*1f5207b7SJohn Levon 247*1f5207b7SJohn Levon if (expr->type == EXPR_ASSIGNMENT) { 248*1f5207b7SJohn Levon left_name = expr_to_var_sym(expr->left, &left_sym); 249*1f5207b7SJohn Levon if (!left_name || !left_sym) 250*1f5207b7SJohn Levon goto free; 251*1f5207b7SJohn Levon 252*1f5207b7SJohn Levon call = strip_expr(expr->right); 253*1f5207b7SJohn Levon if (call->type != EXPR_CALL) 254*1f5207b7SJohn Levon goto free; 255*1f5207b7SJohn Levon 256*1f5207b7SJohn Levon store_mapping_helper(left_name, left_sym, call, return_string); 257*1f5207b7SJohn Levon goto free; 258*1f5207b7SJohn Levon } 259*1f5207b7SJohn Levon 260*1f5207b7SJohn Levon if (expr->type == EXPR_CALL && 261*1f5207b7SJohn Levon expr_get_parent_stmt(expr) && 262*1f5207b7SJohn Levon expr_get_parent_stmt(expr)->type == STMT_RETURN) { 263*1f5207b7SJohn Levon call = strip_expr(expr); 264*1f5207b7SJohn Levon left_sym = expr_to_sym(call->fn); 265*1f5207b7SJohn Levon if (!left_sym) 266*1f5207b7SJohn Levon return; 267*1f5207b7SJohn Levon left_name = expr_to_str(call); 268*1f5207b7SJohn Levon if (!left_name) 269*1f5207b7SJohn Levon return; 270*1f5207b7SJohn Levon 271*1f5207b7SJohn Levon store_mapping_helper(left_name, left_sym, call, return_string); 272*1f5207b7SJohn Levon goto free; 273*1f5207b7SJohn Levon 274*1f5207b7SJohn Levon } 275*1f5207b7SJohn Levon 276*1f5207b7SJohn Levon free: 277*1f5207b7SJohn Levon free_string(left_name); 278*1f5207b7SJohn Levon } 279*1f5207b7SJohn Levon 280*1f5207b7SJohn Levon void register_return_to_param(int id) 281*1f5207b7SJohn Levon { 282*1f5207b7SJohn Levon my_id = id; 283*1f5207b7SJohn Levon add_modification_hook(my_id, &undef); 284*1f5207b7SJohn Levon } 285*1f5207b7SJohn Levon 286*1f5207b7SJohn Levon void register_return_to_param_links(int id) 287*1f5207b7SJohn Levon { 288*1f5207b7SJohn Levon link_id = id; 289*1f5207b7SJohn Levon set_up_link_functions(my_id, link_id); 290*1f5207b7SJohn Levon } 291*1f5207b7SJohn Levon 292