Print this page
11506 smatch resync

@@ -34,15 +34,16 @@
 #include "smatch_slist.h"
 #include "smatch_extra.h"
 
 static int my_id;
 static int link_id;
+extern int check_assigned_expr_id;
 
 static void match_link_modify(struct sm_state *sm, struct expression *mod_expr);
 
 struct string_list *__ignored_macros = NULL;
-static int in_warn_on_macro(void)
+int in_warn_on_macro(void)
 {
         struct statement *stmt;
         char *tmp;
         char *macro;
 

@@ -132,66 +133,127 @@
                 return NULL;
 
         return expr_to_var_sym(assigned->unop, new_sym);
 }
 
-char *get_other_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym)
+char *get_other_name_sym_from_chunk(const char *name, const char *chunk, int len, struct symbol *sym, struct symbol **new_sym)
 {
         struct expression *assigned;
         char *orig_name = NULL;
         char buf[256];
-        char *ret = NULL;
-        int skip;
+        char *ret;
 
-        *new_sym = NULL;
-
-        if (!sym || !sym->ident)
-                return NULL;
-
-        ret = get_pointed_at(name, sym, new_sym);
-        if (ret)
-                return ret;
-
-        skip = strlen(sym->ident->name);
-        if (name[skip] != '-' || name[skip + 1] != '>')
-                return NULL;
-        skip += 2;
-
-        assigned = get_assigned_expr_name_sym(sym->ident->name, sym);
+        assigned = get_assigned_expr_name_sym(chunk, sym);
         if (!assigned)
                 return NULL;
         if (assigned->type == EXPR_CALL)
                 return map_call_to_other_name_sym(name, sym, new_sym);
-        if (assigned->type == EXPR_PREOP || assigned->op == '&') {
+        if (assigned->type == EXPR_PREOP && assigned->op == '&') {
 
                 orig_name = expr_to_var_sym(assigned, new_sym);
                 if (!orig_name || !*new_sym)
                         goto free;
 
-                snprintf(buf, sizeof(buf), "%s.%s", orig_name + 1, name + skip);
+                snprintf(buf, sizeof(buf), "%s.%s", orig_name + 1, name + len);
                 ret = alloc_string(buf);
                 free_string(orig_name);
                 return ret;
         }
 
-        if (assigned->type != EXPR_DEREF)
-                goto free;
-
         orig_name = expr_to_var_sym(assigned, new_sym);
         if (!orig_name || !*new_sym)
                 goto free;
 
-        snprintf(buf, sizeof(buf), "%s->%s", orig_name, name + skip);
+        snprintf(buf, sizeof(buf), "%s->%s", orig_name, name + len);
         ret = alloc_string(buf);
         free_string(orig_name);
         return ret;
-
 free:
         free_string(orig_name);
         return NULL;
 }
 
+static char *get_long_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym)
+{
+        struct expression *tmp;
+        struct sm_state *sm;
+        char buf[256];
+
+        /*
+         * Just prepend the name with a different name/sym and return that.
+         * For example, if we set "foo->bar = bar;" then we clamp "bar->baz",
+         * that also clamps "foo->bar->baz".
+         *
+         */
+
+        FOR_EACH_MY_SM(check_assigned_expr_id, __get_cur_stree(), sm) {
+                tmp = sm->state->data;
+                if (!tmp || tmp->type != EXPR_SYMBOL)
+                        continue;
+                if (tmp->symbol == sym)
+                        goto found;
+        } END_FOR_EACH_SM(sm);
+
+        return NULL;
+
+found:
+        snprintf(buf, sizeof(buf), "%s%s", sm->name, name + tmp->symbol->ident->len);
+        *new_sym = sm->sym;
+        return alloc_string(buf);
+}
+
+char *get_other_name_sym_helper(const char *name, struct symbol *sym, struct symbol **new_sym, bool use_stack)
+{
+        char buf[256];
+        char *ret;
+        int len;
+
+        *new_sym = NULL;
+
+        if (!sym || !sym->ident)
+                return NULL;
+
+        ret = get_pointed_at(name, sym, new_sym);
+        if (ret)
+                return ret;
+
+        ret = map_long_to_short_name_sym(name, sym, new_sym, use_stack);
+        if (ret)
+                return ret;
+
+        len = snprintf(buf, sizeof(buf), "%s", name);
+        if (len >= sizeof(buf) - 2)
+                return NULL;
+
+        while (len >= 1) {
+                if (buf[len] == '>' && buf[len - 1] == '-') {
+                        len--;
+                        buf[len] = '\0';
+                        ret = get_other_name_sym_from_chunk(name, buf, len + 2, sym, new_sym);
+                        if (ret)
+                                return ret;
+                }
+                len--;
+        }
+
+        ret = get_long_name_sym(name, sym, new_sym);
+        if (ret)
+                return ret;
+
+        return NULL;
+}
+
+char *get_other_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym)
+{
+        return get_other_name_sym_helper(name, sym, new_sym, true);
+}
+
+char *get_other_name_sym_nostack(const char *name, struct symbol *sym, struct symbol **new_sym)
+{
+        return get_other_name_sym_helper(name, sym, new_sym, false);
+}
+
 void set_extra_mod(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
 {
         char *new_name;
         struct symbol *new_sym;
 

@@ -299,12 +361,10 @@
 
         set_related(state, estate_related(orig_state));
         FOR_EACH_PTR(estate_related(orig_state), rel) {
                 struct smatch_state *estate;
 
-                if (option_debug_related)
-                        sm_msg("%s updating related %s to %s", name, rel->name, state->name);
                 estate = get_state(SMATCH_EXTRA, rel->name, rel->sym);
                 if (!estate)
                         continue;
                 set_extra_nomod_helper(rel->name, rel->sym, expr, clone_estate_cast(estate_type(estate), state));
         } END_FOR_EACH_PTR(rel);

@@ -482,10 +542,11 @@
 
 static struct sm_state *handle_canonical_while_count_down(struct statement *loop)
 {
         struct expression *iter_var;
         struct expression *condition, *unop;
+        struct symbol *type;
         struct sm_state *sm;
         struct smatch_state *estate;
         int op;
         sval_t start, right;
 

@@ -505,10 +566,15 @@
         if (!sm)
                 return NULL;
         if (sval_cmp(estate_min(sm->state), right) < 0)
                 return NULL;
         start = estate_max(sm->state);
+
+        type = get_type(iter_var);
+        right = sval_cast(type, right);
+        start = sval_cast(type, start);
+
         if  (sval_cmp(start, right) <= 0)
                 return NULL;
         if (!sval_is_max(start))
                 start.value--;
 

@@ -538,21 +604,20 @@
 {
         struct expression *iter_var;
         struct sm_state *sm;
         struct smatch_state *estate;
         sval_t start, end, max;
+        struct symbol *type;
 
         iter_var = iter_expr->unop;
         sm = get_sm_state_expr(SMATCH_EXTRA, iter_var);
         if (!sm)
                 return NULL;
         if (!estate_get_single_value(sm->state, &start))
                 return NULL;
-        if (get_implied_max(condition->right, &end))
-                end = sval_cast(get_type(iter_var), end);
-        else
-                end = sval_type_max(get_type(iter_var));
+        if (!get_implied_value(condition->right, &end))
+                return NULL;
 
         if (get_sm_state_expr(SMATCH_EXTRA, condition->left) != sm)
                 return NULL;
 
         switch (condition->op) {

@@ -568,17 +633,22 @@
         default:
                 return NULL;
         }
         if (sval_cmp(end, start) < 0)
                 return NULL;
+        type = get_type(iter_var);
+        start = sval_cast(type, start);
+        end = sval_cast(type, end);
         estate = alloc_estate_range(start, end);
         if (get_hard_max(condition->right, &max)) {
+                if (!get_macro_name(condition->pos))
                 estate_set_hard_max(estate);
                 if (condition->op == '<' ||
                     condition->op == SPECIAL_UNSIGNED_LT ||
                     condition->op == SPECIAL_NOTEQUAL)
                         max.value--;
+                max = sval_cast(type, max);
                 estate_set_fuzzy_max(estate, max);
         }
         set_extra_expr_mod(iter_var, estate);
         return get_sm_state_expr(SMATCH_EXTRA, iter_var);
 }

@@ -597,17 +667,18 @@
                 return NULL;
         if (!estate_get_single_value(sm->state, &start))
                 return NULL;
         if (!get_implied_min(condition->right, &end))
                 end = sval_type_min(get_type(iter_var));
+        end = sval_cast(estate_type(sm->state), end);
         if (get_sm_state_expr(SMATCH_EXTRA, condition->left) != sm)
                 return NULL;
 
         switch (condition->op) {
         case SPECIAL_NOTEQUAL:
         case '>':
-                if (!sval_is_min(end) && !sval_is_max(end))
+                if (!sval_is_max(end))
                         end.value++;
                 break;
         case SPECIAL_GTE:
                 break;
         default:

@@ -712,10 +783,11 @@
                                    sval_type_val(estate_type(sm->state), 1));
         } else {
                 limit = sval_binop(estate_min(sm->state), '-',
                                    sval_type_val(estate_type(sm->state), 1));
         }
+        limit = sval_cast(estate_type(sm->state), limit);
         if (!estate_has_hard_max(sm->state) && !__has_breaks()) {
                 if (iter_expr->op == SPECIAL_INCREMENT)
                         state = alloc_estate_range(estate_min(sm->state), limit);
                 else
                         state = alloc_estate_range(limit, estate_max(sm->state));

@@ -736,22 +808,38 @@
         }
 
         set_extra_mod(sm->name, sm->sym, iter_expr, state);
 }
 
+static bool get_global_rl(const char *name, struct symbol *sym, struct range_list **rl)
+{
+        struct expression *expr;
+
+        if (!sym || !(sym->ctype.modifiers & MOD_TOPLEVEL) || !sym->ident)
+                return false;
+        if (strcmp(sym->ident->name, name) != 0)
+                return false;
+
+        expr = symbol_expression(sym);
+        return get_implied_rl(expr, rl);
+}
+
 static struct stree *unmatched_stree;
 static struct smatch_state *unmatched_state(struct sm_state *sm)
 {
         struct smatch_state *state;
+        struct range_list *rl;
 
         if (unmatched_stree) {
                 state = get_state_stree(unmatched_stree, SMATCH_EXTRA, sm->name, sm->sym);
                 if (state)
                         return state;
         }
         if (parent_is_gone_var_sym(sm->name, sm->sym))
                 return alloc_estate_empty();
+        if (get_global_rl(sm->name, sm->sym, &rl))
+                return alloc_estate_rl(rl);
         return alloc_estate_whole(estate_type(sm->state));
 }
 
 static void clear_the_pointed_at(struct expression *expr)
 {

@@ -813,19 +901,21 @@
                 else
                         clear_the_pointed_at(tmp);
         } END_FOR_EACH_PTR(arg);
 }
 
