--- old/usr/src/tools/smatch/Makefile	Tue May 19 02:50:29 2020
+++ new/usr/src/tools/smatch/Makefile	Tue May 19 02:50:29 2020
@@ -20,7 +20,7 @@
 #
 
 PROG = smatch
-SPARSE_VERSION = 0.6.1-rc1-il-4
+SPARSE_VERSION = 0.6.1-rc1-il-5
 
 include ../Makefile.tools
 
@@ -221,6 +221,7 @@
 SMATCH_DB_DATA += type_size.schema
 SMATCH_DB_DATA += fn_ptr_data_link.schema
 SMATCH_DB_DATA += mtag_data.schema
+SMATCH_DB_DATA += mtag_info.schema
 SMATCH_DB_DATA += type_value.schema
 
 ROOTONBLDDATAFILES = $(SMATCH_DATA:%=$(SMATCHDATADIR)/smatch_data/%)
--- old/usr/src/tools/smatch/src/Makefile	Tue May 19 02:50:29 2020
+++ new/usr/src/tools/smatch/src/Makefile	Tue May 19 02:50:29 2020
@@ -1,4 +1,4 @@
-VERSION=0.6.1-rc1-il-4
+VERSION=0.6.1-rc1-il-5
 
 ########################################################################
 # The following variables can be overwritten from the command line
--- old/usr/src/tools/smatch/src/check_all_func_returns.c	Tue May 19 02:50:29 2020
+++ new/usr/src/tools/smatch/src/check_all_func_returns.c	Tue May 19 02:50:29 2020
@@ -74,7 +74,7 @@
 
 	stmt = last_ptr_list((struct ptr_list *)big_statement_stack);
 
-	if (stmt->type == STMT_EXPRESSION && stmt->expression == expr)
+	if (stmt && stmt->type == STMT_EXPRESSION && stmt->expression == expr)
 		sm_error("unchecked function return '%s'", expr_to_str(expr->fn));
 }
 
--- old/usr/src/tools/smatch/src/check_allocating_enough_data.c	Tue May 19 02:50:29 2020
+++ new/usr/src/tools/smatch/src/check_allocating_enough_data.c	Tue May 19 02:50:29 2020
@@ -23,6 +23,7 @@
 	struct symbol *left_type, *right_type;
 	int bytes;
 	sval_t sval;
+	char *str;
 
 	if (expr->type != EXPR_ASSIGNMENT)
 		return;
@@ -42,7 +43,10 @@
 		return;
 	if (sval.uvalue >= bytes)
 		return;
-	sm_error("not allocating enough data %d vs %s", bytes, sval_to_str(sval));
+
+	str = expr_to_str(expr->left);
+	sm_error("not allocating enough for = '%s' %d vs %s", str, bytes, sval_to_str(sval));
+	free_string(str);
 }
 
 void check_allocating_enough_data(int id)
--- 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);
 }
--- old/usr/src/tools/smatch/src/check_debug.c	Tue May 19 02:50:29 2020
+++ new/usr/src/tools/smatch/src/check_debug.c	Tue May 19 02:50:29 2020
@@ -78,8 +78,6 @@
 static void match_states(const char *fn, struct expression *expr, void *info)
 {
 	struct expression *check_arg;
-	struct sm_state *sm;
-	int found = 0;
 
 	check_arg = get_argument_from_call_expr(expr->args, 0);
 	if (check_arg->type != EXPR_STRING) {
@@ -87,14 +85,7 @@
 		return;
 	}
 
-	FOR_EACH_SM(__get_cur_stree(), sm) {
-		if (!strstr(check_name(sm->owner), check_arg->string->data))
-			continue;
-		sm_msg("%s", show_sm(sm));
-		found = 1;
-	} END_FOR_EACH_SM(sm);
-
-	if (found)
+	if (__print_states(check_arg->string->data))
 		return;
 
 	if (!id_from_name(check_arg->string->data))
@@ -210,6 +201,9 @@
 	bool capped = false;
 	char *name;
 
+	if (option_project != PROJ_KERNEL)
+		sm_msg("no user data for project = '%s'", option_project_str);
+
 	arg = get_argument_from_call_expr(expr->args, 0);
 	name = expr_to_str(arg);
 
@@ -526,6 +520,17 @@
 	debug_db = 0;
 }
 
+static void mtag_info(struct expression *expr)
+{
+	mtag_t tag = 0;
+	int offset = 0;
+	struct range_list *rl = NULL;
+
+	expr_to_mtag_offset(expr, &tag, &offset);
+	get_mtag_rl(expr, &rl);
+	sm_msg("mtag = %llu offset = %d rl = '%s'", tag, offset, show_rl(rl));
+}
+
 static void match_about(const char *fn, struct expression *expr, void *info)
 {
 	struct expression *arg;
@@ -537,6 +542,7 @@
 	match_buf_size(fn, expr, NULL);
 	match_strlen(fn, expr, NULL);
 	match_real_absolute(fn, expr, NULL);
+	mtag_info(expr);
 
 	arg = get_argument_from_call_expr(expr->args, 0);
 	name = expr_to_str(arg);
@@ -651,6 +657,21 @@
 	sm_msg("stree_id %d", __stree_id);
 }
 
+static void match_bits(const char *fn, struct expression *expr, void *_unused)
+{
+	struct expression *arg;
+	struct bit_info *info;
+	char *name;
+
+	arg = get_argument_from_call_expr(expr->args, 0);
+	name = expr_to_str(arg);
+
+	info = get_bit_info(arg);
+
+	sm_msg("bit info '%s': definitely set 0x%llx.  possibly set 0x%llx.",
+	       name, info->set, info->possible);
+}
+
 static void match_mtag(const char *fn, struct expression *expr, void *info)
 {
 	struct expression *arg;
@@ -695,6 +716,28 @@
 	free_string(name);
 }
 
+static void match_expr(const char *fn, struct expression *expr, void *info)
+{
+	struct expression *arg, *str, *new;
+	char *name, *new_name;
+
+	str = get_argument_from_call_expr(expr->args, 0);
+	arg = get_argument_from_call_expr(expr->args, 1);
+	if (!arg || !str)
+		return;
+
+	if (str->type != EXPR_STRING)
+		return;
+
+	new = gen_expression_from_key(arg, str->string->data);
+	name = expr_to_str(arg);
+	new_name = expr_to_str(new);
+
+	sm_msg("str = '%s', arg = '%s' expr = '%s'", str->string->data, name, new_name);
+
+	free_string(new_name);
+	free_string(name);
+}
 static void match_state_count(const char *fn, struct expression *expr, void *info)
 {
 	sm_msg("state_count = %d\n", sm_state_counter);
@@ -792,8 +835,10 @@
 	add_implied_return_hook("__smatch_type_rl_helper", &match_type_rl_return, NULL);
 	add_function_hook("__smatch_merge_tree", &match_print_merge_tree, NULL);
 	add_function_hook("__smatch_stree_id", &match_print_stree_id, NULL);
+	add_function_hook("__smatch_bits", &match_bits, NULL);
 	add_function_hook("__smatch_mtag", &match_mtag, NULL);
 	add_function_hook("__smatch_mtag_data", &match_mtag_data_offset, NULL);
+	add_function_hook("__smatch_expr", &match_expr, NULL);
 	add_function_hook("__smatch_state_count", &match_state_count, NULL);
 	add_function_hook("__smatch_mem", &match_mem, NULL);
 	add_function_hook("__smatch_exit", &match_exit, NULL);
--- old/usr/src/tools/smatch/src/check_debug.h	Tue May 19 02:50:29 2020
+++ new/usr/src/tools/smatch/src/check_debug.h	Tue May 19 02:50:29 2020
@@ -63,7 +63,7 @@
 #define __smatch_type_rl(type, fmt...) __smatch_type_rl_helper((type)0, fmt)
 #define __smatch_rl(fmt...) __smatch_type_rl(long long, fmt)
 
-static inline void __smatch_bit_info(long long expr){}
+static inline void __smatch_bits(long long expr){}
 
 static inline void __smatch_oops(unsigned long null_val){}
 
@@ -75,8 +75,11 @@
 static inline void __smatch_mtag_data(long long arg){}
 static inline void __smatch_exit(void){}
 
+static inline void __smatch_expr(const char *str, void *p){}
+
 static inline void __smatch_state_count(void){}
 static inline void __smatch_mem(void){}
 
 static inline void __smatch_container(long long container, long long x){}
+
 #endif
--- old/usr/src/tools/smatch/src/check_err_ptr.c	Tue May 19 02:50:29 2020
+++ new/usr/src/tools/smatch/src/check_err_ptr.c	Tue May 19 02:50:30 2020
@@ -37,7 +37,6 @@
 		err_ptr = 1;
 }
 
-extern int check_assigned_expr_id;
 static void match_return(struct expression *ret_value)
 {
 	struct state_list *slist;
--- old/usr/src/tools/smatch/src/check_kernel.c	Tue May 19 02:50:30 2020
+++ new/usr/src/tools/smatch/src/check_kernel.c	Tue May 19 02:50:30 2020
@@ -32,10 +32,10 @@
 	struct expression *arg;
 
 	arg = get_argument_from_call_expr(call->args, 0);
-	if (!get_implied_rl(arg, rl)) {
+	if (!get_implied_rl(arg, rl))
 		*rl = alloc_rl(err_ptr_min, err_ptr_max);
-		*rl = cast_rl(get_type(arg), *rl);
-	}
+
+	*rl = cast_rl(get_type(call), *rl);
 	return 1;
 }
 
@@ -130,12 +130,9 @@
 
 	arg = get_argument_from_call_expr(call_expr->args, 0);
 	pre_state = get_state_expr(SMATCH_EXTRA, arg);
-	if (estate_rl(pre_state)) {
-		rl = estate_rl(pre_state);
-		rl = remove_range(rl, err_ptr_min, err_ptr_max);
-	} else {
-		rl = alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
-	}
+	if (pre_state)
+		return;
+	rl = alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
 	rl = cast_rl(get_type(arg), rl);
 	set_extra_expr_nomod(arg, alloc_estate_rl(rl));
 }
@@ -154,6 +151,14 @@
 		rl = alloc_rl(err_ptr_min, err_ptr_max);
 	rl = rl_intersection(rl, alloc_rl(err_ptr_min, err_ptr_max));
 	rl = cast_rl(get_type(arg), rl);
+	if (pre_state && rl) {
+		/*
+		 * Ideally this would all be handled by smatch_implied.c
+		 * but it doesn't work very well for impossible paths.
+		 *
+		 */
+		return;
+	}
 	set_extra_expr_nomod(arg, alloc_estate_rl(rl));
 }
 
@@ -257,8 +262,6 @@
 	return 1;
 }
 
-
-
 static void find_module_init_exit(struct symbol_list *sym_list)
 {
 	struct symbol *sym;
@@ -406,6 +409,22 @@
 	__in_fake_assign--;
 }
 
+static void match_closure_call(const char *name, struct expression *call,
+			       void *unused)
+{
+	struct expression *cl, *fn, *fake_call;
+	struct expression_list *args = NULL;
+
+	cl = get_argument_from_call_expr(call->args, 0);
+	fn = get_argument_from_call_expr(call->args, 1);
+	if (!fn || !cl)
+		return;
+
+	add_ptr_list(&args, cl);
+	fake_call = call_expression(fn, args);
+	__split_expr(fake_call);
+}
+
 bool is_ignored_kernel_data(const char *name)
 {
 	if (option_project != PROJ_KERNEL)
@@ -463,6 +482,8 @@
 	add_function_hook("__read_once_size", &match__read_once_size, NULL);
 	add_function_hook("__read_once_size_nocheck", &match__read_once_size, NULL);
 
+	add_function_hook("closure_call", &match_closure_call, NULL);
+
 	if (option_info)
 		add_hook(match_end_file, END_FILE_HOOK);
 }
--- old/usr/src/tools/smatch/src/check_shift_to_zero.c	Tue May 19 02:50:30 2020
+++ new/usr/src/tools/smatch/src/check_shift_to_zero.c	Tue May 19 02:50:30 2020
@@ -56,9 +56,9 @@
 	if (left->type != EXPR_BINOP || left->op != '&')
 		return;
 
-	if (!get_implied_value(expr->right, &shift))
+	if (!get_value(expr->right, &shift))
 		return;
-	if (!get_implied_value(left->right, &mask))
+	if (!get_value(left->right, &mask))
 		return;
 
 	if (mask.uvalue >> shift.uvalue)
