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 bool is_local_var(struct expression *expr)
  80 {
  81         struct symbol *sym;
  82 
  83         if (!expr || expr->type != EXPR_SYMBOL)
  84                 return false;
  85         sym = expr->symbol;
  86         if (!(sym->ctype.modifiers & MOD_TOPLEVEL))
  87                 return true;
  88         return false;
  89 }
  90 
  91 static void match_assign(struct expression *expr)
  92 {
  93         struct expression *left;
  94         struct symbol *right_sym;
  95         char *name;
  96         mtag_t tag;
  97         int offset;
  98         int param;
  99 
 100         if (expr->op != '=')
 101                 return;
 102         left = strip_expr(expr->left);
 103         if (is_local_var(left))
 104                 return;
 105         right_sym = expr_to_sym(expr->right);
 106         if (!right_sym)
 107                 return;
 108 
 109         param = get_param_num_from_sym(right_sym);
 110         if (param < 0)
 111                 return;
 112         // FIXME:  modify param_has_filter_data() to take a name/sym
 113         if (!expr_to_mtag_offset(left, &tag, &offset))
 114                 return;
 115         name = expr_to_str(left);
 116         if (!name)
 117                 return;
 118         set_state_expr(my_id, expr->right, alloc_tag_data_state(tag, name, offset));
 119         free_string(name);
 120 }
 121 
 122 static void propogate_assignment(struct expression *expr, mtag_t tag, int offset, int param, char *key)
 123 {
 124         struct expression *arg;
 125         int orig_param;
 126         char buf[32];
 127         char *name;
 128         struct symbol *sym;
 129 
 130         arg = get_argument_from_call_expr(expr->args, param);
 131         if (!arg)
 132                 return;
 133         name = get_variable_from_key(arg, key, &sym);
 134         if (!name || !sym)
 135                 goto free;
 136 
 137         orig_param = get_param_num_from_sym(sym);
 138         if (orig_param < 0)
 139                 goto free;
 140 
 141         snprintf(buf, sizeof(buf), "$->[%d]", offset);
 142         set_state(my_id, name, sym, alloc_tag_data_state(tag, buf, offset));
 143 free:
 144         free_string(name);
 145 }
 146 
 147 static void assign_to_alias(struct expression *expr, int param, mtag_t tag, int offset, char *key)
 148 {
 149         struct expression *arg, *gen_expr;
 150         struct range_list *rl;
 151         mtag_t arg_tag;
 152         mtag_t alias;
 153         int arg_offset;
 154 
 155         arg = get_argument_from_call_expr(expr->args, param);
 156         if (!arg)
 157                 return;
 158 
 159         gen_expr = gen_expression_from_key(arg, key);
 160         if (!gen_expr)
 161                 return;
 162 
 163         get_absolute_rl(gen_expr, &rl);
 164 
 165         if (!create_mtag_alias(tag, expr, &alias))
 166                 return;
 167 
 168 //      insert_mtag_data(alias, offset, rl);
 169 
 170         // FIXME:  is arg_offset handled correctly?
 171         if (expr_to_mtag_offset(gen_expr, &arg_tag, &arg_offset) && arg_offset == 0)
 172                 sql_insert_mtag_map(arg_tag, -offset, alias);
 173 }
 174 
 175 static void call_does_mtag_assign(struct expression *expr, int param, char *key, char *value)
 176 {
 177         char *p;
 178         mtag_t tag;
 179         int offset;
 180 
 181         while (expr->type == EXPR_ASSIGNMENT)
 182                 expr = strip_expr(expr->right);
 183         if (expr->type != EXPR_CALL)
 184                 return;
 185 
 186         tag = strtoul(value, NULL, 10);
 187         p = strchr(value, '+');
 188         if (!p)
 189                 return;
 190         offset = atoi(p + 1);
 191 
 192 //      save_mtag_to_map(expr, tag, offset, param, key, value);
 193         propogate_assignment(expr, tag, offset, param, key);
 194         assign_to_alias(expr, param, tag, offset, key);
 195 }
 196 
 197 static void print_stored_to_mtag(int return_id, char *return_ranges, struct expression *expr)
 198 {
 199         struct sm_state *sm;
 200         struct tag_assign_info *data;
 201         char buf[256];
 202         const char *param_name;
 203         int param;
 204 
 205         FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
 206                 if (!sm->state->data)
 207                         continue;
 208 
 209                 param = get_param_num_from_sym(sm->sym);
 210                 if (param < 0)
 211                         continue;
 212                 param_name = get_param_name(sm);
 213                 if (!param_name)
 214                         continue;
 215 
 216                 data = sm->state->data;
 217                 snprintf(buf, sizeof(buf), "%lld+%d", data->tag, data->offset);
 218                 sql_insert_return_states(return_id, return_ranges, MTAG_ASSIGN, param, param_name, buf);
 219         } END_FOR_EACH_SM(sm);
 220 }
 221 
 222 void register_param_to_mtag_data(int id)
 223 {
 224         my_id = id;
 225 
 226         set_dynamic_states(my_id);
 227         add_hook(&match_assign, ASSIGNMENT_HOOK);
 228         select_return_states_hook(MTAG_ASSIGN, &call_does_mtag_assign);
 229         add_merge_hook(my_id, &merge_tag_info);
 230         add_split_return_callback(&print_stored_to_mtag);
 231 }
 232