1 /*
   2  * Copyright (C) 2017 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  * Take a look at request_threaded_irq().  It takes thread_fn and dev_id.  Then
  20  * it does:
  21  *
  22  *      action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
  23  *      action->thread_fn = thread_fn;
  24  *      action->dev_id = dev_id;
  25  *
  26  * It doesn't ever pass action back to the higher levels, but instead registers
  27  * it with the lower levels.
  28  *
  29  * The kzalloc() allocation creates a new mtag.  We don't know at this point
  30  * what "thread_fn" and "dev_id" are because they come from many different
  31  * sources.
  32  *
  33  * So what we do is we pass the information back to the callers that thread_fn
  34  * and dev_id are stored as a specific mtag data.  Then when the callers *do*
  35  * know what values are passed they create an mtag_alias.  An mtag_alias is a
  36  * many to one relationship.  Then they store that in mtag_data using the
  37  * mtag_alias.
  38  *
  39  */
  40 
  41 #include "smatch.h"
  42 #include "smatch_extra.h"
  43 #include "smatch_slist.h"
  44 
  45 static int my_id;
  46 
  47 struct tag_assign_info {
  48         mtag_t tag;
  49         int offset;
  50 };
  51 ALLOCATOR(tag_assign_info, "tag name offset");
  52 
  53 static struct smatch_state *alloc_tag_data_state(mtag_t tag, char *name, int offset)
  54 {
  55         struct smatch_state *state;
  56         struct tag_assign_info *data;
  57 
  58         data = __alloc_tag_assign_info(0);
  59         data->tag = tag;
  60         data->offset = offset;
  61 
  62         state = __alloc_smatch_state(0);
  63         state->name = alloc_sname(name);
  64         state->data = data;
  65         return state;
  66 }
  67 
  68 struct smatch_state *merge_tag_info(struct smatch_state *s1, struct smatch_state *s2)
  69 {
  70         /* Basically ignore undefined states */
  71         if (s1 == &undefined)
  72                 return s2;
  73         if (s2 == &undefined)
  74                 return s1;
  75 
  76         return &merged;
  77 }
  78 
  79 static void match_assign(struct expression *expr)
  80 {
  81         struct expression *left;
  82         struct symbol *right_sym;
  83         char *name;
  84         mtag_t tag;
  85         int offset;
  86         int param;
  87 
  88         if (expr->op != '=')
  89                 return;
  90         left = strip_expr(expr->left);
  91         if (is_local_variable(left))
  92                 return;
  93         right_sym = expr_to_sym(expr->right);
  94         if (!right_sym)
  95                 return;
  96 
  97         param = get_param_num_from_sym(right_sym);
  98         if (param < 0)
  99                 return;
 100         // FIXME:  modify param_has_filter_data() to take a name/sym
 101         if (!expr_to_mtag_offset(left, &tag, &offset))
 102                 return;
 103         name = expr_to_str(left);
 104         if (!name)
 105                 return;
 106         set_state_expr(my_id, expr->right, alloc_tag_data_state(tag, name, offset));
 107         free_string(name);
 108 }
 109 
 110 static void propogate_assignment(struct expression *expr, mtag_t tag, int offset, int param, char *key)
 111 {
 112         struct expression *arg;
 113         int orig_param;
 114         char buf[32];
 115         char *name;
 116         struct symbol *sym;
 117 
 118         arg = get_argument_from_call_expr(expr->args, param);
 119         if (!arg)
 120                 return;
 121         name = get_variable_from_key(arg, key, &sym);
 122         if (!name || !sym)
 123                 goto free;
 124 
 125         orig_param = get_param_num_from_sym(sym);
 126         if (orig_param < 0)
 127                 goto free;
 128 
 129         snprintf(buf, sizeof(buf), "$->[%d]", offset);
 130         set_state(my_id, name, sym, alloc_tag_data_state(tag, buf, offset));
 131 free:
 132         free_string(name);
 133 }
 134 
 135 static void assign_to_alias(struct expression *expr, int param, mtag_t tag, int offset, char *key)
 136 {
 137         struct expression *arg, *gen_expr;
 138         struct range_list *rl;
 139         mtag_t arg_tag;
 140         mtag_t alias;
 141         int arg_offset;
 142 
 143         arg = get_argument_from_call_expr(expr->args, param);
 144         if (!arg)
 145                 return;
 146 
 147         gen_expr = gen_expression_from_key(arg, key);
 148         if (!gen_expr)
 149                 return;
 150 
 151         get_absolute_rl(gen_expr, &rl);
 152 
 153         if (!create_mtag_alias(tag, expr, &alias))
 154                 return;
 155 
 156 //      insert_mtag_data(alias, offset, rl);
 157 
 158         // FIXME:  is arg_offset handled correctly?
 159         if (expr_to_mtag_offset(gen_expr, &arg_tag, &arg_offset) && arg_offset == 0)
 160                 sql_insert_mtag_map(arg_tag, -offset, alias);
 161 }
 162 
 163 static void call_does_mtag_assign(struct expression *expr, int param, char *key, char *value)
 164 {
 165         char *p;
 166         mtag_t tag;
 167         int offset;
 168 
 169         while (expr->type == EXPR_ASSIGNMENT)
 170                 expr = strip_expr(expr->right);
 171         if (expr->type != EXPR_CALL)
 172                 return;
 173 
 174         tag = strtoul(value, NULL, 10);
 175         p = strchr(value, '+');
 176         if (!p)
 177                 return;
 178         offset = atoi(p + 1);
 179 
 180 //      save_mtag_to_map(expr, tag, offset, param, key, value);
 181         propogate_assignment(expr, tag, offset, param, key);
 182         assign_to_alias(expr, param, tag, offset, key);
 183 }
 184 
 185 static void print_stored_to_mtag(int return_id, char *return_ranges, struct expression *expr)
 186 {
 187         struct sm_state *sm;
 188         struct tag_assign_info *data;
 189         char buf[256];
 190         const char *param_name;
 191         int param;
 192 
 193         FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
 194                 if (!sm->state->data)
 195                         continue;
 196 
 197                 param = get_param_num_from_sym(sm->sym);
 198                 if (param < 0)
 199                         continue;
 200                 param_name = get_param_name(sm);
 201                 if (!param_name)
 202                         continue;
 203 
 204                 data = sm->state->data;
 205                 snprintf(buf, sizeof(buf), "%lld+%d", data->tag, data->offset);
 206                 sql_insert_return_states(return_id, return_ranges, MTAG_ASSIGN, param, param_name, buf);
 207         } END_FOR_EACH_SM(sm);
 208 }
 209 
 210 void register_param_to_mtag_data(int id)
 211 {
 212         my_id = id;
 213 
 214         set_dynamic_states(my_id);
 215         add_hook(&match_assign, ASSIGNMENT_HOOK);
 216         select_return_states_hook(MTAG_ASSIGN, &call_does_mtag_assign);
 217         add_merge_hook(my_id, &merge_tag_info);
 218         add_split_return_callback(&print_stored_to_mtag);
 219 }
 220