--- old/usr/src/tools/smatch/src/expression.h	Tue May 19 02:50:30 2020
+++ new/usr/src/tools/smatch/src/expression.h	Tue May 19 02:50:30 2020
@@ -134,8 +134,9 @@
 
 enum {
 	Handled = 1 << 0,
-	Fake	= 1 << 1,
-}; /* for expr->flags */
+	Tmp	= 1 << 1,
+	Fake	= 1 << 2,
+}; /* for expr->smatch_flags */
 
 enum {
 	Taint_comma = 1,
--- old/usr/src/tools/smatch/src/smatch.c	Tue May 19 02:50:30 2020
+++ new/usr/src/tools/smatch/src/smatch.c	Tue May 19 02:50:30 2020
@@ -340,6 +340,7 @@
 
 	allocate_hook_memory();
 	allocate_dynamic_states_array(num_checks);
+	allocate_tracker_array(num_checks);
 	create_function_hook_hash();
 	open_smatch_db(option_db_file);
 	sparse_initialize(argc, argv, &filelist);
--- old/usr/src/tools/smatch/src/smatch.h	Tue May 19 02:50:30 2020
+++ new/usr/src/tools/smatch/src/smatch.h	Tue May 19 02:50:30 2020
@@ -196,6 +196,7 @@
 void select_return_states_after(void (*fn)(void));
 int get_implied_return(struct expression *expr, struct range_list **rl);
 void allocate_hook_memory(void);
+void allocate_tracker_array(int num_checks);
 
 struct modification_data {
 	struct smatch_state *prev;
@@ -388,6 +389,7 @@
 struct smatch_state *alloc_state_expr(struct expression *expr);
 struct expression *get_argument_from_call_expr(struct expression_list *args,
 					       int num);
+struct expression *get_array_expr(struct expression *expr);
 
 char *expr_to_var(struct expression *expr);
 struct symbol *expr_to_sym(struct expression *expr);
@@ -446,9 +448,16 @@
 struct statement *get_current_statement(void);
 struct statement *get_prev_statement(void);
 struct expression *get_last_expr_from_expression_stmt(struct expression *expr);
+
+#define RETURN_VAR    -1
+#define LOCAL_SCOPE   -2
+#define FILE_SCOPE    -3
+#define GLOBAL_SCOPE  -4
+#define UNKNOWN_SCOPE -5
 int get_param_num_from_sym(struct symbol *sym);
 int get_param_num(struct expression *expr);
 struct symbol *get_param_sym_from_num(int num);
+
 int ms_since(struct timeval *start);
 int parent_is_gone_var_sym(const char *name, struct symbol *sym);
 int parent_is_gone(struct expression *expr);
@@ -478,9 +487,11 @@
 int is_void_pointer(struct expression *expr);
 int is_char_pointer(struct expression *expr);
 int is_string(struct expression *expr);
+bool is_struct_ptr(struct symbol *type);
 int is_static(struct expression *expr);
 bool is_local_variable(struct expression *expr);
 int types_equiv(struct symbol *one, struct symbol *two);
+bool type_fits(struct symbol *type, struct symbol *test);
 int fn_static(void);
 const char *global_static();
 struct symbol *cur_func_return_type(void);
@@ -581,7 +592,7 @@
 
 /* smatch_implied.c */
 struct range_list_stack;
-void param_limit_implications(struct expression *expr, int param, char *key, char *value);
+void param_limit_implications(struct expression *expr, int param, char *key, char *value, struct stree **implied);
 struct stree *__implied_case_stree(struct expression *switch_expr,
 				   struct range_list *case_rl,
 				   struct range_list_stack **remaining_cases,
@@ -780,6 +791,9 @@
 void __merge_gotos(const char *name, struct symbol *sym);
 
 void __print_cur_stree(void);
+bool __print_states(const char *owner);
+typedef void (check_tracker_hook)(int owner, const char *name, struct symbol *sym, struct smatch_state *state);
+void add_check_tracker(const char *check_name, check_tracker_hook *fn);
 
 /* smatch_hooks.c */
 void __pass_to_client(void *data, enum hook_type type);
@@ -847,6 +861,7 @@
 	STMT_CNT	= 1037,
 	TERMINATED	= 1038,
 	FRESH_ALLOC	= 1044,
+	ALLOCATOR	= 1045,
 
 	/* put random temporary stuff in the 7000-7999 range for testing */
 	USER_DATA	= 8017,
@@ -862,6 +877,7 @@
 	SET_FS		= 8022,
 	ATOMIC_INC	= 8023,
 	ATOMIC_DEC	= 8024,
+	REFCOUNT	= 9025,
 	NO_SIDE_EFFECT  = 8025,
 	FN_ARG_LINK	= 8028,
 	DATA_VALUE	= 8029,
@@ -885,6 +901,7 @@
 void db_ignore_states(int id);
 void select_caller_info_hook(void (*callback)(const char *name, struct symbol *sym, char *key, char *value), int type);
 void add_member_info_callback(int owner, void (*callback)(struct expression *call, int param, char *printed_name, struct sm_state *sm));
+void add_caller_info_callback(int owner, void (*callback)(struct expression *call, int param, char *printed_name, struct sm_state *sm));
 void add_split_return_callback(void (*fn)(int return_id, char *return_ranges, struct expression *returned_expr));
 void add_returned_member_callback(int owner, void (*callback)(int return_id, char *return_ranges, struct expression *expr, char *printed_name, struct smatch_state *state));
 void select_call_implies_hook(int type, void (*callback)(struct expression *call, struct expression *arg, char *key, char *value));
@@ -996,11 +1013,12 @@
 void sql_insert_fn_ptr_data_link(const char *ptr, const char *data);
 void sql_insert_fn_data_link(struct expression *fn, int type, int param, const char *key, const char *value);
 void sql_insert_mtag_about(mtag_t tag, const char *left_name, const char *right_name);
-void sql_insert_mtag_map(mtag_t tag, int offset, mtag_t container);
+void sql_insert_mtag_info(mtag_t tag, int type, const char *value);
+void sql_insert_mtag_map(mtag_t container, int container_offset, mtag_t tag, int tag_offset);
 void sql_insert_mtag_alias(mtag_t orig, mtag_t alias);
-int mtag_map_select_container(mtag_t tag, int offset, mtag_t *container);
+int mtag_map_select_container(mtag_t tag, int container_offset, mtag_t *container);
 int mtag_map_select_tag(mtag_t container, int offset, mtag_t *tag);
-struct smatch_state *swap_mtag_return(struct expression *expr, struct smatch_state *state);
+struct smatch_state *get_mtag_return(struct expression *expr, struct smatch_state *state);
 struct range_list *swap_mtag_seed(struct expression *expr, struct range_list *rl);
 
 void sql_select_return_states(const char *cols, struct expression *call,
@@ -1069,6 +1087,7 @@
 void print_held_locks();
 
 /* check_assigned_expr.c */
+extern int check_assigned_expr_id;
 struct expression *get_assigned_expr(struct expression *expr);
 struct expression *get_assigned_expr_name_sym(const char *name, struct symbol *sym);
 /* smatch_return_to_param.c */
@@ -1119,9 +1138,6 @@
 int remove_unsigned_from_comparison(int op);
 int param_compare_limit_is_impossible(struct expression *expr, int left_param, char *left_key, char *value);
 void filter_by_comparison(struct range_list **rl, int comparison, struct range_list *right);
-struct sm_state *comparison_implication_hook(struct expression *expr,
-			struct state_list **true_stack,
-			struct state_list **false_stack);
 void __compare_param_limit_hook(struct expression *left_expr, struct expression *right_expr,
 				const char *state_name,
 				struct smatch_state *true_state, struct smatch_state *false_state);
@@ -1193,6 +1209,10 @@
 struct sm_state *parsed_condition_implication_hook(struct expression *expr,
 			struct state_list **true_stack,
 			struct state_list **false_stack);
+/* smatch_comparison.c */
+struct sm_state *comparison_implication_hook(struct expression *expr,
+					     struct state_list **true_stack,
+					     struct state_list **false_stack);
 
 /* check_string_len.c */
 int get_formatted_string_size(struct expression *call, int arg);
@@ -1240,10 +1260,14 @@
 /* smatch_buf_comparison.c */
 int db_var_is_array_limit(struct expression *array, const char *name, struct var_sym_list *vsl);
 
+struct range_list *get_fs(void);
+
 struct stree *get_all_return_states(void);
 struct stree_stack *get_all_return_strees(void);
 int on_atomic_dec_path(void);
 int was_inced(const char *name, struct symbol *sym);
+void set_refcount_inc(char *name, struct symbol *sym);
+void set_refcount_dec(char *name, struct symbol *sym);
 
 /* smatch_constraints.c */
 char *get_constraint_str(struct expression *expr);
--- old/usr/src/tools/smatch/src/smatch_bits.c	Tue May 19 02:50:30 2020
+++ new/usr/src/tools/smatch/src/smatch_bits.c	Tue May 19 02:50:30 2020
@@ -111,10 +111,46 @@
 	return alloc_bstate(0, possible);
 }
 
+static bool is_loop_iterator(struct expression *expr)
+{
+	struct statement *pre_stmt, *loop_stmt;
+
+	pre_stmt = expr_get_parent_stmt(expr);
+	if (!pre_stmt || pre_stmt->type != STMT_EXPRESSION)
+		return false;
+
+	loop_stmt = stmt_get_parent_stmt(pre_stmt);
+	if (!loop_stmt || loop_stmt->type != STMT_ITERATOR)
+		return false;
+	if (loop_stmt->iterator_pre_statement != pre_stmt)
+		return false;
+
+	return true;
+}
+
+static bool handled_by_assign_hook(struct expression *expr)
+{
+	if (!expr || expr->type != EXPR_ASSIGNMENT)
+		return false;
+	if (__in_fake_assign)
+		return false;
+	if (is_loop_iterator(expr))
+		return false;
+
+	if (expr->op == '=' ||
+	    expr->op == SPECIAL_OR_ASSIGN ||
+	    expr->op == SPECIAL_AND_ASSIGN)
+		return true;
+
+	return false;
+}
+
 static void match_modify(struct sm_state *sm, struct expression *mod_expr)
 {
 	// FIXME: we really need to store the type
 
+	if (handled_by_assign_hook(mod_expr))
+		return;
 	set_state(my_id, sm->name, sm->sym, alloc_bstate(0, -1ULL));
 }
 
@@ -283,40 +319,31 @@
 			(expr->op == SPECIAL_EQUAL) ? NULL : alloc_bstate(val.uvalue, val.uvalue));
 }
 
-static bool is_loop_iterator(struct expression *expr)
-{
-	struct statement *pre_stmt, *loop_stmt;
-
-	pre_stmt = expr_get_parent_stmt(expr);
-	if (!pre_stmt || pre_stmt->type != STMT_EXPRESSION)
-		return false;
-
-	loop_stmt = stmt_get_parent_stmt(pre_stmt);
-	if (!loop_stmt || loop_stmt->type != STMT_ITERATOR)
-		return false;
-	if (loop_stmt->iterator_pre_statement != pre_stmt)
-		return false;
-
-	return true;
-}
-
 static void match_assign(struct expression *expr)
 {
-	struct bit_info *binfo;
+	struct bit_info *start, *binfo;
+	struct smatch_state *new;
 
-	if (expr->op != '=')
+	if (!handled_by_assign_hook(expr))
 		return;
-	if (__in_fake_assign)
-		return;
-	if (is_loop_iterator(expr))
-		return;
 
 	binfo = get_bit_info(expr->right);
 	if (!binfo)
 		return;
-	if (is_unknown_binfo(get_type(expr->left), binfo))
-		return;
-	set_state_expr(my_id, expr->left, alloc_bstate(binfo->set, binfo->possible));
+	if (expr->op == '=') {
+		if (is_unknown_binfo(get_type(expr->left), binfo))
+			return;
+
+		set_state_expr(my_id, expr->left, alloc_bstate(binfo->set, binfo->possible));
+	} else if (expr->op == SPECIAL_OR_ASSIGN) {
+		start = get_bit_info(expr->left);
+		new = alloc_bstate(start->set | binfo->set, start->possible | binfo->possible);
+		set_state_expr(my_id, expr->left, new);
+	} else if (expr->op == SPECIAL_AND_ASSIGN) {
+		start = get_bit_info(expr->left);
+		new = alloc_bstate(start->set & binfo->set, start->possible & binfo->possible);
+		set_state_expr(my_id, expr->left, new);
+	}
 }
 
 static void match_condition(struct expression *expr)
--- old/usr/src/tools/smatch/src/smatch_buf_size.c	Tue May 19 02:50:30 2020
+++ new/usr/src/tools/smatch/src/smatch_buf_size.c	Tue May 19 02:50:30 2020
@@ -269,6 +269,7 @@
 {
 	struct expression *call;
 	struct range_list *rl;
+	sval_t sval;
 
 	if (expr->type != EXPR_ASSIGNMENT)
 		return;
@@ -276,6 +277,8 @@
 
 	call_results_to_rl(call, &int_ctype, math, &rl);
 	rl = cast_rl(&int_ctype, rl);
+	if (rl_to_sval(rl, &sval) && sval.value == 0)
+		return;
 	set_state_expr(my_size_id, expr->left, alloc_estate_rl(rl));
 }
 
@@ -451,7 +454,7 @@
 		return 0;
 
 	state = get_state(my_size_id, sym->ident->name, sym);
-	if (!estate_to_size(state))
+	if (!estate_to_size(state) || estate_to_size(state) == -1)
 		return 0;
 
 	return estate_to_size(state) - type_bytes(base_sym) + type_bytes(get_type(expr));
@@ -504,6 +507,11 @@
 		return alloc_int_rl(size - offset.value);
 	}
 
+	/* buf = malloc(1024); */
+	ret = get_stored_size_bytes(expr);
+	if (ret)
+		return ret;
+
 	size = get_stored_size_end_struct_bytes(expr);
 	if (size)
 		return alloc_int_rl(size);
@@ -513,11 +521,6 @@
 	if (size)
 		return alloc_int_rl(elements_to_bytes(expr, size));
 
-	/* buf = malloc(1024); */
-	ret = get_stored_size_bytes(expr);
-	if (ret)
-		return ret;
-
 	/* char *foo = "BAR" */
 	size = get_size_from_initializer(expr);
 	if (size)
@@ -636,8 +639,12 @@
 	rl = clone_rl(rl); // FIXME!!!
 	if (!rl)
 		rl = size_to_rl(UNKNOWN_SIZE);
-	set_state_expr(my_size_id, expr, alloc_estate_rl(rl));
 
+	if (rl_min(rl).value != UNKNOWN_SIZE ||
+	    rl_max(rl).value != UNKNOWN_SIZE ||
+	    get_state_expr(my_size_id, expr))
+		set_state_expr(my_size_id, expr, alloc_estate_rl(rl));
+
 	type = get_type(expr);
 	if (!type)
 		return;
@@ -654,6 +661,16 @@
 	info_record_alloction(expr, rl);
 }
 
+static bool is_array_base(struct expression *expr)
+{
+	struct symbol *type;
+
+	type = get_type(expr);
+	if (type && type->type == SYM_ARRAY)
+		return true;
+	return false;
+}
+
 static void match_array_assignment(struct expression *expr)
 {
 	struct expression *left;
@@ -664,6 +681,7 @@
 
 	if (expr->op != '=')
 		return;
+
 	left = strip_expr(expr->left);
 	right = strip_expr(expr->right);
 	right = strip_ampersands(right);
@@ -670,6 +688,9 @@
 
 	if (!is_pointer(left))
 		return;
+	/* char buf[24] = "str"; */
+	if (is_array_base(left))
+		return;
 	if (is_allocation_function(right))
 		return;
 
@@ -710,15 +731,16 @@
 	store_alloc(expr->left, rl);
 }
 
