1 /*
   2  * Copyright (C) 2017 Oracle.
   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 /*
  19  * Basically I see constraints as a way of saying "x <= some_limit".  The
  20  * problem is that smatch_capped is not granullar enough.
  21  *
  22  * This is mostly for finding out of bounds errors.  So there are different
  23  * types of constraints.  Quite often we have "foo->xxx[i] = 42;" and we want
  24  * to verify that "i" is less than foo->size.
  25  *
  26  * My idea was that we could automatically figure out these constraints.  And we
  27  * could load them in the DB so that they are the same every time.  As in a
  28  * constraint could be "< (struct whatever)->size" and give that in ID that
  29  * would be constant until you completely wiped the DB.  So when you do a normal
  30  * DB rebuild then the first thing it will do is preserve all the constraints.
  31  * I guess the reason to do it this way is to save space...  I sometimes suspect
  32  * that worrying about saving space is premature optimization.
  33  *
  34  * The other thing that I want to do a little bit different here is how I merge
  35  * constraints.  If a constraint is true on both sides, then that's normal.  If
  36  * we merge constraint 23 and 67 then we get constraint 23|67.  If we merge 23
  37  * with &undefined then we get &undefined.  We can also have two constraints
  38  * that are both true so we could have (45&23)|12 which means either both 45 and
  39  * 23 are true or 12 is true.
  40  *
  41  */
  42 
  43 
  44 #include "smatch.h"
  45 #include "smatch_extra.h"
  46 #include "smatch_slist.h"
  47 
  48 static int my_id;
  49 
  50 ALLOCATOR(constraint, "constraints");
  51 
  52 static void add_constraint(struct constraint_list **list, int op, int constraint)
  53 {
  54         struct constraint *tmp, *new;
  55 
  56         FOR_EACH_PTR(*list, tmp) {
  57                 if (tmp->id < constraint)
  58                         continue;
  59                 if (tmp->id == constraint) {
  60                         if (tmp->op == '<')
  61                                 return;
  62                         if (op == SPECIAL_LTE)
  63                                 return;
  64 
  65                         new = __alloc_constraint(0);
  66                         new->op = op;
  67                         new->id = constraint;
  68                         REPLACE_CURRENT_PTR(tmp, new);
  69                         return;
  70                 }
  71 
  72                 new = __alloc_constraint(0);
  73                 new->op = op;
  74                 new->id = constraint;
  75                 INSERT_CURRENT(new, tmp);
  76                 return;
  77         } END_FOR_EACH_PTR(tmp);
  78 
  79         new = __alloc_constraint(0);
  80         new->op = op;
  81         new->id = constraint;
  82         add_ptr_list(list, new);
  83 }
  84 
  85 static struct constraint_list *merge_constraint_lists(struct constraint_list *one, struct constraint_list *two)
  86 {
  87         struct constraint_list *ret = NULL;
  88         struct constraint *tmp;
  89 
  90         // FIXME: not || but &&
  91         FOR_EACH_PTR(one, tmp) {
  92                 add_constraint(&ret, tmp->op, tmp->id);
  93         } END_FOR_EACH_PTR(tmp);
  94 
  95         FOR_EACH_PTR(two, tmp) {
  96                 add_constraint(&ret, tmp->op, tmp->id);
  97         } END_FOR_EACH_PTR(tmp);
  98 
  99         return ret;
 100 }
 101 
 102 static struct constraint_list *clone_constraint_list(struct constraint_list *list)
 103 {
 104         struct constraint_list *ret = NULL;
 105         struct constraint *tmp;
 106 
 107         FOR_EACH_PTR(list, tmp) {
 108                 add_constraint(&ret, tmp->op, tmp->id);
 109         } END_FOR_EACH_PTR(tmp);
 110 
 111         return ret;
 112 }
 113 
 114 static struct smatch_state *alloc_constraint_state(struct constraint_list *list)
 115 {
 116         struct smatch_state *state;
 117         struct constraint *con;
 118         static char buf[256];
 119         int cnt = 0;
 120 
 121         FOR_EACH_PTR(list, con) {
 122                 if (cnt != 0)
 123                         cnt += snprintf(buf + cnt, sizeof(buf) - cnt, ", ");
 124                 cnt += snprintf(buf + cnt, sizeof(buf) - cnt, "%s%d",
 125                                 show_special(con->op), con->id);
 126         } END_FOR_EACH_PTR(con);
 127 
 128         state = __alloc_smatch_state(0);
 129         state->name = alloc_string(buf);
 130         state->data = list;
 131         return state;
 132 }
 133 
 134 static struct smatch_state *merge_func(struct smatch_state *s1, struct smatch_state *s2)
 135 {
 136         struct constraint_list *list;
 137 
 138         // FIXME:  use the dead code below instead
 139         if (strcmp(s1->name, s2->name) == 0)
 140                 return s1;
 141         return &merged;
 142 
 143         list = merge_constraint_lists(s1->data, s2->data);
 144         return alloc_constraint_state(list);
 145 }
 146 
 147 static int negate_gt(int op)
 148 {
 149         switch (op) {
 150         case '>':
 151         case SPECIAL_UNSIGNED_GT:
 152         case SPECIAL_GTE:
 153         case SPECIAL_UNSIGNED_GTE:
 154                 return negate_comparison(op);
 155         }
 156         return op;
 157 }
 158 
 159 static char *get_func_constraint(struct expression *expr)
 160 {
 161         char buf[256];
 162         char *name;
 163 
 164         if (is_fake_call(expr))
 165                 return NULL;
 166         name = expr_to_str(expr->fn);
 167         if (!name)
 168                 return NULL;
 169         snprintf(buf, sizeof(buf), "%s()", name);
 170         free_string(name);
 171         return alloc_string(buf);
 172 }
 173 
 174 static char *get_toplevel_name(struct expression *expr)
 175 {
 176         struct symbol *sym;
 177         char buf[256];
 178 
 179         expr = strip_expr(expr);
 180         if (expr->type != EXPR_SYMBOL || !expr->symbol || !expr->symbol->ident)
 181                 return NULL;
 182 
 183         sym = expr->symbol;
 184         if (!(sym->ctype.modifiers & MOD_TOPLEVEL))
 185                 return NULL;
 186 
 187         if (sym->ctype.modifiers & MOD_STATIC)
 188                 snprintf(buf, sizeof(buf), "%s %s", get_base_file(), sym->ident->name);
 189         else
 190                 snprintf(buf, sizeof(buf), "extern %s", sym->ident->name);
 191 
 192         return alloc_string(buf);
 193 }
 194 
 195 char *get_constraint_str(struct expression *expr)
 196 {
 197         char *name;
 198 
 199         if (!expr)
 200                 return NULL;
 201         if (expr->type == EXPR_CALL)
 202                 return get_func_constraint(expr);
 203         if (expr->type == EXPR_BINOP)
 204                 return expr_to_str(expr);
 205         name = get_toplevel_name(expr);
 206         if (name)
 207                 return name;
 208         return get_member_name(expr);
 209 }
 210 
 211 static int save_int_callback(void *_p, int argc, char **argv, char **azColName)
 212 {
 213         int *p = _p;
 214 
 215         *p = atoi(argv[0]);
 216         return 0;
 217 }
 218 
 219 static int constraint_str_to_id(const char *str)
 220 {
 221         int id = -1;
 222 
 223         run_sql(save_int_callback, &id,
 224                 "select id from constraints where str = '%q'", str);
 225 
 226         return id;
 227 }
 228 
 229 static int save_constraint_str(void *_str, int argc, char **argv, char **azColName)
 230 {
 231         char **str = _str;
 232 
 233         *str = alloc_string(argv[0]);
 234         return 0;
 235 }
 236 
 237 static char *constraint_id_to_str(int id)
 238 {
 239         char *str = NULL;
 240 
 241         run_sql(save_constraint_str, &str,
 242                 "select str from constraints where id = '%d'", id);
 243 
 244         return str;
 245 }
 246 
 247 static int save_op_callback(void *_p, int argc, char **argv, char **azColName)
 248 {
 249         int *p = _p;
 250 
 251         if (argv[0][0] == '<' && argv[0][1] == '=')
 252                 *p = SPECIAL_LTE;
 253         else
 254                 *p = '<';
 255         return 0;
 256 }
 257 
 258 static int save_str_callback(void *_p, int argc, char **argv, char **azColName)
 259 {
 260         char **p = _p;
 261 
 262         if (!*p) {
 263                 *p = alloc_string(argv[0]);
 264         } else {
 265                 char buf[256];
 266 
 267                 snprintf(buf, sizeof(buf), "%s, %s", *p, argv[0]);
 268                 *p = alloc_string(buf);
 269         }
 270         return 0;
 271 }
 272 
 273 char *get_required_constraint(const char *data_str)
 274 {
 275         char *required = NULL;
 276 
 277         run_sql(save_str_callback, &required,
 278                 "select bound from constraints_required where data = '%q'", data_str);
 279 
 280         return required;
 281 }
 282 
 283 static int get_required_op(char *data_str, char *con_str)
 284 {
 285         int op = 0;
 286 
 287         run_sql(save_op_callback, &op,
 288                 "select op from constraints_required where data = '%q' and bound = '%q'", data_str, con_str);
 289 
 290         return op;
 291 }
 292 
 293 char *unmet_constraint(struct expression *data, struct expression *offset)
 294 {
 295         struct smatch_state *state;
 296         struct constraint_list *list;
 297         struct constraint *con;
 298         char *data_str;
 299         char *required;
 300         int req_op;
 301 
 302         data_str = get_constraint_str(data);
 303         if (!data_str)
 304                 return NULL;
 305 
 306         required = get_required_constraint(data_str);
 307         if (!required)
 308                 goto free_data;
 309 
 310         state = get_state_expr(my_id, offset);
 311         if (!state)
 312                 goto free_data;
 313         list = state->data;
 314 
 315         /* check the list of bounds on our index against the list that work */
 316         FOR_EACH_PTR(list, con) {
 317                 char *con_str;
 318 
 319                 con_str = constraint_id_to_str(con->id);
 320                 if (!con_str) {
 321                         sm_msg("constraint %d not found", con->id);
 322                         continue;
 323                 }
 324 
 325                 req_op = get_required_op(data_str, con_str);
 326                 free_string(con_str);
 327                 if (!req_op)
 328                         continue;
 329                 if (con->op == '<' || con->op == req_op) {
 330                         free_string(required);
 331                         required = NULL;
 332                         goto free_data;
 333                 }
 334         } END_FOR_EACH_PTR(con);
 335 
 336 free_data:
 337         free_string(data_str);
 338         return required;
 339 }
 340 
 341 struct string_list *saved_constraints;
 342 static void save_new_constraint(const char *con)
 343 {
 344         if (list_has_string(saved_constraints, con))
 345                 return;
 346         insert_string(&saved_constraints, con);
 347         sql_save_constraint(con);
 348 }
 349 
 350 static void handle_comparison(struct expression *left, int op, struct expression *right)
 351 {
 352         struct constraint_list *constraints;
 353         struct smatch_state *state;
 354         char *constraint;
 355         int constraint_id;
 356         int orig_op = op;
 357         sval_t sval;
 358 
 359         /* known values are handled in smatch extra */
 360         if (get_value(left, &sval) || get_value(right, &sval))
 361                 return;
 362 
 363         if (local_debug)
 364                 sm_msg("COMPARE: %s %s %s", expr_to_str(left), show_special(op), expr_to_str(right));
 365 
 366         constraint = get_constraint_str(right);
 367         if (!constraint)
 368                 return;
 369         if (local_debug)
 370                 sm_msg("EXPR: %s CONSTRAINT %s", expr_to_str(right), constraint);
 371         constraint_id = constraint_str_to_id(constraint);
 372         if (local_debug)
 373                 sm_msg("CONSTRAINT ID %d", constraint_id);
 374         if (constraint_id < 0)
 375                 save_new_constraint(constraint);
 376         free_string(constraint);
 377         if (constraint_id < 0)
 378                 return;
 379 
 380         constraints = get_constraints(left);
 381         constraints = clone_constraint_list(constraints);
 382         op = negate_gt(orig_op);
 383         add_constraint(&constraints, remove_unsigned_from_comparison(op), constraint_id);
 384         state = alloc_constraint_state(constraints);
 385 
 386         if (op == orig_op) {
 387                 if (local_debug)
 388                         sm_msg("SETTING %s true %s", expr_to_str(left), state->name);
 389                 set_true_false_states_expr(my_id, left, state, NULL);
 390         } else {
 391                 if (local_debug)
 392                         sm_msg("SETTING %s false %s", expr_to_str(left), state->name);
 393 
 394                 set_true_false_states_expr(my_id, left, NULL, state);
 395         }
 396 }
 397 
 398 static void match_condition(struct expression *expr)
 399 {
 400         if (expr->type != EXPR_COMPARE)
 401                 return;
 402 
 403         if (expr->op == SPECIAL_EQUAL ||
 404             expr->op == SPECIAL_NOTEQUAL)
 405                 return;
 406 
 407         handle_comparison(expr->left, expr->op, expr->right);
 408         handle_comparison(expr->right, flip_comparison(expr->op), expr->left);
 409 }
 410 
 411 struct constraint_list *get_constraints(struct expression *expr)
 412 {
 413         struct smatch_state *state;
 414 
 415         state = get_state_expr(my_id, expr);
 416         if (!state)
 417                 return NULL;
 418         return state->data;
 419 }
 420 
 421 static void match_caller_info(struct expression *expr)
 422 {
 423         struct expression *tmp;
 424         struct smatch_state *state;
 425         int i;
 426 
 427         i = -1;
 428         FOR_EACH_PTR(expr->args, tmp) {
 429                 i++;
 430                 state = get_state_expr(my_id, tmp);
 431                 if (!state || state == &merged || state == &undefined)
 432                         continue;
 433                 sql_insert_caller_info(expr, CONSTRAINT, i, "$", state->name);
 434         } END_FOR_EACH_PTR(tmp);
 435 }
 436 
 437 static void struct_member_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
 438 {
 439         if (sm->state == &merged || sm->state == &undefined)
 440                 return;
 441         sql_insert_caller_info(call, CONSTRAINT, param, printed_name, sm->state->name);
 442 }
 443 
 444 static struct smatch_state *constraint_str_to_state(char *value)
 445 {
 446         struct constraint_list *list = NULL;
 447         char *p = value;
 448         int op;
 449         long long id;
 450 
 451         while (true) {
 452                 op = '<';
 453                 if (*p != '<')
 454                         return &undefined;
 455                 p++;
 456                 if (*p == '=') {
 457                         op = SPECIAL_LTE;
 458                         p++;
 459                 }
 460                 id = strtoll(p, &p, 10);
 461                 add_constraint(&list, op, id);
 462                 if (*p != ',')
 463                         break;
 464                 p++;
 465                 if (*p != ' ')
 466                         return &undefined;
 467         }
 468 
 469         return alloc_constraint_state(list);
 470 }
 471 
 472 static void set_param_constrained(const char *name, struct symbol *sym, char *key, char *value)
 473 {
 474         char fullname[256];
 475 
 476         if (strcmp(key, "*$") == 0)
 477                 snprintf(fullname, sizeof(fullname), "*%s", name);
 478         else if (strncmp(key, "$", 1) == 0)
 479                 snprintf(fullname, 256, "%s%s", name, key + 1);
 480         else
 481                 return;
 482 
 483         set_state(my_id, name, sym, constraint_str_to_state(value));
 484 }
 485 
 486 static void print_return_implies_constrained(int return_id, char *return_ranges, struct expression *expr)
 487 {
 488         struct smatch_state *orig;
 489         struct sm_state *sm;
 490         const char *param_name;
 491         int param;
 492 
 493         FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
 494                 if (sm->state == &merged || sm->state == &undefined)
 495                         continue;
 496 
 497                 param = get_param_num_from_sym(sm->sym);
 498                 if (param < 0)
 499                         continue;
 500 
 501                 orig = get_state_stree(get_start_states(), my_id, sm->name, sm->sym);
 502                 if (orig && strcmp(sm->state->name, orig->name) == 0)
 503                         continue;
 504 
 505                 param_name = get_param_name(sm);
 506                 if (!param_name)
 507                         continue;
 508 
 509                 sql_insert_return_states(return_id, return_ranges, CONSTRAINT,
 510                                          param, param_name, sm->state->name);
 511         } END_FOR_EACH_SM(sm);
 512 }
 513 
 514 static void db_returns_constrained(struct expression *expr, int param, char *key, char *value)
 515 {
 516         char *name;
 517         struct symbol *sym;
 518 
 519         name = return_state_to_var_sym(expr, param, key, &sym);
 520         if (!name || !sym)
 521                 goto free;
 522 
 523         set_state(my_id, name, sym, constraint_str_to_state(value));
 524 free:
 525         free_string(name);
 526 }
 527 
 528 void register_constraints(int id)
 529 {
 530         my_id = id;
 531 
 532         add_merge_hook(my_id, &merge_func);
 533         add_hook(&match_condition, CONDITION_HOOK);
 534 
 535         add_hook(&match_caller_info, FUNCTION_CALL_HOOK);
 536         add_member_info_callback(my_id, struct_member_callback);
 537         select_caller_info_hook(&set_param_constrained, CONSTRAINT);
 538 
 539         add_split_return_callback(print_return_implies_constrained);
 540         select_return_states_hook(CONSTRAINT, &db_returns_constrained);
 541 }