1 /*
   2  * Copyright (C) 2012 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 #include "smatch.h"
  19 
  20 static int my_id;
  21 
  22 static void check_pointer(struct expression *expr, char *ptr_name)
  23 {
  24         char *name;
  25         sval_t sval;
  26 
  27         if (!expr || expr->type != EXPR_SIZEOF)
  28                 return;
  29 
  30         get_value(expr, &sval);
  31 
  32         expr = strip_expr(expr->cast_expression);
  33         name = expr_to_str(expr);
  34         if (!name)
  35                 return;
  36 
  37         if (strcmp(ptr_name, name) == 0)
  38                 sm_warning("was 'sizeof(*%s)' intended?", ptr_name);
  39         free_string(name);
  40 }
  41 
  42 static void match_call_assignment(struct expression *expr)
  43 {
  44         struct expression *call = strip_expr(expr->right);
  45         struct expression *arg;
  46         char *ptr_name;
  47 
  48         if (!is_pointer(expr->left))
  49                 return;
  50 
  51         ptr_name = expr_to_str(expr->left);
  52         if (!ptr_name)
  53                 return;
  54 
  55         FOR_EACH_PTR(call->args, arg) {
  56                 check_pointer(arg, ptr_name);
  57         } END_FOR_EACH_PTR(arg);
  58 
  59         free_string(ptr_name);
  60 }
  61 
  62 static void check_passes_pointer(char *name, struct expression *call)
  63 {
  64         struct expression *arg;
  65         char *ptr_name;
  66 
  67         FOR_EACH_PTR(call->args, arg) {
  68                 ptr_name = expr_to_var(arg);
  69                 if (!ptr_name)
  70                         continue;
  71                 if (strcmp(name, ptr_name) == 0)
  72                         sm_warning("was 'sizeof(*%s)' intended?", name);
  73                 free_string(ptr_name);
  74         } END_FOR_EACH_PTR(arg);
  75 }
  76 
  77 static void match_check_params(struct expression *call)
  78 {
  79         struct expression *arg;
  80         struct expression *obj;
  81         char *name;
  82 
  83         FOR_EACH_PTR(call->args, arg) {
  84                 if (arg->type != EXPR_SIZEOF)
  85                         continue;
  86                 obj = strip_expr(arg->cast_expression);
  87                 if (!is_pointer(obj))
  88                         continue;
  89                 name = expr_to_var(obj);
  90                 if (!name)
  91                         continue;
  92                 check_passes_pointer(name, call);
  93                 free_string(name);
  94         } END_FOR_EACH_PTR(arg);
  95 }
  96 
  97 static struct string_list *macro_takes_sizeof_argument;
  98 static void check_sizeof_number(struct expression *expr)
  99 {
 100         char *macro, *tmp;
 101 
 102         if (expr->type != EXPR_VALUE)
 103                 return;
 104         macro = get_macro_name(expr->pos);
 105         FOR_EACH_PTR(macro_takes_sizeof_argument, tmp) {
 106                 if (macro && strcmp(tmp, macro) == 0)
 107                         return;
 108         } END_FOR_EACH_PTR(tmp);
 109 
 110         sm_warning("sizeof(NUMBER)?");
 111 }
 112 
 113 static void match_sizeof(struct expression *expr)
 114 {
 115         check_sizeof_number(expr);
 116         if (expr->type == EXPR_PREOP && expr->op == '&')
 117                 sm_warning("sizeof(&pointer)?");
 118         if (expr->type == EXPR_SIZEOF)
 119                 sm_warning("sizeof(sizeof())?");
 120         /* the ilog2() macro is a valid place to check the size of a binop */
 121         if (expr->type == EXPR_BINOP && !get_macro_name(expr->pos))
 122                 sm_warning("taking sizeof binop");
 123 }
 124 
 125 static void register_macro_takes_sizeof_argument(void)
 126 {
 127         struct token *token;
 128         char *macro;
 129         char name[256];
 130 
 131         snprintf(name, 256, "%s.macro_takes_sizeof_argument", option_project_str);
 132 
 133         token = get_tokens_file(name);
 134         if (!token)
 135                 return;
 136         if (token_type(token) != TOKEN_STREAMBEGIN)
 137                 return;
 138         token = token->next;
 139         while (token_type(token) != TOKEN_STREAMEND) {
 140                 if (token_type(token) != TOKEN_IDENT)
 141                         return;
 142                 macro = alloc_string(show_ident(token->ident));
 143                 add_ptr_list(&macro_takes_sizeof_argument, macro);
 144                 token = token->next;
 145         }
 146         clear_token_alloc();
 147 }
 148 
 149 void check_sizeof(int id)
 150 {
 151         my_id = id;
 152 
 153         register_macro_takes_sizeof_argument();
 154         add_hook(&match_call_assignment, CALL_ASSIGNMENT_HOOK);
 155         add_hook(&match_check_params, FUNCTION_CALL_HOOK);
 156         add_hook(&match_sizeof, SIZEOF_HOOK);
 157 }