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