-static int values_fit_type(struct expression *left, struct expression *right)
+int values_fit_type(struct expression *left, struct expression *right)
 {
         struct range_list *rl;
         struct symbol *type;
 
         type = get_type(left);
         if (!type)
                 return 0;
         get_absolute_rl(right, &rl);
+        if (type == rl_type(rl))
+                return 1;
         if (type_unsigned(type) && sval_is_negative(rl_min(rl)))
                 return 0;
         if (sval_cmp(sval_type_min(type), rl_min(rl)) > 0)
                 return 0;
         if (sval_cmp(sval_type_max(type), rl_max(rl)) < 0)

@@ -949,11 +1039,11 @@
 
                 state = alloc_estate_rl(alloc_whole_rl(left_type));
                 goto done;
         }
 
-        comparison = get_comparison(left, right);
+        comparison = get_comparison_no_extra(left, right);
         if (comparison) {
                 comparison = flip_comparison(comparison);
                 get_implied_rl(left, &orig_rl);
         }
 

@@ -978,50 +1068,19 @@
         set_extra_mod(name, sym, left, state);
 free:
         free_string(right_name);
 }
 
-static int op_remove_assign(int op)
-{
-        switch (op) {
-        case SPECIAL_ADD_ASSIGN:
-                return '+';
-        case SPECIAL_SUB_ASSIGN:
-                return '-';
-        case SPECIAL_MUL_ASSIGN:
-                return '*';
-        case SPECIAL_DIV_ASSIGN:
-                return '/';
-        case SPECIAL_MOD_ASSIGN:
-                return '%';
-        case SPECIAL_AND_ASSIGN:
-                return '&';
-        case SPECIAL_OR_ASSIGN:
-                return '|';
-        case SPECIAL_XOR_ASSIGN:
-                return '^';
-        case SPECIAL_SHL_ASSIGN:
-                return SPECIAL_LEFTSHIFT;
-        case SPECIAL_SHR_ASSIGN:
-                return SPECIAL_RIGHTSHIFT;
-        default:
-                return op;
-        }
-}
-
 static void match_assign(struct expression *expr)
 {
         struct range_list *rl = NULL;
         struct expression *left;
         struct expression *right;
         struct expression *binop_expr;
         struct symbol *left_type;
         struct symbol *sym;
         char *name;
-        sval_t left_min, left_max;
-        sval_t right_min, right_max;
-        sval_t res_min, res_max;
 
         left = strip_expr(expr->left);
 
         right = strip_parens(expr->right);
         if (right->type == EXPR_CALL && sym_name_is("__builtin_expect", right->fn))

@@ -1042,49 +1101,13 @@
         if (!name)
                 return;
 
         left_type = get_type(left);
 
-        res_min = sval_type_min(left_type);
-        res_max = sval_type_max(left_type);
-
         switch (expr->op) {
         case SPECIAL_ADD_ASSIGN:
-                get_absolute_max(left, &left_max);
-                get_absolute_max(right, &right_max);
-                if (sval_binop_overflows(left_max, '+', sval_cast(left_type, right_max)))
-                        break;
-                if (get_implied_min(left, &left_min) &&
-                    !sval_is_negative_min(left_min) &&
-                    get_implied_min(right, &right_min) &&
-                    !sval_is_negative_min(right_min)) {
-                        res_min = sval_binop(left_min, '+', right_min);
-                        res_min = sval_cast(left_type, res_min);
-                }
-                if (inside_loop())  /* we are assuming loops don't lead to wrapping */
-                        break;
-                res_max = sval_binop(left_max, '+', right_max);
-                res_max = sval_cast(left_type, res_max);
-                break;
         case SPECIAL_SUB_ASSIGN:
-                if (get_implied_max(left, &left_max) &&
-                    !sval_is_max(left_max) &&
-                    get_implied_min(right, &right_min) &&
-                    !sval_is_min(right_min)) {
-                        res_max = sval_binop(left_max, '-', right_min);
-                        res_max = sval_cast(left_type, res_max);
-                }
-                if (inside_loop())
-                        break;
-                if (get_implied_min(left, &left_min) &&
-                    !sval_is_min(left_min) &&
-                    get_implied_max(right, &right_max) &&
-                    !sval_is_max(right_max)) {
-                        res_min = sval_binop(left_min, '-', right_max);
-                        res_min = sval_cast(left_type, res_min);
-                }
-                break;
         case SPECIAL_AND_ASSIGN:
         case SPECIAL_MOD_ASSIGN:
         case SPECIAL_SHL_ASSIGN:
         case SPECIAL_SHR_ASSIGN:
         case SPECIAL_OR_ASSIGN:

@@ -1092,19 +1115,27 @@
         case SPECIAL_MUL_ASSIGN:
         case SPECIAL_DIV_ASSIGN:
                 binop_expr = binop_expression(expr->left,
                                               op_remove_assign(expr->op),
                                               expr->right);
-                if (get_absolute_rl(binop_expr, &rl)) {
+                get_absolute_rl(binop_expr, &rl);
                         rl = cast_rl(left_type, rl);
-                        set_extra_mod(name, sym, left, alloc_estate_rl(rl));
-                        goto free;
+                if (inside_loop()) {
+                        if (expr->op == SPECIAL_ADD_ASSIGN)
+                                add_range(&rl, rl_max(rl), sval_type_max(rl_type(rl)));
+
+                        if (expr->op == SPECIAL_SUB_ASSIGN &&
+                            !sval_is_negative(rl_min(rl))) {
+                                sval_t zero = { .type = rl_type(rl) };
+
+                                add_range(&rl, rl_min(rl), zero);
                 }
-                break;
         }
-        rl = cast_rl(left_type, alloc_rl(res_min, res_max));
         set_extra_mod(name, sym, left, alloc_estate_rl(rl));
+                goto free;
+        }
+        set_extra_mod(name, sym, left, alloc_estate_whole(left_type));
 free:
         free_string(name);
 }
 
 static struct smatch_state *increment_state(struct smatch_state *state)

@@ -1232,11 +1263,18 @@
                 rl = rl_intersection(estate_rl(state), valid_ptr_rl);
                 if (rl_equiv(rl, estate_rl(state)))
                         return;
                 set_extra_expr_nomod(expr, alloc_estate_rl(rl));
         } else {
-                set_extra_expr_nomod(expr, alloc_estate_range(valid_ptr_min_sval, valid_ptr_max_sval));
+                struct range_list *rl;
+
+                if (get_mtag_rl(expr, &rl))
+                        rl = rl_intersection(rl, valid_ptr_rl);
+                else
+                        rl = clone_rl(valid_ptr_rl);
+
+                set_extra_expr_nomod(expr, alloc_estate_rl(rl));
         }
 }
 
 static void match_dereferences(struct expression *expr)
 {

@@ -1300,10 +1338,11 @@
 static int handle_postop_inc(struct expression *left, int op, struct expression *right)
 {
         struct statement *stmt;
         struct expression *cond;
         struct smatch_state *true_state, *false_state;
+        struct symbol *type;
         sval_t start;
         sval_t limit;
 
         /*
          * If we're decrementing here then that's a canonical while count down

@@ -1331,11 +1370,12 @@
 
         if (!get_implied_value(left->unop, &start))
                 return 0;
         if (!get_implied_value(right, &limit))
                 return 0;
-
+        type = get_type(left->unop);
+        limit = sval_cast(type, limit);
         if (sval_cmp(start, limit) > 0)
                 return 0;
 
         switch (op) {
         case '<':

@@ -1369,10 +1409,21 @@
         if (state && !estate_rl(state))
                 return true;
         return false;
 }
 
+static bool in_macro(struct expression *left, struct expression *right)
+{
+        if (!left || !right)
+                return 0;
+        if (left->pos.line != right->pos.line || left->pos.pos != right->pos.pos)
+                return 0;
+        if (get_macro_name(left->pos))
+                return 1;
+        return 0;
+}
+
 static void handle_comparison(struct symbol *type, struct expression *left, int op, struct expression *right)
 {
         struct range_list *left_orig;
         struct range_list *left_true;
         struct range_list *left_false;

@@ -1451,22 +1502,22 @@
         switch (op) {
         case '<':
         case SPECIAL_UNSIGNED_LT:
         case SPECIAL_UNSIGNED_LTE:
         case SPECIAL_LTE:
-                if (get_hard_max(right, &dummy))
+                if (get_implied_value(right, &dummy) && !in_macro(left, right))
                         estate_set_hard_max(left_true_state);
-                if (get_hard_max(left, &dummy))
+                if (get_implied_value(left, &dummy) && !in_macro(left, right))
                         estate_set_hard_max(right_false_state);
                 break;
         case '>':
         case SPECIAL_UNSIGNED_GT:
         case SPECIAL_UNSIGNED_GTE:
         case SPECIAL_GTE:
-                if (get_hard_max(left, &dummy))
+                if (get_implied_value(left, &dummy) && !in_macro(left, right))
                         estate_set_hard_max(right_true_state);
-                if (get_hard_max(right, &dummy))
+                if (get_implied_value(right, &dummy) && !in_macro(left, right))
                         estate_set_hard_max(left_false_state);
                 break;
         }
 
         switch (op) {

@@ -1596,10 +1647,49 @@
                 return 1;
         }
         return 0;
 }
 
+static int flip_op(int op)
+{
+        /* We only care about simple math */
+        switch (op) {
+        case '+':
+                return '-';
+        case '-':
+                return '+';
+        case '*':
+                return '/';
+        }
+        return 0;
+}
+
+static void move_known_to_rl(struct expression **expr_p, struct range_list **rl_p)
+{
+        struct expression *expr = *expr_p;
+        struct range_list *rl = *rl_p;
+        sval_t sval;
+
+        if (!is_simple_math(expr))
+                return;
+
+        if (get_implied_value(expr->right, &sval)) {
+                *expr_p = expr->left;
+                *rl_p = rl_binop(rl, flip_op(expr->op), alloc_rl(sval, sval));
+                move_known_to_rl(expr_p, rl_p);
+                return;
+        }
+        if (expr->op == '-')
+                return;
+        if (get_implied_value(expr->left, &sval)) {
+                *expr_p = expr->right;
+                *rl_p = rl_binop(rl, flip_op(expr->op), alloc_rl(sval, sval));
+                move_known_to_rl(expr_p, rl_p);
+                return;
+        }
+}
+
 static void move_known_values(struct expression **left_p, struct expression **right_p)
 {
         struct expression *left = *left_p;
         struct expression *right = *right_p;
         sval_t sval, dummy;

@@ -1704,37 +1794,16 @@
 
 static int match_func_comparison(struct expression *expr)
 {
         struct expression *left = strip_expr(expr->left);
         struct expression *right = strip_expr(expr->right);
-        sval_t sval;
 
-        /*
-         * fixme: think about this harder. We should always be trying to limit
-         * the non-call side as well.  If we can't determine the limitter does
-         * that mean we aren't querying the database and are missing important
-         * information?
-         */
-
-        if (left->type == EXPR_CALL) {
-                if (get_implied_value(left, &sval)) {
-                        handle_comparison(get_type(expr), left, expr->op, right);
-                        return 1;
-                }
+        if (left->type == EXPR_CALL || right->type == EXPR_CALL) {
                 function_comparison(left, expr->op, right);
                 return 1;
         }
 
-        if (right->type == EXPR_CALL) {
-                if (get_implied_value(right, &sval)) {
-                        handle_comparison(get_type(expr), left, expr->op, right);
-                        return 1;
-                }
-                function_comparison(left, expr->op, right);
-                return 1;
-        }
-
         return 0;
 }
 
 /* Handle conditions like "if (foo + bar < foo) {" */
 static int handle_integer_overflow_test(struct expression *expr)

@@ -1770,10 +1839,14 @@
 
         get_absolute_min(left->left, &left_min);
         get_absolute_min(left->right, &right_min);
         min = sval_binop(left_min, '+', right_min);
 
+        type = get_type(left);
+        min = sval_cast(type, min);
+        max = sval_cast(type, max);
+
         set_extra_chunk_true_false(left, NULL, alloc_estate_range(min, max));
         return 1;
 }
 
 static void match_comparison(struct expression *expr)

@@ -1868,10 +1941,55 @@
 
         }
         return ret;
 }
 
