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