--- old/usr/src/tools/smatch/src/check_atomic_inc_dec.c	Tue May 19 02:50:29 2020
+++ new/usr/src/tools/smatch/src/check_atomic_inc_dec.c	Tue May 19 02:50:29 2020
@@ -15,6 +15,8 @@
  * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
  */
 
+#include <ctype.h>
+
 #include "smatch.h"
 #include "smatch_extra.h"
 #include "smatch_slist.h"
@@ -25,12 +27,72 @@
 STATE(orig);
 STATE(dec);
 
+static struct smatch_state *unmatched_state(struct sm_state *sm)
+{
+	if (parent_is_gone_var_sym(sm->name, sm->sym))
+		return sm->state;
+	return &undefined;
+}
+
+static struct stree *start_states;
+static struct stree_stack *saved_stack;
+static void set_start_state(const char *name, struct symbol *sym, struct smatch_state *start)
+{
+	struct smatch_state *orig;
+
+	orig = get_state_stree(start_states, my_id, name, sym);
+	if (!orig)
+		set_state_stree(&start_states, my_id, name, sym, start);
+	else if (orig != start)
+		set_state_stree(&start_states, my_id, name, sym, &undefined);
+}
+
+static struct sm_state *get_best_match(const char *key)
+{
+	struct sm_state *sm;
+	struct sm_state *match;
+	int cnt = 0;
+	int start_pos, state_len, key_len, chunks, i;
+
+	if (strncmp(key, "$->", 3) == 0)
+		key += 3;
+
+	key_len = strlen(key);
+	chunks = 0;
+	for (i = key_len - 1; i > 0; i--) {
+		if (key[i] == '>' || key[i] == '.')
+			chunks++;
+		if (chunks == 2) {
+			key += (i + 1);
+			key_len = strlen(key);
+			break;
+		}
+	}
+
+	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
+		state_len = strlen(sm->name);
+		if (state_len < key_len)
+			continue;
+		start_pos = state_len - key_len;
+		if ((start_pos == 0 || !isalnum(sm->name[start_pos - 1])) &&
+		    strcmp(sm->name + start_pos, key) == 0) {
+			cnt++;
+			match = sm;
+		}
+	} END_FOR_EACH_SM(sm);
+
+	if (cnt == 1)
+		return match;
+	return NULL;
+}
+
 static void db_inc_dec(struct expression *expr, int param, const char *key, const char *value, int inc_dec)
 {
-	struct smatch_state *start_state;
+	struct sm_state *start_sm;
 	struct expression *arg;
 	char *name;
 	struct symbol *sym;
+	bool free_at_end = true;
 
 	while (expr->type == EXPR_ASSIGNMENT)
 		expr = strip_expr(expr->right);
@@ -45,16 +107,28 @@
 	if (!name || !sym)
 		goto free;
 
-	start_state = get_state(my_id, name, sym);
+	start_sm = get_sm_state(my_id, name, sym);
+	if (!start_sm && inc_dec == ATOMIC_DEC) {
+		start_sm = get_best_match(key);
+		if (start_sm) {
+			free_string(name);
+			free_at_end = false;
+			name = (char *)start_sm->name;
+			sym = start_sm->sym;
+		}
+	}
 
 	if (inc_dec == ATOMIC_INC) {
-//		if (start_state == &inc)
-//			sm_error("XXX double increment '%s'", name);
+		if (!start_sm)
+			set_start_state(name, sym, &dec);
+//		set_refcount_inc(name, sym);
 		set_state(my_id, name, sym, &inc);
 	} else {
-//		if (start_state == &dec)
-//			sm_error("XXX double decrement '%s'", name);
-		if (start_state == &inc)
+//		set_refcount_dec(name, sym);
+		if (!start_sm)
+			set_start_state(name, sym, &inc);
+
+		if (start_sm && start_sm->state == &inc)
 			set_state(my_id, name, sym, &orig);
 		else
 			set_state(my_id, name, sym, &dec);
@@ -61,7 +135,8 @@
 	}
 
 free:
-	free_string(name);
+	if (free_at_end)
+		free_string(name);
 }
 
 static void db_inc(struct expression *expr, int param, char *key, char *value)
@@ -123,6 +198,8 @@
 		if (sm->state != &inc &&
 		    sm->state != &dec)
 			continue;
+		if (parent_is_gone_var_sym(sm->name, sm->sym))
+			continue;
 		param = get_param_num_from_sym(sm->sym);
 		if (param < 0)
 			continue;
