Print this page
11506 smatch resync

@@ -110,11 +110,11 @@
         offset = get_offset_from_container_of(__mptr);
 
         return offset;
 }
 
-static char *get_container_name(struct sm_state *sm, int offset)
+static char *get_container_name_sm(struct sm_state *sm, int offset)
 {
         static char buf[256];
         const char *name;
 
         name = get_param_name(sm);

@@ -171,11 +171,11 @@
         FOR_EACH_SM(used_stree, tmp) {
                 arg = get_container_arg(tmp->sym);
                 offset = get_container_offset(tmp->sym);
                 if (arg < 0 || offset < 0)
                         continue;
-                name = get_container_name(tmp, offset);
+                name = get_container_name_sm(tmp, offset);
                 if (!name)
                         continue;
                 sql_insert_return_implies(CONTAINER, arg, name, "");
         } END_FOR_EACH_SM(tmp);
 

@@ -247,77 +247,154 @@
                 return;
         snprintf(buf, sizeof(buf), "$(%d)", offset);
         sql_insert_return_implies(CONTAINER, param, buf, "");
 }
 
-static int get_shared_cnt(const char *one, const char *two)
+static int get_deref_count(struct expression *expr)
 {
-        int i;
-        int on_end = false;
+        int cnt = 0;
 
-        i = 0;
-        while (true) {
-                if (!one[i] || !two[i]) {
-                        on_end = true;
-                        break;
+        while (expr && expr->type == EXPR_DEREF) {
+                expr = expr->deref;
+                if (expr->type == EXPR_PREOP && expr->op == '*')
+                        expr = expr->unop;
+                cnt++;
+                if (cnt > 100)
+                        return -1;
                 }
-                if (one[i] != two[i])
-                        break;
-                i++;
+        return cnt;
+}
+
+static struct expression *get_partial_deref(struct expression *expr, int cnt)
+{
+        while (--cnt >= 0) {
+                if (!expr || expr->type != EXPR_DEREF)
+                        return expr;
+                expr = expr->deref;
+                if (expr->type == EXPR_PREOP && expr->op == '*')
+                        expr = expr->unop;
         }
-        if (i == 0)
-                return 0;
-        i--;
-        while (i > 0 && (one[i] == '>' || one[i] == '-' || one[i] == '.')) {
-                on_end = true;
-                i--;
+        return expr;
+}
+
+static int partial_deref_to_offset_str(struct expression *expr, int cnt, char op, char *buf, int size)
+{
+        int n, offset;
+
+        if (cnt == 0)
+                return snprintf(buf, size, "%c0", op);
+
+        n = 0;
+        while (--cnt >= 0) {
+                offset = get_member_offset_from_deref(expr);
+                if (offset < 0)
+                        return -1;
+                n += snprintf(buf + n, size - n, "%c%d", op, offset);
+                if (expr->type != EXPR_DEREF)
+                        return -1;
+                expr = expr->deref;
+                if (expr->type == EXPR_PREOP && expr->op == '*')
+                        expr = expr->unop;
         }
-        if (!on_end)
-                return 0;
 
-        return i + 1;
+        return n;
 }
 
-static int build_offset_str(struct expression *expr, const char *name,
-                            int shared, char *buf, int size, int op)
+static char *get_shared_str(struct expression *container, struct expression *expr)
 {
-        int chop = 0;
-        int offset;
-        int i;
+        struct expression *one, *two;
+        int cont, exp, min, ret, n;
+        static char buf[48];
 
-        i = shared;
-        while (name[i]) {
-                if (name[i] == '.' || name[i] == '-')
-                        chop++;
-                i++;
+        cont = get_deref_count(container);
+        exp = get_deref_count(expr);
+        if (cont < 0 || exp < 0)
+                return NULL;
+
+        min = (cont < exp) ? cont : exp;
+        while (min >= 0) {
+                one = get_partial_deref(container, cont - min);
+                two = get_partial_deref(expr, exp - min);
+                if (expr_equiv(one, two))
+                        goto found;
+                min--;
         }
 
-        // FIXME:  Handle more chops
-        if (chop > 1)
-                return 0;
+        return NULL;
 
-        if (chop == 0) {
-                offset = 0;
-        } else {
-                offset = get_member_offset_from_deref(expr);
-                if (offset < 0)
-                        return 0;
+found:
+        ret = partial_deref_to_offset_str(container, cont - min, '-', buf, sizeof(buf));
+        if (ret < 0)
+                return NULL;
+        n = ret;
+        ret = partial_deref_to_offset_str(expr, exp - min, '+', buf + ret, sizeof(buf) - ret);
+        if (ret < 0)
+                return NULL;
+        n += ret;
+        if (n >= sizeof(buf))
+                return NULL;
+
+        return buf;
+}
+
+char *get_container_name(struct expression *container, struct expression *expr)
+{
+        struct symbol *container_sym, *sym;
+        struct expression *tmp;
+        static char buf[64];
+        char *shared;
+        bool star;
+        int cnt;
+
+        container_sym = expr_to_sym(container);
+        sym = expr_to_sym(expr);
+        if (container_sym && container_sym == sym)
+                goto found;
+
+        cnt = 0;
+        while ((tmp = get_assigned_expr(expr))) {
+                expr = tmp;
+                if (cnt++ > 3)
+                        break;
         }
 
-        snprintf(buf, size, "%c%d", (op == '+') ? '+' : '-', offset);
-        return 1;
+        cnt = 0;
+        while ((tmp = get_assigned_expr(container))) {
+                container = tmp;
+                if (cnt++ > 3)
+                        break;
+        }
+
+found:
+        expr = strip_expr(expr);
+        star = true;
+        if (expr->type == EXPR_PREOP && expr->op == '&') {
+                expr = strip_expr(expr->unop);
+                star = false;
+        }
+
+        container_sym = expr_to_sym(container);
+        if (!container_sym)
+                return NULL;
+        sym = expr_to_sym(expr);
+        if (!sym || container_sym != sym)
+                return NULL;
+
+        shared = get_shared_str(container, expr);
+        if (star)
+                snprintf(buf, sizeof(buf), "*(%s)", shared);
+        else
+                snprintf(buf, sizeof(buf), "%s", shared);
+
+        return buf;
 }
 
 static void match_call(struct expression *call)
 {
         struct expression *fn, *arg;
-        char *fn_name, *arg_name;
-        int param, shared;
-        char minus_str[64];
-        char plus_str[64];
-        char offset_str[64];
-        bool star;
+        char *name;
+        int param;
 
         /*
          * We're trying to link the function with the parameter.  There are a
          * couple ways this can be passed:
          * foo->func(foo, ...);

@@ -330,76 +407,26 @@
          *
          * If we're taking an address then the offset math is not stared,
          * otherwise it is.  Starred means dereferenced.
          */
         fn = strip_expr(call->fn);
-        fn_name = expr_to_var(fn);
-        if (!fn_name)
-                return;
 
         param = -1;
         FOR_EACH_PTR(call->args, arg) {
                 param++;
 
-                arg = strip_expr(arg);
-                star = true;
-                if (arg->type == EXPR_PREOP && arg->op == '&') {
-                        arg = strip_expr(arg->unop);
-                        star = false;
-                }
-
-                arg_name = expr_to_var(arg);
-                if (!arg_name)
+                name = get_container_name(fn, arg);
+                if (!name)
                         continue;
-                shared = get_shared_cnt(fn_name, arg_name);
-                if (!shared)
-                        goto free_arg_name;
-                if (!build_offset_str(fn, fn_name, shared, minus_str, sizeof(minus_str), '-'))
-                        goto free_arg_name;
-                if (!build_offset_str(arg, arg_name, shared, plus_str, sizeof(plus_str), '+'))
-                        goto free_arg_name;
-                if (star)
-                        snprintf(offset_str, sizeof(offset_str), "*(%s%s)", minus_str, plus_str);
-                else
-                        snprintf(offset_str, sizeof(offset_str), "%s%s", minus_str, plus_str);
-                sql_insert_caller_info(call, CONTAINER, param, offset_str, "$(-1)");
-free_arg_name:
-                free_string(arg_name);
-        } END_FOR_EACH_PTR(arg);
 
-        free_string(fn_name);
+                sql_insert_caller_info(call, CONTAINER, param, name, "$(-1)");
+        } END_FOR_EACH_PTR(arg);
 }
 
 static void db_passed_container(const char *name, struct symbol *sym, char *key, char *value)
 {
-        sval_t offset = {
-                .type = &int_ctype,
-        };
-        const char *arg_offset;
-        int star = 0;
-        int val;
-
-        if (key[0] == '*') {
-                star = 1;
-                key += 2;
-        }
-
-        val = atoi(key);
-        if (val < -4095 || val > 0)
-                return;
-        offset.value = -val;
-        arg_offset = strchr(key, '+');
-        if (!arg_offset)
-                return;
-        val = atoi(arg_offset + 1);
-        if (val > 4095 || val < 0)
-                return;
-        offset.value |= val << 16;
-        if (star)
-                offset.value |= 1ULL << 31;
-
-        set_state(param_id, name, sym, alloc_estate_sval(offset));
+        set_state(param_id, name, sym, alloc_state_str(key));
 }
 
 struct db_info {
         struct symbol *arg;
         int prev_offset;

@@ -572,39 +599,53 @@
                 set_state_stree(&db_info.stree, SMATCH_EXTRA, arg->ident->name, arg, alloc_estate_sval(sval));
         }
         return db_info.stree;
 }
 
-static void handle_passed_container(struct symbol *sym)
+static void load_container_data(struct symbol *arg, const char *info)
 {
-        struct symbol *arg;
-        struct smatch_state *state;
+        mtag_t cur_tag, container_tag, arg_tag;
+        int container_offset, arg_offset;
+        char *p = (char *)info;
         struct sm_state *sm;
         struct stree *stree;
-        mtag_t fn_tag, container_tag, arg_tag;
-        sval_t offset;
-        int container_offset, arg_offset;
-        int star;
+        bool star = 0;
 
-        FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
-                state = get_state(param_id, arg->ident->name, arg);
-                if (state)
-                        goto found;
-        } END_FOR_EACH_PTR(arg);
+        if (p[0] == '*') {
+                star = 1;
+                p += 2;
+        }
 
+        if (!get_toplevel_mtag(cur_func_sym, &cur_tag))
         return;
-found:
-        if (!estate_get_single_value(state, &offset))
+
+        while (true) {
+                container_offset = strtoul(p, &p, 0);
+                if (local_debug)
+                        sm_msg("%s: cur_tag = %llu container_offset = %d",
+                               __func__, cur_tag, container_offset);
+                if (!mtag_map_select_container(cur_tag, container_offset, &container_tag))
                 return;
-        container_offset = -(offset.value & 0xffff);
-        arg_offset = (offset.value & 0xfff0000) >> 16;
-        star = !!(offset.value & (1ULL << 31));
+                cur_tag = container_tag;
+                if (local_debug)
+                        sm_msg("%s: container_tag = %llu p = '%s'",
+                               __func__, container_tag, p);
+                if (!p)
+                        return;
+                if (p[0] != '-')
+                        break;
+                p++;
+        }
 
-        if (!get_toplevel_mtag(cur_func_sym, &fn_tag))
+        if (p[0] != '+')
                 return;
-        if (!mtag_map_select_container(fn_tag, container_offset, &container_tag))
+
+        p++;
+        arg_offset = strtoul(p, &p, 0);
+        if (p && *p && *p != ')')
                 return;
+
         if (!arg_offset || star) {
                 arg_tag = container_tag;
         } else {
                 if (!mtag_map_select_tag(container_tag, -arg_offset, &arg_tag))
                         return;

@@ -615,10 +656,23 @@
                 set_state(sm->owner, sm->name, sm->sym, sm->state);
         } END_FOR_EACH_SM(sm);
         free_stree(&stree);
 }
 
+static void handle_passed_container(struct symbol *sym)
+{
+        struct symbol *arg;
+        struct smatch_state *state;
+
+        FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
+                state = get_state(param_id, arg->ident->name, arg);
+                if (!state || state == &merged)
+                        continue;
+                load_container_data(arg, state->name);
+        } END_FOR_EACH_PTR(arg);
+}
+
 void register_container_of(int id)
 {
         my_id = id;
 
         add_hook(&match_function_def, FUNC_DEF_HOOK);

@@ -635,20 +689,15 @@
         select_return_states_hook(CONTAINER, &returns_container_of);
 
         add_hook(&match_call, FUNCTION_CALL_HOOK);
 }
 
-static struct smatch_state *unmatched_state(struct sm_state *sm)
-{
-        return alloc_estate_whole(estate_type(sm->state));
-}
-
 void register_container_of2(int id)
 {
         param_id = id;
 
+        set_dynamic_states(param_id);
         select_caller_info_hook(db_passed_container, CONTAINER);
+        add_merge_hook(param_id, &merge_str_state);
         add_hook(&handle_passed_container, AFTER_DEF_HOOK);
-        add_unmatched_state_hook(param_id, &unmatched_state);
-        add_merge_hook(param_id, &merge_estates);
 }