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