1 /* 2 * Copyright (C) 2006 Dan Carpenter. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 2 7 * of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt 16 * 17 * Copyright 2019 Joyent, Inc. 18 */ 19 20 #include <stdio.h> 21 #include <unistd.h> 22 #include <libgen.h> 23 #include "smatch.h" 24 #include "smatch_slist.h" 25 #include "check_list.h" 26 27 char *option_debug_check = (char *)""; 28 char *option_project_str = (char *)"smatch_generic"; 29 static char *option_db_file = (char *)"smatch_db.sqlite"; 30 enum project_type option_project = PROJ_NONE; 31 char *bin_dir; 32 char *data_dir; 33 int option_no_data = 0; 34 int option_spammy = 0; 35 int option_info = 0; 36 int option_full_path = 0; 37 int option_param_mapper = 0; 38 int option_call_tree = 0; 39 int option_no_db = 0; 40 int option_enable = 0; 41 int option_disable = 0; 42 int option_file_output; 43 int option_time; 44 int option_mem; 45 char *option_datadir_str; 46 int option_fatal_checks; 47 int option_succeed; 48 int option_timeout = 60; 49 50 FILE *sm_outfd; 51 FILE *sql_outfd; 52 FILE *caller_info_fd; 53 54 int sm_nr_errors; 55 int sm_nr_checks; 56 57 bool __silence_warnings_for_stmt; 58 59 const char *progname; 60 61 typedef void (*reg_func) (int id); 62 #define CK(_x) {.name = #_x, .func = &_x, .enabled = 0}, 63 static struct reg_func_info { 64 const char *name; 65 reg_func func; 66 int enabled; 67 } reg_funcs[] = { 68 {NULL, NULL}, 69 #include "check_list.h" 70 }; 71 #undef CK 72 int num_checks = ARRAY_SIZE(reg_funcs) - 1; 73 74 const char *check_name(unsigned short id) 75 { 76 if (id >= ARRAY_SIZE(reg_funcs)) 77 return "internal"; 78 79 return reg_funcs[id].name; 80 } 81 82 int id_from_name(const char *name) 83 { 84 int i; 85 86 for (i = 1; i < ARRAY_SIZE(reg_funcs); i++) { 87 if (!strcmp(name, reg_funcs[i].name)) 88 return i; 89 } 90 return 0; 91 } 92 93 static void show_checks(void) 94 { 95 int i; 96 97 for (i = 1; i < ARRAY_SIZE(reg_funcs); i++) { 98 if (!strncmp(reg_funcs[i].name, "check_", 6)) 99 printf("%3d. %s\n", i, reg_funcs[i].name); 100 } 101 exit(0); 102 } 103 104 static void enable_disable_checks(char *s, bool enable) 105 { 106 char buf[128]; 107 char *next; 108 int i; 109 110 do { 111 next = strchr(s, ','); 112 if (next) { 113 *next = '\0'; 114 next++; 115 } 116 if (*s == '\0') 117 return; 118 if (strncmp(s, "check_", 6) == 0) 119 snprintf(buf, sizeof(buf), "%s", s); 120 else 121 snprintf(buf, sizeof(buf), "check_%s", s); 122 123 124 for (i = 1; i < ARRAY_SIZE(reg_funcs); i++) { 125 if (strcmp(reg_funcs[i].name, buf) == 0) { 126 reg_funcs[i].enabled = (enable == true) ? 1 : -1; 127 break; 128 } 129 } 130 131 if (i == ARRAY_SIZE(reg_funcs)) 132 sm_fatal("'%s' not found", s); 133 134 } while ((s = next)); 135 } 136 137 static void help(void) 138 { 139 printf("Usage: smatch [smatch arguments][sparse arguments] file.c\n"); 140 printf("--project=<name> or -p=<name>: project specific tests\n"); 141 printf("--succeed: don't exit with an error\n"); 142 printf("--spammy: print superfluous crap.\n"); 143 printf("--info: print info used to fill smatch_data/.\n"); 144 printf("--debug: print lots of debug output.\n"); 145 printf("--param-mapper: enable param_mapper output.\n"); 146 printf("--no-data: do not use the /smatch_data/ directory.\n"); 147 printf("--data=<dir>: overwrite path to default smatch data directory.\n"); 148 printf("--full-path: print the full pathname.\n"); 149 printf("--debug-implied: print debug output about implications.\n"); 150 printf("--assume-loops: assume loops always go through at least once.\n"); 151 printf("--two-passes: use a two pass system for each function.\n"); 152 printf("--file-output: instead of printing stdout, print to \"file.c.smatch_out\".\n"); 153 printf("--fatal-checks: check output is treated as an error.\n"); 154 printf("--help: print this helpful message.\n"); 155 exit(1); 156 } 157 158 static int match_option(const char *arg, const char *option) 159 { 160 char *str; 161 char *tmp; 162 int ret = 0; 163 164 str = malloc(strlen(option) + 3); 165 snprintf(str, strlen(option) + 3, "--%s", option); 166 tmp = str; 167 while (*tmp) { 168 if (*tmp == '_') 169 *tmp = '-'; 170 tmp++; 171 } 172 if (!strcmp(arg, str)) 173 ret = 1; 174 free(str); 175 return ret; 176 } 177 178 #define OPTION(_x) do { \ 179 if (match_option((*argvp)[i], #_x)) { \ 180 option_##_x = 1; \ 181 } \ 182 } while (0) 183 184 void parse_args(int *argcp, char ***argvp) 185 { 186 int i; 187 188 for (i = 1 ; i < *argcp; i++) { 189 if (!strcmp((*argvp)[i], "--help")) 190 help(); 191 192 if (!strcmp((*argvp)[i], "--show-checks")) 193 show_checks(); 194 195 if (!strncmp((*argvp)[i], "--project=", 10)) 196 option_project_str = (*argvp)[i] + 10; 197 198 if (!strncmp((*argvp)[i], "-p=", 3)) 199 option_project_str = (*argvp)[i] + 3; 200 201 if (!strncmp((*argvp)[i], "--db-file=", 10)) 202 option_db_file = (*argvp)[i] + 10; 203 204 if (!strncmp((*argvp)[i], "--data=", 7)) 205 option_datadir_str = (*argvp)[i] + 7; 206 207 if (!strncmp((*argvp)[i], "--debug=", 8)) 208 option_debug_check = (*argvp)[i] + 8; 209 210 if (strncmp((*argvp)[i], "--trace=", 8) == 0) 211 trace_variable = (*argvp)[i] + 8; 212 213 if (strncmp((*argvp)[i], "--enable=", 9) == 0) { 214 enable_disable_checks((*argvp)[i] + 9, 1); 215 option_enable = 1; 216 } 217 218 if (strncmp((*argvp)[i], "--disable=", 10) == 0) { 219 enable_disable_checks((*argvp)[i] + 10, 0); 220 option_enable = 1; 221 option_disable = 1; 222 } 223 224 if (!strncmp((*argvp)[i], "--timeout=", 10)) { 225 if (sscanf((*argvp)[i] + 10, "%d", 226 &option_timeout) != 1) 227 sm_fatal("invalid option %s", (*argvp)[i]); 228 } 229 230 OPTION(fatal_checks); 231 OPTION(spammy); 232 OPTION(info); 233 OPTION(debug); 234 OPTION(assume_loops); 235 OPTION(no_data); 236 OPTION(two_passes); 237 OPTION(full_path); 238 OPTION(param_mapper); 239 OPTION(call_tree); 240 OPTION(file_output); 241 OPTION(time); 242 OPTION(mem); 243 OPTION(no_db); 244 OPTION(succeed); 245 } 246 247 if (strcmp(option_project_str, "smatch_generic") != 0) 248 option_project = PROJ_UNKNOWN; 249 250 if (strcmp(option_project_str, "kernel") == 0) 251 option_project = PROJ_KERNEL; 252 else if (strcmp(option_project_str, "wine") == 0) 253 option_project = PROJ_WINE; 254 else if (strcmp(option_project_str, "illumos_kernel") == 0) 255 option_project = PROJ_ILLUMOS_KERNEL; 256 else if (strcmp(option_project_str, "illumos_user") == 0) 257 option_project = PROJ_ILLUMOS_USER; 258 } 259 260 static char *read_bin_filename(void) 261 { 262 char filename[PATH_MAX] = {}; 263 char proc[PATH_MAX]; 264 265 pid_t pid = getpid(); 266 sprintf(proc, "/proc/%d/exe", pid); 267 if (readlink(proc, filename, PATH_MAX) < 0) 268 return NULL; 269 return alloc_string(filename); 270 } 271 272 static char *get_bin_dir(char *arg0) 273 { 274 char *orig; 275 276 orig = read_bin_filename(); 277 if (!orig) 278 orig = alloc_string(arg0); 279 return dirname(orig); 280 } 281 282 static char *get_data_dir(char *arg0) 283 { 284 char buf[256]; 285 char *dir; 286 287 if (option_no_data) 288 return NULL; 289 290 if (option_datadir_str) { 291 if (access(option_datadir_str, R_OK)) 292 sm_warning("%s is not accessible -- ignored.", 293 option_datadir_str); 294 else 295 return alloc_string(option_datadir_str); 296 } 297 298 strncpy(buf, "smatch_data/", sizeof(buf)); 299 dir = alloc_string(buf); 300 if (!access(dir, R_OK)) 301 return dir; 302 303 strncpy(buf, bin_dir, 254); 304 305 buf[255] = '\0'; 306 strncat(buf, "/smatch_data/", 254 - strlen(buf)); 307 dir = alloc_string(buf); 308 if (!access(dir, R_OK)) 309 return dir; 310 free_string(dir); 311 snprintf(buf, 254, "%s/smatch_data/", SMATCHDATADIR); 312 dir = alloc_string(buf); 313 if (!access(dir, R_OK)) 314 return dir; 315 316 sm_warning("%s is not accessible.", dir); 317 sm_warning("Use --no-data or --data to suppress this message."); 318 return NULL; 319 } 320 321 int main(int argc, char **argv) 322 { 323 struct string_list *filelist = NULL; 324 int i; 325 reg_func func; 326 327 sm_outfd = stdout; 328 sql_outfd = stdout; 329 caller_info_fd = stdout; 330 331 progname = argv[0]; 332 333 parse_args(&argc, &argv); 334 335 if (argc < 2) 336 help(); 337 338 /* this gets set back to zero when we parse the first function */ 339 final_pass = 1; 340 341 bin_dir = get_bin_dir(argv[0]); 342 data_dir = get_data_dir(argv[0]); 343 344 allocate_hook_memory(); 345 allocate_dynamic_states_array(num_checks); 346 create_function_hook_hash(); 347 open_smatch_db(option_db_file); 348 sparse_initialize(argc, argv, &filelist); 349 alloc_valid_ptr_rl(); 350 351 for (i = 1; i < ARRAY_SIZE(reg_funcs); i++) { 352 func = reg_funcs[i].func; 353 /* The script IDs start at 1. 354 0 is used for internal stuff. */ 355 if (!option_enable || reg_funcs[i].enabled == 1 || 356 (option_disable && reg_funcs[i].enabled != -1) || 357 strncmp(reg_funcs[i].name, "register_", 9) == 0) 358 func(i); 359 } 360 361 smatch(filelist); 362 free_string(data_dir); 363 364 if (option_succeed) 365 return 0; 366 if (sm_nr_errors > 0) 367 return 1; 368 if (sm_nr_checks > 0 && option_fatal_checks) 369 return 1; 370 return 0; 371 }