-static void match_calloc(const char *fn, struct expression *expr, void *unused)
+static void match_calloc(const char *fn, struct expression *expr, void *_param)
 {
 	struct expression *right;
 	struct expression *size, *nr, *mult;
 	struct range_list *rl;
+	int param = PTR_INT(_param);
 
 	right = strip_expr(expr->right);
-	nr = get_argument_from_call_expr(right->args, 0);
-	size = get_argument_from_call_expr(right->args, 1);
+	nr = get_argument_from_call_expr(right->args, param);
+	size = get_argument_from_call_expr(right->args, param + 1);
 	mult = binop_expression(nr, '*', size);
 	if (get_implied_rl(mult, &rl))
 		store_alloc(expr->left, rl);
@@ -812,6 +834,9 @@
 		rl = get_array_size_bytes_rl(arg);
 		if (!rl)
 			continue;
+		if (rl_min(rl).value == UNKNOWN_SIZE &&
+		    rl_max(rl).value == UNKNOWN_SIZE)
+			continue;
 		if (is_whole_rl(rl))
 			continue;
 		if (is_type_bytes(rl, arg))
@@ -909,12 +934,12 @@
 		add_allocation_function("kzalloc", &match_alloc, 0);
 		add_allocation_function("kzalloc_node", &match_alloc, 0);
 		add_allocation_function("vmalloc", &match_alloc, 0);
+		add_allocation_function("vzalloc", &match_alloc, 0);
 		add_allocation_function("__vmalloc", &match_alloc, 0);
 		add_allocation_function("kvmalloc", &match_alloc, 0);
 		add_allocation_function("kcalloc", &match_calloc, 0);
 		add_allocation_function("kmalloc_array", &match_calloc, 0);
-		add_allocation_function("drm_malloc_ab", &match_calloc, 0);
-		add_allocation_function("drm_calloc_large", &match_calloc, 0);
+		add_allocation_function("devm_kmalloc_array", &match_calloc, 1);
 		add_allocation_function("sock_kmalloc", &match_alloc, 1);
 		add_allocation_function("kmemdup", &match_alloc, 1);
 		add_allocation_function("kmemdup_user", &match_alloc, 1);
--- old/usr/src/tools/smatch/src/smatch_capped.c	Tue May 19 02:50:30 2020
+++ new/usr/src/tools/smatch/src/smatch_capped.c	Tue May 19 02:50:31 2020
@@ -94,13 +94,15 @@
 
 	if (expr->type == EXPR_BINOP) {
 		struct range_list *left_rl, *right_rl;
+		sval_t sval;
 
-		if (expr->op == '&')
+		if (expr->op == '&' && !get_value(expr->right, &sval))
 			return 1;
 		if (expr->op == SPECIAL_RIGHTSHIFT)
+			return 0;
+		if (expr->op == '%' &&
+		    !get_value(expr->right, &sval) && is_capped(expr->right))
 			return 1;
-		if (expr->op == '%' && is_capped(expr->right))
-			return 1;
 		if (!is_capped(expr->left))
 			return 0;
 		if (expr->op == '/')
--- old/usr/src/tools/smatch/src/smatch_comparison.c	Tue May 19 02:50:31 2020
+++ new/usr/src/tools/smatch/src/smatch_comparison.c	Tue May 19 02:50:31 2020
@@ -2748,8 +2748,8 @@
 }
 
 struct sm_state *comparison_implication_hook(struct expression *expr,
-				struct state_list **true_stack,
-				struct state_list **false_stack)
+					     struct state_list **true_stack,
+					     struct state_list **false_stack)
 {
 	struct sm_state *sm;
 	char *left, *right;
--- old/usr/src/tools/smatch/src/smatch_container_of.c	Tue May 19 02:50:31 2020
+++ new/usr/src/tools/smatch/src/smatch_container_of.c	Tue May 19 02:50:31 2020
@@ -528,7 +528,7 @@
 			tag, arg_offset, DATA_VALUE);
 	} else {  /* presumably the parameter is a struct pointer */
 		run_sql(save_vals, &db_info,
-			"select offset, value from mtag_data where tag = %lld and type = %d;",
+			"select offset, value from mtag_data where tag = %lld and type = %d order by offset;",
 			tag, DATA_VALUE);
 	}
 
@@ -579,7 +579,7 @@
 		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))
+		if (!mtag_map_select_container(cur_tag, -container_offset, &container_tag))
 			return;
 		cur_tag = container_tag;
 		if (local_debug)
@@ -603,7 +603,7 @@
 	if (!arg_offset || star) {
 		arg_tag = container_tag;
 	} else {
-		if (!mtag_map_select_tag(container_tag, -arg_offset, &arg_tag))
+		if (!mtag_map_select_tag(container_tag, arg_offset, &arg_tag))
 			return;
 	}
 
--- old/usr/src/tools/smatch/src/smatch_data/db/create_db.sh	Tue May 19 02:50:31 2020
+++ new/usr/src/tools/smatch/src/smatch_data/db/create_db.sh	Tue May 19 02:50:31 2020
@@ -52,6 +52,8 @@
 echo "delete from function_ptr where rowid not in (select min(rowid) from function_ptr group by file, function, ptr, searchable);" | sqlite3 $db_file
 
 ${bin_dir}/apply_return_fixes.sh -p=${PROJ} $db_file
-${bin_dir}/insert_manual_states.pl ${PROJ} $db_file
+if [ "$PROJ" != "" ] ; then
+    ${bin_dir}/insert_manual_states.pl ${PROJ} $db_file
+fi
 
 mv $db_file smatch_db.sqlite
--- old/usr/src/tools/smatch/src/smatch_data/db/kernel.return_fixes	Tue May 19 02:50:31 2020
+++ new/usr/src/tools/smatch/src/smatch_data/db/kernel.return_fixes	Tue May 19 02:50:31 2020
@@ -20,6 +20,7 @@
 simple_write_to_buffer s64min-s64max 0-s32max[<=$1]
 atomic_read s32min-s32max s32min-s32max[==$0->counter]
 notifier_to_errno (-2147483646)-(-1) (-4095)-(-1)
+notifier_to_errno (-2147450878)-(-1) (-4095)-(-1)
 mc_status_to_error s32min-s32max (-4095)-0
 fls s32min-s32max 0-32
 fls64 s64min-s64max 0-64
@@ -57,3 +58,13 @@
 __roundup_pow_of_two 0-u64max 0-u64max[>=$0]
 kthread_probe_data 0 0-u64max
 bus_for_each_dev (-4095)-1 (-4095)-1[r\ $3]
+ahd_probe_stack_size 0 0-s32max
+mutex_lock_interruptible_nested (-35),(-4) (-4)
+dlfb_get_urb 0-u64max 4096-ptr_max
+bitmap_find_free_region 0 0-s32max
+mutex_lock_interruptible (-35),(-4) (-4)
+mutex_lock_interruptible_nested (-35),(-4) (-4)
+mutex_lock_killable (-35),(-4) (-4)
+mutex_lock_killable_nested (-35),(-4) (-4)
+ww_mutex_lock_interruptible (-35),(-4) (-4)
+ww_mutex_lock (-35),(-4) (-35)
--- old/usr/src/tools/smatch/src/smatch_data/db/mark_function_ptrs_searchable.pl	Tue May 19 02:50:31 2020
+++ new/usr/src/tools/smatch/src/smatch_data/db/mark_function_ptrs_searchable.pl	Tue May 19 02:50:31 2020
@@ -22,17 +22,6 @@
 $sth->execute();
 
 while ($fn_ptr = $sth->fetchrow_array()) {
-
-    # following a pointer to pointer chain is too complicated for now
-    $ptr_to_ptr = $db->selectrow_array("select function from function_ptr where ptr = '$fn_ptr' and function like '% %';");
-    if ($ptr_to_ptr) {
-        next;
-    }
-    $ptr_to_ptr = $db->selectrow_array("select function from function_ptr where ptr = '$fn_ptr' and function like '%[]';");
-    if ($ptr_to_ptr) {
-        next;
-    }
-
     $count = $db->selectrow_array("select count(*) from return_states join function_ptr where return_states.function == function_ptr.function and ptr = '$fn_ptr';");
     # if there are too many states then bail
     if ($count > 1000) {
--- old/usr/src/tools/smatch/src/smatch_data/db/mtag_about.schema	Tue May 19 02:50:31 2020
+++ new/usr/src/tools/smatch/src/smatch_data/db/mtag_about.schema	Tue May 19 02:50:31 2020
@@ -1,1 +1,10 @@
-CREATE TABLE mtag_about (tag big int, file varchar(80), function varchar(80), line integer, left_name varchar(80), right_name varchar(80));
+CREATE TABLE mtag_about (
+	tag big int,
+	file varchar(80),
+	function varchar(80),
+	line integer,
+	left_name varchar(80),
+	right_name varchar(80),
+
+	CONSTRAINT mtag_about_row UNIQUE (tag, file, function, line, left_name, right_name)
+);
--- /dev/null	Tue May 19 02:50:31 2020
+++ new/usr/src/tools/smatch/src/smatch_data/db/mtag_info.schema	Tue May 19 02:50:31 2020
@@ -0,0 +1,1 @@
+CREATE TABLE mtag_info (file varchar(80), tag big int, type integer, value varchar(80));
--- old/usr/src/tools/smatch/src/smatch_data/db/mtag_map.schema	Tue May 19 02:50:31 2020
+++ new/usr/src/tools/smatch/src/smatch_data/db/mtag_map.schema	Tue May 19 02:50:31 2020
@@ -1,1 +1,1 @@
-CREATE TABLE mtag_map (tag big int, offset integer, container big int);
+CREATE TABLE mtag_map (container big int, container_offset int, tag big int, tag_offset int);
--- old/usr/src/tools/smatch/src/smatch_data/db/return_states.schema	Tue May 19 02:50:32 2020
+++ new/usr/src/tools/smatch/src/smatch_data/db/return_states.schema	Tue May 19 02:50:32 2020
@@ -1,1 +1,1 @@
-CREATE TABLE return_states (file varchar(128), function varchar(64), call_id integer, return_id integer, return varchar(256), static boolean, type integer, parameter integer, key varchar(256), value varchar(256));
+CREATE TABLE return_states (file varchar(128), function varchar(64), call_id big int, return_id integer, return varchar(256), static boolean, type integer, parameter integer, key varchar(256), value varchar(256));
--- old/usr/src/tools/smatch/src/smatch_data/db/smdb.py	Tue May 19 02:50:32 2020
+++ new/usr/src/tools/smatch/src/smatch_data/db/smdb.py	Tue May 19 02:50:32 2020
@@ -74,6 +74,7 @@
             1017: "ARRAY_LEN",
             1018: "CAPABLE",
             1019: "NS_CAPABLE",
+            1020: "CONTAINER",
             1022: "TYPE_LINK",
             1023: "UNTRACKED_PARAM",
             1024: "CULL_PATH",
@@ -92,7 +93,7 @@
             1037: "STMT_CNT",
             1038: "TERMINATED",
             1039: "SLEEP",
-            1040: "NO_SLEEP_CNT",
+            1040: "PREEMPT_CNT",
             1041: "SMALLISH",
             1042: "FRESH_MTAG",
 
@@ -102,6 +103,10 @@
             8019: "NO_OVERFLOW_SIMPLE",
             8020: "LOCKED",
             8021: "UNLOCKED",
+            9022: "HALF_LOCKED",
+            9023: "LOCK_RESTORED",
+            9024: "KNOWN_LOCKED",
+            9025: "KNOWN_UNLOCKED",
             8023: "ATOMIC_INC",
             8024: "ATOMIC_DEC",
 };
--- old/usr/src/tools/smatch/src/smatch_db.c	Tue May 19 02:50:32 2020
+++ new/usr/src/tools/smatch/src/smatch_db.c	Tue May 19 02:50:32 2020
@@ -50,6 +50,7 @@
 ALLOCATOR(member_info_callback, "caller_info callbacks");
 DECLARE_PTR_LIST(member_info_cb_list, struct member_info_callback);
 static struct member_info_cb_list *member_callbacks;
+static struct member_info_cb_list *member_callbacks_new;
 
 struct returned_state_callback {
 	void (*callback)(int return_id, char *return_ranges, struct expression *return_expr);
@@ -178,15 +179,36 @@
 	use_states[id] = 0;
 }
 
+unsigned long long __fn_mtag;
+static void set_fn_mtag(struct symbol *sym)
+{
+	char buf[128];
+
+	if (cur_func_sym->ctype.modifiers & MOD_STATIC)
+		snprintf(buf, sizeof(buf), "%s %s", get_base_file(), get_function());
+	else
+		snprintf(buf, sizeof(buf), "extern %s", get_function());
+
+	__fn_mtag = str_to_mtag(buf);
+}
+
 void sql_insert_return_states(int return_id, const char *return_ranges,
 		int type, int param, const char *key, const char *value)
 {
+	unsigned long long id;
+
+
 	if (key && strlen(key) >= 80)
 		return;
+	if (__inline_fn)
+		id = (unsigned long)__inline_fn;
+	else
+		id = __fn_mtag;
+
 	return_ranges = replace_return_ranges(return_ranges);
-	sql_insert(return_states, "'%s', '%s', %lu, %d, '%s', %d, %d, %d, '%s', '%s'",
-		   get_base_file(), get_function(), (unsigned long)__inline_fn,
-		   return_id, return_ranges, fn_static(), type, param, key, value);
+	sql_insert(return_states, "'%s', '%s', %llu, %d, '%s', %d, %d, %d, '%s', '%s'",
+		   get_base_file(), get_function(), id, return_id,
+		   return_ranges, fn_static(), type, param, key, value);
 }
 
 static struct string_list *common_funcs;
@@ -372,15 +394,21 @@
 
 void sql_insert_mtag_about(mtag_t tag, const char *left_name, const char *right_name)
 {
-	sql_insert(mtag_about, "%lld, '%s', '%s', %d, '%s', '%s'",
-		   tag, get_filename(), get_function(), get_lineno(), left_name, right_name);
+	sql_insert_cache(mtag_about, "%lld, '%s', '%s', %d, '%s', '%s'",
+			 tag, get_filename(), get_function(), get_lineno(),
+			 left_name, right_name);
 }
 
-void sql_insert_mtag_map(mtag_t tag, int offset, mtag_t container)
+void sql_insert_mtag_info(mtag_t tag, int type, const char *value)
 {
-	sql_insert(mtag_map, "%lld, %d, %lld", tag, offset, container);
+	sql_insert_cache(mtag_info, "'%s', %lld, %d, '%s'", get_filename(), tag, type, value);
 }
 
+void sql_insert_mtag_map(mtag_t container, int container_offset, mtag_t tag, int tag_offset)
+{
+	sql_insert(mtag_map, "%lld, %d, %lld, %d", container, container_offset, tag, tag_offset);
+}
+
 void sql_insert_mtag_alias(mtag_t orig, mtag_t alias)
 {
 	sql_insert(mtag_alias, "%lld, %lld", orig, alias);
@@ -401,13 +429,13 @@
 	return 0;
 }
 
-int mtag_map_select_container(mtag_t tag, int offset, mtag_t *container)
+int mtag_map_select_container(mtag_t tag, int container_offset, mtag_t *container)
 {
 	mtag_t tmp = 0;
 
 	run_sql(save_mtag, &tmp,
-		"select container from mtag_map where tag = %lld and offset = %d;",
-		tag, offset);
+		"select container from mtag_map where tag = %lld and container_offset = %d and tag_offset = 0;",
+		tag, container_offset);
 
 	if (tmp == 0 || tmp == -1ULL)
 		return 0;
@@ -420,7 +448,7 @@
 	mtag_t tmp = 0;
 
 	run_sql(save_mtag, &tmp,
-		"select tag from mtag_map where container = %lld and offset = %d;",
+		"select tag from mtag_map where container = %lld and container_offset = %d;",
 		container, offset);
 
 	if (tmp == 0 || tmp == -1ULL)
@@ -622,6 +650,15 @@
 	add_ptr_list(&member_callbacks, member_callback);
 }
 