+static bool handle_bit_test(struct expression *expr)
+{
+        struct range_list *orig_rl, *rl;
+        struct expression *shift, *mask, *var;
+        struct bit_info *bit_info;
+        sval_t sval;
+        sval_t high = { .type = &int_ctype };
+        sval_t low = { .type = &int_ctype };
+
+        shift = strip_expr(expr->right);
+        mask = strip_expr(expr->left);
+        if (shift->type != EXPR_BINOP || shift->op != SPECIAL_LEFTSHIFT) {
+                shift = strip_expr(expr->left);
+                mask = strip_expr(expr->right);
+                if (shift->type != EXPR_BINOP || shift->op != SPECIAL_LEFTSHIFT)
+                        return false;
+        }
+        if (!get_implied_value(shift->left, &sval) || sval.value != 1)
+                return false;
+        var = strip_expr(shift->right);
+
+        bit_info = get_bit_info(mask);
+        if (!bit_info)
+                return false;
+        if (!bit_info->possible)
+                return false;
+
+        get_absolute_rl(var, &orig_rl);
+        if (sval_is_negative(rl_min(orig_rl)) ||
+            rl_max(orig_rl).uvalue > type_bits(get_type(shift->left)))
+                return false;
+
+        low.value = ffsll(bit_info->possible);
+        high.value = sm_fls64(bit_info->possible);
+        rl = alloc_rl(low, high);
+        rl = cast_rl(get_type(var), rl);
+        rl = rl_intersection(orig_rl, rl);
+        if (!rl)
+                return false;
+
+        set_extra_expr_true_false(shift->right, alloc_estate_rl(rl), NULL);
+
+        return true;
+}
+
 static void handle_AND_op(struct expression *var, sval_t known)
 {
         struct range_list *orig_rl;
         struct range_list *true_rl = NULL;
         struct range_list *false_rl = NULL;

@@ -1914,10 +2032,13 @@
 
 static void handle_AND_condition(struct expression *expr)
 {
         sval_t known;
 
+        if (handle_bit_test(expr))
+                return;
+
         if (get_implied_value(expr->left, &known))
                 handle_AND_op(expr->right, known);
         else if (get_implied_value(expr->right, &known))
                 handle_AND_op(expr->left, known);
 }

@@ -1926,11 +2047,11 @@
 {
         struct range_list *orig_rl;
         struct range_list *true_rl;
         struct range_list *false_rl = NULL;
         sval_t right;
-        sval_t zero;
+        sval_t zero = { 0, };
 
         if (!get_implied_value(expr->right, &right) || right.value == 0)
                 return;
         get_absolute_rl(expr->left, &orig_rl);
 

@@ -1986,43 +2107,20 @@
 }
 
 /* this is actually hooked from smatch_implied.c...  it's hacky, yes */
 void __extra_match_condition(struct expression *expr)
 {
-        struct smatch_state *pre_state;
-        struct smatch_state *true_state;
-        struct smatch_state *false_state;
-        struct range_list *pre_rl;
-
         expr = strip_expr(expr);
         switch (expr->type) {
         case EXPR_CALL:
                 function_comparison(expr, SPECIAL_NOTEQUAL, zero_expr());
                 return;
         case EXPR_PREOP:
         case EXPR_SYMBOL:
-        case EXPR_DEREF: {
-                sval_t zero;
-
-                zero = sval_blank(expr);
-                zero.value = 0;
-
-                pre_state = get_extra_state(expr);
-                if (estate_is_empty(pre_state))
+        case EXPR_DEREF:
+                handle_comparison(get_type(expr), expr, SPECIAL_NOTEQUAL, zero_expr());
                         return;
-                if (pre_state)
-                        pre_rl = estate_rl(pre_state);
-                else
-                        get_absolute_rl(expr, &pre_rl);
-                if (possibly_true_rl(pre_rl, SPECIAL_EQUAL, rl_zero()))
-                        false_state = alloc_estate_sval(zero);
-                else
-                        false_state = alloc_estate_empty();
-                true_state = alloc_estate_rl(remove_range(pre_rl, zero, zero));
-                set_extra_expr_true_false(expr, true_state, false_state);
-                return;
-        }
         case EXPR_COMPARE:
                 match_comparison(expr);
                 return;
         case EXPR_ASSIGNMENT:
                 __extra_match_condition(expr->left);

@@ -2151,13 +2249,49 @@
 {
         *(int *)found = 1;
         return 0;
 }
 
-static int filter_unused_kzalloc_info(struct expression *call, int param, char *printed_name, struct sm_state *sm)
+static int is_kzalloc_info(struct sm_state *sm)
 {
         sval_t sval;
+
+        /*
+         * kzalloc() information is treated as special because so there is just
+         * a lot of stuff initialized to zero and it makes building the database
+         * take hours and hours.
+         *
+         * In theory, we should just remove this line and not pass any unused
+         * information, but I'm not sure enough that this code works so I want
+         * to hold off on that for now.
+         */
+        if (!estate_get_single_value(sm->state, &sval))
+                return 0;
+        if (sval.value != 0)
+                return 0;
+        return 1;
+}
+
+static int is_really_long(struct sm_state *sm)
+{
+        const char *p;
+        int cnt = 0;
+
+        p = sm->name;
+        while ((p = strstr(p, "->"))) {
+                p += 2;
+                cnt++;
+        }
+
+        if (cnt < 3 ||
+            strlen(sm->name) < 40)
+                return 0;
+        return 1;
+}
+
+static int filter_unused_param_value_info(struct expression *call, int param, char *printed_name, struct sm_state *sm)
+{
         int found = 0;
 
         /* for function pointers assume everything is used */
         if (call->fn->type != EXPR_SYMBOL)
                 return 0;

@@ -2168,20 +2302,11 @@
          *
          */
         if (!call->fn->symbol)
                 return 0;
 
-        /*
-         * kzalloc() information is treated as special because so there is just
-         * a lot of stuff initialized to zero and it makes building the database
-         * take hours and hours.
-         *
-         * In theory, we should just remove this line and not pass any unused
-         * information, but I'm not sure enough that this code works so I want
-         * to hold off on that for now.
-         */
-        if (!estate_get_single_value(sm->state, &sval) || sval.value != 0)
+        if (!is_kzalloc_info(sm) && !is_really_long(sm))
                 return 0;
 
         run_sql(&param_used_callback, &found,
                 "select * from return_implies where %s and type = %d and parameter = %d and key = '%s';",
                 get_static_filter(call->fn->symbol), PARAM_USED, param, printed_name);

@@ -2244,54 +2369,72 @@
 }
 
 static void struct_member_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
 {
         struct range_list *rl;
+        sval_t dummy;
 
         if (estate_is_whole(sm->state))
                 return;
-        if (filter_unused_kzalloc_info(call, param, printed_name, sm))
+        if (filter_unused_param_value_info(call, param, printed_name, sm))
                 return;
         rl = estate_rl(sm->state);
         rl = intersect_with_real_abs_var_sym(sm->name, sm->sym, rl);
         sql_insert_caller_info(call, PARAM_VALUE, param, printed_name, show_rl(rl));
+        if (!estate_get_single_value(sm->state, &dummy)) {
+                if (estate_has_hard_max(sm->state))
+                        sql_insert_caller_info(call, HARD_MAX, param, printed_name,
+                                               sval_to_str(estate_max(sm->state)));
         if (estate_has_fuzzy_max(sm->state))
                 sql_insert_caller_info(call, FUZZY_MAX, param, printed_name,
                                        sval_to_str(estate_get_fuzzy_max(sm->state)));
+        }
 }
 
 static void returned_struct_members(int return_id, char *return_ranges, struct expression *expr)
 {
         struct symbol *returned_sym;
+        char *returned_name;
         struct sm_state *sm;
-        const char *param_name;
         char *compare_str;
-        char buf[256];
+        char name_buf[256];
+        char val_buf[256];
+        int len;
 
-        returned_sym = expr_to_sym(expr);
-        if (!returned_sym)
+        // FIXME handle *$
+
+        if (!is_pointer(expr))
                 return;
 
+        returned_name = expr_to_var_sym(expr, &returned_sym);
+        if (!returned_name || !returned_sym)
+                goto free;
+        len = strlen(returned_name);
+
         FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
                 if (!estate_rl(sm->state))
                         continue;
                 if (returned_sym != sm->sym)
                         continue;
-
-                param_name = get_param_name(sm);
-                if (!param_name)
+                if (strncmp(returned_name, sm->name, len) != 0)
                         continue;
-                if (strcmp(param_name, "$") == 0)
+                if (sm->name[len] != '-')
                         continue;
+
+                snprintf(name_buf, sizeof(name_buf), "$%s", sm->name + len);
+
                 compare_str = name_sym_to_param_comparison(sm->name, sm->sym);
                 if (!compare_str && estate_is_whole(sm->state))
                         continue;
-                snprintf(buf, sizeof(buf), "%s%s", sm->state->name, compare_str ?: "");
+                snprintf(val_buf, sizeof(val_buf), "%s%s", sm->state->name, compare_str ?: "");
 
                 sql_insert_return_states(return_id, return_ranges, PARAM_VALUE,
-                                         -1, param_name, buf);
+                                         -1, name_buf, val_buf);
         } END_FOR_EACH_SM(sm);
+
+free:
+        free_string(returned_name);
 }
 
 static void db_limited_before(void)
 {
         unmatched_stree = clone_stree(__get_cur_stree());

@@ -2300,22 +2443,10 @@
 static void db_limited_after(void)
 {
         free_stree(&unmatched_stree);
 }
 
-static int rl_fits_in_type(struct range_list *rl, struct symbol *type)
-{
-        if (type_bits(rl_type(rl)) <= type_bits(type))
-                return 1;
-        if (sval_cmp(rl_max(rl), sval_type_max(type)) > 0)
-                return 0;
-        if (sval_is_negative(rl_min(rl)) &&
-            sval_cmp(rl_min(rl), sval_type_min(type)) < 0)
-                return 0;
-        return 1;
-}
-
 static int basically_the_same(struct range_list *orig, struct range_list *new)
 {
         if (rl_equiv(orig, new))
                 return 1;
 

@@ -2361,12 +2492,12 @@
         struct sm_state *sm;
         struct symbol *compare_type, *var_type;
         struct range_list *rl;
         struct range_list *limit;
         struct range_list *new;
-        char *tmp_name;
-        struct symbol *tmp_sym;
+        char *other_name;
+        struct symbol *other_sym;
 
         while (expr->type == EXPR_ASSIGNMENT)
                 expr = strip_expr(expr->right);
         if (expr->type != EXPR_CALL)
                 return;

@@ -2373,51 +2504,55 @@
 
         arg = get_argument_from_call_expr(expr->args, param);
         if (!arg)
                 return;
 
+        if (strcmp(key, "$") == 0)
+                compare_type = get_arg_type(expr->fn, param);
+        else
+                compare_type = get_member_type_from_key(arg, key);
+
+        call_results_to_rl(expr, compare_type, value, &limit);
+        if (strcmp(key, "$") == 0)
+                move_known_to_rl(&arg, &limit);
         name = get_chunk_from_key(arg, key, &sym, &vsl);
         if (!name)
                 return;
         if (op != PARAM_LIMIT && !sym)
                 goto free;
 
-        if (strcmp(key, "$") == 0)
-                compare_type = get_arg_type(expr->fn, param);
-        else
-                compare_type = get_member_type_from_key(arg, key);
-
         sm = get_sm_state(SMATCH_EXTRA, name, sym);
         if (sm)
                 rl = estate_rl(sm->state);
         else
                 rl = alloc_whole_rl(compare_type);
 
         if (op == PARAM_LIMIT && !rl_fits_in_type(rl, compare_type))
                 goto free;
 
-        call_results_to_rl(expr, compare_type, value, &limit);
         new = rl_intersection(rl, limit);
 
         var_type = get_member_type_from_key(arg, key);
         new = cast_rl(var_type, new);
 
         /* We want to preserve the implications here */
-        if (sm && basically_the_same(estate_rl(sm->state), new))
+        if (sm && basically_the_same(rl, new))
                 goto free;
-        tmp_name = map_long_to_short_name_sym(name, sym, &tmp_sym);
-        if (tmp_name && tmp_sym) {
-                free_string(name);
-                name = tmp_name;
-                sym = tmp_sym;
-        }
+        other_name = get_other_name_sym(name, sym, &other_sym);
 
         if (op == PARAM_LIMIT)
                 set_extra_nomod_vsl(name, sym, vsl, NULL, alloc_estate_rl(new));
         else
                 set_extra_mod(name, sym, NULL, alloc_estate_rl(new));
 
+        if (other_name && other_sym) {
+                if (op == PARAM_LIMIT)
+                        set_extra_nomod_vsl(other_name, other_sym, vsl, NULL, alloc_estate_rl(new));
+                else
+                        set_extra_mod(other_name, other_sym, NULL, alloc_estate_rl(new));
+        }
+
         if (op == PARAM_LIMIT && arg->type == EXPR_BINOP)
                 db_param_limit_binops(arg, key, new);
 free:
         free_string(name);
 }

@@ -2433,12 +2568,13 @@
 }
 
 static void db_param_add_set(struct expression *expr, int param, char *key, char *value, enum info_type op)
 {
         struct expression *arg;
-        char *name, *tmp_name;
-        struct symbol *sym, *tmp_sym;
+        char *name;
+        char *other_name = NULL;
+        struct symbol *sym, *other_sym;
         struct symbol *param_type, *arg_type;
         struct smatch_state *state;
         struct range_list *new = NULL;
         struct range_list *added = NULL;
 

@@ -2466,18 +2602,16 @@
         if (op == PARAM_SET)
                 new = added;
         else
                 new = rl_union(new, added);
 
-        tmp_name = map_long_to_short_name_sym_nostack(name, sym, &tmp_sym);
-        if (tmp_name && tmp_sym) {
-                free_string(name);
-                name = tmp_name;
-                sym = tmp_sym;
-        }
+        other_name = get_other_name_sym_nostack(name, sym, &other_sym);
         set_extra_mod(name, sym, NULL, alloc_estate_rl(new));
+        if (other_name && other_sym)
+                set_extra_mod(other_name, other_sym, NULL, alloc_estate_rl(new));
 free:
+        free_string(other_name);
         free_string(name);
 }
 
 static void db_param_add(struct expression *expr, int param, char *key, char *value)
 {

@@ -2491,10 +2625,28 @@
         in_param_set = true;
         db_param_add_set(expr, param, key, value, PARAM_SET);
         in_param_set = false;
 }
 
+static void match_lost_param(struct expression *call, int param)
+{
+        struct expression *arg;
+
+        if (is_const_param(call->fn, param))
+                return;
+
+        arg = get_argument_from_call_expr(call->args, param);
+        if (!arg)
+                return;
+
+        arg = strip_expr(arg);
+        if (arg->type == EXPR_PREOP && arg->op == '&')
+                set_extra_expr_mod(arg->unop, alloc_estate_whole(get_type(arg->unop)));
+        else
+                ; /* if pointer then set struct members, maybe?*/
+}
+
 static void db_param_value(struct expression *expr, int param, char *key, char *value)
 {
         struct expression *call;
         char *name;
         struct symbol *sym;

@@ -2526,10 +2678,11 @@
 {
         struct smatch_state *state;
         struct range_list *rl = NULL;
         struct expression *arg;
         struct symbol *type;
+        sval_t dummy;
         int i = 0;
 
         FOR_EACH_PTR(expr->args, arg) {
                 type = get_arg_type(expr->fn, i);
 

@@ -2539,10 +2692,14 @@
                 if (!is_whole_rl(rl)) {
                         rl = intersect_with_real_abs_expr(arg, rl);
                         sql_insert_caller_info(expr, PARAM_VALUE, i, "$", show_rl(rl));
                 }
                 state = get_state_expr(SMATCH_EXTRA, arg);
+                if (!estate_get_single_value(state, &dummy) && estate_has_hard_max(state)) {
+                        sql_insert_caller_info(expr, HARD_MAX, i, "$",
+                                               sval_to_str(estate_max(state)));
+                }
                 if (estate_has_fuzzy_max(state)) {
                         sql_insert_caller_info(expr, FUZZY_MAX, i, "$",
                                                sval_to_str(estate_get_fuzzy_max(state)));
                 }
                 i++;

@@ -2549,32 +2706,36 @@
         } END_FOR_EACH_PTR(arg);
 }
 
 static void set_param_value(const char *name, struct symbol *sym, char *key, char *value)
 {
+        struct expression *expr;
         struct range_list *rl = NULL;
         struct smatch_state *state;
         struct symbol *type;
         char fullname[256];
+        char *key_orig = key;
+        bool add_star = false;
         sval_t dummy;
 
-        if (strcmp(key, "*$") == 0)
-                snprintf(fullname, sizeof(fullname), "*%s", name);
-        else if (strncmp(key, "$", 1) == 0)
-                snprintf(fullname, 256, "%s%s", name, key + 1);
-        else
-                return;
+        if (key[0] == '*') {
+                add_star = true;
+                key++;
+        }
 
-        type = get_member_type_from_key(symbol_expression(sym), key);
+        snprintf(fullname, 256, "%s%s%s", add_star ? "*" : "", name, key + 1);
+
+        expr = symbol_expression(sym);
+        type = get_member_type_from_key(expr, key_orig);
         str_to_rl(type, value, &rl);
         state = alloc_estate_rl(rl);
         if (estate_get_single_value(state, &dummy))
                 estate_set_hard_max(state);
         set_state(SMATCH_EXTRA, fullname, sym, state);
 }
 
-static void set_param_hard_max(const char *name, struct symbol *sym, char *key, char *value)
+static void set_param_fuzzy_max(const char *name, struct symbol *sym, char *key, char *value)
 {
         struct range_list *rl = NULL;
         struct smatch_state *state;
         struct symbol *type;
         char fullname[256];

@@ -2588,17 +2749,35 @@
                 return;
 
         state = get_state(SMATCH_EXTRA, fullname, sym);
         if (!state)
                 return;
-        type = get_member_type_from_key(symbol_expression(sym), key);
+        type = estate_type(state);
         str_to_rl(type, value, &rl);
         if (!rl_to_sval(rl, &max))
                 return;
         estate_set_fuzzy_max(state, max);
 }
 
+static void set_param_hard_max(const char *name, struct symbol *sym, char *key, char *value)
+{
+        struct smatch_state *state;
+        char fullname[256];
+
+        if (strcmp(key, "*$") == 0)
+                snprintf(fullname, sizeof(fullname), "*%s", name);
+        else if (strncmp(key, "$", 1) == 0)
+                snprintf(fullname, 256, "%s%s", name, key + 1);
+        else
+                return;
+
+        state = get_state(SMATCH_EXTRA, fullname, sym);
+        if (!state)
+                return;
+        estate_set_hard_max(state);
+}
+
 struct sm_state *get_extra_sm_state(struct expression *expr)
 {
         char *name;
         struct symbol *sym;
         struct sm_state *ret = NULL;

@@ -2625,19 +2804,22 @@
 
 void register_smatch_extra(int id)
 {
         my_id = id;
 
+        set_dynamic_states(my_id);
         add_merge_hook(my_id, &merge_estates);
         add_unmatched_state_hook(my_id, &unmatched_state);
         select_caller_info_hook(set_param_value, PARAM_VALUE);
-        select_caller_info_hook(set_param_hard_max, FUZZY_MAX);
+        select_caller_info_hook(set_param_fuzzy_max, FUZZY_MAX);
+        select_caller_info_hook(set_param_hard_max, HARD_MAX);
         select_return_states_before(&db_limited_before);
         select_return_states_hook(PARAM_LIMIT, &db_param_limit);
         select_return_states_hook(PARAM_FILTER, &db_param_filter);
         select_return_states_hook(PARAM_ADD, &db_param_add);
         select_return_states_hook(PARAM_SET, &db_param_set);
+        add_lost_param_hook(&match_lost_param);
         select_return_states_hook(PARAM_VALUE, &db_param_value);
         select_return_states_after(&db_limited_after);
 }
 
 static void match_link_modify(struct sm_state *sm, struct expression *mod_expr)

@@ -2661,10 +2843,11 @@
 }
 
 void register_smatch_extra_links(int id)
 {
         link_id = id;
+        set_dynamic_states(link_id);
 }
 
 void register_smatch_extra_late(int id)
 {
         add_merge_hook(link_id, &merge_link_states);