1 /*
   2  * Copyright (C) 2010 Dan Carpenter.
   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  * According to an email on lkml you are not allowed to reuse the skb
  20  * passed to dev_queue_xmit()
  21  *
  22  */
  23 
  24 #include "smatch.h"
  25 #include "smatch_slist.h"
  26 
  27 static int my_id;
  28 
  29 STATE(do_not_use);
  30 
  31 static void ok_to_use(struct sm_state *sm, struct expression *mod_expr)
  32 {
  33         set_state(my_id, sm->name, sm->sym, &undefined);
  34 }
  35 
  36 static int valid_use(void)
  37 {
  38         struct expression *tmp;
  39         int i = 0;
  40         int dot_ops = 0;
  41 
  42         FOR_EACH_PTR_REVERSE(big_expression_stack, tmp) {
  43                 if (!i++)
  44                         continue;
  45                 if (tmp->type == EXPR_PREOP && tmp->op == '(')
  46                         continue;
  47                 if (tmp->op == '.' && !dot_ops++)
  48                         continue;
  49 //              if (tmp->type == EXPR_POSTOP)
  50 //                      return 1;
  51                 if (tmp->type == EXPR_CALL && sym_name_is("kfree_skb", tmp->fn))
  52                         return 1;
  53                 return 0;
  54         } END_FOR_EACH_PTR_REVERSE(tmp);
  55         return 0;
  56 }
  57 
  58 /* match symbol is expensive.  only turn it on after we match the xmit function */
  59 static int match_symbol_active;
  60 static void match_symbol(struct expression *expr)
  61 {
  62         struct sm_state *sm;
  63         char *name;
  64 
  65         sm = get_sm_state_expr(my_id, expr);
  66         if (!sm || !slist_has_state(sm->possible, &do_not_use))
  67                 return;
  68         if (valid_use())
  69                 return;
  70         name = expr_to_var(expr);
  71         sm_error("'%s' was already used up by dev_queue_xmit()", name);
  72         free_string(name);
  73 }
  74 
  75 static void match_kfree_skb(const char *fn, struct expression *expr, void *param)
  76 {
  77         struct expression *arg;
  78 
  79         arg = get_argument_from_call_expr(expr->args, 0);
  80         if (!arg)
  81                 return;
  82         set_state_expr(my_id, arg, &undefined);
  83 }
  84 
  85 static void match_xmit(const char *fn, struct expression *expr, void *param)
  86 {
  87         struct expression *arg;
  88 
  89         arg = get_argument_from_call_expr(expr->args, PTR_INT(param));
  90         if (!arg)
  91                 return;
  92         set_state_expr(my_id, arg, &do_not_use);
  93         if (!match_symbol_active++) {
  94                 add_hook(&match_symbol, SYM_HOOK);
  95                 add_function_hook("kfree_skb", &match_kfree_skb, NULL);
  96         }
  97 }
  98 
  99 static void register_funcs_from_file(void)
 100 {
 101         struct token *token;
 102         const char *func;
 103         int arg;
 104 
 105         token = get_tokens_file("kernel.dev_queue_xmit");
 106         if (!token)
 107                 return;
 108         if (token_type(token) != TOKEN_STREAMBEGIN)
 109                 return;
 110         token = token->next;
 111         while (token_type(token) != TOKEN_STREAMEND) {
 112                 if (token_type(token) != TOKEN_IDENT)
 113                         return;
 114                 func = show_ident(token->ident);
 115                 token = token->next;
 116                 if (token_type(token) != TOKEN_NUMBER)
 117                         return;
 118                 arg = atoi(token->number);
 119                 add_function_hook(func, &match_xmit, INT_PTR(arg));
 120                 token = token->next;
 121         }
 122         clear_token_alloc();
 123 }
 124 
 125 void check_dev_queue_xmit(int id)
 126 {
 127         if (option_project != PROJ_KERNEL)
 128                 return;
 129         my_id = id;
 130         add_modification_hook(my_id, ok_to_use);
 131         register_funcs_from_file();
 132 }