+void add_caller_info_callback(int owner, void (*callback)(struct expression *call, int param, char *printed_name, struct sm_state *sm))
+{
+	struct member_info_callback *member_callback = __alloc_member_info_callback(0);
+
+	member_callback->owner = owner;
+	member_callback->callback = callback;
+	add_ptr_list(&member_callbacks_new, member_callback);
+}
+
 void add_split_return_callback(void (*fn)(int return_id, char *return_ranges, struct expression *returned_expr))
 {
 	struct returned_state_callback *callback = __alloc_returned_state_callback(0);
@@ -851,7 +888,8 @@
 }
 
 static void print_struct_members(struct expression *call, struct expression *expr, int param, struct stree *stree,
-	void (*callback)(struct expression *call, int param, char *printed_name, struct sm_state *sm))
+	void (*callback)(struct expression *call, int param, char *printed_name, struct sm_state *sm),
+	bool new)
 {
 	struct sm_state *sm;
 	const char *sm_name;
@@ -891,10 +929,14 @@
 		}
 		// FIXME: simplify?
 		if (!add_star && strcmp(name, sm_name) == 0) {
-			if (is_address)
+			if (is_address) {
 				snprintf(printed_name, sizeof(printed_name), "*$");
-			else /* these are already handled. fixme: handle them here */
-				continue;
+			} else {
+				if (new)
+					snprintf(printed_name, sizeof(printed_name), "$");
+				else
+					continue;
+			}
 		} else if (add_star && strcmp(name, sm_name) == 0) {
 			snprintf(printed_name, sizeof(printed_name), "%s*$",
 				 is_address ? "*" : "");
@@ -901,14 +943,19 @@
 		} else if (strncmp(name, sm_name, len) == 0) {
 			if (sm_name[len] != '.' && sm_name[len] != '-')
 				continue;
-			if (is_address)
+			if (is_address && sm_name[len] == '.') {
 				snprintf(printed_name, sizeof(printed_name),
 					 "%s$->%s", add_star ? "*" : "",
 					 sm_name + len + 1);
-			else
+			} else if (is_address && sm_name[len] == '-') {
 				snprintf(printed_name, sizeof(printed_name),
+					 "%s(*$)%s", add_star ? "*" : "",
+					 sm_name + len);
+			} else {
+				snprintf(printed_name, sizeof(printed_name),
 					 "%s$%s", add_star ? "*" : "",
 					 sm_name + len);
+			}
 		} else {
 			continue;
 		}
@@ -936,7 +983,7 @@
 		stree = get_all_states_stree(cb->owner);
 		i = 0;
 		FOR_EACH_PTR(call->args, arg) {
-			print_struct_members(call, arg, i, stree, cb->callback);
+			print_struct_members(call, arg, i, stree, cb->callback, 0);
 			i++;
 		} END_FOR_EACH_PTR(arg);
 		free_stree(&stree);
@@ -945,6 +992,31 @@
 	free_string(name);
 }
 
+static void match_call_info_new(struct expression *call)
+{
+	struct member_info_callback *cb;
+	struct expression *arg;
+	struct stree *stree;
+	char *name;
+	int i;
+
+	name = get_fnptr_name(call->fn);
+	if (!name)
+		return;
+
+	FOR_EACH_PTR(member_callbacks_new, cb) {
+		stree = get_all_states_stree(cb->owner);
+		i = 0;
+		FOR_EACH_PTR(call->args, arg) {
+			print_struct_members(call, arg, i, stree, cb->callback, 1);
+			i++;
+		} END_FOR_EACH_PTR(arg);
+		free_stree(&stree);
+	} END_FOR_EACH_PTR(cb);
+
+	free_string(name);
+}
+
 static int get_param(int param, char **name, struct symbol **sym)
 {
 	struct symbol *arg;
@@ -1106,6 +1178,7 @@
 	if (!sym || !sym->ident)
 		return;
 
+	set_fn_mtag(sym);
 	gettimeofday(&data.start_time, NULL);
 
 	__push_fake_cur_stree();
@@ -1569,7 +1642,7 @@
 {
 	struct sm_state *sm;
 
-	if (!expr || expr_equal_to_param(expr, -1))
+	if (!expr)
 		return 0;
 
 	sm = get_sm_state_expr(SMATCH_EXTRA, expr);
@@ -1707,6 +1780,8 @@
 		return 0;
 	if (estate_min(state).value == 0 && estate_max(state).value == 0)
 		return 0;
+	if (has_possible_negative(sm))
+		return 0;
 	if (!has_separate_zero_null(sm))
 		return 0;
 
@@ -2020,6 +2095,24 @@
 	return strip_expr(last_stmt->expression);
 }
 
+static bool is_kernel_error_path(struct expression *expr)
+{
+	struct range_list *rl;
+
+	/*
+	 * Splitting up returns requires resources.  It also requires resources
+	 * for the caller.  It doesn't seem worth it to split anything up.
+	 */
+	if (!get_implied_rl(expr, &rl))
+		return false;
+	if (rl_type(rl) != &int_ctype)
+		return false;
+	if (rl_min(rl).value >= -4095 &&
+	    rl_max(rl).value < 0)
+		return true;
+	return false;
+}
+
 static void call_return_state_hooks(struct expression *expr)
 {
 	struct returned_state_callback *cb;
@@ -2044,6 +2137,8 @@
 		return;
 	} else if (call_return_state_hooks_conditional(expr)) {
 		return;
+	} else if (is_kernel_error_path(expr)) {
+		goto vanilla;
 	} else if (call_return_state_hooks_split_possible(expr)) {
 		return;
 	} else if (split_positive_from_negative(expr)) {
@@ -2165,6 +2260,7 @@
 		"db/fn_ptr_data_link.schema",
 		"db/fn_data_link.schema",
 		"db/mtag_about.schema",
+		"db/mtag_info.schema",
 		"db/mtag_map.schema",
 		"db/mtag_data.schema",
 		"db/mtag_alias.schema",
@@ -2212,7 +2308,9 @@
 		"db/call_implies.schema",
 		"db/return_implies.schema",
 		"db/type_info.schema",
+		"db/mtag_about.schema",
 		"db/mtag_data.schema",
+		"db/mtag_info.schema",
 		"db/sink_info.schema",
 	};
 	static char buf[4096];
@@ -2277,13 +2375,20 @@
 
 static void dump_cache(struct symbol_list *sym_list)
 {
+	const char *cache_tables[] = {
+		"type_info", "return_implies", "call_implies", "mtag_data",
+		"mtag_info", "mtag_about", "sink_info",
+	};
+	char buf[64];
+	int i;
+
 	if (!option_info)
 		return;
-	cache_sql(&save_cache_data, (char *)"type_info", "select * from type_info;");
-	cache_sql(&save_cache_data, (char *)"return_implies", "select * from return_implies;");
-	cache_sql(&save_cache_data, (char *)"call_implies", "select * from call_implies;");
-	cache_sql(&save_cache_data, (char *)"mtag_data", "select * from mtag_data;");
-	cache_sql(&save_cache_data, (char *)"sink_info", "select * from sink_info;");
+
+	for (i = 0; i < ARRAY_SIZE(cache_tables); i++) {
+		snprintf(buf, sizeof(buf), "select * from %s;", cache_tables[i]);
+		cache_sql(&save_cache_data, (char *)cache_tables[i], buf);
+	}
 }
 
 void open_smatch_db(char *db_file)
@@ -2429,6 +2534,7 @@
 void register_definition_db_callbacks(int id)
 {
 	add_hook(&match_call_info, FUNCTION_CALL_HOOK);
+	add_hook(&match_call_info_new, FUNCTION_CALL_HOOK);
 	add_split_return_callback(match_return_info);
 	add_split_return_callback(print_returned_struct_members);
 	add_hook(&call_return_state_hooks, RETURN_HOOK);
@@ -2493,9 +2599,11 @@
 
 char *get_variable_from_key(struct expression *arg, const char *key, struct symbol **sym)
 {
+	struct symbol *type;
 	char buf[256];
 	char *tmp;
 	int star_cnt = 0;
+	bool add_dot = false;
 
 	if (!arg)
 		return NULL;
@@ -2519,12 +2627,37 @@
 		}
 	}
 
+	if (strncmp(key, "(*$)", 4) == 0) {
+		char buf[64];
+
+		if (arg->type == EXPR_PREOP && arg->op == '&') {
+			arg = strip_expr(arg->unop);
+			snprintf(buf, sizeof(buf), "$%s", key + 4);
+			return get_variable_from_key(arg, buf, sym);
+		} else {
+			tmp = expr_to_var_sym(arg, sym);
+			if (!tmp)
+				return NULL;
+			snprintf(buf, sizeof(buf), "(*%s)%s", tmp, key + 4);
+			free_string(tmp);
+			return alloc_string(buf);
+		}
+	}
+
 	while (key[0] == '*') {
 		star_cnt++;
 		key++;
 	}
 
-	if (arg->type == EXPR_PREOP && arg->op == '&' && star_cnt) {
+	/*
+	 * FIXME:  This is a hack.
+	 * We should be able to parse expressions like (*$)->foo and *$->foo.
+	 */
+	type = get_type(arg);
+	if (is_struct_ptr(type))
+		add_dot = true;
+
+	if (arg->type == EXPR_PREOP && arg->op == '&' && star_cnt && !add_dot) {
 		arg = strip_expr(arg->unop);
 		star_cnt--;
 	}
@@ -2573,6 +2706,14 @@
 	if (star_cnt > 10)
 		return NULL;
 
+	if (strncmp(state_name, "(*", 2) == 0 &&
+	    strncmp(state_name + 2, param_name, name_len) == 0 &&
+	    state_name[name_len + 2] == ')') {
+		snprintf(buf, sizeof(buf), "%.*s(*$)%s", star_cnt, "**********",
+			 state_name + name_len + 3);
+		return alloc_sname(buf);
+	}
+
 	if (strcmp(state_name, param_name) == 0) {
 		snprintf(buf, sizeof(buf), "%.*s$", star_cnt, "**********");
 		return alloc_sname(buf);
--- old/usr/src/tools/smatch/src/smatch_estate.c	Tue May 19 02:50:32 2020
+++ new/usr/src/tools/smatch/src/smatch_estate.c	Tue May 19 02:50:32 2020
@@ -56,6 +56,9 @@
 	if (estate_treat_untagged(s1) && estate_treat_untagged(s2))
 		estate_set_treat_untagged(tmp);
 
+	if (estate_new(s1) || estate_new(s2))
+		estate_set_new(tmp);
+
 	return tmp;
 }
 
@@ -126,6 +129,9 @@
 
 void estate_set_hard_max(struct smatch_state *state)
 {
+	/* pointers don't have a hard max */
+	if (is_ptr_type(estate_type(state)))
+		return;
 	get_dinfo(state)->hard_max = 1;
 }
 
@@ -174,6 +180,18 @@
 	get_dinfo(state)->treat_untagged = true;
 }
 
+bool estate_new(struct smatch_state *state)
+{
+	if (!estate_rl(state))
+		return false;
+	return get_dinfo(state)->set;
+}
+
+void estate_set_new(struct smatch_state *state)
+{
+	get_dinfo(state)->set = true;
+}
+
 sval_t estate_min(struct smatch_state *state)
 {
 	return rl_min(estate_rl(state));
@@ -226,6 +244,10 @@
 		return 0;
 	if (estate_treat_untagged(one) != estate_treat_untagged(two))
 		return 0;
+	if (estate_has_hard_max(one) != estate_has_hard_max(two))
+		return 0;
+	if (estate_new(one) != estate_new(two))
+		return 0;
 	if (strcmp(one->name, two->name) == 0)
 		return 1;
 	return 0;
--- old/usr/src/tools/smatch/src/smatch_expressions.c	Tue May 19 02:50:32 2020
+++ new/usr/src/tools/smatch/src/smatch_expressions.c	Tue May 19 02:50:32 2020
@@ -27,7 +27,7 @@
 	struct expression *expr;
 
 	expr = __alloc_tmp_expression(0);
-	expr->smatch_flags |= Fake;
+	expr->smatch_flags |= Tmp;
 	expr->type = type;
 	expr->pos = pos;
 	return expr;
@@ -300,7 +300,7 @@
 {
 	if (!expr)
 		return;
-	if (parent->smatch_flags & Fake)
+	if (parent && parent->smatch_flags & Tmp)
 		return;
 
 	expr->parent = (unsigned long)parent | 0x1UL;
--- old/usr/src/tools/smatch/src/smatch_extra.c	Tue May 19 02:50:32 2020
+++ new/usr/src/tools/smatch/src/smatch_extra.c	Tue May 19 02:50:32 2020
@@ -36,7 +36,6 @@
 
 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);
 
@@ -172,12 +171,17 @@
 static bool in_param_set;
 void set_extra_mod_helper(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
 {
+	struct expression *faked;
+
 	if (!expr)
 		expr = gen_expression_from_name_sym(name, sym);
 	remove_from_equiv(name, sym);
 	set_union_info(name, sym, expr, state);
 	call_extra_mod_hooks(name, sym, expr, state);
-	update_mtag_data(expr, state);
+	faked = get_faked_expression();
+	if (!faked ||
+	    (faked->type == EXPR_ASSIGNMENT && is_fresh_alloc(faked->right)))
+		update_mtag_data(expr, state);
 	if (in_param_set &&
 	    estate_is_unknown(state) && !get_state(SMATCH_EXTRA, name, sym))
 		return;
@@ -2800,19 +2804,15 @@
 	struct range_list *rl = NULL;
 	struct smatch_state *state;
 	struct symbol *type;
-	char fullname[256];
 	char *key_orig = key;
-	bool add_star = false;
+	char *fullname;
 	sval_t dummy;
 
-	if (key[0] == '*') {
-		add_star = true;
-		key++;
-	}
-
-	snprintf(fullname, 256, "%s%s%s", add_star ? "*" : "", name, key + 1);
-
 	expr = symbol_expression(sym);
+	fullname = get_variable_from_key(expr, key, NULL);
+	if (!fullname)
+		return;
+
 	type = get_member_type_from_key(expr, key_orig);
 	str_to_rl(type, value, &rl);
 	state = alloc_estate_rl(rl);
@@ -2823,17 +2823,16 @@
 
 static void set_param_fuzzy_max(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 *fullname;
 	sval_t max;
 
-	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
+	expr = symbol_expression(sym);
+	fullname = get_variable_from_key(expr, key, NULL);
+	if (!fullname)
 		return;
 
 	state = get_state(SMATCH_EXTRA, fullname, sym);
@@ -2849,13 +2848,12 @@
 static void set_param_hard_max(const char *name, struct symbol *sym, char *key, char *value)
 {
 	struct smatch_state *state;
-	char fullname[256];
+	struct expression *expr;
+	char *fullname;
 
-	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
+	expr = symbol_expression(sym);
+	fullname = get_variable_from_key(expr, key, NULL);
+	if (!fullname)
 		return;
 
 	state = get_state(SMATCH_EXTRA, fullname, sym);
--- old/usr/src/tools/smatch/src/smatch_extra.h	Tue May 19 02:50:33 2020
+++ new/usr/src/tools/smatch/src/smatch_extra.h	Tue May 19 02:50:33 2020
@@ -32,6 +32,7 @@
 	unsigned int hard_max:1;
 	unsigned int capped:1;
 	unsigned int treat_untagged:1;
+	unsigned int set:1;
 };
 DECLARE_ALLOCATOR(data_info);
 
@@ -101,6 +102,7 @@
 struct range_list *cast_rl(struct symbol *type, struct range_list *rl);
 int get_implied_rl(struct expression *expr, struct range_list **rl);
 int get_absolute_rl(struct expression *expr, struct range_list **rl);
+void set_real_absolute(struct expression *expr, struct smatch_state *state);
 int get_real_absolute_rl(struct expression *expr, struct range_list **rl);
 struct range_list *var_to_absolute_rl(struct expression *expr);
 int custom_get_absolute_rl(struct expression *expr,
@@ -151,6 +153,8 @@
 void estate_set_capped(struct smatch_state *state);
 bool estate_treat_untagged(struct smatch_state *state);
 void estate_set_treat_untagged(struct smatch_state *state);
+bool estate_new(struct smatch_state *state);
+void estate_set_new(struct smatch_state *state);
 
 int estate_get_single_value(struct smatch_state *state, sval_t *sval);
 struct smatch_state *get_implied_estate(struct expression *expr);
--- old/usr/src/tools/smatch/src/smatch_flow.c	Tue May 19 02:50:33 2020
+++ new/usr/src/tools/smatch/src/smatch_flow.c	Tue May 19 02:50:33 2020
@@ -438,20 +438,20 @@
 
 		/* foo = !bar() */
 		if (__handle_condition_assigns(expr))
-			break;
+			goto after_assign;
 		/* foo = (x < 5 ? foo : 5); */
 		if (__handle_select_assigns(expr))
-			break;
+			goto after_assign;
 		/* foo = ({frob(); frob(); frob(); 1;}) */
 		if (__handle_expr_statement_assigns(expr))
-			break;
+			break;  // FIXME: got after
 		/* foo = (3, 4); */
 		if (handle_comma_assigns(expr))
-			break;
-		if (handle_postop_assigns(expr))
-			break;
+			goto after_assign;
 		if (handle__builtin_choose_expr_assigns(expr))
-			break;
+			goto after_assign;
+		if (handle_postop_assigns(expr))
+			break;  /* no need to goto after_assign */
 
 		__split_expr(expr->right);
 		if (outside_of_function())
@@ -470,8 +470,8 @@
 		    get_macro_name(expr->pos) != get_macro_name(right->pos))
 			__pass_to_client(expr, MACRO_ASSIGNMENT_HOOK);
 
+after_assign:
 		__pass_to_client(expr, ASSIGNMENT_HOOK_AFTER);
-
 		__split_expr(expr->left);
 		break;
 	}
--- old/usr/src/tools/smatch/src/smatch_fresh_alloc.c	Tue May 19 02:50:33 2020
+++ new/usr/src/tools/smatch/src/smatch_fresh_alloc.c	Tue May 19 02:50:33 2020
@@ -23,6 +23,7 @@
  */
 
 #include "smatch.h"
+#include "smatch_extra.h"
 #include "smatch_slist.h"
 
 static int my_id;
@@ -65,6 +66,16 @@
 	{},
 };
 
+static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
+{
+	struct smatch_state *state;
+	sval_t sval;
+
+	state = get_state(SMATCH_EXTRA, cur->name, cur->sym);
+	if (estate_get_single_value(state, &sval) && sval.value == 0)
+		set_state(my_id, cur->name, cur->sym, &undefined);
+}
+
 static int fresh_callback(void *fresh, int argc, char **argv, char **azColName)
 {
 	*(int *)fresh = 1;
@@ -150,12 +161,23 @@
 	} END_FOR_EACH_PTR(arg);
 }
 
+static struct expression *handled;
 static void set_fresh(struct expression *expr)
 {
+	struct range_list *rl;
+
 	expr = strip_expr(expr);
 	if (expr->type != EXPR_SYMBOL)
 		return;
+	if (expr == handled)
+		return;
+
+	get_absolute_rl(expr, &rl);
+	rl = rl_intersection(rl, valid_ptr_rl);
+	if (!rl)
+		return;
 	set_state_expr(my_id, expr, &fresh);
+	handled = expr;
 }
 
 static void returns_fresh_alloc(struct expression *expr, int param, char *key, char *value)
@@ -192,4 +214,6 @@
 	select_return_states_hook(FRESH_ALLOC, &returns_fresh_alloc);
 	add_hook(&match_assign, ASSIGNMENT_HOOK);
 	add_hook(&match_call, FUNCTION_CALL_HOOK);
+
+	add_pre_merge_hook(my_id, &pre_merge_hook);
 }
--- old/usr/src/tools/smatch/src/smatch_function_hooks.c	Tue May 19 02:50:33 2020
+++ new/usr/src/tools/smatch/src/smatch_function_hooks.c	Tue May 19 02:50:33 2020
@@ -357,6 +357,7 @@
 	struct range_list *rl;
 	int left;
 	struct stree *stree;
+	struct stree *implied;
 	struct db_implies_list *callbacks;
 	int prev_return_id;
 	int cull;
@@ -367,6 +368,17 @@
 	int handled;
 };
 
