• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2021 SUSE LLC <rpalethorpe@suse.com>
4  *
5  * Sparse allows us to perform checks on the AST (struct symbol) or on
6  * a linearized representation. In the latter case we are given a set
7  * of entry points (functions) containing basic blocks of
8  * instructions.
9  *
10  * The basic blocks contain byte code in SSA form. This is similar to
11  * the intermediate representation most compilers use during
12  * optimisation.
13  */
14 #include <stdarg.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 
22 #include "lib.h"
23 #include "allocate.h"
24 #include "opcode.h"
25 #include "token.h"
26 #include "parse.h"
27 #include "symbol.h"
28 #include "expression.h"
29 #include "linearize.h"
30 
31 /* The rules for test, library and tool code are different */
32 enum ltp_tu_kind {
33 	LTP_LIB,
34 	LTP_OTHER,
35 };
36 
37 static enum ltp_tu_kind tu_kind = LTP_OTHER;
38 
39 /* Check for LTP-002
40  *
41  * Inspects the destination symbol of each store instruction. If it is
42  * TST_RET or TST_ERR then emit a warning.
43  */
check_lib_sets_TEST_vars(const struct instruction * insn)44 static void check_lib_sets_TEST_vars(const struct instruction *insn)
45 {
46 	static struct ident *TST_RES_id, *TST_ERR_id;
47 
48 	if (!TST_RES_id) {
49 		TST_RES_id = built_in_ident("TST_RET");
50 		TST_ERR_id = built_in_ident("TST_ERR");
51 	}
52 
53 	if (insn->opcode != OP_STORE)
54 		return;
55 	if (insn->src->ident != TST_RES_id &&
56 	    insn->src->ident != TST_ERR_id)
57 		return;
58 
59 	warning(insn->pos,
60 		"LTP-002: Library should not write to TST_RET or TST_ERR");
61 }
62 
do_basicblock_checks(struct basic_block * bb)63 static void do_basicblock_checks(struct basic_block *bb)
64 {
65 	struct instruction *insn;
66 
67 	FOR_EACH_PTR(bb->insns, insn) {
68 		if (!bb_reachable(insn->bb))
69 			continue;
70 
71 		if (tu_kind == LTP_LIB)
72 			check_lib_sets_TEST_vars(insn);
73 	} END_FOR_EACH_PTR(insn);
74 }
75 
do_entrypoint_checks(struct entrypoint * ep)76 static void do_entrypoint_checks(struct entrypoint *ep)
77 {
78 	struct basic_block *bb;
79 
80 	FOR_EACH_PTR(ep->bbs, bb) {
81 		do_basicblock_checks(bb);
82 	} END_FOR_EACH_PTR(bb);
83 }
84 
85 /* The old API can not comply with the rules. So when we see one of
86  * these symbols we know that it will result in further
87  * warnings. Probably these will suggest inappropriate things. Usually
88  * these symbols should be removed and the new API used
89  * instead. Otherwise they can be ignored until all tests have been
90  * converted to the new API.
91  */
check_symbol_deprecated(const struct symbol * const sym)92 static bool check_symbol_deprecated(const struct symbol *const sym)
93 {
94 	static struct ident *TCID_id, *TST_TOTAL_id;
95 	const struct ident *id = sym->ident;
96 
97 	if (!TCID_id) {
98 		TCID_id = built_in_ident("TCID");
99 		TST_TOTAL_id = built_in_ident("TST_TOTAL");
100 	}
101 
102 	if (id != TCID_id && id != TST_TOTAL_id)
103 		return false;
104 
105 	warning(sym->pos,
106 		"Ignoring deprecated API symbol: '%s'. Should this code be converted to the new API?",
107 		show_ident(id));
108 
109 	return true;
110 }
111 
112 /* Check for LTP-003 and LTP-004
113  *
114  * Try to find cases where the static keyword was forgotten.
115  */
check_symbol_visibility(const struct symbol * const sym)116 static void check_symbol_visibility(const struct symbol *const sym)
117 {
118 	const unsigned long mod = sym->ctype.modifiers;
119 	const char *const name = show_ident(sym->ident);
120 	const int has_lib_prefix = !strncmp("tst_", name, 4) ||
121 		!strncmp("TST_", name, 4) ||
122 		!strncmp("ltp_", name, 4) ||
123 		!strncmp("safe_", name, 5);
124 
125 	if (!(mod & MOD_TOPLEVEL))
126 		return;
127 
128 	if (has_lib_prefix && (mod & MOD_STATIC) && !(mod & MOD_INLINE)) {
129 		warning(sym->pos,
130 			"LTP-003: Symbol '%s' has the LTP public library prefix, but is static (private).",
131 			name);
132 		return;
133 	}
134 
135 	if ((mod & MOD_STATIC))
136 		return;
137 
138 	if (tu_kind == LTP_LIB && !has_lib_prefix) {
139 		warning(sym->pos,
140 			"LTP-003: Symbol '%s' is a public library function, but is missing the 'tst_' prefix",
141 			name);
142 		return;
143 	}
144 
145 	if (sym->same_symbol)
146 		return;
147 
148 	if (sym->ident == &main_ident)
149 		return;
150 
151 	warning(sym->pos,
152 		"Symbol '%s' has no prototype or library ('tst_') prefix. Should it be static?",
153 		name);
154 }
155 
156 /* See base_type() in dissect.c */
unwrap_base_type(const struct symbol * sym)157 static struct symbol *unwrap_base_type(const struct symbol *sym)
158 {
159 	switch (sym->ctype.base_type->type) {
160 	case SYM_ARRAY:
161 	case SYM_NODE:
162 	case SYM_PTR:
163 		return unwrap_base_type(sym->ctype.base_type);
164 	default:
165 		return sym->ctype.base_type;
166 	}
167 }
168 
169 /* Checks if some struct array initializer is terminated with a blank
170  * (zeroed) item i.e. {}
171  */
is_terminated_with_null_struct(const struct symbol * const sym)172 static bool is_terminated_with_null_struct(const struct symbol *const sym)
173 {
174 	const struct expression *const arr_init = sym->initializer;
175 	const struct expression *item_init =
176 		last_ptr_list((struct ptr_list *)arr_init->expr_list);
177 
178 	if (item_init->type == EXPR_POS)
179 		item_init = item_init->init_expr;
180 
181 	if (item_init->type != EXPR_INITIALIZER)
182 		return false;
183 
184 	return ptr_list_empty((struct ptr_list *)item_init->expr_list);
185 }
186 
187 /* LTP-005: Check array sentinel value
188  *
189  * This is most important for the tags array. It is only accessed when
190  * the test fails. So we perform a static check to ensure it ends with
191  * {}.
192  */
check_struct_array_initializer(const struct symbol * const sym)193 static void check_struct_array_initializer(const struct symbol *const sym)
194 {
195 	if (is_terminated_with_null_struct(sym))
196 		return;
197 
198 	warning(sym->pos,
199 		"LTP-005: Struct array doesn't appear to be null-terminated; did you forget to add '{}' as the final entry?");
200 }
201 
202 /* Find struct tst_test test = { ... } and perform tests on its initializer */
check_test_struct(const struct symbol * const sym)203 static void check_test_struct(const struct symbol *const sym)
204 {
205 	static struct ident *tst_test, *tst_test_test;
206 	struct ident *ctype_name = NULL;
207 	struct expression *init = sym->initializer;
208 	struct expression *entry;
209 
210 	if (!sym->ctype.base_type)
211 		return;
212 
213 	ctype_name = sym->ctype.base_type->ident;
214 
215 	if (!init)
216 		return;
217 
218 	if (!tst_test_test) {
219 		tst_test = built_in_ident("tst_test");
220 		tst_test_test = built_in_ident("test");
221 	}
222 
223 	if (sym->ident != tst_test_test)
224 		return;
225 
226 	if (ctype_name != tst_test)
227 		return;
228 
229 	FOR_EACH_PTR(init->expr_list, entry) {
230 		if (entry->init_expr->type != EXPR_SYMBOL)
231 			continue;
232 
233 		switch (entry->ctype->ctype.base_type->type) {
234 		case SYM_PTR:
235 		case SYM_ARRAY:
236 			break;
237 		default:
238 			return;
239 		}
240 
241 		const struct symbol *entry_init = entry->init_expr->symbol;
242 		const struct symbol *entry_ctype = unwrap_base_type(entry_init);
243 
244 		if (entry_ctype->type == SYM_STRUCT)
245 			check_struct_array_initializer(entry_init);
246 	} END_FOR_EACH_PTR(entry);
247 
248 }
249 
250 /* AST level checks */
do_symbol_checks(struct symbol * sym)251 static void do_symbol_checks(struct symbol *sym)
252 {
253 	if (check_symbol_deprecated(sym))
254 		return;
255 
256 	check_symbol_visibility(sym);
257 	check_test_struct(sym);
258 }
259 
260 /* Compile the AST into a graph of basicblocks */
process_symbols(struct symbol_list * list)261 static void process_symbols(struct symbol_list *list)
262 {
263 	struct symbol *sym;
264 
265 	FOR_EACH_PTR(list, sym) {
266 		struct entrypoint *ep;
267 
268 		do_symbol_checks(sym);
269 
270 		expand_symbol(sym);
271 		ep = linearize_symbol(sym);
272 		if (!ep || !ep->entry)
273 			continue;
274 
275 		do_entrypoint_checks(ep);
276 
277 		if (dbg_entry)
278 			show_entry(ep);
279 	} END_FOR_EACH_PTR(sym);
280 }
281 
collect_info_from_args(const int argc,char * const * const argv)282 static void collect_info_from_args(const int argc, char *const *const argv)
283 {
284 	int i;
285 
286 	for (i = 0; i < argc; i++) {
287 		if (!strcmp("-DLTPLIB", argv[i]))
288 			tu_kind = LTP_LIB;
289 	}
290 }
291 
main(int argc,char ** argv)292 int main(int argc, char **argv)
293 {
294 	struct string_list *filelist = NULL;
295 	char *file;
296 
297 	Waddress_space = 0;
298 	Wbitwise = 0;
299 	Wcast_truncate = 0;
300 	Wcontext = 0;
301 	Wdecl = 0;
302 	Wexternal_function_has_definition = 0;
303 	Wflexible_array_array = 0;
304 	Wimplicit_int = 0;
305 	Wint_to_pointer_cast = 0;
306 	Wmemcpy_max_count = 0;
307 	Wnon_pointer_null = 0;
308 	Wone_bit_signed_bitfield = 0;
309 	Woverride_init = 0;
310 	Wpointer_to_int_cast = 0;
311 	Wvla = 0;
312 
313 	do_output = 0;
314 
315 	collect_info_from_args(argc, argv);
316 
317 	process_symbols(sparse_initialize(argc, argv, &filelist));
318 	FOR_EACH_PTR(filelist, file) {
319 		process_symbols(sparse(file));
320 	} END_FOR_EACH_PTR(file);
321 
322 	report_stats();
323 	return 0;
324 }
325