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