+static void set_implied_states(struct db_callback_info *db_info)
+{
+	struct sm_state *sm;
+
+	FOR_EACH_SM(db_info->implied, sm) {
+		__set_sm(sm);
+	} END_FOR_EACH_SM(sm);
+
+	free_stree(&db_info->implied);
+}
+
 static void store_return_state(struct db_callback_info *db_info, const char *ret_str, struct smatch_state *state)
 {
 	db_info->ret_str = alloc_sname(ret_str),
@@ -460,6 +472,26 @@
 	return true;
 }
 
+static void set_fresh_mtag_returns(struct db_callback_info *db_info)
+{
+	struct expression *expr = db_info->expr->left;
+	struct smatch_state *state;
+
+	if (!db_info->ret_state)
+		return;
+
+	state = alloc_estate_rl(cast_rl(get_type(expr), clone_rl(estate_rl(db_info->ret_state))));
+	state = get_mtag_return(db_info->expr, state);
+	if (!state)
+		return;
+
+	set_real_absolute(expr, state);
+	set_extra_expr_mod(expr, state);
+
+	db_info->ret_state = NULL;
+	db_info->ret_str = NULL;
+}
+
 static void set_return_assign_state(struct db_callback_info *db_info)
 {
 	struct expression *expr = db_info->expr->left;
@@ -635,8 +667,8 @@
 	db_info->has_states = 1;
 	if (db_info->prev_return_id != -1 && type == INTERNAL) {
 		set_other_side_state(db_info);
+		set_implied_states(db_info);
 		stree = __pop_fake_cur_stree();
-
 		if (!db_info->cull)
 			merge_fake_stree(&db_info->stree, stree);
 		free_stree(&stree);
@@ -672,7 +704,7 @@
 		if (!possibly_true_rl(var_rl, comparison, ret_range))
 			return 0;
 		if (type == PARAM_LIMIT)
-			param_limit_implications(db_info->expr, param, key, value);
+			param_limit_implications(db_info->expr, param, key, value, &db_info->implied);
 		filter_by_comparison(&var_rl, comparison, ret_range);
 		filter_by_comparison(&ret_range, flip_comparison(comparison), var_rl);
 	} else {
@@ -679,7 +711,7 @@
 		if (!possibly_false_rl(var_rl, comparison, ret_range))
 			return 0;
 		if (type == PARAM_LIMIT)
-			param_limit_implications(db_info->expr, param, key, value);
+			param_limit_implications(db_info->expr, param, key, value, &db_info->implied);
 		filter_by_comparison(&var_rl, negate_comparison(comparison), ret_range);
 		filter_by_comparison(&ret_range, flip_comparison(negate_comparison(comparison)), var_rl);
 	}
@@ -744,6 +776,7 @@
 	sql_select_return_states("return_id, return, type, parameter, key, value",
 				 call_expr, db_compare_callback, &db_info);
 	set_other_side_state(&db_info);
+	set_implied_states(&db_info);
 	stree = __pop_fake_cur_stree();
 	if (!db_info.cull)
 		merge_fake_stree(&db_info.stree, stree);
@@ -769,6 +802,7 @@
 	sql_select_return_states("return_id, return, type, parameter, key, value", call_expr,
 			db_compare_callback, &db_info);
 	set_other_side_state(&db_info);
+	set_implied_states(&db_info);
 	stree = __pop_fake_cur_stree();
 	if (!db_info.cull)
 		merge_fake_stree(&db_info.stree, stree);
@@ -893,6 +927,7 @@
 	if (db_info->prev_return_id != -1 && type == INTERNAL) {
 		call_ranged_return_hooks(db_info);
 		set_return_assign_state(db_info);
+		set_implied_states(db_info);
 		stree = __pop_fake_cur_stree();
 		if (!db_info->cull)
 			merge_fake_stree(&db_info->stree, stree);
@@ -916,7 +951,7 @@
 	}
 
 	if (type == PARAM_LIMIT)
-		param_limit_implications(db_info->expr, param, key, value);
+		param_limit_implications(db_info->expr, param, key, value, &db_info->implied);
 
 	db_info->handled = 1;
 	call_results_to_rl(db_info->expr->right, get_type(strip_expr(db_info->expr->right)), ret_str, &ret_range);
@@ -930,6 +965,7 @@
 		__add_comparison_info(db_info->expr->left, strip_expr(db_info->expr->right), ret_str);
 		__add_return_to_param_mapping(db_info->expr, ret_str);
 		store_return_state(db_info, ret_str, alloc_estate_rl(ret_range));
+		set_fresh_mtag_returns(db_info);
 	}
 
 	FOR_EACH_PTR(db_return_states_list, tmp) {
@@ -968,6 +1004,7 @@
 	if (db_info.handled)
 		call_ranged_return_hooks(&db_info);
 	set_return_assign_state(&db_info);
+	set_implied_states(&db_info);
 	stree = __pop_fake_cur_stree();
 	if (!db_info.cull)
 		merge_fake_stree(&db_info.stree, stree);
@@ -1079,6 +1116,7 @@
 
 	if (db_info->prev_return_id != -1 && type == INTERNAL) {
 		call_ranged_return_hooks(db_info);
+		set_implied_states(db_info);
 		stree = __pop_fake_cur_stree();
 		if (!db_info->cull)
 			merge_fake_stree(&db_info->stree, stree);
@@ -1103,7 +1141,7 @@
 	}
 
 	if (type == PARAM_LIMIT)
-		param_limit_implications(db_info->expr, param, key, value);
+		param_limit_implications(db_info->expr, param, key, value, &db_info->implied);
 
 	call_results_to_rl(db_info->expr, get_type(strip_expr(db_info->expr)), ret_str, &ret_range);
 	ret_range = cast_rl(get_type(db_info->expr), ret_range);
@@ -1154,6 +1192,7 @@
 	sql_select_return_states("return_id, return, type, parameter, key, value",
 			expr, db_return_states_callback, &db_info);
 	call_ranged_return_hooks(&db_info);
+	set_implied_states(&db_info);
 	stree = __pop_fake_cur_stree();
 	if (!db_info.cull)
 		merge_fake_stree(&db_info.stree, stree);
--- old/usr/src/tools/smatch/src/smatch_function_ptrs.c	Tue May 19 02:50:33 2020
+++ new/usr/src/tools/smatch/src/smatch_function_ptrs.c	Tue May 19 02:50:33 2020
@@ -218,7 +218,7 @@
 			return alloc_string(buf);
 		}
 
