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 */ 32enum ltp_tu_kind { 33 LTP_LIB, 34 LTP_OTHER, 35}; 36 37static 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 */ 44static 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 63static 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 76static 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 */ 92static 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 */ 116static 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 */ 157static 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 */ 172static 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 */ 193static 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 */ 203static 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 */ 251static 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 */ 261static 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 282static 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 292int 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