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 }