-		name =  expr_to_var_sym(expr, &sym);
+		name = expr_to_var_sym(expr, &sym);
 		if (!name)
 			return NULL;
 		type = get_type(expr);
--- old/usr/src/tools/smatch/src/smatch_helper.c	Tue May 19 02:50:33 2020
+++ new/usr/src/tools/smatch/src/smatch_helper.c	Tue May 19 02:50:33 2020
@@ -147,7 +147,7 @@
 	return NULL;
 }
 
-static struct expression *get_array_expr(struct expression *expr)
+struct expression *get_array_expr(struct expression *expr)
 {
 	struct expression *parent;
 	struct symbol *type;
@@ -174,10 +174,8 @@
 
 static void __get_variable_from_expr(struct symbol **sym_ptr, char *buf,
 				     struct expression *expr, int len,
-				     int *complicated, int no_parens)
+				     int *complicated)
 {
-
-
 	if (!expr) {
 		/* can't happen on valid code */
 		*complicated = 1;
@@ -204,7 +202,7 @@
 			}
 		}
 
-		__get_variable_from_expr(sym_ptr, buf, deref, len, complicated, no_parens);
+		__get_variable_from_expr(sym_ptr, buf, deref, len, complicated);
 
 		if (op == '*')
 			append(buf, "->", len);
@@ -236,7 +234,7 @@
 		}
 
 		if (expr->op == '(') {
-			if (!no_parens && expr->unop->type != EXPR_SYMBOL)
+			if (expr->unop->type != EXPR_SYMBOL)
 				append(buf, "(", len);
 		} else if (expr->op != '*' || !get_array_expr(expr->unop)) {
 			tmp = show_special(expr->op);
@@ -243,13 +241,13 @@
 			append(buf, tmp, len);
 		}
 		__get_variable_from_expr(sym_ptr, buf, expr->unop,
-						 len, complicated, no_parens);
+						 len, complicated);
 
-		if (expr->op == '(' && !no_parens && expr->unop->type != EXPR_SYMBOL)
+		if (expr->op == '(' && expr->unop->type != EXPR_SYMBOL)
 			append(buf, ")", len);
 
 		if (expr->op == SPECIAL_DECREMENT ||
-				expr->op == SPECIAL_INCREMENT)
+		    expr->op == SPECIAL_INCREMENT)
 			*complicated = 1;
 
 		return;
@@ -258,7 +256,7 @@
 		const char *tmp;
 
 		__get_variable_from_expr(sym_ptr, buf, expr->unop,
-						 len, complicated, no_parens);
+						 len, complicated);
 		tmp = show_special(expr->op);
 		append(buf, tmp, len);
 
@@ -276,14 +274,14 @@
 		*complicated = 1;
 		array_expr = get_array_expr(expr);
 		if (array_expr) {
-			__get_variable_from_expr(sym_ptr, buf, array_expr, len, complicated, no_parens);
+			__get_variable_from_expr(sym_ptr, buf, array_expr, len, complicated);
 			append(buf, "[", len);
 		} else {
-			__get_variable_from_expr(sym_ptr, buf, expr->left, len, complicated, no_parens);
+			__get_variable_from_expr(sym_ptr, buf, expr->left, len, complicated);
 			snprintf(tmp, sizeof(tmp), " %s ", show_special(expr->op));
 			append(buf, tmp, len);
 		}
-		__get_variable_from_expr(NULL, buf, expr->right, len, complicated, no_parens);
+		__get_variable_from_expr(NULL, buf, expr->right, len, complicated);
 		if (array_expr)
 			append(buf, "]", len);
 		return;
@@ -321,13 +319,13 @@
 		int i;
 
 		*complicated = 1;
-		__get_variable_from_expr(NULL, buf, expr->fn, len, complicated, no_parens);
+		__get_variable_from_expr(NULL, buf, expr->fn, len, complicated);
 		append(buf, "(", len);
 		i = 0;
 		FOR_EACH_PTR(expr->args, tmp) {
 			if (i++)
 				append(buf, ", ", len);
-			__get_variable_from_expr(NULL, buf, tmp, len, complicated, no_parens);
+			__get_variable_from_expr(NULL, buf, tmp, len, complicated);
 		} END_FOR_EACH_PTR(tmp);
 		append(buf, ")", len);
 		return;
@@ -336,7 +334,7 @@
 	case EXPR_FORCE_CAST:
 		__get_variable_from_expr(sym_ptr, buf,
 					 expr->cast_expression, len,
-					 complicated, no_parens);
+					 complicated);
 		return;
 	case EXPR_SIZEOF: {
 		sval_t sval;
@@ -358,16 +356,30 @@
 		if (expr->expr_ident)
 			append(buf, expr->expr_ident->name, len);
 		return;
-	default:
+	case EXPR_SELECT:
+	case EXPR_CONDITIONAL:
 		*complicated = 1;
-		//printf("unknown type = %d\n", expr->type);
+		append(buf, "(", len);
+		__get_variable_from_expr(NULL, buf, expr->conditional, len, complicated);
+		append(buf, ") ?", len);
+		if (expr->cond_true)
+			__get_variable_from_expr(NULL, buf, expr->cond_true, len, complicated);
+		append(buf, ":", len);
+		__get_variable_from_expr(NULL, buf, expr->cond_false, len, complicated);
 		return;
+	default: {
+			char tmp[64];
+
+			snprintf(tmp, sizeof(tmp), "$expr_%p(%d)", expr, expr->type);
+			append(buf, tmp, len);
+			*complicated = 1;
+		}
+		return;
 	}
 }
 
 struct expr_str_cache_results {
 	struct expression *expr;
-	int no_parens;
 	char str[VAR_LEN];
 	struct symbol *sym;
 	int complicated;
@@ -375,7 +387,7 @@
 
 static void get_variable_from_expr(struct symbol **sym_ptr, char *buf,
 				     struct expression *expr, int len,
-				     int *complicated, int no_parens)
+				     int *complicated)
 {
 	static struct expr_str_cache_results cached[8];
 	struct symbol *tmp_sym = NULL;
@@ -383,8 +395,7 @@
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(cached); i++) {
-		if (expr == cached[i].expr &&
-		    no_parens == cached[i].no_parens) {
+		if (expr == cached[i].expr) {
 			strncpy(buf, cached[i].str, len);
 			if (sym_ptr)
 				*sym_ptr = cached[i].sym;
@@ -393,12 +404,14 @@
 		}
 	}
 
-	__get_variable_from_expr(&tmp_sym, buf, expr, len, complicated, no_parens);
+	__get_variable_from_expr(&tmp_sym, buf, expr, len, complicated);
 	if (sym_ptr)
 		*sym_ptr = tmp_sym;
 
+	if (expr->smatch_flags & Tmp)
+		return;
+
 	cached[idx].expr = expr;
-	cached[idx].no_parens = no_parens;
 	strncpy(cached[idx].str, buf, VAR_LEN);
 	cached[idx].sym = tmp_sym;
 	cached[idx].complicated = *complicated;
@@ -427,7 +440,7 @@
 	if (!expr)
 		return NULL;
 	get_variable_from_expr(sym_ptr, var_name, expr, sizeof(var_name),
-				 &complicated, 0);
+			       &complicated);
 	if (complicated < 2)
 		return alloc_string(var_name);
 	else
@@ -458,7 +471,7 @@
 		return NULL;
 	expr = strip_expr(expr);
 	get_variable_from_expr(sym_ptr, var_name, expr, sizeof(var_name),
-				 &complicated, 1);
+			       &complicated);
 
 	if (complicated) {
 		if (sym_ptr)
@@ -1051,9 +1064,24 @@
 	struct symbol *tmp;
 	int i;
 
-	if (!cur_func_sym)
-		return -1;
+	if (!sym)
+		return UNKNOWN_SCOPE;
 
+	if (sym->ctype.modifiers & MOD_TOPLEVEL) {
+		if (sym->ctype.modifiers & MOD_STATIC)
+			return FILE_SCOPE;
+		return GLOBAL_SCOPE;
+	}
+
+	if (!cur_func_sym) {
+		if (!parse_error) {
+			sm_msg("warn: internal.  problem with scope:  %s",
+			       sym->ident ? sym->ident->name : "<anon var>");
+		}
+		return GLOBAL_SCOPE;
+	}
+
+
 	i = 0;
 	FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, tmp) {
 		if (tmp == sym)
@@ -1060,7 +1088,7 @@
 			return i;
 		i++;
 	} END_FOR_EACH_PTR(tmp);
-	return -1;
+	return LOCAL_SCOPE;
 }
 
 int get_param_num(struct expression *expr)
@@ -1069,11 +1097,11 @@
 	char *name;
 
 	if (!cur_func_sym)
-		return -1;
+		return UNKNOWN_SCOPE;
 	name = expr_to_var_sym(expr, &sym);
 	free_string(name);
 	if (!sym)
-		return -1;
+		return UNKNOWN_SCOPE;
 	return get_param_num_from_sym(sym);
 }
 
--- old/usr/src/tools/smatch/src/smatch_implied.c	Tue May 19 02:50:33 2020
+++ new/usr/src/tools/smatch/src/smatch_implied.c	Tue May 19 02:50:33 2020
@@ -72,6 +72,8 @@
 
 bool debug_implied(void)
 {
+	if (option_debug)
+		return true;
 	return implied_debug;
 }
 
@@ -91,6 +93,13 @@
 	return my_list;
 }
 
+static const char *show_comparison(int op)
+{
+	if (op == PARAM_LIMIT)
+		return "<lim>";
+	return show_special(op);
+}
+
 static void print_debug_tf(struct sm_state *sm, int istrue, int isfalse)
 {
 	if (!implied_debug && !option_debug)
@@ -115,6 +124,18 @@
 	}
 }
 
+void split_comparison_helper(struct range_list *left_orig, int op, struct range_list *right_orig,
+		struct range_list **left_true_rl, struct range_list **left_false_rl)
+{
+	if (op == PARAM_LIMIT) {
+		*left_true_rl = rl_intersection(left_orig, right_orig);
+		*left_false_rl = rl_filter(left_orig, right_orig);
+		return;
+	}
+
+	split_comparison_rl(left_orig, op, right_orig, left_true_rl, left_false_rl, NULL, NULL);
+}
+
 static int create_fake_history(struct sm_state *sm, int comparison, struct range_list *rl)
 {
 	struct range_list *orig_rl;
@@ -131,7 +152,7 @@
 		return 0;
 
 	orig_rl = cast_rl(rl_type(rl), estate_rl(sm->state));
-	split_comparison_rl(orig_rl, comparison, rl, &true_rl, &false_rl, NULL, NULL);
+	split_comparison_helper(orig_rl, comparison, rl, &true_rl, &false_rl);
 
 	true_rl = rl_truncate_cast(estate_type(sm->state), true_rl);
 	false_rl = rl_truncate_cast(estate_type(sm->state), false_rl);
@@ -154,7 +175,7 @@
 
 	if (implied_debug)
 		sm_msg("fake_history: %s vs %s.  %s %s %s. --> T: %s F: %s",
-		       sm->name, show_rl(rl), sm->state->name, show_special(comparison), show_rl(rl),
+		       sm->name, show_rl(rl), sm->state->name, show_comparison(comparison), show_rl(rl),
 		       show_rl(true_rl), show_rl(false_rl));
 
 	true_sm = clone_sm(sm);
@@ -233,6 +254,32 @@
 	return ret;
 }
 