@@ -136,13 +213,13 @@
 }
 
 enum {
-	NEGATIVE, ZERO, POSITIVE,
+	EMPTY, NEGATIVE, ZERO, POSITIVE, NUM_BUCKETS
 };
 
 static int success_fail_positive(struct range_list *rl)
 {
 	if (!rl)
-		return ZERO;
+		return EMPTY;
 
 	if (sval_is_negative(rl_min(rl)))
 		return NEGATIVE;
@@ -157,31 +234,49 @@
 {
 	struct range_list *inc_lines = NULL;
 	struct range_list *dec_lines = NULL;
-	int inc_buckets[3] = {};
-	struct stree *stree;
+	int inc_buckets[NUM_BUCKETS] = {};
+	int dec_buckets[NUM_BUCKETS] = {};
+	struct stree *stree, *orig_stree;
 	struct sm_state *return_sm;
 	struct sm_state *sm;
 	sval_t line = sval_type_val(&int_ctype, 0);
+	int bucket;
 
 	FOR_EACH_PTR(get_all_return_strees(), stree) {
-		return_sm = get_sm_state_stree(stree, RETURN_ID, "return_ranges", NULL);
+		orig_stree = __swap_cur_stree(stree);
+
+		return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL);
 		if (!return_sm)
-			continue;
+			goto swap_stree;
 		line.value = return_sm->line;
 
-		sm = get_sm_state_stree(stree, my_id, name, sym);
+		if (get_state_stree(start_states, my_id, name, sym) == &inc)
+			goto swap_stree;
+
+		if (parent_is_gone_var_sym(name, sym))
+			goto swap_stree;
+
+		sm = get_sm_state(my_id, name, sym);
 		if (!sm)
-			continue;
+			goto swap_stree;
 
-		if (sm->state != &inc && sm->state != &dec)
-			continue;
+		if (sm->state != &inc &&
+		    sm->state != &dec &&
+		    sm->state != &orig)
+			goto swap_stree;
 
+		bucket = success_fail_positive(estate_rl(return_sm->state));
+
 		if (sm->state == &inc) {
 			add_range(&inc_lines, line, line);
-			inc_buckets[success_fail_positive(estate_rl(return_sm->state))] = 1;
+			inc_buckets[bucket] = true;
 		}
-		if (sm->state == &dec)
+		if (sm->state == &dec || sm->state == &orig) {
 			add_range(&dec_lines, line, line);
+			dec_buckets[bucket] = true;
+		}
+swap_stree:
+		__swap_cur_stree(orig_stree);
 	} END_FOR_EACH_PTR(stree);
 
 	if (inc_buckets[NEGATIVE] &&
@@ -217,6 +312,22 @@
 	return get_state(my_id, name, sym) == &inc;
 }
 
+static void match_save_states(struct expression *expr)
+{
+	push_stree(&saved_stack, start_states);
+	start_states = NULL;
+}
+
+static void match_restore_states(struct expression *expr)
+{
+	start_states = pop_stree(&saved_stack);
+}
+
+static void match_after_func(struct symbol *sym)
+{
+	free_stree(&start_states);
+}
+
 void check_atomic_inc_dec(int id)
 {
 	my_id = id;
@@ -224,8 +335,12 @@
 	if (option_project != PROJ_KERNEL)
 		return;
 
+	add_unmatched_state_hook(my_id, &unmatched_state);
+
+	add_split_return_callback(match_return_info);
 	select_return_states_hook(ATOMIC_INC, &db_inc);
 	select_return_states_hook(ATOMIC_DEC, &db_dec);
+
 	add_function_hook("atomic_inc_return", &match_atomic_inc, NULL);
 	add_function_hook("atomic_add_return", &match_atomic_add, NULL);
 	add_function_hook("atomic_sub_return", &match_atomic_sub, NULL);
@@ -237,12 +352,18 @@
 	add_function_hook("atomic_long_dec", &match_atomic_dec, NULL);
 	add_function_hook("atomic_inc", &match_atomic_inc, NULL);
 	add_function_hook("atomic_sub", &match_atomic_sub, NULL);
-	add_split_return_callback(match_return_info);
 
+	add_function_hook("refcount_inc", &refcount_inc, INT_PTR(0));
+	add_function_hook("refcount_dec", &refcount_dec, INT_PTR(0));
+	add_function_hook("refcount_add", &refcount_inc, INT_PTR(1));
 	add_function_hook("refcount_add_not_zero", &refcount_inc, INT_PTR(1));
 	add_function_hook("refcount_inc_not_zero", &refcount_inc, INT_PTR(0));
 	add_function_hook("refcount_sub_and_test", &refcount_dec, INT_PTR(1));
-	add_function_hook("refcount_dec_and_test", &refcount_dec, INT_PTR(1));
+	add_function_hook("refcount_dec_and_test", &refcount_dec, INT_PTR(0));
 
 	add_hook(&match_check_missed, END_FUNC_HOOK);
+
+	add_hook(&match_after_func, AFTER_FUNC_HOOK);
+	add_hook(&match_save_states, INLINE_FN_START);
+	add_hook(&match_restore_states, INLINE_FN_END);
 }
