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 } 211