+static bool possibly_true_helper(struct range_list *var_rl, int comparison, struct range_list *rl)
+{
+	if (comparison == PARAM_LIMIT) {
+		struct range_list *intersect;
+
+		intersect = rl_intersection(var_rl, rl);
+		if (intersect)
+			return true;
+		return false;
+	}
+	return possibly_true_rl(var_rl, comparison, rl);
+}
+
+static bool possibly_false_helper(struct range_list *var_rl, int comparison, struct range_list *rl)
+{
+	if (comparison == PARAM_LIMIT) {
+		struct range_list *intersect;
+
+		intersect = rl_intersection(var_rl, rl);
+		if (!rl_equiv(var_rl, intersect))
+			return true;
+		return false;
+	}
+	return possibly_false_rl(var_rl, comparison, rl);
+}
+
 /*
  * If 'foo' == 99 add it that pool to the true pools.  If it's false, add it to
  * the false pools.  If we're not sure, then we don't add it to either.
@@ -252,8 +299,8 @@
 
 	var_rl = cast_rl(rl_type(rl), estate_rl(sm->state));
 
-	istrue = !possibly_false_rl(var_rl, comparison, rl);
-	isfalse = !possibly_true_rl(var_rl, comparison, rl);
+	istrue = !possibly_false_helper(var_rl, comparison, rl);
+	isfalse = !possibly_true_helper(var_rl, comparison, rl);
 
 	print_debug_tf(sm, istrue, isfalse);
 
@@ -315,7 +362,7 @@
 	if (diff.tv_sec >= 1) {
 		if (implied_debug) {
 			sm_msg("debug: %s: implications taking too long.  (%s %s %s)",
-			       __func__, sm->state->name, show_special(comparison), show_rl(rl));
+			       __func__, sm->state->name, show_comparison(comparison), show_rl(rl));
 		}
 		if (mixed)
 			*mixed = 1;
@@ -538,9 +585,9 @@
 }
 
 static struct stree *filter_stack(struct sm_state *gate_sm,
-				       struct stree *pre_stree,
-				       const struct state_list *remove_stack,
-				       const struct state_list *keep_stack)
+				  struct stree *pre_stree,
+				  const struct state_list *remove_stack,
+				  const struct state_list *keep_stack)
 {
 	struct stree *ret = NULL;
 	struct sm_state *tmp;
@@ -593,7 +640,7 @@
 	gettimeofday(&time_before, NULL);
 
 	DIMPLIED("checking implications: (%s (%s) %s %s)\n",
-		 sm->name, sm->state->name, show_special(comparison), show_rl(rl));
+		 sm->name, sm->state->name, show_comparison(comparison), show_rl(rl));
 
 	if (!is_merged(sm)) {
 		DIMPLIED("%d '%s' from line %d is not merged.\n", get_lineno(), sm->name, sm->line);
@@ -752,7 +799,7 @@
 	if (!name || !sym)
 		goto free;
 	sm = get_sm_state(SMATCH_EXTRA, name, sym);
-	if (!sm)
+	if (!sm || !sm->merged)
 		goto free;
 
 	separate_and_filter(sm, SPECIAL_NOTEQUAL, tmp_range_list(estate_type(sm->state), 0), __get_cur_stree(), implied_true, implied_false, &mixed);
@@ -770,13 +817,13 @@
 }
 
 static int handled_by_comparison_hook(struct expression *expr,
-				   struct stree **implied_true,
-				   struct stree **implied_false)
+				      struct stree **implied_true,
+				      struct stree **implied_false)
 {
+	struct sm_state *sm, *true_sm, *false_sm;
 	struct state_list *true_stack = NULL;
 	struct state_list *false_stack = NULL;
 	struct stree *pre_stree;
-	struct sm_state *sm;
 
 	sm = comparison_implication_hook(expr, &true_stack, &false_stack);
 	if (!sm)
@@ -787,6 +834,13 @@
 	*implied_true = filter_stack(sm, pre_stree, false_stack, true_stack);
 	*implied_false = filter_stack(sm, pre_stree, true_stack, false_stack);
 
+	true_sm = get_sm_state_stree(*implied_true, sm->owner, sm->name, sm->sym);
+	false_sm = get_sm_state_stree(*implied_false, sm->owner, sm->name, sm->sym);
+	if (true_sm && strcmp(true_sm->state->name, "unknown") == 0)
+		delete_state_stree(implied_true, sm->owner, sm->name, sm->sym);
+	if (false_sm && strcmp(false_sm->state->name, "unknown") == 0)
+		delete_state_stree(implied_false, sm->owner, sm->name, sm->sym);
+
 	free_stree(&pre_stree);
 	free_slist(&true_stack);
 	free_slist(&false_stack);
@@ -953,9 +1007,9 @@
 	set_implied_states(NULL);
 }
 
-void param_limit_implications(struct expression *expr, int param, char *key, char *value)
+void param_limit_implications(struct expression *expr, int param, char *key, char *value, struct stree **implied)
 {
-	struct expression *arg;
+	struct expression *orig_expr, *arg;
 	struct symbol *compare_type;
 	char *name;
 	struct symbol *sym;
@@ -964,10 +1018,14 @@
 	struct stree *implied_true = NULL;
 	struct stree *implied_false = NULL;
 	struct range_list *orig, *limit;
+	char *left_name = NULL;
+	struct symbol *left_sym = NULL;
+	int mixed = 0;
 
 	if (time_parsing_function() > 40)
 		return;
 
+	orig_expr = expr;
 	while (expr->type == EXPR_ASSIGNMENT)
 		expr = strip_expr(expr->right);
 	if (expr->type != EXPR_CALL)
@@ -999,9 +1057,25 @@
 
 	call_results_to_rl(expr, compare_type, value, &limit);
 
-	separate_and_filter(sm, SPECIAL_EQUAL, limit, __get_cur_stree(), &implied_true, &implied_false, NULL);
+	separate_and_filter(sm, PARAM_LIMIT, limit, __get_cur_stree(), &implied_true, &implied_false, &mixed);
 
+	if (orig_expr->type == EXPR_ASSIGNMENT)
+		left_name = expr_to_var_sym(orig_expr->left, &left_sym);
+
 	FOR_EACH_SM(implied_true, tmp) {
+		/*
+		 * What we're trying to do here is preserve the sm state so that
+		 * smatch extra doesn't create a new sm state when it parses the
+		 * PARAM_LIMIT.
+		 */
+		if (!mixed && tmp->sym == sym &&
+		    strcmp(tmp->name, name) == 0 &&
+		    (!left_name || strcmp(left_name, name) != 0)) {
+			overwrite_sm_state_stree(implied, tmp);
+			continue;
+		}
+
+		// TODO why can't this just be __set_sm()?
 		__set_sm_fake_stree(tmp);
 	} END_FOR_EACH_SM(tmp);
 
--- old/usr/src/tools/smatch/src/smatch_kernel_user_data.c	Tue May 19 02:50:34 2020
+++ new/usr/src/tools/smatch/src/smatch_kernel_user_data.c	Tue May 19 02:50:34 2020
@@ -1067,21 +1067,6 @@
 	} END_FOR_EACH_PTR(arg);
 }
 
-static int is_struct_ptr(struct symbol *sym)
-{
-	struct symbol *type;
-
-	if (!sym)
-		return 0;
-	type = get_real_base_type(sym);
-	if (!type || type->type != SYM_PTR)
-		return 0;
-	type = get_real_base_type(type);
-	if (!type || type->type != SYM_STRUCT)
-		return 0;
-	return 1;
-}
-
 static void struct_member_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
 {
 	struct smatch_state *state;
--- old/usr/src/tools/smatch/src/smatch_math.c	Tue May 19 02:50:34 2020
+++ new/usr/src/tools/smatch/src/smatch_math.c	Tue May 19 02:50:34 2020
@@ -335,6 +335,27 @@
 	return left_offset - right_offset;
 }
 
+static bool max_is_unknown_max(struct range_list *rl)
+{
+	/*
+	 * The issue with this code is that we had:
+	 * if (foo > 1) return 1 - foo;
+	 * Ideally we would say that returns s32min-(-1) but what Smatch
+	 * was saying was that the lowest possible value was "1 - INT_MAX"
+	 *
+	 * My solution is to ignore max values for int or larger.  I keep
+	 * the max for shorts etc, because those might be worthwhile.
+	 *
+	 * The problem with just returning 1 - INT_MAX is that that is
+	 * treated as useful information but s32min is treated as basically
+	 * unknown.
+	 */
+
+	if (type_bits(rl_type(rl)) < 31)
+		return false;
+	return sval_is_max(rl_max(rl));
+}
+
 static bool handle_subtract_rl(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res)
 {
 	struct symbol *type;
@@ -409,7 +430,8 @@
 		return true;
 	}
 
