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