--- old/usr/src/tools/smatch/src/smatch_container_of.c	Mon Aug  5 08:38:22 2019
+++ new/usr/src/tools/smatch/src/smatch_container_of.c	Mon Aug  5 08:38:22 2019
@@ -112,7 +112,7 @@
 	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;
@@ -173,7 +173,7 @@
 		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, "");
@@ -249,73 +249,150 @@
 	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;
-		}
-		if (one[i] != two[i])
-			break;
-		i++;
+	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 (i == 0)
-		return 0;
-	i--;
-	while (i > 0 && (one[i] == '>' || one[i] == '-' || one[i] == '.')) {
-		on_end = true;
-		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 (!on_end)
-		return 0;
+	return expr;
+}
 
-	return i + 1;
+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;
+	}
+
+	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
@@ -332,72 +409,22 @@
 	 * 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 {
@@ -574,35 +601,49 @@
 	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;
+	}
 
-	return;
-found:
-	if (!estate_get_single_value(state, &offset))
+	if (!get_toplevel_mtag(cur_func_sym, &cur_tag))
 		return;
-	container_offset = -(offset.value & 0xffff);
-	arg_offset = (offset.value & 0xfff0000) >> 16;
-	star = !!(offset.value & (1ULL << 31));
 
-	if (!get_toplevel_mtag(cur_func_sym, &fn_tag))
+	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;
+		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 (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 {
@@ -617,6 +658,19 @@
 	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;
@@ -637,18 +691,13 @@
 	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);
 }
 