-	if (!sval_binop_overflows(rl_min(left_rl), '-', rl_max(right_rl))) {
+	if (!max_is_unknown_max(right_rl) &&
+	    !sval_binop_overflows(rl_min(left_rl), '-', rl_max(right_rl))) {
 		tmp = sval_binop(rl_min(left_rl), '-', rl_max(right_rl));
 		if (sval_cmp(tmp, min) > 0)
 			min = tmp;
@@ -1215,7 +1237,7 @@
 	if (sym_name_is("strlen", expr->fn))
 		return handle_strlen(expr, implied, recurse_cnt, res, res_sval);
 
-	if (implied == RL_EXACT || implied == RL_HARD || implied == RL_FUZZY)
+	if (implied == RL_EXACT || implied == RL_HARD)
 		return false;
 
 	if (custom_handle_variable) {
--- old/usr/src/tools/smatch/src/smatch_mtag.c	Tue May 19 02:50:34 2020
+++ new/usr/src/tools/smatch/src/smatch_mtag.c	Tue May 19 02:50:34 2020
@@ -68,42 +68,64 @@
 	return *tag;
 }
 
-const struct {
-	const char *name;
-	int size_arg;
-} allocator_info[] = {
-	{ "kmalloc", 0 },
-	{ "kzalloc", 0 },
-	{ "devm_kmalloc", 1},
-	{ "devm_kzalloc", 1},
-};
+static int save_allocator(void *_allocator, int argc, char **argv, char **azColName)
+{
+	char **allocator = _allocator;
 
-static bool is_mtag_call(struct expression *expr)
+	if (*allocator) {
+		if (strcmp(*allocator, argv[0]) == 0)
+			return 0;
+		/* should be impossible */
+		free_string(*allocator);
+		*allocator = alloc_string("unknown");
+		return 0;
+	}
+	*allocator = alloc_string(argv[0]);
+	return 0;
+}
+
+char *get_allocator_info_from_tag(mtag_t tag)
 {
-	struct expression *arg;
-	int i;
+	char *allocator = NULL;
+
+	run_sql(save_allocator, &allocator,
+		"select value from mtag_info where tag = %lld and type = %d;",
+		tag, ALLOCATOR);
+
+	return allocator;
+}
+
+static char *get_allocator_info(struct expression *expr, struct smatch_state *state)
+{
 	sval_t sval;
 
+	if (expr->type != EXPR_ASSIGNMENT)
+		return NULL;
+	if (estate_get_single_value(state, &sval))
+		return get_allocator_info_from_tag(sval.value);
+
+	expr = strip_expr(expr->right);
 	if (expr->type != EXPR_CALL ||
-	    expr->fn->type != EXPR_SYMBOL ||
-	    !expr->fn->symbol)
-		return false;
+	    !expr->fn ||
+	    expr->fn->type != EXPR_SYMBOL)
+		return NULL;
+	return expr_to_str(expr->fn);
+}
 
-	for (i = 0; i < ARRAY_SIZE(allocator_info); i++) {
-		if (strcmp(expr->fn->symbol->ident->name, allocator_info[i].name) == 0)
-			break;
-	}
-	if (i == ARRAY_SIZE(allocator_info))
-		return false;
+static void update_mtag_info(struct expression *expr, mtag_t tag,
+			     const char *left_name, const char *tag_info,
+			     struct smatch_state *state)
+{
+	char *allocator;
 
-	arg = get_argument_from_call_expr(expr->args, allocator_info[i].size_arg);
-	if (!get_implied_value(arg, &sval))
-		return false;
+	sql_insert_mtag_about(tag, left_name, tag_info);
 
-	return true;
+	allocator = get_allocator_info(expr, state);
+	if (allocator)
+		sql_insert_mtag_info(tag, ALLOCATOR, allocator);
 }
 
-struct smatch_state *swap_mtag_return(struct expression *expr, struct smatch_state *state)
+struct smatch_state *get_mtag_return(struct expression *expr, struct smatch_state *state)
 {
 	struct expression *left, *right;
 	char *left_name, *right_name;
@@ -114,20 +136,18 @@
 	sval_t tag_sval;
 
 	if (!expr || expr->type != EXPR_ASSIGNMENT || expr->op != '=')
-		return state;
+		return NULL;
+	if (!is_fresh_alloc(expr->right))
+		return NULL;
+	if (!rl_intersection(estate_rl(state), valid_ptr_rl))
+		return NULL;
 
-	if (!estate_rl(state) || strcmp(state->name, "0,4096-ptr_max") != 0)
-		return state;
-
 	left = strip_expr(expr->left);
 	right = strip_expr(expr->right);
 
-	if (!is_mtag_call(right))
-		return state;
-
 	left_name = expr_to_str_sym(left, &left_sym);
 	if (!left_name || !left_sym)
-		return state;
+		return NULL;
 	right_name = expr_to_str(right);
 
 	snprintf(buf, sizeof(buf), "%s %s %s %s", get_filename(), get_function(),
@@ -140,7 +160,7 @@
 	rl = clone_rl(rl);
 	add_range(&rl, tag_sval, tag_sval);
 
-	sql_insert_mtag_about(tag, left_name, buf);
+	update_mtag_info(expr, tag, left_name, buf, state);
 
 	free_string(left_name);
 	free_string(right_name);
--- old/usr/src/tools/smatch/src/smatch_mtag_data.c	Tue May 19 02:50:34 2020
+++ new/usr/src/tools/smatch/src/smatch_mtag_data.c	Tue May 19 02:50:34 2020
@@ -81,8 +81,47 @@
 	return false;
 }
 
+static bool is_head_next(struct expression *expr)
+{
+	struct symbol *type;
+
+	/* Smatch thinks head->next == head is always true.  *sad face* */
+
+	if (option_project != PROJ_KERNEL)
+		return false;
+
+	if (expr->type != EXPR_DEREF)
+		return false;
+	if (!expr->member || !expr->member->name ||
+	    strcmp(expr->member->name, "next") != 0)
+		return false;
+
+	type = get_type(expr->deref);
+	if (!type)
+		return false;
+	if (type->type == SYM_PTR)
+		type = get_real_base_type(type);
+	if (type->type != SYM_STRUCT)
+		return false;
+	if (!type->ident || !type->ident->name ||
+	    strcmp(type->ident->name, "list_head") != 0)
+		return false;
+	return true;
+}
+
+mtag_t ignored_mtag;
+static bool is_ignored_tag(mtag_t tag)
+{
+	if (tag == ignored_mtag)
+		return true;
+	return false;
+}
+
 static void insert_mtag_data(mtag_t tag, int offset, struct range_list *rl)
 {
+	if (is_ignored_tag(tag))
+		return;
+
 	rl = clone_rl_permanent(rl);
 
 	mem_sql(NULL, NULL, "delete from mtag_data where tag = %lld and offset = %d and type = %d",
@@ -104,6 +143,16 @@
 	return false;
 }
 
+static bool parent_is_fresh_alloc(struct expression *expr)
+{
+	struct symbol *sym;
+
+	sym = expr_to_sym(expr);
+	if (!sym || !sym->ident)
+		return false;
+	return is_fresh_alloc_var_sym(sym->ident->name, sym);
+}
+
 void update_mtag_data(struct expression *expr, struct smatch_state *state)
 {
 	struct range_list *orig, *new;
@@ -118,6 +167,8 @@
 		return;
 	if (is_ignored_macro(expr))
 		return;
+	if (is_head_next(expr))
+		return;
 	name = expr_to_var(expr);
 	if (is_kernel_param(name)) {
 		free_string(name);
@@ -132,7 +183,10 @@
 	if (offset == 0 && invalid_type(type))
 		return;
 
-	orig = select_orig(tag, offset);
+	if (parent_is_fresh_alloc(expr))
+		orig = NULL;
+	else
+		orig = select_orig(tag, offset);
 	new = rl_union(orig, estate_rl(state));
 	insert_mtag_data(tag, offset, new);
 }
@@ -146,6 +200,8 @@
 
 	if (is_ignored_macro(expr))
 		return;
+	if (is_head_next(expr->left))
+		return;
 	name = expr_to_var(expr->left);
 	if (is_kernel_param(name)) {
 		free_string(name);
@@ -278,6 +334,7 @@
 {
 	my_id = id;
 
+	ignored_mtag = str_to_mtag("extern boot_params");
 	add_hook(&clear_cache, FUNC_DEF_HOOK);
 
 //	if (!option_info)
--- old/usr/src/tools/smatch/src/smatch_mtag_map.c	Tue May 19 02:50:34 2020
+++ new/usr/src/tools/smatch/src/smatch_mtag_map.c	Tue May 19 02:50:34 2020
@@ -29,8 +29,8 @@
 static void match_assign(struct expression *expr)
 {
 	struct expression *left, *right;
-	mtag_t left_tag;
-	int offset;
+	mtag_t right_tag, left_tag;
+	int right_offset, left_offset;
 	sval_t sval;
 
 	if (expr->op != '=')
@@ -46,13 +46,14 @@
 	if (sval_cmp(sval, valid_ptr_min_sval) < 0 ||
 	    sval_cmp(sval, valid_ptr_max_sval) > 0)
 		return;
-	if (sval.uvalue & MTAG_OFFSET_MASK)
-		return;
+	right_tag = sval.uvalue & ~MTAG_OFFSET_MASK;
+	right_offset = sval.uvalue & MTAG_OFFSET_MASK;
 
-	if (!expr_to_mtag_offset(left, &left_tag, &offset))
+	if (!expr_to_mtag_offset(left, &left_tag, &left_offset) ||
+	    left_offset >= MTAG_OFFSET_MASK)
 		return;
 
-	sql_insert_mtag_map(sval.uvalue, -offset, left_tag);
+	sql_insert_mtag_map(left_tag, left_offset, right_tag, right_offset);
 }
 
 void register_mtag_map(int id)
--- old/usr/src/tools/smatch/src/smatch_param_to_mtag_data.c	Tue May 19 02:50:34 2020
+++ new/usr/src/tools/smatch/src/smatch_param_to_mtag_data.c	Tue May 19 02:50:34 2020
@@ -156,8 +156,9 @@
 //	insert_mtag_data(alias, offset, rl);
 
 	// FIXME:  is arg_offset handled correctly?
-	if (expr_to_mtag_offset(gen_expr, &arg_tag, &arg_offset) && arg_offset == 0)
-		sql_insert_mtag_map(arg_tag, -offset, alias);
+	if (expr_to_mtag_offset(gen_expr, &arg_tag, &arg_offset) &&
+	    arg_offset < MTAG_OFFSET_MASK)
+		sql_insert_mtag_map(alias, offset, arg_tag, arg_offset);
 }
 
 static void call_does_mtag_assign(struct expression *expr, int param, char *key, char *value)
--- old/usr/src/tools/smatch/src/smatch_real_absolute.c	Tue May 19 02:50:34 2020
+++ new/usr/src/tools/smatch/src/smatch_real_absolute.c	Tue May 19 02:50:34 2020
@@ -36,6 +36,11 @@
 
 static int my_id;
 
+void set_real_absolute(struct expression *expr, struct smatch_state *state)
+{
+	set_state_expr(my_id, expr, clone_estate(state));
+}
+
 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
 {
 	struct smatch_state *abs;
--- old/usr/src/tools/smatch/src/smatch_return_to_param.c	Tue May 19 02:50:34 2020
+++ new/usr/src/tools/smatch/src/smatch_return_to_param.c	Tue May 19 02:50:34 2020
@@ -28,7 +28,6 @@
 #include "smatch_slist.h"
 #include "smatch_extra.h"
 
-extern int check_assigned_expr_id;
 static int my_id;
 static int link_id;
 
--- old/usr/src/tools/smatch/src/smatch_scripts/build_kernel_data.sh	Tue May 19 02:50:35 2020
+++ new/usr/src/tools/smatch/src/smatch_scripts/build_kernel_data.sh	Tue May 19 02:50:35 2020
@@ -37,6 +37,7 @@
 
 BUILD_STATUS=0
 $SCRIPT_DIR/test_kernel.sh --call-tree --info --spammy --data=$DATA_DIR || BUILD_STATUS=$?
+echo "smatch_warns.txt built."
 
 for i in $SCRIPT_DIR/gen_* ; do
 	$i smatch_warns.txt -p=kernel
--- old/usr/src/tools/smatch/src/smatch_scripts/summarize_errs.sh	Tue May 19 02:50:35 2020
+++ new/usr/src/tools/smatch/src/smatch_scripts/summarize_errs.sh	Tue May 19 02:50:35 2020
@@ -29,7 +29,7 @@
     read ans
     if echo $ans | grep ^$ > /dev/null ; then
         return
-    fi 
+    fi
 
     #store the result
     echo $sm_err       >> summary
@@ -76,7 +76,7 @@
 
     set_title $sm_err
 
-    #grep -A1 "$file $line" *summary* 2> /dev/null 
+    #grep -A1 "$file $line" *summary* 2> /dev/null
     grep -A1 -F "$last" *summary* 2> /dev/null
 
     ans="?"
@@ -102,5 +102,5 @@
     vim $file +${line}
 
     save_thoughts
-done	
+done
 IFS=
--- old/usr/src/tools/smatch/src/smatch_slist.c	Tue May 19 02:50:35 2020
+++ new/usr/src/tools/smatch/src/smatch_slist.c	Tue May 19 02:50:35 2020
@@ -456,6 +456,9 @@
 	struct sm_state *result;
 	static int warned;
 
+	if (one->state->data && !has_dynamic_states(one->owner))
+		sm_msg("dynamic state: %s", show_sm(one));
+
 	if (one == two)
 		return one;
 	if (out_of_memory()) {
--- old/usr/src/tools/smatch/src/smatch_states.c	Tue May 19 02:50:35 2020
+++ new/usr/src/tools/smatch/src/smatch_states.c	Tue May 19 02:50:35 2020
@@ -74,6 +74,24 @@
 	__print_stree(cur_stree);
 }
 
+bool __print_states(const char *owner)
+{
+	struct sm_state *sm;
+	bool found = false;
+
+	if (!owner)
+		return false;
+
+	FOR_EACH_SM(__get_cur_stree(), sm) {
+		if (!strstr(check_name(sm->owner), owner))
+			continue;
+		sm_msg("%s", show_sm(sm));
+		found = true;
+	} END_FOR_EACH_SM(sm);
+
+	return found;
+}
+
 int unreachable(void)
 {
 	if (!cur_stree)
@@ -91,6 +109,45 @@
 	read_only--;
 }
 
+DECLARE_PTR_LIST(check_tracker_list, check_tracker_hook *);
+static struct check_tracker_list **tracker_hooks;
+
+void add_check_tracker(const char *check_name, check_tracker_hook *fn)
+{
+	check_tracker_hook **p;
+	int owner;
+
+	owner = id_from_name(check_name);
+	if (!owner) {
+		printf("check not found.  '%s'\n", check_name);
+		return;
+	}
+
+	p = malloc(sizeof(check_tracker_hook *));
+	*p = fn;
+	add_ptr_list(&tracker_hooks[owner], p);
+}
+
+static void call_tracker_hooks(int owner, const char *name, struct symbol *sym, struct smatch_state *state)
+{
+	struct check_tracker_list *hooks;
+	check_tracker_hook **fn;
+
+	if ((unsigned short)owner == USHRT_MAX)
+		return;
+
+	hooks = tracker_hooks[owner];
+	FOR_EACH_PTR(hooks, fn) {
+		(*fn)(owner, name, sym, state);
+	} END_FOR_EACH_PTR(fn);
+}
+
+void allocate_tracker_array(int num_checks)
+{
+	tracker_hooks = malloc(num_checks * sizeof(void *));
+	memset(tracker_hooks, 0, num_checks * sizeof(void *));
+}
+
 struct sm_state *set_state(int owner, const char *name, struct symbol *sym, struct smatch_state *state)
 {
 	struct sm_state *ret;
@@ -114,6 +171,8 @@
 				show_state(state));
 	}
 
+	call_tracker_hooks(owner, name, sym, state);
+
 	if (owner != -1 && unreachable())
 		return NULL;
 
--- old/usr/src/tools/smatch/src/smatch_struct_assignment.c	Tue May 19 02:50:35 2020
+++ new/usr/src/tools/smatch/src/smatch_struct_assignment.c	Tue May 19 02:50:35 2020
@@ -151,6 +151,9 @@
 	struct symbol *type;
 	struct expression *assign;
 
+	while (right && right->type == EXPR_ASSIGNMENT)
+		right = strip_parens(right->left);
+
 	type = get_type(left);
 	if (!type)
 		return;
@@ -234,7 +237,6 @@
 	struct expression *assign;
 	int op = '.';
 
-
 	if (__in_fake_assign)
 		return;
 	faked_expression = faked;
@@ -242,6 +244,9 @@
 	left = strip_expr(left);
 	right = strip_expr(right);
 
+	if (left->type == EXPR_PREOP && left->op == '*' && is_pointer(left))
+		left = preop_expression(left, '(');
+
 	struct_type = get_struct_type(left);
 	if (!struct_type) {
 		/*
--- old/usr/src/tools/smatch/src/smatch_type.c	Tue May 19 02:50:35 2020
+++ new/usr/src/tools/smatch/src/smatch_type.c	Tue May 19 02:50:35 2020
@@ -551,6 +551,16 @@
 	return 0;
 }
 
+bool is_struct_ptr(struct symbol *type)
+{
+	if (!type || type->type != SYM_PTR)
+		return false;
+	type = get_real_base_type(type);
+	if (!type || type->type != SYM_STRUCT)
+		return false;
+	return true;
+}
+
 int is_static(struct expression *expr)
 {
 	char *name;
@@ -595,6 +605,23 @@
 	return 1;
 }
 
+bool type_fits(struct symbol *type, struct symbol *test)
+{
+	if (!type || !test)
+		return false;
+
+	if (type == test)
+		return true;
+
+	if (type_bits(test) > type_bits(type))
+		return false;
+	if (type_signed(test) && !type_signed(type))
+		return false;
+	if (type_positive_bits(test) > type_positive_bits(type))
+		return false;
+	return true;
+}
+
 int fn_static(void)
 {
 	return !!(cur_func_sym->ctype.modifiers & MOD_STATIC);
@@ -685,6 +712,8 @@
 struct symbol *get_member_type_from_key(struct expression *expr, const char *key)
 {
 	struct symbol *sym;
+	int star = 0;
+	int i;
 
 	if (strcmp(key, "$") == 0)
 		return get_type(expr);
@@ -702,11 +731,26 @@
 	if (sym->type == SYM_PTR)
 		sym = get_real_base_type(sym);
 
-	key = key + 1;
+	while (*key == '*') {
+		key++;
+		star++;
+	}
+
+	if (*key != '$')
+		return NULL;
+	key++;
+
 	sym = get_member_from_string(sym->symbol_list, key);
 	if (!sym)
 		return NULL;
-	return get_real_base_type(sym);
+	if (sym->type == SYM_RESTRICT || sym->type == SYM_NODE)
+		sym = get_real_base_type(sym);
+	for (i = 0; i < star; i++) {
+		if (!sym || sym->type != SYM_PTR)
+			return NULL;
+		sym = get_real_base_type(sym);
+	}
+	return sym;
 }
 
 struct symbol *get_arg_type_from_key(struct expression *fn, int param, struct expression *arg, const char *key)
--- old/usr/src/tools/smatch/src/smatch_type_val.c	Tue May 19 02:50:35 2020
+++ new/usr/src/tools/smatch/src/smatch_type_val.c	Tue May 19 02:50:35 2020
@@ -337,6 +337,8 @@
 		return 1;
 	if (sym_name_is("i2c_get_clientdata", expr->fn))
 		return 1;
+	if (sym_name_is("idr_find", expr->fn))
+		return 1;
 
 	return 0;
 }
--- old/usr/src/tools/smatch/src/token.h	Tue May 19 02:50:35 2020
+++ new/usr/src/tools/smatch/src/token.h	Tue May 19 02:50:35 2020
@@ -218,6 +218,7 @@
 
 extern int init_stream(const char *, int fd, const char **next_path);
 extern const char *stream_name(int stream);
+struct ident *alloc_ident(const char *name, int len);
 extern struct ident *hash_ident(struct ident *);
 extern struct ident *built_in_ident(const char *);
 extern struct token *built_in_token(int, struct ident *);
--- old/usr/src/tools/smatch/src/tokenize.c	Tue May 19 02:50:36 2020
+++ new/usr/src/tools/smatch/src/tokenize.c	Tue May 19 02:50:36 2020
@@ -829,7 +829,7 @@
 	}
 }
 
-static struct ident *alloc_ident(const char *name, int len)
+struct ident *alloc_ident(const char *name, int len)
 {
 	struct ident *ident = __alloc_ident(len);
 	ident->symbols = NULL;
--- old/usr/src/tools/smatch/src/validation/sm_impossible1.c	Tue May 19 02:50:36 2020
+++ new/usr/src/tools/smatch/src/validation/sm_impossible1.c	Tue May 19 02:50:36 2020
@@ -19,7 +19,7 @@
  * check-command: smatch -I.. sm_impossible1.c
  *
  * check-output-start
-sm_impossible1.c:12 main() [register_impossible_return] 'impossible' = 'impossible'
+sm_impossible1.c:12 main() [register_impossible_return] impossible = 'impossible'
 sm_impossible1.c:14 main() register_impossible_return: no states
  * check-output-end
  */
--- old/usr/src/tools/smatch/src/validation/sm_impossible2.c	Tue May 19 02:50:36 2020
+++ new/usr/src/tools/smatch/src/validation/sm_impossible2.c	Tue May 19 02:50:36 2020
@@ -20,6 +20,6 @@
  *
  * check-output-start
 sm_impossible2.c:12 main() register_impossible_return: no states
-sm_impossible2.c:14 main() [register_impossible_return] 'impossible' = 'impossible'
+sm_impossible2.c:14 main() [register_impossible_return] impossible = 'impossible'
  * check-output-end
  */
