1 #include "smatch.h"
   2 #include "smatch_slist.h"
   3 
   4 static int my_id;
   5 
   6 /* If set, we ignore struct type symbols as implicit dependencies */
   7 static int ignore_structs;
   8 
   9 static struct symbol *cur_syscall;
  10 /* note: cannot track return type and remove from implicit dependencies,
  11  * because every syscall returns a long, and we don't have a good way to know
  12  * whether or not this is a resource. The only example I can think of is open
  13  * returning a filedescriptor, so in the implicit dep parsing, we will just
  14  * blacklist struct fd --> file
  15  */
  16 static struct symbol *cur_return_type;
  17 static char *syscall_name;
  18 
  19 static struct tracker_list *read_list;  // what fields does syscall branch on?
  20 static struct tracker_list *write_list; // what fields does syscall modify?
  21 static struct tracker_list *arg_list;   // what struct arguments does the syscall take?
  22 static struct tracker_list *parsed_syscalls; // syscalls we have already checked
  23 
  24 static inline void prefix(void)
  25 {
  26         printf("%s:%d %s() ", get_filename(), get_lineno(), get_function());
  27 }
  28 
  29 static void match_syscall_definition(struct symbol *sym)
  30 {
  31         struct symbol *arg;
  32         struct tracker *tracker;
  33         char *macro;
  34         char *name;
  35         int is_syscall = 0;
  36 
  37         macro = get_macro_name(sym->pos);
  38         if (macro &&
  39             (strncmp("SYSCALL_DEFINE", macro, strlen("SYSCALL_DEFINE")) == 0 ||
  40              strncmp("COMPAT_SYSCALL_DEFINE", macro, strlen("COMPAT_SYSCALL_DEFINE")) == 0))
  41                 is_syscall = 1;
  42 
  43         name = get_function();
  44 
  45         if (name && strncmp(name, "sys_", 4) == 0)
  46                 is_syscall = 1;
  47 
  48         if (name && strncmp(name, "compat_sys_", 11) == 0)
  49                 is_syscall = 1;
  50 
  51         if (!is_syscall)
  52                 return;
  53 
  54         FOR_EACH_PTR(parsed_syscalls, tracker) {
  55                 if (tracker->sym == sym) // don't re-parse
  56                         return;
  57         } END_FOR_EACH_PTR(tracker);
  58 
  59         syscall_name = name;
  60         cur_syscall = sym;
  61 
  62         cur_return_type = cur_func_return_type();
  63         if (cur_return_type && cur_return_type->ident)
  64                 sm_msg("return type: %s\n", cur_return_type->ident->name);
  65 
  66 
  67         FOR_EACH_PTR(sym->ctype.base_type->arguments, arg) {
  68                 // set_state(my_id, arg->ident->name, arg, &user_data_set);
  69                 sm_msg("=======check_impl: arguments for call %s=========\n", syscall_name);
  70                 if (arg->type == SYM_STRUCT)
  71                         arg = get_real_base_type(arg);
  72                 if (cur_return_type && cur_return_type->ident)
  73                         sm_msg("arg type: %s\n", cur_return_type->ident->name);
  74                 // add_tracker(&arg_list, my_id, member, arg);
  75                 sm_msg("=================================\n");
  76         } END_FOR_EACH_PTR(arg);
  77 }
  78 
  79 static void print_read_list(void)
  80 {
  81     struct tracker *tracker;
  82     int i = 0;
  83 
  84     FOR_EACH_PTR(read_list, tracker) {
  85             if (i == 0)
  86                     sm_printf("%s read_list: [", syscall_name);
  87             sm_printf("%s, ", tracker->name);
  88             i++;
  89     } END_FOR_EACH_PTR(tracker);
  90 
  91     if (i > 0)
  92             sm_printf("]\n");
  93 }
  94 
  95 static void print_write_list(void)
  96 {
  97         struct tracker *tracker;
  98         int i = 0;
  99 
 100         FOR_EACH_PTR(write_list, tracker) {
 101                 if (i == 0)
 102                         sm_printf("%s write_list: [", syscall_name);
 103                 sm_printf("%s, ", tracker->name);
 104                 i++;
 105         } END_FOR_EACH_PTR(tracker);
 106 
 107         if (i > 0)
 108                 sm_printf("]\n");
 109 }
 110 
 111 static void print_arg_list(void)
 112 {
 113         struct tracker *tracker;
 114         int i = 0;
 115 
 116         FOR_EACH_PTR(write_list, tracker) {
 117                 if (i == 0)
 118                         sm_printf("%s arg_list: [", syscall_name);
 119                 sm_printf("%s, ", tracker->name);
 120                 i++;
 121         } END_FOR_EACH_PTR(tracker);
 122 
 123         if (i > 0)
 124                 sm_printf("]\n");
 125 }
 126 
 127 static void match_after_syscall(struct symbol *sym)
 128 {
 129         if (!cur_syscall || sym != cur_syscall)
 130                 return;
 131         // printf("\n"); prefix();
 132         // printf("exiting scope of syscall %s\n", get_function());
 133         // printf("-------------------------\n");
 134         print_read_list();
 135         print_write_list();
 136         print_arg_list();
 137         free_trackers_and_list(&read_list);
 138         free_trackers_and_list(&write_list);
 139         free_trackers_and_list(&arg_list);
 140         add_tracker(&parsed_syscalls, my_id, syscall_name, sym);
 141         cur_syscall = NULL;
 142         cur_return_type = NULL;
 143         syscall_name = NULL;
 144 }
 145 
 146 static void print_read_member_type(struct expression *expr)
 147 {
 148         char *member;
 149         struct symbol *sym;
 150         struct symbol *member_sym;
 151 
 152         member = get_member_name(expr);
 153         if (!member)
 154                 return;
 155 
 156         sym = get_type(expr->deref);
 157         member_sym = get_type(expr);
 158 
 159         if (member_sym->type == SYM_PTR)
 160                 member_sym = get_real_base_type(member_sym);
 161 
 162         /*
 163         if (member_sym->type == SYM_STRUCT)
 164                 printf("found struct type %s\n", member);
 165         else
 166                 printf("found non-struct type %s with enum value%d\n", member, member_sym->type);
 167         */
 168 
 169         if (ignore_structs && member_sym->type == SYM_STRUCT) {
 170                 // printf("ignoring %s\n", member);
 171                 return;
 172         }
 173 
 174         add_tracker(&read_list, my_id, member, sym);
 175         // sm_msg("info: uses %s", member);
 176         // prefix();
 177         // printf("info: uses %s\n", member);
 178         free_string(member);
 179 }
 180 
 181 static void print_write_member_type(struct expression *expr)
 182 {
 183         char *member;
 184         struct symbol *sym;
 185         struct symbol *member_sym;
 186 
 187         member = get_member_name(expr);
 188         if (!member)
 189                 return;
 190 
 191         sym = get_type(expr->deref);
 192         member_sym = get_type(expr);
 193 
 194         if (member_sym->type == SYM_PTR)
 195                 member_sym = get_real_base_type(member_sym);
 196 
 197         /*
 198         if (member_sym->type == SYM_STRUCT)
 199                 printf("found struct type %s\n", member);
 200         else
 201                 printf("found non-struct type %s with enum value%d\n", member, member_sym->type);
 202         */
 203 
 204         if (ignore_structs && member_sym->type == SYM_STRUCT) {
 205                 // printf("ignoring %s\n", member);
 206                 return;
 207         }
 208 
 209         add_tracker(&write_list, my_id, member, sym);
 210         free_string(member);
 211 }
 212 
 213 static void match_condition(struct expression *expr)
 214 {
 215         struct expression *arg;
 216 
 217         if (!cur_syscall)
 218                 return;
 219 
 220         // prefix(); printf("-- condition found\n");
 221 
 222         if (expr->type == EXPR_COMPARE ||
 223             expr->type == EXPR_BINOP ||
 224             expr->type == EXPR_LOGICAL ||
 225             expr->type == EXPR_ASSIGNMENT ||
 226             expr->type == EXPR_COMMA) {
 227                 match_condition(expr->left);
 228                 match_condition(expr->right);
 229                 return;
 230         }
 231 
 232         if (expr->type == EXPR_CALL) {
 233                 FOR_EACH_PTR(expr->args, arg) {
 234                         // if we find deref in conditional call,
 235                         // mark it as a read dependency
 236                         print_read_member_type(arg);
 237                 } END_FOR_EACH_PTR(arg);
 238                 return;
 239         }
 240 
 241         print_read_member_type(expr);
 242 }
 243 
 244 
 245 /* when we are parsing an inline function and can no longer nest,
 246  * assume that all struct fields passed to nested inline functions
 247  * are read dependencies
 248  */
 249 static void match_call_info(struct expression *expr)
 250 {
 251         struct expression *arg;
 252         int i;
 253 
 254         if (!__inline_fn || !cur_syscall)
 255                 return;
 256 
 257         // prefix(); printf("fn: %s\n", expr->fn->symbol->ident->name);
 258 
 259         i = 0;
 260         FOR_EACH_PTR(expr->args, arg) {
 261                 /*
 262                    if (arg->type == EXPR_DEREF)
 263                    printf("arg %d is deref\n", i);
 264                  */
 265                 print_read_member_type(arg);
 266                 i++;
 267         } END_FOR_EACH_PTR(arg);
 268 }
 269 
 270 static void match_assign_value(struct expression *expr)
 271 {
 272         if (!cur_syscall)
 273                 return;
 274         print_write_member_type(expr->left);
 275 }
 276 
 277 static void unop_expr(struct expression *expr)
 278 {
 279         if (!cur_syscall)
 280                 return;
 281 
 282         if (expr->op == SPECIAL_ADD_ASSIGN || expr->op == SPECIAL_INCREMENT ||
 283             expr->op == SPECIAL_SUB_ASSIGN || expr->op == SPECIAL_DECREMENT ||
 284             expr->op == SPECIAL_MUL_ASSIGN || expr->op == SPECIAL_DIV_ASSIGN ||
 285             expr->op == SPECIAL_MOD_ASSIGN || expr->op == SPECIAL_AND_ASSIGN ||
 286             expr->op == SPECIAL_OR_ASSIGN || expr->op == SPECIAL_XOR_ASSIGN ||
 287             expr->op == SPECIAL_SHL_ASSIGN || expr->op == SPECIAL_SHR_ASSIGN)
 288                 print_write_member_type(strip_expr(expr->unop));
 289 }
 290 
 291 void check_implicit_dependencies(int id)
 292 {
 293         my_id = id;
 294         ignore_structs = 0;
 295 
 296         if (option_project != PROJ_KERNEL)
 297                 return;
 298         if (!option_info)
 299                 return;
 300 
 301         add_hook(&match_syscall_definition, AFTER_DEF_HOOK);
 302         add_hook(&match_after_syscall, AFTER_FUNC_HOOK);
 303         add_hook(&match_condition, CONDITION_HOOK);
 304         add_hook(&match_call_info, FUNCTION_CALL_HOOK);
 305 
 306         /* hooks to track written fields */
 307         add_hook(&match_assign_value, ASSIGNMENT_HOOK_AFTER);
 308         add_hook(&unop_expr, OP_HOOK);
 309 }