1 /*
   2  * Copyright (C) 2018 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 #include "smatch_extra.h"
  20 
  21 static int my_id;
  22 
  23 static int suppress_multiple = 1;
  24 
  25 static int is_write(struct expression *expr)
  26 {
  27         return 0;
  28 }
  29 
  30 static int is_read(struct expression *expr)
  31 {
  32         struct expression *parent, *last_parent;
  33         struct statement *stmt;
  34 
  35         if (is_write(expr))
  36                 return 0;
  37 
  38         last_parent = expr;
  39         while ((parent = expr_get_parent_expr(expr))){
  40 
  41                 last_parent = parent;
  42 
  43                 /* If we pass a value as a parameter that's a read, probably? */
  44 //              if (parent->type == EXPR_CALL)
  45 //                      return 1;
  46 
  47                 if (parent->type == EXPR_ASSIGNMENT) {
  48                         if (parent->right == expr)
  49                                 return 1;
  50                         if (parent->left == expr)
  51                                 return 0;
  52                 }
  53                 expr = parent;
  54         }
  55 
  56         stmt = expr_get_parent_stmt(last_parent);
  57         if (stmt && stmt->type == STMT_RETURN)
  58                 return 1;
  59 
  60         return 0;
  61 }
  62 
  63 static int is_harmless(struct expression *expr)
  64 {
  65         struct expression *tmp, *parent;
  66         struct statement *stmt;
  67         int count = 0;
  68 
  69         parent = expr;
  70         while ((tmp = expr_get_parent_expr(parent))) {
  71                 if (tmp->type == EXPR_ASSIGNMENT || tmp->type == EXPR_CALL)
  72                         return 0;
  73                 parent = tmp;
  74                 if (count++ > 4)
  75                         break;
  76         }
  77 
  78         stmt = expr_get_parent_stmt(parent);
  79         if (!stmt)
  80                 return 0;
  81         if (stmt->type == STMT_IF && stmt->if_conditional == parent)
  82                 return 1;
  83         if (stmt->type == STMT_ITERATOR &&
  84             (stmt->iterator_pre_condition == parent ||
  85              stmt->iterator_post_condition == parent))
  86                 return 1;
  87 
  88         return 0;
  89 }
  90 
  91 static unsigned long long get_max_by_type(struct expression *expr)
  92 {
  93         struct symbol *type;
  94         int cnt = 0;
  95         sval_t max;
  96 
  97         max.type = &ullong_ctype;
  98         max.uvalue = -1ULL;
  99 
 100         while (true) {
 101                 expr = strip_parens(expr);
 102                 type = get_type(expr);
 103                 if (type && sval_type_max(type).uvalue < max.uvalue)
 104                         max = sval_type_max(type);
 105                 if (expr->type == EXPR_PREOP) {
 106                         expr = expr->unop;
 107                 } else if (expr->type == EXPR_BINOP) {
 108                         if (expr->op == '%' || expr->op == '&')
 109                                 expr = expr->right;
 110                         else
 111                                 return max.uvalue;
 112                 } else {
 113                         expr = get_assigned_expr(expr);
 114                         if (!expr)
 115                                 return max.uvalue;
 116                 }
 117                 if (cnt++ > 5)
 118                         return max.uvalue;
 119         }
 120 
 121         return max.uvalue;
 122 }
 123 
 124 static unsigned long long get_mask(struct expression *expr)
 125 {
 126         struct expression *tmp;
 127         sval_t mask;
 128         int cnt = 0;
 129 
 130         expr = strip_expr(expr);
 131 
 132         tmp = get_assigned_expr(expr);
 133         while (tmp) {
 134                 expr = tmp;
 135                 if (++cnt > 3)
 136                         break;
 137                 tmp = get_assigned_expr(expr);
 138         }
 139 
 140         if (expr->type == EXPR_BINOP && expr->op == '&') {
 141                 if (get_value(expr->right, &mask))  /* right is the common case */
 142                         return mask.uvalue;
 143                 if (get_value(expr->left, &mask))
 144                         return mask.uvalue;
 145         }
 146 
 147         return ULLONG_MAX;
 148 }
 149 
 150 static void array_check(struct expression *expr)
 151 {
 152         struct expression_list *conditions;
 153         struct expression *array_expr, *offset;
 154         unsigned long long mask;
 155         int array_size;
 156         char *name;
 157 
 158         expr = strip_expr(expr);
 159         if (!is_array(expr))
 160                 return;
 161 
 162         if (is_impossible_path())
 163                 return;
 164         if (is_harmless(expr))
 165                 return;
 166 
 167         array_expr = get_array_base(expr);
 168         if (suppress_multiple && is_ignored_expr(my_id, array_expr))
 169                 return;
 170 
 171         offset = get_array_offset(expr);
 172         if (!is_user_rl(offset))
 173                 return;
 174         if (is_nospec(offset))
 175                 return;
 176 
 177         array_size = get_array_size(array_expr);
 178         if (array_size > 0 && get_max_by_type(offset) < array_size)
 179                 return;
 180 //      binfo = get_bit_info(offset);
 181 //      if (array_size > 0 && binfo && binfo->possible < array_size)
 182 //              return;
 183 
 184         mask = get_mask(offset);
 185         if (mask <= array_size)
 186                 return;
 187 
 188         conditions = get_conditions(offset);
 189 
 190         name = expr_to_str(array_expr);
 191         sm_warning("potential spectre issue '%s' [%s]%s",
 192                name,
 193                is_read(expr) ? "r" : "w",
 194                conditions ? " (local cap)" : "");
 195         if (suppress_multiple)
 196                 add_ignore_expr(my_id, array_expr);
 197         free_string(name);
 198 }
 199 
 200 void check_spectre(int id)
 201 {
 202         my_id = id;
 203 
 204         suppress_multiple = getenv("FULL_SPECTRE") == NULL;
 205 
 206         if (option_project != PROJ_KERNEL)
 207                 return;
 208 
 209         add_hook(&array_check, OP_HOOK);
 210 }