Print this page
12013 fix GCC4 as primary compiler
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/tools/smatch/src/check_arm64_tagged.c
+++ new/usr/src/tools/smatch/src/check_arm64_tagged.c
1 1 /*
2 2 * Copyright (C) 2019 ARM.
3 3 *
4 4 * This program is free software; you can redistribute it and/or
5 5 * modify it under the terms of the GNU General Public License
6 6 * as published by the Free Software Foundation; either version 2
7 7 * of the License, or (at your option) any later version.
8 8 *
9 9 * This program is distributed in the hope that it will be useful,
10 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 * GNU General Public License for more details.
13 13 *
14 14 * You should have received a copy of the GNU General Public License
15 15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
16 16 */
17 17
18 18 #include "smatch.h"
19 19 #include "smatch_extra.h"
20 20 #include "smatch_function_hashtable.h"
21 21
22 22 static bool expr_has_memory_addr(struct expression *expr);
23 23
24 24 static DEFINE_HASHTABLE_SEARCH(search_symbol, char, char);
25 25 static DEFINE_HASHTABLE_INSERT(insert_symbol, char, char);
26 26 static struct hashtable *symbols;
27 27
28 28 static void match_assign(struct expression *expr)
29 29 {
30 30 char *left_name;
31 31 struct symbol *left_sym;
32 32
33 33 left_name = expr_to_var_sym(expr->left, &left_sym);
34 34 if (!left_name || !left_sym)
35 35 return;
36 36
37 37 /*
38 38 * Once we have spotted a symbol of interest (one that may hold
39 39 * an untagged memory address), we keep track of any assignments
40 40 * made, such that we can also treat the assigned symbol as something
41 41 * of interest. This tracking is limited in scope to the function.
42 42 */
43 43 if (expr_has_memory_addr(expr->right))
44 44 insert_symbol(symbols, left_name, left_name);
45 45 }
46 46
47 47 static void match_endfunc(struct symbol *sym)
48 48 {
49 49 destroy_function_hashtable(symbols);
50 50 symbols = create_function_hashtable(4000);
51 51 }
52 52
53 53 static bool expr_has_untagged_symbol(struct expression *expr)
54 54 {
55 55 char *name;
56 56 struct symbol *sym;
57 57
58 58 if (expr->type != EXPR_SYMBOL)
59 59 return false;
60 60
61 61 name = expr_to_var_sym(expr, &sym);
62 62 if (!name || !sym)
63 63 return false;
64 64
65 65 /* See if this is something we already know is of interest */
66 66 if (search_symbol(symbols, name))
67 67 return true;
68 68
69 69 return false;
70 70 }
71 71
72 72 static bool expr_has_untagged_member(struct expression *expr)
73 73 {
74 74 if (expr->type != EXPR_DEREF)
75 75 return false;
76 76
77 77 if (!strcmp(expr->member->name, "vm_start") ||
78 78 !strcmp(expr->member->name, "vm_end") ||
79 79 !strcmp(expr->member->name, "addr_limit"))
80 80 return true;
81 81
82 82 return false;
83 83 }
84 84
85 85 static bool expr_has_macro_with_name(struct expression *expr, const char *macro_name)
86 86 {
87 87 char *name;
88 88
89 89 name = get_macro_name(expr->pos);
90 90 return (name && !strcmp(name, macro_name));
91 91 }
92 92
93 93 static bool expr_has_untagged_macro(struct expression *expr)
94 94 {
95 95 if (expr_has_macro_with_name(expr, "PAGE_SIZE") ||
96 96 expr_has_macro_with_name(expr, "PAGE_MASK") ||
97 97 expr_has_macro_with_name(expr, "TASK_SIZE"))
98 98 return true;
99 99
100 100 /**
101 101 * We can't detect a marco (such as PAGE_MASK) inside another macro
102 102 * such as offset_in_page, therefore we have to detect the outer macro
103 103 * instead.
104 104 */
105 105 if (expr_has_macro_with_name(expr, "offset_in_page"))
106 106 return true;
107 107
108 108 return false;
109 109 }
110 110
111 111 /*
112 112 * Identify expressions that contain memory addresses, in the future
113 113 * we may use annotations on symbols or function parameters.
114 114 */
115 115 static bool expr_has_memory_addr(struct expression *expr)
116 116 {
117 117 if (expr->type == EXPR_PREOP || expr->type == EXPR_POSTOP)
118 118 expr = strip_expr(expr->unop);
119 119
120 120 if (expr_has_untagged_member(expr))
121 121 return true;
122 122
123 123 if (expr_has_untagged_macro(expr))
124 124 return true;
125 125
126 126 if (expr_has_untagged_symbol(expr))
127 127 return true;
128 128
129 129 return false;
130 130 }
131 131
132 132 int rl_is_larger_or_equal(struct range_list *rl, sval_t sval)
133 133 {
134 134 struct data_range *tmp;
135 135
136 136 FOR_EACH_PTR(rl, tmp) {
137 137 if (sval_cmp(tmp->max, sval) >= 0)
138 138 return 1;
139 139 } END_FOR_EACH_PTR(tmp);
140 140 return 0;
141 141 }
142 142
143 143 int rl_range_has_min_value(struct range_list *rl, sval_t sval)
144 144 {
145 145 struct data_range *tmp;
146 146
↓ open down ↓ |
146 lines elided |
↑ open up ↑ |
147 147 FOR_EACH_PTR(rl, tmp) {
148 148 if (!sval_cmp(tmp->min, sval)) {
149 149 return 1;
150 150 }
151 151 } END_FOR_EACH_PTR(tmp);
152 152 return 0;
153 153 }
154 154
155 155 static bool rl_is_tagged(struct range_list *rl)
156 156 {
157 - sval_t invalid = { .type = &ullong_ctype, .value = (1ULL << 56) };
158 - sval_t invalid_kernel = { .type = &ullong_ctype, .value = (0xff8ULL << 52) };
157 + sval_t invalid;
158 + sval_t invalid_kernel;
159 159
160 + invalid.type = &ullong_ctype;
161 + invalid.value = 1ULL << 56;
162 + invalid_kernel.type = &ullong_ctype;
163 + invalid_kernel.value = 0xff8ULL << 52;
164 +
160 165 /*
161 166 * We only care for tagged addresses, thus ignore anything where the
162 167 * ranges of potential values cannot possibly have any of the top byte
163 168 * bits set.
164 169 */
165 170 if (!rl_is_larger_or_equal(rl, invalid))
166 171 return false;
167 172
168 173 /*
169 174 * Tagged addresses are untagged in the kernel by using sign_extend64 in
170 175 * the untagged_addr macro. For userspace addresses bit 55 will always
171 176 * be 0 and thus this has the effect of clearing the top byte. However
172 177 * for kernel addresses this is not true and the top bits end up set to
173 178 * all 1s. The untagged_addr macro results in leaving a gap in the range
174 179 * of possible values which can exist, thus let's look for a tell-tale
175 180 * range which starts from (0xff8ULL << 52).
176 181 */
177 182 if (rl_range_has_min_value(rl, invalid_kernel))
178 183 return false;
179 184
180 185 return true;
181 186 }
182 187
183 188 static void match_condition(struct expression *expr)
184 189 {
185 190 struct range_list *rl = NULL;
186 191 struct expression *val = NULL;
187 192 struct symbol *type;
188 193 char *var_name;
189 194
190 195 /*
191 196 * Match instances where something is compared against something
192 197 * else - we include binary operators as these are commonly used
193 198 * to make a comparison, e.g. if (start & ~PAGE_MASK).
194 199 */
195 200 if (expr->type != EXPR_COMPARE &&
196 201 expr->type != EXPR_BINOP)
197 202 return;
198 203
199 204 /*
200 205 * Look on both sides of the comparison for something that shouldn't
201 206 * be compared with a tagged address, e.g. macros such as PAGE_MASK
202 207 * or struct members named .vm_start.
203 208 */
204 209 if (expr_has_memory_addr(expr->left))
205 210 val = expr->right;
206 211
207 212 /*
208 213 * The macro 'offset_in_page' has the PAGE_MASK macro inside it, this
209 214 * results in 'expr_has_memory_addr' returning true for both sides. To
210 215 * work around this we assume PAGE_MASK (or similar) is on the right
211 216 * side, thus we do the following test last.
212 217 */
213 218 if (expr_has_memory_addr(expr->right))
214 219 val = expr->left;
215 220
216 221 if (!val)
217 222 return;
218 223
219 224 /* We only care about memory addresses which are 64 bits */
220 225 type = get_type(val);
221 226 if (!type || type_bits(type) != 64)
222 227 return;
223 228
224 229 /* We only care for comparison against user originated data */
225 230 if (!get_user_rl(val, &rl))
226 231 return;
227 232
228 233 /* We only care for tagged addresses */
229 234 if (!rl_is_tagged(rl))
230 235 return;
231 236
232 237 /* Finally, we believe we may have spotted a risky comparison */
233 238 var_name = expr_to_var(val);
234 239 if (var_name)
235 240 sm_warning("comparison of a potentially tagged address (%s, %d, %s)", get_function(), get_param_num(val), var_name);
236 241 }
237 242
238 243 void check_arm64_tagged(int id)
239 244 {
240 245 char *arch;
241 246
242 247 if (option_project != PROJ_KERNEL)
243 248 return;
244 249
245 250 /* Limit to aarch64 */
246 251 arch = getenv("ARCH");
247 252 if (!arch || strcmp(arch, "arm64"))
248 253 return;
249 254
250 255 symbols = create_function_hashtable(4000);
251 256
252 257 add_hook(&match_assign, ASSIGNMENT_HOOK);
253 258 add_hook(&match_condition, CONDITION_HOOK);
254 259 add_hook(&match_endfunc, END_FUNC_HOOK);
255 260 }
↓ open down ↓ |
86 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX