--- old/usr/src/Makefile.smatch	Mon Aug  5 08:37:45 2019
+++ new/usr/src/Makefile.smatch	Mon Aug  5 08:37:45 2019
@@ -28,7 +28,7 @@
 SMATCH_ARGS += -Wno-old-style-definition
 SMATCH_ARGS += -Wno-strict-prototypes
 SMATCH_ARGS += --fatal-checks
-SMATCH_ARGS += --timeout=120
+SMATCH_ARGS += --timeout=0
 
 CERRWARN += $(SMATCH_ARGS:%=-_smatch=%)
 
--- old/usr/src/boot/lib/libstand/Makefile.inc	Mon Aug  5 08:37:45 2019
+++ new/usr/src/boot/lib/libstand/Makefile.inc	Mon Aug  5 08:37:45 2019
@@ -11,6 +11,7 @@
 
 #
 # Copyright 2016 Toomas Soome <tsoome@me.com>
+# Copyright 2019 Joyent, Inc.
 #
 
 #
@@ -85,6 +86,9 @@
 := CFLAGS += -DBZ_LOADER -DBZ_NO_STDIO -DBZ_NO_COMPRESS
 SRCS +=	libstand_bzlib_private.h
 
+# too hairy
+_inflate.o := SMATCH=off
+
 SRCS +=	_bzlib.c _crctable.c _decompress.c _huffman.c _randtable.c
 OBJS +=	_bzlib.o _crctable.o _decompress.o _huffman.o _randtable.o
 CLEANFILES +=	_bzlib.c _crctable.c _decompress.c _huffman.c _randtable.c
--- old/usr/src/boot/sys/boot/Makefile.inc	Mon Aug  5 08:37:46 2019
+++ new/usr/src/boot/sys/boot/Makefile.inc	Mon Aug  5 08:37:46 2019
@@ -11,6 +11,7 @@
 
 #
 # Copyright 2017 Toomas Soome <tsoome@me.com>
+# Copyright 2019 Joyent, Inc.
 #
 
 # loader.help build needs better awk
@@ -52,6 +53,10 @@
 CCASFLAGS=	-fPIC -Wa,--divide
 ASFLAGS=	--divide
 
+SMATCH_ =
+SMATCH_on =
+SMATCH_off = -_smatch=off
+
 # smatch does not define __amd64 and __amd64__
 SMATCH_amd64=	-_smatch=-D__amd64 -_smatch=-D__amd64__
 
@@ -59,6 +64,8 @@
 #CFLAGS +=	$(SMATCH_ARGS:%=-_smatch=%)
 CFLAGS +=	$(SMOFF:%=-_smatch=--disable=%)
 CFLAGS +=	$(SMATCH_$(MACHINE))
+CFLAGS +=	$(SMATCH_$(SMATCH))
+CFLAGS +=	-_smatch=--timeout=0
 
 COMPILE.S=	$(CC) $(SMATCH_off) $(CCASFLAGS) $(CPPFLAGS) -c
 
--- old/usr/src/boot/sys/boot/efi/libefi/i386/Makefile	Mon Aug  5 08:37:47 2019
+++ new/usr/src/boot/sys/boot/efi/libefi/i386/Makefile	Mon Aug  5 08:37:47 2019
@@ -12,6 +12,7 @@
 #
 # Copyright 2016 Toomas Soome <tsoome@me.com>
 # Copyright 2016 RackTop Systems.
+# Copyright 2019 Joyent, Inc.
 #
 
 MACHINE=	$(MACH)
@@ -23,6 +24,9 @@
 
 CFLAGS +=	-m32
 
+# false positive only with a 64-bit smatch
+SMOFF += uninitialized
+
 CLEANFILES +=	machine x86
 
 $(OBJS): machine x86
--- old/usr/src/boot/sys/boot/libstand/Makefile.com	Mon Aug  5 08:37:47 2019
+++ new/usr/src/boot/sys/boot/libstand/Makefile.com	Mon Aug  5 08:37:48 2019
@@ -11,6 +11,7 @@
 
 #
 # Copyright 2016 Toomas Soome <tsoome@me.com>
+# Copyright 2019 Joyent, Inc.
 #
 
 include $(SRC)/Makefile.master
@@ -27,6 +28,15 @@
 
 CPPFLAGS +=	-I$(SRC)/uts/common
 
+# needs work
+printf.o := SMOFF += 64bit_shift
+
+# too hairy
+_inflate.o := SMATCH=off
+
+# 64-bit smatch false positive :/
+SMOFF += uninitialized
+
 clean: clobber
 clobber:
 	$(RM) $(CLEANFILES) $(OBJS) machine $(LIBRARY)
--- old/usr/src/cmd/ls/Makefile.com	Mon Aug  5 08:37:48 2019
+++ new/usr/src/cmd/ls/Makefile.com	Mon Aug  5 08:37:48 2019
@@ -22,7 +22,7 @@
 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# cmd/ls/Makefile.com
+# Copyright 2019 Joyent, Inc.
 #
 
 PROG=		ls
@@ -43,14 +43,14 @@
 
 CFLAGS64 +=	$(CCVERBOSE)
 CPPFLAGS += -D_FILE_OFFSET_BITS=64
-LINTFLAGS64 +=	-errchk=longptr64
 
+# main() can be too hairy
+SMATCH=off
+
 .KEEP_STATE:
 
 all:	$(PROG) $(XPG4) $(XPG6)
 
-lint:	lint_SRCS
-
 clean:
 	$(RM) $(CLEANFILES)
 
@@ -61,7 +61,7 @@
 	$(POST_PROCESS)
 
 %.xpg6: ../%.c
-	$(LINK.c) -o $@ $< $(LDLIBS) 
+	$(LINK.c) -o $@ $< $(LDLIBS)
 	$(POST_PROCESS)
 
 %: ../%.c
--- old/usr/src/cmd/sgs/libld/Makefile.com	Mon Aug  5 08:37:49 2019
+++ new/usr/src/cmd/sgs/libld/Makefile.com	Mon Aug  5 08:37:49 2019
@@ -23,7 +23,7 @@
 # Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# Copyright (c) 2018, Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
 # Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
 
 LIBRARY =	libld.a
@@ -113,6 +113,10 @@
 
 native:=	DYNFLAGS	+= $(CONVLIBDIR)
 
+# too hairy
+pics/sections32.o := SMATCH=off
+pics/sections64.o := SMATCH=off
+
 BLTDEFS =	msg.h
 BLTDATA =	msg.c
 BLTMESG =	$(SGSMSGDIR)/libld
--- old/usr/src/cmd/svc/configd/Makefile	Mon Aug  5 08:37:50 2019
+++ new/usr/src/cmd/svc/configd/Makefile	Mon Aug  5 08:37:50 2019
@@ -24,6 +24,8 @@
 #
 # Copyright 2015 RackTop Systems.
 #
+# Copyright 2019 Joyent, Inc.
+#
 
 MYPROG = svc.configd
 MYOBJS = \
@@ -60,9 +62,12 @@
 CERRWARN += -_gcc=-Wno-unused-variable
 CERRWARN += -_gcc=-Wno-unused-function
 CERRWARN += -_gcc=-Wno-uninitialized
+
+# strange false positive
+SMOFF += free
+
 MYLDLIBS = -lumem -luutil
 LDLIBS	+= -lsecdb -lbsm $(MYLDLIBS)
-LINTFLAGS += -errtags -erroff=E_BAD_FORMAT_ARG_TYPE2 -erroff=E_NAME_DEF_NOT_USED2
 
 CLOBBERFILES +=	$(MYPROG:%=%-native)
 
@@ -125,10 +130,6 @@
 
 clobber:
 
-lint:   lint_SRCS
-
-lint_SRCS:
-
 include ../../Makefile.targ
 
 FRC:
--- old/usr/src/cmd/syseventd/modules/sysevent_conf_mod/Makefile	Mon Aug  5 08:37:50 2019
+++ new/usr/src/cmd/syseventd/modules/sysevent_conf_mod/Makefile	Mon Aug  5 08:37:50 2019
@@ -22,6 +22,8 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
+# Copyright 2019 Joyent, Inc.
+#
 
 LIBRARY =	sysevent_conf_mod
 
@@ -32,11 +34,14 @@
 
 CERRWARN +=	-_gcc=-Wno-uninitialized
 
+# strange smatch false positive
+SMOFF +=       allocating_enough_data
+
 .KEEP_STATE:
 
 all: $(DYNLIB)
 
-install: all 				\
+install: all				\
 	$(ROOTLIBSYSEVENTDIR)		\
 	$(ROOTLIBDIR)			\
 	$(ROOTLIBS)
--- old/usr/src/common/ficl/vm.c	Mon Aug  5 08:37:51 2019
+++ new/usr/src/common/ficl/vm.c	Mon Aug  5 08:37:51 2019
@@ -47,6 +47,10 @@
  * SUCH DAMAGE.
  */
 
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
 #include "ficl.h"
 
 #if FICL_ROBUST >= 2
@@ -2165,8 +2169,8 @@
 	char *pad = (char *)vm->pad;
 	s = ficlVmGetWord(vm);
 
-	if (FICL_STRING_GET_LENGTH(s) > FICL_PAD_SIZE)
-		FICL_STRING_SET_LENGTH(s, FICL_PAD_SIZE);
+	if (FICL_STRING_GET_LENGTH(s) >= FICL_PAD_SIZE)
+		FICL_STRING_SET_LENGTH(s, FICL_PAD_SIZE - 1);
 
 	(void) strncpy(pad, FICL_STRING_GET_POINTER(s),
 	    FICL_STRING_GET_LENGTH(s));
--- old/usr/src/lib/libpctx/Makefile.com	Mon Aug  5 08:37:51 2019
+++ new/usr/src/lib/libpctx/Makefile.com	Mon Aug  5 08:37:51 2019
@@ -22,7 +22,7 @@
 # Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
+# Copyright 2019 Joyent, Inc.
 #
 
 LIBRARY = libpctx.a
@@ -33,25 +33,20 @@
 # include library definitions
 include ../../Makefile.lib
 
-LIBS = $(DYNLIB) $(LINTLIB)
-$(LINTLIB) :=	SRCS = ../common/llib-lpctx
+LIBS = $(DYNLIB)
 LDLIBS +=	-lproc -lc
 
 SRCDIR =	../common
 
 CFLAGS +=	$(CCVERBOSE)
-CPPFLAGS += 	-D_REENTRANT -I$(SRCDIR)
+CPPFLAGS +=	-D_REENTRANT -I$(SRCDIR)
 
+# false positive: pctx_run() error: dereferencing freed memory 'pctx'
+SMOFF += free
+
 .KEEP_STATE:
 
 all: $(LIBS)
 
-# x86 and sparc have different alignment complaints (all LINTED).
-# Make lint shut up about suppression directive not used.
-lint := LINTFLAGS += -erroff=E_SUPPRESSION_DIRECTIVE_UNUSED
-lint := LINTFLAGS64 += -erroff=E_SUPPRESSION_DIRECTIVE_UNUSED
-
-lint: lintcheck
-
 # include library targets
 include ../../Makefile.targ
--- old/usr/src/lib/libumem/Makefile.com	Mon Aug  5 08:37:52 2019
+++ new/usr/src/lib/libumem/Makefile.com	Mon Aug  5 08:37:52 2019
@@ -22,7 +22,7 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# Copyright (c) 2019, Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
 #
 
 #
@@ -112,7 +112,7 @@
 CLOBBERFILES += $(CLOBBERFILES_$(CURTYPE))
 
 LIBS_standalone = $(STANDLIBRARY)
-LIBS_library = $(DYNLIB) $(LINTLIB)
+LIBS_library = $(DYNLIB)
 LIBS =	$(LIBS_$(CURTYPE))
 
 MAPFILE_SUPPLEMENTAL_standalone = ../common/stand_mapfile
@@ -128,8 +128,6 @@
 ASFLAGS_library =
 ASFLAGS += -P $(ASFLAGS_$(CURTYPE)) -D_ASM
 
-$(LINTLIB) := SRCS = ../common/$(LINTSRC)
-
 # We want the thread-specific errno in the library, but we don't want it in
 # the standalone.  $(DTS_ERRNO) is designed to add -D_TS_ERRNO to $(CPPFLAGS),
 # in order to enable this feature.  Conveniently, -D_REENTRANT does the same
@@ -157,13 +155,11 @@
 CFLAGS64_standalone = $(STAND_FLAGS_64)
 CFLAGS64 += $(CCVERBOSE) $(CFLAGS64_$(CURTYPE)) $(CFLAGS64_common)
 
-INSTALL_DEPS_library =		$(ROOTLINKS) $(ROOTLINT) $(ROOTLIBS)
+# false positive for umem_alloc_sizes_add()
+pics/umem.o := SMOFF += index_overflow
+objs/umem.o := SMOFF += index_overflow
 
-#
-# turn off ptr-cast warnings, since we do them all the time
-#
-LINTFLAGS +=	-erroff=E_BAD_PTR_CAST_ALIGN
-LINTFLAGS64 +=	-erroff=E_BAD_PTR_CAST_ALIGN
+INSTALL_DEPS_library =		$(ROOTLINKS) $(ROOTLIBS)
 
 DYNFLAGS +=     $(ZINTERPOSE)
 
--- old/usr/src/tools/smatch/Makefile	Mon Aug  5 08:37:52 2019
+++ new/usr/src/tools/smatch/Makefile	Mon Aug  5 08:37:52 2019
@@ -13,7 +13,7 @@
 
 #
 # The src/ sub-directory is un-modified copy of
-# https://github.com/illumos/smatch/tree/0.5.1-il-3
+# https://github.com/illumos/smatch/tree/0.5.1-il-4
 #
 # This Makefile installs just enough for us to be able to run smatch
 # locally.
@@ -20,7 +20,7 @@
 #
 
 PROG = smatch
-SPARSE_VERSION = 0.5.1-il-3
+SPARSE_VERSION = 0.5.1-il-4
 
 include ../Makefile.tools
 
@@ -28,7 +28,7 @@
 i386_CC = $(GNUC_ROOT)/bin/gcc
 sparc_CC = $(GNUC_ROOT)/bin/gcc
 
-CFLAGS = -O -D__sun -Wall -Wno-unknown-pragmas -std=gnu99 -nodefaultlibs
+CFLAGS = -O -m64 -msave-args -D__sun -Wall -Wno-unknown-pragmas -std=gnu99 -nodefaultlibs
 
 SMATCHDATADIR = $(ROOTONBLDSHARE)/smatch
 
@@ -77,7 +77,7 @@
 	smatch_mtag_map.o smatch_mtag_data.o \
 	smatch_param_to_mtag_data.o smatch_mem_tracker.o smatch_array_values.o \
 	smatch_nul_terminator.o smatch_assigned_expr.o smatch_kernel_user_data.o \
-	smatch_statement_count.o
+	smatch_statement_count.o smatch_bits.o smatch_integer_overflow.o
 
 OBJS += target.o parse.o tokenize.o pre-process.o symbol.o lib.o scope.o \
 	expression.o show-parse.o evaluate.o expand.o inline.o linearize.o \
--- old/usr/src/tools/smatch/src/Documentation/sparse-README.txt	Mon Aug  5 08:37:52 2019
+++ new/usr/src/tools/smatch/src/Documentation/sparse-README.txt	Mon Aug  5 08:37:52 2019
@@ -1,5 +1,5 @@
 
-  sparse (sp�rs), adj,., spars-er, spars-est.
+  sparse (spärs), adj,., spars-er, spars-est.
 	1. thinly scattered or distributed; "a sparse population"
 	2. thin; not thick or dense: "sparse hair"
 	3. scanty; meager.
--- old/usr/src/tools/smatch/src/Makefile	Mon Aug  5 08:37:53 2019
+++ new/usr/src/tools/smatch/src/Makefile	Mon Aug  5 08:37:53 2019
@@ -1,4 +1,4 @@
-VERSION=0.5.1
+VERSION=0.5.1-il-4
 
 # Generating file version.h if current version has changed
 SPARSE_VERSION:=$(shell git describe 2>/dev/null || echo '$(VERSION)')
@@ -90,7 +90,7 @@
 	smatch_mtag_map.o smatch_mtag_data.o \
 	smatch_param_to_mtag_data.o smatch_mem_tracker.o smatch_array_values.o \
 	smatch_nul_terminator.o smatch_assigned_expr.o smatch_kernel_user_data.o \
-	smatch_statement_count.o
+	smatch_statement_count.o smatch_integer_overflow.o smatch_bits.o
 
 SMATCH_CHECKS=$(shell ls check_*.c | sed -e 's/\.c/.o/')
 SMATCH_DATA=smatch_data/kernel.allocation_funcs \
--- old/usr/src/tools/smatch/src/check_64bit_shift.c	Mon Aug  5 08:37:53 2019
+++ new/usr/src/tools/smatch/src/check_64bit_shift.c	Mon Aug  5 08:37:53 2019
@@ -16,9 +16,40 @@
  */
 
 #include "smatch.h"
+#include "smatch_extra.h"
 
 static int my_id;
 
+static void match_shift_mask(struct expression *expr)
+{
+	struct expression *right, *shifter;
+	struct range_list *rl;
+	char *str;
+
+	expr = strip_expr(expr);
+	if (expr->type != EXPR_BINOP || expr->op != '&')
+		return;
+
+	if (get_type(expr->left) != &ullong_ctype)
+		return;
+
+	if (type_bits(get_type(expr->right)) == 64)
+		return;
+
+	right = strip_expr(expr->right);
+	if (right->type != EXPR_BINOP || right->op != SPECIAL_LEFTSHIFT)
+		return;
+
+	shifter = strip_expr(right->right);
+	get_real_absolute_rl(shifter, &rl);
+	if (rl_max(rl).uvalue < 32)
+		return;
+
+	str = expr_to_str(expr->right);
+	sm_warning("should '%s' be a 64 bit type?", str);
+	free_string(str);
+}
+
 static void match_shift_assignment(struct expression *expr)
 {
 	struct symbol *left_type, *right_type;
@@ -63,4 +94,5 @@
 	my_id = id;
 
 	add_hook(&match_shift_assignment, ASSIGNMENT_HOOK);
+	add_hook(&match_shift_mask, BINOP_HOOK);
 }
--- old/usr/src/tools/smatch/src/check_buffer_too_small_for_struct.c	Mon Aug  5 08:37:54 2019
+++ new/usr/src/tools/smatch/src/check_buffer_too_small_for_struct.c	Mon Aug  5 08:37:54 2019
@@ -26,6 +26,8 @@
 	struct symbol *left_type, *right_type;
 	struct expression *size_expr;
 	sval_t min_size;
+	int limit_type;
+	int bytes;
 
 	left_type = get_type(expr->left);
 	if (!left_type || left_type->type != SYM_PTR)
@@ -43,9 +45,15 @@
 	if (right_type != &void_ctype && type_bits(right_type) != 8)
 		return;
 
-	size_expr = get_size_variable(expr->right);
+	bytes = get_array_size_bytes(expr->right);
+	if (bytes >= type_bytes(left_type))
+		return;
+
+	size_expr = get_size_variable(expr->right, &limit_type);
 	if (!size_expr)
 		return;
+	if (limit_type != ELEM_COUNT)
+		return;
 
 	get_absolute_min(size_expr, &min_size);
 	if (min_size.value >= type_bytes(left_type))
@@ -62,6 +70,7 @@
 	char *name;
 	struct expression *size_expr;
 	sval_t min_size;
+	int limit_type;
 
 	if (expr->type != EXPR_PREOP)
 		return;
@@ -79,9 +88,11 @@
 		return;
 
 	right = get_assigned_expr(expr);
-	size_expr = get_size_variable(right);
+	size_expr = get_size_variable(right, &limit_type);
 	if (!size_expr)
 		return;
+	if (limit_type != ELEM_COUNT)
+		return;
 
 	get_absolute_min(size_expr, &min_size);
 	if (min_size.value >= type_bytes(left_type))
--- /dev/null	Mon Aug  5 08:37:55 2019
+++ new/usr/src/tools/smatch/src/check_cmn_err.c	Mon Aug  5 08:37:55 2019
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 Oracle.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
+ *
+ * Copyright 2019 Joyent, Inc.
+ */
+
+/*
+ * Heavily borrowed from check_wine.c: what we're doing here is teaching smatch
+ * that cmn_err(CE_PANIC, ...) is noreturn.
+ */
+
+#include "scope.h"
+#include "smatch.h"
+#include "smatch_extra.h"
+
+#define	CE_PANIC (3)
+
+void match_cmn_err(const char *fn, struct expression *expr,
+			void *unused)
+{
+	struct expression *arg;
+	sval_t sval;
+
+	arg = get_argument_from_call_expr(expr->args, 0);
+	if (!get_implied_value(arg, &sval))
+		return;
+
+	if (sval.value == CE_PANIC)
+		nullify_path();
+}
+
+
+void check_cmn_err(int id)
+{
+	if (option_project != PROJ_ILLUMOS_KERNEL)
+		return;
+
+	add_function_hook("cmn_err", &match_cmn_err, NULL);
+}
--- old/usr/src/tools/smatch/src/check_debug.c	Mon Aug  5 08:37:55 2019
+++ new/usr/src/tools/smatch/src/check_debug.c	Mon Aug  5 08:37:55 2019
@@ -19,6 +19,11 @@
 #include "smatch_slist.h"
 #include "smatch_extra.h"
 
+void show_sname_alloc(void);
+void show_data_range_alloc(void);
+void show_ptrlist_alloc(void);
+void show_sm_state_alloc(void);
+
 int local_debug;
 static int my_id;
 char *trace_variable;
@@ -198,7 +203,7 @@
 static void match_user_rl(const char *fn, struct expression *expr, void *info)
 {
 	struct expression *arg;
-	struct range_list *rl;
+	struct range_list *rl = NULL;
 	char *name;
 
 	arg = get_argument_from_call_expr(expr->args, 0);
@@ -384,6 +389,7 @@
 	int elements, bytes;
 	char *name;
 	char buf[256] = "";
+	int limit_type;
 	int n;
 	sval_t sval;
 
@@ -392,7 +398,7 @@
 	elements = get_array_size(arg);
 	bytes = get_array_size_bytes_max(arg);
 	rl = get_array_size_bytes_rl(arg);
-	comp = get_size_variable(arg);
+	comp = get_size_variable(arg, &limit_type);
 
 	name = expr_to_str(arg);
 	n = snprintf(buf, sizeof(buf), "buf size: '%s' %d elements, %d bytes", name, elements, bytes);
@@ -403,7 +409,7 @@
 
 	if (comp) {
 		name = expr_to_str(comp);
-		snprintf(buf + n, sizeof(buf) - n, "[size_var=%s]", name);
+		snprintf(buf + n, sizeof(buf) - n, "[size_var=%s %s]", limit_type_str(limit_type), name);
 		free_string(name);
 	}
 	sm_msg("%s", buf);
@@ -504,16 +510,6 @@
 	local_debug = 0;
 }
 
-static void match_debug_implied_on(const char *fn, struct expression *expr, void *info)
-{
-	option_debug_implied = 1;
-}
-
-static void match_debug_implied_off(const char *fn, struct expression *expr, void *info)
-{
-	option_debug_implied = 0;
-}
-
 static void match_about(const char *fn, struct expression *expr, void *info)
 {
 	struct expression *arg;
@@ -644,11 +640,12 @@
 	struct expression *arg;
 	char *name;
 	mtag_t tag = 0;
+	int offset = 0;
 
 	arg = get_argument_from_call_expr(expr->args, 0);
 	name = expr_to_str(arg);
-	get_mtag(arg, &tag);
-	sm_msg("mtag: '%s' => tag: %lld", name, tag);
+	expr_to_mtag_offset(arg, &tag, &offset);
+	sm_msg("mtag: '%s' => tag: %llu %d", name, tag, offset);
 	free_string(name);
 }
 
@@ -666,6 +663,22 @@
 	free_string(name);
 }
 
+static void match_container(const char *fn, struct expression *expr, void *info)
+{
+	struct expression *container, *x;
+	char *cont, *name, *str;
+
+	container = get_argument_from_call_expr(expr->args, 0);
+	x = get_argument_from_call_expr(expr->args, 1);
+
+	str = get_container_name(container, x);
+	cont = expr_to_str(container);
+	name = expr_to_str(x);
+	sm_msg("container: '%s' vs '%s' --> '%s'", cont, name, str);
+	free_string(cont);
+	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);
@@ -754,8 +767,6 @@
 	add_function_hook("__smatch_debug_off", &match_debug_off, NULL);
 	add_function_hook("__smatch_local_debug_on", &match_local_debug_on, NULL);
 	add_function_hook("__smatch_local_debug_off", &match_local_debug_off, NULL);
-	add_function_hook("__smatch_debug_implied_on", &match_debug_implied_on, NULL);
-	add_function_hook("__smatch_debug_implied_off", &match_debug_implied_off, NULL);
 	add_function_hook("__smatch_intersection", &match_intersection, NULL);
 	add_function_hook("__smatch_type", &match_type, NULL);
 	add_implied_return_hook("__smatch_type_rl_helper", &match_type_rl_return, NULL);
@@ -766,6 +777,7 @@
 	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);
+	add_function_hook("__smatch_container", &match_container, NULL);
 
 	add_hook(free_old_stree, AFTER_FUNC_HOOK);
 	add_hook(trace_var, STMT_HOOK_AFTER);
--- old/usr/src/tools/smatch/src/check_debug.h	Mon Aug  5 08:37:56 2019
+++ new/usr/src/tools/smatch/src/check_debug.h	Mon Aug  5 08:37:56 2019
@@ -74,4 +74,6 @@
 
 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_dma_mapping_error.c	Mon Aug  5 08:37:56 2019
+++ new/usr/src/tools/smatch/src/check_dma_mapping_error.c	Mon Aug  5 08:37:56 2019
@@ -32,6 +32,12 @@
 
 static void match_assign(const char *fn, struct expression *expr, void *unused)
 {
+	struct range_list *rl;
+
+	if (!get_implied_rl(expr->right, &rl))
+		return;
+	if (rl_max(rl).value != 1)
+		return;
 	set_state_expr(my_id, expr->left, &positive);
 }
 
--- old/usr/src/tools/smatch/src/check_err_ptr_deref.c	Mon Aug  5 08:37:56 2019
+++ new/usr/src/tools/smatch/src/check_err_ptr_deref.c	Mon Aug  5 08:37:56 2019
@@ -222,7 +222,7 @@
 	return_implies_state("IS_ERR_OR_NULL", 0, 0, &match_checked, NULL);
 	return_implies_state("IS_ERR_OR_NULL", 1, 1, &match_err, NULL);
 	return_implies_state("PTR_RET", 0, 0, &match_checked, NULL);
-	return_implies_state("PTR_RET", -4096, -1, &match_err, NULL);
+	return_implies_state("PTR_RET", -4095, -1, &match_err, NULL);
 	register_err_ptr_funcs();
 	add_hook(&match_dereferences, DEREF_HOOK);
 	add_function_hook("ERR_PTR", &match_err_ptr_positive_const, NULL);
--- old/usr/src/tools/smatch/src/check_free_strict.c	Mon Aug  5 08:37:57 2019
+++ new/usr/src/tools/smatch/src/check_free_strict.c	Mon Aug  5 08:37:57 2019
@@ -291,6 +291,38 @@
 	return ret;
 }
 
+static void match_untracked(struct expression *call, int param)
+{
+	struct state_list *slist = NULL;
+	struct expression *arg;
+	struct sm_state *sm;
+	char *name;
+	char buf[64];
+	int len;
+
+	arg = get_argument_from_call_expr(call->args, param);
+	if (!arg)
+		return;
+
+	name = expr_to_var(arg);
+	if (!name)
+		return;
+	snprintf(buf, sizeof(buf), "%s->", name);
+	free_string(name);
+	len = strlen(buf);
+
+	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
+		if (strncmp(sm->name, buf, len) == 0)
+			add_ptr_list(&slist, sm);
+	} END_FOR_EACH_SM(sm);
+
+	FOR_EACH_PTR(slist, sm) {
+		set_state(sm->owner, sm->name, sm->sym, &ok);
+	} END_FOR_EACH_PTR(sm);
+
+	free_slist(&slist);
+}
+
 void check_free_strict(int id)
 {
 	my_id = id;
@@ -311,4 +343,5 @@
 	add_pre_merge_hook(my_id, &pre_merge_hook);
 
 	select_return_states_hook(PARAM_FREED, &set_param_freed);
+	add_untracked_param_hook(&match_untracked);
 }
--- old/usr/src/tools/smatch/src/check_held_dev.c	Mon Aug  5 08:37:59 2019
+++ new/usr/src/tools/smatch/src/check_held_dev.c	Mon Aug  5 08:37:59 2019
@@ -113,7 +113,7 @@
 		if (token_type(token) != TOKEN_IDENT)
 			return;
 		func = show_ident(token->ident);
-		return_implies_state(func, valid_ptr_min, valid_ptr_max,
+		return_implies_state_sval(func, valid_ptr_min_sval, valid_ptr_max_sval,
 				     &match_returns_held, NULL);
 		return_implies_state(func, 0, 0, &match_returns_null,
 					 NULL);
--- old/usr/src/tools/smatch/src/check_kernel.c	Mon Aug  5 08:38:00 2019
+++ new/usr/src/tools/smatch/src/check_kernel.c	Mon Aug  5 08:38:00 2019
@@ -23,13 +23,19 @@
 #include "smatch.h"
 #include "smatch_extra.h"
 
+static sval_t err_ptr_min;
+static sval_t err_ptr_max;
+static sval_t null_ptr;
+
 static int implied_err_cast_return(struct expression *call, void *unused, struct range_list **rl)
 {
 	struct expression *arg;
 
 	arg = get_argument_from_call_expr(call->args, 0);
-	if (!get_implied_rl(arg, rl))
-		*rl = alloc_rl(ll_to_sval(-4095), ll_to_sval(-1));
+	if (!get_implied_rl(arg, rl)) {
+		*rl = alloc_rl(err_ptr_min, err_ptr_max);
+		*rl = cast_rl(get_type(arg), *rl);
+	}
 	return 1;
 }
 
@@ -78,10 +84,18 @@
 	struct expression *arg;
 	struct smatch_state *pre_state;
 	struct smatch_state *end_state;
+	struct range_list *rl;
 
 	arg = get_argument_from_call_expr(call_expr->args, param);
 	pre_state = get_state_expr(SMATCH_EXTRA, arg);
-	end_state = estate_filter_range(pre_state, ll_to_sval(-4095), ll_to_sval(0));
+	if (estate_rl(pre_state)) {
+		rl = estate_rl(pre_state);
+		rl = remove_range(rl, null_ptr, null_ptr);
+		rl = remove_range(rl, err_ptr_min, err_ptr_max);
+	} else {
+		rl = alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
+	}
+	end_state = alloc_estate_rl(rl);
 	set_extra_expr_nomod(arg, end_state);
 }
 
@@ -96,9 +110,9 @@
 
 	arg = get_argument_from_call_expr(call_expr->args, param);
 	pre_state = get_state_expr(SMATCH_EXTRA, arg);
-	rl = alloc_rl(ll_to_sval(-4095), ll_to_sval(0));
+	call_results_to_rl(call_expr, &ptr_ctype, "0,(-4095)-(-1)", &rl);
 	rl = rl_intersection(estate_rl(pre_state), rl);
-	rl = cast_rl(estate_type(pre_state), rl);
+	rl = cast_rl(get_type(arg), rl);
 	end_state = alloc_estate_rl(rl);
 	set_extra_expr_nomod(arg, end_state);
 }
@@ -108,12 +122,18 @@
 {
 	struct expression *arg;
 	struct smatch_state *pre_state;
-	struct smatch_state *new_state;
+	struct range_list *rl;
 
 	arg = get_argument_from_call_expr(call_expr->args, 0);
 	pre_state = get_state_expr(SMATCH_EXTRA, arg);
-	new_state = estate_filter_range(pre_state, sval_type_min(&long_ctype), ll_to_sval(-1));
-	set_extra_expr_nomod(arg, new_state);
+	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);
+	}
+	rl = cast_rl(get_type(arg), rl);
+	set_extra_expr_nomod(arg, alloc_estate_rl(rl));
 }
 
 static void match_err(const char *fn, struct expression *call_expr,
@@ -121,13 +141,16 @@
 {
 	struct expression *arg;
 	struct smatch_state *pre_state;
-	struct smatch_state *new_state;
+	struct range_list *rl;
 
 	arg = get_argument_from_call_expr(call_expr->args, 0);
 	pre_state = get_state_expr(SMATCH_EXTRA, arg);
-	new_state = estate_filter_range(pre_state, sval_type_min(&long_ctype), ll_to_sval(-4096));
-	new_state = estate_filter_range(new_state, ll_to_sval(0), sval_type_max(&long_ctype));
-	set_extra_expr_nomod(arg, new_state);
+	rl = estate_rl(pre_state);
+	if (!rl)
+		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);
+	set_extra_expr_nomod(arg, alloc_estate_rl(rl));
 }
 
 static void match_container_of_macro(const char *fn, struct expression *expr, void *unused)
@@ -379,11 +402,36 @@
 	__in_fake_assign--;
 }
 
+bool is_ignored_kernel_data(const char *name)
+{
+	if (option_project != PROJ_KERNEL)
+		return false;
+
+	/*
+	 * On the file I was looking at lockdep was 25% of the DB.
+	 */
+	if (strstr(name, ".dep_map."))
+		return true;
+	if (strstr(name, ".lockdep_map."))
+		return true;
+	return false;
+}
+
 void check_kernel(int id)
 {
 	if (option_project != PROJ_KERNEL)
 		return;
 
+	err_ptr_min.type = &ptr_ctype;
+	err_ptr_min.value = -4095;
+	err_ptr_max.type = &ptr_ctype;
+	err_ptr_max.value = -1l;
+	null_ptr.type = &ptr_ctype;
+	null_ptr.value = 0;
+
+	err_ptr_min = sval_cast(&ptr_ctype, err_ptr_min);
+	err_ptr_max = sval_cast(&ptr_ctype, err_ptr_max);
+
 	add_implied_return_hook("ERR_PTR", &implied_err_cast_return, NULL);
 	add_implied_return_hook("ERR_CAST", &implied_err_cast_return, NULL);
 	add_implied_return_hook("PTR_ERR", &implied_err_cast_return, NULL);
--- old/usr/src/tools/smatch/src/check_kernel_printf.c	Mon Aug  5 08:38:00 2019
+++ new/usr/src/tools/smatch/src/check_kernel_printf.c	Mon Aug  5 08:38:00 2019
@@ -521,6 +521,15 @@
 			fmt[0], tag, vaidx, type_to_str(type));
 }
 
+static void time_and_date(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
+{
+	assert(tolower(fmt[0]) == 't');
+
+	if (fmt[1] == 'R' && !is_struct_tag(basetype, "rtc_time"))
+		sm_error("'%%ptR' expects argument of type struct 'rtc_time', argument %d has type '%s'",
+			 vaidx, type_to_str(type));
+}
+
 static void check_clock(const char *fmt, struct symbol *type, struct symbol *basetype, int vaidx)
 {
 	assert(fmt[0] == 'C');
@@ -756,6 +765,9 @@
 	case 'd':
 		dentry_file(fmt, type, basetype, vaidx);
 		break;
+	case 't':
+		time_and_date(fmt, type, basetype, vaidx);
+		break;
 	case 'C':
 		check_clock(fmt, type, basetype, vaidx);
 		break;
--- old/usr/src/tools/smatch/src/check_list.h	Mon Aug  5 08:38:00 2019
+++ new/usr/src/tools/smatch/src/check_list.h	Mon Aug  5 08:38:00 2019
@@ -53,8 +53,11 @@
 CK(register_impossible)
 CK(register_impossible_return)
 CK(register_strings)
+CK(register_integer_overflow)
+CK(register_integer_overflow_links)
 CK(register_real_absolute)
 CK(register_imaginary_absolute)
+CK(register_bits)
 CK(register_fn_arg_link)
 CK(register_parameter_names)
 CK(register_return_to_param)
@@ -71,8 +74,8 @@
 CK(register_nul_terminator_param_set)
 CK(register_statement_count)
 
+CK(register_kernel_user_data)
 CK(register_kernel_user_data2)
-CK(register_kernel_user_data3)
 
 CK(check_debug)
 
@@ -187,6 +190,7 @@
 CK(check_nospec)
 CK(check_nospec_barrier)
 CK(check_spectre)
+CK(check_spectre_second_half)
 CK(check_implicit_dependencies)
 
 /* wine specific stuff */
@@ -195,6 +199,7 @@
 
 /* illumos specific */
 CK(check_all_func_returns)
+CK(check_cmn_err)
 
 #include "check_list_local.h"
 
--- old/usr/src/tools/smatch/src/check_locking.c	Mon Aug  5 08:38:00 2019
+++ new/usr/src/tools/smatch/src/check_locking.c	Mon Aug  5 08:38:00 2019
@@ -49,6 +49,7 @@
 	ret_any,
 	ret_non_zero,
 	ret_zero,
+	ret_one,
 	ret_negative,
 	ret_positive,
 };
@@ -151,18 +152,18 @@
 	{"__spin_lock_bh",             LOCK,   "spin_lock", 0, ret_any},
 	{"__spin_unlock_bh",           UNLOCK, "spin_lock", 0, ret_any},
 
-	{"spin_trylock",               LOCK,   "spin_lock", 0, ret_non_zero},
-	{"_spin_trylock",              LOCK,   "spin_lock", 0, ret_non_zero},
-	{"__spin_trylock",             LOCK,   "spin_lock", 0, ret_non_zero},
-	{"raw_spin_trylock",           LOCK,   "spin_lock", 0, ret_non_zero},
-	{"_raw_spin_trylock",          LOCK,   "spin_lock", 0, ret_non_zero},
-	{"spin_trylock_irq",           LOCK,   "spin_lock", 0, ret_non_zero},
-	{"spin_trylock_irqsave",       LOCK,   "spin_lock", 0, ret_non_zero},
-	{"spin_trylock_bh",            LOCK,   "spin_lock", 0, ret_non_zero},
-	{"_spin_trylock_bh",           LOCK,   "spin_lock", 0, ret_non_zero},
-	{"__spin_trylock_bh",          LOCK,   "spin_lock", 0, ret_non_zero},
-	{"__raw_spin_trylock",         LOCK,   "spin_lock", 0, ret_non_zero},
-	{"_atomic_dec_and_lock",       LOCK,   "spin_lock", 1, ret_non_zero},
+	{"spin_trylock",               LOCK,   "spin_lock", 0, ret_one},
+	{"_spin_trylock",              LOCK,   "spin_lock", 0, ret_one},
+	{"__spin_trylock",             LOCK,   "spin_lock", 0, ret_one},
+	{"raw_spin_trylock",           LOCK,   "spin_lock", 0, ret_one},
+	{"_raw_spin_trylock",          LOCK,   "spin_lock", 0, ret_one},
+	{"spin_trylock_irq",           LOCK,   "spin_lock", 0, ret_one},
+	{"spin_trylock_irqsave",       LOCK,   "spin_lock", 0, ret_one},
+	{"spin_trylock_bh",            LOCK,   "spin_lock", 0, ret_one},
+	{"_spin_trylock_bh",           LOCK,   "spin_lock", 0, ret_one},
+	{"__spin_trylock_bh",          LOCK,   "spin_lock", 0, ret_one},
+	{"__raw_spin_trylock",         LOCK,   "spin_lock", 0, ret_one},
+	{"_atomic_dec_and_lock",       LOCK,   "spin_lock", 1, ret_one},
 
 	{"read_lock",                 LOCK,   "read_lock", 0, ret_any},
 	{"read_unlock",               UNLOCK, "read_lock", 0, ret_any},
@@ -197,13 +198,13 @@
 	{"__raw_read_lock_bh",        LOCK,   "read_lock", 0, ret_any},
 	{"__raw_read_unlock_bh",      UNLOCK, "read_lock", 0, ret_any},
 
-	{"generic__raw_read_trylock", LOCK,   "read_lock", 0, ret_non_zero},
-	{"read_trylock",              LOCK,   "read_lock", 0, ret_non_zero},
-	{"_read_trylock",             LOCK,   "read_lock", 0, ret_non_zero},
-	{"raw_read_trylock",          LOCK,   "read_lock", 0, ret_non_zero},
-	{"_raw_read_trylock",         LOCK,   "read_lock", 0, ret_non_zero},
-	{"__raw_read_trylock",        LOCK,   "read_lock", 0, ret_non_zero},
-	{"__read_trylock",            LOCK,   "read_lock", 0, ret_non_zero},
+	{"generic__raw_read_trylock", LOCK,   "read_lock", 0, ret_one},
+	{"read_trylock",              LOCK,   "read_lock", 0, ret_one},
+	{"_read_trylock",             LOCK,   "read_lock", 0, ret_one},
+	{"raw_read_trylock",          LOCK,   "read_lock", 0, ret_one},
+	{"_raw_read_trylock",         LOCK,   "read_lock", 0, ret_one},
+	{"__raw_read_trylock",        LOCK,   "read_lock", 0, ret_one},
+	{"__read_trylock",            LOCK,   "read_lock", 0, ret_one},
 
 	{"write_lock",                LOCK,   "write_lock", 0, ret_any},
 	{"write_unlock",              UNLOCK, "write_lock", 0, ret_any},
@@ -234,12 +235,12 @@
 	{"_raw_write_unlock",         UNLOCK, "write_lock", 0, ret_any},
 	{"__raw_write_unlock",        UNLOCK, "write_lock", 0, ret_any},
 
-	{"write_trylock",             LOCK,   "write_lock", 0, ret_non_zero},
-	{"_write_trylock",            LOCK,   "write_lock", 0, ret_non_zero},
-	{"raw_write_trylock",         LOCK,   "write_lock", 0, ret_non_zero},
-	{"_raw_write_trylock",        LOCK,   "write_lock", 0, ret_non_zero},
-	{"__write_trylock",           LOCK,   "write_lock", 0, ret_non_zero},
-	{"__raw_write_trylock",       LOCK,   "write_lock", 0, ret_non_zero},
+	{"write_trylock",             LOCK,   "write_lock", 0, ret_one},
+	{"_write_trylock",            LOCK,   "write_lock", 0, ret_one},
+	{"raw_write_trylock",         LOCK,   "write_lock", 0, ret_one},
+	{"_raw_write_trylock",        LOCK,   "write_lock", 0, ret_one},
+	{"__write_trylock",           LOCK,   "write_lock", 0, ret_one},
+	{"__raw_write_trylock",       LOCK,   "write_lock", 0, ret_one},
 
 	{"down",               LOCK,   "sem", 0, ret_any},
 	{"up",                 UNLOCK, "sem", 0, ret_any},
@@ -247,9 +248,23 @@
 	{"down_timeout",       LOCK,   "sem", 0, ret_zero},
 	{"down_interruptible", LOCK,   "sem", 0, ret_zero},
 
+
+	{"down_write",          LOCK,   "rw_sem", 0, ret_any},
+	{"downgrade_write",     UNLOCK, "rw_sem", 0, ret_any},
+	{"downgrade_write",     LOCK,   "read_sem", 0, ret_any},
+	{"up_write",            UNLOCK, "rw_sem", 0, ret_any},
+	{"down_write_trylock",  LOCK,   "rw_sem", 0, ret_one},
+	{"down_write_killable", LOCK,   "rw_sem", 0, ret_zero},
+	{"down_read",           LOCK,   "read_sem", 0, ret_any},
+	{"down_read_trylock",   LOCK,   "read_sem", 0, ret_one},
+	{"down_read_killable",  LOCK,   "read_sem", 0, ret_zero},
+	{"up_read",             UNLOCK, "read_sem", 0, ret_any},
+
 	{"mutex_lock",                      LOCK,   "mutex", 0, ret_any},
+	{"mutex_lock_io",                   LOCK,   "mutex", 0, ret_any},
 	{"mutex_unlock",                    UNLOCK, "mutex", 0, ret_any},
 	{"mutex_lock_nested",               LOCK,   "mutex", 0, ret_any},
+	{"mutex_lock_io_nested",            LOCK,   "mutex", 0, ret_any},
 
 	{"mutex_lock_interruptible",        LOCK,   "mutex", 0, ret_zero},
 	{"mutex_lock_interruptible_nested", LOCK,   "mutex", 0, ret_zero},
@@ -256,7 +271,7 @@
 	{"mutex_lock_killable",             LOCK,   "mutex", 0, ret_zero},
 	{"mutex_lock_killable_nested",      LOCK,   "mutex", 0, ret_zero},
 
-	{"mutex_trylock",                   LOCK,   "mutex", 0, ret_non_zero},
+	{"mutex_trylock",                   LOCK,   "mutex", 0, ret_one},
 
 	{"raw_local_irq_disable", LOCK,   "irq", NO_ARG, ret_any},
 	{"raw_local_irq_enable",  UNLOCK, "irq", NO_ARG, ret_any},
@@ -269,7 +284,7 @@
 	{"_raw_spin_lock_irq",    LOCK,   "irq", NO_ARG, ret_any},
 	{"_raw_spin_unlock_irq",  UNLOCK, "irq", NO_ARG, ret_any},
 	{"__raw_spin_unlock_irq", UNLOCK, "irq", NO_ARG, ret_any},
-	{"spin_trylock_irq",      LOCK,   "irq", NO_ARG, ret_non_zero},
+	{"spin_trylock_irq",      LOCK,   "irq", NO_ARG, ret_one},
 	{"read_lock_irq",         LOCK,   "irq", NO_ARG, ret_any},
 	{"read_unlock_irq",       UNLOCK, "irq", NO_ARG, ret_any},
 	{"_read_lock_irq",        LOCK,   "irq", NO_ARG, ret_any},
@@ -304,7 +319,7 @@
 	{"__raw_spin_lock_irqsave",    LOCK,   "irqsave", RETURN_VAL, ret_any},
 	{"__raw_spin_unlock_irqrestore",UNLOCK, "irqsave", 1, ret_any},
 	{"_raw_spin_lock_irqsave_nested", LOCK, "irqsave", RETURN_VAL, ret_any},
-	{"spin_trylock_irqsave",       LOCK,   "irqsave", 1, ret_non_zero},
+	{"spin_trylock_irqsave",       LOCK,   "irqsave", 1, ret_one},
 	{"read_lock_irqsave",          LOCK,   "irqsave", RETURN_VAL, ret_any},
 	{"read_lock_irqsave",          LOCK,   "irqsave", 1, ret_any},
 	{"read_unlock_irqrestore",     UNLOCK, "irqsave", 1, ret_any},
@@ -348,9 +363,9 @@
 	{"_write_unlock_bh",    UNLOCK, "bottom_half", NO_ARG, ret_any},
 	{"__write_lock_bh",     LOCK,   "bottom_half", NO_ARG, ret_any},
 	{"__write_unlock_bh",   UNLOCK, "bottom_half", NO_ARG, ret_any},
-	{"spin_trylock_bh",     LOCK,   "bottom_half", NO_ARG, ret_non_zero},
-	{"_spin_trylock_bh",    LOCK,   "bottom_half", NO_ARG, ret_non_zero},
-	{"__spin_trylock_bh",   LOCK,   "bottom_half", NO_ARG, ret_non_zero},
+	{"spin_trylock_bh",     LOCK,   "bottom_half", NO_ARG, ret_one},
+	{"_spin_trylock_bh",    LOCK,   "bottom_half", NO_ARG, ret_one},
+	{"__spin_trylock_bh",   LOCK,   "bottom_half", NO_ARG, ret_one},
 
 	{"ffs_mutex_lock",        LOCK,   "mutex", 0, ret_zero},
 };
@@ -447,6 +462,15 @@
 		set_state(my_id, sm->name, sm->sym, &impossible);
 }
 
+static bool nestable(const char *name)
+{
+	if (strstr(name, "read_sem:"))
+		return true;
+	if (strcmp(name, "bottom_half:") == 0)
+		return true;
+	return false;
+}
+
 static void do_lock(const char *name)
 {
 	struct sm_state *sm;
@@ -457,8 +481,7 @@
 	sm = get_sm_state(my_id, name, NULL);
 	if (!sm)
 		add_tracker(&starts_unlocked, my_id, name, NULL);
-	if (sm && slist_has_state(sm->possible, &locked) &&
-			strcmp(name, "bottom_half:") != 0)
+	if (sm && slist_has_state(sm->possible, &locked) && !nestable(name))
 		sm_error("double lock '%s'", name);
 	if (sm)
 		func_has_transition = TRUE;
@@ -744,6 +767,7 @@
 static int matches_return_type(struct range_list *rl, enum return_type type)
 {
 	sval_t zero_sval = ll_to_sval(0);
+	sval_t one_sval = ll_to_sval(1);
 
 	/* All these double negatives are super ugly!  */
 
@@ -750,6 +774,8 @@
 	switch (type) {
 	case ret_zero:
 		return !possibly_true_rl(rl, SPECIAL_NOTEQUAL, alloc_rl(zero_sval, zero_sval));
+	case ret_one:
+		return !possibly_true_rl(rl, SPECIAL_NOTEQUAL, alloc_rl(one_sval, one_sval));
 	case ret_non_zero:
 		return !possibly_true_rl(rl, SPECIAL_EQUAL, alloc_rl(zero_sval, zero_sval));
 	case ret_negative:
@@ -906,7 +932,7 @@
 	void *idx = INT_PTR(index);
 
 	if (lock->return_type == ret_non_zero) {
-		return_implies_state(lock->function, valid_ptr_min, valid_ptr_max, &match_lock_held, idx);
+		return_implies_state(lock->function, 1, INT_MAX, &match_lock_held, idx);
 		return_implies_state(lock->function, 0, 0, &match_lock_failed, idx);
 	} else if (lock->return_type == ret_any && lock->arg == RETURN_VAL) {
 		add_function_assign_hook(lock->function, &match_returns_locked, idx);
@@ -915,6 +941,9 @@
 	} else if (lock->return_type == ret_zero) {
 		return_implies_state(lock->function, 0, 0, &match_lock_held, idx);
 		return_implies_state(lock->function, -4095, -1, &match_lock_failed, idx);
+	} else if (lock->return_type == ret_one) {
+		return_implies_state(lock->function, 1, 1, &match_lock_held, idx);
+		return_implies_state(lock->function, 0, 0, &match_lock_failed, idx);
 	}
 }
 
--- old/usr/src/tools/smatch/src/check_macro_side_effects.c	Mon Aug  5 08:38:01 2019
+++ new/usr/src/tools/smatch/src/check_macro_side_effects.c	Mon Aug  5 08:38:01 2019
@@ -30,9 +30,12 @@
 	struct smatch_state *state;
 	char *name;
 
-	state = __alloc_smatch_state(0);
 	expr = strip_expr(expr);
 	name = expr_to_str(expr);
+	if (!name)
+		return NULL;
+
+	state = __alloc_smatch_state(0);
 	state->name = alloc_sname(name);
 	free_string(name);
 	state->data = expr;
@@ -160,6 +163,7 @@
 	if (!option_spammy)
 		return;
 
+	set_dynamic_states(my_id);
 	add_hook(&match_unop, OP_HOOK);
 	add_hook(&match_stmt, STMT_HOOK);
 	register_ignored_macros();
--- old/usr/src/tools/smatch/src/check_missing_break.c	Mon Aug  5 08:38:01 2019
+++ new/usr/src/tools/smatch/src/check_missing_break.c	Mon Aug  5 08:38:01 2019
@@ -177,6 +177,7 @@
 	if (!option_spammy)
 		return;
 
+	set_dynamic_states(my_id);
 	add_unmatched_state_hook(my_id, &unmatched_state);
 	add_merge_hook(my_id, &merge_hook);
 
--- old/usr/src/tools/smatch/src/check_no_return.c	Mon Aug  5 08:38:02 2019
+++ new/usr/src/tools/smatch/src/check_no_return.c	Mon Aug  5 08:38:02 2019
@@ -16,6 +16,7 @@
  */
 
 #include "smatch.h"
+#include "smatch_slist.h"
 
 static int my_id;
 static int returned;
@@ -32,6 +33,8 @@
 {
 	if (__inline_fn)
 		return;
+	if (out_of_memory() || taking_too_long())
+		return;
 	if (!is_reachable() && !returned)
 		sm_info("info: add to no_return_funcs");
 	returned = 0;
--- old/usr/src/tools/smatch/src/check_nospec.c	Mon Aug  5 08:38:02 2019
+++ new/usr/src/tools/smatch/src/check_nospec.c	Mon Aug  5 08:38:02 2019
@@ -96,6 +96,7 @@
 
 static void returned_struct_members(int return_id, char *return_ranges, struct expression *expr)
 {
+	struct stree *start_states = get_start_states();
 	struct symbol *returned_sym;
 	struct sm_state *sm;
 	const char *param_name;
@@ -105,6 +106,8 @@
 	returned_sym = expr_to_sym(expr);
 
 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
+		if (get_state_stree(start_states, my_id, sm->name, sm->sym) == sm->state)
+			continue;
 		param = get_param_num_from_sym(sm->sym);
 		if (param < 0) {
 			if (!returned_sym || returned_sym != sm->sym)
@@ -222,7 +225,8 @@
 		return;
 	if (strcmp(macro, "rmb") != 0 &&
 	    strcmp(macro, "smp_rmb") != 0 &&
-	    strcmp(macro, "barrier_nospec") != 0)
+	    strcmp(macro, "barrier_nospec") != 0 &&
+	    strcmp(macro, "preempt_disable") != 0)
 		return;
 
 	set_state(barrier_id, "barrier", NULL, &nospec);
@@ -234,6 +238,15 @@
 	mark_user_data_as_nospec();
 }
 
+static void select_return_stmt_cnt(struct expression *expr, int param, char *key, char *value)
+{
+	int cnt;
+
+	cnt = atoi(value);
+	if (cnt > 400)
+		mark_user_data_as_nospec();
+}
+
 void check_nospec(int id)
 {
 	my_id = id;
@@ -248,6 +261,7 @@
 	add_split_return_callback(&returned_struct_members);
 	select_return_states_hook(NOSPEC, &db_returns_nospec);
 	select_return_states_hook(NOSPEC_WB, &db_returns_barrier);
+	select_return_states_hook(STMT_CNT, &select_return_stmt_cnt);
 
 	add_hook(&match_asm, ASM_HOOK);
 	add_hook(&match_after_nospec_asm, STMT_HOOK_AFTER);
--- old/usr/src/tools/smatch/src/check_off_by_one_relative.c	Mon Aug  5 08:38:02 2019
+++ new/usr/src/tools/smatch/src/check_off_by_one_relative.c	Mon Aug  5 08:38:02 2019
@@ -33,6 +33,7 @@
 	struct expression *size;
 	struct expression *offset;
 	char *array_str, *offset_str;
+	int limit_type;
 
 	expr = strip_expr(expr);
 	if (!is_array(expr))
@@ -39,13 +40,16 @@
 		return;
 
 	array = get_array_base(expr);
-	size = get_size_variable(array);
-	if (!size)
+	size = get_size_variable(array, &limit_type);
+	if (!size || limit_type != ELEM_COUNT)
 		return;
 	offset = get_array_offset(expr);
 	if (!possible_comparison(size, SPECIAL_EQUAL, offset))
 		return;
 
+	if (buf_comparison_index_ok(expr))
+		return;
+
 	array_str = expr_to_str(array);
 	offset_str = expr_to_str(offset);
 	sm_warning("potentially one past the end of array '%s[%s]'", array_str, offset_str);
@@ -53,25 +57,6 @@
 	free_string(offset_str);
 }
 
-static int known_access_ok_comparison(struct expression *expr)
-{
-	struct expression *array;
-	struct expression *size;
-	struct expression *offset;
-	int comparison;
-
-	array = get_array_base(expr);
-	size = get_size_variable(array);
-	if (!size)
-		return 0;
-	offset = get_array_offset(expr);
-	comparison = get_comparison(size, offset);
-	if (comparison == '>' || comparison == SPECIAL_UNSIGNED_GT)
-		return 1;
-
-	return 0;
-}
-
 static int known_access_ok_numbers(struct expression *expr)
 {
 	struct expression *array;
@@ -108,7 +93,7 @@
 
 	if (known_access_ok_numbers(expr))
 		return;
-	if (known_access_ok_comparison(expr))
+	if (buf_comparison_index_ok(expr))
 		return;
 
 	array = get_array_base(expr);
--- old/usr/src/tools/smatch/src/check_precedence.c	Mon Aug  5 08:38:03 2019
+++ new/usr/src/tools/smatch/src/check_precedence.c	Mon Aug  5 08:38:03 2019
@@ -120,6 +120,16 @@
 	sm_warning("shift has higher precedence than mask");
 }
 
+static void match_mask_compare(struct expression *expr)
+{
+	if (expr->op != '&')
+		return;
+	if (expr->right->type != EXPR_COMPARE)
+		return;
+
+	sm_warning("compare has higher precedence than mask");
+}
+
 static void match_subtract_shift(struct expression *expr)
 {
 	if (expr->op != SPECIAL_LEFTSHIFT)
@@ -138,5 +148,6 @@
 	add_hook(&match_condition, CONDITION_HOOK);
 	add_hook(&match_binop, BINOP_HOOK);
 	add_hook(&match_mask, BINOP_HOOK);
+	add_hook(&match_mask_compare, BINOP_HOOK);
 	add_hook(&match_subtract_shift, BINOP_HOOK);
 }
--- old/usr/src/tools/smatch/src/check_return_cast.c	Mon Aug  5 08:38:03 2019
+++ new/usr/src/tools/smatch/src/check_return_cast.c	Mon Aug  5 08:38:03 2019
@@ -28,6 +28,9 @@
 	struct symbol *func_type = get_real_base_type(cur_func_sym);
 	sval_t sval;
 
+	if (!func_type || func_type->type != SYM_FN)
+		return;
+	func_type = get_real_base_type(func_type);
 	if (!func_type)
 		return;
 	if (!type_unsigned(func_type))
--- old/usr/src/tools/smatch/src/check_rosenberg.c	Mon Aug  5 08:38:04 2019
+++ new/usr/src/tools/smatch/src/check_rosenberg.c	Mon Aug  5 08:38:04 2019
@@ -382,6 +382,7 @@
 		return;
 
 	my_member_id = id;
+	set_dynamic_states(my_member_id);
 	add_extra_mod_hook(&extra_mod_hook);
 }
 
--- old/usr/src/tools/smatch/src/check_shift_to_zero.c	Mon Aug  5 08:38:04 2019
+++ new/usr/src/tools/smatch/src/check_shift_to_zero.c	Mon Aug  5 08:38:04 2019
@@ -35,6 +35,8 @@
 		return;
 	if (type_bits(type) == -1 || type_bits(type) > bits.value)
 		return;
+	if (is_ignored_expr(my_id, expr))
+		return;
 	sm_warning("right shifting more than type allows %d vs %lld", type_bits(type), bits.value);
 }
 
--- old/usr/src/tools/smatch/src/check_snprintf.c	Mon Aug  5 08:38:05 2019
+++ new/usr/src/tools/smatch/src/check_snprintf.c	Mon Aug  5 08:38:05 2019
@@ -79,6 +79,7 @@
 		return;
 
 	my_id = id;
+	set_dynamic_states(my_id);
 	add_hook(&match_call, FUNCTION_CALL_HOOK);
 	add_function_assign_hook("snprintf", &match_snprintf, NULL);
 	add_modification_hook(my_id, &ok_to_use);
--- old/usr/src/tools/smatch/src/check_spectre.c	Mon Aug  5 08:38:06 2019
+++ new/usr/src/tools/smatch/src/check_spectre.c	Mon Aug  5 08:38:06 2019
@@ -19,6 +19,8 @@
 #include "smatch_extra.h"
 
 static int my_id;
+extern int second_half_id;
+extern void set_spectre_first_half(struct expression *expr);
 
 static int suppress_multiple = 1;
 
@@ -165,8 +167,10 @@
 		return;
 
 	array_expr = get_array_base(expr);
-	if (suppress_multiple && is_ignored_expr(my_id, array_expr))
+	if (suppress_multiple && is_ignored_expr(my_id, array_expr)) {
+		set_spectre_first_half(expr);
 		return;
+	}
 
 	offset = get_array_offset(expr);
 	if (!is_user_rl(offset))
@@ -192,6 +196,8 @@
 	       name,
 	       is_read(expr) ? "r" : "w",
 	       conditions ? " (local cap)" : "");
+
+	set_spectre_first_half(expr);
 	if (suppress_multiple)
 		add_ignore_expr(my_id, array_expr);
 	free_string(name);
--- /dev/null	Mon Aug  5 08:38:06 2019
+++ new/usr/src/tools/smatch/src/check_spectre_second_half.c	Mon Aug  5 08:38:06 2019
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2018 Oracle.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
+ */
+
+#include "smatch.h"
+#include "smatch_extra.h"
+#include "smatch_slist.h"
+
+/* New chips will probably be able to speculate further ahead */
+#define MAX_SPEC_STMT 200
+
+static int my_id;
+
+struct stree *first_halfs;
+
+struct expression *recently_set;
+
+void set_spectre_first_half(struct expression *expr)
+{
+	char buf[64];
+	char *name;
+
+	name = expr_to_str(expr);
+	snprintf(buf, sizeof(buf), "%p %s", expr, name);
+	free_string(name);
+
+	set_state_stree(&first_halfs, my_id, buf, NULL, alloc_state_num(get_stmt_cnt()));
+}
+
+void clear_spectre_second_halfs(void)
+{
+	struct sm_state *sm;
+
+	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
+		set_state(my_id, sm->name, sm->sym, alloc_state_num(-MAX_SPEC_STMT));
+	} END_FOR_EACH_SM(sm);
+}
+
+static struct smatch_state *get_spectre_first_half(struct expression *expr)
+{
+	char buf[64];
+	char *name;
+
+	name = expr_to_str(expr);
+	snprintf(buf, sizeof(buf), "%p %s", expr, name);
+	free_string(name);
+
+	return get_state_stree(first_halfs, my_id, buf, NULL);
+}
+
+static void match_assign(struct expression *expr)
+{
+	struct smatch_state *state;
+
+	if (expr->op == SPECIAL_AND_ASSIGN)
+		return;
+
+	state = get_spectre_first_half(expr->right);
+	if (state) {
+		set_state_expr(my_id, expr->left, state);
+		recently_set = expr->left;
+		return;
+	}
+	state = get_state_expr(my_id, expr->right);
+	if (!state)
+		return;
+	set_state_expr(my_id, expr->left, state);
+	recently_set = expr->left;
+}
+
+static void match_done(struct expression *expr)
+{
+	struct smatch_state *state;
+	char *name;
+
+	if (expr == recently_set)
+		return;
+
+	state = get_state_expr(my_id, expr);
+	if (!state)
+		return;
+
+	if (get_stmt_cnt() - (long)state->data > MAX_SPEC_STMT)
+		return;
+
+	name = expr_to_str(expr);
+	sm_msg("warn: possible spectre second half.  '%s'", name);
+	free_string(name);
+
+	set_state_expr(my_id, expr, alloc_state_num(-MAX_SPEC_STMT));
+}
+
+static void match_end_func(struct symbol *sym)
+{
+	if (__inline_fn)
+		return;
+	free_stree(&first_halfs);
+}
+
+void check_spectre_second_half(int id)
+{
+	my_id = id;
+
+	if (option_project != PROJ_KERNEL)
+		return;
+	set_dynamic_states(my_id);
+	add_hook(&match_assign, ASSIGNMENT_HOOK);
+	add_hook(&match_done, SYM_HOOK);
+	add_hook(&match_done, DEREF_HOOK);
+
+	add_hook(&match_end_func, END_FUNC_HOOK);
+}
--- old/usr/src/tools/smatch/src/check_string_len.c	Mon Aug  5 08:38:06 2019
+++ new/usr/src/tools/smatch/src/check_string_len.c	Mon Aug  5 08:38:06 2019
@@ -35,13 +35,13 @@
 
 struct param_info zero_one = {0, 1};
 
-static int handle_format(struct expression *call, char **pp, int *arg_nr)
+static int handle_format(struct expression *call, char **pp, int *arg_nr, bool use_max)
 {
 	struct expression *arg;
 	char *p = *pp;
 	int ret = 1;
 	char buf[256];
-	sval_t max;
+	sval_t sval;
 
 	p++;  /* we passed it with *p == '%' */
 
@@ -141,23 +141,30 @@
 		goto out;
 	}
 
-	get_absolute_max(arg, &max);
+	if (use_max) {
+		get_absolute_max(arg, &sval);
+	} else {
+		get_absolute_min(arg, &sval);
+		if (sval_is_negative(sval))
+			sval.value = 0;
+	}
 
+
 	if (*p == 'x' || *p == 'X' || *p == 'p') {
-		ret = snprintf(buf, sizeof(buf), "%llx", max.uvalue);
+		ret = snprintf(buf, sizeof(buf), "%llx", sval.uvalue);
 	} else if (*p == 'u') {
-		ret = snprintf(buf, sizeof(buf), "%llu", max.uvalue);
+		ret = snprintf(buf, sizeof(buf), "%llu", sval.uvalue);
 	} else if (!expr_unsigned(arg)) {
 		sval_t min;
 		int tmp;
 
-		ret = snprintf(buf, sizeof(buf), "%lld", max.value);
+		ret = snprintf(buf, sizeof(buf), "%lld", sval.value);
 		get_absolute_min(arg, &min);
 		tmp = snprintf(buf, sizeof(buf), "%lld", min.value);
 		if (tmp > ret)
 			ret = tmp;
 	} else {
-		ret = snprintf(buf, sizeof(buf), "%lld", max.value);
+		ret = snprintf(buf, sizeof(buf), "%lld", sval.value);
 	}
 	p++;
 
@@ -168,7 +175,7 @@
 	return ret;
 }
 
-int get_formatted_string_size(struct expression *call, int arg)
+int get_formatted_string_size_helper(struct expression *call, int arg, bool use_max)
 {
 	struct expression *expr;
 	char *p;
@@ -184,7 +191,7 @@
 	while (*p) {
 
 		if (*p == '%') {
-			count += handle_format(call, &p, &arg);
+			count += handle_format(call, &p, &arg, use_max);
 		} else if (*p == '\\') {
 			p++;
 		}else {
@@ -193,10 +200,19 @@
 		}
 	}
 
-	count++; /* count the NUL terminator */
 	return count;
 }
 
+int get_formatted_string_size(struct expression *call, int arg)
+{
+	return get_formatted_string_size_helper(call, arg, true);
+}
+
+int get_formatted_string_min_size(struct expression *call, int arg)
+{
+	return get_formatted_string_size_helper(call, arg, false);
+}
+
 static void match_not_limited(const char *fn, struct expression *call, void *info)
 {
 	struct param_info *params = info;
@@ -224,10 +240,11 @@
 		return;
 
 	size = get_formatted_string_size(call, params->string);
-	if (size <= 0)
+	if (size < 0)
 		return;
 	if (size < offset)
 		size -= offset;
+	size++; /* add the NULL terminator */
 	if (size <= buf_size)
 		return;
 
--- old/usr/src/tools/smatch/src/check_syscall_arg_type.c	Mon Aug  5 08:38:07 2019
+++ new/usr/src/tools/smatch/src/check_syscall_arg_type.c	Mon Aug  5 08:38:07 2019
@@ -161,6 +161,7 @@
 	if (option_project != PROJ_KERNEL)
 		return;
 
+	set_dynamic_states(my_id);
 	add_merge_hook(my_id, &merge_states);
 	add_function_hook("fdget", &match_fdget, NULL);
 }
--- old/usr/src/tools/smatch/src/check_testing_index_after_use.c	Mon Aug  5 08:38:07 2019
+++ new/usr/src/tools/smatch/src/check_testing_index_after_use.c	Mon Aug  5 08:38:07 2019
@@ -35,29 +35,12 @@
 	set_state(my_used_id, sm->name, sm->sym, &undefined);
 }
 
-static int get_the_max(struct expression *expr, sval_t *sval)
-{
-	struct range_list *rl;
-
-	if (get_hard_max(expr, sval))
-		return 1;
-	if (!option_spammy)
-		return 0;
-	if (get_fuzzy_max(expr, sval))
-		return 1;
-	if (get_user_rl(expr, &rl)) {
-		*sval = rl_max(rl);
-		return 1;
-	}
-	return 0;
-}
-
 static void array_check(struct expression *expr)
 {
 	struct expression *array_expr;
 	int array_size;
 	struct expression *offset;
-	sval_t max;
+	struct range_list *rl;
 
 	expr = strip_expr(expr);
 	if (!is_array(expr))
@@ -69,13 +52,17 @@
 		return;
 
 	offset = get_array_offset(expr);
-	if (!get_the_max(offset, &max)) {
-		if (getting_address())
-			return;
-		if (is_capped(offset))
-			return;
-		set_state_expr(my_used_id, offset, alloc_state_num(array_size));
-	}
+	get_absolute_rl(offset, &rl);
+	if (rl_max(rl).uvalue < array_size)
+		return;
+	if (buf_comparison_index_ok(expr))
+		return;
+
+	if (getting_address())
+		return;
+	if (is_capped(offset))
+		return;
+	set_state_expr(my_used_id, offset, alloc_state_num(array_size));
 }
 
 static void match_condition(struct expression *expr)
@@ -121,6 +108,7 @@
 void check_testing_index_after_use(int id)
 {
 	my_used_id = id;
+	set_dynamic_states(my_used_id);
 	add_hook(&array_check, OP_HOOK);
 	add_hook(&match_condition, CONDITION_HOOK);
 	add_modification_hook(my_used_id, &delete);
--- old/usr/src/tools/smatch/src/check_uninitialized.c	Mon Aug  5 08:38:09 2019
+++ new/usr/src/tools/smatch/src/check_uninitialized.c	Mon Aug  5 08:38:09 2019
@@ -95,6 +95,41 @@
 		set_state_expr(my_id, right->unop, &initialized);
 }
 
+static void match_negative_comparison(struct expression *expr)
+{
+	struct expression *success;
+	struct sm_state *sm;
+	sval_t max;
+
+	/*
+	 * In the kernel, people don't use "if (ret) {" and "if (ret < 0) {"
+	 * consistently.  Ideally Smatch would know the return but often it
+	 * doesn't.
+	 *
+	 */
+
+	if (option_project != PROJ_KERNEL)
+		return;
+
+	if (expr->type != EXPR_COMPARE || expr->op != '<')
+		return;
+	if (!is_zero(expr->right))
+		return;
+	if (get_implied_max(expr->left, &max) && max.value == 0)
+		return;
+
+	success = compare_expression(expr->left, SPECIAL_EQUAL, expr->right);
+	if (!assume(success))
+		return;
+
+	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
+		if (sm->state == &initialized)
+			set_true_false_states(my_id, sm->name, sm->sym, NULL, &initialized);
+	} END_FOR_EACH_SM(sm);
+
+	end_assume();
+}
+
 static int is_initialized(struct expression *expr)
 {
 	struct sm_state *sm;
@@ -114,7 +149,7 @@
 {
 	char *name;
 
-	if (parse_error)
+	if (implications_off || parse_error)
 		return;
 
 	if (expr->type != EXPR_PREOP)
@@ -135,7 +170,7 @@
 {
 	char *name;
 
-	if (parse_error)
+	if (implications_off || parse_error)
 		return;
 
 	if (is_impossible_path())
@@ -265,7 +300,7 @@
 {
 	char *name;
 
-	if (parse_error)
+	if (implications_off || parse_error)
 		return;
 
 	if (is_impossible_path())
@@ -352,6 +387,7 @@
 	add_hook(&match_declarations, DECLARATION_HOOK);
 	add_extra_mod_hook(&extra_mod_hook);
 	add_hook(&match_assign, ASSIGNMENT_HOOK);
+	add_hook(&match_negative_comparison, CONDITION_HOOK);
 	add_untracked_param_hook(&match_untracked);
 	add_pre_merge_hook(my_id, &pre_merge_hook);
 
--- old/usr/src/tools/smatch/src/check_unwind.c	Mon Aug  5 08:38:10 2019
+++ new/usr/src/tools/smatch/src/check_unwind.c	Mon Aug  5 08:38:10 2019
@@ -194,21 +194,21 @@
 	add_function_hook("release_resource", &match_release, INT_PTR(0));
 	release_function_indicator("release_resource");
 
-	return_implies_state("__request_region", valid_ptr_min, valid_ptr_max, &request_granted, INT_PTR(1));
+	return_implies_state_sval("__request_region", valid_ptr_min_sval, valid_ptr_max_sval, &request_granted, INT_PTR(1));
 	return_implies_state("__request_region", 0, 0, &request_denied, INT_PTR(1));
 	add_function_hook("__release_region", &match_release, INT_PTR(1));
 	release_function_indicator("__release_region");
 
-	return_implies_state("ioremap", valid_ptr_min, valid_ptr_max, &request_granted, INT_PTR(-1));
+	return_implies_state_sval("ioremap", valid_ptr_min_sval, valid_ptr_max_sval, &request_granted, INT_PTR(-1));
 	return_implies_state("ioremap", 0, 0, &request_denied, INT_PTR(-1));
 	add_function_hook("iounmap", &match_release, INT_PTR(0));
 
-	return_implies_state("pci_iomap", valid_ptr_min, valid_ptr_max, &request_granted, INT_PTR(-1));
+	return_implies_state_sval("pci_iomap", valid_ptr_min_sval, valid_ptr_max_sval, &request_granted, INT_PTR(-1));
 	return_implies_state("pci_iomap", 0, 0, &request_denied, INT_PTR(-1));
 	add_function_hook("pci_iounmap", &match_release, INT_PTR(1));
 	release_function_indicator("pci_iounmap");
 
-	return_implies_state("__create_workqueue_key", valid_ptr_min, valid_ptr_max, &request_granted,
+	return_implies_state_sval("__create_workqueue_key", valid_ptr_min_sval, valid_ptr_max_sval, &request_granted,
 			INT_PTR(-1));
 	return_implies_state("__create_workqueue_key", 0, 0, &request_denied, INT_PTR(-1));
 	add_function_hook("destroy_workqueue", &match_release, INT_PTR(0));
@@ -228,6 +228,5 @@
 	add_function_hook("misc_deregister", &match_release, INT_PTR(0));
 	release_function_indicator("misc_deregister");
 
-
 	add_hook(&match_return, RETURN_HOOK);
 }
--- old/usr/src/tools/smatch/src/check_wine_WtoA.c	Mon Aug  5 08:38:10 2019
+++ new/usr/src/tools/smatch/src/check_wine_WtoA.c	Mon Aug  5 08:38:10 2019
@@ -16,7 +16,7 @@
  */
 
 /*
- * Idea from Michael Stefaniuc and Vincent B�ron's earlier WtoA
+ * Idea from Michael Stefaniuc and Vincent Béron's earlier WtoA
  * check.
  *
  * Apparently when you are coding WINE, you are not allowed to call 
--- old/usr/src/tools/smatch/src/check_zero_to_err_ptr.c	Mon Aug  5 08:38:11 2019
+++ new/usr/src/tools/smatch/src/check_zero_to_err_ptr.c	Mon Aug  5 08:38:11 2019
@@ -74,24 +74,59 @@
 	return expr_equiv(next, arg);
 }
 
+static int is_non_zero_int(struct range_list *rl)
+{
+	struct data_range *tmp;
+	int cnt = -1;
+
+	FOR_EACH_PTR(rl, tmp) {
+		cnt++;
+
+		if (cnt == 0) {
+			if (tmp->min.value == INT_MIN &&
+			    tmp->max.value == -1)
+				continue;
+		} else if (cnt == 1) {
+			if (tmp->min.value == 1 &&
+			    tmp->max.value == INT_MAX)
+				return 1;
+		}
+		return 0;
+	} END_FOR_EACH_PTR(tmp);
+	return 0;
+}
+
 static int is_valid_ptr(sval_t sval)
 {
-	if (sval.type == &int_ctype &&
-	    (sval.value == INT_MIN || sval.value == INT_MAX))
+	if (sval.value == INT_MIN || sval.value == INT_MAX)
 		return 0;
 
 	if (sval_cmp(valid_ptr_min_sval, sval) <= 0 &&
-	    sval_cmp(valid_ptr_max_sval, sval) >= 0)
+	    sval_cmp(valid_ptr_max_sval, sval) >= 0) {
 		return 1;
+	}
 	return 0;
 }
 
+static int has_distinct_zero(struct range_list *rl)
+{
+	struct data_range *tmp;
+
+	FOR_EACH_PTR(rl, tmp) {
+		if (tmp->min.value == 0 || tmp->max.value == 0)
+			return 1;
+	} END_FOR_EACH_PTR(tmp);
+	return 0;
+}
+
 static void match_err_ptr(const char *fn, struct expression *expr, void *data)
 {
 	struct expression *arg_expr;
 	struct sm_state *sm, *tmp;
-	sval_t sval;
 
+	if (is_impossible_path())
+		return;
+
 	arg_expr = get_argument_from_call_expr(expr->args, 0);
 	sm = get_sm_state_expr(SMATCH_EXTRA, arg_expr);
 	if (!sm)
@@ -109,17 +144,19 @@
 	FOR_EACH_PTR(sm->possible, tmp) {
 		if (!estate_rl(tmp->state))
 			continue;
+		if (is_non_zero_int(estate_rl(tmp->state)))
+			continue;
+		if (has_distinct_zero(estate_rl(tmp->state))) {
+			sm_warning("passing zero to '%s'", fn);
+			return;
+		}
+		if (strcmp(fn, "PTR_ERR") != 0)
+			continue;
 		if (is_valid_ptr(estate_min(tmp->state)) &&
 		    is_valid_ptr(estate_max(tmp->state))) {
 			sm_warning("passing a valid pointer to '%s'", fn);
 			return;
 		}
-		if (!rl_to_sval(estate_rl(tmp->state), &sval))
-			continue;
-		if (sval.value != 0)
-			continue;
-		sm_warning("passing zero to '%s'", fn);
-		return;
 	} END_FOR_EACH_PTR(tmp);
 }
 
--- old/usr/src/tools/smatch/src/evaluate.c	Mon Aug  5 08:38:11 2019
+++ new/usr/src/tools/smatch/src/evaluate.c	Mon Aug  5 08:38:11 2019
@@ -1946,7 +1946,7 @@
 	return ctype;
 }
 
-static struct symbol *find_identifier(struct ident *ident, struct symbol_list *_list, int *offset)
+struct symbol *find_identifier(struct ident *ident, struct symbol_list *_list, int *offset)
 {
 	struct ptr_list *head = (struct ptr_list *)_list;
 	struct ptr_list *list = head;
--- old/usr/src/tools/smatch/src/expression.h	Mon Aug  5 08:38:11 2019
+++ new/usr/src/tools/smatch/src/expression.h	Mon Aug  5 08:38:11 2019
@@ -261,6 +261,7 @@
 extern void evaluate_symbol_list(struct symbol_list *list);
 extern struct symbol *evaluate_statement(struct statement *stmt);
 extern struct symbol *evaluate_expression(struct expression *);
+struct symbol *find_identifier(struct ident *ident, struct symbol_list *_list, int *offset);
 
 extern int expand_symbol(struct symbol *);
 
--- old/usr/src/tools/smatch/src/graph.c	Mon Aug  5 08:38:11 2019
+++ new/usr/src/tools/smatch/src/graph.c	Mon Aug  5 08:38:11 2019
@@ -1,4 +1,4 @@
-/* Copyright � International Business Machines Corp., 2006
+/* Copyright © International Business Machines Corp., 2006
  *              Adelard LLP, 2007
  *
  * Author: Josh Triplett <josh@freedesktop.org>
--- old/usr/src/tools/smatch/src/smatch.c	Mon Aug  5 08:38:12 2019
+++ new/usr/src/tools/smatch/src/smatch.c	Mon Aug  5 08:38:12 2019
@@ -21,6 +21,7 @@
 #include <unistd.h>
 #include <libgen.h>
 #include "smatch.h"
+#include "smatch_slist.h"
 #include "check_list.h"
 
 char *option_debug_check = (char *)"";
@@ -38,7 +39,6 @@
 int option_no_db = 0;
 int option_enable = 0;
 int option_disable = 0;
-int option_debug_related;
 int option_file_output;
 int option_time;
 int option_mem;
@@ -176,7 +176,7 @@
 }
 
 #define OPTION(_x) do {					\
-	if (match_option((*argvp)[i], #_x)) { 		\
+	if (match_option((*argvp)[1], #_x)) { 		\
 		option_##_x = 1;			\
 	}                                               \
 } while (0)
@@ -231,8 +231,6 @@
 		OPTION(spammy);
 		OPTION(info);
 		OPTION(debug);
-		OPTION(debug_implied);
-		OPTION(debug_related);
 		OPTION(assume_loops);
 		OPTION(no_data);
 		OPTION(two_passes);
@@ -322,6 +320,7 @@
 
 int main(int argc, char **argv)
 {
+	struct string_list *filelist = NULL;
 	int i;
 	reg_func func;
 
@@ -343,8 +342,12 @@
 	data_dir = get_data_dir(argv[0]);
 
 	allocate_hook_memory();
+	allocate_dynamic_states_array(num_checks);
 	create_function_hook_hash();
 	open_smatch_db(option_db_file);
+	sparse_initialize(argc, argv, &filelist);
+	alloc_valid_ptr_rl();
+
 	for (i = 1; i < ARRAY_SIZE(reg_funcs); i++) {
 		func = reg_funcs[i].func;
 		/* The script IDs start at 1.
@@ -355,7 +358,7 @@
 			func(i);
 	}
 
-	smatch(argc, argv);
+	smatch(filelist);
 	free_string(data_dir);
 
 	if (option_succeed)
--- old/usr/src/tools/smatch/src/smatch.h	Mon Aug  5 08:38:12 2019
+++ new/usr/src/tools/smatch/src/smatch.h	Mon Aug  5 08:38:12 2019
@@ -79,8 +79,6 @@
 	struct symbol *sym;
 	unsigned short owner;
 	unsigned short merged:1;
-	unsigned short skip_implications:1;
-	unsigned int nr_children;
 	unsigned int line;
   	struct smatch_state *state;
 	struct stree *pool;
@@ -102,6 +100,11 @@
 };
 DECLARE_PTR_LIST(constraint_list, struct constraint);
 
+struct bit_info {
+	unsigned long long set;
+	unsigned long long possible;
+};
+
 enum hook_type {
 	EXPR_HOOK,
 	STMT_HOOK,
@@ -175,6 +178,8 @@
 			      void *info);
 void return_implies_state(const char *look_for, long long start, long long end,
 			 implication_hook *call_back, void *info);
+void return_implies_state_sval(const char *look_for, sval_t start, sval_t end,
+			 implication_hook *call_back, void *info);
 void select_return_states_hook(int type, return_implies_hook *callback);
 void select_return_states_before(void (*fn)(void));
 void select_return_states_after(void (*fn)(void));
@@ -207,6 +212,7 @@
 extern struct stree *global_states;
 int is_skipped_function(void);
 int is_silenced_function(void);
+extern bool implications_off;
 
 /* smatch_impossible.c */
 int is_impossible_path(void);
@@ -366,6 +372,7 @@
 void remove_parens(char *str);
 struct smatch_state *alloc_state_num(int num);
 struct smatch_state *alloc_state_str(const char *name);
+struct smatch_state *merge_str_state(struct smatch_state *s1, struct smatch_state *s2);
 struct smatch_state *alloc_state_expr(struct expression *expr);
 struct expression *get_argument_from_call_expr(struct expression_list *args,
 					       int num);
@@ -393,7 +400,7 @@
 int get_absolute_min(struct expression *expr, sval_t *sval);
 int get_absolute_max(struct expression *expr, sval_t *sval);
 int parse_call_math(struct expression *expr, char *math, sval_t *val);
-int parse_call_math_rl(struct expression *call, char *math, struct range_list **rl);
+int parse_call_math_rl(struct expression *call, const char *math, struct range_list **rl);
 char *get_value_in_terms_of_parameter_math(struct expression *expr);
 char *get_value_in_terms_of_parameter_math_var_sym(const char *var, struct symbol *sym);
 int is_zero(struct expression *expr);
@@ -429,6 +436,7 @@
 int parent_is_gone_var_sym(const char *name, struct symbol *sym);
 int parent_is_gone(struct expression *expr);
 int invert_op(int op);
+int op_remove_assign(int op);
 int expr_equiv(struct expression *one, struct expression *two);
 void push_int(struct int_stack **stack, int num);
 int pop_int(struct int_stack **stack);
@@ -506,7 +514,7 @@
 extern int __in_fake_parameter_assign;
 extern int __in_fake_struct_assign;
 extern int in_fake_env;
-void smatch (int argc, char **argv);
+void smatch (struct string_list *filelist);
 int inside_loop(void);
 int definitely_inside_loop(void);
 struct expression *get_switch_expr(void);
@@ -536,6 +544,7 @@
 void init_fake_env(void);
 void end_fake_env(void);
 int time_parsing_function(void);
+bool taking_too_long(void);
 
 /* smatch_struct_assignment.c */
 struct expression *get_faked_expression(void);
@@ -553,8 +562,6 @@
 int __handle_expr_statement_assigns(struct expression *expr);
 
 /* smatch_implied.c */
-extern int option_debug_implied;
-extern int option_debug_related;
 struct range_list_stack;
 void param_limit_implications(struct expression *expr, int param, char *key, char *value);
 struct stree *__implied_case_stree(struct expression *switch_expr,
@@ -566,7 +573,12 @@
 void end_assume(void);
 int impossible_assumption(struct expression *left, int op, sval_t sval);
 
+/* smatch_slist.h */
+bool has_dynamic_states(unsigned short owner);
+void set_dynamic_states(unsigned short owner);
+
 /* smatch_extras.c */
+int in_warn_on_macro(void);
 #define SMATCH_EXTRA 5 /* this is my_id from smatch extra set in smatch.c */
 extern int RETURN_ID;
 
@@ -577,10 +589,15 @@
 
 #define MTAG_ALIAS_BIT (1ULL << 63)
 #define MTAG_OFFSET_MASK 0xfffULL
+#define MTAG_SEED 0xdead << 12
 
-extern long long valid_ptr_min, valid_ptr_max;
-extern sval_t valid_ptr_min_sval, valid_ptr_max_sval;
+const extern unsigned long valid_ptr_min;
+extern unsigned long valid_ptr_max;
+extern const sval_t valid_ptr_min_sval;
+extern sval_t valid_ptr_max_sval;
 extern struct range_list *valid_ptr_rl;
+void alloc_valid_ptr_rl(void);
+
 static const sval_t array_min_sval = {
 	.type = &ptr_ctype,
 	{.value = 100000},
@@ -587,69 +604,68 @@
 };
 static const sval_t array_max_sval = {
 	.type = &ptr_ctype,
-	{.value = 199999},
+	{.value = ULONG_MAX - 4095},
 };
 static const sval_t text_seg_min = {
 	.type = &ptr_ctype,
-	{.value = 100000000},
+	{.value = 4096},
 };
 static const sval_t text_seg_max = {
 	.type = &ptr_ctype,
-	{.value = 177777777},
+	{.value = ULONG_MAX - 4095},
 };
 static const sval_t data_seg_min = {
 	.type = &ptr_ctype,
-	{.value = 200000000},
+	{.value = 4096},
 };
 static const sval_t data_seg_max = {
 	.type = &ptr_ctype,
-	{.value = 277777777},
+	{.value = ULONG_MAX - 4095},
 };
 static const sval_t bss_seg_min = {
 	.type = &ptr_ctype,
-	{.value = 300000000},
+	{.value = 4096},
 };
 static const sval_t bss_seg_max = {
 	.type = &ptr_ctype,
-	{.value = 377777777},
+	{.value = ULONG_MAX - 4095},
 };
 static const sval_t stack_seg_min = {
 	.type = &ptr_ctype,
-	{.value = 400000000},
+	{.value = 4096},
 };
 static const sval_t stack_seg_max = {
 	.type = &ptr_ctype,
-	{.value = 477777777},
+	{.value = ULONG_MAX - 4095},
 };
 static const sval_t kmalloc_seg_min = {
 	.type = &ptr_ctype,
-	{.value = 500000000},
+	{.value = 4096},
 };
 static const sval_t kmalloc_seg_max = {
 	.type = &ptr_ctype,
-	{.value = 577777777},
+	{.value = ULONG_MAX - 4095},
 };
 static const sval_t vmalloc_seg_min = {
 	.type = &ptr_ctype,
-	{.value = 600000000},
+	{.value = 4096},
 };
 static const sval_t vmalloc_seg_max = {
 	.type = &ptr_ctype,
-	{.value = 677777777},
+	{.value = ULONG_MAX - 4095},
 };
 static const sval_t fn_ptr_min = {
 	.type = &ptr_ctype,
-	{.value = 700000000},
+	{.value = 4096},
 };
 static const sval_t fn_ptr_max = {
 	.type = &ptr_ctype,
-	{.value = 777777777},
+	{.value = ULONG_MAX - 4095},
 };
 
 char *get_other_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym);
 char *map_call_to_other_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym);
-char *map_long_to_short_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym);
-char *map_long_to_short_name_sym_nostack(const char *name, struct symbol *sym, struct symbol **new_sym);
+char *map_long_to_short_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym, bool use_stack);
 
 #define STRLEN_MAX_RET 1010101
 
@@ -783,7 +799,6 @@
 
 	PARAM_VALUE	= 1001,
 	BUF_SIZE	= 1002,
-	USER_DATA	= 1003,
 	CAPPED_DATA	= 1004,
 	RETURN_VALUE	= 1005,
 	DEREFERENCE	= 1006,
@@ -795,6 +810,7 @@
 	PARAM_FREED	= 1013,
 	DATA_SOURCE	= 1014,
 	FUZZY_MAX	= 1015,
+	HARD_MAX	= 2015,
 	STR_LEN		= 1016,
 	ARRAY_LEN	= 1017,
 	CAPABLE		= 1018,
@@ -803,6 +819,7 @@
 	CASTED_CALL	= 1021,
 	TYPE_LINK	= 1022,
 	UNTRACKED_PARAM = 1023,
+	LOST_PARAM	= 2023,
 	CULL_PATH	= 1024,
 	PARAM_SET	= 1025,
 	PARAM_USED	= 1026,
@@ -812,6 +829,7 @@
 	CONSTRAINT	= 1031,
 	PASSES_TYPE	= 1032,
 	CONSTRAINT_REQUIRED = 1033,
+	BIT_INFO	= 1034,
 	NOSPEC		= 1035,
 	NOSPEC_WB	= 1036,
 	STMT_CNT	= 1037,
@@ -818,8 +836,8 @@
 	TERMINATED	= 1038,
 
 	/* put random temporary stuff in the 7000-7999 range for testing */
-	USER_DATA3	= 8017,
-	USER_DATA3_SET	= 9017,
+	USER_DATA	= 8017,
+	USER_DATA_SET	= 9017,
 	NO_OVERFLOW	= 8018,
 	NO_OVERFLOW_SIMPLE = 8019,
 	LOCKED		= 8020,
@@ -835,6 +853,12 @@
 	MEMORY_TAG	= 8036,
 	MTAG_ASSIGN	= 8035,
 	STRING_VALUE	= 8041,
+
+	BYTE_COUNT	= 8050,
+	ELEM_COUNT	= 8051,
+	ELEM_LAST	= 8052,
+	USED_LAST	= 8053,
+	USED_COUNT	= 8054,
 };
 
 extern struct sqlite3 *smatch_db;
@@ -859,6 +883,7 @@
 const char *get_mtag_name_var_sym(const char *state_name, struct symbol *sym);
 const char *get_mtag_name_expr(struct expression *expr);
 char *get_data_info_name(struct expression *expr);
+int is_recursive_member(const char *param_name);
 
 char *escape_newlines(const char *str);
 void sql_exec(struct sqlite3 *db, int (*callback)(void*, int, char**, char**), void *data, const char *sql);
@@ -952,11 +977,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 insert_mtag_data(sval_t sval, struct range_list *rl);
 void sql_insert_mtag_map(mtag_t tag, int offset, mtag_t container);
 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_tag(mtag_t container, int offset, mtag_t *tag);
+struct smatch_state *swap_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,
 	int (*callback)(void*, int, char**, char**), void *info);
@@ -1012,14 +1038,14 @@
 
 /* check_user_data.c */
 int is_user_macro(struct expression *expr);
-int is_user_data(struct expression *expr);
 int is_capped_user_data(struct expression *expr);
 int implied_user_data(struct expression *expr, struct range_list **rl);
 struct stree *get_user_stree(void);
 int get_user_rl(struct expression *expr, struct range_list **rl);
-int get_user_rl_spammy(struct expression *expr, struct range_list **rl);
 int is_user_rl(struct expression *expr);
 int get_user_rl_var_sym(const char *name, struct symbol *sym, struct range_list **rl);
+bool user_rl_capped(struct expression *expr);
+struct range_list *var_user_rl(struct expression *expr);
 
 /* check_locking.c */
 void print_held_locks();
@@ -1055,6 +1081,7 @@
 int state_to_comparison(struct smatch_state *state);
 struct smatch_state *merge_compare_states(struct smatch_state *s1, struct smatch_state *s2);
 int get_comparison(struct expression *left, struct expression *right);
+int get_comparison_no_extra(struct expression *a, struct expression *b);
 int get_comparison_strings(const char *one, const char *two);
 int possible_comparison(struct expression *a, int comparison, struct expression *b);
 struct state_list *get_all_comparisons(struct expression *expr);
@@ -1109,15 +1136,18 @@
 sval_t sval_binop(sval_t left, int op, sval_t right);
 int sval_binop_overflows(sval_t left, int op, sval_t right);
 int sval_binop_overflows_no_sign(sval_t left, int op, sval_t right);
+int find_first_zero_bit(unsigned long long uvalue);
+int sm_fls64(unsigned long long uvalue);
 unsigned long long fls_mask(unsigned long long uvalue);
 unsigned long long sval_fls_mask(sval_t sval);
 const char *sval_to_str(sval_t sval);
+const char *sval_to_str_or_err_ptr(sval_t sval);
 const char *sval_to_numstr(sval_t sval);
 sval_t ll_to_sval(long long val);
 
 /* smatch_string_list.c */
 int list_has_string(struct string_list *str_list, const char *str);
-void insert_string(struct string_list **str_list, const char *str);
+int insert_string(struct string_list **str_list, const char *str);
 struct string_list *clone_str_list(struct string_list *orig);
 struct string_list *combine_string_lists(struct string_list *one, struct string_list *two);
 
@@ -1138,6 +1168,7 @@
 
 /* check_string_len.c */
 int get_formatted_string_size(struct expression *call, int arg);
+int get_formatted_string_min_size(struct expression *call, int arg);
 
 /* smatch_param_set.c */
 int param_was_set(struct expression *expr);
@@ -1154,12 +1185,15 @@
 void set_auto_copy(int owner);
 
 /* check_buf_comparison */
-struct expression *get_size_variable(struct expression *buf);
+const char *limit_type_str(unsigned int limit_type);
+struct expression *get_size_variable(struct expression *buf, int *limit_type);
 struct expression *get_array_variable(struct expression *size);
+int buf_comparison_index_ok(struct expression *expr);
 
 /* smatch_untracked_param.c */
 void mark_untracked(struct expression *expr, int param, const char *key, const char *value);
 void add_untracked_param_hook(void (func)(struct expression *call, int param));
+void add_lost_param_hook(void (func)(struct expression *call, int param));
 void mark_all_params_untracked(int return_id, char *return_ranges, struct expression *expr);
 
 /* smatch_strings.c */
@@ -1194,53 +1228,61 @@
 /* smatch_container_of.c */
 int get_param_from_container_of(struct expression *expr);
 int get_offset_from_container_of(struct expression *expr);
+char *get_container_name(struct expression *container, struct expression *expr);
 
 /* smatch_mtag.c */
 int get_string_mtag(struct expression *expr, mtag_t *tag);
 int get_toplevel_mtag(struct symbol *sym, mtag_t *tag);
-int get_mtag(struct expression *expr, mtag_t *tag);
-int get_mtag_offset(struct expression *expr, mtag_t *tag, int *offset);
 int create_mtag_alias(mtag_t tag, struct expression *expr, mtag_t *new);
 int expr_to_mtag_offset(struct expression *expr, mtag_t *tag, int *offset);
 void update_mtag_data(struct expression *expr);
 int get_mtag_sval(struct expression *expr, sval_t *sval);
-int get_mtag_addr_sval(struct expression *expr, sval_t *sval);
 
 /* Trinity fuzzer stuff */
 const char *get_syscall_arg_type(struct symbol *sym);
 
+/* smatch_bit_info.c */
+struct bit_info *get_bit_info(struct expression *expr);
+struct bit_info *get_bit_info_var_sym(const char *name, struct symbol *sym);
 /* smatch_mem_tracker.c */
 extern int option_mem;
+unsigned long get_mem_kb(void);
 unsigned long get_max_memory(void);
 
 /* check_is_nospec.c */
 bool is_nospec(struct expression *expr);
+long get_stmt_cnt(void);
 
 /* smatch_nul_terminator.c */
 bool is_nul_terminated(struct expression *expr);
+/* check_kernel.c  */
+bool is_ignored_kernel_data(const char *name);
 
+static inline bool type_is_ptr(struct symbol *type)
+{
+	return type &&
+	       (type->type == SYM_PTR ||
+		type->type == SYM_ARRAY ||
+		type->type == SYM_FN);
+}
+
 static inline int type_bits(struct symbol *type)
 {
 	if (!type)
 		return 0;
-	if (type->type == SYM_PTR)  /* Sparse doesn't set this for &pointers */
+	if (type_is_ptr(type))
 		return bits_in_pointer;
-	if (type->type == SYM_ARRAY)
-		return bits_in_pointer;
 	if (!type->examined)
 		examine_symbol_type(type);
 	return type->bit_size;
 }
 
-static inline bool type_is_ptr(struct symbol *type)
-{
-	return type && (type->type == SYM_PTR || type->type == SYM_ARRAY);
-}
-
 static inline int type_unsigned(struct symbol *base_type)
 {
 	if (!base_type)
 		return 0;
+	if (is_ptr_type(base_type))
+		return 1;
 	if (base_type->ctype.modifiers & MOD_UNSIGNED)
 		return 1;
 	return 0;
@@ -1250,8 +1292,8 @@
 {
 	if (!type)
 		return 0;
-	if (type->type == SYM_ARRAY)
-		return bits_in_pointer - 1;
+	if (is_ptr_type(type))
+		return bits_in_pointer;
 	if (type_unsigned(type))
 		return type_bits(type);
 	return type_bits(type) - 1;
--- old/usr/src/tools/smatch/src/smatch_about_fn_ptr_arg.c	Mon Aug  5 08:38:14 2019
+++ new/usr/src/tools/smatch/src/smatch_about_fn_ptr_arg.c	Mon Aug  5 08:38:14 2019
@@ -139,7 +139,7 @@
 	}
 
 	strncpy(buf, fn_str, sizeof(buf));
-	snprintf(buf + len_str, sizeof(buf) - len_str, end_type);
+	snprintf(buf + len_str, sizeof(buf) - len_str, "%s", end_type);
 	*sym = tmp_sym;
 	return alloc_string(buf);
 }
--- old/usr/src/tools/smatch/src/smatch_address.c	Mon Aug  5 08:38:17 2019
+++ new/usr/src/tools/smatch/src/smatch_address.c	Mon Aug  5 08:38:17 2019
@@ -62,22 +62,53 @@
 	return 0;
 }
 
+static bool matches_anonymous_union(struct symbol *sym, const char *member_name)
+{
+	struct symbol *type, *tmp;
+
+	if (sym->ident)
+		return false;
+	type = get_real_base_type(sym);
+	if (!type || type->type != SYM_UNION)
+		return false;
+
+	FOR_EACH_PTR(type->symbol_list, tmp) {
+		if (tmp->ident &&
+		    strcmp(member_name, tmp->ident->name) == 0) {
+			return true;
+		}
+	} END_FOR_EACH_PTR(tmp);
+
+	return false;
+}
+
 int get_member_offset(struct symbol *type, const char *member_name)
 {
 	struct symbol *tmp;
 	int offset;
+	int bits;
 
 	if (!type || type->type != SYM_STRUCT)
 		return -1;
 
+	bits = 0;
 	offset = 0;
 	FOR_EACH_PTR(type->symbol_list, tmp) {
+		if (bits_to_bytes(bits + type_bits(tmp)) > tmp->ctype.alignment) {
+			offset += bits_to_bytes(bits);
+			bits = 0;
+		}
 		offset = ALIGN(offset, tmp->ctype.alignment);
 		if (tmp->ident &&
 		    strcmp(member_name, tmp->ident->name) == 0) {
 			return offset;
 		}
-		offset += type_bytes(tmp);
+		if (matches_anonymous_union(tmp, member_name))
+			return offset;
+		if (!(type_bits(tmp) % 8) && type_bits(tmp) / 8 == type_bytes(tmp))
+			offset += type_bytes(tmp);
+		else
+			bits += type_bits(tmp);
 	} END_FOR_EACH_PTR(tmp);
 	return -1;
 }
@@ -99,6 +130,8 @@
 		return -1;
 
 	type = get_type(expr->deref);
+	if (type_is_ptr(type))
+		type = get_real_base_type(type);
 	if (!type || type->type != SYM_STRUCT)
 		return -1;
 
@@ -108,23 +141,6 @@
 	return offset;
 }
 
-static struct range_list *filter_unknown_negatives(struct range_list *rl)
-{
-	struct data_range *first;
-	struct range_list *filter = NULL;
-
-	first = first_ptr_list((struct ptr_list *)rl);
-
-	if (sval_is_min(first->min) &&
-	    sval_is_negative(first->max) &&
-	    first->max.value == -1) {
-		add_ptr_list(&filter, first);
-		return rl_filter(rl, filter);
-	}
-
-	return rl;
-}
-
 static void add_offset_to_pointer(struct range_list **rl, int offset)
 {
 	sval_t min, max, remove, sval;
@@ -138,6 +154,9 @@
 	if (offset == 0)
 		return;
 
+	if (is_unknown_ptr(orig))
+		return;
+
 	/*
 	 * This function doesn't necessarily work how you might expect...
 	 *
@@ -164,16 +183,6 @@
 		return;
 	}
 
-	orig = filter_unknown_negatives(orig);
-	/*
-	 * FIXME:  This is not really accurate but we're a bit screwed anyway
-	 * when we start doing pointer math with error pointers so it's probably
-	 * not important.
-	 *
-	 */
-	if (sval_is_negative(rl_min(orig)))
-		return;
-
 	/* no wrap around */
 	max.uvalue = rl_max(orig).uvalue;
 	if (max.uvalue > sval_type_max(&ptr_ctype).uvalue - offset) {
@@ -193,51 +202,93 @@
 	if (!sym)
 		return NULL;
 
-	if (sym->ctype.modifiers & (MOD_TOPLEVEL | MOD_STATIC)) {
-		if (sym->initializer)
-			return alloc_rl(data_seg_min, data_seg_max);
-		else
-			return alloc_rl(bss_seg_min, bss_seg_max);
-	}
-	return alloc_rl(stack_seg_min, stack_seg_max);
+	return alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
 }
 
 int get_address_rl(struct expression *expr, struct range_list **rl)
 {
+	struct expression *unop;
+
 	expr = strip_expr(expr);
 	if (!expr)
 		return 0;
 
 	if (expr->type == EXPR_STRING) {
-		*rl = alloc_rl(text_seg_min, text_seg_max);
+		*rl = alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
 		return 1;
 	}
 
-	if (expr->type == EXPR_PREOP && expr->op == '&') {
-		struct expression *unop;
+	if (expr->type == EXPR_PREOP && expr->op == '&')
+		expr = strip_expr(expr->unop);
+	else {
+		struct symbol *type;
 
-		unop = strip_expr(expr->unop);
-		if (unop->type == EXPR_SYMBOL) {
-			*rl = where_allocated_rl(unop->symbol);
+		type = get_type(expr);
+		if (!type || type->type != SYM_ARRAY)
+			return 0;
+	}
+
+	if (expr->type == EXPR_SYMBOL) {
+		*rl = where_allocated_rl(expr->symbol);
+		return 1;
+	}
+
+	if (is_array(expr)) {
+		struct expression *array;
+		struct expression *offset_expr;
+		struct range_list *array_rl, *offset_rl, *bytes_rl, *res;
+		struct symbol *type;
+		sval_t bytes;
+
+		array = get_array_base(expr);
+		offset_expr = get_array_offset(expr);
+
+		type = get_type(array);
+		type = get_real_base_type(type);
+		bytes.type = ssize_t_ctype;
+		bytes.uvalue = type_bytes(type);
+		bytes_rl = alloc_rl(bytes, bytes);
+
+		get_absolute_rl(array, &array_rl);
+		get_absolute_rl(offset_expr, &offset_rl);
+
+		if (type_bytes(type)) {
+			res = rl_binop(offset_rl, '*', bytes_rl);
+			res = rl_binop(res, '+', array_rl);
+			*rl = res;
+			return true;
+		}
+
+		if (implied_not_equal(array, 0) ||
+		    implied_not_equal(offset_expr, 0)) {
+			*rl = alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
 			return 1;
 		}
 
-		if (unop->type == EXPR_DEREF) {
-			int offset = get_member_offset_from_deref(unop);
+		return 0;
+	}
 
+	if (expr->type == EXPR_DEREF && expr->member) {
+		struct range_list *unop_rl;
+		int offset;
+
+		offset = get_member_offset_from_deref(expr);
+		unop = strip_expr(expr->unop);
+		if (unop->type == EXPR_PREOP && unop->op == '*')
 			unop = strip_expr(unop->unop);
-			if (unop->type == EXPR_SYMBOL) {
-				*rl = where_allocated_rl(unop->symbol);
-			} else if (unop->type == EXPR_PREOP && unop->op == '*') {
-				unop = strip_expr(unop->unop);
-				get_absolute_rl(unop, rl);
-			} else {
-				return 0;
-			}
 
+		if (offset >= 0 &&
+		    get_implied_rl(unop, &unop_rl) &&
+		    !is_whole_rl(unop_rl)) {
+			*rl = unop_rl;
 			add_offset_to_pointer(rl, offset);
 			return 1;
 		}
+
+		if (implied_not_equal(unop, 0) || offset > 0) {
+			*rl = alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
+			return 1;
+		}
 
 		return 0;
 	}
--- old/usr/src/tools/smatch/src/smatch_array_values.c	Mon Aug  5 08:38:19 2019
+++ new/usr/src/tools/smatch/src/smatch_array_values.c	Mon Aug  5 08:38:19 2019
@@ -163,7 +163,7 @@
 	struct symbol *type;
 	char *name;
 
-	type = get_type(expr->right);
+	type = get_type(expr->left);
 	if (!type || type->type != SYM_BASETYPE)
 		return;
 
@@ -176,7 +176,8 @@
 		return;
 
 	if (expr->op != '=') {
-		rl = alloc_whole_rl(type);
+		rl = alloc_whole_rl(get_type(expr->right));
+		rl = cast_rl(type, rl);
 	} else {
 		get_absolute_rl(expr->right, &rl);
 		rl = cast_rl(type, rl);
--- old/usr/src/tools/smatch/src/smatch_assigned_expr.c	Mon Aug  5 08:38:19 2019
+++ new/usr/src/tools/smatch/src/smatch_assigned_expr.c	Mon Aug  5 08:38:19 2019
@@ -28,8 +28,12 @@
 static int my_id;
 static int link_id;
 
+static struct expression *skip_mod;
+
 static void undef(struct sm_state *sm, struct expression *mod_expr)
 {
+	if (mod_expr == skip_mod)
+		return;
 	set_state(my_id, sm->name, sm->sym, &undefined);
 }
 
@@ -55,6 +59,7 @@
 
 static void match_assignment(struct expression *expr)
 {
+	static struct expression *ignored_expr;
 	struct symbol *left_sym, *right_sym;
 	char *left_name = NULL;
 	char *right_name = NULL;
@@ -72,6 +77,12 @@
 			return;
 	}
 
+	if (expr->left == ignored_expr)
+		return;
+	ignored_expr = NULL;
+	if (__in_fake_parameter_assign)
+		ignored_expr = expr->left;
+
 	left_name = expr_to_var_sym(expr->left, &left_sym);
 	if (!left_name || !left_sym)
 		goto free;
@@ -118,6 +129,7 @@
 	if (!name || !sym)
 		goto free;
 
+	skip_mod = expr;
 	set_state(my_id, name, sym, alloc_state_expr(right));
 free:
 	free_string(name);
@@ -126,6 +138,7 @@
 void register_assigned_expr(int id)
 {
 	my_id = check_assigned_expr_id = id;
+	set_dynamic_states(check_assigned_expr_id);
 	add_hook(&match_assignment, ASSIGNMENT_HOOK_AFTER);
 	add_modification_hook(my_id, &undef);
 	select_return_states_hook(PARAM_SET, &record_param_assignment);
@@ -134,6 +147,7 @@
 void register_assigned_expr_links(int id)
 {
 	link_id = id;
+	set_dynamic_states(link_id);
 	db_ignore_states(link_id);
 	set_up_link_functions(my_id, link_id);
 }
--- /dev/null	Mon Aug  5 08:38:20 2019
+++ new/usr/src/tools/smatch/src/smatch_bits.c	Mon Aug  5 08:38:20 2019
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2015 Oracle.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
+ */
+
+/*
+ * This is to track when variables are masked away.
+ *
+ */
+
+#include "smatch.h"
+#include "smatch_extra.h"
+#include "smatch_slist.h"
+
+static int my_id;
+
+static const struct bit_info unknown_bit_info = {
+	.possible = -1ULL,
+};
+
+ALLOCATOR(bit_info, "bit data");
+static struct bit_info *alloc_bit_info(unsigned long long set, unsigned long long possible)
+{
+	struct bit_info *bit_info = __alloc_bit_info(0);
+
+	bit_info->set = set;
+	bit_info->possible = possible;
+
+	return bit_info;
+}
+
+static struct smatch_state *alloc_bstate(unsigned long long set, unsigned long long possible)
+{
+	struct smatch_state *state;
+	char buf[64];
+
+	state = __alloc_smatch_state(0);
+	snprintf(buf, sizeof(buf), "0x%llx + 0x%llx", set, possible);
+	state->name = alloc_sname(buf);
+	state->data = alloc_bit_info(set, possible);
+
+	return state;
+}
+
+static struct bit_info *rl_to_binfo(struct range_list *rl)
+{
+	struct bit_info *ret = __alloc_bit_info(0);
+	sval_t sval;
+
+	if (rl_to_sval(rl, &sval)) {
+		ret->set = sval.uvalue;
+		ret->possible = sval.uvalue;
+
+		return ret;
+	}
+
+	ret->set = 0;
+	ret->possible = sval_fls_mask(rl_max(rl));
+	// FIXME: what about negatives?
+
+	return ret;
+}
+
+static int is_unknown_binfo(struct symbol *type, struct bit_info *binfo)
+{
+	if (!type)
+		type = &ullong_ctype;
+
+	if (binfo->set != 0)
+		return 0;
+	if (binfo->possible < (-1ULL >> (64 - type_bits(type))))
+		return 0;
+
+	return 1;
+}
+
+static struct smatch_state *unmatched_state(struct sm_state *sm)
+{
+	struct smatch_state *estate;
+	struct symbol *type;
+	unsigned long long possible;
+	struct bit_info *p;
+
+	estate = get_state(SMATCH_EXTRA, sm->name, sm->sym);
+	if (estate_rl(estate)) {
+		p = rl_to_binfo(estate_rl(estate));
+		return alloc_bstate(p->set, p->possible);
+	}
+
+	type = estate_type(estate);
+	if (!type)
+		return alloc_bstate(0, -1ULL);
+
+	if (type_bits(type) == 64)
+		possible = -1ULL;
+	else
+		possible = (1ULL << type_bits(type)) - 1;
+
+	return alloc_bstate(0, possible);
+}
+
+static void match_modify(struct sm_state *sm, struct expression *mod_expr)
+{
+	// FIXME: we really need to store the type
+
+	set_state(my_id, sm->name, sm->sym, alloc_bstate(0, -1ULL));
+}
+
+static int binfo_equiv(struct bit_info *one, struct bit_info *two)
+{
+	if (one->set == two->set &&
+	    one->possible == two->possible)
+		return 1;
+	return 0;
+}
+
+static struct smatch_state *merge_bstates(struct smatch_state *one_state, struct smatch_state *two_state)
+{
+	struct bit_info *one, *two;
+
+	one = one_state->data;
+	two = two_state->data;
+
+	if (binfo_equiv(one, two))
+		return one_state;
+
+	return alloc_bstate(one->set & two->set, one->possible | two->possible);
+}
+
+/*
+ * The combine_bit_info() takes two bit_infos and takes creates the most
+ * accurate picture we can assuming both are true.  Or it returns unknown if
+ * the information is logically impossible.
+ *
+ * Which means that it takes the | of the ->set bits and the & of the possibly
+ * set bits, which is the opposite of what merge_bstates() does.
+ *
+ */
+static struct bit_info *combine_bit_info(struct bit_info *one, struct bit_info *two)
+{
+	struct bit_info *ret = __alloc_bit_info(0);
+
+	if ((one->set & two->possible) != one->set)
+		return alloc_bit_info(0, -1ULL);
+	if ((two->set & one->possible) != two->set)
+		return alloc_bit_info(0, -1ULL);
+
+	ret->set = one->set | two->set;
+	ret->possible = one->possible & two->possible;
+
+	return ret;
+}
+
+static struct bit_info *binfo_AND(struct bit_info *left, struct bit_info *right)
+{
+	unsigned long long set = 0;
+	unsigned long long possible = -1ULL;
+
+	if (!left && !right) {
+		/* nothing */
+	} else if (!left) {
+		possible = right->possible;
+	} else if (!right) {
+		possible = left->possible;
+	} else {
+		set = left->set & right->set;
+		possible = left->possible & right->possible;
+	}
+
+	return alloc_bit_info(set, possible);
+}
+
+static struct bit_info *binfo_OR(struct bit_info *left, struct bit_info *right)
+{
+	unsigned long long set = 0;
+	unsigned long long possible = -1ULL;
+
+	if (!left && !right) {
+		/* nothing */
+	} else if (!left) {
+		set = right->set;
+	} else if (!right) {
+		set = left->set;
+	} else {
+		set = left->set | right->set;
+		possible = left->possible | right->possible;
+	}
+
+	return alloc_bit_info(set, possible);
+}
+
+struct bit_info *get_bit_info(struct expression *expr)
+{
+	struct range_list *rl;
+	struct smatch_state *bstate;
+	struct bit_info tmp;
+	struct bit_info *extra_info;
+	struct bit_info *bit_info;
+	sval_t known;
+
+	expr = strip_parens(expr);
+
+	if (get_implied_value(expr, &known))
+		return alloc_bit_info(known.value, known.value);
+
+	if (expr->type == EXPR_BINOP) {
+		if (expr->op == '&')
+			return binfo_AND(get_bit_info(expr->left),
+					 get_bit_info(expr->right));
+		if (expr->op == '|')
+			return binfo_OR(get_bit_info(expr->left),
+					get_bit_info(expr->right));
+	}
+
+	if (get_implied_rl(expr, &rl))
+		extra_info = rl_to_binfo(rl);
+	else {
+		struct symbol *type;
+
+		tmp = unknown_bit_info;
+		extra_info = &tmp;
+
+		type = get_type(expr);
+		if (!type)
+			type = &ullong_ctype;
+		if (type_bits(type) == 64)
+			extra_info->possible = -1ULL;
+		else
+			extra_info->possible = (1ULL << type_bits(type)) - 1;
+	}
+
+	bstate = get_state_expr(my_id, expr);
+	if (bstate)
+		bit_info = bstate->data;
+	else
+		bit_info = (struct bit_info *)&unknown_bit_info;
+
+	return combine_bit_info(extra_info, bit_info);
+}
+
+static int is_single_bit(sval_t sval)
+{
+	int i;
+	int count = 0;
+
+	for (i = 0; i < 64; i++) {
+		if (sval.uvalue & 1ULL << i &&
+		    count++)
+			return 0;
+	}
+	if (count == 1)
+		return 1;
+	return 0;
+}
+
+static void match_compare(struct expression *expr)
+{
+	sval_t val;
+
+	if (expr->type != EXPR_COMPARE)
+		return;
+	if (expr->op != SPECIAL_EQUAL &&
+	    expr->op != SPECIAL_NOTEQUAL)
+		return;
+
+	if (!get_implied_value(expr->right, &val))
+		return;
+
+	set_true_false_states_expr(my_id, expr->left,
+			(expr->op == SPECIAL_EQUAL) ? alloc_bstate(val.uvalue, val.uvalue) : NULL,
+			(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;
+
+	if (expr->op != '=')
+		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));
+}
+
+static void match_condition(struct expression *expr)
+{
+	struct bit_info *orig;
+	struct bit_info true_info;
+	struct bit_info false_info;
+	sval_t right;
+
+	if (expr->type != EXPR_BINOP ||
+	    expr->op != '&')
+		return;
+
+	if (!get_value(expr->right, &right))
+		return;
+
+	orig = get_bit_info(expr->left);
+	true_info = *orig;
+	false_info = *orig;
+
+	if (right.uvalue == 0 || is_single_bit(right))
+		true_info.set &= right.uvalue;
+
+	true_info.possible &= right.uvalue;
+	false_info.possible &= ~right.uvalue;
+
+	set_true_false_states_expr(my_id, expr->left,
+				   alloc_bstate(true_info.set, true_info.possible),
+				   alloc_bstate(false_info.set, false_info.possible));
+}
+
+static void match_call_info(struct expression *expr)
+{
+	struct bit_info *binfo, *rl_binfo;
+	struct expression *arg;
+	struct range_list *rl;
+	char buf[64];
+	int i;
+
+	i = -1;
+	FOR_EACH_PTR(expr->args, arg) {
+		i++;
+		binfo = get_bit_info(arg);
+		if (!binfo)
+			continue;
+		if (is_unknown_binfo(get_type(arg), binfo))
+			continue;
+		if (get_implied_rl(arg, &rl)) {
+			rl_binfo = rl_to_binfo(rl);
+			if (binfo_equiv(rl_binfo, binfo))
+				continue;
+		}
+		// If is just non-negative continue
+		// If ->set == ->possible continue
+		snprintf(buf, sizeof(buf), "0x%llx,0x%llx", binfo->set, binfo->possible);
+		sql_insert_caller_info(expr, BIT_INFO, i, "$", buf);
+	} END_FOR_EACH_PTR(arg);
+}
+
+static void struct_member_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
+{
+	struct bit_info *binfo = sm->state->data;
+	struct smatch_state *estate;
+	struct bit_info *implied_binfo;
+	char buf[64];
+
+	if (!binfo)
+		return;
+
+	/* This means it can only be one value, so it's handled by smatch_extra. */
+	if (binfo->set == binfo->possible)
+		return;
+
+	estate = get_state(SMATCH_EXTRA, sm->name, sm->sym);
+	if (is_unknown_binfo(estate_type(estate), binfo))
+		return;
+
+	if (estate_rl(estate)) {
+		sval_t sval;
+
+		if (estate_get_single_value(estate, &sval))
+			return;
+
+		implied_binfo = rl_to_binfo(estate_rl(estate));
+		if (binfo_equiv(implied_binfo, binfo))
+			return;
+	}
+
+	snprintf(buf, sizeof(buf), "0x%llx,0x%llx", binfo->set, binfo->possible);
+	sql_insert_caller_info(call, BIT_INFO, param, printed_name, buf);
+}
+
+static void set_param_bits(const char *name, struct symbol *sym, char *key, char *value)
+{
+	char fullname[256];
+	unsigned long long set, possible;
+
+	if (strcmp(key, "*$") == 0)
+		snprintf(fullname, sizeof(fullname), "*%s", name);
+	else if (strncmp(key, "$", 1) == 0)
+		snprintf(fullname, 256, "%s%s", name, key + 1);
+	else
+		return;
+
+	set = strtoull(value, &value, 16);
+	if (*value != ',')
+		return;
+	value++;
+	possible = strtoull(value, &value, 16);
+
+	set_state(my_id, fullname, sym, alloc_bstate(set, possible));
+}
+
+void register_bits(int id)
+{
+	my_id = id;
+
+	set_dynamic_states(my_id);
+
+	add_unmatched_state_hook(my_id, &unmatched_state);
+	add_merge_hook(my_id, &merge_bstates);
+
+	add_hook(&match_condition, CONDITION_HOOK);
+	add_hook(&match_compare, CONDITION_HOOK);
+	add_hook(&match_assign, ASSIGNMENT_HOOK);
+	add_modification_hook(my_id, &match_modify);
+
+	add_hook(&match_call_info, FUNCTION_CALL_HOOK);
+	add_member_info_callback(my_id, struct_member_callback);
+	select_caller_info_hook(set_param_bits, BIT_INFO);
+}
--- old/usr/src/tools/smatch/src/smatch_buf_comparison.c	Mon Aug  5 08:38:20 2019
+++ new/usr/src/tools/smatch/src/smatch_buf_comparison.c	Mon Aug  5 08:38:20 2019
@@ -29,24 +29,18 @@
 static int link_id;
 
 /*
- * We need this for code which does:
+ * There is a bunch of code which does this:
  *
  *     if (size)
  *         foo = malloc(size);
  *
- * We want to record that the size of "foo" is "size" even after the merge.
+ * So if "size" is non-zero then the size of "foo" is size.  But really it's
+ * also true if size is zero.  It's just better to assume to not trample over
+ * the data that we have by merging &undefined states.
  *
  */
 static struct smatch_state *unmatched_state(struct sm_state *sm)
 {
-	struct expression *size_expr;
-	sval_t sval;
-
-	if (!sm->state->data)
-		return &undefined;
-	size_expr = sm->state->data;
-	if (!get_implied_value(size_expr, &sval) || sval.value != 0)
-		return &undefined;
 	return sm->state;
 }
 
@@ -82,15 +76,50 @@
 	set_state(link_id, sm->name, sm->sym, &undefined);
 }
 
-static struct smatch_state *alloc_expr_state(struct expression *expr)
+static const char *limit_map[] = {
+	"byte_count",
+	"elem_count",
+	"elem_last",
+	"used_count",
+	"used_last",
+};
+
+int state_to_limit(struct smatch_state *state)
 {
+	int i;
+
+	if (!state || !state->data)
+		return -1;
+
+	for (i = 0; i < ARRAY_SIZE(limit_map); i++) {
+		if (strncmp(state->name, limit_map[i], strlen(limit_map[i])) == 0)
+			return i + BYTE_COUNT;
+	}
+
+	return -1;
+}
+
+const char *limit_type_str(unsigned int limit_type)
+{
+	if (limit_type - BYTE_COUNT >= ARRAY_SIZE(limit_map)) {
+		sm_msg("internal: wrong size type %u", limit_type);
+		return "unknown";
+	}
+
+	return limit_map[limit_type - BYTE_COUNT];
+}
+
+static struct smatch_state *alloc_compare_size(int limit_type, struct expression *expr)
+{
 	struct smatch_state *state;
 	char *name;
+	char buf[256];
 
 	state = __alloc_smatch_state(0);
 	expr = strip_expr(expr);
 	name = expr_to_str(expr);
-	state->name = alloc_sname(name);
+	snprintf(buf, sizeof(buf), "%s %s", limit_type_str(limit_type), name);
+	state->name = alloc_sname(buf);
 	free_string(name);
 	state->data = expr;
 	return state;
@@ -111,7 +140,7 @@
 	return type_bytes(type);
 }
 
-static void db_save_type_links(struct expression *array, struct expression *size)
+static void db_save_type_links(struct expression *array, int type_limit, struct expression *size)
 {
 	const char *array_name;
 
@@ -118,7 +147,7 @@
 	array_name = get_data_info_name(array);
 	if (!array_name)
 		array_name = "";
-	sql_insert_data_info(size, ARRAY_LEN, array_name);
+	sql_insert_data_info(size, type_limit, array_name);
 }
 
 static void match_alloc_helper(struct expression *pointer, struct expression *size)
@@ -125,6 +154,7 @@
 {
 	struct expression *tmp;
 	struct sm_state *sm;
+	int limit_type = ELEM_COUNT;
 	sval_t sval;
 	int cnt = 0;
 
@@ -159,11 +189,17 @@
 	if (get_value(size, &sval))
 		return;
 
-	db_save_type_links(pointer, size);
-	sm = set_state_expr(size_id, pointer, alloc_expr_state(size));
+	if (size->type == EXPR_BINOP && size->op == '+' &&
+	    get_value(size->right, &sval) && sval.value == 1) {
+		size = size->left;
+		limit_type = ELEM_LAST;
+	}
+
+	db_save_type_links(pointer, limit_type, size);
+	sm = set_state_expr(size_id, pointer, alloc_compare_size(limit_type, size));
 	if (!sm)
 		return;
-	set_state_expr(link_id, size, alloc_expr_state(pointer));
+	set_state_expr(link_id, size, alloc_state_expr(pointer));
 }
 
 static void match_alloc(const char *fn, struct expression *expr, void *_size_arg)
@@ -182,6 +218,7 @@
 	int start_arg = PTR_INT(_start_arg);
 	struct expression *pointer, *call, *arg;
 	struct sm_state *tmp;
+	int limit_type = ELEM_COUNT;
 	sval_t sval;
 
 	pointer = strip_expr(expr->left);
@@ -191,21 +228,28 @@
 	    sval.value == bytes_per_element(pointer))
 		arg = get_argument_from_call_expr(call->args, start_arg + 1);
 
-	db_save_type_links(pointer, arg);
-	tmp = set_state_expr(size_id, pointer, alloc_expr_state(arg));
+	if (arg->type == EXPR_BINOP && arg->op == '+' &&
+	    get_value(arg->right, &sval) && sval.value == 1) {
+		arg = arg->left;
+		limit_type = ELEM_LAST;
+	}
+
+	db_save_type_links(pointer, limit_type, arg);
+	tmp = set_state_expr(size_id, pointer, alloc_compare_size(limit_type, arg));
 	if (!tmp)
 		return;
-	set_state_expr(link_id, arg, alloc_expr_state(pointer));
+	set_state_expr(link_id, arg, alloc_state_expr(pointer));
 }
 
-struct expression *get_size_variable(struct expression *buf)
+struct expression *get_size_variable(struct expression *buf, int *limit_type)
 {
 	struct smatch_state *state;
 
 	state = get_state_expr(size_id, buf);
-	if (state)
-		return state->data;
-	return NULL;
+	if (!state)
+		return NULL;
+	*limit_type = state_to_limit(state);
+	return state->data;
 }
 
 struct expression *get_array_variable(struct expression *size)
@@ -224,6 +268,7 @@
 	struct expression *size;
 	struct expression *offset;
 	char *array_str, *offset_str;
+	int limit_type;
 
 	expr = strip_expr(expr);
 	if (!is_array(expr))
@@ -230,9 +275,11 @@
 		return;
 
 	array = get_array_base(expr);
-	size = get_size_variable(array);
+	size = get_size_variable(array, &limit_type);
 	if (!size)
 		return;
+	if (limit_type != ELEM_COUNT)
+		return;
 	offset = get_array_offset(expr);
 	if (!possible_comparison(size, SPECIAL_EQUAL, offset))
 		return;
@@ -317,21 +364,31 @@
 	return db_info.ret;
 }
 
-static int known_access_ok_comparison(struct expression *expr)
+int buf_comparison_index_ok(struct expression *expr)
 {
 	struct expression *array;
 	struct expression *size;
 	struct expression *offset;
+	int limit_type;
 	int comparison;
 
 	array = get_array_base(expr);
-	size = get_size_variable(array);
+	size = get_size_variable(array, &limit_type);
 	if (!size)
 		return 0;
 	offset = get_array_offset(expr);
-	comparison = get_comparison(size, offset);
-	if (comparison == '>' || comparison == SPECIAL_UNSIGNED_GT)
+	comparison = get_comparison(offset, size);
+	if (!comparison)
+		return 0;
+
+	if ((limit_type == ELEM_COUNT || limit_type == ELEM_LAST) &&
+	    (comparison == '<' || comparison == SPECIAL_UNSIGNED_LT))
 		return 1;
+	if (limit_type == ELEM_LAST &&
+	    (comparison == SPECIAL_LTE ||
+	     comparison == SPECIAL_UNSIGNED_LTE ||
+	     comparison == SPECIAL_EQUAL))
+		return 1;
 
 	return 0;
 }
@@ -372,7 +429,7 @@
 
 	if (known_access_ok_numbers(expr))
 		return;
-	if (known_access_ok_comparison(expr))
+	if (buf_comparison_index_ok(expr))
 		return;
 
 	array = get_array_base(expr);
@@ -416,27 +473,90 @@
 	add_function_assign_hook(func, call_back, INT_PTR(param));
 }
 
-static char *buf_size_param_comparison(struct expression *array, struct expression_list *args)
+static int is_sizeof(struct expression *expr)
 {
-	struct expression *arg;
+	const char *name;
+
+	if (expr->type == EXPR_SIZEOF)
+		return 1;
+	name = pos_ident(expr->pos);
+	if (name && strcmp(name, "sizeof") == 0)
+		return 1;
+	return 0;
+}
+
+static int match_size_binop(struct expression *size, struct expression *expr, int *limit_type)
+{
+	int orig_type = *limit_type;
+	struct expression *left;
+	sval_t sval;
+
+	left = expr->left;
+	if (!expr_equiv(size, left))
+		return 0;
+
+	if (expr->op == '-' &&
+	    get_value(expr->right, &sval) &&
+	    sval.value == 1 &&
+	    orig_type == ELEM_COUNT) {
+		*limit_type = ELEM_LAST;
+		return 1;
+	}
+
+	if (expr->op == '+' &&
+	    get_value(expr->right, &sval) &&
+	    sval.value == 1 &&
+	    orig_type == ELEM_LAST) {
+		*limit_type = ELEM_COUNT;
+		return 1;
+	}
+
+	if (expr->op == '*' &&
+	    is_sizeof(expr->right) &&
+	    orig_type == ELEM_COUNT) {
+		*limit_type = BYTE_COUNT;
+		return 1;
+	}
+
+	if (expr->op == '/' &&
+	    is_sizeof(expr->right) &&
+	    orig_type == BYTE_COUNT) {
+		*limit_type = ELEM_COUNT;
+		return 1;
+	}
+
+	return 0;
+}
+
+static char *buf_size_param_comparison(struct expression *array, struct expression_list *args, int *limit_type)
+{
+	struct expression *tmp, *arg;
 	struct expression *size;
 	static char buf[32];
 	int i;
 
-	size = get_size_variable(array);
+	size = get_size_variable(array, limit_type);
 	if (!size)
 		return NULL;
 
+	if (*limit_type == USED_LAST)
+		*limit_type = ELEM_LAST;
+	if (*limit_type == USED_COUNT)
+		*limit_type = ELEM_COUNT;
+
 	i = -1;
-	FOR_EACH_PTR(args, arg) {
+	FOR_EACH_PTR(args, tmp) {
 		i++;
+		arg = tmp;
 		if (arg == array)
 			continue;
-		if (!expr_equiv(arg, size))
-			continue;
-		snprintf(buf, sizeof(buf), "==$%d", i);
-		return buf;
-	} END_FOR_EACH_PTR(arg);
+		if (expr_equiv(arg, size) ||
+		    (arg->type == EXPR_BINOP &&
+		     match_size_binop(size, arg, limit_type))) {
+			snprintf(buf, sizeof(buf), "==$%d", i);
+			return buf;
+		}
+	} END_FOR_EACH_PTR(tmp);
 
 	return NULL;
 }
@@ -446,6 +566,8 @@
 	struct expression *arg;
 	char *compare;
 	int param;
+	char buf[5];
+	int limit_type;
 
 	param = -1;
 	FOR_EACH_PTR(call->args, arg) {
@@ -452,10 +574,11 @@
 		param++;
 		if (!is_pointer(arg))
 			continue;
-		compare = buf_size_param_comparison(arg, call->args);
+		compare = buf_size_param_comparison(arg, call->args, &limit_type);
 		if (!compare)
 			continue;
-		sql_insert_caller_info(call, ARRAY_LEN, param, "$", compare);
+		snprintf(buf, sizeof(buf), "%d", limit_type);
+		sql_insert_caller_info(call, limit_type, param, compare, buf);
 	} END_FOR_EACH_PTR(arg);
 }
 
@@ -495,40 +618,44 @@
 	char *size_name;
 	long param;
 	struct sm_state *tmp;
+	int limit_type;
 
-	if (strncmp(value, "==$", 3) != 0)
+	if (strncmp(key, "==$", 3) != 0)
 		return;
-	param = strtol(value + 3, NULL, 10);
+	param = strtol(key + 3, NULL, 10);
 	if (!get_param(param, &size_name, &size_sym))
 		return;
 	array_expr = symbol_expression(array_sym);
 	size_expr = symbol_expression(size_sym);
+	limit_type = strtol(value, NULL, 10);
 
-	tmp = set_state_expr(size_id, array_expr, alloc_expr_state(size_expr));
+	tmp = set_state_expr(size_id, array_expr, alloc_compare_size(limit_type, size_expr));
 	if (!tmp)
 		return;
-	set_state_expr(link_id, size_expr, alloc_expr_state(array_expr));
+	set_state_expr(link_id, size_expr, alloc_state_expr(array_expr));
 }
 
-static void set_arraysize_arg(const char *array_name, struct symbol *array_sym, char *key, char *value)
+static void set_implied(struct expression *call, struct expression *array_expr, char *key, char *value)
 {
-	struct expression *array_expr;
 	struct expression *size_expr;
 	struct symbol *size_sym;
 	char *size_name;
 	long param;
 	struct sm_state *tmp;
+	int limit_type;
 
-	param = strtol(key, NULL, 10);
+	if (strncmp(key, "==$", 3) != 0)
+		return;
+	param = strtol(key + 3, NULL, 10);
 	if (!get_param(param, &size_name, &size_sym))
 		return;
-	array_expr = symbol_expression(array_sym);
 	size_expr = symbol_expression(size_sym);
 
-	tmp = set_state_expr(size_id, array_expr, alloc_expr_state(size_expr));
+	limit_type = strtol(value, NULL, 10);
+	tmp = set_state_expr(size_id, array_expr, alloc_compare_size(limit_type, size_expr));
 	if (!tmp)
 		return;
-	set_state_expr(link_id, size_expr, alloc_expr_state(array_expr));
+	set_state_expr(link_id, size_expr, alloc_state_expr(array_expr));
 }
 
 static void munge_start_states(struct statement *stmt)
@@ -560,10 +687,120 @@
 	free_slist(&slist);
 }
 
+static void set_used(struct expression *expr)
+{
+	struct expression *parent;
+	struct expression *array;
+	struct expression *offset;
+	struct sm_state *tmp;
+	int limit_type;
+
+	if (expr->op != SPECIAL_INCREMENT)
+		return;
+
+	limit_type = USED_LAST;
+	if (expr->type == EXPR_POSTOP)
+		limit_type = USED_COUNT;
+
+	parent = expr_get_parent_expr(expr);
+	if (!parent || parent->type != EXPR_BINOP)
+		return;
+	parent = expr_get_parent_expr(parent);
+	if (!parent || !is_array(parent))
+		return;
+
+	array = get_array_base(parent);
+	offset = get_array_offset(parent);
+	if (offset != expr)
+		return;
+
+	tmp = set_state_expr(size_id, array, alloc_compare_size(limit_type, offset->unop));
+	if (!tmp)
+		return;
+	set_state_expr(link_id, offset->unop, alloc_state_expr(array));
+}
+
+static int match_assign_array(struct expression *expr)
+{
+	// FIXME: implement
+	return 0;
+}
+
+static int match_assign_size(struct expression *expr)
+{
+	struct expression *right, *size, *array;
+	struct smatch_state *state;
+	struct sm_state *tmp;
+	int limit_type;
+
+	right = expr->right;
+	size = right;
+	if (size->type == EXPR_BINOP)
+		size = size->left;
+
+	array = get_array_variable(size);
+	if (!array)
+		return 0;
+	state = get_state_expr(size_id, array);
+	if (!state || !state->data)
+		return 0;
+
+	limit_type = state_to_limit(state);
+	if (limit_type < 0)
+		return 0;
+
+	if (right->type == EXPR_BINOP && !match_size_binop(size, right, &limit_type))
+		return 0;
+
+	tmp = set_state_expr(size_id, array, alloc_compare_size(limit_type, expr->left));
+	if (!tmp)
+		return 0;
+	set_state_expr(link_id, expr->left, alloc_state_expr(array));
+	return 1;
+}
+
+static void match_assign(struct expression *expr)
+{
+	if (expr->op != '=')
+		return;
+
+	if (match_assign_array(expr))
+		return;
+	match_assign_size(expr);
+}
+
+static void match_copy(const char *fn, struct expression *expr, void *unused)
+{
+	struct expression *src, *size;
+	int src_param, size_param;
+
+	src = get_argument_from_call_expr(expr->args, 1);
+	size = get_argument_from_call_expr(expr->args, 2);
+	src = strip_expr(src);
+	size = strip_expr(size);
+	if (!src || !size)
+		return;
+	if (src->type != EXPR_SYMBOL || size->type != EXPR_SYMBOL)
+		return;
+
+	src_param = get_param_num_from_sym(src->symbol);
+	size_param = get_param_num_from_sym(size->symbol);
+	if (src_param < 0 || size_param < 0)
+		return;
+
+	sql_insert_cache(call_implies, "'%s', '%s', 0, %d, %d, %d, '==$%d', '%d'",
+			 get_base_file(), get_function(), fn_static(),
+			 BYTE_COUNT, src_param, size_param, BYTE_COUNT);
+}
+
 void register_buf_comparison(int id)
 {
+	int i;
+
 	size_id = id;
 
+	set_dynamic_states(size_id);
+
 	add_unmatched_state_hook(size_id, &unmatched_state);
 
 	add_allocation_function("malloc", &match_alloc, 0);
@@ -586,20 +823,31 @@
 		add_allocation_function("devm_kcalloc", &match_calloc, 1);
 		add_allocation_function("kmalloc_array", &match_calloc, 0);
 		add_allocation_function("krealloc", &match_alloc, 1);
+
+		add_function_hook("copy_from_user", &match_copy, NULL);
+		add_function_hook("__copy_from_user", &match_copy, NULL);
 	}
 
 	add_hook(&array_check, OP_HOOK);
 	add_hook(&array_check_data_info, OP_HOOK);
+	add_hook(&set_used, OP_HOOK);
 
 	add_hook(&match_call, FUNCTION_CALL_HOOK);
-	select_caller_info_hook(set_param_compare, ARRAY_LEN);
-	select_caller_info_hook(set_arraysize_arg, ARRAYSIZE_ARG);
 	add_hook(&munge_start_states, AFTER_DEF_HOOK);
+
+	add_hook(&match_assign, ASSIGNMENT_HOOK);
+
+	for (i = BYTE_COUNT; i <= USED_COUNT; i++) {
+		select_call_implies_hook(i, &set_implied);
+		select_caller_info_hook(set_param_compare, i);
+		select_return_implies_hook(i, &set_implied);
+	}
 }
 
 void register_buf_comparison_links(int id)
 {
 	link_id = id;
+	set_dynamic_states(link_id);
 	add_merge_hook(link_id, &merge_links);
 	add_modification_hook(link_id, &match_link_modify);
 }
--- old/usr/src/tools/smatch/src/smatch_buf_size.c	Mon Aug  5 08:38:20 2019
+++ new/usr/src/tools/smatch/src/smatch_buf_size.c	Mon Aug  5 08:38:20 2019
@@ -330,8 +330,6 @@
 	struct symbol *type;
 	int ret;
 
-	if (!option_spammy)
-		return 0;
 	if (expr->type != EXPR_PREOP || expr->op != '&')
 		return 0;
 	type = get_type(expr);
@@ -506,6 +504,10 @@
 		return alloc_int_rl(size - offset.value);
 	}
 
+	size = get_stored_size_end_struct_bytes(expr);
+	if (size)
+		return alloc_int_rl(size);
+
 	/* buf[4] */
 	size = get_real_array_size(expr);
 	if (size)
@@ -516,10 +518,6 @@
 	if (ret)
 		return ret;
 
-	size = get_stored_size_end_struct_bytes(expr);
-	if (size)
-		return alloc_int_rl(size);
-
 	/* char *foo = "BAR" */
 	size = get_size_from_initializer(expr);
 	if (size)
@@ -711,17 +709,15 @@
 static void match_calloc(const char *fn, struct expression *expr, void *unused)
 {
 	struct expression *right;
-	struct expression *arg;
-	sval_t elements;
-	sval_t size;
+	struct expression *size, *nr, *mult;
+	struct range_list *rl;
 
 	right = strip_expr(expr->right);
-	arg = get_argument_from_call_expr(right->args, 0);
-	if (!get_implied_value(arg, &elements))
-		return; // FIXME!!!
-	arg = get_argument_from_call_expr(right->args, 1);
-	if (get_implied_value(arg, &size))
-		store_alloc(expr->left, size_to_rl(elements.value * size.value));
+	nr = get_argument_from_call_expr(right->args, 0);
+	size = get_argument_from_call_expr(right->args, 1);
+	mult = binop_expression(nr, '*', size);
+	if (get_implied_rl(mult, &rl))
+		store_alloc(expr->left, rl);
 	else
 		store_alloc(expr->left, size_to_rl(-1));
 }
@@ -873,6 +869,8 @@
 {
 	my_size_id = id;
 
+	set_dynamic_states(my_size_id);
+
 	add_unmatched_state_hook(my_size_id, &unmatched_size_state);
 
 	select_caller_info_hook(set_param_buf_size, BUF_SIZE);
@@ -908,6 +906,9 @@
 		add_allocation_function("alloc_bootmem", &match_alloc, 0);
 		add_allocation_function("kmap", &match_page, 0);
 		add_allocation_function("get_zeroed_page", &match_page, 0);
+		add_allocation_function("alloc_page", &match_page, 0);
+		add_allocation_function("page_address", &match_page, 0);
+		add_allocation_function("lowmem_page_address", &match_page, 0);
 		add_allocation_function("alloc_pages", &match_alloc_pages, 1);
 		add_allocation_function("alloc_pages_current", &match_alloc_pages, 1);
 		add_allocation_function("__get_free_pages", &match_alloc_pages, 1);
--- old/usr/src/tools/smatch/src/smatch_capped.c	Mon Aug  5 08:38:21 2019
+++ new/usr/src/tools/smatch/src/smatch_capped.c	Mon Aug  5 08:38:21 2019
@@ -42,7 +42,7 @@
 {
 	struct smatch_state *state;
 
-	state = get_state(SMATCH_EXTRA, sm->name, sm->sym);
+	state = __get_state(SMATCH_EXTRA, sm->name, sm->sym);
 	if (state && !estate_is_whole(state))
 		return &capped;
 	return &uncapped;
@@ -68,6 +68,7 @@
 
 int is_capped(struct expression *expr)
 {
+	struct symbol *type;
 	sval_t dummy;
 
 	expr = strip_expr(expr);
@@ -77,6 +78,14 @@
 	if (!expr)
 		return 0;
 
+	type = get_type(expr);
+	if (is_ptr_type(type))
+		return 0;
+	if (type == &bool_ctype)
+		return 0;
+	if (type_bits(type) >= 0 && type_bits(type) <= 2)
+		return 0;
+
 	if (get_hard_max(expr, &dummy))
 		return 1;
 
@@ -90,8 +99,8 @@
 			return 1;
 		if (expr->op == SPECIAL_RIGHTSHIFT)
 			return 1;
-		if (expr->op == '%')
-			return is_capped(expr->right);
+		if (expr->op == '%' && is_capped(expr->right))
+			return 1;
 		if (!is_capped(expr->left))
 			return 0;
 		if (expr->op == '/')
@@ -131,15 +140,28 @@
 
 static void match_condition(struct expression *expr)
 {
+	struct expression *left, *right;
 	struct smatch_state *left_true = NULL;
 	struct smatch_state *left_false = NULL;
 	struct smatch_state *right_true = NULL;
 	struct smatch_state *right_false = NULL;
+	sval_t sval;
 
 
 	if (expr->type != EXPR_COMPARE)
 		return;
 
+	left = strip_expr(expr->left);
+	right = strip_expr(expr->right);
+
+	while (left->type == EXPR_ASSIGNMENT)
+		left = strip_expr(left->left);
+
+	/* If we're dealing with known expressions, that's for smatch_extra.c */
+	if (get_implied_value(left, &sval) ||
+	    get_implied_value(right, &sval))
+		return;
+
 	switch (expr->op) {
 	case '<':
 	case SPECIAL_LTE:
@@ -168,12 +190,22 @@
 		return;
 	}
 
-	set_true_false_states_expr(my_id, expr->left, left_true, left_false);
-	set_true_false_states_expr(my_id, expr->right, right_true, right_false);
+	set_true_false_states_expr(my_id, left, left_true, left_false);
+	set_true_false_states_expr(my_id, right, right_true, right_false);
 }
 
 static void match_assign(struct expression *expr)
 {
+	struct symbol *type;
+
+	type = get_type(expr);
+	if (is_ptr_type(type))
+		return;
+	if (type == &bool_ctype)
+		return;
+	if (type_bits(type) >= 0 && type_bits(type) <= 2)
+		return;
+
 	if (is_capped(expr->right)) {
 		set_state_expr(my_id, expr->left, &capped);
 	} else {
@@ -206,7 +238,7 @@
 
 	if (sm->state != &capped)
 		return;
-	estate = get_state(SMATCH_EXTRA, sm->name, sm->sym);
+	estate = __get_state(SMATCH_EXTRA, sm->name, sm->sym);
 	if (estate_get_single_value(estate, &sval))
 		return;
 	sql_insert_caller_info(call, CAPPED_DATA, param, printed_name, "1");
@@ -235,12 +267,12 @@
 		if (param < 0)
 			continue;
 
-		estate = get_state(SMATCH_EXTRA, sm->name, sm->sym);
+		estate = __get_state(SMATCH_EXTRA, sm->name, sm->sym);
 		if (estate_get_single_value(estate, &sval))
 			continue;
 
 		orig = get_state_stree(get_start_states(), my_id, sm->name, sm->sym);
-		if (orig == &capped)
+		if (orig == &capped && !param_was_set_var_sym(sm->name, sm->sym))
 			continue;
 
 		param_name = get_param_name(sm);
@@ -254,9 +286,15 @@
 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
 		if (!ret_sym)
 			break;
+		if (sm->state != &capped)
+			continue;
 		if (ret_sym != sm->sym)
 			continue;
 
+		estate = __get_state(SMATCH_EXTRA, sm->name, sm->sym);
+		if (estate_get_single_value(estate, &sval))
+			continue;
+
 		param_name = state_name_to_param_name(sm->name, return_str);
 		if (!param_name)
 			continue;
--- old/usr/src/tools/smatch/src/smatch_comparison.c	Mon Aug  5 08:38:21 2019
+++ new/usr/src/tools/smatch/src/smatch_comparison.c	Mon Aug  5 08:38:21 2019
@@ -641,7 +641,7 @@
 	set_state(link_id, var, sym, new_state);
 }
 
-static void match_inc(struct sm_state *sm)
+static void match_inc(struct sm_state *sm, bool preserve)
 {
 	struct string_list *links;
 	struct smatch_state *state, *new;
@@ -673,6 +673,8 @@
 		case SPECIAL_UNSIGNED_GTE:
 		case '>':
 		case SPECIAL_UNSIGNED_GT:
+			if (preserve)
+				break;
 			new = alloc_compare_state(
 					data->left, data->left_var, data->left_vsl,
 					flip ? '<' : '>',
@@ -693,7 +695,7 @@
 	} END_FOR_EACH_PTR(tmp);
 }
 
-static void match_dec(struct sm_state *sm)
+static void match_dec(struct sm_state *sm, bool preserve)
 {
 	struct string_list *links;
 	struct smatch_state *state;
@@ -713,6 +715,9 @@
 			struct compare_data *data = state->data;
 			struct smatch_state *new;
 
+			if (preserve)
+				break;
+
 			new = alloc_compare_state(
 					data->left, data->left_var, data->left_vsl,
 					'<',
@@ -726,6 +731,42 @@
 	} END_FOR_EACH_PTR(tmp);
 }
 
+static void reset_sm(struct sm_state *sm)
+{
+	struct string_list *links;
+	char *tmp;
+
+	links = sm->state->data;
+
+	FOR_EACH_PTR(links, tmp) {
+		set_state(compare_id, tmp, NULL, &undefined);
+	} END_FOR_EACH_PTR(tmp);
+	set_state(link_id, sm->name, sm->sym, &undefined);
+}
+
+static bool match_add_sub_assign(struct sm_state *sm, struct expression *expr)
+{
+	struct range_list *rl;
+	sval_t zero = { .type = &int_ctype };
+
+	if (!expr || expr->type != EXPR_ASSIGNMENT)
+		return false;
+	if (expr->op != SPECIAL_ADD_ASSIGN && expr->op != SPECIAL_SUB_ASSIGN)
+		return false;
+
+	get_absolute_rl(expr->right, &rl);
+	if (sval_is_negative(rl_min(rl))) {
+		reset_sm(sm);
+		return false;
+	}
+
+	if (expr->op == SPECIAL_ADD_ASSIGN)
+		match_inc(sm, rl_has_sval(rl, zero));
+	else
+		match_dec(sm, rl_has_sval(rl, zero));
+	return true;
+}
+
 static void match_inc_dec(struct sm_state *sm, struct expression *mod_expr)
 {
 	/*
@@ -733,13 +774,15 @@
 	 */
 	if (!mod_expr)
 		return;
+	if (match_add_sub_assign(sm, mod_expr))
+		return;
 	if (mod_expr->type != EXPR_PREOP && mod_expr->type != EXPR_POSTOP)
 		return;
 
 	if (mod_expr->op == SPECIAL_INCREMENT)
-		match_inc(sm);
+		match_inc(sm, false);
 	else if (mod_expr->op == SPECIAL_DECREMENT)
-		match_dec(sm);
+		match_dec(sm, false);
 }
 
 static int is_self_assign(struct expression *expr)
@@ -751,9 +794,6 @@
 
 static void match_modify(struct sm_state *sm, struct expression *mod_expr)
 {
-	struct string_list *links;
-	char *tmp;
-
 	if (mod_expr && is_self_assign(mod_expr))
 		return;
 
@@ -762,13 +802,11 @@
 	    ((mod_expr->type == EXPR_PREOP || mod_expr->type == EXPR_POSTOP) &&
 	     (mod_expr->op == SPECIAL_INCREMENT || mod_expr->op == SPECIAL_DECREMENT)))
 		return;
+	if (mod_expr && mod_expr->type == EXPR_ASSIGNMENT &&
+	    (mod_expr->op == SPECIAL_ADD_ASSIGN || mod_expr->op == SPECIAL_SUB_ASSIGN))
+		return;
 
-	links = sm->state->data;
-
-	FOR_EACH_PTR(links, tmp) {
-		set_state(compare_id, tmp, NULL, &undefined);
-	} END_FOR_EACH_PTR(tmp);
-	set_state(link_id, sm->name, sm->sym, &undefined);
+	reset_sm(sm);
 }
 
 static void match_preop(struct expression *expr)
@@ -1604,7 +1642,7 @@
 	return ret;
 }
 
-int get_comparison(struct expression *a, struct expression *b)
+static int get_comparison_helper(struct expression *a, struct expression *b, bool use_extra)
 {
 	char *one = NULL;
 	char *two = NULL;
@@ -1653,11 +1691,21 @@
 	free_string(one);
 	free_string(two);
 
-	if (!ret)
+	if (!ret && use_extra)
 		return comparison_from_extra(a, b);
 	return ret;
 }
 
+int get_comparison(struct expression *a, struct expression *b)
+{
+	return get_comparison_helper(a, b, true);
+}
+
+int get_comparison_no_extra(struct expression *a, struct expression *b)
+{
+	return get_comparison_helper(a, b, false);
+}
+
 int possible_comparison(struct expression *a, int comparison, struct expression *b)
 {
 	char *one = NULL;
@@ -2327,7 +2375,7 @@
 	if (!parse_comparison(&value, op))
 		return 0;
 
-	snprintf(buf, sizeof(buf), value);
+	snprintf(buf, sizeof(buf), "%s", value);
 
 	p = buf;
 	if (*p++ != '$')
@@ -2495,6 +2543,7 @@
 void register_comparison(int id)
 {
 	compare_id = id;
+	set_dynamic_states(compare_id);
 	add_hook(&save_start_states, AFTER_DEF_HOOK);
 	add_unmatched_state_hook(compare_id, unmatched_comparison);
 	add_pre_merge_hook(compare_id, &pre_merge_hook);
@@ -2515,6 +2564,8 @@
 void register_comparison_links(int id)
 {
 	link_id = id;
+	db_ignore_states(link_id);
+	set_dynamic_states(link_id);
 	add_merge_hook(link_id, &merge_links);
 	add_modification_hook(link_id, &match_modify);
 	add_modification_hook_late(link_id, match_inc_dec);
@@ -2531,6 +2582,7 @@
 void register_comparison_inc_dec_links(int id)
 {
 	inc_dec_link_id = id;
+	set_dynamic_states(inc_dec_link_id);
 	set_up_link_functions(inc_dec_id, inc_dec_link_id);
 }
 
--- old/usr/src/tools/smatch/src/smatch_conditions.c	Mon Aug  5 08:38:21 2019
+++ new/usr/src/tools/smatch/src/smatch_conditions.c	Mon Aug  5 08:38:21 2019
@@ -420,6 +420,8 @@
 	 * too complicated to deal with.
 	 */
 	if (expr->type == EXPR_BINOP && expr->op == '|') {
+		expr_set_parent_expr(expr->left, expr);
+		expr_set_parent_expr(expr->right, expr);
 		handle_logical(expr);
 		return;
 	}
--- old/usr/src/tools/smatch/src/smatch_constraints.c	Mon Aug  5 08:38:22 2019
+++ new/usr/src/tools/smatch/src/smatch_constraints.c	Mon Aug  5 08:38:22 2019
@@ -196,6 +196,7 @@
 {
 	char *name;
 
+	expr = strip_expr(expr);
 	if (!expr)
 		return NULL;
 	if (expr->type == EXPR_CALL)
@@ -341,9 +342,8 @@
 struct string_list *saved_constraints;
 static void save_new_constraint(const char *con)
 {
-	if (list_has_string(saved_constraints, con))
+	if (!insert_string(&saved_constraints, con))
 		return;
-	insert_string(&saved_constraints, con);
 	sql_save_constraint(con);
 }
 
@@ -360,17 +360,10 @@
 	if (get_value(left, &sval) || get_value(right, &sval))
 		return;
 
-	if (local_debug)
-		sm_msg("COMPARE: %s %s %s", expr_to_str(left), show_special(op), expr_to_str(right));
-
 	constraint = get_constraint_str(right);
 	if (!constraint)
 		return;
-	if (local_debug)
-		sm_msg("EXPR: %s CONSTRAINT %s", expr_to_str(right), constraint);
 	constraint_id = constraint_str_to_id(constraint);
-	if (local_debug)
-		sm_msg("CONSTRAINT ID %d", constraint_id);
 	if (constraint_id < 0)
 		save_new_constraint(constraint);
 	free_string(constraint);
@@ -383,16 +376,10 @@
 	add_constraint(&constraints, remove_unsigned_from_comparison(op), constraint_id);
 	state = alloc_constraint_state(constraints);
 
-	if (op == orig_op) {
-		if (local_debug)
-			sm_msg("SETTING %s true %s", expr_to_str(left), state->name);
+	if (op == orig_op)
 		set_true_false_states_expr(my_id, left,	state, NULL);
-	} else {
-		if (local_debug)
-			sm_msg("SETTING %s false %s", expr_to_str(left), state->name);
-
+	else
 		set_true_false_states_expr(my_id, left, NULL, state);
-	}
 }
 
 static void match_condition(struct expression *expr)
@@ -529,6 +516,7 @@
 {
 	my_id = id;
 
+	set_dynamic_states(my_id);
 	add_merge_hook(my_id, &merge_func);
 	add_hook(&match_condition, CONDITION_HOOK);
 
--- old/usr/src/tools/smatch/src/smatch_constraints_required.c	Mon Aug  5 08:38:22 2019
+++ new/usr/src/tools/smatch/src/smatch_constraints_required.c	Mon Aug  5 08:38:22 2019
@@ -281,14 +281,17 @@
 static void match_assign_has_buf_comparison(struct expression *expr)
 {
 	struct expression *size;
+	int limit_type;
 
 	if (expr->op != '=')
 		return;
 	if (expr->right->type == EXPR_CALL)
 		return;
-	size = get_size_variable(expr->right);
+	size = get_size_variable(expr->right, &limit_type);
 	if (!size)
 		return;
+	if (limit_type != ELEM_COUNT)
+		return;
 	match_alloc_helper(expr->left, size, 1);
 }
 
@@ -455,6 +458,7 @@
 {
 	my_id = id;
 
+	set_dynamic_states(my_id);
 	add_hook(&match_assign_size, ASSIGNMENT_HOOK);
 	add_hook(&match_assign_data, ASSIGNMENT_HOOK);
 	add_hook(&match_assign_has_buf_comparison, ASSIGNMENT_HOOK);
--- old/usr/src/tools/smatch/src/smatch_container_of.c	Mon Aug  5 08:38:22 2019
+++ new/usr/src/tools/smatch/src/smatch_container_of.c	Mon Aug  5 08:38:22 2019
@@ -112,7 +112,7 @@
 	return offset;
 }
 
-static char *get_container_name(struct sm_state *sm, int offset)
+static char *get_container_name_sm(struct sm_state *sm, int offset)
 {
 	static char buf[256];
 	const char *name;
@@ -173,7 +173,7 @@
 		offset = get_container_offset(tmp->sym);
 		if (arg < 0 || offset < 0)
 			continue;
-		name = get_container_name(tmp, offset);
+		name = get_container_name_sm(tmp, offset);
 		if (!name)
 			continue;
 		sql_insert_return_implies(CONTAINER, arg, name, "");
@@ -249,73 +249,150 @@
 	sql_insert_return_implies(CONTAINER, param, buf, "");
 }
 
-static int get_shared_cnt(const char *one, const char *two)
+static int get_deref_count(struct expression *expr)
 {
-	int i;
-	int on_end = false;
+	int cnt = 0;
 
-	i = 0;
-	while (true) {
-		if (!one[i] || !two[i]) {
-			on_end = true;
-			break;
-		}
-		if (one[i] != two[i])
-			break;
-		i++;
+	while (expr && expr->type == EXPR_DEREF) {
+		expr = expr->deref;
+		if (expr->type == EXPR_PREOP && expr->op == '*')
+			expr = expr->unop;
+		cnt++;
+		if (cnt > 100)
+			return -1;
 	}
-	if (i == 0)
-		return 0;
-	i--;
-	while (i > 0 && (one[i] == '>' || one[i] == '-' || one[i] == '.')) {
-		on_end = true;
-		i--;
+	return cnt;
+}
+
+static struct expression *get_partial_deref(struct expression *expr, int cnt)
+{
+	while (--cnt >= 0) {
+		if (!expr || expr->type != EXPR_DEREF)
+			return expr;
+		expr = expr->deref;
+		if (expr->type == EXPR_PREOP && expr->op == '*')
+			expr = expr->unop;
 	}
-	if (!on_end)
-		return 0;
+	return expr;
+}
 
-	return i + 1;
+static int partial_deref_to_offset_str(struct expression *expr, int cnt, char op, char *buf, int size)
+{
+	int n, offset;
+
+	if (cnt == 0)
+		return snprintf(buf, size, "%c0", op);
+
+	n = 0;
+	while (--cnt >= 0) {
+		offset = get_member_offset_from_deref(expr);
+		if (offset < 0)
+			return -1;
+		n += snprintf(buf + n, size - n, "%c%d", op, offset);
+		if (expr->type != EXPR_DEREF)
+			return -1;
+		expr = expr->deref;
+		if (expr->type == EXPR_PREOP && expr->op == '*')
+			expr = expr->unop;
+	}
+
+	return n;
 }
 
-static int build_offset_str(struct expression *expr, const char *name,
-			    int shared, char *buf, int size, int op)
+static char *get_shared_str(struct expression *container, struct expression *expr)
 {
-	int chop = 0;
-	int offset;
-	int i;
+	struct expression *one, *two;
+	int cont, exp, min, ret, n;
+	static char buf[48];
 
-	i = shared;
-	while (name[i]) {
-		if (name[i] == '.' || name[i] == '-')
-			chop++;
-		i++;
+	cont = get_deref_count(container);
+	exp = get_deref_count(expr);
+	if (cont < 0 || exp < 0)
+		return NULL;
+
+	min = (cont < exp) ? cont : exp;
+	while (min >= 0) {
+		one = get_partial_deref(container, cont - min);
+		two = get_partial_deref(expr, exp - min);
+		if (expr_equiv(one, two))
+			goto found;
+		min--;
 	}
 
-	// FIXME:  Handle more chops
-	if (chop > 1)
-		return 0;
+	return NULL;
 
-	if (chop == 0) {
-		offset = 0;
-	} else {
-		offset = get_member_offset_from_deref(expr);
-		if (offset < 0)
-			return 0;
+found:
+	ret = partial_deref_to_offset_str(container, cont - min, '-', buf, sizeof(buf));
+	if (ret < 0)
+		return NULL;
+	n = ret;
+	ret = partial_deref_to_offset_str(expr, exp - min, '+', buf + ret, sizeof(buf) - ret);
+	if (ret < 0)
+		return NULL;
+	n += ret;
+	if (n >= sizeof(buf))
+		return NULL;
+
+	return buf;
+}
+
+char *get_container_name(struct expression *container, struct expression *expr)
+{
+	struct symbol *container_sym, *sym;
+	struct expression *tmp;
+	static char buf[64];
+	char *shared;
+	bool star;
+	int cnt;
+
+	container_sym = expr_to_sym(container);
+	sym = expr_to_sym(expr);
+	if (container_sym && container_sym == sym)
+		goto found;
+
+	cnt = 0;
+	while ((tmp = get_assigned_expr(expr))) {
+		expr = tmp;
+		if (cnt++ > 3)
+			break;
 	}
 
-	snprintf(buf, size, "%c%d", (op == '+') ? '+' : '-', offset);
-	return 1;
+	cnt = 0;
+	while ((tmp = get_assigned_expr(container))) {
+		container = tmp;
+		if (cnt++ > 3)
+			break;
+	}
+
+found:
+	expr = strip_expr(expr);
+	star = true;
+	if (expr->type == EXPR_PREOP && expr->op == '&') {
+		expr = strip_expr(expr->unop);
+		star = false;
+	}
+
+	container_sym = expr_to_sym(container);
+	if (!container_sym)
+		return NULL;
+	sym = expr_to_sym(expr);
+	if (!sym || container_sym != sym)
+		return NULL;
+
+	shared = get_shared_str(container, expr);
+	if (star)
+		snprintf(buf, sizeof(buf), "*(%s)", shared);
+	else
+		snprintf(buf, sizeof(buf), "%s", shared);
+
+	return buf;
 }
 
 static void match_call(struct expression *call)
 {
 	struct expression *fn, *arg;
-	char *fn_name, *arg_name;
-	int param, shared;
-	char minus_str[64];
-	char plus_str[64];
-	char offset_str[64];
-	bool star;
+	char *name;
+	int param;
 
 	/*
 	 * We're trying to link the function with the parameter.  There are a
@@ -332,72 +409,22 @@
 	 * otherwise it is.  Starred means dereferenced.
 	 */
 	fn = strip_expr(call->fn);
-	fn_name = expr_to_var(fn);
-	if (!fn_name)
-		return;
 
 	param = -1;
 	FOR_EACH_PTR(call->args, arg) {
 		param++;
 
-		arg = strip_expr(arg);
-		star = true;
-		if (arg->type == EXPR_PREOP && arg->op == '&') {
-			arg = strip_expr(arg->unop);
-			star = false;
-		}
-
-		arg_name = expr_to_var(arg);
-		if (!arg_name)
+		name = get_container_name(fn, arg);
+		if (!name)
 			continue;
-		shared = get_shared_cnt(fn_name, arg_name);
-		if (!shared)
-			goto free_arg_name;
-		if (!build_offset_str(fn, fn_name, shared, minus_str, sizeof(minus_str), '-'))
-			goto free_arg_name;
-		if (!build_offset_str(arg, arg_name, shared, plus_str, sizeof(plus_str), '+'))
-			goto free_arg_name;
-		if (star)
-			snprintf(offset_str, sizeof(offset_str), "*(%s%s)", minus_str, plus_str);
-		else
-			snprintf(offset_str, sizeof(offset_str), "%s%s", minus_str, plus_str);
-		sql_insert_caller_info(call, CONTAINER, param, offset_str, "$(-1)");
-free_arg_name:
-		free_string(arg_name);
-	} END_FOR_EACH_PTR(arg);
 
-	free_string(fn_name);
+		sql_insert_caller_info(call, CONTAINER, param, name, "$(-1)");
+	} END_FOR_EACH_PTR(arg);
 }
 
 static void db_passed_container(const char *name, struct symbol *sym, char *key, char *value)
 {
-	sval_t offset = {
-		.type = &int_ctype,
-	};
-	const char *arg_offset;
-	int star = 0;
-	int val;
-
-	if (key[0] == '*') {
-		star = 1;
-		key += 2;
-	}
-
-	val = atoi(key);
-	if (val < -4095 || val > 0)
-		return;
-	offset.value = -val;
-	arg_offset = strchr(key, '+');
-	if (!arg_offset)
-		return;
-	val = atoi(arg_offset + 1);
-	if (val > 4095 || val < 0)
-		return;
-	offset.value |= val << 16;
-	if (star)
-		offset.value |= 1ULL << 31;
-
-	set_state(param_id, name, sym, alloc_estate_sval(offset));
+	set_state(param_id, name, sym, alloc_state_str(key));
 }
 
 struct db_info {
@@ -574,35 +601,49 @@
 	return db_info.stree;
 }
 
-static void handle_passed_container(struct symbol *sym)
+static void load_container_data(struct symbol *arg, const char *info)
 {
-	struct symbol *arg;
-	struct smatch_state *state;
+	mtag_t cur_tag, container_tag, arg_tag;
+	int container_offset, arg_offset;
+	char *p = (char *)info;
 	struct sm_state *sm;
 	struct stree *stree;
-	mtag_t fn_tag, container_tag, arg_tag;
-	sval_t offset;
-	int container_offset, arg_offset;
-	int star;
+	bool star = 0;
 
-	FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
-		state = get_state(param_id, arg->ident->name, arg);
-		if (state)
-			goto found;
-	} END_FOR_EACH_PTR(arg);
+	if (p[0] == '*') {
+		star = 1;
+		p += 2;
+	}
 
-	return;
-found:
-	if (!estate_get_single_value(state, &offset))
+	if (!get_toplevel_mtag(cur_func_sym, &cur_tag))
 		return;
-	container_offset = -(offset.value & 0xffff);
-	arg_offset = (offset.value & 0xfff0000) >> 16;
-	star = !!(offset.value & (1ULL << 31));
 
-	if (!get_toplevel_mtag(cur_func_sym, &fn_tag))
+	while (true) {
+		container_offset = strtoul(p, &p, 0);
+		if (local_debug)
+			sm_msg("%s: cur_tag = %llu container_offset = %d",
+			       __func__, cur_tag, container_offset);
+		if (!mtag_map_select_container(cur_tag, container_offset, &container_tag))
+			return;
+		cur_tag = container_tag;
+		if (local_debug)
+			sm_msg("%s: container_tag = %llu p = '%s'",
+			       __func__, container_tag, p);
+		if (!p)
+			return;
+		if (p[0] != '-')
+			break;
+		p++;
+	}
+
+	if (p[0] != '+')
 		return;
-	if (!mtag_map_select_container(fn_tag, container_offset, &container_tag))
+
+	p++;
+	arg_offset = strtoul(p, &p, 0);
+	if (p && *p && *p != ')')
 		return;
+
 	if (!arg_offset || star) {
 		arg_tag = container_tag;
 	} else {
@@ -617,6 +658,19 @@
 	free_stree(&stree);
 }
 
+static void handle_passed_container(struct symbol *sym)
+{
+	struct symbol *arg;
+	struct smatch_state *state;
+
+	FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
+		state = get_state(param_id, arg->ident->name, arg);
+		if (!state || state == &merged)
+			continue;
+		load_container_data(arg, state->name);
+	} END_FOR_EACH_PTR(arg);
+}
+
 void register_container_of(int id)
 {
 	my_id = id;
@@ -637,18 +691,13 @@
 	add_hook(&match_call, FUNCTION_CALL_HOOK);
 }
 
-static struct smatch_state *unmatched_state(struct sm_state *sm)
-{
-	return alloc_estate_whole(estate_type(sm->state));
-}
-
 void register_container_of2(int id)
 {
 	param_id = id;
 
+	set_dynamic_states(param_id);
 	select_caller_info_hook(db_passed_container, CONTAINER);
+	add_merge_hook(param_id, &merge_str_state);
 	add_hook(&handle_passed_container, AFTER_DEF_HOOK);
-	add_unmatched_state_hook(param_id, &unmatched_state);
-	add_merge_hook(param_id, &merge_estates);
 }
 
--- /dev/null	Mon Aug  5 08:38:23 2019
+++ new/usr/src/tools/smatch/src/smatch_data/db/apply_return_fixes.sh	Mon Aug  5 08:38:23 2019
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+if echo $1 | grep -q '^-p' ; then
+    PROJ=$(echo $1 | cut -d = -f 2)
+    shift
+fi
+
+bin_dir=$(dirname $0)
+db_file=$1
+if [ "$db_file" == "" ] ; then
+    echo "usage: $0 -p=<project> <db_file>"
+    exit
+fi
+
+test -e  ${bin_dir}/${PROJ}.return_fixes && \
+cat ${bin_dir}/${PROJ}.return_fixes | \
+while read func old new ; do
+    echo "update return_states set return = '$new' where function = '$func' and return = '$old';" | sqlite3 $db_file
+done
+
--- old/usr/src/tools/smatch/src/smatch_data/db/create_db.sh	Mon Aug  5 08:38:23 2019
+++ new/usr/src/tools/smatch/src/smatch_data/db/create_db.sh	Mon Aug  5 08:38:23 2019
@@ -44,15 +44,12 @@
 fi
 
 ${bin_dir}/remove_mixed_up_pointer_params.pl $db_file
+${bin_dir}/delete_too_common_fn_ptr.sh $db_file
 ${bin_dir}/mark_function_ptrs_searchable.pl $db_file
 
 # delete duplicate entrees and speed things up
 echo "delete from function_ptr where rowid not in (select min(rowid) from function_ptr group by file, function, ptr, searchable);" | sqlite3 $db_file
 
-test -e  ${bin_dir}/${PROJ}.return_fixes && \
-cat ${bin_dir}/${PROJ}.return_fixes | \
-while read func old new ; do
-    echo "update return_states set return = '$new' where function = '$func' and return = '$old';" | sqlite3 $db_file
-done
+${bin_dir}/apply_return_fixes.sh -p=${PROJ} $db_file
 
 mv $db_file smatch_db.sqlite
--- /dev/null	Mon Aug  5 08:38:23 2019
+++ new/usr/src/tools/smatch/src/smatch_data/db/delete_too_common_fn_ptr.sh	Mon Aug  5 08:38:23 2019
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+db_file=$1
+
+IFS="|"
+echo "select count(function), function from function_ptr group by function;" | \
+    sqlite3 $db_file  | sort -n | tail -n 100 | \
+
+while read cnt func ; do
+    if [ $cnt -lt 200 ] ; then
+        continue
+    fi
+    echo "delete from function_ptr where function = '$func';" | sqlite3 $db_file
+done
--- old/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh	Mon Aug  5 08:38:23 2019
+++ new/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh	Mon Aug  5 08:38:23 2019
@@ -23,18 +23,8 @@
 delete from caller_info where function = '(struct net_device_ops)->ndo_change_mtu' and caller = 'i40e_dbg_netdev_ops_write';
 delete from caller_info where function = '(struct timer_list)->function' and type != 0;
 
-/* type 1003 is USER_DATA */
-delete from caller_info where caller = 'hid_input_report' and type = 1003;
-delete from caller_info where caller = 'nes_process_iwarp_aeqe' and type = 1003;
-delete from caller_info where caller = 'oz_process_ep0_urb' and type = 1003;
-delete from caller_info where function = 'dev_hard_start_xmit' and key = '\$' and type = 1003;
-delete from caller_info where function like '%->ndo_start_xmit' and key = '\$' and type = 1003;
-delete from caller_info where caller = 'packet_rcv_fanout' and function = '(struct packet_type)->func' and parameter = 1 and type = 1003;
-delete from caller_info where caller = 'hptiop_probe' and type = 1003;
-delete from caller_info where caller = 'p9_fd_poll' and function = '(struct file_operations)->poll' and type = 1003;
-delete from caller_info where caller = 'proc_reg_poll' and function = 'proc_reg_poll ptr poll' and type = 1003;
-delete from caller_info where function = 'blkdev_ioctl' and type = 1003 and parameter = 0 and key = '\$';
-/* 9017 is USER_DATA3_SET */
+/* 8017 is USER_DATA and  9017 is USER_DATA_SET */
+delete from caller_info where function = 'dev_hard_start_xmit' and type = 8017;
 delete from return_states where function='vscnprintf' and type = 9017;
 delete from return_states where function='scnprintf' and type = 9017;
 delete from return_states where function='vsnprintf' and type = 9017;
@@ -49,6 +39,10 @@
 /* because of recursion it gets passed to everything and is impossible to debug */
 delete from caller_info where function = '__dev_queue_xmit' and type = 8017;
 delete from caller_info where function = '__netdev_start_xmit' and type = 8017;
+delete from caller_info where function = '(struct packet_type)->func' and type = 8017;
+delete from caller_info where function = '(struct bio)->bi_end_io' and type = 8017;
+delete from caller_info where caller = 'NF_HOOK_COND' and type = 8017;
+delete from caller_info where caller = 'NF_HOOK' and type = 8017;
 /* comparison doesn't deal with chunks, I guess.  */
 delete from return_states where function='get_tty_driver' and type = 8017;
 delete from caller_info where caller = 'snd_ctl_elem_write' and function = '(struct snd_kcontrol)->put' and type = 8017;
@@ -57,9 +51,9 @@
 delete from caller_info where caller = 'fb_set_var' and function = '(struct fb_ops)->fb_set_par' and type = 8017 and parameter = 0;
 delete from return_states where function = 'tty_lookup_driver' and parameter = 2 and type = 8017;
 
-insert into caller_info values ('userspace', '', 'compat_sys_ioctl', 0, 0, 1003, 0, '\$', '1');
-insert into caller_info values ('userspace', '', 'compat_sys_ioctl', 0, 0, 1003, 1, '\$', '1');
-insert into caller_info values ('userspace', '', 'compat_sys_ioctl', 0, 0, 1003, 2, '\$', '1');
+insert into caller_info values ('userspace', '', 'compat_sys_ioctl', 0, 0, 8017, 0, '\$', '1');
+insert into caller_info values ('userspace', '', 'compat_sys_ioctl', 0, 0, 8017, 1, '\$', '1');
+insert into caller_info values ('userspace', '', 'compat_sys_ioctl', 0, 0, 8017, 2, '\$', '1');
 
 delete from caller_info where function = '(struct timer_list)->function' and parameter = 0;
 
@@ -76,36 +70,15 @@
 
 delete from return_states where function = 'is_kernel_rodata';
 insert into return_states values ('faked', 'is_kernel_rodata', 0, 1, '1', 0, 0,   -1,  '', '');
-insert into return_states values ('faked', 'is_kernel_rodata', 0, 1, '1', 0, 103,  0,  '\$', '100000000-177777777');
+insert into return_states values ('faked', 'is_kernel_rodata', 0, 1, '1', 0, 103,  0,  '\$', '4096-ptr_max');
 insert into return_states values ('faked', 'is_kernel_rodata', 0, 2, '0', 0, 0,   -1,  '', '');
 
 /*
- * I am a bad person for doing this to __kmalloc() which is a very deep function
- * and can easily be removed instead of to kmalloc().  But kmalloc() is an
- * inline function so it ends up being recorded thousands of times in the
- * database.  Doing this is easier.
- *
- */
-delete from return_states where function = '__kmalloc';
-insert into return_states values ('faked', '__kmalloc', 0, 1, '16', 0,    0,  -1, '', '');
-insert into return_states values ('faked', '__kmalloc', 0, 1, '16', 0, 103,   0, '\$', '0');
-insert into return_states values ('faked', '__kmalloc', 0, 2, '0,500000000-577777777', 0,    0, -1, '', '');
-insert into return_states values ('faked', '__kmalloc', 0, 2, '0,500000000-577777777', 0, 103,  0, '\$', '1-4000000');
-insert into return_states values ('faked', '__kmalloc', 0, 2, '0,500000000-577777777', 0, 1037,  -1, '', 400);
-insert into return_states values ('faked', '__kmalloc', 0, 3, '0', 0,    0,  -1, '', '');
-insert into return_states values ('faked', '__kmalloc', 0, 3, '0', 0,    103,  0, '\$', '4000000-long_max');
-
-/*
  * Other kmalloc hacking.
  */
-update return_states set return = '0,500000000-577777777' where function = 'kmalloc_slab' and return = 's64min-s64max';
-update return_states set return = '0,500000000-577777777' where function = 'slab_alloc_node' and return = 's64min-s64max';
-update return_states set return = '0,500000000-577777777' where function = 'kmalloc_large' and return != '0';
-update return_states set return = '0,500000000-577777777' where function = 'kmalloc_order_trace' and return != '0';
-
 delete from return_states where function = 'vmalloc';
-insert into return_states values ('faked', 'vmalloc', 0, 1, '0,600000000-677777777', 0,    0, -1, '', '');
-insert into return_states values ('faked', 'vmalloc', 0, 1, '0,600000000-677777777', 0, 103,  0, '\$', '1-128000000');
+insert into return_states values ('faked', 'vmalloc', 0, 1, '4096-ptr_max', 0,    0, -1, '', '');
+insert into return_states values ('faked', 'vmalloc', 0, 1, '4096-ptr_max', 0, 103,  0, '\$', '1-128000000');
 insert into return_states values ('faked', 'vmalloc', 0, 2, '0', 0,    0,  -1, '', '');
 
 delete from return_states where function = 'ksize';
@@ -141,9 +114,6 @@
 /* Just delete a lot of returns that everyone ignores */
 delete from return_states where file = 'drivers/pci/access.c' and (return >= 129 and return <= 137);
 
-update return_states set return = '(-4095)-s32max[<=\$1]' where function = 'get_user_pages' and return = 's32min-s32max';
-update return_states set return = '(-4095)-s64max[<=\$1]' where function = 'get_user_pages' and return = 's64min-s64max';
-
 /* Smatch can't parse wait_for_completion() */
 update return_states set return = '(-108),(-22),0' where function = '__spi_sync' and return = '(-115),(-108),(-22)';
 
@@ -155,9 +125,9 @@
 delete from caller_info where caller='init_fw_attribute_group' and function='(struct device_attribute)->show';
 /* and let's fake the next dev_attr_show() call entirely */
 delete from caller_info where caller='sysfs_kf_seq_show' and function='(struct sysfs_ops)->show';
-insert into caller_info values ('fake', 'sysfs_kf_seq_show', '(struct sysfs_ops)->show', 0, 0, 1001, 0, '\$', '4096-2117777777777777777');
+insert into caller_info values ('fake', 'sysfs_kf_seq_show', '(struct sysfs_ops)->show', 0, 0, 1001, 0, '\$', '4096-ptr_max');
 insert into caller_info values ('fake', 'sysfs_kf_seq_show', '(struct sysfs_ops)->show', 0, 0, 1002, 2, '\$', '4096');
-insert into caller_info values ('fake', 'sysfs_kf_seq_show', '(struct sysfs_ops)->show', 0, 0, 1001, 2, '\$', '4096-2117777777777777777');
+insert into caller_info values ('fake', 'sysfs_kf_seq_show', '(struct sysfs_ops)->show', 0, 0, 1001, 2, '\$', '4096-ptr_max');
 insert into caller_info values ('fake', 'sysfs_kf_seq_show', '(struct sysfs_ops)->show', 0, 0, 0,   -1, ''  , '');
 /* config fs confuses smatch a little */
 update caller_info set value = 4096 where caller='fill_read_buffer' and function='(struct configfs_item_operations)->show_attribute' and type = 1002 and parameter = 2;
@@ -198,12 +168,18 @@
 /* this if from READ_ONCE().  We can't know anything about the data.  */
 delete from type_info where key = '(union anonymous)->__val';
 
+/* This is RIO_BAD_SIZE */
+delete from return_states where file = 'drivers/rapidio/rio-access.c' and return = '129';
+
+/* Smatch sucks at loops */
+delete from return_states where function = 'ata_dev_next' and type = 103;
+
 EOF
 
 # fixme: this is totally broken
 call_id=$(echo "select distinct call_id from caller_info where function = '__kernel_write';" | sqlite3 $db_file)
 for id in $call_id ; do
-    echo "insert into caller_info values ('fake', '', '__kernel_write', $id, 0, 1003, 1, '*\$', '');" | sqlite3 $db_file
+    echo "insert into caller_info values ('fake', '', '__kernel_write', $id, 0, 8017, 1, '*\$', '');" | sqlite3 $db_file
 done
 
 for i in $(echo "select distinct return from return_states where function = 'clear_user';" | sqlite3 $db_file ) ; do
@@ -227,3 +203,17 @@
          | sqlite3 $db_file
 done
 
+
+for func in __kmalloc __kmalloc_track_caller ; do
+
+    cat << EOF | sqlite3 $db_file
+delete from return_states where function = '$func';
+insert into return_states values ('faked', '$func', 0, 1, '16', 0,    0,  -1, '', '');
+insert into return_states values ('faked', '$func', 0, 1, '16', 0, 103,   0, '\$', '0');
+insert into return_states values ('faked', '$func', 0, 2, '4096-ptr_max', 0,    0, -1, '', '');
+insert into return_states values ('faked', '$func', 0, 2, '4096-ptr_max', 0, 103,  0, '\$', '1-4000000');
+insert into return_states values ('faked', '$func', 0, 2, '4096-ptr_max', 0, 1037,  -1, '', 400);
+insert into return_states values ('faked', '$func', 0, 3, '0', 0,    0,  -1, '', '');
+insert into return_states values ('faked', '$func', 0, 3, '0', 0,    103,  0, '\$', '1-long_max');
+EOF
+done
--- old/usr/src/tools/smatch/src/smatch_data/db/function_ptr.schema	Mon Aug  5 08:38:24 2019
+++ new/usr/src/tools/smatch/src/smatch_data/db/function_ptr.schema	Mon Aug  5 08:38:24 2019
@@ -1,1 +1,9 @@
-CREATE TABLE function_ptr (file varchar(128), function varchar(64), ptr varchar(256), searchable integer);
+CREATE TABLE function_ptr (
+	file varchar(128),
+	function varchar(64),
+	ptr varchar(256),
+	searchable integer,
+
+	CONSTRAINT function_ptr_constraint UNIQUE (file, function, ptr)
+);
+
--- old/usr/src/tools/smatch/src/smatch_data/db/init_constraints.pl	Mon Aug  5 08:38:24 2019
+++ new/usr/src/tools/smatch/src/smatch_data/db/init_constraints.pl	Mon Aug  5 08:38:24 2019
@@ -46,6 +46,10 @@
     my $project = shift;
     my $dir = dirname($full_path);
 
+    if ($project =~ /^$/) {
+        return;
+    }
+
     open(FILE, "$dir/$project.constraints");
     while (<FILE>) {
         s/\n//;
--- old/usr/src/tools/smatch/src/smatch_data/db/init_constraints_required.pl	Mon Aug  5 08:38:24 2019
+++ new/usr/src/tools/smatch/src/smatch_data/db/init_constraints_required.pl	Mon Aug  5 08:38:24 2019
@@ -34,6 +34,10 @@
     my $dir = dirname($full_path);
     my ($data, $op, $limit);
 
+    if ($project =~ /^$/) {
+        return;
+    }
+
     open(FILE, "$dir/$project.constraints_required");
     while (<FILE>) {
         ($data, $op, $limit) = split(/,/);
--- old/usr/src/tools/smatch/src/smatch_data/db/kernel.return_fixes	Mon Aug  5 08:38:24 2019
+++ new/usr/src/tools/smatch/src/smatch_data/db/kernel.return_fixes	Mon Aug  5 08:38:24 2019
@@ -11,7 +11,9 @@
 vscnprintf s32min-(-2),0-s32max[<$1] 0-s32max[<$1]
 down_interruptible s32min-s32max (-62),(-4)
 __sock_create s32min-(-1),1-s32max (-4095)-(-1)
+__sock_create s32min-(-90),(-88)-(-1),1-s32max (-4095)-(-90),(-88)-(-1)
 sock_create_kern s32min-(-1),1-s32max (-4095)-(-1)
+sock_create_kern s32min-(-90),(-88)-(-1),1-s32max (-4095)-(-90),(-88)-(-1)
 nilfs_cpfile_get_checkpoint_block s32min-(-18),(-16)-s32max (-4095)-(-18),(-16)-(-1)
 nilfs_cpfile_get_checkpoint_block s32min-(-18),(-16)-(-3),(-1),1-s32max (-4095)-(-18),(-16)-(-3),(-1)
 nilfs_mdt_insert_new_block s32min-(-23),(-21)-(-1),1-s32max (-4095)-(-23),(-21)-(-1)
@@ -19,8 +21,6 @@
 atomic_read s32min-s32max s32min-s32max[==$0->counter]
 notifier_to_errno (-2147483646)-(-1) (-4095)-(-1)
 mc_status_to_error s32min-s32max (-4095)-0
-dma_fence_wait_timeout s64min-s64max (-4095)-s64max
-dma_fence_wait_timeout s32min-s32max (-4095)-s32max
 fls s32min-s32max 0-32
 fls64 s64min-s64max 0-64
 __bitmap_weight s32min-s32max 0-s32max[<=$1]
@@ -27,8 +27,29 @@
 __bitmap_weight 0-s32max 0-s32max[<=$1]
 __ffs 0-u64max 0-63
 __ffs 0-u32max 0-31
+find_last_bit 0-u64max 0-u32max[<=$1]
 __spi_sync (-524),(-115),(-108),(-22) (-4095)-0
 tpm_tis_spi_read_bytes s32min-s32max (-4095)-0
 __irq_domain_activate_irq s32min-s32max (-4095)-0
-get_user_pages_fast s32min-s32max 1-s32max[<$1]
+get_user_pages_fast s32min-s32max 1-s32max[<=$1]
+get_user_pages s32min-s32max (-4095)-s32max[<=$1]
+get_user_pages s64min-s64max (-4095)-s64max[<=$1]
+get_user_pages_remote 1-s64max 1-s64max[<=$3]
+get_user_pages_remote (-133),(-14),(-12),1-s64max (-133),(-14),(-12),1-s64max[<=$3]
 __nci_request s32min-s32max (-4095)-0
+wait_for_common s64min-s64max 0-s64max[<=$1]
+wait_for_common 64min-(-1),1-s64max 1-s64max[<=$1]
+dma_fence_wait_timeout s64min-(-1),1-s64max (-4095)-(-1),1-s32max[<=2]
+dma_fence_wait_timeout s64min-s64max (-4095)-s32max
+dma_fence_wait_timeout s32min-s32max (-4095)-s32max
+__fw_state_wait_common s32min-s32max (-4095)-(-1)
+__ilog2_u32 s32min-s32max 0-31
+__ilog2_u64 s32min-s32max 0-63
+driver_attach s32min-s32max (-4095)-0
+mbox_post_sync_cmd 255 0-255
+mmc_io_rw_extended s32min-(-1),1-s32max (-4095)-(-1)
+kernel_read s64min-s64max (-4095)-1000000000
+security_kernel_post_read_file s32min-(-1),1-s32max (-4095)-(-1)
+array_index_mask_nospec 0-u64max u64max
+array_index_mask_nospec 0-u32max u32max
+nla_len (-4)-65531[$0->nla_len\ -\ 4] 0-65531[$0->nla_len\ -\ 4]
--- old/usr/src/tools/smatch/src/smatch_data/db/smdb.py	Mon Aug  5 08:38:25 2019
+++ new/usr/src/tools/smatch/src/smatch_data/db/smdb.py	Mon Aug  5 08:38:25 2019
@@ -17,6 +17,7 @@
 def usage():
     print "%s" %(sys.argv[0])
     print "<function> - how a function is called"
+    print "info <type> - how a function is called, filtered by type"
     print "return_states <function> - what a function returns"
     print "call_tree <function> - show the call tree"
     print "where <struct_type> <member> - where a struct member is set"
@@ -55,7 +56,6 @@
              104: "PARAM_FILTER",
             1001: "PARAM_VALUE",
             1002: "BUF_SIZE",
-            1003: "USER_DATA",
             1004: "CAPPED_DATA",
             1005: "RETURN_VALUE",
             1006: "DEREFERENCE",
@@ -79,7 +79,22 @@
             1027: "BYTE_UNITS",
             1028: "COMPARE_LIMIT",
             1029: "PARAM_COMPARE",
-            8017: "USER_DATA2",
+            1030: "EXPECTS_TYPE",
+            1031: "CONSTRAINT",
+            1032: "PASSES_TYPE",
+            1033: "CONSTRAINT_REQUIRED",
+            1034: "BIT_INFO",
+            1035: "NOSPEC",
+            1036: "NOSPEC_WB",
+            1037: "STMT_CNT",
+            1038: "TERMINATED",
+            1039: "SLEEP",
+            1040: "NO_SLEEP_CNT",
+            1041: "SMALLISH",
+            1042: "FRESH_MTAG",
+
+            8017: "USER_DATA",
+            9017: "USER_DATA_SET",
             8018: "NO_OVERFLOW",
             8019: "NO_OVERFLOW_SIMPLE",
             8020: "LOCKED",
@@ -197,6 +212,8 @@
         return 2**15 - 1
     elif txt == "u64max":
         return 2**64 - 1
+    elif txt == "ptr_max":
+        return 2**64 - 1
     elif txt == "u32max":
         return 2**32 - 1
     elif txt == "u16max":
@@ -589,6 +606,12 @@
 if len(sys.argv) == 2:
     func = sys.argv[1]
     print_caller_info("", func)
+elif sys.argv[1] == "info":
+    my_type = ""
+    if len(sys.argv) == 4:
+        my_type = sys.argv[3]
+    func = sys.argv[2]
+    print_caller_info("", func, my_type)
 elif sys.argv[1] == "call_info":
     if len(sys.argv) != 4:
         usage()
@@ -596,12 +619,6 @@
     func = sys.argv[3]
     caller_info_values(filename, func)
     print_caller_info(filename, func)
-elif sys.argv[1] == "user_data":
-    func = sys.argv[2]
-    print_caller_info(filename, func, "USER_DATA")
-elif sys.argv[1] == "param_value":
-    func = sys.argv[2]
-    print_caller_info(filename, func, "PARAM_VALUE")
 elif sys.argv[1] == "function_ptr" or sys.argv[1] == "fn_ptr":
     func = sys.argv[2]
     print_fn_ptrs(func)
--- old/usr/src/tools/smatch/src/smatch_data/db/vim_smdb	Mon Aug  5 08:38:25 2019
+++ new/usr/src/tools/smatch/src/smatch_data/db/vim_smdb	Mon Aug  5 08:38:25 2019
@@ -25,7 +25,7 @@
 next=$(($i + 1))
 
 rm -f $DIR/$next
-rm $DIR/.${i}.swp
+rm -f $DIR/.${i}.swp
 smdb $* > $DIR/$i
 
 echo "$DIR/$i" > $DIR/cur
--- old/usr/src/tools/smatch/src/smatch_data/illumos_kernel.skipped_functions	Mon Aug  5 08:38:25 2019
+++ new/usr/src/tools/smatch/src/smatch_data/illumos_kernel.skipped_functions	Mon Aug  5 08:38:25 2019
@@ -1,8 +1,13 @@
 /* These are "too hairy" for smatch. */
+ECDSA_VerifyDigest
 dtrace_disx86
 elf32exec
 elfexec
 iscsi_ioctl
 lm_idle_chk
+ld64_sym_validate
+luaV_settable
+nostore_generate_key_pair
+sadb_common_add
 segvn_fault_vnodepages
 tcp_input_data
--- old/usr/src/tools/smatch/src/smatch_data/illumos_user.skipped_functions	Mon Aug  5 08:38:26 2019
+++ new/usr/src/tools/smatch/src/smatch_data/illumos_user.skipped_functions	Mon Aug  5 08:38:26 2019
@@ -24,8 +24,12 @@
 AslCompilerparse
 /* cmd/fs.d/autofs */
 nfsmount
+/* cmd/mdb */
+iob_doprnt
 /* cmd/pppd */
 lcp_nakci
+/* cmd/cmd-crypto */
+execute_cmd
 
 /* generated code */
 ipf_yyparse
--- old/usr/src/tools/smatch/src/smatch_data/kernel.allocation_funcs_gfp.remove	Mon Aug  5 08:38:26 2019
+++ new/usr/src/tools/smatch/src/smatch_data/kernel.allocation_funcs_gfp.remove	Mon Aug  5 08:38:26 2019
@@ -1,2 +1,3 @@
+acquire_group
 acquire_group 2
 acquire_group X
--- /dev/null	Mon Aug  5 08:38:26 2019
+++ new/usr/src/tools/smatch/src/smatch_data/kernel.ignore_casted_params	Mon Aug  5 08:38:26 2019
@@ -0,0 +1,15 @@
+set_bit
+clear_bit
+__clear_bit
+__set_bit
+test_and_set_bit
+find_last_bit
+change_bit
+xfs_next_bit
+find_next_bit
+find_first_bit
+__test_and_set_bit
+sync_set_bit
+bitmap_weight
+bitmap_intersects
+bitmap_empty
--- old/usr/src/tools/smatch/src/smatch_data/kernel.ignore_side_effects	Mon Aug  5 08:38:26 2019
+++ new/usr/src/tools/smatch/src/smatch_data/kernel.ignore_side_effects	Mon Aug  5 08:38:26 2019
@@ -53,6 +53,8 @@
 RCU_INIT_POINTER
 READ64
 rtnl_dereference
+SK_REUSEPORT_LOAD_SKB_FIELD
+SK_REUSEPORT_LOAD_SK_FIELD_SIZE_OFF
 send_bits
 send_code
 SOCK_ADDR_LOAD_NESTED_FIELD
@@ -59,6 +61,8 @@
 SOCK_ADDR_LOAD_NESTED_FIELD_SIZE_OFF
 SOCK_ADDR_LOAD_OR_STORE_NESTED_FIELD_SIZE_OFF
 SOCK_ADDR_LOAD_OR_STORE_NESTED_FIELD
+SOCK_OPS_GET_FIELD
+SOCK_OPS_GET_OR_SET_FIELD
 SOCK_OPS_GET_TCP32
 unsafe_get_user
 unsafe_put_user
--- old/usr/src/tools/smatch/src/smatch_data/kernel.ignore_uninitialized_param	Mon Aug  5 08:38:26 2019
+++ new/usr/src/tools/smatch/src/smatch_data/kernel.ignore_uninitialized_param	Mon Aug  5 08:38:26 2019
@@ -102,3 +102,10 @@
 pwrap_read 2
 dibusb_read_eeprom_byte 2
 of_fdt_unflatten_tree 2
+pci_user_read_config_byte 2
+gen11_gu_misc_irq_ack 2
+vsc73xx_read 4
+smb_hc_read 2
+smb_word_op 5
+atl1c_read_phy_reg 2
+adf7242_read_reg 2
--- /dev/null	Mon Aug  5 08:38:27 2019
+++ new/usr/src/tools/smatch/src/smatch_data/kernel.ignored_warnings	Mon Aug  5 08:38:27 2019
@@ -0,0 +1,1 @@
+check_shift_to_zero overflows_type
--- old/usr/src/tools/smatch/src/smatch_data/kernel.no_inline_functions	Mon Aug  5 08:38:27 2019
+++ new/usr/src/tools/smatch/src/smatch_data/kernel.no_inline_functions	Mon Aug  5 08:38:27 2019
@@ -10,3 +10,5 @@
 __arch_hweight64
 __write_once_size
 atomic_set
+atomic_read
+notifier_to_errno
--- /dev/null	Mon Aug  5 08:38:27 2019
+++ new/usr/src/tools/smatch/src/smatch_data/kernel.no_return_funcs.add	Mon Aug  5 08:38:27 2019
@@ -0,0 +1,2 @@
+YY_FATAL_ERROR
+malformed_line
--- old/usr/src/tools/smatch/src/smatch_data/kernel.silenced_functions	Mon Aug  5 08:38:27 2019
+++ new/usr/src/tools/smatch/src/smatch_data/kernel.silenced_functions	Mon Aug  5 08:38:27 2019
@@ -4,7 +4,6 @@
 atomic64_dec_and_test
 atomic_sub_and_test
 test_and_clear_bit
-test_and_set_bit
 __copy_to_user_nocheck
 __copy_from_user_nocheck
 arch_static_branch
--- old/usr/src/tools/smatch/src/smatch_data_source.c	Mon Aug  5 08:38:28 2019
+++ new/usr/src/tools/smatch/src/smatch_data_source.c	Mon Aug  5 08:38:28 2019
@@ -24,6 +24,7 @@
 static char *get_source_parameter(struct expression *expr)
 {
 	struct expression *tmp;
+	const char *param_name;
 	struct symbol *sym;
 	char *name;
 	int param;
@@ -30,6 +31,7 @@
 	char *ret = NULL;
 	char buf[32];
 	int cnt = 0;
+	bool modified = false;
 
 	tmp = expr;
 	while ((tmp = get_assigned_expr(tmp))) {
@@ -48,10 +50,14 @@
 	param = get_param_num_from_sym(sym);
 	if (param < 0)
 		goto free;
-	if (param_was_set(expr))
+	param_name = get_param_name_var_sym(name, sym);
+	if (!param_name)
 		goto free;
+	if (param_was_set_var_sym(name, sym))
+		modified = true;
 
-	snprintf(buf, sizeof(buf), "p %d", param);
+	snprintf(buf, sizeof(buf), "$%d%s%s", param, param_name + 1,
+		 modified ? " [m]" : "");
 	ret = alloc_string(buf);
 
 free:
--- old/usr/src/tools/smatch/src/smatch_db.c	Mon Aug  5 08:38:28 2019
+++ new/usr/src/tools/smatch/src/smatch_db.c	Mon Aug  5 08:38:28 2019
@@ -79,7 +79,7 @@
 	int i, j;
 
 	for (i = 0, j = 0; str[i] != '\0' && j != sizeof(buf); i++, j++) {
-		if (str[i] != '\n') {
+		if (str[i] != '\r' && str[i] != '\n') {
 			buf[j] = str[i];
 			continue;
 		}
@@ -252,8 +252,8 @@
 
 void sql_insert_function_ptr(const char *fn, const char *struct_name)
 {
-	sql_insert(function_ptr, "'%s', '%s', '%s', 0", get_base_file(), fn,
-		   struct_name);
+	sql_insert_or_ignore(function_ptr, "'%s', '%s', '%s', 0",
+			     get_base_file(), fn, struct_name);
 }
 
 void sql_insert_return_implies(int type, int param, const char *key, const char *value)
@@ -331,7 +331,7 @@
 	if (!option_info)
 		return;
 
-        sm_msg("SQL: insert or ignore into constraints (str) values('%s');", con);
+        sm_msg("SQL: insert or ignore into constraints (str) values('%s');", escape_newlines(con));
 }
 
 void sql_save_constraint_required(const char *data, int op, const char *limit)
@@ -372,11 +372,6 @@
 		   tag, get_filename(), get_function(), get_lineno(), left_name, right_name);
 }
 
-void sql_insert_mtag_data(mtag_t tag, const char *var, int offset, int type, const char *value)
-{
-	sql_insert(mtag_data, "%lld, '%s', %d, %d, '%s'", tag, var, offset, type, value);
-}
-
 void sql_insert_mtag_map(mtag_t tag, int offset, mtag_t container)
 {
 	sql_insert(mtag_map, "%lld, %d, %lld", tag, offset, container);
@@ -750,29 +745,58 @@
 	return buf;
 }
 
+int is_recursive_member(const char *name)
+{
+	char buf[256];
+	const char *p, *next;
+	int size;
+
+	p = strchr(name, '>');
+	if (!p)
+		return 0;
+	p++;
+	while (true) {
+		next = strchr(p, '>');
+		if (!next)
+			return 0;
+		next++;
+
+		size = next - p;
+		if (size >= sizeof(buf))
+			return 0;
+		memcpy(buf, p, size);
+		buf[size] = '\0';
+		if (strstr(next, buf))
+			return 1;
+		p = next;
+	}
+}
+
 static void print_struct_members(struct expression *call, struct expression *expr, int param, int offset, struct stree *stree,
 	void (*callback)(struct expression *call, int param, char *printed_name, struct sm_state *sm))
 {
 	struct sm_state *sm;
+	const char *sm_name;
 	char *name;
 	struct symbol *sym;
 	int len;
 	char printed_name[256];
 	int is_address = 0;
+	bool add_star;
 	struct symbol *type;
 
 	expr = strip_expr(expr);
 	if (!expr)
 		return;
+	type = get_type(expr);
+	if (type && type_bits(type) < type_bits(&ulong_ctype))
+		return;
+
 	if (expr->type == EXPR_PREOP && expr->op == '&') {
 		expr = strip_expr(expr->unop);
 		is_address = 1;
 	}
 
-	type = get_type(expr);
-	if (type && type_bits(type) < type_bits(&ulong_ctype))
-		return;
-
 	name = expr_to_var_sym(expr, &sym);
 	if (!name || !sym)
 		goto free;
@@ -781,23 +805,37 @@
 	FOR_EACH_SM(stree, sm) {
 		if (sm->sym != sym)
 			continue;
-		if (strcmp(name, sm->name) == 0) {
+		sm_name = sm->name;
+		add_star = false;
+		if (sm_name[0] == '*') {
+			add_star = true;
+			sm_name++;
+		}
+		// FIXME: simplify?
+		if (!add_star && strcmp(name, sm_name) == 0) {
 			if (is_address)
 				snprintf(printed_name, sizeof(printed_name), "*$%s", show_offset(offset));
 			else /* these are already handled. fixme: handle them here */
 				continue;
-		} else if (sm->name[0] == '*' && strcmp(name, sm->name + 1) == 0) {
-			snprintf(printed_name, sizeof(printed_name), "*$%s", show_offset(offset));
-		} else if (strncmp(name, sm->name, len) == 0) {
-			if (isalnum(sm->name[len]))
+		} else if (add_star && strcmp(name, sm_name) == 0) {
+			snprintf(printed_name, sizeof(printed_name), "%s*$%s",
+				 is_address ? "*" : "", show_offset(offset));
+		} else if (strncmp(name, sm_name, len) == 0) {
+			if (sm_name[len] != '.' && sm_name[len] != '-')
 				continue;
 			if (is_address)
-				snprintf(printed_name, sizeof(printed_name), "$%s->%s", show_offset(offset), sm->name + len + 1);
+				snprintf(printed_name, sizeof(printed_name),
+					 "%s$%s->%s", add_star ? "*" : "",
+					 show_offset(offset), sm_name + len + 1);
 			else
-				snprintf(printed_name, sizeof(printed_name), "$%s%s", show_offset(offset), sm->name + len);
+				snprintf(printed_name, sizeof(printed_name),
+					 "%s$%s%s", add_star ? "*" : "",
+					 show_offset(offset), sm_name + len);
 		} else {
 			continue;
 		}
+		if (is_recursive_member(printed_name))
+			continue;
 		callback(call, param, printed_name, sm);
 	} END_FOR_EACH_SM(sm);
 free:
@@ -1009,9 +1047,8 @@
 	char *ptr;
 
 	FOR_EACH_PTR(ptr_names, ptr) {
-		if (list_has_string(ptr_names_done, ptr))
+		if (!insert_string(&ptr_names_done, ptr))
 			continue;
-		insert_string(&ptr_names_done, ptr);
 		return ptr;
 	} END_FOR_EACH_PTR(ptr);
 	return NULL;
@@ -1224,53 +1261,97 @@
 			   call_implies_callbacks);
 }
 
-static void print_initializer_list(struct expression_list *expr_list,
-		struct symbol *struct_type)
+static char *get_return_compare_is_param(struct expression *expr)
 {
-	struct expression *expr;
-	struct symbol *base_type;
-	char struct_name[256];
+	char *var;
+	char buf[256];
+	int comparison;
+	int param;
 
-	FOR_EACH_PTR(expr_list, expr) {
-		if (expr->type == EXPR_INDEX && expr->idx_expression && expr->idx_expression->type == EXPR_INITIALIZER) {
-			print_initializer_list(expr->idx_expression->expr_list, struct_type);
-			continue;
-		}
-		if (expr->type != EXPR_IDENTIFIER)
-			continue;
-		if (!expr->expr_ident)
-			continue;
-		if (!expr->ident_expression || !expr->ident_expression->symbol_name)
-			continue;
-		base_type = get_type(expr->ident_expression);
-		if (!base_type || base_type->type != SYM_FN)
-			continue;
-		snprintf(struct_name, sizeof(struct_name), "(struct %s)->%s",
-			 struct_type->ident->name, expr->expr_ident->name);
-		sql_insert_function_ptr(expr->ident_expression->symbol_name->name,
-				        struct_name);
-	} END_FOR_EACH_PTR(expr);
+	param = get_param_num(expr);
+	if (param < 0)
+		return NULL;
+
+	var = expr_to_var(expr);
+	if (!var)
+		return NULL;
+	snprintf(buf, sizeof(buf), "%s orig", var);
+	comparison = get_comparison_strings(var, buf);
+	free_string(var);
+
+	if (!comparison)
+		return NULL;
+
+	snprintf(buf, sizeof(buf), "[%s$%d]", show_special(comparison), param);
+	return alloc_sname(buf);
 }
 
-static void global_variable(struct symbol *sym)
+static char *get_return_compare_str(struct expression *expr)
 {
-	struct symbol *struct_type;
+	char *compare_str;
 
-	if (!sym->ident)
-		return;
-	if (!sym->initializer || sym->initializer->type != EXPR_INITIALIZER)
-		return;
-	struct_type = get_base_type(sym);
-	if (!struct_type)
-		return;
-	if (struct_type->type == SYM_ARRAY) {
-		struct_type = get_base_type(struct_type);
-		if (!struct_type)
-			return;
+	compare_str = get_return_compare_is_param(expr);
+	if (compare_str)
+		return compare_str;
+
+	compare_str = expr_lte_to_param(expr, -1);
+	if (compare_str)
+		return compare_str;
+
+	return expr_param_comparison(expr, -1);
+}
+
+static const char *get_return_ranges_str(struct expression *expr, struct range_list **rl_p)
+{
+	struct range_list *rl;
+	char *return_ranges;
+	sval_t sval;
+	char *compare_str;
+	char *math_str;
+	char buf[128];
+
+	*rl_p = NULL;
+
+	if (!expr)
+		return alloc_sname("");
+
+	if (get_implied_value(expr, &sval)) {
+		sval = sval_cast(cur_func_return_type(), sval);
+		*rl_p = alloc_rl(sval, sval);
+		return sval_to_str_or_err_ptr(sval);
 	}
-	if (struct_type->type != SYM_STRUCT || !struct_type->ident)
-		return;
-	print_initializer_list(sym->initializer->expr_list, struct_type);
+
+	compare_str = expr_equal_to_param(expr, -1);
+	math_str = get_value_in_terms_of_parameter_math(expr);
+
+	if (get_implied_rl(expr, &rl) && !is_whole_rl(rl)) {
+		rl = cast_rl(cur_func_return_type(), rl);
+		return_ranges = show_rl(rl);
+	} else if (get_imaginary_absolute(expr, &rl)){
+		rl = cast_rl(cur_func_return_type(), rl);
+		return alloc_sname(show_rl(rl));
+	} else {
+		get_absolute_rl(expr, &rl);
+		rl = cast_rl(cur_func_return_type(), rl);
+		return_ranges = show_rl(rl);
+	}
+	*rl_p = rl;
+
+	if (compare_str) {
+		snprintf(buf, sizeof(buf), "%s%s", return_ranges, compare_str);
+		return alloc_sname(buf);
+	}
+	if (math_str) {
+		snprintf(buf, sizeof(buf), "%s[%s]", return_ranges, math_str);
+		return alloc_sname(buf);
+	}
+	compare_str = get_return_compare_str(expr);
+	if (compare_str) {
+		snprintf(buf, sizeof(buf), "%s%s", return_ranges, compare_str);
+		return alloc_sname(buf);
+	}
+
+	return return_ranges;
 }
 
 static void match_return_info(int return_id, char *return_ranges, struct expression *expr)
@@ -1282,7 +1363,7 @@
 {
 	struct returned_state_callback *cb;
 	struct range_list *rl;
-	char *return_ranges;
+	const char *return_ranges;
 	int final_pass_orig = final_pass;
 
 	__push_fake_cur_stree();
@@ -1291,31 +1372,24 @@
 	__split_whole_condition(expr->conditional);
 	final_pass = final_pass_orig;
 
-	if (get_implied_rl(expr->cond_true, &rl))
-		rl = cast_rl(cur_func_return_type(), rl);
-	else
-		rl = cast_rl(cur_func_return_type(), alloc_whole_rl(get_type(expr->cond_true)));
-	return_ranges = show_rl(rl);
+	return_ranges = get_return_ranges_str(expr->cond_true ?: expr->conditional, &rl);
+
 	set_state(RETURN_ID, "return_ranges", NULL, alloc_estate_rl(rl));
 
 	return_id++;
 	FOR_EACH_PTR(returned_state_callbacks, cb) {
-		cb->callback(return_id, return_ranges, expr->cond_true);
+		cb->callback(return_id, (char *)return_ranges, expr->cond_true);
 	} END_FOR_EACH_PTR(cb);
 
 	__push_true_states();
 	__use_false_states();
 
-	if (get_implied_rl(expr->cond_false, &rl))
-		rl = cast_rl(cur_func_return_type(), rl);
-	else
-		rl = cast_rl(cur_func_return_type(), alloc_whole_rl(get_type(expr->cond_false)));
-	return_ranges = show_rl(rl);
+	return_ranges = get_return_ranges_str(expr->cond_false, &rl);
 	set_state(RETURN_ID, "return_ranges", NULL, alloc_estate_rl(rl));
 
 	return_id++;
 	FOR_EACH_PTR(returned_state_callbacks, cb) {
-		cb->callback(return_id, return_ranges, expr->cond_false);
+		cb->callback(return_id, (char *)return_ranges, expr->cond_false);
 	} END_FOR_EACH_PTR(cb);
 
 	__merge_true_states();
@@ -1380,35 +1454,6 @@
 	return 0;
 }
 
-static char *get_return_compare_str(struct expression *expr)
-{
-	char *compare_str;
-	char *var;
-	char buf[256];
-	int comparison;
-	int param;
-
-	compare_str = expr_lte_to_param(expr, -1);
-	if (compare_str)
-		return compare_str;
-	param = get_param_num(expr);
-	if (param < 0)
-		return NULL;
-
-	var = expr_to_var(expr);
-	if (!var)
-		return NULL;
-	snprintf(buf, sizeof(buf), "%s orig", var);
-	comparison = get_comparison_strings(var, buf);
-	free_string(var);
-
-	if (!comparison)
-		return NULL;
-
-	snprintf(buf, sizeof(buf), "[%s$%d]", show_special(comparison), param);
-	return alloc_sname(buf);
-}
-
 static int split_possible_helper(struct sm_state *sm, struct expression *expr)
 {
 	struct returned_state_callback *cb;
@@ -1417,9 +1462,10 @@
 	struct sm_state *tmp;
 	int ret = 0;
 	int nr_possible, nr_states;
-	char *compare_str = NULL;
+	char *compare_str;
 	char buf[128];
 	struct state_list *already_handled = NULL;
+	sval_t sval;
 
 	if (!sm || !sm->merged)
 		return 0;
@@ -1428,7 +1474,12 @@
 		return 0;
 
 	/* bail if it gets too complicated */
-	nr_possible = ptr_list_size((struct ptr_list *)sm->possible);
+	nr_possible = 0;
+	FOR_EACH_PTR(sm->possible, tmp) {
+		if (tmp->merged)
+			continue;
+		nr_possible++;
+	} END_FOR_EACH_PTR(tmp);
 	nr_states = get_db_state_count();
 	if (nr_states * nr_possible >= 2000)
 		return 0;
@@ -1448,10 +1499,12 @@
 		rl = cast_rl(cur_func_return_type(), estate_rl(tmp->state));
 		return_ranges = show_rl(rl);
 		set_state(RETURN_ID, "return_ranges", NULL, alloc_estate_rl(clone_rl(rl)));
-		compare_str = get_return_compare_str(expr);
-		if (compare_str) {
-			snprintf(buf, sizeof(buf), "%s%s", return_ranges, compare_str);
-			return_ranges = alloc_sname(buf);
+		if (!rl_to_sval(rl, &sval)) {
+			compare_str = get_return_compare_str(expr);
+			if (compare_str) {
+				snprintf(buf, sizeof(buf), "%s%s", return_ranges, compare_str);
+				return_ranges = alloc_sname(buf);
+			}
 		}
 
 		return_id++;
@@ -1478,58 +1531,6 @@
 	return split_possible_helper(sm, expr);
 }
 
-static const char *get_return_ranges_str(struct expression *expr, struct range_list **rl_p)
-{
-	struct range_list *rl;
-	char *return_ranges;
-	sval_t sval;
-	char *compare_str;
-	char *math_str;
-	char buf[128];
-
-	*rl_p = NULL;
-
-	if (!expr)
-		return alloc_sname("");
-
-	if (get_implied_value(expr, &sval)) {
-		sval = sval_cast(cur_func_return_type(), sval);
-		*rl_p = alloc_rl(sval, sval);
-		return sval_to_str(sval);
-	}
-
-	compare_str = expr_equal_to_param(expr, -1);
-	math_str = get_value_in_terms_of_parameter_math(expr);
-
-	if (get_implied_rl(expr, &rl)) {
-		rl = cast_rl(cur_func_return_type(), rl);
-		return_ranges = show_rl(rl);
-	} else if (get_imaginary_absolute(expr, &rl)){
-		rl = cast_rl(cur_func_return_type(), rl);
-		return alloc_sname(show_rl(rl));
-	} else {
-		rl = cast_rl(cur_func_return_type(), alloc_whole_rl(get_type(expr)));
-		return_ranges = show_rl(rl);
-	}
-	*rl_p = rl;
-
-	if (compare_str) {
-		snprintf(buf, sizeof(buf), "%s%s", return_ranges, compare_str);
-		return alloc_sname(buf);
-	}
-	if (math_str) {
-		snprintf(buf, sizeof(buf), "%s[%s]", return_ranges, math_str);
-		return alloc_sname(buf);
-	}
-	compare_str = get_return_compare_str(expr);
-	if (compare_str) {
-		snprintf(buf, sizeof(buf), "%s%s", return_ranges, compare_str);
-		return alloc_sname(buf);
-	}
-
-	return return_ranges;
-}
-
 static bool has_possible_negative(struct sm_state *sm)
 {
 	struct sm_state *tmp;
@@ -1568,6 +1569,7 @@
 	const char *return_ranges;
 	struct range_list *ret_rl;
 	int undo;
+	bool has_zero;
 
 	/* We're going to print the states 3 times */
 	if (get_db_state_count() > 10000 / 3)
@@ -1588,8 +1590,9 @@
 		return 0;
 	if (!has_possible_negative(sm))
 		return 0;
+	has_zero = has_possible_zero_null(sm);
 
-	if (!assume(compare_expression(expr, '>', zero_expr())))
+	if (!assume(compare_expression(expr, has_zero ? '>' : SPECIAL_GTE, zero_expr())))
 		return 0;
 
 	return_id++;
@@ -1601,7 +1604,7 @@
 
 	end_assume();
 
-	if (rl_has_sval(rl, sval_type_val(rl_type(rl), 0))) {
+	if (has_zero) {
 		undo = assume(compare_expression(expr, SPECIAL_EQUAL, zero_expr()));
 
 		return_id++;
@@ -1630,7 +1633,7 @@
 	return 1;
 }
 
-static int call_return_state_hooks_split_null_non_null(struct expression *expr)
+static int call_return_state_hooks_split_null_non_null_zero(struct expression *expr)
 {
 	struct returned_state_callback *cb;
 	struct range_list *rl;
@@ -1647,8 +1650,6 @@
 		return 0;
 	if (expr->type == EXPR_CALL)
 		return 0;
-	if (!is_pointer(expr))
-		return 0;
 
 	sm = get_sm_state_expr(SMATCH_EXTRA, expr);
 	if (!sm)
@@ -1992,14 +1993,14 @@
 		return;
 	} else if (call_return_state_hooks_split_possible(expr)) {
 		return;
-	} else if (call_return_state_hooks_split_null_non_null(expr)) {
+	} else if (split_positive_from_negative(expr)) {
 		return;
+	} else if (call_return_state_hooks_split_null_non_null_zero(expr)) {
+		return;
 	} else if (call_return_state_hooks_split_success_fail(expr)) {
 		return;
 	} else if (splitable_function_call(expr)) {
 		return;
-	} else if (split_positive_from_negative(expr)) {
-		return;
 	} else if (split_by_bool_param(expr)) {
 	} else if (split_by_null_nonnull_param(expr)) {
 		return;
@@ -2208,7 +2209,7 @@
 	for (i = 0; i < argc; i++) {
 		if (i)
 			p += snprintf(p, 4096 - (p - buf), ", ");
-		sqlite3_snprintf(sizeof(tmp), tmp, "%q", argv[i]);
+		sqlite3_snprintf(sizeof(tmp), tmp, "%q", escape_newlines(argv[i]));
 		p += snprintf(p, 4096 - (p - buf), "'%s'", tmp);
 
 	}
@@ -2361,8 +2362,6 @@
 void register_definition_db_callbacks(int id)
 {
 	add_hook(&match_call_info, FUNCTION_CALL_HOOK);
-	add_hook(&global_variable, BASE_HOOK);
-	add_hook(&global_variable, DECLARATION_HOOK);
 	add_split_return_callback(match_return_info);
 	add_split_return_callback(print_returned_struct_members);
 	add_hook(&call_return_state_hooks, RETURN_HOOK);
@@ -2397,6 +2396,8 @@
 
 		if (expr->type != EXPR_ASSIGNMENT)
 			return NULL;
+		if (get_type(expr->left) == &int_ctype && strcmp(key, "$") != 0)
+			return NULL;
 		name = expr_to_var_sym(expr->left, sym);
 		if (!name)
 			return NULL;
@@ -2427,6 +2428,7 @@
 {
 	char buf[256];
 	char *tmp;
+	bool add_star = false;
 
 	if (!arg)
 		return NULL;
@@ -2450,12 +2452,18 @@
 		}
 	}
 
+	if (key[0] == '*') {
+		add_star = true;
+		key++;
+	}
+
 	if (arg->type == EXPR_PREOP && arg->op == '&') {
 		arg = strip_expr(arg->unop);
 		tmp = expr_to_var_sym(arg, sym);
 		if (!tmp)
 			return NULL;
-		snprintf(buf, sizeof(buf), "%s.%s", tmp, key + 3);
+		snprintf(buf, sizeof(buf), "%s%s.%s",
+			 add_star ? "*" : "", tmp, key + 3);
 		return alloc_string(buf);
 	}
 
@@ -2462,7 +2470,7 @@
 	tmp = expr_to_var_sym(arg, sym);
 	if (!tmp)
 		return NULL;
-	snprintf(buf, sizeof(buf), "%s%s", tmp, key + 1);
+	snprintf(buf, sizeof(buf), "%s%s%s", add_star ? "*" : "", tmp, key + 1);
 	free_string(tmp);
 	return alloc_string(buf);
 }
@@ -2480,17 +2488,25 @@
 {
 	int name_len;
 	static char buf[256];
+	bool add_star = false;
 
 	name_len = strlen(param_name);
 
+	if (state_name[0] == '*') {
+		add_star = true;
+		state_name++;
+	}
+
 	if (strcmp(state_name, param_name) == 0) {
-		return "$";
-	} else if (state_name[name_len] == '-' && /* check for '-' from "->" */
+		snprintf(buf, sizeof(buf), "%s$", add_star ? "*" : "");
+		return buf;
+	}
+
+	if (state_name[name_len] == '-' && /* check for '-' from "->" */
 	    strncmp(state_name, param_name, name_len) == 0) {
-		snprintf(buf, sizeof(buf), "$%s", state_name + name_len);
+		snprintf(buf, sizeof(buf), "%s$%s",
+			 add_star ? "*" : "", state_name + name_len);
 		return buf;
-	} else if (state_name[0] == '*' && strcmp(state_name + 1, param_name) == 0) {
-		return "*$";
 	}
 	return NULL;
 }
--- old/usr/src/tools/smatch/src/smatch_equiv.c	Mon Aug  5 08:38:29 2019
+++ new/usr/src/tools/smatch/src/smatch_equiv.c	Mon Aug  5 08:38:29 2019
@@ -102,21 +102,6 @@
 	return ret;
 }
 
-static void debug_addition(struct related_list *rlist, const char *name)
-{
-	struct relation *tmp;
-
-	if (!option_debug_related)
-		return;
-
-	sm_prefix();
-	sm_printf("(");
-	FOR_EACH_PTR(rlist, tmp) {
-		sm_printf("%s ", tmp->name);
-	} END_FOR_EACH_PTR(tmp);
-	sm_printf(") <-- %s\n", name);
-}
-
 static void add_related(struct related_list **rlist, const char *name, struct symbol *sym)
 {
 	struct relation *rel;
@@ -126,8 +111,6 @@
 		.sym = sym
 	};
 
-	debug_addition(*rlist, name);
-
 	FOR_EACH_PTR(*rlist, rel) {
 		if (cmp_relation(rel, &tmp) < 0)
 			continue;
@@ -212,20 +195,29 @@
  */
 void set_equiv(struct expression *left, struct expression *right)
 {
-	struct sm_state *right_sm, *left_sm;
+	struct sm_state *right_sm, *left_sm, *other_sm;
 	struct relation *rel;
 	char *left_name;
 	struct symbol *left_sym;
 	struct related_list *rlist;
+	char *other_name;
+	struct symbol *other_sym;
 
 	left_name = expr_to_var_sym(left, &left_sym);
 	if (!left_name || !left_sym)
 		goto free;
 
+	other_name = get_other_name_sym(left_name, left_sym, &other_sym);
+
 	right_sm = get_sm_state_expr(SMATCH_EXTRA, right);
+	if (!right_sm) {
+		struct range_list *rl;
+
+		if (!get_implied_rl(right, &rl))
+			rl = alloc_whole_rl(get_type(right));
+		right_sm = set_state_expr(SMATCH_EXTRA, right, alloc_estate_rl(rl));
+	}
 	if (!right_sm)
-		right_sm = set_state_expr(SMATCH_EXTRA, right, alloc_estate_whole(get_type(right)));
-	if (!right_sm)
 		goto free;
 
 	/* This block is because we want to preserve the implications. */
@@ -233,12 +225,24 @@
 	left_sm->name = alloc_string(left_name);
 	left_sm->sym = left_sym;
 	left_sm->state = clone_estate_cast(get_type(left), right_sm->state);
+	/* FIXME: The expression we're passing is wrong */
 	set_extra_mod_helper(left_name, left_sym, left, left_sm->state);
 	__set_sm(left_sm);
 
+	if (other_name && other_sym) {
+		other_sm = clone_sm(right_sm);
+		other_sm->name = alloc_string(other_name);
+		other_sm->sym = other_sym;
+		other_sm->state = clone_estate_cast(get_type(left), left_sm->state);
+		set_extra_mod_helper(other_name, other_sym, NULL, other_sm->state);
+		__set_sm(other_sm);
+	}
+
 	rlist = clone_related_list(estate_related(right_sm->state));
 	add_related(&rlist, right_sm->name, right_sm->sym);
 	add_related(&rlist, left_name, left_sym);
+	if (other_name && other_sym)
+		add_related(&rlist, other_name, other_sym);
 
 	FOR_EACH_PTR(rlist, rel) {
 		struct sm_state *old_sm, *new_sm;
--- old/usr/src/tools/smatch/src/smatch_estate.c	Mon Aug  5 08:38:29 2019
+++ new/usr/src/tools/smatch/src/smatch_estate.c	Mon Aug  5 08:38:29 2019
@@ -43,11 +43,16 @@
 	tmp = alloc_estate_rl(value_ranges);
 	rlist = get_shared_relations(estate_related(s1), estate_related(s2));
 	set_related(tmp, rlist);
-	if (estate_has_hard_max(s1) && estate_has_hard_max(s2))
+
+	if ((estate_has_hard_max(s1) && (!estate_rl(s2) || estate_has_hard_max(s2))) ||
+	    (estate_has_hard_max(s2) && (!estate_rl(s1) || estate_has_hard_max(s1))))
 		estate_set_hard_max(tmp);
 
 	estate_set_fuzzy_max(tmp, sval_max(estate_get_fuzzy_max(s1), estate_get_fuzzy_max(s2)));
 
+	if (estate_capped(s1) && estate_capped(s2))
+		estate_set_capped(tmp);
+
 	return tmp;
 }
 
@@ -134,6 +139,21 @@
 	return 1;
 }
 
+bool estate_capped(struct smatch_state *state)
+{
+	if (!state)
+		return false;
+	/* impossible states are capped */
+	if (!estate_rl(state))
+		return true;
+	return get_dinfo(state)->capped;
+}
+
+void estate_set_capped(struct smatch_state *state)
+{
+	get_dinfo(state)->capped = true;
+}
+
 sval_t estate_min(struct smatch_state *state)
 {
 	return rl_min(estate_rl(state));
@@ -182,6 +202,8 @@
 		return 1;
 	if (!rlists_equiv(estate_related(one), estate_related(two)))
 		return 0;
+	if (estate_capped(one) != estate_capped(two))
+		return 0;
 	if (strcmp(one->name, two->name) == 0)
 		return 1;
 	return 0;
@@ -272,6 +294,25 @@
 	return ret;
 }
 
+struct smatch_state *clone_partial_estate(struct smatch_state *state, struct range_list *rl)
+{
+	struct smatch_state *ret;
+
+	if (!state)
+		return NULL;
+
+	rl = cast_rl(estate_type(state), rl);
+
+	ret = alloc_estate_rl(rl);
+	set_related(ret, clone_related_list(estate_related(state)));
+	if (estate_has_hard_max(state))
+		estate_set_hard_max(ret);
+	if (estate_has_fuzzy_max(state))
+		estate_set_fuzzy_max(ret, estate_get_fuzzy_max(state));
+
+	return ret;
+}
+
 struct smatch_state *alloc_estate_empty(void)
 {
 	struct smatch_state *state;
@@ -365,29 +406,6 @@
 	return alloc_estate_rl(rl);
 }
 
-struct smatch_state *estate_filter_range(struct smatch_state *orig,
-				 sval_t filter_min, sval_t filter_max)
-{
-	struct range_list *rl;
-	struct smatch_state *state;
-
-	if (!orig)
-		orig = alloc_estate_whole(filter_min.type);
-
-	rl = remove_range(estate_rl(orig), filter_min, filter_max);
-	state = alloc_estate_rl(rl);
-	if (estate_has_hard_max(orig))
-		estate_set_hard_max(state);
-	if (estate_has_fuzzy_max(orig))
-		estate_set_fuzzy_max(state, estate_get_fuzzy_max(orig));
-	return state;
-}
-
-struct smatch_state *estate_filter_sval(struct smatch_state *orig, sval_t sval)
-{
-	return estate_filter_range(orig, sval, sval);
-}
-
 /*
  * One of the complications is that smatch tries to free a bunch of data at the
  * end of every function.
--- old/usr/src/tools/smatch/src/smatch_expressions.c	Mon Aug  5 08:38:30 2019
+++ new/usr/src/tools/smatch/src/smatch_expressions.c	Mon Aug  5 08:38:30 2019
@@ -165,7 +165,7 @@
 struct expression *gen_expression_from_key(struct expression *arg, const char *key)
 {
 	struct expression *ret;
-	struct token *token, *end;
+	struct token *token, *prev, *end;
 	const char *p = key;
 	char buf[4095];
 	char *alloc;
@@ -189,12 +189,15 @@
 
 	ret = arg;
 	while (token_type(token) == TOKEN_SPECIAL &&
-	       token->special == SPECIAL_DEREFERENCE) {
+	       (token->special == SPECIAL_DEREFERENCE || token->special == '.')) {
+		prev = token;
 		token = token->next;
 		if (token_type(token) != TOKEN_IDENT)
 			return NULL;
 		ret = deref_expression(ret);
-		ret = member_expression(ret, '*', token->ident);
+		ret = member_expression(ret,
+				        (prev->special == SPECIAL_DEREFERENCE) ? '*' : '.',
+					token->ident);
 		token = token->next;
 	}
 
--- old/usr/src/tools/smatch/src/smatch_extra.c	Mon Aug  5 08:38:30 2019
+++ new/usr/src/tools/smatch/src/smatch_extra.c	Mon Aug  5 08:38:30 2019
@@ -36,11 +36,12 @@
 
 static int my_id;
 static int link_id;
+extern int check_assigned_expr_id;
 
 static void match_link_modify(struct sm_state *sm, struct expression *mod_expr);
 
 struct string_list *__ignored_macros = NULL;
-static int in_warn_on_macro(void)
+int in_warn_on_macro(void)
 {
 	struct statement *stmt;
 	char *tmp;
@@ -134,62 +135,123 @@
 	return expr_to_var_sym(assigned->unop, new_sym);
 }
 
-char *get_other_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym)
+char *get_other_name_sym_from_chunk(const char *name, const char *chunk, int len, struct symbol *sym, struct symbol **new_sym)
 {
 	struct expression *assigned;
 	char *orig_name = NULL;
 	char buf[256];
-	char *ret = NULL;
-	int skip;
+	char *ret;
 
-	*new_sym = NULL;
-
-	if (!sym || !sym->ident)
-		return NULL;
-
-	ret = get_pointed_at(name, sym, new_sym);
-	if (ret)
-		return ret;
-
-	skip = strlen(sym->ident->name);
-	if (name[skip] != '-' || name[skip + 1] != '>')
-		return NULL;
-	skip += 2;
-
-	assigned = get_assigned_expr_name_sym(sym->ident->name, sym);
+	assigned = get_assigned_expr_name_sym(chunk, sym);
 	if (!assigned)
 		return NULL;
 	if (assigned->type == EXPR_CALL)
 		return map_call_to_other_name_sym(name, sym, new_sym);
-	if (assigned->type == EXPR_PREOP || assigned->op == '&') {
+	if (assigned->type == EXPR_PREOP && assigned->op == '&') {
 
 		orig_name = expr_to_var_sym(assigned, new_sym);
 		if (!orig_name || !*new_sym)
 			goto free;
 
-		snprintf(buf, sizeof(buf), "%s.%s", orig_name + 1, name + skip);
+		snprintf(buf, sizeof(buf), "%s.%s", orig_name + 1, name + len);
 		ret = alloc_string(buf);
 		free_string(orig_name);
 		return ret;
 	}
 
-	if (assigned->type != EXPR_DEREF)
-		goto free;
-
 	orig_name = expr_to_var_sym(assigned, new_sym);
 	if (!orig_name || !*new_sym)
 		goto free;
 
-	snprintf(buf, sizeof(buf), "%s->%s", orig_name, name + skip);
+	snprintf(buf, sizeof(buf), "%s->%s", orig_name, name + len);
 	ret = alloc_string(buf);
 	free_string(orig_name);
 	return ret;
-
 free:
 	free_string(orig_name);
 	return NULL;
 }
 
+static char *get_long_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym)
+{
+	struct expression *tmp;
+	struct sm_state *sm;
+	char buf[256];
+
+	/*
+	 * Just prepend the name with a different name/sym and return that.
+	 * For example, if we set "foo->bar = bar;" then we clamp "bar->baz",
+	 * that also clamps "foo->bar->baz".
+	 *
+	 */
+
+	FOR_EACH_MY_SM(check_assigned_expr_id, __get_cur_stree(), sm) {
+		tmp = sm->state->data;
+		if (!tmp || tmp->type != EXPR_SYMBOL)
+			continue;
+		if (tmp->symbol == sym)
+			goto found;
+	} END_FOR_EACH_SM(sm);
+
+	return NULL;
+
+found:
+	snprintf(buf, sizeof(buf), "%s%s", sm->name, name + tmp->symbol->ident->len);
+	*new_sym = sm->sym;
+	return alloc_string(buf);
+}
+
+char *get_other_name_sym_helper(const char *name, struct symbol *sym, struct symbol **new_sym, bool use_stack)
+{
+	char buf[256];
+	char *ret;
+	int len;
+
+	*new_sym = NULL;
+
+	if (!sym || !sym->ident)
+		return NULL;
+
+	ret = get_pointed_at(name, sym, new_sym);
+	if (ret)
+		return ret;
+
+	ret = map_long_to_short_name_sym(name, sym, new_sym, use_stack);
+	if (ret)
+		return ret;
+
+	len = snprintf(buf, sizeof(buf), "%s", name);
+	if (len >= sizeof(buf) - 2)
+		return NULL;
+
+	while (len >= 1) {
+		if (buf[len] == '>' && buf[len - 1] == '-') {
+			len--;
+			buf[len] = '\0';
+			ret = get_other_name_sym_from_chunk(name, buf, len + 2, sym, new_sym);
+			if (ret)
+				return ret;
+		}
+		len--;
+	}
+
+	ret = get_long_name_sym(name, sym, new_sym);
+	if (ret)
+		return ret;
+
+	return NULL;
+}
+
+char *get_other_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym)
+{
+	return get_other_name_sym_helper(name, sym, new_sym, true);
+}
+
+char *get_other_name_sym_nostack(const char *name, struct symbol *sym, struct symbol **new_sym)
+{
+	return get_other_name_sym_helper(name, sym, new_sym, false);
+}
+
 void set_extra_mod(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
 {
 	char *new_name;
@@ -301,8 +363,6 @@
 	FOR_EACH_PTR(estate_related(orig_state), rel) {
 		struct smatch_state *estate;
 
-		if (option_debug_related)
-			sm_msg("%s updating related %s to %s", name, rel->name, state->name);
 		estate = get_state(SMATCH_EXTRA, rel->name, rel->sym);
 		if (!estate)
 			continue;
@@ -484,6 +544,7 @@
 {
 	struct expression *iter_var;
 	struct expression *condition, *unop;
+	struct symbol *type;
 	struct sm_state *sm;
 	struct smatch_state *estate;
 	int op;
@@ -507,6 +568,11 @@
 	if (sval_cmp(estate_min(sm->state), right) < 0)
 		return NULL;
 	start = estate_max(sm->state);
+
+	type = get_type(iter_var);
+	right = sval_cast(type, right);
+	start = sval_cast(type, start);
+
 	if  (sval_cmp(start, right) <= 0)
 		return NULL;
 	if (!sval_is_max(start))
@@ -540,6 +606,7 @@
 	struct sm_state *sm;
 	struct smatch_state *estate;
 	sval_t start, end, max;
+	struct symbol *type;
 
 	iter_var = iter_expr->unop;
 	sm = get_sm_state_expr(SMATCH_EXTRA, iter_var);
@@ -547,10 +614,8 @@
 		return NULL;
 	if (!estate_get_single_value(sm->state, &start))
 		return NULL;
-	if (get_implied_max(condition->right, &end))
-		end = sval_cast(get_type(iter_var), end);
-	else
-		end = sval_type_max(get_type(iter_var));
+	if (!get_implied_value(condition->right, &end))
+		return NULL;
 
 	if (get_sm_state_expr(SMATCH_EXTRA, condition->left) != sm)
 		return NULL;
@@ -570,13 +635,18 @@
 	}
 	if (sval_cmp(end, start) < 0)
 		return NULL;
+	type = get_type(iter_var);
+	start = sval_cast(type, start);
+	end = sval_cast(type, end);
 	estate = alloc_estate_range(start, end);
 	if (get_hard_max(condition->right, &max)) {
-		estate_set_hard_max(estate);
+		if (!get_macro_name(condition->pos))
+			estate_set_hard_max(estate);
 		if (condition->op == '<' ||
 		    condition->op == SPECIAL_UNSIGNED_LT ||
 		    condition->op == SPECIAL_NOTEQUAL)
 			max.value--;
+		max = sval_cast(type, max);
 		estate_set_fuzzy_max(estate, max);
 	}
 	set_extra_expr_mod(iter_var, estate);
@@ -599,6 +669,7 @@
 		return NULL;
 	if (!get_implied_min(condition->right, &end))
 		end = sval_type_min(get_type(iter_var));
+	end = sval_cast(estate_type(sm->state), end);
 	if (get_sm_state_expr(SMATCH_EXTRA, condition->left) != sm)
 		return NULL;
 
@@ -605,7 +676,7 @@
 	switch (condition->op) {
 	case SPECIAL_NOTEQUAL:
 	case '>':
-		if (!sval_is_min(end) && !sval_is_max(end))
+		if (!sval_is_max(end))
 			end.value++;
 		break;
 	case SPECIAL_GTE:
@@ -714,6 +785,7 @@
 		limit = sval_binop(estate_min(sm->state), '-',
 				   sval_type_val(estate_type(sm->state), 1));
 	}
+	limit = sval_cast(estate_type(sm->state), limit);
 	if (!estate_has_hard_max(sm->state) && !__has_breaks()) {
 		if (iter_expr->op == SPECIAL_INCREMENT)
 			state = alloc_estate_range(estate_min(sm->state), limit);
@@ -738,10 +810,24 @@
 	set_extra_mod(sm->name, sm->sym, iter_expr, state);
 }
 
+static bool get_global_rl(const char *name, struct symbol *sym, struct range_list **rl)
+{
+	struct expression *expr;
+
+	if (!sym || !(sym->ctype.modifiers & MOD_TOPLEVEL) || !sym->ident)
+		return false;
+	if (strcmp(sym->ident->name, name) != 0)
+		return false;
+
+	expr = symbol_expression(sym);
+	return get_implied_rl(expr, rl);
+}
+
 static struct stree *unmatched_stree;
 static struct smatch_state *unmatched_state(struct sm_state *sm)
 {
 	struct smatch_state *state;
+	struct range_list *rl;
 
 	if (unmatched_stree) {
 		state = get_state_stree(unmatched_stree, SMATCH_EXTRA, sm->name, sm->sym);
@@ -750,6 +836,8 @@
 	}
 	if (parent_is_gone_var_sym(sm->name, sm->sym))
 		return alloc_estate_empty();
+	if (get_global_rl(sm->name, sm->sym, &rl))
+		return alloc_estate_rl(rl);
 	return alloc_estate_whole(estate_type(sm->state));
 }
 
@@ -815,7 +903,7 @@
 	} END_FOR_EACH_PTR(arg);
 }
 
-static int values_fit_type(struct expression *left, struct expression *right)
+int values_fit_type(struct expression *left, struct expression *right)
 {
 	struct range_list *rl;
 	struct symbol *type;
@@ -824,6 +912,8 @@
 	if (!type)
 		return 0;
 	get_absolute_rl(right, &rl);
+	if (type == rl_type(rl))
+		return 1;
 	if (type_unsigned(type) && sval_is_negative(rl_min(rl)))
 		return 0;
 	if (sval_cmp(sval_type_min(type), rl_min(rl)) > 0)
@@ -951,7 +1041,7 @@
 		goto done;
 	}
 
-	comparison = get_comparison(left, right);
+	comparison = get_comparison_no_extra(left, right);
 	if (comparison) {
 		comparison = flip_comparison(comparison);
 		get_implied_rl(left, &orig_rl);
@@ -980,34 +1070,6 @@
 	free_string(right_name);
 }
 
-static int op_remove_assign(int op)
-{
-	switch (op) {
-	case SPECIAL_ADD_ASSIGN:
-		return '+';
-	case SPECIAL_SUB_ASSIGN:
-		return '-';
-	case SPECIAL_MUL_ASSIGN:
-		return '*';
-	case SPECIAL_DIV_ASSIGN:
-		return '/';
-	case SPECIAL_MOD_ASSIGN:
-		return '%';
-	case SPECIAL_AND_ASSIGN:
-		return '&';
-	case SPECIAL_OR_ASSIGN:
-		return '|';
-	case SPECIAL_XOR_ASSIGN:
-		return '^';
-	case SPECIAL_SHL_ASSIGN:
-		return SPECIAL_LEFTSHIFT;
-	case SPECIAL_SHR_ASSIGN:
-		return SPECIAL_RIGHTSHIFT;
-	default:
-		return op;
-	}
-}
-
 static void match_assign(struct expression *expr)
 {
 	struct range_list *rl = NULL;
@@ -1017,9 +1079,6 @@
 	struct symbol *left_type;
 	struct symbol *sym;
 	char *name;
-	sval_t left_min, left_max;
-	sval_t right_min, right_max;
-	sval_t res_min, res_max;
 
 	left = strip_expr(expr->left);
 
@@ -1044,45 +1103,9 @@
 
 	left_type = get_type(left);
 
-	res_min = sval_type_min(left_type);
-	res_max = sval_type_max(left_type);
-
 	switch (expr->op) {
 	case SPECIAL_ADD_ASSIGN:
-		get_absolute_max(left, &left_max);
-		get_absolute_max(right, &right_max);
-		if (sval_binop_overflows(left_max, '+', sval_cast(left_type, right_max)))
-			break;
-		if (get_implied_min(left, &left_min) &&
-		    !sval_is_negative_min(left_min) &&
-		    get_implied_min(right, &right_min) &&
-		    !sval_is_negative_min(right_min)) {
-			res_min = sval_binop(left_min, '+', right_min);
-			res_min = sval_cast(left_type, res_min);
-		}
-		if (inside_loop())  /* we are assuming loops don't lead to wrapping */
-			break;
-		res_max = sval_binop(left_max, '+', right_max);
-		res_max = sval_cast(left_type, res_max);
-		break;
 	case SPECIAL_SUB_ASSIGN:
-		if (get_implied_max(left, &left_max) &&
-		    !sval_is_max(left_max) &&
-		    get_implied_min(right, &right_min) &&
-		    !sval_is_min(right_min)) {
-			res_max = sval_binop(left_max, '-', right_min);
-			res_max = sval_cast(left_type, res_max);
-		}
-		if (inside_loop())
-			break;
-		if (get_implied_min(left, &left_min) &&
-		    !sval_is_min(left_min) &&
-		    get_implied_max(right, &right_max) &&
-		    !sval_is_max(right_max)) {
-			res_min = sval_binop(left_min, '-', right_max);
-			res_min = sval_cast(left_type, res_min);
-		}
-		break;
 	case SPECIAL_AND_ASSIGN:
 	case SPECIAL_MOD_ASSIGN:
 	case SPECIAL_SHL_ASSIGN:
@@ -1094,15 +1117,23 @@
 		binop_expr = binop_expression(expr->left,
 					      op_remove_assign(expr->op),
 					      expr->right);
-		if (get_absolute_rl(binop_expr, &rl)) {
-			rl = cast_rl(left_type, rl);
-			set_extra_mod(name, sym, left, alloc_estate_rl(rl));
-			goto free;
+		get_absolute_rl(binop_expr, &rl);
+		rl = cast_rl(left_type, rl);
+		if (inside_loop()) {
+			if (expr->op == SPECIAL_ADD_ASSIGN)
+				add_range(&rl, rl_max(rl), sval_type_max(rl_type(rl)));
+
+			if (expr->op == SPECIAL_SUB_ASSIGN &&
+			    !sval_is_negative(rl_min(rl))) {
+				sval_t zero = { .type = rl_type(rl) };
+
+				add_range(&rl, rl_min(rl), zero);
+			}
 		}
-		break;
+		set_extra_mod(name, sym, left, alloc_estate_rl(rl));
+		goto free;
 	}
-	rl = cast_rl(left_type, alloc_rl(res_min, res_max));
-	set_extra_mod(name, sym, left, alloc_estate_rl(rl));
+	set_extra_mod(name, sym, left, alloc_estate_whole(left_type));
 free:
 	free_string(name);
 }
@@ -1234,7 +1265,14 @@
 			return;
 		set_extra_expr_nomod(expr, alloc_estate_rl(rl));
 	} else {
-		set_extra_expr_nomod(expr, alloc_estate_range(valid_ptr_min_sval, valid_ptr_max_sval));
+		struct range_list *rl;
+
+		if (get_mtag_rl(expr, &rl))
+			rl = rl_intersection(rl, valid_ptr_rl);
+		else
+			rl = clone_rl(valid_ptr_rl);
+
+		set_extra_expr_nomod(expr, alloc_estate_rl(rl));
 	}
 }
 
@@ -1302,6 +1340,7 @@
 	struct statement *stmt;
 	struct expression *cond;
 	struct smatch_state *true_state, *false_state;
+	struct symbol *type;
 	sval_t start;
 	sval_t limit;
 
@@ -1333,7 +1372,8 @@
 		return 0;
 	if (!get_implied_value(right, &limit))
 		return 0;
-
+	type = get_type(left->unop);
+	limit = sval_cast(type, limit);
 	if (sval_cmp(start, limit) > 0)
 		return 0;
 
@@ -1371,6 +1411,17 @@
 	return false;
 }
 
+static bool in_macro(struct expression *left, struct expression *right)
+{
+	if (!left || !right)
+		return 0;
+	if (left->pos.line != right->pos.line || left->pos.pos != right->pos.pos)
+		return 0;
+	if (get_macro_name(left->pos))
+		return 1;
+	return 0;
+}
+
 static void handle_comparison(struct symbol *type, struct expression *left, int op, struct expression *right)
 {
 	struct range_list *left_orig;
@@ -1453,9 +1504,9 @@
 	case SPECIAL_UNSIGNED_LT:
 	case SPECIAL_UNSIGNED_LTE:
 	case SPECIAL_LTE:
-		if (get_hard_max(right, &dummy))
+		if (get_implied_value(right, &dummy) && !in_macro(left, right))
 			estate_set_hard_max(left_true_state);
-		if (get_hard_max(left, &dummy))
+		if (get_implied_value(left, &dummy) && !in_macro(left, right))
 			estate_set_hard_max(right_false_state);
 		break;
 	case '>':
@@ -1462,9 +1513,9 @@
 	case SPECIAL_UNSIGNED_GT:
 	case SPECIAL_UNSIGNED_GTE:
 	case SPECIAL_GTE:
-		if (get_hard_max(left, &dummy))
+		if (get_implied_value(left, &dummy) && !in_macro(left, right))
 			estate_set_hard_max(right_true_state);
-		if (get_hard_max(right, &dummy))
+		if (get_implied_value(right, &dummy) && !in_macro(left, right))
 			estate_set_hard_max(left_false_state);
 		break;
 	}
@@ -1598,6 +1649,45 @@
 	return 0;
 }
 
+static int flip_op(int op)
+{
+	/* We only care about simple math */
+	switch (op) {
+	case '+':
+		return '-';
+	case '-':
+		return '+';
+	case '*':
+		return '/';
+	}
+	return 0;
+}
+
+static void move_known_to_rl(struct expression **expr_p, struct range_list **rl_p)
+{
+	struct expression *expr = *expr_p;
+	struct range_list *rl = *rl_p;
+	sval_t sval;
+
+	if (!is_simple_math(expr))
+		return;
+
+	if (get_implied_value(expr->right, &sval)) {
+		*expr_p = expr->left;
+		*rl_p = rl_binop(rl, flip_op(expr->op), alloc_rl(sval, sval));
+		move_known_to_rl(expr_p, rl_p);
+		return;
+	}
+	if (expr->op == '-')
+		return;
+	if (get_implied_value(expr->left, &sval)) {
+		*expr_p = expr->right;
+		*rl_p = rl_binop(rl, flip_op(expr->op), alloc_rl(sval, sval));
+		move_known_to_rl(expr_p, rl_p);
+		return;
+	}
+}
+
 static void move_known_values(struct expression **left_p, struct expression **right_p)
 {
 	struct expression *left = *left_p;
@@ -1706,33 +1796,12 @@
 {
 	struct expression *left = strip_expr(expr->left);
 	struct expression *right = strip_expr(expr->right);
-	sval_t sval;
 
-	/*
-	 * fixme: think about this harder. We should always be trying to limit
-	 * the non-call side as well.  If we can't determine the limitter does
-	 * that mean we aren't querying the database and are missing important
-	 * information?
-	 */
-
-	if (left->type == EXPR_CALL) {
-		if (get_implied_value(left, &sval)) {
-			handle_comparison(get_type(expr), left, expr->op, right);
-			return 1;
-		}
+	if (left->type == EXPR_CALL || right->type == EXPR_CALL) {
 		function_comparison(left, expr->op, right);
 		return 1;
 	}
 
-	if (right->type == EXPR_CALL) {
-		if (get_implied_value(right, &sval)) {
-			handle_comparison(get_type(expr), left, expr->op, right);
-			return 1;
-		}
-		function_comparison(left, expr->op, right);
-		return 1;
-	}
-
 	return 0;
 }
 
@@ -1772,6 +1841,10 @@
 	get_absolute_min(left->right, &right_min);
 	min = sval_binop(left_min, '+', right_min);
 
+	type = get_type(left);
+	min = sval_cast(type, min);
+	max = sval_cast(type, max);
+
 	set_extra_chunk_true_false(left, NULL, alloc_estate_range(min, max));
 	return 1;
 }
@@ -1870,6 +1943,51 @@
 	return ret;
 }
 
+static bool handle_bit_test(struct expression *expr)
+{
+	struct range_list *orig_rl, *rl;
+	struct expression *shift, *mask, *var;
+	struct bit_info *bit_info;
+	sval_t sval;
+	sval_t high = { .type = &int_ctype };
+	sval_t low = { .type = &int_ctype };
+
+	shift = strip_expr(expr->right);
+	mask = strip_expr(expr->left);
+	if (shift->type != EXPR_BINOP || shift->op != SPECIAL_LEFTSHIFT) {
+		shift = strip_expr(expr->left);
+		mask = strip_expr(expr->right);
+		if (shift->type != EXPR_BINOP || shift->op != SPECIAL_LEFTSHIFT)
+			return false;
+	}
+	if (!get_implied_value(shift->left, &sval) || sval.value != 1)
+		return false;
+	var = strip_expr(shift->right);
+
+	bit_info = get_bit_info(mask);
+	if (!bit_info)
+		return false;
+	if (!bit_info->possible)
+		return false;
+
+	get_absolute_rl(var, &orig_rl);
+	if (sval_is_negative(rl_min(orig_rl)) ||
+	    rl_max(orig_rl).uvalue > type_bits(get_type(shift->left)))
+		return false;
+
+	low.value = ffsll(bit_info->possible);
+	high.value = sm_fls64(bit_info->possible);
+	rl = alloc_rl(low, high);
+	rl = cast_rl(get_type(var), rl);
+	rl = rl_intersection(orig_rl, rl);
+	if (!rl)
+		return false;
+
+	set_extra_expr_true_false(shift->right, alloc_estate_rl(rl), NULL);
+
+	return true;
+}
+
 static void handle_AND_op(struct expression *var, sval_t known)
 {
 	struct range_list *orig_rl;
@@ -1916,6 +2034,9 @@
 {
 	sval_t known;
 
+	if (handle_bit_test(expr))
+		return;
+
 	if (get_implied_value(expr->left, &known))
 		handle_AND_op(expr->right, known);
 	else if (get_implied_value(expr->right, &known))
@@ -1928,7 +2049,7 @@
 	struct range_list *true_rl;
 	struct range_list *false_rl = NULL;
 	sval_t right;
-	sval_t zero;
+	sval_t zero = { 0, };
 
 	if (!get_implied_value(expr->right, &right) || right.value == 0)
 		return;
@@ -1988,11 +2109,6 @@
 /* this is actually hooked from smatch_implied.c...  it's hacky, yes */
 void __extra_match_condition(struct expression *expr)
 {
-	struct smatch_state *pre_state;
-	struct smatch_state *true_state;
-	struct smatch_state *false_state;
-	struct range_list *pre_rl;
-
 	expr = strip_expr(expr);
 	switch (expr->type) {
 	case EXPR_CALL:
@@ -2000,27 +2116,9 @@
 		return;
 	case EXPR_PREOP:
 	case EXPR_SYMBOL:
-	case EXPR_DEREF: {
-		sval_t zero;
-
-		zero = sval_blank(expr);
-		zero.value = 0;
-
-		pre_state = get_extra_state(expr);
-		if (estate_is_empty(pre_state))
-			return;
-		if (pre_state)
-			pre_rl = estate_rl(pre_state);
-		else
-			get_absolute_rl(expr, &pre_rl);
-		if (possibly_true_rl(pre_rl, SPECIAL_EQUAL, rl_zero()))
-			false_state = alloc_estate_sval(zero);
-		else
-			false_state = alloc_estate_empty();
-		true_state = alloc_estate_rl(remove_range(pre_rl, zero, zero));
-		set_extra_expr_true_false(expr, true_state, false_state);
+	case EXPR_DEREF:
+		handle_comparison(get_type(expr), expr, SPECIAL_NOTEQUAL, zero_expr());
 		return;
-	}
 	case EXPR_COMPARE:
 		match_comparison(expr);
 		return;
@@ -2153,9 +2251,45 @@
 	return 0;
 }
 
-static int filter_unused_kzalloc_info(struct expression *call, int param, char *printed_name, struct sm_state *sm)
+static int is_kzalloc_info(struct sm_state *sm)
 {
 	sval_t sval;
+
+	/*
+	 * kzalloc() information is treated as special because so there is just
+	 * a lot of stuff initialized to zero and it makes building the database
+	 * take hours and hours.
+	 *
+	 * In theory, we should just remove this line and not pass any unused
+	 * information, but I'm not sure enough that this code works so I want
+	 * to hold off on that for now.
+	 */
+	if (!estate_get_single_value(sm->state, &sval))
+		return 0;
+	if (sval.value != 0)
+		return 0;
+	return 1;
+}
+
+static int is_really_long(struct sm_state *sm)
+{
+	const char *p;
+	int cnt = 0;
+
+	p = sm->name;
+	while ((p = strstr(p, "->"))) {
+		p += 2;
+		cnt++;
+	}
+
+	if (cnt < 3 ||
+	    strlen(sm->name) < 40)
+		return 0;
+	return 1;
+}
+
+static int filter_unused_param_value_info(struct expression *call, int param, char *printed_name, struct sm_state *sm)
+{
 	int found = 0;
 
 	/* for function pointers assume everything is used */
@@ -2170,16 +2304,7 @@
 	if (!call->fn->symbol)
 		return 0;
 
-	/*
-	 * kzalloc() information is treated as special because so there is just
-	 * a lot of stuff initialized to zero and it makes building the database
-	 * take hours and hours.
-	 *
-	 * In theory, we should just remove this line and not pass any unused
-	 * information, but I'm not sure enough that this code works so I want
-	 * to hold off on that for now.
-	 */
-	if (!estate_get_single_value(sm->state, &sval) || sval.value != 0)
+	if (!is_kzalloc_info(sm) && !is_really_long(sm))
 		return 0;
 
 	run_sql(&param_used_callback, &found,
@@ -2246,50 +2371,68 @@
 static void struct_member_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
 {
 	struct range_list *rl;
+	sval_t dummy;
 
 	if (estate_is_whole(sm->state))
 		return;
-	if (filter_unused_kzalloc_info(call, param, printed_name, sm))
+	if (filter_unused_param_value_info(call, param, printed_name, sm))
 		return;
 	rl = estate_rl(sm->state);
 	rl = intersect_with_real_abs_var_sym(sm->name, sm->sym, rl);
 	sql_insert_caller_info(call, PARAM_VALUE, param, printed_name, show_rl(rl));
-	if (estate_has_fuzzy_max(sm->state))
-		sql_insert_caller_info(call, FUZZY_MAX, param, printed_name,
-				       sval_to_str(estate_get_fuzzy_max(sm->state)));
+	if (!estate_get_single_value(sm->state, &dummy)) {
+		if (estate_has_hard_max(sm->state))
+			sql_insert_caller_info(call, HARD_MAX, param, printed_name,
+					       sval_to_str(estate_max(sm->state)));
+		if (estate_has_fuzzy_max(sm->state))
+			sql_insert_caller_info(call, FUZZY_MAX, param, printed_name,
+					       sval_to_str(estate_get_fuzzy_max(sm->state)));
+	}
 }
 
 static void returned_struct_members(int return_id, char *return_ranges, struct expression *expr)
 {
 	struct symbol *returned_sym;
+	char *returned_name;
 	struct sm_state *sm;
-	const char *param_name;
 	char *compare_str;
-	char buf[256];
+	char name_buf[256];
+	char val_buf[256];
+	int len;
 
-	returned_sym = expr_to_sym(expr);
-	if (!returned_sym)
+	// FIXME handle *$
+
+	if (!is_pointer(expr))
 		return;
 
+	returned_name = expr_to_var_sym(expr, &returned_sym);
+	if (!returned_name || !returned_sym)
+		goto free;
+	len = strlen(returned_name);
+
 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
 		if (!estate_rl(sm->state))
 			continue;
 		if (returned_sym != sm->sym)
 			continue;
-
-		param_name = get_param_name(sm);
-		if (!param_name)
+		if (strncmp(returned_name, sm->name, len) != 0)
 			continue;
-		if (strcmp(param_name, "$") == 0)
+		if (sm->name[len] != '-')
 			continue;
+
+		snprintf(name_buf, sizeof(name_buf), "$%s", sm->name + len);
+
 		compare_str = name_sym_to_param_comparison(sm->name, sm->sym);
 		if (!compare_str && estate_is_whole(sm->state))
 			continue;
-		snprintf(buf, sizeof(buf), "%s%s", sm->state->name, compare_str ?: "");
+		snprintf(val_buf, sizeof(val_buf), "%s%s", sm->state->name, compare_str ?: "");
 
 		sql_insert_return_states(return_id, return_ranges, PARAM_VALUE,
-					 -1, param_name, buf);
+					 -1, name_buf, val_buf);
 	} END_FOR_EACH_SM(sm);
+
+free:
+	free_string(returned_name);
 }
 
 static void db_limited_before(void)
@@ -2302,18 +2445,6 @@
 	free_stree(&unmatched_stree);
 }
 
-static int rl_fits_in_type(struct range_list *rl, struct symbol *type)
-{
-	if (type_bits(rl_type(rl)) <= type_bits(type))
-		return 1;
-	if (sval_cmp(rl_max(rl), sval_type_max(type)) > 0)
-		return 0;
-	if (sval_is_negative(rl_min(rl)) &&
-	    sval_cmp(rl_min(rl), sval_type_min(type)) < 0)
-		return 0;
-	return 1;
-}
-
 static int basically_the_same(struct range_list *orig, struct range_list *new)
 {
 	if (rl_equiv(orig, new))
@@ -2363,8 +2494,8 @@
 	struct range_list *rl;
 	struct range_list *limit;
 	struct range_list *new;
-	char *tmp_name;
-	struct symbol *tmp_sym;
+	char *other_name;
+	struct symbol *other_sym;
 
 	while (expr->type == EXPR_ASSIGNMENT)
 		expr = strip_expr(expr->right);
@@ -2375,6 +2506,14 @@
 	if (!arg)
 		return;
 
+	if (strcmp(key, "$") == 0)
+		compare_type = get_arg_type(expr->fn, param);
+	else
+		compare_type = get_member_type_from_key(arg, key);
+
+	call_results_to_rl(expr, compare_type, value, &limit);
+	if (strcmp(key, "$") == 0)
+		move_known_to_rl(&arg, &limit);
 	name = get_chunk_from_key(arg, key, &sym, &vsl);
 	if (!name)
 		return;
@@ -2381,11 +2520,6 @@
 	if (op != PARAM_LIMIT && !sym)
 		goto free;
 
-	if (strcmp(key, "$") == 0)
-		compare_type = get_arg_type(expr->fn, param);
-	else
-		compare_type = get_member_type_from_key(arg, key);
-
 	sm = get_sm_state(SMATCH_EXTRA, name, sym);
 	if (sm)
 		rl = estate_rl(sm->state);
@@ -2395,7 +2529,6 @@
 	if (op == PARAM_LIMIT && !rl_fits_in_type(rl, compare_type))
 		goto free;
 
-	call_results_to_rl(expr, compare_type, value, &limit);
 	new = rl_intersection(rl, limit);
 
 	var_type = get_member_type_from_key(arg, key);
@@ -2402,14 +2535,9 @@
 	new = cast_rl(var_type, new);
 
 	/* We want to preserve the implications here */
-	if (sm && basically_the_same(estate_rl(sm->state), new))
+	if (sm && basically_the_same(rl, new))
 		goto free;
-	tmp_name = map_long_to_short_name_sym(name, sym, &tmp_sym);
-	if (tmp_name && tmp_sym) {
-		free_string(name);
-		name = tmp_name;
-		sym = tmp_sym;
-	}
+	other_name = get_other_name_sym(name, sym, &other_sym);
 
 	if (op == PARAM_LIMIT)
 		set_extra_nomod_vsl(name, sym, vsl, NULL, alloc_estate_rl(new));
@@ -2416,6 +2544,13 @@
 	else
 		set_extra_mod(name, sym, NULL, alloc_estate_rl(new));
 
+	if (other_name && other_sym) {
+		if (op == PARAM_LIMIT)
+			set_extra_nomod_vsl(other_name, other_sym, vsl, NULL, alloc_estate_rl(new));
+		else
+			set_extra_mod(other_name, other_sym, NULL, alloc_estate_rl(new));
+	}
+
 	if (op == PARAM_LIMIT && arg->type == EXPR_BINOP)
 		db_param_limit_binops(arg, key, new);
 free:
@@ -2435,8 +2570,9 @@
 static void db_param_add_set(struct expression *expr, int param, char *key, char *value, enum info_type op)
 {
 	struct expression *arg;
-	char *name, *tmp_name;
-	struct symbol *sym, *tmp_sym;
+	char *name;
+	char *other_name = NULL;
+	struct symbol *sym, *other_sym;
 	struct symbol *param_type, *arg_type;
 	struct smatch_state *state;
 	struct range_list *new = NULL;
@@ -2468,14 +2604,12 @@
 	else
 		new = rl_union(new, added);
 
-	tmp_name = map_long_to_short_name_sym_nostack(name, sym, &tmp_sym);
-	if (tmp_name && tmp_sym) {
-		free_string(name);
-		name = tmp_name;
-		sym = tmp_sym;
-	}
+	other_name = get_other_name_sym_nostack(name, sym, &other_sym);
 	set_extra_mod(name, sym, NULL, alloc_estate_rl(new));
+	if (other_name && other_sym)
+		set_extra_mod(other_name, other_sym, NULL, alloc_estate_rl(new));
 free:
+	free_string(other_name);
 	free_string(name);
 }
 
@@ -2493,6 +2627,24 @@
 	in_param_set = false;
 }
 
+static void match_lost_param(struct expression *call, int param)
+{
+	struct expression *arg;
+
+	if (is_const_param(call->fn, param))
+		return;
+
+	arg = get_argument_from_call_expr(call->args, param);
+	if (!arg)
+		return;
+
+	arg = strip_expr(arg);
+	if (arg->type == EXPR_PREOP && arg->op == '&')
+		set_extra_expr_mod(arg->unop, alloc_estate_whole(get_type(arg->unop)));
+	else
+		; /* if pointer then set struct members, maybe?*/
+}
+
 static void db_param_value(struct expression *expr, int param, char *key, char *value)
 {
 	struct expression *call;
@@ -2528,6 +2680,7 @@
 	struct range_list *rl = NULL;
 	struct expression *arg;
 	struct symbol *type;
+	sval_t dummy;
 	int i = 0;
 
 	FOR_EACH_PTR(expr->args, arg) {
@@ -2541,6 +2694,10 @@
 			sql_insert_caller_info(expr, PARAM_VALUE, i, "$", show_rl(rl));
 		}
 		state = get_state_expr(SMATCH_EXTRA, arg);
+		if (!estate_get_single_value(state, &dummy) && estate_has_hard_max(state)) {
+			sql_insert_caller_info(expr, HARD_MAX, i, "$",
+					       sval_to_str(estate_max(state)));
+		}
 		if (estate_has_fuzzy_max(state)) {
 			sql_insert_caller_info(expr, FUZZY_MAX, i, "$",
 					       sval_to_str(estate_get_fuzzy_max(state)));
@@ -2551,20 +2708,24 @@
 
 static void set_param_value(const char *name, struct symbol *sym, char *key, char *value)
 {
+	struct expression *expr;
 	struct range_list *rl = NULL;
 	struct smatch_state *state;
 	struct symbol *type;
 	char fullname[256];
+	char *key_orig = key;
+	bool add_star = false;
 	sval_t dummy;
 
-	if (strcmp(key, "*$") == 0)
-		snprintf(fullname, sizeof(fullname), "*%s", name);
-	else if (strncmp(key, "$", 1) == 0)
-		snprintf(fullname, 256, "%s%s", name, key + 1);
-	else
-		return;
+	if (key[0] == '*') {
+		add_star = true;
+		key++;
+	}
 
-	type = get_member_type_from_key(symbol_expression(sym), key);
+	snprintf(fullname, 256, "%s%s%s", add_star ? "*" : "", name, key + 1);
+
+	expr = symbol_expression(sym);
+	type = get_member_type_from_key(expr, key_orig);
 	str_to_rl(type, value, &rl);
 	state = alloc_estate_rl(rl);
 	if (estate_get_single_value(state, &dummy))
@@ -2572,7 +2733,7 @@
 	set_state(SMATCH_EXTRA, fullname, sym, state);
 }
 
-static void set_param_hard_max(const char *name, struct symbol *sym, char *key, char *value)
+static void set_param_fuzzy_max(const char *name, struct symbol *sym, char *key, char *value)
 {
 	struct range_list *rl = NULL;
 	struct smatch_state *state;
@@ -2590,7 +2751,7 @@
 	state = get_state(SMATCH_EXTRA, fullname, sym);
 	if (!state)
 		return;
-	type = get_member_type_from_key(symbol_expression(sym), key);
+	type = estate_type(state);
 	str_to_rl(type, value, &rl);
 	if (!rl_to_sval(rl, &max))
 		return;
@@ -2597,6 +2758,24 @@
 	estate_set_fuzzy_max(state, max);
 }
 
+static void set_param_hard_max(const char *name, struct symbol *sym, char *key, char *value)
+{
+	struct smatch_state *state;
+	char fullname[256];
+
+	if (strcmp(key, "*$") == 0)
+		snprintf(fullname, sizeof(fullname), "*%s", name);
+	else if (strncmp(key, "$", 1) == 0)
+		snprintf(fullname, 256, "%s%s", name, key + 1);
+	else
+		return;
+
+	state = get_state(SMATCH_EXTRA, fullname, sym);
+	if (!state)
+		return;
+	estate_set_hard_max(state);
+}
+
 struct sm_state *get_extra_sm_state(struct expression *expr)
 {
 	char *name;
@@ -2627,15 +2806,18 @@
 {
 	my_id = id;
 
+	set_dynamic_states(my_id);
 	add_merge_hook(my_id, &merge_estates);
 	add_unmatched_state_hook(my_id, &unmatched_state);
 	select_caller_info_hook(set_param_value, PARAM_VALUE);
-	select_caller_info_hook(set_param_hard_max, FUZZY_MAX);
+	select_caller_info_hook(set_param_fuzzy_max, FUZZY_MAX);
+	select_caller_info_hook(set_param_hard_max, HARD_MAX);
 	select_return_states_before(&db_limited_before);
 	select_return_states_hook(PARAM_LIMIT, &db_param_limit);
 	select_return_states_hook(PARAM_FILTER, &db_param_filter);
 	select_return_states_hook(PARAM_ADD, &db_param_add);
 	select_return_states_hook(PARAM_SET, &db_param_set);
+	add_lost_param_hook(&match_lost_param);
 	select_return_states_hook(PARAM_VALUE, &db_param_value);
 	select_return_states_after(&db_limited_after);
 }
@@ -2663,6 +2845,7 @@
 void register_smatch_extra_links(int id)
 {
 	link_id = id;
+	set_dynamic_states(link_id);
 }
 
 void register_smatch_extra_late(int id)
--- old/usr/src/tools/smatch/src/smatch_extra.h	Mon Aug  5 08:38:31 2019
+++ new/usr/src/tools/smatch/src/smatch_extra.h	Mon Aug  5 08:38:31 2019
@@ -30,6 +30,7 @@
 	struct range_list *value_ranges;
 	sval_t fuzzy_max;
 	unsigned int hard_max:1;
+	unsigned int capped:1;
 };
 DECLARE_ALLOCATOR(data_info);
 
@@ -41,11 +42,13 @@
 char *show_rl(struct range_list *list);
 int str_to_comparison_arg(const char *c, struct expression *call, int *comparison, struct expression **arg);
 void str_to_rl(struct symbol *type, char *value, struct range_list **rl);
-void call_results_to_rl(struct expression *call, struct symbol *type, char *value, struct range_list **rl);
+void call_results_to_rl(struct expression *call, struct symbol *type, const char *value, struct range_list **rl);
 
 struct data_range *alloc_range(sval_t min, sval_t max);
 struct data_range *alloc_range_perm(sval_t min, sval_t max);
 
+int rl_fits_in_type(struct range_list *rl, struct symbol *type);
+
 struct range_list *alloc_rl(sval_t min, sval_t max);
 struct range_list *clone_rl(struct range_list *list);
 struct range_list *clone_rl_permanent(struct range_list *list);
@@ -72,6 +75,7 @@
 
 int rl_equiv(struct range_list *one, struct range_list *two);
 int is_whole_rl(struct range_list *rl);
+int is_unknown_ptr(struct range_list *rl);
 int is_whole_rl_non_zero(struct range_list *rl);
 int estate_is_unknown(struct smatch_state *state);
 
@@ -80,7 +84,6 @@
 int rl_to_sval(struct range_list *rl, sval_t *sval);
 struct symbol *rl_type(struct range_list *rl);
 
-struct range_list *rl_invert(struct range_list *orig);
 struct range_list *rl_filter(struct range_list *rl, struct range_list *filter);
 struct range_list *rl_intersection(struct range_list *one, struct range_list *two);
 struct range_list *rl_union(struct range_list *one, struct range_list *two);
@@ -117,6 +120,7 @@
 struct smatch_state *alloc_estate_whole(struct symbol *type);
 struct smatch_state *clone_estate(struct smatch_state *state);
 struct smatch_state *clone_estate_cast(struct symbol *type, struct smatch_state *state);
+struct smatch_state *clone_partial_estate(struct smatch_state *state, struct range_list *rl);
 
 struct smatch_state *merge_estates(struct smatch_state *s1, struct smatch_state *s2);
 
@@ -140,12 +144,13 @@
 void estate_set_hard_max(struct smatch_state *state);
 void estate_clear_hard_max(struct smatch_state *state);
 int estate_get_hard_max(struct smatch_state *state, sval_t *sval);
+bool estate_capped(struct smatch_state *state);
+void estate_set_capped(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);
 
 struct smatch_state *estate_filter_sval(struct smatch_state *orig, sval_t filter);
-struct smatch_state *estate_filter_range(struct smatch_state *orig, sval_t filter_min, sval_t filter_max);
 struct data_info *clone_dinfo_perm(struct data_info *dinfo);
 struct smatch_state *clone_estate_perm(struct smatch_state *state);
 
--- old/usr/src/tools/smatch/src/smatch_flow.c	Mon Aug  5 08:38:31 2019
+++ new/usr/src/tools/smatch/src/smatch_flow.c	Mon Aug  5 08:38:31 2019
@@ -76,30 +76,23 @@
 struct symbol *cur_func_sym = NULL;
 struct stree *global_states;
 
-long long valid_ptr_min = 4096;
-long long valid_ptr_max = 2117777777;
-sval_t valid_ptr_min_sval = {
+const unsigned long valid_ptr_min = 4096;
+unsigned long valid_ptr_max = ULONG_MAX & ~(MTAG_OFFSET_MASK);
+const sval_t valid_ptr_min_sval = {
 	.type = &ptr_ctype,
 	{.value = 4096},
 };
 sval_t valid_ptr_max_sval = {
 	.type = &ptr_ctype,
-	{.value = LONG_MAX - 100000},
+	{.value = ULONG_MAX & ~(MTAG_OFFSET_MASK)},
 };
 struct range_list *valid_ptr_rl;
 
-static void set_valid_ptr_max(void)
+void alloc_valid_ptr_rl(void)
 {
-	if (type_bits(&ptr_ctype) == 32)
-		valid_ptr_max = 2117777777;
-	else if (type_bits(&ptr_ctype) == 64)
-		valid_ptr_max = 2117777777777777777LL;
-
+	valid_ptr_max = sval_type_max(&ulong_ctype).value & ~(MTAG_OFFSET_MASK);
 	valid_ptr_max_sval.value = valid_ptr_max;
-}
 
-static void alloc_valid_ptr_rl(void)
-{
 	valid_ptr_rl = alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
 	valid_ptr_rl = cast_rl(&ptr_ctype, valid_ptr_rl);
 	valid_ptr_rl = clone_rl_permanent(valid_ptr_rl);
@@ -504,7 +497,6 @@
 		break;
 	case EXPR_OFFSETOF:
 	case EXPR_ALIGNOF:
-		evaluate_expression(expr);
 		break;
 	case EXPR_CONDITIONAL:
 	case EXPR_SELECT:
@@ -878,9 +870,14 @@
 	return ms_since(&fn_start_time) / 1000;
 }
 
-static int taking_too_long(void)
+/*
+ * This defaults to 60 * 5 == 5 minutes, so we'll just multiply
+ * whatever we're given by 5.
+ */
+bool taking_too_long(void)
 {
-	if ((ms_since(&outer_fn_start_time) / 1000) > 60 * 5) /* five minutes */
+	if (option_timeout &&
+	    (ms_since(&outer_fn_start_time) / 1000) > option_timeout * 5)
 		return 1;
 	return 0;
 }
@@ -1908,9 +1905,8 @@
 		sm_fatal("Error:  Cannot open %s", buf);
 }
 
-void smatch(int argc, char **argv)
+void smatch(struct string_list *filelist)
 {
-	struct string_list *filelist = NULL;
 	struct symbol_list *sym_list;
 	struct timeval stop, start;
 	char *path;
@@ -1918,9 +1914,6 @@
 
 	gettimeofday(&start, NULL);
 
-	sparse_initialize(argc, argv, &filelist);
-	set_valid_ptr_max();
-	alloc_valid_ptr_rl();
 	FOR_EACH_PTR_NOTAG(filelist, base_file) {
 		path = getcwd(NULL, 0);
 		free(full_base_file);
@@ -1940,6 +1933,7 @@
 	gettimeofday(&stop, NULL);
 
 	set_position(last_pos);
+	final_pass = 1;
 	if (option_time)
 		sm_msg("time: %lu", stop.tv_sec - start.tv_sec);
 	if (option_mem)
--- old/usr/src/tools/smatch/src/smatch_function_hooks.c	Mon Aug  5 08:38:31 2019
+++ new/usr/src/tools/smatch/src/smatch_function_hooks.c	Mon Aug  5 08:38:31 2019
@@ -139,6 +139,16 @@
 	add_callback(func_hash, look_for, cb);
 }
 
+void return_implies_state_sval(const char *look_for, sval_t start, sval_t end,
+			 implication_hook *call_back, void *info)
+{
+	struct fcall_back *cb;
+
+	cb = alloc_fcall_back(RANGED_CALL, call_back, info);
+	cb->range = alloc_range_perm(start, end);
+	add_callback(func_hash, look_for, cb);
+}
+
 void select_return_states_hook(int type, return_implies_hook *callback)
 {
 	struct return_implies_callback *cb = __alloc_return_implies_callback(0);
@@ -246,6 +256,7 @@
 	struct stree *final_states = NULL;
 	struct range_list *handled_ranges = NULL;
 	struct call_back_list *same_range_call_backs = NULL;
+	struct range_list *rl;
 	int handled = 0;
 
 	if (!call_backs)
@@ -268,7 +279,9 @@
 		call_ranged_call_backs(same_range_call_backs, fn, expr->right, expr);
 		__free_ptr_list((struct ptr_list **)&same_range_call_backs);
 
-		estate = alloc_estate_range(tmp->range->min, tmp->range->max);
+		rl = alloc_rl(tmp->range->min, tmp->range->max);
+		rl = cast_rl(get_type(expr->left), rl);
+		estate = alloc_estate_rl(rl);
 		set_extra_mod(var_name, sym, expr->left, estate);
 
 		tmp_stree = __pop_fake_cur_stree();
@@ -362,7 +375,7 @@
 
 static bool fake_a_param_assignment(struct expression *expr, const char *return_str)
 {
-	struct expression *arg, *left, *right, *fake_assign;
+	struct expression *arg, *left, *right, *tmp, *fake_assign;
 	char *p;
 	int param;
 	char buf[256];
@@ -402,6 +415,12 @@
 	arg = get_argument_from_call_expr(right->args, param);
 	if (!arg)
 		return false;
+
+	/* There should be a get_other_name() function which returns an expr */
+	tmp = get_assigned_expr(arg);
+	if (tmp)
+		arg = tmp;
+
 	/*
 	 * This is a sanity check to prevent side effects from evaluating stuff
 	 * twice.
@@ -421,8 +440,9 @@
 	return true;
 }
 
-static void set_return_state(struct expression *expr, struct db_callback_info *db_info)
+static void set_return_assign_state(struct db_callback_info *db_info)
 {
+	struct expression *expr = db_info->expr->left;
 	struct smatch_state *state;
 
 	if (!db_info->ret_state)
@@ -435,6 +455,20 @@
 	db_info->ret_str = NULL;
 }
 
+static void set_other_side_state(struct db_callback_info *db_info)
+{
+	struct expression *expr = db_info->var_expr;
+	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))));
+	set_extra_expr_nomod(expr, state);
+	db_info->ret_state = NULL;
+	db_info->ret_str = NULL;
+}
+
 static void handle_ret_equals_param(char *ret_string, struct range_list *rl, struct expression *call)
 {
 	char *str;
@@ -561,7 +595,7 @@
 	struct range_list *var_rl = db_info->rl;
 	struct range_list *ret_range;
 	int type, param;
-	char *key, *value;
+	char *ret_str, *key, *value;
 	struct return_implies_callback *tmp;
 	struct stree *stree;
 	int return_id;
@@ -571,6 +605,7 @@
 		return 0;
 
 	return_id = atoi(argv[0]);
+	ret_str = argv[1];
 	type = atoi(argv[2]);
 	param = atoi(argv[3]);
 	key = argv[4];
@@ -578,7 +613,7 @@
 
 	db_info->has_states = 1;
 	if (db_info->prev_return_id != -1 && type == INTERNAL) {
-		set_return_state(db_info->var_expr, db_info);
+		set_other_side_state(db_info);
 		stree = __pop_fake_cur_stree();
 
 		if (!db_info->cull)
@@ -603,7 +638,7 @@
 		return 0;
 	}
 
-	call_results_to_rl(db_info->expr, get_type(strip_expr(db_info->expr)), argv[1], &ret_range);
+	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);
 	if (!ret_range)
 		ret_range = alloc_whole_rl(get_type(db_info->expr));
@@ -628,13 +663,13 @@
 		filter_by_comparison(&ret_range, flip_comparison(negate_comparison(comparison)), var_rl);
 	}
 
-	handle_ret_equals_param(argv[1], ret_range, db_info->expr);
+	handle_ret_equals_param(ret_str, ret_range, db_info->expr);
 
 	if (type == INTERNAL) {
 		set_state(-1, "unnull_path", NULL, &true_state);
-		__add_return_comparison(strip_expr(db_info->expr), argv[1]);
-		__add_return_to_param_mapping(db_info->expr, argv[1]);
-		store_return_state(db_info, argv[1], alloc_estate_rl(clone_rl(var_rl)));
+		__add_return_comparison(strip_expr(db_info->expr), ret_str);
+		__add_return_to_param_mapping(db_info->expr, ret_str);
+		store_return_state(db_info, ret_str, alloc_estate_rl(clone_rl(var_rl)));
 	}
 
 	FOR_EACH_PTR(db_info->callbacks, tmp) {
@@ -687,12 +722,10 @@
 	__push_fake_cur_stree();
 	sql_select_return_states("return_id, return, type, parameter, key, value",
 				 call_expr, db_compare_callback, &db_info);
-	set_return_state(db_info.var_expr, &db_info);
+	set_other_side_state(&db_info);
 	stree = __pop_fake_cur_stree();
-	if (!db_info.cull) {
-		set_return_state(db_info.var_expr, &db_info);
+	if (!db_info.cull)
 		merge_fake_stree(&db_info.stree, stree);
-	}
 	free_stree(&stree);
 	true_states = db_info.stree;
 	if (!true_states && db_info.has_states) {
@@ -714,11 +747,10 @@
 	__push_fake_cur_stree();
 	sql_select_return_states("return_id, return, type, parameter, key, value", call_expr,
 			db_compare_callback, &db_info);
+	set_other_side_state(&db_info);
 	stree = __pop_fake_cur_stree();
-	if (!db_info.cull) {
-		set_return_state(db_info.var_expr, &db_info);
+	if (!db_info.cull)
 		merge_fake_stree(&db_info.stree, stree);
-	}
 	free_stree(&stree);
 	false_states = db_info.stree;
 	if (!false_states && db_info.has_states) {
@@ -806,18 +838,14 @@
 
 	call_backs = search_callback(func_hash, fn);
 	FOR_EACH_PTR(call_backs, tmp) {
-		struct range_list *range_rl = NULL;
+		struct range_list *range_rl;
 
 		if (tmp->type != RANGED_CALL)
 			continue;
-		add_range(&range_rl, tmp->range->min, tmp->range->max);
+		range_rl = alloc_rl(tmp->range->min, tmp->range->max);
 		range_rl = cast_rl(estate_type(db_info->ret_state), range_rl);
-		if (possibly_true_rl(range_rl, SPECIAL_EQUAL, estate_rl(db_info->ret_state))) {
-			if (!possibly_true_rl(rl_invert(range_rl), SPECIAL_EQUAL, estate_rl(db_info->ret_state)))
-				(tmp->u.ranged)(fn, expr, db_info->expr, tmp->info);
-			else
-				db_info->handled = -1;
-		}
+		if (possibly_true_rl(range_rl, SPECIAL_EQUAL, estate_rl(db_info->ret_state)))
+			(tmp->u.ranged)(fn, expr, db_info->expr, tmp->info);
 	} END_FOR_EACH_PTR(tmp);
 }
 
@@ -826,7 +854,7 @@
 	struct db_callback_info *db_info = _info;
 	struct range_list *ret_range;
 	int type, param;
-	char *key, *value;
+	char *ret_str, *key, *value;
 	struct return_implies_callback *tmp;
 	struct stree *stree;
 	int return_id;
@@ -835,6 +863,7 @@
 		return 0;
 
 	return_id = atoi(argv[0]);
+	ret_str = argv[1];
 	type = atoi(argv[2]);
 	param = atoi(argv[3]);
 	key = argv[4];
@@ -842,7 +871,7 @@
 
 	if (db_info->prev_return_id != -1 && type == INTERNAL) {
 		call_ranged_return_hooks(db_info);
-		set_return_state(db_info->expr->left, db_info);
+		set_return_assign_state(db_info);
 		stree = __pop_fake_cur_stree();
 		if (!db_info->cull)
 			merge_fake_stree(&db_info->stree, stree);
@@ -869,7 +898,7 @@
 		param_limit_implications(db_info->expr, param, key, value);
 
 	db_info->handled = 1;
-	call_results_to_rl(db_info->expr->right, get_type(strip_expr(db_info->expr->right)), argv[1], &ret_range);
+	call_results_to_rl(db_info->expr->right, get_type(strip_expr(db_info->expr->right)), ret_str, &ret_range);
 	if (!ret_range)
 		ret_range = alloc_whole_rl(get_type(strip_expr(db_info->expr->right)));
 	ret_range = cast_rl(get_type(db_info->expr->right), ret_range);
@@ -876,10 +905,10 @@
 
 	if (type == INTERNAL) {
 		set_state(-1, "unnull_path", NULL, &true_state);
-		__add_return_comparison(strip_expr(db_info->expr->right), argv[1]);
-		__add_comparison_info(db_info->expr->left, strip_expr(db_info->expr->right), argv[1]);
-		__add_return_to_param_mapping(db_info->expr, argv[1]);
-		store_return_state(db_info, argv[1], alloc_estate_rl(ret_range));
+		__add_return_comparison(strip_expr(db_info->expr->right), ret_str);
+		__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));
 	}
 
 	FOR_EACH_PTR(db_return_states_list, tmp) {
@@ -917,7 +946,7 @@
 	}
 	if (db_info.handled)
 		call_ranged_return_hooks(&db_info);
-	set_return_state(db_info.expr->left, &db_info);
+	set_return_assign_state(&db_info);
 	stree = __pop_fake_cur_stree();
 	if (!db_info.cull)
 		merge_fake_stree(&db_info.stree, stree);
@@ -1011,7 +1040,7 @@
 	struct db_callback_info *db_info = _info;
 	struct range_list *ret_range;
 	int type, param;
-	char *key, *value;
+	char *ret_str, *key, *value;
 	struct return_implies_callback *tmp;
 	struct stree *stree;
 	int return_id;
@@ -1021,6 +1050,7 @@
 		return 0;
 
 	return_id = atoi(argv[0]);
+	ret_str = argv[1];
 	type = atoi(argv[2]);
 	param = atoi(argv[3]);
 	key = argv[4];
@@ -1053,13 +1083,13 @@
 	if (type == PARAM_LIMIT)
 		param_limit_implications(db_info->expr, param, key, value);
 
-	call_results_to_rl(db_info->expr, get_type(strip_expr(db_info->expr)), argv[1], &ret_range);
+	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);
 
 	if (type == INTERNAL) {
 		set_state(-1, "unnull_path", NULL, &true_state);
-		__add_return_comparison(strip_expr(db_info->expr), argv[1]);
-		__add_return_to_param_mapping(db_info->expr, argv[1]);
+		__add_return_comparison(strip_expr(db_info->expr), ret_str);
+		__add_return_to_param_mapping(db_info->expr, ret_str);
 	}
 
 
--- old/usr/src/tools/smatch/src/smatch_function_ptrs.c	Mon Aug  5 08:38:32 2019
+++ new/usr/src/tools/smatch/src/smatch_function_ptrs.c	Mon Aug  5 08:38:32 2019
@@ -141,6 +141,9 @@
 {
 	char *name;
 
+	if (is_zero(expr))
+		return NULL;
+
 	expr = strip_expr(expr);
 
 	/* (*ptrs[0])(a, b, c) is the same as ptrs[0](a, b, c); */
@@ -345,6 +348,57 @@
 	sql_insert_function_ptr(fn_name, ptr_name);
 }
 
+static void print_initializer_list(struct expression_list *expr_list,
+		struct symbol *struct_type)
+{
+	struct expression *expr;
+	struct symbol *base_type;
+	char struct_name[256];
+
+	FOR_EACH_PTR(expr_list, expr) {
+		if (expr->type == EXPR_INDEX && expr->idx_expression && expr->idx_expression->type == EXPR_INITIALIZER) {
+			print_initializer_list(expr->idx_expression->expr_list, struct_type);
+			continue;
+		}
+		if (expr->type != EXPR_IDENTIFIER)
+			continue;
+		if (!expr->expr_ident)
+			continue;
+		if (!expr->ident_expression ||
+		    expr->ident_expression->type != EXPR_SYMBOL ||
+		    !expr->ident_expression->symbol_name)
+			continue;
+		base_type = get_type(expr->ident_expression);
+		if (!base_type || base_type->type != SYM_FN)
+			continue;
+		snprintf(struct_name, sizeof(struct_name), "(struct %s)->%s",
+			 struct_type->ident->name, expr->expr_ident->name);
+		sql_insert_function_ptr(expr->ident_expression->symbol_name->name,
+				        struct_name);
+	} END_FOR_EACH_PTR(expr);
+}
+
+static void global_variable(struct symbol *sym)
+{
+	struct symbol *struct_type;
+
+	if (!sym->ident)
+		return;
+	if (!sym->initializer || sym->initializer->type != EXPR_INITIALIZER)
+		return;
+	struct_type = get_base_type(sym);
+	if (!struct_type)
+		return;
+	if (struct_type->type == SYM_ARRAY) {
+		struct_type = get_base_type(struct_type);
+		if (!struct_type)
+			return;
+	}
+	if (struct_type->type != SYM_STRUCT || !struct_type->ident)
+		return;
+	print_initializer_list(sym->initializer->expr_list, struct_type);
+}
+
 void register_function_ptrs(int id)
 {
 	my_id = id;
@@ -352,6 +406,8 @@
 	if (!option_info)
 		return;
 
+	add_hook(&global_variable, BASE_HOOK);
+	add_hook(&global_variable, DECLARATION_HOOK);
 	add_hook(&match_passes_function_pointer, FUNCTION_CALL_HOOK);
 	add_hook(&match_returns_function_pointer, RETURN_HOOK);
 	add_hook(&match_function_assign, ASSIGNMENT_HOOK);
--- old/usr/src/tools/smatch/src/smatch_helper.c	Mon Aug  5 08:38:33 2019
+++ new/usr/src/tools/smatch/src/smatch_helper.c	Mon Aug  5 08:38:33 2019
@@ -81,14 +81,26 @@
 	return state;
 }
 
+struct smatch_state *merge_str_state(struct smatch_state *s1, struct smatch_state *s2)
+{
+	if (!s1->name || !s2->name)
+		return &merged;
+	if (strcmp(s1->name, s2->name) == 0)
+		return s1;
+	return &merged;
+}
+
 struct smatch_state *alloc_state_expr(struct expression *expr)
 {
 	struct smatch_state *state;
 	char *name;
 
-	state = __alloc_smatch_state(0);
 	expr = strip_expr(expr);
 	name = expr_to_str(expr);
+	if (!name)
+		return NULL;
+
+	state = __alloc_smatch_state(0);
 	state->name = alloc_sname(name);
 	free_string(name);
 	state->data = expr;
@@ -166,7 +178,7 @@
 
 		deref = expr->deref;
 		op = deref->op;
-		if (op == '*') {
+		if (deref->type == EXPR_PREOP && op == '*') {
 			struct expression *unop = strip_expr(deref->unop);
 
 			if (unop->type == EXPR_PREOP && unop->op == '&') {
@@ -173,9 +185,9 @@
 				deref = unop->unop;
 				op = '.';
 			} else {
-				deref = deref->unop;
-				if (!is_pointer(deref))
+				if (!is_pointer(deref) && !is_pointer(deref->unop))
 					op = '.';
+				deref = deref->unop;
 			}
 		}
 
@@ -527,7 +539,7 @@
 		if (sym)
 			*sym = tmp;
 		if (vsl)
-			*vsl = expr_to_vsl(expr);
+			add_var_sym(vsl, name, tmp);
 		return name;
 	}
 	free_string(name);
@@ -869,8 +881,52 @@
 			 expr->member->name);
 		return alloc_string(buf);
 	}
-	if (!sym->ident)
-		return NULL;
+	if (!sym->ident) {
+		struct expression *deref;
+		char *full, *outer;
+		int len;
+
+		/*
+		 * If we're in an anonymous struct then maybe we can find an
+		 * outer struct name to use as a name.  This code should be
+		 * recursive and cleaner.  I am not very proud of it.
+		 *
+		 */
+
+		deref = expr->deref;
+		if (deref->type != EXPR_DEREF || !deref->member)
+			return NULL;
+		sym = get_type(deref->deref);
+		if (!sym || sym->type != SYM_STRUCT || !sym->ident)
+			return NULL;
+
+		full = expr_to_str(expr);
+		if (!full)
+			return NULL;
+		deref = deref->deref;
+		if (deref->type == EXPR_PREOP && deref->op == '*')
+			deref = deref->unop;
+		outer = expr_to_str(deref);
+		if (!outer) {
+			free_string(full);
+			return NULL;
+		}
+		len = strlen(outer);
+		if (strncmp(outer, full, len) != 0) {
+			free_string(full);
+			free_string(outer);
+			return NULL;
+		}
+		if (full[len] == '-' && full[len + 1] == '>')
+			len += 2;
+		if (full[len] == '.')
+			len++;
+		snprintf(buf, sizeof(buf), "(struct %s)->%s", sym->ident->name, full + len);
+		free_string(outer);
+		free_string(full);
+
+		return alloc_string(buf);
+	}
 	snprintf(buf, sizeof(buf), "(struct %s)->%s", sym->ident->name, expr->member->name);
 	return alloc_string(buf);
 }
@@ -1054,6 +1110,34 @@
 	return 0;
 }
 
+int op_remove_assign(int op)
+{
+	switch (op) {
+	case SPECIAL_ADD_ASSIGN:
+		return '+';
+	case SPECIAL_SUB_ASSIGN:
+		return '-';
+	case SPECIAL_MUL_ASSIGN:
+		return '*';
+	case SPECIAL_DIV_ASSIGN:
+		return '/';
+	case SPECIAL_MOD_ASSIGN:
+		return '%';
+	case SPECIAL_AND_ASSIGN:
+		return '&';
+	case SPECIAL_OR_ASSIGN:
+		return '|';
+	case SPECIAL_XOR_ASSIGN:
+		return '^';
+	case SPECIAL_SHL_ASSIGN:
+		return SPECIAL_LEFTSHIFT;
+	case SPECIAL_SHR_ASSIGN:
+		return SPECIAL_RIGHTSHIFT;
+	default:
+		return op;
+	}
+}
+
 int expr_equiv(struct expression *one, struct expression *two)
 {
 	struct symbol *one_sym = NULL;
--- old/usr/src/tools/smatch/src/smatch_ignore.c	Mon Aug  5 08:38:34 2019
+++ new/usr/src/tools/smatch/src/smatch_ignore.c	Mon Aug  5 08:38:34 2019
@@ -20,6 +20,7 @@
 
 STATE(ignore);
 static struct stree *ignored;
+static struct stree *ignored_from_file;
 
 void add_ignore(int owner, const char *name, struct symbol *sym)
 {
@@ -54,7 +55,18 @@
 		return 0;
 	ret = is_ignored(owner, name, sym);
 	free_string(name);
-	return ret;
+	if (ret)
+		return true;
+
+	name = get_macro_name(expr->pos);
+	if (name && get_state_stree(ignored_from_file, owner, name, NULL))
+		return true;
+
+	name = get_function();
+	if (name && get_state_stree(ignored_from_file, owner, name, NULL))
+		return true;
+
+	return false;
 }
 
 static void clear_ignores(void)
@@ -64,7 +76,39 @@
 	free_stree(&ignored);
 }
 
+static void load_ignores(void)
+{
+	struct token *token;
+	const char *name, *str;
+	int owner;
+	char buf[64];
+
+	snprintf(buf, sizeof(buf), "%s.ignored_warnings", option_project_str);
+	token = get_tokens_file(buf);
+	if (!token)
+		return;
+	if (token_type(token) != TOKEN_STREAMBEGIN)
+		return;
+	token = token->next;
+	while (token_type(token) != TOKEN_STREAMEND) {
+		if (token_type(token) != TOKEN_IDENT)
+			break;
+		name = show_ident(token->ident);
+		token = token->next;
+		owner = id_from_name(name);
+
+		if (token_type(token) != TOKEN_IDENT)
+			break;
+		str = show_ident(token->ident);
+		token = token->next;
+
+		set_state_stree_perm(&ignored_from_file, owner, str, NULL, &ignore);
+	}
+	clear_token_alloc();
+}
+
 void register_smatch_ignore(int id)
 {
 	add_hook(&clear_ignores, AFTER_FUNC_HOOK);
+	load_ignores();
 }
--- old/usr/src/tools/smatch/src/smatch_imaginary_absolute.c	Mon Aug  5 08:38:34 2019
+++ new/usr/src/tools/smatch/src/smatch_imaginary_absolute.c	Mon Aug  5 08:38:34 2019
@@ -53,6 +53,8 @@
 
 void __save_imaginary_state(struct expression *expr, struct range_list *true_rl, struct range_list *false_rl)
 {
+	if (__in_pre_condition)
+		return;
 	set_true_false_states_expr(my_id, expr, alloc_estate_rl(true_rl), alloc_estate_rl(false_rl));
 }
 
@@ -74,6 +76,7 @@
 {
 	my_id = id;
 
+	set_dynamic_states(my_id);
 	add_unmatched_state_hook(my_id, &empty_state);
 	add_merge_hook(my_id, &merge_is_empty);
 	add_modification_hook(my_id, &reset);
--- old/usr/src/tools/smatch/src/smatch_implied.c	Mon Aug  5 08:38:35 2019
+++ new/usr/src/tools/smatch/src/smatch_implied.c	Mon Aug  5 08:38:35 2019
@@ -65,10 +65,12 @@
 #include "smatch_extra.h"
 
 char *implied_debug_msg;
-#define DIMPLIED(msg...) do { if (option_debug_implied || option_debug) printf(msg); } while (0)
 
-int option_debug_implied = 0;
+bool implications_off;
 
+#define implied_debug 0
+#define DIMPLIED(msg...) do { if (implied_debug) printf(msg); } while (0)
+
 /*
  * tmp_range_list():
  * It messes things up to free range list allocations.  This helper fuction
@@ -87,7 +89,7 @@
 
 static void print_debug_tf(struct sm_state *sm, int istrue, int isfalse)
 {
-	if (!option_debug_implied && !option_debug)
+	if (!implied_debug && !option_debug)
 		return;
 
 	if (istrue && isfalse) {
@@ -143,8 +145,8 @@
 		return 0;
 	}
 
-	if (option_debug)
-		sm_info("fake_history: %s vs %s.  %s %s %s. --> T: %s F: %s",
+	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),
 		       show_rl(true_rl), show_rl(false_rl));
 
@@ -151,10 +153,10 @@
 	true_sm = clone_sm(sm);
 	false_sm = clone_sm(sm);
 
-	true_sm->state = alloc_estate_rl(cast_rl(estate_type(sm->state), true_rl));
+	true_sm->state = clone_partial_estate(sm->state, true_rl);
 	free_slist(&true_sm->possible);
 	add_possible_sm(true_sm, true_sm);
-	false_sm->state = alloc_estate_rl(cast_rl(estate_type(sm->state), false_rl));
+	false_sm->state = clone_partial_estate(sm->state, false_rl);
 	free_slist(&false_sm->possible);
 	add_possible_sm(false_sm, false_sm);
 
@@ -266,7 +268,6 @@
 		add_pool(false_stack, sm);
 	else
 		add_pool(maybe_stack, sm);
-
 }
 
 static int is_checked(struct state_list *checked, struct sm_state *sm)
@@ -292,34 +293,24 @@
 			struct state_list **true_stack,
 			struct state_list **maybe_stack,
 			struct state_list **false_stack,
-			struct state_list **checked, int *mixed, struct sm_state *gate_sm)
+			struct state_list **checked, int *mixed, struct sm_state *gate_sm,
+			struct timeval *start_time)
 {
 	int free_checked = 0;
 	struct state_list *checked_states = NULL;
+	struct timeval now;
 
 	if (!sm)
 		return;
 
-	/*
-	 * If it looks like this is going to take too long as-is, then don't
-	 * create even more fake history.
-	 */
-	if (mixed && sm->nr_children > 100)
-		*mixed = 1;
-
-	/*
-	   Sometimes the implications are just too big to deal with
-	   so we bail.  Theoretically, bailing out here can cause more false
-	   positives but won't hide actual bugs.
-	*/
-	if (sm->nr_children > 4000) {
-		if (option_debug || option_debug_implied) {
-			static char buf[1028];
-			snprintf(buf, sizeof(buf), "debug: %s: nr_children over 4000 (%d). (%s %s)",
-				 __func__, sm->nr_children, sm->name, show_state(sm->state));
-			implied_debug_msg = buf;
+	gettimeofday(&now, NULL);
+	if (now.tv_usec - start_time->tv_usec > 1000000) {
+		if (implied_debug) {
+			sm_msg("debug: %s: implications taking too long.  (%s %s %s)",
+			       __func__, sm->state->name, show_special(comparison), show_rl(rl));
 		}
-		return;
+		if (mixed)
+			*mixed = 1;
 	}
 
 	if (checked == NULL) {
@@ -332,8 +323,8 @@
 
 	do_compare(sm, comparison, rl, true_stack, maybe_stack, false_stack, mixed, gate_sm);
 
-	__separate_pools(sm->left, comparison, rl, true_stack, maybe_stack, false_stack, checked, mixed, gate_sm);
-	__separate_pools(sm->right, comparison, rl, true_stack, maybe_stack, false_stack, checked, mixed, gate_sm);
+	__separate_pools(sm->left, comparison, rl, true_stack, maybe_stack, false_stack, checked, mixed, gate_sm, start_time);
+	__separate_pools(sm->right, comparison, rl, true_stack, maybe_stack, false_stack, checked, mixed, gate_sm, start_time);
 	if (free_checked)
 		free_slist(checked);
 }
@@ -345,10 +336,13 @@
 {
 	struct state_list *maybe_stack = NULL;
 	struct sm_state *tmp;
+	struct timeval start_time;
 
-	__separate_pools(sm, comparison, rl, true_stack, &maybe_stack, false_stack, checked, mixed, sm);
 
-	if (option_debug) {
+	gettimeofday(&start_time, NULL);
+	__separate_pools(sm, comparison, rl, true_stack, &maybe_stack, false_stack, checked, mixed, sm, &start_time);
+
+	if (implied_debug) {
 		struct sm_state *sm;
 
 		FOR_EACH_PTR(*true_stack, sm) {
@@ -356,7 +350,8 @@
 		} END_FOR_EACH_PTR(sm);
 
 		FOR_EACH_PTR(maybe_stack, sm) {
-			sm_msg("MAYBE %s [stree %d]", show_sm(sm), get_stree_id(sm->pool));
+			sm_msg("MAYBE %s %s[stree %d]",
+			       show_sm(sm), sm->merged ? "(merged) ": "", get_stree_id(sm->pool));
 		} END_FOR_EACH_PTR(sm);
 
 		FOR_EACH_PTR(*false_stack, sm) {
@@ -392,24 +387,52 @@
 	return 0;
 }
 
-static int taking_too_long(void)
+static int going_too_slow(void)
 {
 	static void *printed;
 
-	if (out_of_memory())
+	if (out_of_memory()) {
+		implications_off = true;
 		return 1;
+	}
 
-	if (time_parsing_function() < option_timeout)
+	if (!option_timeout || time_parsing_function() < option_timeout) {
+		implications_off = false;
 		return 0;
+	}
 
 	if (!__inline_fn && printed != cur_func_sym) {
 		if (!is_skipped_function())
-			sm_perror("turning off implications after 60 seconds");
+			sm_perror("turning off implications after %d seconds", option_timeout);
 		printed = cur_func_sym;
 	}
+	implications_off = true;
 	return 1;
 }
 
+static char *sm_state_info(struct sm_state *sm)
+{
+	static char buf[512];
+	int n = 0;
+
+	n += snprintf(buf + n, sizeof(buf) - n, "[stree %d line %d] ",
+		      get_stree_id(sm->pool),  sm->line);
+	if (n >= sizeof(buf))
+		return buf;
+	n += snprintf(buf + n, sizeof(buf) - n, "%s ", show_sm(sm));
+	if (n >= sizeof(buf))
+		return buf;
+	n += snprintf(buf + n, sizeof(buf) - n, "left = %s [stree %d] ",
+		      sm->left ? sm->left->state->name : "<none>",
+		      sm->left ? get_stree_id(sm->left->pool) : -1);
+	if (n >= sizeof(buf))
+		return buf;
+	n += snprintf(buf + n, sizeof(buf) - n, "right = %s [stree %d]",
+		      sm->right ? sm->right->state->name : "<none>",
+		      sm->right ? get_stree_id(sm->right->pool) : -1);
+	return buf;
+}
+
 /*
  * NOTE: If a state is in both the keep stack and the remove stack then that is
  * a bug.  Only add states which are definitely true or definitely false.  If
@@ -417,11 +440,12 @@
  * split history where one side is true and one side is false.  Otherwise, if
  * you can't do that, then don't add it to either list.
  */
+#define RECURSE_LIMIT 300
 struct sm_state *filter_pools(struct sm_state *sm,
 			      const struct state_list *remove_stack,
 			      const struct state_list *keep_stack,
 			      int *modified, int *recurse_cnt,
-			      struct timeval *start)
+			      struct timeval *start, int *skip, int *bail)
 {
 	struct sm_state *ret = NULL;
 	struct sm_state *left;
@@ -431,51 +455,46 @@
 
 	if (!sm)
 		return NULL;
-	if (sm->skip_implications)
-		return sm;
-	if (taking_too_long())
-		return sm;
-
+	if (*bail)
+		return NULL;
 	gettimeofday(&now, NULL);
-	if ((*recurse_cnt)++ > 1000 || now.tv_sec - start->tv_sec > 5) {
-		if (local_debug || option_debug_implied) {
-			static char buf[1028];
-			snprintf(buf, sizeof(buf), "debug: %s: nr_children over 4000 (%d). (%s %s)",
-				 __func__, sm->nr_children, sm->name, show_state(sm->state));
-			implied_debug_msg = buf;
-		}
-		sm->skip_implications = 1;
-		return sm;
+	if (now.tv_usec - start->tv_usec > 3000000) {
+		DIMPLIED("%s: implications taking too long: %s\n", __func__, sm_state_info(sm));
+		*bail = 1;
+		return NULL;
 	}
+	if ((*recurse_cnt)++ > RECURSE_LIMIT) {
+		DIMPLIED("%s: recursed too far:  %s\n", __func__, sm_state_info(sm));
+		*skip = 1;
+		return NULL;
+	}
 
 	if (pool_in_pools(sm->pool, remove_stack)) {
-		DIMPLIED("removed [stree %d] %s from %d\n", get_stree_id(sm->pool), show_sm(sm), sm->line);
+		DIMPLIED("%s: remove: %s\n", __func__, sm_state_info(sm));
 		*modified = 1;
 		return NULL;
 	}
 
 	if (!is_merged(sm) || pool_in_pools(sm->pool, keep_stack) || sm_in_keep_leafs(sm, keep_stack)) {
-		DIMPLIED("kept [stree %d] %s from %d. %s. %s. %s.\n", get_stree_id(sm->pool), show_sm(sm), sm->line,
+		DIMPLIED("%s: keep %s (%s, %s, %s): %s\n", __func__, sm->state->name,
 			is_merged(sm) ? "merged" : "not merged",
 			pool_in_pools(sm->pool, keep_stack) ? "not in keep pools" : "in keep pools",
-			sm_in_keep_leafs(sm, keep_stack) ? "reachable keep leaf" : "no keep leaf");
+			sm_in_keep_leafs(sm, keep_stack) ? "reachable keep leaf" : "no keep leaf",
+			sm_state_info(sm));
 		return sm;
 	}
 
-	DIMPLIED("checking [stree %d] %s from %d (%d) left = %s [stree %d] right = %s [stree %d]\n",
-		 get_stree_id(sm->pool),
-		 show_sm(sm), sm->line, sm->nr_children,
-		 sm->left ? sm->left->state->name : "<none>", sm->left ? get_stree_id(sm->left->pool) : -1,
-		 sm->right ? sm->right->state->name : "<none>", sm->right ? get_stree_id(sm->right->pool) : -1);
-	left = filter_pools(sm->left, remove_stack, keep_stack, &removed, recurse_cnt, start);
-	right = filter_pools(sm->right, remove_stack, keep_stack, &removed, recurse_cnt, start);
+	left = filter_pools(sm->left, remove_stack, keep_stack, &removed, recurse_cnt, start, skip, bail);
+	right = filter_pools(sm->right, remove_stack, keep_stack, &removed, recurse_cnt, start, skip, bail);
+	if (*bail || *skip)
+		return NULL;
 	if (!removed) {
-		DIMPLIED("kept [stree %d] %s from %d\n", get_stree_id(sm->pool), show_sm(sm), sm->line);
+		DIMPLIED("%s: kept all: %s\n", __func__, sm_state_info(sm));
 		return sm;
 	}
 	*modified = 1;
 	if (!left && !right) {
-		DIMPLIED("removed [stree %d] %s from %d <none>\n", get_stree_id(sm->pool), show_sm(sm), sm->line);
+		DIMPLIED("%s: removed all: %s\n", __func__, sm_state_info(sm));
 		return NULL;
 	}
 
@@ -505,8 +524,7 @@
 
 	ret->pool = sm->pool;
 
-	DIMPLIED("partial %s => ", show_sm(sm));
-	DIMPLIED("%s from %d [stree %d]\n", show_sm(ret), sm->line, get_stree_id(sm->pool));
+	DIMPLIED("%s: partial: %s\n", __func__, sm_state_info(sm));
 	return ret;
 }
 
@@ -521,33 +539,32 @@
 	int modified;
 	int recurse_cnt;
 	struct timeval start;
+	int skip;
+	int bail = 0;
 
 	if (!remove_stack)
 		return NULL;
 
-	if (taking_too_long())
-		return NULL;
-
+	gettimeofday(&start, NULL);
 	FOR_EACH_SM(pre_stree, tmp) {
-		if (option_debug)
-			sm_msg("%s: %s", __func__, show_sm(tmp));
-		if (!tmp->merged)
+		if (!tmp->merged || sm_in_keep_leafs(tmp, keep_stack))
 			continue;
-		if (sm_in_keep_leafs(tmp, keep_stack))
-			continue;
 		modified = 0;
 		recurse_cnt = 0;
-		gettimeofday(&start, NULL);
-		filtered_sm = filter_pools(tmp, remove_stack, keep_stack, &modified, &recurse_cnt, &start);
-		if (!filtered_sm || !modified)
+		skip = 0;
+		filtered_sm = filter_pools(tmp, remove_stack, keep_stack, &modified, &recurse_cnt, &start, &skip, &bail);
+		if (going_too_slow())
+			return NULL;
+		if (bail)
+			return ret;  /* Return the implications we figured out before time ran out. */
+
+
+		if (skip || !filtered_sm || !modified)
 			continue;
 		/* the assignments here are for borrowed implications */
 		filtered_sm->name = tmp->name;
 		filtered_sm->sym = tmp->sym;
 		avl_insert(&ret, filtered_sm);
-		if (out_of_memory() || taking_too_long())
-			return NULL;
-
 	} END_FOR_EACH_SM(tmp);
 	return ret;
 }
@@ -566,16 +583,14 @@
 
 	gettimeofday(&time_before, NULL);
 
+	DIMPLIED("checking implications: (%s (%s) %s %s)\n",
+		 sm->name, sm->state->name, show_special(comparison), show_rl(rl));
+
 	if (!is_merged(sm)) {
-		DIMPLIED("%d '%s' is not merged.\n", get_lineno(), sm->name);
+		DIMPLIED("%d '%s' from line %d is not merged.\n", get_lineno(), sm->name, sm->line);
 		return;
 	}
 
-	if (option_debug_implied || option_debug) {
-		sm_msg("checking implications: (%s %s %s)",
-		       sm->name, show_special(comparison), show_rl(rl));
-	}
-
 	separate_pools(sm, comparison, rl, &true_stack, &false_stack, NULL, mixed);
 
 	DIMPLIED("filtering true stack.\n");
@@ -584,19 +599,18 @@
 	*false_states = filter_stack(sm, pre_stree, true_stack, false_stack);
 	free_slist(&true_stack);
 	free_slist(&false_stack);
-	if (option_debug_implied || option_debug) {
-		printf("These are the implied states for the true path: (%s %s %s)\n",
-		       sm->name, show_special(comparison), show_rl(rl));
+	if (implied_debug) {
+		printf("These are the implied states for the true path: (%s (%s) %s %s)\n",
+		       sm->name, sm->state->name, show_special(comparison), show_rl(rl));
 		__print_stree(*true_states);
-		printf("These are the implied states for the false path: (%s %s %s)\n",
-		       sm->name, show_special(comparison), show_rl(rl));
+		printf("These are the implied states for the false path: (%s (%s) %s %s)\n",
+		       sm->name, sm->state->name, show_special(comparison), show_rl(rl));
 		__print_stree(*false_states);
 	}
 
 	gettimeofday(&time_after, NULL);
 	sec = time_after.tv_sec - time_before.tv_sec;
-	if (sec > option_timeout) {
-		sm->nr_children = 4000;
+	if (option_timeout && sec > option_timeout) {
 		sm_perror("Function too hairy.  Ignoring implications after %d seconds.", sec);
 	}
 }
@@ -814,7 +828,6 @@
 	return 1;
 }
 
-static int found_implications;
 static struct stree *saved_implied_true;
 static struct stree *saved_implied_false;
 static struct stree *extra_saved_implied_true;
@@ -848,24 +861,20 @@
 			  struct stree **implied_false)
 {
 	if (handled_by_comparison_hook(expr, implied_true, implied_false))
-		goto found;
+		return;
 
 	if (handled_by_extra_states(expr, implied_true, implied_false)) {
 		separate_extra_states(implied_true, implied_false);
-		goto found;
+		return;
 	}
 
 	if (handled_by_stored_conditions(expr, implied_true, implied_false))
-		goto found;
-
-	return;
-found:
-	found_implications = 1;
+		return;
 }
 
 static void save_implications_hook(struct expression *expr)
 {
-	if (taking_too_long())
+	if (going_too_slow())
 		return;
 	get_tf_states(expr, &saved_implied_true, &saved_implied_false);
 }
@@ -906,6 +915,9 @@
 	struct stree *implied_false = NULL;
 	struct range_list *orig, *limit;
 
+	if (time_parsing_function() > 40)
+		return;
+
 	while (expr->type == EXPR_ASSIGNMENT)
 		expr = strip_expr(expr->right);
 	if (expr->type != EXPR_CALL)
@@ -1072,7 +1084,6 @@
 	in_fake_env++;
 	final_pass = 0;
 	__push_fake_cur_stree();
-	found_implications = 0;
 	__split_whole_condition(expr);
 	final_pass = orig_final_pass;
 	in_fake_env--;
--- /dev/null	Mon Aug  5 08:38:35 2019
+++ new/usr/src/tools/smatch/src/smatch_integer_overflow.c	Mon Aug  5 08:38:35 2019
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2015 Oracle.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
+ */
+
+#include "smatch.h"
+#include "smatch_slist.h"
+#include "smatch_extra.h"
+
+static int my_id;
+static int link_id;
+
+static struct smatch_state *safe_state(struct expression *expr)
+{
+	struct smatch_state *state;
+
+	state = __alloc_smatch_state(0);
+	expr = strip_expr(expr);
+	state->name = alloc_sname("safe");
+	state->data = expr;
+	return state;
+}
+
+static char *save_links(struct expression *expr, struct symbol **sym, struct var_sym_list **vsl)
+{
+	struct var_sym *vs;
+	char *name;
+
+	name = expr_to_chunk_sym_vsl(expr, sym, vsl);
+	if (!name || !*vsl) {
+		free_string(name);
+		return NULL;
+	}
+
+	FOR_EACH_PTR(*vsl, vs) {
+		store_link(link_id, vs->var, vs->sym, name, *sym);
+	} END_FOR_EACH_PTR(vs);
+
+	return name;
+}
+
+static void match_divide(struct expression *expr)
+{
+	struct expression *left, *right, *binop;
+	struct symbol *type;
+	char *name;
+	struct symbol *sym;
+	struct var_sym_list *vsl;
+	sval_t max;
+
+	if (expr->type != EXPR_COMPARE)
+		return;
+	if (expr->op != '>' && expr->op != SPECIAL_UNSIGNED_GT &&
+	    expr->op != SPECIAL_GTE && expr->op != SPECIAL_UNSIGNED_GTE)
+		return;
+
+	left = strip_parens(expr->left);
+	right = strip_parens(expr->right);
+
+	if (right->type != EXPR_BINOP || right->op != '/')
+		return;
+	if (!get_value(right->left, &max))
+		return;
+	if (max.value != INT_MAX && max.value != UINT_MAX &&
+	    max.value != LLONG_MAX && max.uvalue != ULLONG_MAX)
+		return;
+
+	type = get_type(expr);
+	if (!type)
+		return;
+	if (type_bits(type) != 32 && type_bits(type) != 64)
+		return;
+
+
+	binop = binop_expression(left, '*', right->right);
+
+	name = save_links(binop, &sym, &vsl);
+	if (!name)
+		return;
+	set_true_false_states(my_id, name, sym, NULL, safe_state(binop));
+	free_string(name);
+}
+
+static void match_overflow_to_less_than(struct expression *expr)
+{
+	struct expression *left, *right;
+	struct symbol *type;
+	char *name;
+	struct symbol *sym;
+	struct var_sym_list *vsl;
+
+	if (expr->type != EXPR_COMPARE)
+		return;
+	if (expr->op != '<' && expr->op != SPECIAL_UNSIGNED_LT)
+		return;
+
+	left = strip_parens(expr->left);
+	right = strip_parens(expr->right);
+
+	if (left->op != '+')
+		return;
+
+	type = get_type(expr);
+	if (!type)
+		return;
+	if (type_bits(type) != 32 && type_bits(type) != 64)
+		return;
+
+	if (!expr_equiv(left->left, right) && !expr_equiv(left->right, right))
+		return;
+
+	name = save_links(left, &sym, &vsl);
+	if (!name)
+		return;
+	set_true_false_states(my_id, name, sym, NULL, safe_state(left));
+	free_string(name);
+}
+
+static void match_condition(struct expression *expr)
+{
+	match_overflow_to_less_than(expr);
+	match_divide(expr);
+}
+
+int can_integer_overflow(struct symbol *type, struct expression *expr)
+{
+	int op;
+	sval_t lmax, rmax, res;
+
+	if (!type)
+		type = &int_ctype;
+
+	expr = strip_expr(expr);
+
+	if (expr->type == EXPR_ASSIGNMENT) {
+		switch(expr->op) {
+		case SPECIAL_MUL_ASSIGN:
+			op = '*';
+			break;
+		case SPECIAL_ADD_ASSIGN:
+			op = '+';
+			break;
+		case SPECIAL_SHL_ASSIGN:
+			op = SPECIAL_LEFTSHIFT;
+			break;
+		default:
+			return 0;
+		}
+	} else if (expr->type == EXPR_BINOP) {
+		if (expr->op != '*' && expr->op != '+' && expr->op != SPECIAL_LEFTSHIFT)
+			return 0;
+		op = expr->op;
+	} else {
+		return 0;
+	}
+
+	get_absolute_max(expr->left, &lmax);
+	get_absolute_max(expr->right, &rmax);
+
+	if (sval_binop_overflows(lmax, op, rmax))
+		return 1;
+
+	res = sval_binop(lmax, op, rmax);
+	if (sval_cmp(res, sval_type_max(type)) > 0)
+		return 1;
+	return 0;
+}
+
+int can_integer_overflow_expr(struct expression *expr)
+{
+	struct symbol *type;
+	struct smatch_state *state;
+	char *name;
+	struct symbol *sym;
+	int ret;
+
+	type = get_type(expr);
+	if (!type)
+		return 0;
+
+	if (!can_integer_overflow(type, expr))
+		return 0;
+
+	name = expr_to_known_chunk_sym(expr, &sym);
+	if (!name || !sym)
+		goto free;
+
+	state = get_state(my_id, name, sym);
+	if (state && state->data)
+		ret = 0;
+free:
+	free_string(name);
+	return ret;
+}
+
+static int get_arg_nr(struct expression *call, struct expression *expr)
+{
+	struct expression *arg;
+	int i;
+
+	i = -1;
+	FOR_EACH_PTR(call->args, arg) {
+		i++;
+		if (expr_equiv(arg, expr))
+			return i;
+	} END_FOR_EACH_PTR(arg);
+
+	return -1;
+}
+
+static void check_links(struct expression *call, struct expression *arg, int nr, struct sm_state *sm, void *_vsl)
+{
+	struct var_sym_list *vsl = _vsl;
+	struct var_sym *vs;
+	struct smatch_state *state;
+	struct expression *expr;
+	int left = -1;
+	int right = -1;
+
+	FOR_EACH_PTR(vsl, vs) {
+		state = get_state(my_id, vs->var, vs->sym);
+		if (!state || !state->data)
+			continue;
+
+		expr = state->data;
+
+		if (expr_equiv(arg, expr->left)) {
+			left = nr;
+			right = get_arg_nr(call, expr->right);
+		} else if (expr_equiv(arg, expr->right)) {
+			left = get_arg_nr(call, expr->left);
+			right = nr;
+		}
+
+		if (left == -1 || right == -1)
+			continue;
+
+		left = -1;
+		right = -1;
+	} END_FOR_EACH_PTR(vs);
+}
+
+static void match_call_info(struct expression *call)
+{
+	struct expression *arg;
+	struct sm_state *link;
+	struct stree *done = NULL;
+	int i;
+
+	i = -1;
+	FOR_EACH_PTR(call->args, arg) {
+		i++;
+
+		link = get_sm_state_expr(link_id, arg);
+		if (!link)
+			continue;
+
+		if (get_state_stree(done, my_id, link->state->name, NULL))
+			continue;
+//		set_state_stree(&done, my_id, link->state->name, NULL, &undefined);
+
+		check_links(call, arg, i, link, link->state->data);
+	} END_FOR_EACH_PTR(arg);
+
+	free_stree(&done);
+}
+
+void register_integer_overflow(int id)
+{
+	my_id = id;
+	set_dynamic_states(my_id);
+	add_hook(&match_condition, CONDITION_HOOK);
+	add_hook(&match_call_info, FUNCTION_CALL_HOOK);
+}
+
+void register_integer_overflow_links(int id)
+{
+	link_id = id;
+	set_up_link_functions(my_id, link_id);
+}
--- old/usr/src/tools/smatch/src/smatch_kernel_user_data.c	Mon Aug  5 08:38:35 2019
+++ new/usr/src/tools/smatch/src/smatch_kernel_user_data.c	Mon Aug  5 08:38:35 2019
@@ -32,7 +32,7 @@
 STATE(called);
 static bool func_gets_user_data;
 
-static const char * kstr_funcs[] = {
+static const char *kstr_funcs[] = {
 	"kstrtoull", "kstrtoll", "kstrtoul", "kstrtol", "kstrtouint",
 	"kstrtoint", "kstrtou64", "kstrtos64", "kstrtou32", "kstrtos32",
 	"kstrtou16", "kstrtos16", "kstrtou8", "kstrtos8", "kstrtoull_from_user"
@@ -45,10 +45,13 @@
 
 static const char *returns_user_data[] = {
 	"simple_strtol", "simple_strtoll", "simple_strtoul", "simple_strtoull",
-	"kvm_register_read", "nlmsg_data", "nla_data", "memdup_user",
-	"kmap_atomic", "skb_network_header",
+	"kvm_register_read",
 };
 
+static const char *returns_pointer_to_user_data[] = {
+	"nlmsg_data", "nla_data", "memdup_user", "kmap_atomic", "skb_network_header",
+};
+
 static void set_points_to_user_data(struct expression *expr);
 
 static struct stree *start_states;
@@ -84,6 +87,7 @@
 {
 	struct smatch_state *user;
 	struct smatch_state *extra;
+	struct smatch_state *state;
 	struct range_list *rl;
 	sval_t dummy;
 	sval_t sval_100;
@@ -91,54 +95,92 @@
 	sval_100.value = 100;
 	sval_100.type = &int_ctype;
 
-	user = get_state(my_id, sm->name, sm->sym);
-	if (!user)
+	user = __get_state(my_id, sm->name, sm->sym);
+	if (!user || !estate_rl(user))
 		return;
-	if (!__in_function_def && !estate_rl(sm->state)) {
-		/*
-		 * If the one side is capped and the other side is empty then
-		 * let's just mark it as not-user data because the information
-		 * isn't going to be useful.  How this looks is:
-		 *
-		 * if (user_var > trusted)
-		 *	user_var = trusted;  <-- empty state
-		 * else
-		 *	<-- capped
-		 *
-		 * The problem is that sometimes things are capped to a literal
-		 * and we'd like to keep the state in that case...  Ugh.  I've
-		 * added a check which assumes that everything less than 100 is
-		 * probably capped against a literal.
-		 *
-		 */
-		if (is_capped_var_sym(sm->name, sm->sym) &&
-		    sval_cmp(estate_max(user), sval_100) > 0)
-			set_state(my_id, sm->name, sm->sym, alloc_estate_empty());
+	extra = __get_state(SMATCH_EXTRA, sm->name, sm->sym);
+	if (!extra)
 		return;
-	}
-	extra = get_state(SMATCH_EXTRA, sm->name, sm->sym);
-	if (!extra || !estate_rl(extra))
-		return;
 	rl = rl_intersection(estate_rl(user), estate_rl(extra));
 	if (rl_to_sval(rl, &dummy))
 		rl = NULL;
-	set_state(my_id, sm->name, sm->sym, alloc_estate_rl(clone_rl(rl)));
+	state = alloc_estate_rl(clone_rl(rl));
+	if (estate_capped(user) || is_capped_var_sym(sm->name, sm->sym))
+		estate_set_capped(state);
+	set_state(my_id, sm->name, sm->sym, state);
 }
 
 static void extra_nomod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
 {
-	struct smatch_state *user;
+	struct smatch_state *user, *new;
 	struct range_list *rl;
 
-	user = get_state(my_id, name, sym);
+	user = __get_state(my_id, name, sym);
 	if (!user)
 		return;
 	rl = rl_intersection(estate_rl(user), estate_rl(state));
 	if (rl_equiv(rl, estate_rl(user)))
 		return;
-	set_state(my_id, name, sym, alloc_estate_rl(rl));
+	new = alloc_estate_rl(rl);
+	if (estate_capped(user))
+		estate_set_capped(new);
+	set_state(my_id, name, sym, new);
 }
 
+static bool binop_capped(struct expression *expr)
+{
+	struct range_list *left_rl;
+	int comparison;
+
+	if (expr->op == '-' && get_user_rl(expr->left, &left_rl)) {
+		if (user_rl_capped(expr->left))
+			return true;
+		comparison = get_comparison(expr->left, expr->right);
+		if (comparison && show_special(comparison)[0] == '>')
+			return true;
+		return false;
+	}
+
+	if (expr->op == '&' || expr->op == '%') {
+		if (is_capped(expr->left) || is_capped(expr->right))
+			return true;
+		if (user_rl_capped(expr->left) || user_rl_capped(expr->right))
+			return true;
+		return false;
+	}
+
+	if (user_rl_capped(expr->left) &&
+	    user_rl_capped(expr->right))
+		return true;
+	return false;
+}
+
+bool user_rl_capped(struct expression *expr)
+{
+	struct smatch_state *state;
+	struct range_list *rl;
+	sval_t sval;
+
+	expr = strip_expr(expr);
+	if (!expr)
+		return false;
+	if (get_value(expr, &sval))
+		return true;
+	if (expr->type == EXPR_BINOP)
+		return binop_capped(expr);
+	if ((expr->type == EXPR_PREOP || expr->type == EXPR_POSTOP) &&
+	    (expr->op == SPECIAL_INCREMENT || expr->op == SPECIAL_DECREMENT))
+		return user_rl_capped(expr->unop);
+	state = get_state_expr(my_id, expr);
+	if (state)
+		return estate_capped(state);
+
+	if (get_user_rl(expr, &rl))
+		return false;  /* uncapped user data */
+
+	return true;  /* not actually user data */
+}
+
 static void tag_inner_struct_members(struct expression *expr, struct symbol *member)
 {
 	struct expression *edge_member;
@@ -347,6 +389,22 @@
 	return 1;
 }
 
+static bool is_points_to_user_data_fn(struct expression *expr)
+{
+	int i;
+
+	expr = strip_expr(expr);
+	if (expr->type != EXPR_CALL || expr->fn->type != EXPR_SYMBOL ||
+	    !expr->fn->symbol)
+		return false;
+	expr = expr->fn;
+	for (i = 0; i < ARRAY_SIZE(returns_pointer_to_user_data); i++) {
+		if (sym_name_is(returns_pointer_to_user_data[i], expr))
+			return true;
+	}
+	return false;
+}
+
 static int get_rl_from_function(struct expression *expr, struct range_list **rl)
 {
 	int i;
@@ -378,6 +436,8 @@
 		return 0;
 	if (is_skb_data(expr))
 		return 1;
+	if (is_points_to_user_data_fn(expr))
+		return 1;
 	if (get_rl_from_function(expr, &rl))
 		return 1;
 
@@ -393,7 +453,7 @@
 	if (!name || !sym)
 		goto free;
 	snprintf(buf, sizeof(buf), "*%s", name);
-	state = get_state(my_id, buf, sym);
+	state = __get_state(my_id, buf, sym);
 	if (state && estate_rl(state))
 		ret = 1;
 free:
@@ -406,12 +466,18 @@
 	char *name;
 	struct symbol *sym;
 	char buf[256];
+	struct symbol *type;
 
 	name = expr_to_var_sym(expr, &sym);
 	if (!name || !sym)
 		goto free;
 	snprintf(buf, sizeof(buf), "*%s", name);
-	set_state(my_id, buf, sym, alloc_estate_whole(&llong_ctype));
+	type = get_type(expr);
+	if (type && type->type == SYM_PTR)
+		type = get_real_base_type(type);
+	if (!type || type->type != SYM_BASETYPE)
+		type = &llong_ctype;
+	set_state(my_id, buf, sym, alloc_estate_whole(type));
 free:
 	free_string(name);
 }
@@ -489,24 +555,77 @@
 	return ret;
 }
 
+static bool handle_op_assign(struct expression *expr)
+{
+	struct expression *binop_expr;
+	struct smatch_state *state;
+	struct range_list *rl;
+
+	switch (expr->op) {
+	case SPECIAL_ADD_ASSIGN:
+	case SPECIAL_SUB_ASSIGN:
+	case SPECIAL_AND_ASSIGN:
+	case SPECIAL_MOD_ASSIGN:
+	case SPECIAL_SHL_ASSIGN:
+	case SPECIAL_SHR_ASSIGN:
+	case SPECIAL_OR_ASSIGN:
+	case SPECIAL_XOR_ASSIGN:
+	case SPECIAL_MUL_ASSIGN:
+	case SPECIAL_DIV_ASSIGN:
+		binop_expr = binop_expression(expr->left,
+					      op_remove_assign(expr->op),
+					      expr->right);
+		if (!get_user_rl(binop_expr, &rl))
+			return true;
+
+		rl = cast_rl(get_type(expr->left), rl);
+		state = alloc_estate_rl(rl);
+		if (user_rl_capped(binop_expr))
+			estate_set_capped(state);
+		set_state_expr(my_id, expr->left, state);
+		return true;
+	}
+	return false;
+}
+
 static void match_assign(struct expression *expr)
 {
 	struct range_list *rl;
+	static struct expression *handled;
+	struct smatch_state *state;
+	struct expression *faked;
 
+	faked = get_faked_expression();
+	if (faked && faked == handled)
+		return;
 	if (is_fake_call(expr->right))
 		goto clear_old_state;
 	if (handle_get_user(expr))
 		return;
-	if (points_to_user_data(expr->right))
+	if (points_to_user_data(expr->right)) {
+		handled = expr;
 		set_points_to_user_data(expr->left);
+	}
 	if (handle_struct_assignment(expr))
 		return;
 
+	if (handle_op_assign(expr))
+		return;
+	if (expr->op != '=')
+		goto clear_old_state;
+
+	/* Handled by DB code */
+	if (expr->right->type == EXPR_CALL || __in_fake_parameter_assign)
+		return;
+
 	if (!get_user_rl(expr->right, &rl))
 		goto clear_old_state;
 
 	rl = cast_rl(get_type(expr->left), rl);
-	set_state_expr(my_id, expr->left, alloc_estate_rl(rl));
+	state = alloc_estate_rl(rl);
+	if (user_rl_capped(expr->right))
+		estate_set_capped(state);
+	set_state_expr(my_id, expr->left, state);
 
 	return;
 
@@ -538,61 +657,115 @@
 	}
 }
 
-static void handle_unsigned_lt_gt(struct expression *expr)
+static struct range_list *strip_negatives(struct range_list *rl)
 {
+	sval_t min = rl_min(rl);
+	sval_t minus_one;
+	sval_t over;
+	sval_t max = sval_type_max(rl_type(rl));
+
+	minus_one.type = rl_type(rl);
+	minus_one.value = INT_MAX + 1ULL;
+	over.type = rl_type(rl);
+	over.value = -1;
+
+	if (!rl)
+		return NULL;
+
+	if (type_unsigned(rl_type(rl)) && type_bits(rl_type(rl)) > 31)
+		return remove_range(rl, over, max);
+
+	return remove_range(rl, min, minus_one);
+}
+
+static void handle_compare(struct expression *expr)
+{
+	struct expression  *left, *right;
+	struct range_list *left_rl = NULL;
+	struct range_list *right_rl = NULL;
+	struct range_list *user_rl;
+	struct smatch_state *capped_state;
+	struct smatch_state *left_true = NULL;
+	struct smatch_state *left_false = NULL;
+	struct smatch_state *right_true = NULL;
+	struct smatch_state *right_false = NULL;
 	struct symbol *type;
-	struct range_list *left;
-	struct range_list *right;
-	struct range_list *non_negative;
-	sval_t min, minus_one;
+	sval_t sval;
 
+	left = strip_expr(expr->left);
+	right = strip_expr(expr->right);
+
+	while (left->type == EXPR_ASSIGNMENT)
+		left = strip_expr(left->left);
+
 	/*
-	 * conditions are mostly handled by smatch_extra.c.  The special case
-	 * here is that say you have if (user_int < unknown_u32) {
-	 * In Smatch extra we say that, We have no idea what value
-	 * unknown_u32 is so the only thin we can say for sure is that
-	 * user_int is not -1 (UINT_MAX).  But in check_user_data2.c we should
-	 * assume that unless unknown_u32 is user data, it's probably less than
-	 * INT_MAX.
+	 * Conditions are mostly handled by smatch_extra.c, but there are some
+	 * times where the exact values are not known so we can't do that.
 	 *
+	 * Normally, we might consider using smatch_capped.c to supliment smatch
+	 * extra but that doesn't work when we merge unknown uncapped kernel
+	 * data with unknown capped user data.  The result is uncapped user
+	 * data.  We need to keep it separate and say that the user data is
+	 * capped.  In the past, I would have marked this as just regular
+	 * kernel data (not user data) but we can't do that these days because
+	 * we need to track user data for Spectre.
+	 *
+	 * The other situation which we have to handle is when we do have an
+	 * int and we compare against an unknown unsigned kernel variable.  In
+	 * that situation we assume that the kernel data is less than INT_MAX.
+	 * Otherwise then we get all sorts of array underflow false positives.
+	 *
 	 */
 
-	type = get_type(expr);
-	if (!type_unsigned(type))
+	/* Handled in smatch_extra.c */
+	if (get_implied_value(left, &sval) ||
+	    get_implied_value(right, &sval))
 		return;
 
-	/*
-	 * Assume if (user < trusted) { ... because I am lazy and because this
-	 * is the correct way to write code.
-	 */
-	if (!get_user_rl(expr->left, &left))
+	get_user_rl(left, &left_rl);
+	get_user_rl(right, &right_rl);
+
+	/* nothing to do */
+	if (!left_rl && !right_rl)
 		return;
-	if (get_user_rl(expr->right, &right))
+	/* if both sides are user data that's not a good limit */
+	if (left_rl && right_rl)
 		return;
 
-	if (!sval_is_negative(rl_min(left)))
-		return;
-	min = rl_min(left);
-	minus_one.type = rl_type(left);
-	minus_one.value = -1;
-	non_negative = remove_range(left, min, minus_one);
+	if (left_rl)
+		user_rl = left_rl;
+	else
+		user_rl = right_rl;
 
+	type = get_type(expr);
+	if (type_unsigned(type))
+		user_rl = strip_negatives(user_rl);
+	capped_state = alloc_estate_rl(user_rl);
+	estate_set_capped(capped_state);
+
 	switch (expr->op) {
 	case '<':
 	case SPECIAL_UNSIGNED_LT:
 	case SPECIAL_LTE:
 	case SPECIAL_UNSIGNED_LTE:
-		set_true_false_states_expr(my_id, expr->left,
-					   alloc_estate_rl(non_negative), NULL);
+		if (left_rl)
+			left_true = capped_state;
+		else
+			right_false = capped_state;
 		break;
 	case '>':
 	case SPECIAL_UNSIGNED_GT:
 	case SPECIAL_GTE:
 	case SPECIAL_UNSIGNED_GTE:
-		set_true_false_states_expr(my_id, expr->left,
-					   NULL, alloc_estate_rl(non_negative));
+		if (left_rl)
+			left_false = capped_state;
+		else
+			right_true = capped_state;
 		break;
 	}
+
+	set_true_false_states_expr(my_id, left, left_true, left_false);
+	set_true_false_states_expr(my_id, right, right_true, right_false);
 }
 
 static void match_condition(struct expression *expr)
@@ -606,7 +779,7 @@
 		return;
 	}
 
-	handle_unsigned_lt_gt(expr);
+	handle_compare(expr);
 }
 
 static void match_user_assign_function(const char *fn, struct expression *expr, void *unused)
@@ -654,38 +827,6 @@
 	return 0;
 }
 
-struct db_info {
-	struct range_list *rl;
-	struct expression *call;
-};
-static int returned_rl_callback(void *_info, int argc, char **argv, char **azColName)
-{
-	struct db_info *db_info = _info;
-	struct range_list *rl;
-	char *return_ranges = argv[0];
-	char *user_ranges = argv[1];
-	struct expression *arg;
-	int comparison;
-
-	if (argc != 2)
-		return 0;
-
-	call_results_to_rl(db_info->call, get_type(db_info->call), user_ranges, &rl);
-	if (str_to_comparison_arg(return_ranges, db_info->call, &comparison, &arg) &&
-	    comparison == SPECIAL_EQUAL) {
-		struct range_list *orig_rl;
-
-		if (!get_user_rl(arg, &orig_rl))
-			return 0;
-		rl = rl_intersection(rl, orig_rl);
-		if (!rl)
-			return 0;
-	}
-	db_info->rl = rl_union(db_info->rl, rl);
-
-	return 0;
-}
-
 static int has_user_data(struct symbol *sym)
 {
 	struct sm_state *tmp;
@@ -715,35 +856,17 @@
 
 static int db_returned_user_rl(struct expression *call, struct range_list **rl)
 {
-	struct db_info db_info = {};
+	struct smatch_state *state;
+	char buf[48];
 
-	/* for function pointers assume everything is used */
-	if (call->fn->type != EXPR_SYMBOL)
-		return 0;
 	if (is_fake_call(call))
 		return 0;
-
-	db_info.call = call;
-	run_sql(&returned_rl_callback, &db_info,
-		"select return, value from return_states where %s and type = %d and parameter = -1 and key = '$';",
-		get_static_filter(call->fn->symbol), USER_DATA3_SET);
-	if (db_info.rl) {
-		func_gets_user_data = true;
-		*rl = db_info.rl;
-		return 1;
-	}
-
-	run_sql(&returned_rl_callback, &db_info,
-		"select return, value from return_states where %s and type = %d and parameter = -1 and key = '$';",
-		get_static_filter(call->fn->symbol), USER_DATA3);
-	if (db_info.rl) {
-		if (!we_pass_user_data(call))
-			return 0;
-		*rl = db_info.rl;
-		return 1;
-	}
-
-	return 0;
+	snprintf(buf, sizeof(buf), "return %p", call);
+	state = get_state(my_id, buf, NULL);
+	if (!state || !estate_rl(state))
+		return 0;
+	*rl = estate_rl(state);
+	return 1;
 }
 
 struct stree *get_user_stree(void)
@@ -753,12 +876,17 @@
 
 static int user_data_flag;
 static int no_user_data_flag;
-static struct range_list *var_user_rl(struct expression *expr)
+struct range_list *var_user_rl(struct expression *expr)
 {
 	struct smatch_state *state;
 	struct range_list *rl;
 	struct range_list *absolute_rl;
 
+	if (expr->type == EXPR_PREOP && expr->op == '&') {
+		no_user_data_flag = 1;
+		return NULL;
+	}
+
 	if (expr->type == EXPR_BINOP && expr->op == '%') {
 		struct range_list *left, *right;
 
@@ -769,7 +897,7 @@
 		goto found;
 	}
 
-	if (!option_spammy && expr->type == EXPR_BINOP && expr->op == '/') {
+	if (expr->type == EXPR_BINOP && expr->op == '/') {
 		struct range_list *left = NULL;
 		struct range_list *right = NULL;
 		struct range_list *abs_right;
@@ -839,8 +967,23 @@
 	return clone_rl(rl_intersection(rl, absolute_rl));
 }
 
+static bool is_ptr_subtract(struct expression *expr)
+{
+	expr = strip_expr(expr);
+	if (!expr)
+		return false;
+	if (expr->type == EXPR_BINOP && expr->op == '-' &&
+	    type_is_ptr(get_type(expr->left))) {
+		return true;
+	}
+	return false;
+}
+
 int get_user_rl(struct expression *expr, struct range_list **rl)
 {
+	if (is_ptr_subtract(expr))
+		return 0;
+
 	user_data_flag = 0;
 	no_user_data_flag = 0;
 	custom_get_absolute_rl(expr, &var_user_rl, rl);
@@ -850,22 +993,11 @@
 	return !!*rl;
 }
 
-int get_user_rl_spammy(struct expression *expr, struct range_list **rl)
-{
-	int ret;
-
-	option_spammy++;
-	ret = get_user_rl(expr, rl);
-	option_spammy--;
-
-	return ret;
-}
-
 int is_user_rl(struct expression *expr)
 {
 	struct range_list *tmp;
 
-	return get_user_rl_spammy(expr, &tmp);
+	return !!get_user_rl(expr, &tmp);
 }
 
 int get_user_rl_var_sym(const char *name, struct symbol *sym, struct range_list **rl)
@@ -880,23 +1012,35 @@
 	return 0;
 }
 
-static void match_call_info(struct expression *expr)
+static char *get_user_rl_str(struct expression *expr, struct symbol *type)
 {
 	struct range_list *rl;
+	static char buf[64];
+
+	if (!get_user_rl(expr, &rl))
+		return NULL;
+	rl = cast_rl(type, rl);
+	snprintf(buf, sizeof(buf), "%s%s",
+		 show_rl(rl), user_rl_capped(expr) ? "[c]" : "");
+	return buf;
+}
+
+static void match_call_info(struct expression *expr)
+{
 	struct expression *arg;
 	struct symbol *type;
-	int i = 0;
+	char *str;
+	int i;
 
 	i = -1;
 	FOR_EACH_PTR(expr->args, arg) {
 		i++;
 		type = get_arg_type(expr->fn, i);
-
-		if (!get_user_rl(arg, &rl))
+		str = get_user_rl_str(arg, type);
+		if (!str)
 			continue;
 
-		rl = cast_rl(type, rl);
-		sql_insert_caller_info(expr, USER_DATA3, i, "$", show_rl(rl));
+		sql_insert_caller_info(expr, USER_DATA, i, "$", str);
 	} END_FOR_EACH_PTR(arg);
 }
 
@@ -920,6 +1064,7 @@
 	struct smatch_state *state;
 	struct range_list *rl;
 	struct symbol *type;
+	char buf[64];
 
 	/*
 	 * Smatch uses a hack where if we get an unsigned long we say it's
@@ -939,41 +1084,102 @@
 	    is_struct_ptr(sm->sym))
 		return;
 
-	state = get_state(SMATCH_EXTRA, sm->name, sm->sym);
+	state = __get_state(SMATCH_EXTRA, sm->name, sm->sym);
 	if (!state || !estate_rl(state))
 		rl = estate_rl(sm->state);
 	else
 		rl = rl_intersection(estate_rl(sm->state), estate_rl(state));
 
-	sql_insert_caller_info(call, USER_DATA3, param, printed_name, show_rl(rl));
+	if (!rl)
+		return;
+
+	snprintf(buf, sizeof(buf), "%s%s", show_rl(rl),
+		 estate_capped(sm->state) ? "[c]" : "");
+	sql_insert_caller_info(call, USER_DATA, param, printed_name, buf);
 }
 
+static void db_param_set(struct expression *expr, int param, char *key, char *value)
+{
+	struct expression *arg;
+	char *name;
+	struct symbol *sym;
+	struct smatch_state *state;
+
+	while (expr->type == EXPR_ASSIGNMENT)
+		expr = strip_expr(expr->right);
+	if (expr->type != EXPR_CALL)
+		return;
+
+	arg = get_argument_from_call_expr(expr->args, param);
+	if (!arg)
+		return;
+	name = get_variable_from_key(arg, key, &sym);
+	if (!name || !sym)
+		goto free;
+
+	state = get_state(my_id, name, sym);
+	if (!state)
+		goto free;
+
+	set_state(my_id, name, sym, alloc_estate_empty());
+free:
+	free_string(name);
+}
+
+static bool param_data_capped(const char *value)
+{
+	if (strstr(value, ",c") || strstr(value, "[c"))
+		return true;
+	return false;
+}
+
 static void set_param_user_data(const char *name, struct symbol *sym, char *key, char *value)
 {
 	struct range_list *rl = NULL;
 	struct smatch_state *state;
+	struct expression *expr;
 	struct symbol *type;
 	char fullname[256];
+	char *key_orig = key;
+	bool add_star = false;
 
-	if (strcmp(key, "*$") == 0)
-		snprintf(fullname, sizeof(fullname), "*%s", name);
-	else if (strncmp(key, "$", 1) == 0)
-		snprintf(fullname, 256, "%s%s", name, key + 1);
-	else
-		return;
+	if (strcmp(key, "**$") == 0) {
+		snprintf(fullname, sizeof(fullname), "**%s", name);
+	} else {
+		if (key[0] == '*') {
+			add_star = true;
+			key++;
+		}
 
-	type = get_member_type_from_key(symbol_expression(sym), key);
+		snprintf(fullname, 256, "%s%s%s", add_star ? "*" : "", name, key + 1);
+	}
 
-	/* if the caller passes a void pointer with user data */
-	if (strcmp(key, "*$") == 0 && type && type != &void_ctype) {
-		struct expression *expr = symbol_expression(sym);
+	expr = symbol_expression(sym);
+	type = get_member_type_from_key(expr, key_orig);
 
-		tag_as_user_data(expr);
-		set_points_to_user_data(expr);
-		return;
+	/*
+	 * Say this function takes a struct ponter but the caller passes
+	 * this_function(skb->data).  We have two options, we could pass *$
+	 * as user data or we could pass foo->bar, foo->baz as user data.
+	 * The second option is easier to implement so we do that.
+	 *
+	 */
+	if (strcmp(key_orig, "*$") == 0) {
+		struct symbol *tmp = type;
+
+		while (tmp && tmp->type == SYM_PTR)
+			tmp = get_real_base_type(tmp);
+
+		if (tmp && (tmp->type == SYM_STRUCT || tmp->type == SYM_UNION)) {
+			tag_as_user_data(symbol_expression(sym));
+			return;
+		}
 	}
+
 	str_to_rl(type, value, &rl);
 	state = alloc_estate_rl(rl);
+	if (param_data_capped(value) || is_capped(expr))
+		estate_set_capped(state);
 	set_state(my_id, fullname, sym, state);
 }
 
@@ -1012,8 +1218,25 @@
 	} END_FOR_EACH_PTR(arg);
 }
 
+static void store_user_data_return(struct expression *expr, char *key, char *value)
+{
+	struct range_list *rl;
+	struct symbol *type;
+	char buf[48];
+
+	if (strcmp(key, "$") != 0)
+		return;
+
+	type = get_type(expr);
+	snprintf(buf, sizeof(buf), "return %p", expr);
+	call_results_to_rl(expr, type, value, &rl);
+
+	set_state(my_id, buf, NULL, alloc_estate_rl(rl));
+}
+
 static void set_to_user_data(struct expression *expr, char *key, char *value)
 {
+	struct smatch_state *state;
 	char *name;
 	struct symbol *sym;
 	struct symbol *type;
@@ -1026,10 +1249,12 @@
 
 	call_results_to_rl(expr, type, value, &rl);
 
-	set_state(my_id, name, sym, alloc_estate_rl(rl));
+	state = alloc_estate_rl(rl);
+	if (param_data_capped(value))
+		estate_set_capped(state);
+	set_state(my_id, name, sym, state);
 free:
 	free_string(name);
-
 }
 
 static void returns_param_user_data(struct expression *expr, int param, char *key, char *value)
@@ -1047,8 +1272,10 @@
 		return;
 
 	if (param == -1) {
-		if (expr->type != EXPR_ASSIGNMENT)
+		if (expr->type != EXPR_ASSIGNMENT) {
+			store_user_data_return(expr, key, value);
 			return;
+		}
 		set_to_user_data(expr->left, key, value);
 		return;
 	}
@@ -1066,8 +1293,10 @@
 	func_gets_user_data = true;
 
 	if (param == -1) {
-		if (expr->type != EXPR_ASSIGNMENT)
+		if (expr->type != EXPR_ASSIGNMENT) {
+			store_user_data_return(expr, key, value);
 			return;
+		}
 		if (strcmp(key, "*$") == 0) {
 			set_points_to_user_data(expr->left);
 			tag_as_user_data(expr->left);
@@ -1088,18 +1317,6 @@
 	set_to_user_data(arg, key, value);
 }
 
-static int has_empty_state(struct sm_state *sm)
-{
-	struct sm_state *tmp;
-
-	FOR_EACH_PTR(sm->possible, tmp) {
-		if (!estate_rl(tmp->state))
-			return 1;
-	} END_FOR_EACH_PTR(tmp);
-
-	return 0;
-}
-
 static void param_set_to_user_data(int return_id, char *return_ranges, struct expression *expr)
 {
 	struct sm_state *sm;
@@ -1110,6 +1327,8 @@
 	const char *param_name;
 	struct symbol *ret_sym;
 	bool return_found = false;
+	bool pointed_at_found = false;
+	char buf[64];
 
 	expr = strip_expr(expr);
 	return_str = expr_to_str(expr);
@@ -1116,13 +1335,13 @@
 	ret_sym = expr_to_sym(expr);
 
 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
-		if (has_empty_state(sm))
-			continue;
-
 		param = get_param_num_from_sym(sm->sym);
 		if (param < 0)
 			continue;
 
+		if (!param_was_set_var_sym(sm->name, sm->sym))
+			continue;
+
 		/* The logic here was that if we were passed in a user data then
 		 * we don't record that.  It's like the difference between
 		 * param_filter and param_set.  When I think about it, I'm not
@@ -1140,20 +1359,15 @@
 		if (strcmp(param_name, "$") == 0)  /* The -1 param is handled after the loop */
 			continue;
 
+		snprintf(buf, sizeof(buf), "%s%s",
+			 show_rl(estate_rl(sm->state)),
+			 estate_capped(sm->state) ? "[c]" : "");
 		sql_insert_return_states(return_id, return_ranges,
-					 func_gets_user_data ? USER_DATA3_SET : USER_DATA3,
-					 param, param_name, show_rl(estate_rl(sm->state)));
+					 func_gets_user_data ? USER_DATA_SET : USER_DATA,
+					 param, param_name, buf);
 	} END_FOR_EACH_SM(sm);
 
-	if (points_to_user_data(expr)) {
-		sql_insert_return_states(return_id, return_ranges,
-					 (is_skb_data(expr) || !func_gets_user_data) ?
-					 USER_DATA3_SET : USER_DATA3,
-					 -1, "*$", "");
-		goto free_string;
-	}
-
-
+	/* This if for "return foo;" where "foo->bar" is user data. */
 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
 		if (!ret_sym)
 			break;
@@ -1165,23 +1379,61 @@
 			continue;
 		if (strcmp(param_name, "$") == 0)
 			return_found = true;
+		if (strcmp(param_name, "*$") == 0)
+			pointed_at_found = true;
+		snprintf(buf, sizeof(buf), "%s%s",
+			 show_rl(estate_rl(sm->state)),
+			 estate_capped(sm->state) ? "[c]" : "");
 		sql_insert_return_states(return_id, return_ranges,
-					 func_gets_user_data ? USER_DATA3_SET : USER_DATA3,
-					 -1, param_name, show_rl(estate_rl(sm->state)));
+					 func_gets_user_data ? USER_DATA_SET : USER_DATA,
+					 -1, param_name, buf);
 	} END_FOR_EACH_SM(sm);
 
-
+	/* This if for "return ntohl(foo);" */
 	if (!return_found && get_user_rl(expr, &rl)) {
+		snprintf(buf, sizeof(buf), "%s%s",
+			 show_rl(rl), user_rl_capped(expr) ? "[c]" : "");
 		sql_insert_return_states(return_id, return_ranges,
-					 func_gets_user_data ? USER_DATA3_SET : USER_DATA3,
-					 -1, "$", show_rl(rl));
-		goto free_string;
+					 func_gets_user_data ? USER_DATA_SET : USER_DATA,
+					 -1, "$", buf);
 	}
 
-free_string:
+	/*
+	 * This is to handle things like return skb->data where we don't set a
+	 * state for that.
+	 */
+	if (!pointed_at_found && points_to_user_data(expr)) {
+		sql_insert_return_states(return_id, return_ranges,
+					 (is_skb_data(expr) || func_gets_user_data) ?
+					 USER_DATA_SET : USER_DATA,
+					 -1, "*$", "s64min-s64max");
+	}
+
 	free_string(return_str);
 }
 
+static void returns_param_capped(struct expression *expr, int param, char *key, char *value)
+{
+	struct smatch_state *state, *new;
+	struct symbol *sym;
+	char *name;
+
+	name = return_state_to_var_sym(expr, param, key, &sym);
+	if (!name || !sym)
+		goto free;
+
+	state = get_state(my_id, name, sym);
+	if (!state || estate_capped(state))
+		goto free;
+
+	new = clone_estate(state);
+	estate_set_capped(new);
+
+	set_state(my_id, name, sym, new);
+free:
+	free_string(name);
+}
+
 static struct int_stack *gets_data_stack;
 static void match_function_def(struct symbol *sym)
 {
@@ -1198,7 +1450,7 @@
 	func_gets_user_data = pop_int(&gets_data_stack);
 }
 
-void register_kernel_user_data2(int id)
+void register_kernel_user_data(int id)
 {
 	int i;
 
@@ -1207,6 +1459,8 @@
 	if (option_project != PROJ_KERNEL)
 		return;
 
+	set_dynamic_states(my_id);
+
 	add_hook(&match_function_def, FUNC_DEF_HOOK);
 	add_hook(&match_inline_start, INLINE_FN_START);
 	add_hook(&match_inline_end, INLINE_FN_END);
@@ -1238,17 +1492,19 @@
 	add_hook(&match_syscall_definition, AFTER_DEF_HOOK);
 
 	add_hook(&match_assign, ASSIGNMENT_HOOK);
+	select_return_states_hook(PARAM_SET, &db_param_set);
 	add_hook(&match_condition, CONDITION_HOOK);
 
 	add_hook(&match_call_info, FUNCTION_CALL_HOOK);
 	add_member_info_callback(my_id, struct_member_callback);
-	select_caller_info_hook(set_param_user_data, USER_DATA3);
-	select_return_states_hook(USER_DATA3, &returns_param_user_data);
-	select_return_states_hook(USER_DATA3_SET, &returns_param_user_data_set);
+	select_caller_info_hook(set_param_user_data, USER_DATA);
+	select_return_states_hook(USER_DATA, &returns_param_user_data);
+	select_return_states_hook(USER_DATA_SET, &returns_param_user_data_set);
+	select_return_states_hook(CAPPED_DATA, &returns_param_capped);
 	add_split_return_callback(&param_set_to_user_data);
 }
 
-void register_kernel_user_data3(int id)
+void register_kernel_user_data2(int id)
 {
 	my_call_id = id;
 
@@ -1256,4 +1512,3 @@
 		return;
 	select_caller_info_hook(set_called, INTERNAL);
 }
-
--- old/usr/src/tools/smatch/src/smatch_links.c	Mon Aug  5 08:38:36 2019
+++ new/usr/src/tools/smatch/src/smatch_links.c	Mon Aug  5 08:38:36 2019
@@ -102,6 +102,7 @@
 	if (id + 1 != link_id)
 		sm_fatal("FATAL ERROR: links need to be registered directly after the check");
 
+	set_dynamic_states(link_id);
 	add_merge_hook(link_id, &merge_link_states);
 	add_modification_hook(link_id, &match_link_modify);
 	// free link at the end of function
--- old/usr/src/tools/smatch/src/smatch_local_values.c	Mon Aug  5 08:38:36 2019
+++ new/usr/src/tools/smatch/src/smatch_local_values.c	Mon Aug  5 08:38:36 2019
@@ -234,6 +234,7 @@
 	if (!option_info)
 		return;
 
+	set_dynamic_states(my_id);
 	add_extra_mod_hook(&extra_mod_hook);
 	add_unmatched_state_hook(my_id, &unmatched_state);
 	add_merge_hook(my_id, &merge_estates);
--- old/usr/src/tools/smatch/src/smatch_math.c	Mon Aug  5 08:38:36 2019
+++ new/usr/src/tools/smatch/src/smatch_math.c	Mon Aug  5 08:38:36 2019
@@ -20,11 +20,12 @@
 #include "smatch_slist.h"
 #include "smatch_extra.h"
 
-static struct range_list *_get_rl(struct expression *expr, int implied, int *recurse_cnt);
-static struct range_list *handle_variable(struct expression *expr, int implied, int *recurse_cnt);
+static bool get_rl_sval(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *sval_res);
+static bool get_rl_internal(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res);
+static bool handle_variable(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval);
 static struct range_list *(*custom_handle_variable)(struct expression *expr);
 
-static int get_implied_value_internal(struct expression *expr, sval_t *sval, int *recurse_cnt);
+static bool get_implied_value_internal(struct expression *expr, int *recurse_cnt, sval_t *res_sval);
 static int get_absolute_rl_internal(struct expression *expr, struct range_list **rl, int *recurse_cnt);
 
 static sval_t zero  = {.type = &int_ctype, {.value = 0} };
@@ -58,12 +59,12 @@
 	RL_REAL_ABSOLUTE,
 };
 
-static struct range_list *last_stmt_rl(struct statement *stmt, int implied, int *recurse_cnt)
+static bool last_stmt_rl(struct statement *stmt, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
 {
 	struct expression *expr;
 
 	if (!stmt)
-		return NULL;
+		return false;
 
 	stmt = last_ptr_list((struct ptr_list *)stmt->stmts);
 	if (stmt->type == STMT_LABEL) {
@@ -71,116 +72,224 @@
 		    stmt->label_statement->type == STMT_EXPRESSION)
 			expr = stmt->label_statement->expression;
 		else
-			return NULL;
+			return false;
 	} else if (stmt->type == STMT_EXPRESSION) {
 		expr = stmt->expression;
 	} else {
-		return NULL;
+		return false;
 	}
-	return _get_rl(expr, implied, recurse_cnt);
+	return get_rl_sval(expr, implied, recurse_cnt, res, res_sval);
 }
 
-static struct range_list *handle_expression_statement_rl(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_expression_statement_rl(struct expression *expr, int implied,
+		int *recurse_cnt, struct range_list **res, sval_t *res_sval)
 {
-	return last_stmt_rl(get_expression_statement(expr), implied, recurse_cnt);
+	return last_stmt_rl(get_expression_statement(expr), implied, recurse_cnt, res, res_sval);
 }
 
-static struct range_list *handle_ampersand_rl(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_address(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
 {
 	struct range_list *rl;
+	static int recursed;
 	sval_t sval;
 
-	if (implied == RL_EXACT || implied == RL_HARD)
-		return NULL;
-	if (get_mtag_sval(expr, &sval))
-		return alloc_rl(sval, sval);
-	if (get_address_rl(expr, &rl))
-		return rl;
-	return alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
+	if (recursed > 10)
+		return false;
+	if (implied == RL_EXACT)
+		return false;
+
+	if (custom_handle_variable) {
+		rl = custom_handle_variable(expr);
+		if (rl) {
+			*res = rl;
+			return true;
+		}
+	}
+
+	recursed++;
+	if (get_mtag_sval(expr, &sval)) {
+		recursed--;
+		*res_sval = sval;
+		return true;
+	}
+
+	if (get_address_rl(expr, res)) {
+		recursed--;
+		return true;
+	}
+	recursed--;
+	return 0;
 }
 
-static struct range_list *handle_negate_rl(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_ampersand_rl(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
 {
-	if (known_condition_true(expr->unop))
-		return rl_zero();
-	if (known_condition_false(expr->unop))
-		return rl_one();
+	return handle_address(expr, implied, recurse_cnt, res, res_sval);
+}
 
+static bool handle_negate_rl(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
+{
+	if (known_condition_true(expr->unop)) {
+		*res_sval = zero;
+		return true;
+	}
+	if (known_condition_false(expr->unop)) {
+		*res_sval = one;
+		return true;
+	}
+
 	if (implied == RL_EXACT)
-		return NULL;
+		return false;
 
-	if (implied_condition_true(expr->unop))
-		return rl_zero();
-	if (implied_condition_false(expr->unop))
-		return rl_one();
-	return alloc_rl(zero, one);
+	if (implied_condition_true(expr->unop)) {
+		*res_sval = zero;
+		return true;
+	}
+	if (implied_condition_false(expr->unop)) {
+		*res_sval = one;
+		return true;
+	}
+
+	*res = alloc_rl(zero, one);
+	return true;
 }
 
-static struct range_list *handle_bitwise_negate(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_bitwise_negate(struct expression *expr, int implied, int *recurse_cnt, sval_t *res_sval)
 {
 	struct range_list *rl;
-	sval_t sval;
+	sval_t sval = {};
 
-	rl = _get_rl(expr->unop, implied, recurse_cnt);
-	if (!rl_to_sval(rl, &sval))
-		return NULL;
+	if (!get_rl_sval(expr->unop, implied, recurse_cnt, &rl, &sval))
+		return false;
+	if (!sval.type && !rl_to_sval(rl, &sval))
+		return false;
 	sval = sval_preop(sval, '~');
 	sval_cast(get_type(expr->unop), sval);
-	return alloc_rl(sval, sval);
+	*res_sval = sval;
+	return true;
 }
 
-static struct range_list *handle_minus_preop(struct expression *expr, int implied, int *recurse_cnt)
+static bool untrusted_type_min(struct expression *expr)
 {
 	struct range_list *rl;
-	sval_t min, max;
 
-	rl = _get_rl(expr->unop, implied, recurse_cnt);
-	min = sval_preop(rl_max(rl), '-');
-	max = sval_preop(rl_min(rl), '-');
-	return alloc_rl(min, max);
+	rl = var_user_rl(expr);
+	return rl && sval_is_min(rl_min(rl));
 }
 
-static struct range_list *handle_preop_rl(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_minus_preop(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
 {
+	struct range_list *rl;
+	struct range_list *ret = NULL;
+	struct symbol *type;
+	sval_t neg_one = { 0 };
+	sval_t zero = { 0 };
+	sval_t sval = {};
+
+	neg_one.value = -1;
+	zero.value = 0;
+
+	if (!get_rl_sval(expr->unop, implied, recurse_cnt, &rl, &sval))
+		return false;
+	if (sval.type) {
+		*res_sval = sval_preop(sval, '-');
+		return true;
+	}
+	/*
+	 * One complication is that -INT_MIN is still INT_MIN because of integer
+	 * overflows...  But how many times do we set a time out to INT_MIN?
+	 * So normally when we call abs() then it does return a positive value.
+	 *
+	 */
+	type = rl_type(rl);
+	neg_one.type = zero.type = type;
+
+	if (sval_is_negative(rl_min(rl))) {
+		struct range_list *neg;
+		struct data_range *drange;
+		sval_t new_min, new_max;
+
+		neg = alloc_rl(sval_type_min(type), neg_one);
+		neg = rl_intersection(rl, neg);
+
+		if (sval_is_min(rl_min(neg)) && !sval_is_min(rl_max(neg)))
+			neg = remove_range(neg, sval_type_min(type), sval_type_min(type));
+
+		FOR_EACH_PTR(neg, drange) {
+			new_min = drange->max;
+			new_min.value = -new_min.value;
+			new_max = drange->min;
+			new_max.value = -new_max.value;
+			add_range(&ret, new_min, new_max);
+		} END_FOR_EACH_PTR(drange);
+
+		if (untrusted_type_min(expr))
+			add_range(&ret, sval_type_min(type), sval_type_min(type));
+	}
+
+	if (!sval_is_negative(rl_max(rl))) {
+		struct range_list *pos;
+		struct data_range *drange;
+		sval_t new_min, new_max;
+
+		pos = alloc_rl(zero, sval_type_max(type));
+		pos = rl_intersection(rl, pos);
+
+		FOR_EACH_PTR(pos, drange) {
+			new_min = drange->max;
+			new_min.value = -new_min.value;
+			new_max = drange->min;
+			new_max.value = -new_max.value;
+			add_range(&ret, new_min, new_max);
+		} END_FOR_EACH_PTR(drange);
+	}
+
+	*res = ret;
+	return true;
+}
+
+static bool handle_preop_rl(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
+{
 	switch (expr->op) {
 	case '&':
-		return handle_ampersand_rl(expr, implied, recurse_cnt);
+		return handle_ampersand_rl(expr, implied, recurse_cnt, res, res_sval);
 	case '!':
-		return handle_negate_rl(expr, implied, recurse_cnt);
+		return handle_negate_rl(expr, implied, recurse_cnt, res, res_sval);
 	case '~':
-		return handle_bitwise_negate(expr, implied, recurse_cnt);
+		return handle_bitwise_negate(expr, implied, recurse_cnt, res_sval);
 	case '-':
-		return handle_minus_preop(expr, implied, recurse_cnt);
+		return handle_minus_preop(expr, implied, recurse_cnt, res, res_sval);
 	case '*':
-		return handle_variable(expr, implied, recurse_cnt);
+		return handle_variable(expr, implied, recurse_cnt, res, res_sval);
 	case '(':
-		return handle_expression_statement_rl(expr, implied, recurse_cnt);
+		return handle_expression_statement_rl(expr, implied, recurse_cnt, res, res_sval);
 	default:
-		return NULL;
+		return false;
 	}
 }
 
-static struct range_list *handle_divide_rl(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_divide_rl(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res)
 {
-	struct range_list *left_rl, *right_rl;
+	struct range_list *left_rl = NULL;
+	struct range_list *right_rl = NULL;
 	struct symbol *type;
 
 	type = get_type(expr);
 
-	left_rl = _get_rl(expr->left, implied, recurse_cnt);
+	get_rl_internal(expr->left, implied, recurse_cnt, &left_rl);
 	left_rl = cast_rl(type, left_rl);
-	right_rl = _get_rl(expr->right, implied, recurse_cnt);
+	get_rl_internal(expr->right, implied, recurse_cnt, &right_rl);
 	right_rl = cast_rl(type, right_rl);
 
 	if (!left_rl || !right_rl)
-		return NULL;
+		return false;
 
 	if (implied != RL_REAL_ABSOLUTE) {
 		if (is_whole_rl(left_rl) || is_whole_rl(right_rl))
-			return NULL;
+			return false;
 	}
 
-	return rl_binop(left_rl, '/', right_rl);
+	*res = rl_binop(left_rl, '/', right_rl);
+	return true;
 }
 
 static int handle_offset_subtraction(struct expression *expr)
@@ -224,12 +333,12 @@
 	return left_offset - right_offset;
 }
 
-static struct range_list *handle_subtract_rl(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_subtract_rl(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res)
 {
 	struct symbol *type;
 	struct range_list *left_orig, *right_orig;
 	struct range_list *left_rl, *right_rl;
-	sval_t max, min, tmp;
+	sval_t min, max, tmp;
 	int comparison;
 	int offset;
 
@@ -240,19 +349,22 @@
 		tmp.type = type;
 		tmp.value = offset;
 
-		return alloc_rl(tmp, tmp);
+		*res = alloc_rl(tmp, tmp);
+		return true;
 	}
 
 	comparison = get_comparison(expr->left, expr->right);
 
-	left_orig = _get_rl(expr->left, implied, recurse_cnt);
+	left_orig = NULL;
+	get_rl_internal(expr->left, implied, recurse_cnt, &left_orig);
 	left_rl = cast_rl(type, left_orig);
-	right_orig = _get_rl(expr->right, implied, recurse_cnt);
+	right_orig = NULL;
+	get_rl_internal(expr->right, implied, recurse_cnt, &right_orig);
 	right_rl = cast_rl(type, right_orig);
 
 	if ((!left_rl || !right_rl) &&
 	    (implied == RL_EXACT || implied == RL_HARD || implied == RL_FUZZY))
-		return NULL;
+		return false;
 
 	if (!left_rl)
 		left_rl = alloc_whole_rl(type);
@@ -261,7 +373,7 @@
 
 	/* negative values complicate everything fix this later */
 	if (sval_is_negative(rl_min(right_rl)))
-		return NULL;
+		return false;
 	max = rl_max(left_rl);
 	min = sval_type_min(type);
 
@@ -290,8 +402,9 @@
 		break;
 	default:
 		if (!left_orig || !right_orig)
-			return NULL;
-		return rl_binop(left_rl, '-', right_rl);
+			return false;
+		*res = rl_binop(left_rl, '-', right_rl);
+		return true;
 	}
 
 	if (!sval_binop_overflows(rl_min(left_rl), '-', rl_max(right_rl))) {
@@ -307,12 +420,13 @@
 	}
 
 	if (sval_is_min(min) && sval_is_max(max))
-		return NULL;
+		return false;
 
-	return cast_rl(type, alloc_rl(min, max));
+	*res = cast_rl(type, alloc_rl(min, max));
+	return true;
 }
 
-static struct range_list *handle_mod_rl(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_mod_rl(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res)
 {
 	struct range_list *rl;
 	sval_t left, right, sval;
@@ -319,117 +433,62 @@
 
 	if (implied == RL_EXACT) {
 		if (!get_implied_value(expr->right, &right))
-			return NULL;
+			return false;
 		if (!get_implied_value(expr->left, &left))
-			return NULL;
+			return false;
 		sval = sval_binop(left, '%', right);
-		return alloc_rl(sval, sval);
+		*res = alloc_rl(sval, sval);
+		return true;
 	}
 	/* if we can't figure out the right side it's probably hopeless */
-	if (!get_implied_value_internal(expr->right, &right, recurse_cnt))
-		return NULL;
+	if (!get_implied_value_internal(expr->right, recurse_cnt, &right))
+		return false;
 
 	right = sval_cast(get_type(expr), right);
 	right.value--;
 
-	rl = _get_rl(expr->left, implied, recurse_cnt);
-	if (rl && rl_max(rl).uvalue < right.uvalue)
+	if (get_rl_internal(expr->left, implied, recurse_cnt, &rl) && rl &&
+	    rl_max(rl).uvalue < right.uvalue)
 		right.uvalue = rl_max(rl).uvalue;
 
-	return alloc_rl(sval_cast(right.type, zero), right);
+	*res = alloc_rl(sval_cast(right.type, zero), right);
+	return true;
 }
 
-static sval_t sval_lowest_set_bit(sval_t sval)
+static bool handle_bitwise_AND(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res)
 {
-	int i;
-	int found = 0;
-
-	for (i = 0; i < 64; i++) {
-		if (sval.uvalue & 1ULL << i) {
-			if (!found++)
-				continue;
-			sval.uvalue &= ~(1ULL << i);
-		}
-	}
-	return sval;
-}
-
-static struct range_list *handle_bitwise_AND(struct expression *expr, int implied, int *recurse_cnt)
-{
 	struct symbol *type;
 	struct range_list *left_rl, *right_rl;
-	sval_t known;
 	int new_recurse;
 
 	if (implied != RL_IMPLIED && implied != RL_ABSOLUTE && implied != RL_REAL_ABSOLUTE)
-		return NULL;
+		return false;
 
 	type = get_type(expr);
 
-	if (get_implied_value_internal(expr->left, &known, recurse_cnt)) {
-		sval_t min;
+	if (!get_rl_internal(expr->left, implied, recurse_cnt, &left_rl))
+		left_rl = alloc_whole_rl(type);
+	left_rl = cast_rl(type, left_rl);
 
-		min = sval_lowest_set_bit(known);
-		left_rl = alloc_rl(min, known);
-		left_rl = cast_rl(type, left_rl);
-		add_range(&left_rl, sval_type_val(type, 0), sval_type_val(type, 0));
-	} else {
-		left_rl = _get_rl(expr->left, implied, recurse_cnt);
-		if (left_rl) {
-			left_rl = cast_rl(type, left_rl);
-			left_rl = alloc_rl(sval_type_val(type, 0), rl_max(left_rl));
-		} else {
-			if (implied == RL_HARD)
-				return NULL;
-			left_rl = alloc_whole_rl(type);
-		}
-	}
-
 	new_recurse = *recurse_cnt;
 	if (*recurse_cnt >= 200)
 		new_recurse = 100;  /* Let's try super hard to get the mask */
-	if (get_implied_value_internal(expr->right, &known, &new_recurse)) {
-		sval_t min, left_max, mod;
+	if (!get_rl_internal(expr->right, implied, &new_recurse, &right_rl))
+		right_rl = alloc_whole_rl(type);
+	right_rl = cast_rl(type, right_rl);
+	*recurse_cnt = new_recurse;
 
-		*recurse_cnt = new_recurse;
-
-		min = sval_lowest_set_bit(known);
-		right_rl = alloc_rl(min, known);
-		right_rl = cast_rl(type, right_rl);
-		add_range(&right_rl, sval_type_val(type, 0), sval_type_val(type, 0));
-
-		if (min.value != 0) {
-			left_max = rl_max(left_rl);
-			mod = sval_binop(left_max, '%', min);
-			if (mod.value) {
-				left_max = sval_binop(left_max, '-', mod);
-				left_max.value++;
-				if (left_max.value > 0 && sval_cmp(left_max, rl_max(left_rl)) < 0)
-					left_rl = remove_range(left_rl, left_max, rl_max(left_rl));
-			}
-		}
-	} else {
-		right_rl = _get_rl(expr->right, implied, recurse_cnt);
-		if (right_rl) {
-			right_rl = cast_rl(type, right_rl);
-			right_rl = alloc_rl(sval_type_val(type, 0), rl_max(right_rl));
-		} else {
-			if (implied == RL_HARD)
-				return NULL;
-			right_rl = alloc_whole_rl(type);
-		}
-	}
-
-	return rl_intersection(left_rl, right_rl);
+	*res = rl_binop(left_rl, '&', right_rl);
+	return true;
 }
 
-static struct range_list *use_rl_binop(struct expression *expr, int implied, int *recurse_cnt)
+static bool use_rl_binop(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res)
 {
 	struct symbol *type;
 	struct range_list *left_rl, *right_rl;
 
 	if (implied != RL_IMPLIED && implied != RL_ABSOLUTE && implied != RL_REAL_ABSOLUTE)
-		return NULL;
+		return false;
 
 	type = get_type(expr);
 
@@ -438,90 +497,78 @@
 	left_rl = cast_rl(type, left_rl);
 	right_rl = cast_rl(type, right_rl);
 	if (!left_rl || !right_rl)
-		return NULL;
+		return false;
 
-	return rl_binop(left_rl, expr->op, right_rl);
+	*res = rl_binop(left_rl, expr->op, right_rl);
+	return true;
 }
 
-static struct range_list *handle_right_shift(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_right_shift(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res)
 {
-	struct range_list *left_rl;
-	sval_t right;
+	struct range_list *left_rl, *right_rl;
 	sval_t min, max;
 
 	if (implied == RL_EXACT || implied == RL_HARD)
-		return NULL;
+		return false;
 
-	left_rl = _get_rl(expr->left, implied, recurse_cnt);
-	if (left_rl) {
+	if (get_rl_internal(expr->left, implied, recurse_cnt, &left_rl)) {
 		max = rl_max(left_rl);
 		min = rl_min(left_rl);
 	} else {
 		if (implied == RL_FUZZY)
-			return NULL;
+			return false;
 		max = sval_type_max(get_type(expr->left));
 		min = sval_type_val(get_type(expr->left), 0);
 	}
 
-	if (get_implied_value_internal(expr->right, &right, recurse_cnt)) {
-		min = sval_binop(min, SPECIAL_RIGHTSHIFT, right);
-		max = sval_binop(max, SPECIAL_RIGHTSHIFT, right);
+	if (get_rl_internal(expr->right, implied, recurse_cnt, &right_rl) &&
+	    !sval_is_negative(rl_min(right_rl))) {
+		min = sval_binop(min, SPECIAL_RIGHTSHIFT, rl_max(right_rl));
+		max = sval_binop(max, SPECIAL_RIGHTSHIFT, rl_min(right_rl));
 	} else if (!sval_is_negative(min)) {
 		min.value = 0;
 		max = sval_type_max(max.type);
 	} else {
-		return NULL;
+		return false;
 	}
 
-	return alloc_rl(min, max);
+	*res = alloc_rl(min, max);
+	return true;
 }
 
-static struct range_list *handle_left_shift(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_left_shift(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res)
 {
-	struct range_list *left_rl, *res;
+	struct range_list *left_rl, *rl;
 	sval_t right;
-	sval_t min, max;
-	int add_zero = 0;
 
 	if (implied == RL_EXACT || implied == RL_HARD)
-		return NULL;
+		return false;
 	/* this is hopeless without the right side */
-	if (!get_implied_value_internal(expr->right, &right, recurse_cnt))
-		return NULL;
-	left_rl = _get_rl(expr->left, implied, recurse_cnt);
-	if (left_rl) {
-		max = rl_max(left_rl);
-		min = rl_min(left_rl);
-		if (min.value == 0) {
-			min.value = 1;
-			add_zero = 1;
-		}
-	} else {
+	if (!get_implied_value_internal(expr->right, recurse_cnt, &right))
+		return false;
+	if (!get_rl_internal(expr->left, implied, recurse_cnt, &left_rl)) {
 		if (implied == RL_FUZZY)
-			return NULL;
-		max = sval_type_max(get_type(expr->left));
-		min = sval_type_val(get_type(expr->left), 1);
-		add_zero = 1;
+			return false;
+		left_rl = alloc_whole_rl(get_type(expr->left));
 	}
 
-	max = sval_binop(max, SPECIAL_LEFTSHIFT, right);
-	min = sval_binop(min, SPECIAL_LEFTSHIFT, right);
-	res = alloc_rl(min, max);
-	if (add_zero)
-		res = rl_union(res, rl_zero());
-	return res;
+	rl = rl_binop(left_rl, SPECIAL_LEFTSHIFT, alloc_rl(right, right));
+	if (!rl)
+		return false;
+	*res = rl;
+	return true;
 }
 
-static struct range_list *handle_known_binop(struct expression *expr)
+static bool handle_known_binop(struct expression *expr, sval_t *res)
 {
 	sval_t left, right;
 
 	if (!get_value(expr->left, &left))
-		return NULL;
+		return false;
 	if (!get_value(expr->right, &right))
-		return NULL;
-	left = sval_binop(left, expr->op, right);
-	return alloc_rl(left, left);
+		return false;
+	*res = sval_binop(left, expr->op, right);
+	return true;
 }
 
 static int has_actual_ranges(struct range_list *rl)
@@ -566,76 +613,94 @@
 	return res_rl;
 }
 
-static struct range_list *handle_binop_rl(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_binop_rl_helper(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
 {
-	struct smatch_state *state;
 	struct symbol *type;
-	struct range_list *left_rl, *right_rl, *rl;
+	struct range_list *left_rl = NULL;
+	struct range_list *right_rl = NULL;
+	struct range_list *rl;
 	sval_t min, max;
 
-	rl = handle_known_binop(expr);
-	if (rl)
-		return rl;
-	if (implied == RL_EXACT)
-		return NULL;
-
-	if (custom_handle_variable) {
-		rl = custom_handle_variable(expr);
-		if (rl)
-			return rl;
-	}
-
-	state = get_extra_state(expr);
-	if (state && !is_whole_rl(estate_rl(state))) {
-		if (implied != RL_HARD || estate_has_hard_max(state))
-			return clone_rl(estate_rl(state));
-	}
-
-	type = get_type(expr);
-	left_rl = _get_rl(expr->left, implied, recurse_cnt);
+	type = get_promoted_type(get_type(expr->left), get_type(expr->right));
+	get_rl_internal(expr->left, implied, recurse_cnt, &left_rl);
 	left_rl = cast_rl(type, left_rl);
-	right_rl = _get_rl(expr->right, implied, recurse_cnt);
+	get_rl_internal(expr->right, implied, recurse_cnt, &right_rl);
 	right_rl = cast_rl(type, right_rl);
-
 	if (!left_rl && !right_rl)
-		return NULL;
+		return false;
 
 	rl = handle_implied_binop(left_rl, expr->op, right_rl);
-	if (rl)
-		return rl;
+	if (rl) {
+		*res = rl;
+		return true;
+	}
 
 	switch (expr->op) {
 	case '%':
-		return handle_mod_rl(expr, implied, recurse_cnt);
+		return handle_mod_rl(expr, implied, recurse_cnt, res);
 	case '&':
-		return handle_bitwise_AND(expr, implied, recurse_cnt);
+		return handle_bitwise_AND(expr, implied, recurse_cnt, res);
 	case '|':
 	case '^':
-		return use_rl_binop(expr, implied, recurse_cnt);
+		return use_rl_binop(expr, implied, recurse_cnt, res);
 	case SPECIAL_RIGHTSHIFT:
-		return handle_right_shift(expr, implied, recurse_cnt);
+		return handle_right_shift(expr, implied, recurse_cnt, res);
 	case SPECIAL_LEFTSHIFT:
-		return handle_left_shift(expr, implied, recurse_cnt);
+		return handle_left_shift(expr, implied, recurse_cnt, res);
 	case '-':
-		return handle_subtract_rl(expr, implied, recurse_cnt);
+		return handle_subtract_rl(expr, implied, recurse_cnt, res);
 	case '/':
-		return handle_divide_rl(expr, implied, recurse_cnt);
+		return handle_divide_rl(expr, implied, recurse_cnt, res);
 	}
 
 	if (!left_rl || !right_rl)
-		return NULL;
+		return false;
 
 	if (sval_binop_overflows(rl_min(left_rl), expr->op, rl_min(right_rl)))
-		return NULL;
+		return false;
 	if (sval_binop_overflows(rl_max(left_rl), expr->op, rl_max(right_rl)))
-		return NULL;
+		return false;
 
 	min = sval_binop(rl_min(left_rl), expr->op, rl_min(right_rl));
 	max = sval_binop(rl_max(left_rl), expr->op, rl_max(right_rl));
 
-	return alloc_rl(min, max);
+	*res = alloc_rl(min, max);
+	return true;
+
 }
 
+static bool handle_binop_rl(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
+{
+	struct smatch_state *state;
+	struct range_list *rl;
+	sval_t val;
+
+	if (handle_known_binop(expr, &val)) {
+		*res_sval = val;
+		return true;
+	}
+	if (implied == RL_EXACT)
+		return false;
+
+	if (custom_handle_variable) {
+		rl = custom_handle_variable(expr);
+		if (rl) {
+			*res = rl;
+			return true;
+		}
+	}
+
+	state = get_extra_state(expr);
+	if (state && !is_whole_rl(estate_rl(state))) {
+		if (implied != RL_HARD || estate_has_hard_max(state)) {
+			*res = clone_rl(estate_rl(state));
+			return true;
+		}
+	}
+
+	return handle_binop_rl_helper(expr, implied, recurse_cnt, res, res_sval);
+}
+
 static int do_comparison(struct expression *expr)
 {
 	struct range_list *left_ranges = NULL;
@@ -662,19 +727,25 @@
 	return 0x3;
 }
 
-static struct range_list *handle_comparison_rl(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_comparison_rl(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
 {
 	sval_t left, right;
-	int res;
+	int cmp;
 
 	if (expr->op == SPECIAL_EQUAL && expr->left->type == EXPR_TYPE) {
 		struct symbol *left, *right;
 
+		if (expr->right->type != EXPR_TYPE)
+			return false;
+
 		left = get_real_base_type(expr->left->symbol);
-		right = get_real_base_type(expr->left->symbol);
-		if (left == right)
-			return rl_one();
-		return rl_zero();
+		right = get_real_base_type(expr->right->symbol);
+		if (type_bits(left) == type_bits(right) &&
+		    type_positive_bits(left) == type_positive_bits(right))
+			*res_sval = one;
+		else
+			*res_sval = zero;
+		return true;
 	}
 
 	if (get_value(expr->left, &left) && get_value(expr->right, &right)) {
@@ -685,23 +756,30 @@
 		tmp_right.min = right;
 		tmp_right.max = right;
 		if (true_comparison_range(&tmp_left, expr->op, &tmp_right))
-			return rl_one();
-		return rl_zero();
+			*res_sval = one;
+		else
+			*res_sval = zero;
+		return true;
 	}
 
 	if (implied == RL_EXACT)
-		return NULL;
+		return false;
 
-	res = do_comparison(expr);
-	if (res == 1)
-		return rl_one();
-	if (res == 2)
-		return rl_zero();
+	cmp = do_comparison(expr);
+	if (cmp == 1) {
+		*res_sval = one;
+		return true;
+	}
+	if (cmp == 2) {
+		*res_sval = zero;
+		return true;
+	}
 
-	return alloc_rl(zero, one);
+	*res = alloc_rl(zero, one);
+	return true;
 }
 
-static struct range_list *handle_logical_rl(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_logical_rl(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
 {
 	sval_t left, right;
 	int left_known = 0;
@@ -713,9 +791,9 @@
 		if (get_value(expr->right, &right))
 			right_known = 1;
 	} else {
-		if (get_implied_value_internal(expr->left, &left, recurse_cnt))
+		if (get_implied_value_internal(expr->left, recurse_cnt, &left))
 			left_known = 1;
-		if (get_implied_value_internal(expr->right, &right, recurse_cnt))
+		if (get_implied_value_internal(expr->right, recurse_cnt, &right))
 			right_known = 1;
 	}
 
@@ -722,30 +800,38 @@
 	switch (expr->op) {
 	case SPECIAL_LOGICAL_OR:
 		if (left_known && left.value)
-			return rl_one();
+			goto one;
 		if (right_known && right.value)
-			return rl_one();
+			goto one;
 		if (left_known && right_known)
-			return rl_zero();
+			goto zero;
 		break;
 	case SPECIAL_LOGICAL_AND:
 		if (left_known && right_known) {
 			if (left.value && right.value)
-				return rl_one();
-			return rl_zero();
+				goto one;
+			goto zero;
 		}
 		break;
 	default:
-		return NULL;
+		return false;
 	}
 
 	if (implied == RL_EXACT)
-		return NULL;
+		return false;
 
-	return alloc_rl(zero, one);
+	*res = alloc_rl(zero, one);
+	return true;
+
+zero:
+	*res_sval = zero;
+	return true;
+one:
+	*res_sval = one;
+	return true;
 }
 
-static struct range_list *handle_conditional_rl(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_conditional_rl(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
 {
 	struct expression *cond_true;
 	struct range_list *true_rl, *false_rl;
@@ -757,22 +843,21 @@
 		cond_true = expr->conditional;
 
 	if (known_condition_true(expr->conditional))
-		return _get_rl(cond_true, implied, recurse_cnt);
+		return get_rl_sval(cond_true, implied, recurse_cnt, res, res_sval);
 	if (known_condition_false(expr->conditional))
-		return _get_rl(expr->cond_false, implied, recurse_cnt);
+		return get_rl_sval(expr->cond_false, implied, recurse_cnt, res, res_sval);
 
 	if (implied == RL_EXACT)
-		return NULL;
+		return false;
 
 	if (implied_condition_true(expr->conditional))
-		return _get_rl(cond_true, implied, recurse_cnt);
+		return get_rl_sval(cond_true, implied, recurse_cnt, res, res_sval);
 	if (implied_condition_false(expr->conditional))
-		return _get_rl(expr->cond_false, implied, recurse_cnt);
+		return get_rl_sval(expr->cond_false, implied, recurse_cnt, res, res_sval);
 
-
 	/* this becomes a problem with deeply nested conditional statements */
 	if (low_on_memory())
-		return NULL;
+		return false;
 
 	type = get_type(expr);
 
@@ -779,23 +864,26 @@
 	__push_fake_cur_stree();
 	final_pass = 0;
 	__split_whole_condition(expr->conditional);
-	true_rl = _get_rl(cond_true, implied, recurse_cnt);
+	true_rl = NULL;
+	get_rl_internal(cond_true, implied, recurse_cnt, &true_rl);
 	__push_true_states();
 	__use_false_states();
-	false_rl = _get_rl(expr->cond_false, implied, recurse_cnt);
+	false_rl = NULL;
+	get_rl_internal(expr->cond_false, implied, recurse_cnt, &false_rl);
 	__merge_true_states();
 	__free_fake_cur_stree();
 	final_pass = final_pass_orig;
 
 	if (!true_rl || !false_rl)
-		return NULL;
+		return false;
 	true_rl = cast_rl(type, true_rl);
 	false_rl = cast_rl(type, false_rl);
 
-	return rl_union(true_rl, false_rl);
+	*res = rl_union(true_rl, false_rl);
+	return true;
 }
 
-static int get_fuzzy_max_helper(struct expression *expr, sval_t *max)
+static bool get_fuzzy_max_helper(struct expression *expr, sval_t *max)
 {
 	struct smatch_state *state;
 	sval_t sval;
@@ -802,17 +890,17 @@
 
 	if (get_hard_max(expr, &sval)) {
 		*max = sval;
-		return 1;
+		return true;
 	}
 
 	state = get_extra_state(expr);
 	if (!state || !estate_has_fuzzy_max(state))
-		return 0;
+		return false;
 	*max = sval_cast(get_type(expr), estate_get_fuzzy_max(state));
-	return 1;
+	return true;
 }
 
-static int get_fuzzy_min_helper(struct expression *expr, sval_t *min)
+static bool get_fuzzy_min_helper(struct expression *expr, sval_t *min)
 {
 	struct smatch_state *state;
 	sval_t sval;
@@ -819,17 +907,17 @@
 
 	state = get_extra_state(expr);
 	if (!state || !estate_rl(state))
-		return 0;
+		return false;
 
 	sval = estate_min(state);
 	if (sval_is_negative(sval) && sval_is_min(sval))
-		return 0;
+		return false;
 
 	if (sval_is_max(sval))
-		return 0;
+		return false;
 
 	*min = sval_cast(get_type(expr), sval);
-	return 1;
+	return true;
 }
 
 int get_const_value(struct expression *expr, sval_t *sval)
@@ -873,7 +961,7 @@
 	return clone_rl(estate_rl(state));
 }
 
-static struct range_list *handle_variable(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_variable(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
 {
 	struct smatch_state *state;
 	struct range_list *rl;
@@ -880,47 +968,60 @@
 	sval_t sval, min, max;
 	struct symbol *type;
 
-	if (get_const_value(expr, &sval))
-		return alloc_rl(sval, sval);
+	if (get_const_value(expr, &sval)) {
+		*res_sval = sval;
+		return true;
+	}
 
+	if (implied == RL_EXACT)
+		return false;
+
 	if (custom_handle_variable) {
 		rl = custom_handle_variable(expr);
-		if (!rl)
-			return var_to_absolute_rl(expr);
-		return rl;
+		if (rl) {
+			if (!rl_to_sval(rl, res_sval))
+				*res = rl;
+		} else {
+			*res = var_to_absolute_rl(expr);
+		}
+		return true;
 	}
 
-	if (implied == RL_EXACT)
-		return NULL;
+	if (get_mtag_sval(expr, &sval)) {
+		*res_sval = sval;
+		return true;
+	}
 
-	if (get_mtag_sval(expr, &sval))
-		return alloc_rl(sval, sval);
-
 	type = get_type(expr);
-	if (type && type->type == SYM_FN)
-		return alloc_rl(fn_ptr_min, fn_ptr_max);
+	if (type &&
+	    (type->type == SYM_ARRAY ||
+	     type->type == SYM_FN))
+		return handle_address(expr, implied, recurse_cnt, res, res_sval);
 
+	/* FIXME: call rl_to_sval() on the results */
+
 	switch (implied) {
 	case RL_HARD:
 	case RL_IMPLIED:
 	case RL_ABSOLUTE:
 		state = get_extra_state(expr);
-		if (!state || !state->data) {
+		if (!state) {
 			if (implied == RL_HARD)
-				return NULL;
-			if (get_local_rl(expr, &rl))
-				return rl;
-			if (get_mtag_rl(expr, &rl))
-				return rl;
-			if (get_db_type_rl(expr, &rl))
-				return rl;
-			if (is_array(expr) && get_array_rl(expr, &rl))
-				return rl;
-			return NULL;
+				return false;
+			if (get_local_rl(expr, res))
+				return true;
+			if (get_mtag_rl(expr, res))
+				return true;
+			if (get_db_type_rl(expr, res))
+				return true;
+			if (is_array(expr) && get_array_rl(expr, res))
+				return true;
+			return false;
 		}
 		if (implied == RL_HARD && !estate_has_hard_max(state))
-			return NULL;
-		return clone_rl(estate_rl(state));
+			return false;
+		*res = clone_rl(estate_rl(state));
+		return true;
 	case RL_REAL_ABSOLUTE: {
 		struct smatch_state *abs_state;
 
@@ -928,10 +1029,12 @@
 		abs_state = get_real_absolute_state(expr);
 
 		if (estate_rl(state) && estate_rl(abs_state)) {
-			return clone_rl(rl_intersection(estate_rl(state),
+			*res = clone_rl(rl_intersection(estate_rl(state),
 							estate_rl(abs_state)));
+			return true;
 		} else if (estate_rl(state)) {
-			return clone_rl(estate_rl(state));
+			*res = clone_rl(estate_rl(state));
+			return true;
 		} else if (estate_is_empty(state)) {
 			/*
 			 * FIXME: we don't handle empty extra states correctly.
@@ -951,26 +1054,27 @@
 			 * get_real_absolute_rl().
 			 *
 			 */
-			return NULL;
+			return false;
 		} else if (estate_rl(abs_state)) {
-			return clone_rl(estate_rl(abs_state));
+			*res = clone_rl(estate_rl(abs_state));
+			return true;
 		}
 
-		if (get_local_rl(expr, &rl))
-			return rl;
-		if (get_mtag_rl(expr, &rl))
-			return rl;
-		if (get_db_type_rl(expr, &rl))
-			return rl;
-		if (is_array(expr) && get_array_rl(expr, &rl))
-			return rl;
-		return NULL;
+		if (get_local_rl(expr, res))
+			return true;
+		if (get_mtag_rl(expr, res))
+			return true;
+		if (get_db_type_rl(expr, res))
+			return true;
+		if (is_array(expr) && get_array_rl(expr, res))
+			return true;
+		return false;
 	}
 	case RL_FUZZY:
 		if (!get_fuzzy_min_helper(expr, &min))
 			min = sval_type_min(get_type(expr));
 		if (!get_fuzzy_max_helper(expr, &max))
-			return NULL;
+			return false;
 		/* fuzzy ranges are often inverted */
 		if (sval_cmp(min, max) > 0) {
 			sval = min;
@@ -977,9 +1081,10 @@
 			min = max;
 			max = sval;
 		}
-		return alloc_rl(min, max);
+		*res = alloc_rl(min, max);
+		return true;
 	}
-	return NULL;
+	return false;
 }
 
 static sval_t handle_sizeof(struct expression *expr)
@@ -1026,52 +1131,55 @@
 	return ret;
 }
 
-static struct range_list *handle_strlen(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_strlen(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
 {
-	struct range_list *rl;
 	struct expression *arg, *tmp;
 	sval_t tag;
 	sval_t ret = { .type = &ulong_ctype };
+	struct range_list *rl;
 
-	if (implied == RL_EXACT)
-		return NULL;
-
 	arg = get_argument_from_call_expr(expr->args, 0);
 	if (!arg)
-		return NULL;
+		return false;
 	if (arg->type == EXPR_STRING) {
 		ret.value = arg->string->length - 1;
-		return alloc_rl(ret, ret);
+		*res_sval = ret;
+		return true;
 	}
+	if (implied == RL_EXACT)
+		return false;
 	if (get_implied_value(arg, &tag) &&
 	    (tmp = fake_string_from_mtag(tag.uvalue))) {
 		ret.value = tmp->string->length - 1;
-		return alloc_rl(ret, ret);
+		*res_sval = ret;
+		return true;
 	}
 
 	if (implied == RL_HARD || implied == RL_FUZZY)
-		return NULL;
+		return false;
 
-	if (get_implied_return(expr, &rl))
-		return rl;
+	if (get_implied_return(expr, &rl)) {
+		*res = rl;
+		return true;
+	}
 
-	return NULL;
+	return false;
 }
 
-static struct range_list *handle_builtin_constant_p(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_builtin_constant_p(struct expression *expr, int implied, int *recurse_cnt, sval_t *res_sval)
 {
 	struct expression *arg;
 	struct range_list *rl;
-	sval_t sval;
 
 	arg = get_argument_from_call_expr(expr->args, 0);
-	rl = _get_rl(arg, RL_EXACT, recurse_cnt);
-	if (rl_to_sval(rl, &sval))
-		return rl_one();
-	return rl_zero();
+	if (get_rl_internal(arg, RL_EXACT, recurse_cnt, &rl))
+		*res_sval = one;
+	else
+		*res_sval = zero;
+	return true;
 }
 
-static struct range_list *handle__builtin_choose_expr(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle__builtin_choose_expr(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
 {
 	struct expression *const_expr, *expr1, *expr2;
 	sval_t sval;
@@ -1081,21 +1189,22 @@
 	expr2 = get_argument_from_call_expr(expr->args, 2);
 
 	if (!get_value(const_expr, &sval) || !expr1 || !expr2)
-		return NULL;
+		return false;
 	if (sval.value)
-		return _get_rl(expr1, implied, recurse_cnt);
-	return _get_rl(expr2, implied, recurse_cnt);
+		return get_rl_sval(expr1, implied, recurse_cnt, res, res_sval);
+	else
+		return get_rl_sval(expr2, implied, recurse_cnt, res, res_sval);
 }
 
-static struct range_list *handle_call_rl(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_call_rl(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
 {
 	struct range_list *rl;
 
 	if (sym_name_is("__builtin_constant_p", expr->fn))
-		return handle_builtin_constant_p(expr, implied, recurse_cnt);
+		return handle_builtin_constant_p(expr, implied, recurse_cnt, res_sval);
 
 	if (sym_name_is("__builtin_choose_expr", expr->fn))
-		return handle__builtin_choose_expr(expr, implied, recurse_cnt);
+		return handle__builtin_choose_expr(expr, implied, recurse_cnt, res, res_sval);
 
 	if (sym_name_is("__builtin_expect", expr->fn) ||
 	    sym_name_is("__builtin_bswap16", expr->fn) ||
@@ -1104,121 +1213,301 @@
 		struct expression *arg;
 
 		arg = get_argument_from_call_expr(expr->args, 0);
-		return _get_rl(arg, implied, recurse_cnt);
+		return get_rl_sval(arg, implied, recurse_cnt, res, res_sval);
 	}
 
 	if (sym_name_is("strlen", expr->fn))
-		return handle_strlen(expr, implied, recurse_cnt);
+		return handle_strlen(expr, implied, recurse_cnt, res, res_sval);
 
 	if (implied == RL_EXACT || implied == RL_HARD || implied == RL_FUZZY)
-		return NULL;
+		return false;
 
 	if (custom_handle_variable) {
 		rl = custom_handle_variable(expr);
-		if (rl)
-			return rl;
+		if (rl) {
+			*res = rl;
+			return true;
+		}
 	}
 
-	if (get_implied_return(expr, &rl))
-		return rl;
-	return db_return_vals(expr);
+	/* Ugh...  get_implied_return() sets *rl to NULL on failure */
+	if (get_implied_return(expr, &rl)) {
+		*res = rl;
+		return true;
+	}
+	rl = db_return_vals(expr);
+	if (rl) {
+		*res = rl;
+		return true;
+	}
+	return false;
 }
 
-static struct range_list *handle_cast(struct expression *expr, int implied, int *recurse_cnt)
+static bool handle_cast(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
 {
 	struct range_list *rl;
 	struct symbol *type;
+	sval_t sval = {};
 
 	type = get_type(expr);
-	rl = _get_rl(expr->cast_expression, implied, recurse_cnt);
-	if (rl)
-		return cast_rl(type, rl);
-	if (implied == RL_ABSOLUTE || implied == RL_REAL_ABSOLUTE)
-		return alloc_whole_rl(type);
+	if (get_rl_sval(expr->cast_expression, implied, recurse_cnt, &rl, &sval)) {
+		if (sval.type)
+			*res_sval = sval_cast(type, sval);
+		else
+			*res = cast_rl(type, rl);
+		return true;
+	}
+	if (implied == RL_ABSOLUTE || implied == RL_REAL_ABSOLUTE) {
+		*res = alloc_whole_rl(type);
+		return true;
+	}
 	if (implied == RL_IMPLIED && type &&
-	    type_bits(type) > 0 && type_bits(type) < 32)
-		return alloc_whole_rl(type);
-	return NULL;
+	    type_bits(type) > 0 && type_bits(type) < 32) {
+		*res = alloc_whole_rl(type);
+		return true;
+	}
+	return false;
 }
 
-static struct range_list *_get_rl(struct expression *expr, int implied, int *recurse_cnt)
+static bool get_offset_from_down(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
 {
+	struct expression *index;
+	struct symbol *type = expr->in;
 	struct range_list *rl;
+	struct symbol *field;
+	int offset = 0;
+	sval_t sval = { .type = ssize_t_ctype };
+	sval_t tmp_sval = {};
+
+	/*
+	 * FIXME:  I don't really know what I'm doing here.  I wish that I
+	 * could just get rid of the __builtin_offset() function and use:
+	 * "&((struct bpf_prog *)NULL)->insns[fprog->len]" instead...
+	 * Anyway, I have done the minimum ammount of work to get that
+	 * expression to work.
+	 *
+	 */
+
+	if (expr->op != '.' || !expr->down ||
+	    expr->down->type != EXPR_OFFSETOF ||
+	    expr->down->op != '[' ||
+	    !expr->down->index)
+		return false;
+
+	index = expr->down->index;
+
+	examine_symbol_type(type);
+	type = get_real_base_type(type);
+	if (!type)
+		return false;
+	field = find_identifier(expr->ident, type->symbol_list, &offset);
+	if (!field)
+		return false;
+
+	type = get_real_base_type(field);
+	if (!type || type->type != SYM_ARRAY)
+		return false;
+	type = get_real_base_type(type);
+
+	if (get_implied_value_internal(index, recurse_cnt, &sval)) {
+		res_sval->type = ssize_t_ctype;
+		res_sval->value = offset + sval.value * type_bytes(type);
+		return true;
+	}
+
+	if (!get_rl_sval(index, implied, recurse_cnt, &rl, &tmp_sval))
+		return false;
+
+	/*
+	 * I'm not sure why get_rl_sval() would return an sval when
+	 * get_implied_value_internal() failed but it does when I
+	 * parse drivers/net/ethernet/mellanox/mlx5/core/en/monitor_stats.c
+	 *
+	 */
+	if (tmp_sval.type) {
+		res_sval->type = ssize_t_ctype;
+		res_sval->value = offset + sval.value * type_bytes(type);
+		return true;
+	}
+
+	sval.value = type_bytes(type);
+	rl = rl_binop(rl, '*', alloc_rl(sval, sval));
+	sval.value = offset;
+	*res = rl_binop(rl, '+', alloc_rl(sval, sval));
+	return true;
+}
+
+static bool get_offset_from_in(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
+{
+	struct symbol *type = get_real_base_type(expr->in);
+	struct symbol *field;
+	int offset = 0;
+
+	if (expr->op != '.' || !type || !expr->ident)
+		return false;
+
+	field = find_identifier(expr->ident, type->symbol_list, &offset);
+	if (!field)
+		return false;
+
+	res_sval->type = size_t_ctype;
+	res_sval->value = offset;
+
+	return true;
+}
+
+static bool handle_offsetof_rl(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval)
+{
+	if (get_offset_from_down(expr, implied, recurse_cnt, res, res_sval))
+		return true;
+
+	if (get_offset_from_in(expr, implied, recurse_cnt, res, res_sval))
+		return true;
+
+	evaluate_expression(expr);
+	if (expr->type == EXPR_VALUE) {
+		*res_sval = sval_from_val(expr, expr->value);
+		return true;
+	}
+	return false;
+}
+
+static bool get_rl_sval(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *sval_res)
+{
+	struct range_list *rl = (void *)-1UL;
 	struct symbol *type;
-	sval_t sval;
+	sval_t sval = {};
 
 	type = get_type(expr);
 	expr = strip_parens(expr);
 	if (!expr)
-		return NULL;
+		return false;
 
 	if (++(*recurse_cnt) >= 200)
-		return NULL;
+		return false;
 
 	switch(expr->type) {
 	case EXPR_CAST:
 	case EXPR_FORCE_CAST:
 	case EXPR_IMPLIED_CAST:
-		rl = handle_cast(expr, implied, recurse_cnt);
+		handle_cast(expr, implied, recurse_cnt, &rl, &sval);
 		goto out_cast;
 	}
 
 	expr = strip_expr(expr);
 	if (!expr)
-		return NULL;
+		return false;
 
 	switch (expr->type) {
 	case EXPR_VALUE:
 		sval = sval_from_val(expr, expr->value);
-		rl = alloc_rl(sval, sval);
 		break;
 	case EXPR_PREOP:
-		rl = handle_preop_rl(expr, implied, recurse_cnt);
+		handle_preop_rl(expr, implied, recurse_cnt, &rl, &sval);
 		break;
 	case EXPR_POSTOP:
-		rl = _get_rl(expr->unop, implied, recurse_cnt);
+		get_rl_sval(expr->unop, implied, recurse_cnt, &rl, &sval);
 		break;
 	case EXPR_BINOP:
-		rl = handle_binop_rl(expr, implied, recurse_cnt);
+		handle_binop_rl(expr, implied, recurse_cnt, &rl, &sval);
 		break;
 	case EXPR_COMPARE:
-		rl = handle_comparison_rl(expr, implied, recurse_cnt);
+		handle_comparison_rl(expr, implied, recurse_cnt, &rl, &sval);
 		break;
 	case EXPR_LOGICAL:
-		rl = handle_logical_rl(expr, implied, recurse_cnt);
+		handle_logical_rl(expr, implied, recurse_cnt, &rl, &sval);
 		break;
 	case EXPR_PTRSIZEOF:
 	case EXPR_SIZEOF:
 		sval = handle_sizeof(expr);
-		rl = alloc_rl(sval, sval);
 		break;
 	case EXPR_SELECT:
 	case EXPR_CONDITIONAL:
-		rl = handle_conditional_rl(expr, implied, recurse_cnt);
+		handle_conditional_rl(expr, implied, recurse_cnt, &rl, &sval);
 		break;
 	case EXPR_CALL:
-		rl = handle_call_rl(expr, implied, recurse_cnt);
+		handle_call_rl(expr, implied, recurse_cnt, &rl, &sval);
 		break;
 	case EXPR_STRING:
-		rl = NULL;
 		if (get_mtag_sval(expr, &sval))
-			rl = alloc_rl(sval, sval);
+			break;
+		if (implied == RL_EXACT)
+			break;
+		rl = alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
 		break;
+	case EXPR_OFFSETOF:
+		handle_offsetof_rl(expr, implied, recurse_cnt, &rl, &sval);
+		break;
+	case EXPR_ALIGNOF:
+		evaluate_expression(expr);
+		if (expr->type == EXPR_VALUE)
+			sval = sval_from_val(expr, expr->value);
+		break;
 	default:
-		rl = handle_variable(expr, implied, recurse_cnt);
+		handle_variable(expr, implied, recurse_cnt, &rl, &sval);
 	}
 
 out_cast:
-	if (rl)
-		return rl;
-	if (type && (implied == RL_ABSOLUTE || implied == RL_REAL_ABSOLUTE))
-		return alloc_whole_rl(type);
-	return NULL;
+	if (rl == (void *)-1UL)
+		rl = NULL;
+
+	if (sval.type || (rl && rl_to_sval(rl, &sval))) {
+		*sval_res = sval;
+		return true;
+	}
+	if (implied == RL_EXACT)
+		return false;
+
+	if (rl) {
+		*res = rl;
+		return true;
+	}
+	if (type && (implied == RL_ABSOLUTE || implied == RL_REAL_ABSOLUTE)) {
+		*res = alloc_whole_rl(type);
+		return true;
+	}
+	return false;
 }
 
+static bool get_rl_internal(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res)
+{
+	struct range_list *rl = NULL;
+	sval_t sval = {};
+
+	if (!get_rl_sval(expr, implied, recurse_cnt, &rl, &sval))
+		return false;
+
+	if (sval.type)
+		*res = alloc_rl(sval, sval);
+	else
+		*res = rl;
+	return true;
+}
+
+static bool get_rl_helper(struct expression *expr, int implied, struct range_list **res)
+{
+	struct range_list *rl = NULL;
+	sval_t sval = {};
+	int recurse_cnt = 0;
+
+	if (get_value(expr, &sval)) {
+		*res = alloc_rl(sval, sval);
+		return true;
+	}
+
+	if (!get_rl_sval(expr, implied, &recurse_cnt, &rl, &sval))
+		return false;
+
+	if (sval.type)
+		*res = alloc_rl(sval, sval);
+	else
+		*res = rl;
+	return true;
+}
+
 struct {
 	struct expression *expr;
-	struct range_list *rl;
+	sval_t sval;
 } cached_results[24];
 static int cache_idx;
 
@@ -1227,59 +1516,83 @@
 	memset(cached_results, 0, sizeof(cached_results));
 }
 
+/*
+ * Don't cache EXPR_VALUE because values are fast already.
+ *
+ */
+static bool get_value_literal(struct expression *expr, sval_t *res_sval)
+{
+	struct expression *tmp;
+	int recurse_cnt = 0;
+
+	tmp = strip_expr(expr);
+	if (!tmp || tmp->type != EXPR_VALUE)
+		return false;
+
+	return get_rl_sval(expr, RL_EXACT, &recurse_cnt, NULL, res_sval);
+}
+
 /* returns 1 if it can get a value literal or else returns 0 */
-int get_value(struct expression *expr, sval_t *sval)
+int get_value(struct expression *expr, sval_t *res_sval)
 {
 	struct range_list *(*orig_custom_fn)(struct expression *expr);
-	struct range_list *rl;
 	int recurse_cnt = 0;
-	sval_t tmp;
+	sval_t sval = {};
 	int i;
 
+	if (get_value_literal(expr, res_sval))
+		return 1;
+
 	/*
 	 * This only handles RL_EXACT because other expr statements can be
 	 * different at different points.  Like the list iterator, for example.
 	 */
 	for (i = 0; i < ARRAY_SIZE(cached_results); i++) {
-		if (expr == cached_results[i].expr)
-			return rl_to_sval(cached_results[i].rl, sval);
+		if (expr == cached_results[i].expr) {
+			if (cached_results[i].sval.type) {
+				*res_sval = cached_results[i].sval;
+				return true;
+			}
+			return false;
+		}
 	}
 
 	orig_custom_fn = custom_handle_variable;
 	custom_handle_variable = NULL;
-	rl = _get_rl(expr, RL_EXACT, &recurse_cnt);
-	if (!rl_to_sval(rl, &tmp))
-		rl = NULL;
+	get_rl_sval(expr, RL_EXACT, &recurse_cnt, NULL, &sval);
+
 	custom_handle_variable = orig_custom_fn;
 
 	cached_results[cache_idx].expr = expr;
-	cached_results[cache_idx].rl = rl;
+	cached_results[cache_idx].sval = sval;
 	cache_idx = (cache_idx + 1) % ARRAY_SIZE(cached_results);
 
-	if (!rl)
+	if (!sval.type)
 		return 0;
 
-	*sval = tmp;
+	*res_sval = sval;
 	return 1;
 }
 
-static int get_implied_value_internal(struct expression *expr, sval_t *sval, int *recurse_cnt)
+static bool get_implied_value_internal(struct expression *expr, int *recurse_cnt, sval_t *res_sval)
 {
 	struct range_list *rl;
 
-	rl =  _get_rl(expr, RL_IMPLIED, recurse_cnt);
-	if (!rl_to_sval(rl, sval))
-		return 0;
-	return 1;
+	res_sval->type = NULL;
+
+	if (!get_rl_sval(expr, RL_IMPLIED, recurse_cnt, &rl, res_sval))
+		return false;
+	if (!res_sval->type && !rl_to_sval(rl, res_sval))
+		return false;
+	return true;
 }
 
 int get_implied_value(struct expression *expr, sval_t *sval)
 {
 	struct range_list *rl;
-	int recurse_cnt = 0;
 
-	rl =  _get_rl(expr, RL_IMPLIED, &recurse_cnt);
-	if (!rl_to_sval(rl, sval))
+	if (!get_rl_helper(expr, RL_IMPLIED, &rl) ||
+	    !rl_to_sval(rl, sval))
 		return 0;
 	return 1;
 }
@@ -1287,10 +1600,8 @@
 int get_implied_min(struct expression *expr, sval_t *sval)
 {
 	struct range_list *rl;
-	int recurse_cnt = 0;
 
-	rl =  _get_rl(expr, RL_IMPLIED, &recurse_cnt);
-	if (!rl)
+	if (!get_rl_helper(expr, RL_IMPLIED, &rl) || !rl)
 		return 0;
 	*sval = rl_min(rl);
 	return 1;
@@ -1299,10 +1610,8 @@
 int get_implied_max(struct expression *expr, sval_t *sval)
 {
 	struct range_list *rl;
-	int recurse_cnt = 0;
 
-	rl =  _get_rl(expr, RL_IMPLIED, &recurse_cnt);
-	if (!rl)
+	if (!get_rl_helper(expr, RL_IMPLIED, &rl) || !rl)
 		return 0;
 	*sval = rl_max(rl);
 	return 1;
@@ -1310,17 +1619,15 @@
 
 int get_implied_rl(struct expression *expr, struct range_list **rl)
 {
-	int recurse_cnt = 0;
-
-	*rl = _get_rl(expr, RL_IMPLIED, &recurse_cnt);
-	if (*rl)
-		return 1;
-	return 0;
+	if (!get_rl_helper(expr, RL_IMPLIED, rl) || !*rl)
+		return 0;
+	return 1;
 }
 
 static int get_absolute_rl_internal(struct expression *expr, struct range_list **rl, int *recurse_cnt)
 {
-	*rl = _get_rl(expr, RL_ABSOLUTE, recurse_cnt);
+	*rl = NULL;
+	get_rl_internal(expr, RL_ABSOLUTE, recurse_cnt, rl);
 	if (!*rl)
 		*rl = alloc_whole_rl(get_type(expr));
 	return 1;
@@ -1328,9 +1635,8 @@
 
 int get_absolute_rl(struct expression *expr, struct range_list **rl)
 {
-	int recurse_cnt = 0;
-
-	*rl = _get_rl(expr, RL_ABSOLUTE, &recurse_cnt);
+	*rl = NULL;
+	 get_rl_helper(expr, RL_ABSOLUTE, rl);
 	if (!*rl)
 		*rl = alloc_whole_rl(get_type(expr));
 	return 1;
@@ -1338,9 +1644,8 @@
 
 int get_real_absolute_rl(struct expression *expr, struct range_list **rl)
 {
-	int recurse_cnt = 0;
-
-	*rl = _get_rl(expr, RL_REAL_ABSOLUTE, &recurse_cnt);
+	*rl = NULL;
+	get_rl_helper(expr, RL_REAL_ABSOLUTE, rl);
 	if (!*rl)
 		*rl = alloc_whole_rl(get_type(expr));
 	return 1;
@@ -1350,13 +1655,13 @@
 			   struct range_list *(*fn)(struct expression *expr),
 			   struct range_list **rl)
 {
-	int recurse_cnt = 0;
+	int ret;
 
 	*rl = NULL;
 	custom_handle_variable = fn;
-	*rl = _get_rl(expr, RL_REAL_ABSOLUTE, &recurse_cnt);
+	ret = get_rl_helper(expr, RL_REAL_ABSOLUTE, rl);
 	custom_handle_variable = NULL;
-	return 1;
+	return ret;
 }
 
 int get_implied_rl_var_sym(const char *var, struct symbol *sym, struct range_list **rl)
@@ -1373,10 +1678,8 @@
 int get_hard_max(struct expression *expr, sval_t *sval)
 {
 	struct range_list *rl;
-	int recurse_cnt = 0;
 
-	rl =  _get_rl(expr, RL_HARD, &recurse_cnt);
-	if (!rl)
+	if (!get_rl_helper(expr, RL_HARD, &rl) || !rl)
 		return 0;
 	*sval = rl_max(rl);
 	return 1;
@@ -1386,10 +1689,8 @@
 {
 	struct range_list *rl;
 	sval_t tmp;
-	int recurse_cnt = 0;
 
-	rl =  _get_rl(expr, RL_FUZZY, &recurse_cnt);
-	if (!rl)
+	if (!get_rl_helper(expr, RL_FUZZY, &rl) || !rl)
 		return 0;
 	tmp = rl_min(rl);
 	if (sval_is_negative(tmp) && sval_is_min(tmp))
@@ -1402,10 +1703,8 @@
 {
 	struct range_list *rl;
 	sval_t max;
-	int recurse_cnt = 0;
 
-	rl =  _get_rl(expr, RL_FUZZY, &recurse_cnt);
-	if (!rl)
+	if (!get_rl_helper(expr, RL_FUZZY, &rl) || !rl)
 		return 0;
 	max = rl_max(rl);
 	if (max.uvalue > INT_MAX - 10000)
@@ -1418,12 +1717,12 @@
 {
 	struct range_list *rl;
 	struct symbol *type;
-	int recurse_cnt = 0;
 
 	type = get_type(expr);
 	if (!type)
 		type = &llong_ctype;  // FIXME: this is wrong but places assume get type can't fail.
-	rl = _get_rl(expr, RL_REAL_ABSOLUTE, &recurse_cnt);
+	rl = NULL;
+	get_rl_helper(expr, RL_REAL_ABSOLUTE, &rl);
 	if (rl)
 		*sval = rl_min(rl);
 	else
@@ -1438,12 +1737,12 @@
 {
 	struct range_list *rl;
 	struct symbol *type;
-	int recurse_cnt = 0;
 
 	type = get_type(expr);
 	if (!type)
 		type = &llong_ctype;
-	rl = _get_rl(expr, RL_REAL_ABSOLUTE, &recurse_cnt);
+	rl = NULL;
+	get_rl_helper(expr, RL_REAL_ABSOLUTE, &rl);
 	if (rl)
 		*sval = rl_max(rl);
 	else
@@ -1552,46 +1851,4 @@
 	return 0;
 }
 
-int can_integer_overflow(struct symbol *type, struct expression *expr)
-{
-	int op;
-	sval_t lmax, rmax, res;
 
-	if (!type)
-		type = &int_ctype;
-
-	expr = strip_expr(expr);
-
-	if (expr->type == EXPR_ASSIGNMENT) {
-		switch(expr->op) {
-		case SPECIAL_MUL_ASSIGN:
-			op = '*';
-			break;
-		case SPECIAL_ADD_ASSIGN:
-			op = '+';
-			break;
-		case SPECIAL_SHL_ASSIGN:
-			op = SPECIAL_LEFTSHIFT;
-			break;
-		default:
-			return 0;
-		}
-	} else if (expr->type == EXPR_BINOP) {
-		if (expr->op != '*' && expr->op != '+' && expr->op != SPECIAL_LEFTSHIFT)
-			return 0;
-		op = expr->op;
-	} else {
-		return 0;
-	}
-
-	get_absolute_max(expr->left, &lmax);
-	get_absolute_max(expr->right, &rmax);
-
-	if (sval_binop_overflows(lmax, op, rmax))
-		return 1;
-
-	res = sval_binop(lmax, op, rmax);
-	if (sval_cmp(res, sval_type_max(type)) > 0)
-		return 1;
-	return 0;
-}
--- old/usr/src/tools/smatch/src/smatch_mem_tracker.c	Mon Aug  5 08:38:37 2019
+++ new/usr/src/tools/smatch/src/smatch_mem_tracker.c	Mon Aug  5 08:38:37 2019
@@ -22,7 +22,7 @@
 
 static unsigned long max_size;
 
-static void match_end_func(struct symbol *sym)
+unsigned long get_mem_kb(void)
 {
 	FILE *file;
 	char buf[1024];
@@ -30,16 +30,26 @@
 
 	file = fopen("/proc/self/statm", "r");
 	if (!file)
-		return;
+		return 0;
 	fread(buf, 1, sizeof(buf), file);
 	fclose(file);
 
 	size = strtoul(buf, NULL, 10);
 	size = size * sysconf(_SC_PAGESIZE) / 1024;
-	if (size > max_size)
-		max_size = size;
+	return size;
 }
 
+static void match_end_func(struct symbol *sym)
+{
+	unsigned long size;
+
+	if (option_mem) {
+		size = get_mem_kb();
+		if (size > max_size)
+			max_size = size;
+	}
+}
+
 unsigned long get_max_memory(void)
 {
 	return max_size;
--- old/usr/src/tools/smatch/src/smatch_modification_hooks.c	Mon Aug  5 08:38:37 2019
+++ new/usr/src/tools/smatch/src/smatch_modification_hooks.c	Mon Aug  5 08:38:37 2019
@@ -52,9 +52,12 @@
 	struct modification_data *data;
 	char *name;
 
-	state = __alloc_smatch_state(0);
 	expr = strip_expr(expr);
 	name = expr_to_str(expr);
+	if (!name)
+		return NULL;
+
+	state = __alloc_smatch_state(0);
 	state->name = alloc_sname(name);
 	free_string(name);
 
@@ -178,7 +181,7 @@
 	call_modification_hooks_name_sym(name, sym, expr, BOTH);
 	__in_fake_assign--;
 
-	other_name = map_long_to_short_name_sym(name, sym, &other_sym);
+	other_name = get_other_name_sym(name, sym, &other_sym);
 	if (other_name) {
 		__in_fake_assign++;
 		call_modification_hooks_name_sym(other_name, other_sym, expr, BOTH);
@@ -279,6 +282,8 @@
 {
 	my_id = id;
 
+	set_dynamic_states(my_id);
+
 	hooks = malloc((num_checks + 1) * sizeof(*hooks));
 	memset(hooks, 0, (num_checks + 1) * sizeof(*hooks));
 	hooks_late = malloc((num_checks + 1) * sizeof(*hooks));
--- old/usr/src/tools/smatch/src/smatch_mtag.c	Mon Aug  5 08:38:38 2019
+++ new/usr/src/tools/smatch/src/smatch_mtag.c	Mon Aug  5 08:38:38 2019
@@ -50,20 +50,6 @@
 
 static int my_id;
 
-static struct smatch_state *alloc_tag_state(mtag_t tag)
-{
-	struct smatch_state *state;
-	char buf[64];
-
-	state = __alloc_smatch_state(0);
-	snprintf(buf, sizeof(buf), "%lld", tag);
-	state->name = alloc_sname(buf);
-	state->data = malloc(sizeof(mtag_t));
-	*(mtag_t *)state->data = tag;
-
-	return state;
-}
-
 static mtag_t str_to_tag(const char *str)
 {
 	unsigned char c[MD5_DIGEST_LENGTH];
@@ -82,40 +68,84 @@
 	return *tag;
 }
 
-static void alloc_assign(const char *fn, struct expression *expr, void *unused)
+const struct {
+	const char *name;
+	int size_arg;
+} allocator_info[] = {
+	{ "kmalloc", 0 },
+	{ "kzalloc", 0 },
+	{ "devm_kmalloc", 1},
+	{ "devm_kzalloc", 1},
+};
+
+static bool is_mtag_call(struct expression *expr)
 {
+	struct expression *arg;
+	int i;
+	sval_t sval;
+
+	if (expr->type != EXPR_CALL ||
+	    expr->fn->type != EXPR_SYMBOL ||
+	    !expr->fn->symbol)
+		return false;
+
+	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;
+
+	arg = get_argument_from_call_expr(expr->args, allocator_info[i].size_arg);
+	if (!get_implied_value(arg, &sval))
+		return false;
+
+	return true;
+}
+
+struct smatch_state *swap_mtag_return(struct expression *expr, struct smatch_state *state)
+{
 	struct expression *left, *right;
 	char *left_name, *right_name;
 	struct symbol *left_sym;
+	struct range_list *rl;
 	char buf[256];
 	mtag_t tag;
+	sval_t tag_sval;
 
+	if (!expr || expr->type != EXPR_ASSIGNMENT || expr->op != '=')
+		return state;
 
-	// FIXME:  This should only happen when the size is not a paramter of
-	// the caller
-	return;
+	if (!estate_rl(state) || strcmp(state->name, "0,4096-ptr_max") != 0)
+		return state;
 
-	if (expr->type != EXPR_ASSIGNMENT || expr->op != '=')
-		return;
 	left = strip_expr(expr->left);
 	right = strip_expr(expr->right);
-	if (right->type != EXPR_CALL || right->fn->type != EXPR_SYMBOL)
-		return;
 
+	if (!is_mtag_call(right))
+		return state;
+
 	left_name = expr_to_str_sym(left, &left_sym);
+	if (!left_name || !left_sym)
+		return state;
 	right_name = expr_to_str(right);
 
 	snprintf(buf, sizeof(buf), "%s %s %s %s", get_filename(), get_function(),
 		 left_name, right_name);
 	tag = str_to_tag(buf);
+	tag_sval.type = estate_type(state);
+	tag_sval.uvalue = tag;
 
-	sql_insert_mtag_about(tag, left_name, right_name);
+	rl = rl_filter(estate_rl(state), valid_ptr_rl);
+	rl = clone_rl(rl);
+	add_range(&rl, tag_sval, tag_sval);
 
-	if (left_name && left_sym)
-		set_state(my_id, left_name, left_sym, alloc_tag_state(tag));
+	sql_insert_mtag_about(tag, left_name, buf);
 
 	free_string(left_name);
 	free_string(right_name);
+
+	return alloc_estate_rl(rl);
 }
 
 int get_string_mtag(struct expression *expr, mtag_t *tag)
@@ -151,31 +181,23 @@
 	return 1;
 }
 
-int get_deref_mtag(struct expression *expr, mtag_t *tag)
+bool get_symbol_mtag(struct symbol *sym, mtag_t *tag)
 {
-	mtag_t container_tag, member_tag;
-	int offset;
+	char buf[256];
 
-	/*
-	 * I'm not totally sure what I'm doing...
-	 *
-	 * This is supposed to get something like "global_var->ptr", but I don't
-	 * feel like it's complete at all.
-	 *
-	 */
+	if (!sym || !sym->ident)
+		return false;
 
-	if (!get_mtag(expr->unop, &container_tag))
-		return 0;
+	if (get_toplevel_mtag(sym, tag))
+		return true;
 
-	offset = get_member_offset_from_deref(expr);
-	if (offset < 0)
-		return 0;
+	if (get_param_num_from_sym(sym) >= 0)
+		return false;
 
-	if (!mtag_map_select_tag(container_tag, -offset, &member_tag))
-		return 0;
-
-	*tag = member_tag;
-	return 1;
+	snprintf(buf, sizeof(buf), "%s %s %s",
+		 get_filename(), get_function(), sym->ident->name);
+	*tag = str_to_tag(buf);
+	return true;
 }
 
 static void global_variable(struct symbol *sym)
@@ -190,87 +212,12 @@
 			      (sym->ctype.modifiers & MOD_STATIC) ? get_filename() : "extern");
 }
 
-static void db_returns_buf_size(struct expression *expr, int param, char *unused, char *math)
-{
-	struct expression *call;
-	struct range_list *rl;
-
-	if (expr->type != EXPR_ASSIGNMENT)
-		return;
-	call = strip_expr(expr->right);
-
-	if (!parse_call_math_rl(call, math, &rl))
-		return;
-//	rl = cast_rl(&int_ctype, rl);
-//	set_state_expr(my_size_id, expr->left, alloc_estate_rl(rl));
-}
-
-static void db_returns_memory_tag(struct expression *expr, int param, char *key, char *value)
-{
-	struct expression *call, *arg;
-	mtag_t tag, alias;
-	char *name;
-	struct symbol *sym;
-
-	call = strip_expr(expr);
-	while (call->type == EXPR_ASSIGNMENT)
-		call = strip_expr(call->right);
-	if (call->type != EXPR_CALL)
-		return;
-
-	tag = strtoul(value, NULL, 10);
-
-	if (!create_mtag_alias(tag, call, &alias))
-		return;
-
-	arg = get_argument_from_call_expr(call->args, param);
-	if (!arg)
-		return;
-
-	name = get_variable_from_key(arg, key, &sym);
-	if (!name || !sym)
-		goto free;
-
-	set_state(my_id, name, sym, alloc_tag_state(alias));
-free:
-	free_string(name);
-}
-
-static void match_call_info(struct expression *expr)
-{
-	struct smatch_state *state;
-	struct expression *arg;
-	int i = -1;
-
-	FOR_EACH_PTR(expr->args, arg) {
-		i++;
-		state = get_state_expr(my_id, arg);
-		if (!state || !state->data)
-			continue;
-		sql_insert_caller_info(expr, MEMORY_TAG, i, "$", state->name);
-	} END_FOR_EACH_PTR(arg);
-}
-
-static void save_caller_info(const char *name, struct symbol *sym, char *key, char *value)
-{
-	struct smatch_state *state;
-	char fullname[256];
-	mtag_t tag;
-
-	if (strncmp(key, "$", 1) != 0)
-		return;
-
-	tag = atoll(value);
-	snprintf(fullname, 256, "%s%s", name, key + 1);
-	state = alloc_tag_state(tag);
-	set_state(my_id, fullname, sym, state);
-}
-
 static int get_array_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
 {
 	struct expression *array, *offset_expr;
 	struct symbol *type;
 	sval_t sval;
+	int start_offset;
 
 	if (!is_array(expr))
 		return 0;
@@ -285,109 +232,37 @@
 	if (!type_bytes(type))
 		return 0;
 
-	if (!get_mtag(array, tag))
+	if (!expr_to_mtag_offset(array, tag, &start_offset))
 		return 0;
 
 	offset_expr = get_array_offset(expr);
 	if (!get_value(offset_expr, &sval))
 		return 0;
-	*offset = sval.value * type_bytes(type);
+	*offset = start_offset + sval.value * type_bytes(type);
 
 	return 1;
 }
 
-static int get_implied_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
+struct range_list *swap_mtag_seed(struct expression *expr, struct range_list *rl)
 {
-	struct smatch_state *state;
-	struct symbol *type;
+	char buf[256];
+	char *name;
 	sval_t sval;
+	mtag_t tag;
 
-	type = get_type(expr);
-	if (!type_is_ptr(type))
-		return 0;
-	state = get_extra_state(expr);
-	if (!state || !estate_get_single_value(state, &sval) || sval.value == 0)
-		return 0;
+	if (!rl_to_sval(rl, &sval))
+		return rl;
+	if (sval.type->type != SYM_PTR || sval.uvalue != MTAG_SEED)
+		return rl;
 
-	*tag = sval.uvalue & ~MTAG_OFFSET_MASK;
-	*offset = sval.uvalue & MTAG_OFFSET_MASK;
-	return 1;
+	name = expr_to_str(expr);
+	snprintf(buf, sizeof(buf), "%s %s %s", get_filename(), get_function(), name);
+	free_string(name);
+	tag = str_to_tag(buf);
+	sval.value = tag;
+	return alloc_rl(sval, sval);
 }
 
-static int get_mtag_cnt;
-int get_mtag(struct expression *expr, mtag_t *tag)
-{
-	struct smatch_state *state;
-	int ret = 0;
-
-	expr = strip_expr(expr);
-	if (!expr)
-		return 0;
-
-	if (get_mtag_cnt > 0)
-		return 0;
-
-	get_mtag_cnt++;
-
-	switch (expr->type) {
-	case EXPR_STRING:
-		if (get_string_mtag(expr, tag)) {
-			ret = 1;
-			goto dec_cnt;
-		}
-		break;
-	case EXPR_SYMBOL:
-		if (get_toplevel_mtag(expr->symbol, tag)) {
-			ret = 1;
-			goto dec_cnt;
-		}
-		break;
-	case EXPR_DEREF:
-		if (get_deref_mtag(expr, tag)) {
-			ret = 1;
-			goto dec_cnt;
-		}
-		break;
-	}
-
-	state = get_state_expr(my_id, expr);
-	if (!state)
-		goto dec_cnt;
-	if (state->data) {
-		*tag = *(mtag_t *)state->data;
-		ret = 1;
-		goto dec_cnt;
-	}
-
-dec_cnt:
-	get_mtag_cnt--;
-	return ret;
-}
-
-int get_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
-{
-	int val;
-
-	if (!expr)
-		return 0;
-	if (expr->type == EXPR_PREOP && expr->op == '*')
-		return get_mtag_offset(expr->unop, tag, offset);
-	if (get_implied_mtag_offset(expr, tag, offset))
-		return 1;
-	if (!get_mtag(expr, tag))
-		return 0;
-	expr = strip_expr(expr);
-	if (expr->type == EXPR_SYMBOL) {
-		*offset = 0;
-		return 1;
-	}
-	val = get_member_offset_from_deref(expr);
-	if (val < 0)
-		return 0;
-	*offset = val;
-	return 1;
-}
-
 int create_mtag_alias(mtag_t tag, struct expression *expr, mtag_t *new)
 {
 	char buf[256];
@@ -415,10 +290,45 @@
 	return 1;
 }
 
+static int get_implied_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
+{
+	struct smatch_state *state;
+	struct symbol *type;
+	sval_t sval;
+
+	type = get_type(expr);
+	if (!type_is_ptr(type))
+		return 0;
+	state = get_extra_state(expr);
+	if (!state || !estate_get_single_value(state, &sval) || sval.value == 0)
+		return 0;
+
+	*tag = sval.uvalue & ~MTAG_OFFSET_MASK;
+	*offset = sval.uvalue & MTAG_OFFSET_MASK;
+	return 1;
+}
+
+/*
+ * The point of this function is to give you the mtag and the offset so
+ * you can look up the data in the DB.  It takes an expression.
+ *
+ * So say you give it "foo->bar".  Then it would give you the offset of "bar"
+ * and the implied value of "foo".  Or if you lookup "*foo" then the offset is
+ * zero and we look up the implied value of "foo.  But if the expression is
+ * foo, then if "foo" is a global variable, then we get the mtag and the offset
+ * is zero.  If "foo" is a local variable, then there is nothing to look up in
+ * the mtag_data table because that's handled by smatch_extra.c to this returns
+ * false.
+ *
+ */
 int expr_to_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
 {
+	*tag = 0;
 	*offset = 0;
 
+	if (bits_in_pointer != 64)
+		return 0;
+
 	expr = strip_expr(expr);
 	if (!expr)
 		return 0;
@@ -426,19 +336,54 @@
 	if (is_array(expr))
 		return get_array_mtag_offset(expr, tag, offset);
 
-	if (expr->type ==  EXPR_DEREF) {
-		*offset = get_member_offset_from_deref(expr);
-		if (*offset < 0)
-			return 0;
-		return get_mtag(expr->deref, tag);
-	}
+	if (expr->type == EXPR_PREOP && expr->op == '*') {
+		expr = strip_expr(expr->unop);
+		return get_implied_mtag_offset(expr, tag, offset);
+	} else if (expr->type == EXPR_DEREF) {
+		int tmp, tmp_offset = 0;
 
-	if (get_implied_mtag_offset(expr, tag, offset))
-		return 1;
+		while (expr->type == EXPR_DEREF) {
+			tmp = get_member_offset_from_deref(expr);
+			if (tmp < 0)
+				return 0;
+			tmp_offset += tmp;
+			expr = expr->deref;
+		}
+		*offset = tmp_offset;
+		if (expr->type == EXPR_PREOP && expr->op == '*') {
+			expr = strip_expr(expr->unop);
 
-	return get_mtag(expr, tag);
+			if (get_implied_mtag_offset(expr, tag, &tmp_offset)) {
+				// FIXME:  look it up recursively?
+				if (tmp_offset)
+					return 0;
+				return 1;
+			}
+			return 0;
+		} else if (expr->type == EXPR_SYMBOL) {
+			return get_symbol_mtag(expr->symbol, tag);
+		}
+		return 0;
+	} else if (expr->type == EXPR_SYMBOL) {
+		return get_symbol_mtag(expr->symbol, tag);
+	}
+	return 0;
 }
 
+/*
+ * This function takes an address and returns an sval.  Let's take some
+ * example things you might pass to it:
+ * foo->bar:
+ *   If we were only called from smatch_math, we wouldn't need to bother with
+ *   this because it's already been looked up in smatch_extra.c but this is
+ *   also called from other places so we have to check smatch_extra.c.
+ * &foo
+ *   If "foo" is global return the mtag for "foo".
+ * &foo.bar
+ *   If "foo" is global return the mtag for "foo" + the offset of ".bar".
+ * It also handles string literals.
+ *
+ */
 int get_mtag_sval(struct expression *expr, sval_t *sval)
 {
 	struct symbol *type;
@@ -454,14 +399,11 @@
 	if (!type_is_ptr(type))
 		return 0;
 	/*
-	 * There are only three options:
+	 * There are several options:
 	 *
-	 * 1) An array address:
-	 *    p = array;
-	 * 2) An address like so:
-	 *    p = &my_struct->member;
-	 * 3) A pointer:
-	 *    p = pointer;
+	 * If the expr is a string literal, that's an address/mtag.
+	 * SYM_ARRAY and SYM_FN are mtags.  There are "&foo" type addresses.
+	 * And there are saved pointers "p = &foo;"
 	 *
 	 */
 
@@ -468,22 +410,26 @@
 	if (expr->type == EXPR_STRING && get_string_mtag(expr, &tag))
 		goto found;
 
-	if (type->type == SYM_ARRAY && get_toplevel_mtag(expr->symbol, &tag))
+	if (expr->type == EXPR_SYMBOL &&
+	    (type->type == SYM_ARRAY || type->type == SYM_FN) &&
+	    get_toplevel_mtag(expr->symbol, &tag))
 		goto found;
 
+	if (expr->type == EXPR_PREOP && expr->op == '&') {
+		expr = strip_expr(expr->unop);
+		if (expr_to_mtag_offset(expr, &tag, &offset))
+			goto found;
+		return 0;
+	}
+
 	if (get_implied_mtag_offset(expr, &tag, &offset))
 		goto found;
 
-	if (expr->type != EXPR_PREOP || expr->op != '&')
+	return 0;
+found:
+	if (offset >= MTAG_OFFSET_MASK)
 		return 0;
-	expr = strip_expr(expr->unop);
 
-	if (!expr_to_mtag_offset(expr, &tag, &offset))
-		return 0;
-	if (offset > MTAG_OFFSET_MASK)
-		offset = MTAG_OFFSET_MASK;
-
-found:
 	sval->type = type;
 	sval->uvalue = tag | offset;
 
@@ -490,45 +436,6 @@
 	return 1;
 }
 
-static struct expression *remove_dereference(struct expression *expr)
-{
-	expr = strip_expr(expr);
-
-	if (expr->type == EXPR_PREOP && expr->op == '*')
-		return strip_expr(expr->unop);
-	return preop_expression(expr, '&');
-}
-
-int get_mtag_addr_sval(struct expression *expr, sval_t *sval)
-{
-	return get_mtag_sval(remove_dereference(expr), sval);
-}
-
-static void print_stored_to_mtag(int return_id, char *return_ranges, struct expression *expr)
-{
-	struct sm_state *sm;
-	char buf[256];
-	const char *param_name;
-	int param;
-
-	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
-		if (!sm->state->data)
-			continue;
-
-		param = get_param_num_from_sym(sm->sym);
-		if (param < 0)
-			continue;
-		param_name = get_param_name(sm);
-		if (!param_name)
-			continue;
-		if (strcmp(param_name, "$") == 0)
-			continue;
-
-		snprintf(buf, sizeof(buf), "%lld", *(mtag_t *)sm->state->data);
-		sql_insert_return_states(return_id, return_ranges, MEMORY_TAG, param, param_name, buf);
-	} END_FOR_EACH_SM(sm);
-}
-
 void register_mtag(int id)
 {
 	my_id = id;
@@ -542,18 +449,6 @@
 	 * bit 11-0 : offset
 	 *
 	 */
-	if (bits_in_pointer != 64)
-		return;
 
 	add_hook(&global_variable, BASE_HOOK);
-
-	add_function_assign_hook("kmalloc", &alloc_assign, NULL);
-	add_function_assign_hook("kzalloc", &alloc_assign, NULL);
-
-	select_return_states_hook(BUF_SIZE, &db_returns_buf_size);
-
-	add_hook(&match_call_info, FUNCTION_CALL_HOOK);
-	select_caller_info_hook(save_caller_info, MEMORY_TAG);
-	add_split_return_callback(&print_stored_to_mtag);
-	select_return_states_hook(MEMORY_TAG, db_returns_memory_tag);
 }
--- old/usr/src/tools/smatch/src/smatch_mtag_data.c	Mon Aug  5 08:38:38 2019
+++ new/usr/src/tools/smatch/src/smatch_mtag_data.c	Mon Aug  5 08:38:38 2019
@@ -36,11 +36,9 @@
 	return 0;
 }
 
-static struct range_list *select_orig_rl(sval_t sval)
+static struct range_list *select_orig(mtag_t tag, int offset)
 {
 	struct range_list *rl = NULL;
-	mtag_t tag = sval.uvalue & ~MTAG_OFFSET_MASK;
-	int offset = sval.uvalue & MTAG_OFFSET_MASK;
 
 	mem_sql(&save_rl, &rl, "select value from mtag_data where tag = %lld and offset = %d;",
 		tag, offset);
@@ -71,11 +69,8 @@
 	return 0;
 }
 
-void insert_mtag_data(sval_t sval, struct range_list *rl)
+static void insert_mtag_data(mtag_t tag, int offset, struct range_list *rl)
 {
-	mtag_t tag = sval.uvalue & ~MTAG_OFFSET_MASK;
-	int offset = sval.uvalue & MTAG_OFFSET_MASK;
-
 	rl = clone_rl_permanent(rl);
 
 	mem_sql(NULL, NULL, "delete from mtag_data where tag = %lld and offset = %d and type = %d",
@@ -87,8 +82,10 @@
 void update_mtag_data(struct expression *expr)
 {
 	struct range_list *orig, *new, *rl;
+	struct symbol *type;
 	char *name;
-	sval_t sval;
+	mtag_t tag;
+	int offset;
 
 	name = expr_to_var(expr);
 	if (is_kernel_param(name)) {
@@ -97,20 +94,27 @@
 	}
 	free_string(name);
 
-	if (!get_mtag_addr_sval(expr, &sval))
+	if (!expr_to_mtag_offset(expr, &tag, &offset))
 		return;
 
+	type = get_type(expr);
+	if ((offset == 0) &&
+	    (!type || type == &void_ctype ||
+	     type->type == SYM_STRUCT || type->type == SYM_UNION || type->type == SYM_ARRAY))
+		return;
+
 	get_absolute_rl(expr, &rl);
 
-	orig = select_orig_rl(sval);
+	orig = select_orig(tag, offset);
 	new = rl_union(orig, rl);
-	insert_mtag_data(sval, new);
+	insert_mtag_data(tag, offset, new);
 }
 
 static void match_global_assign(struct expression *expr)
 {
 	struct range_list *rl;
-	sval_t sval;
+	mtag_t tag;
+	int offset;
 	char *name;
 
 	name = expr_to_var(expr->left);
@@ -120,11 +124,11 @@
 	}
 	free_string(name);
 
-	if (!get_mtag_addr_sval(expr->left, &sval))
+	if (!expr_to_mtag_offset(expr->left, &tag, &offset))
 		return;
 
 	get_absolute_rl(expr->right, &rl);
-	insert_mtag_data(sval, rl);
+	insert_mtag_data(tag, offset, rl);
 }
 
 static int save_mtag_data(void *_unused, int argc, char **argv, char **azColName)
@@ -171,22 +175,25 @@
 }
 
 struct db_cache_results {
-	sval_t sval;
+	mtag_t tag;
 	struct range_list *rl;
 };
 static struct db_cache_results cached_results[8];
 
-static int get_rl_from_mtag_sval(sval_t sval, struct symbol *type, struct range_list **rl)
+static int get_rl_from_mtag_offset(mtag_t tag, int offset, struct symbol *type, struct range_list **rl)
 {
 	struct db_info db_info = {};
-	mtag_t tag;
-	int offset;
+	mtag_t merged = tag | offset;
 	static int idx;
 	int ret;
 	int i;
 
+	if (!type || type == &void_ctype ||
+	    (type->type == SYM_STRUCT || type->type == SYM_ARRAY || type->type == SYM_UNION))
+		return 0;
+
 	for (i = 0; i < ARRAY_SIZE(cached_results); i++) {
-		if (sval.uvalue == cached_results[i].sval.uvalue) {
+		if (merged == cached_results[i].tag) {
 			if (cached_results[i].rl) {
 				*rl = cached_results[i].rl;
 				return 1;
@@ -195,12 +202,6 @@
 		}
 	}
 
-	tag = sval.uvalue & ~MTAG_OFFSET_MASK;
-	offset = sval.uvalue & MTAG_OFFSET_MASK;
-	if (offset == MTAG_OFFSET_MASK) {
-		ret = 0;
-		goto update_cache;
-	}
 	db_info.type = type;
 
 	run_sql(get_vals, &db_info,
@@ -216,7 +217,7 @@
 	ret = 1;
 
 update_cache:
-	cached_results[idx].sval = sval;
+	cached_results[idx].tag = merged;
 	cached_results[idx].rl = db_info.rl;
 	idx = (idx + 1) % ARRAY_SIZE(cached_results);
 
@@ -231,16 +232,19 @@
 int get_mtag_rl(struct expression *expr, struct range_list **rl)
 {
 	struct symbol *type;
-	sval_t sval;
+	mtag_t tag;
+	int offset;
 
-	if (!get_mtag_addr_sval(expr, &sval))
+	if (!expr_to_mtag_offset(expr, &tag, &offset))
 		return 0;
+	if (offset >= MTAG_OFFSET_MASK)
+		return 0;
 
 	type = get_type(expr);
 	if (!type)
 		return 0;
 
-	return get_rl_from_mtag_sval(sval, type, rl);
+	return get_rl_from_mtag_offset(tag, offset, type, rl);
 }
 
 void register_mtag_data(int id)
--- old/usr/src/tools/smatch/src/smatch_mtag_map.c	Mon Aug  5 08:38:39 2019
+++ new/usr/src/tools/smatch/src/smatch_mtag_map.c	Mon Aug  5 08:38:39 2019
@@ -29,8 +29,9 @@
 static void match_assign(struct expression *expr)
 {
 	struct expression *left, *right;
-	mtag_t left_tag, right_tag;
+	mtag_t left_tag;
 	int offset;
+	sval_t sval;
 
 	if (expr->op != '=')
 		return;
@@ -38,19 +39,20 @@
 	left = strip_expr(expr->left);
 	right = strip_expr(expr->right);
 
-	if (left->type != EXPR_DEREF)
+	if (!type_is_ptr(get_type(right)))
 		return;
-
-	offset = get_member_offset_from_deref(left);
-	if (offset < 0)
+	if (!get_implied_value(right, &sval))
 		return;
-
-	if (!get_mtag(left->deref, &left_tag))
+	if (sval_cmp(sval, valid_ptr_min_sval) < 0 ||
+	    sval_cmp(sval, valid_ptr_max_sval) > 0)
 		return;
-	if (!get_mtag(right, &right_tag))
+	if (sval.uvalue & MTAG_OFFSET_MASK)
 		return;
 
-	sql_insert_mtag_map(right_tag, -offset, left_tag);
+	if (!expr_to_mtag_offset(left, &left_tag, &offset))
+		return;
+
+	sql_insert_mtag_map(sval.uvalue, -offset, left_tag);
 }
 
 void register_mtag_map(int id)
--- old/usr/src/tools/smatch/src/smatch_nul_terminator.c	Mon Aug  5 08:38:39 2019
+++ new/usr/src/tools/smatch/src/smatch_nul_terminator.c	Mon Aug  5 08:38:39 2019
@@ -247,6 +247,38 @@
 	return 0;
 }
 
+static void match_strnlen_test(struct expression *expr)
+{
+	struct expression *left, *tmp, *arg;
+	int cnt;
+
+	if (expr->type != EXPR_COMPARE)
+		return;
+	if (expr->op != SPECIAL_EQUAL && expr->op != SPECIAL_NOTEQUAL)
+		return;
+
+	left = strip_expr(expr->left);
+	cnt = 0;
+	while ((tmp = get_assigned_expr(left))) {
+		if (cnt++ > 3)
+			break;
+		left = tmp;
+	}
+
+	if (left->type != EXPR_CALL)
+		return;
+	if (!sym_name_is("strnlen", left->fn))
+		return;
+	arg = get_argument_from_call_expr(left->args, 0);
+	set_true_false_states_expr(my_id, arg,
+			(expr->op == SPECIAL_EQUAL) ? &terminated : NULL,
+			(expr->op == SPECIAL_NOTEQUAL) ? &terminated : NULL);
+	if (get_param_num(arg) >= 0)
+		set_true_false_states_expr(param_set_id, arg,
+				(expr->op == SPECIAL_EQUAL) ? &terminated : NULL,
+				(expr->op == SPECIAL_NOTEQUAL) ? &terminated : NULL);
+}
+
 void register_nul_terminator(int id)
 {
 	my_id = id;
@@ -260,6 +292,8 @@
 
 	select_caller_info_hook(caller_info_terminated, TERMINATED);
 	select_return_states_hook(TERMINATED, return_info_terminated);
+
+	add_hook(&match_strnlen_test, CONDITION_HOOK);
 }
 
 void register_nul_terminator_param_set(int id)
--- old/usr/src/tools/smatch/src/smatch_param_compare_limit.c	Mon Aug  5 08:38:39 2019
+++ new/usr/src/tools/smatch/src/smatch_param_compare_limit.c	Mon Aug  5 08:38:39 2019
@@ -180,8 +180,8 @@
 	struct compare_data *data;
 	struct var_sym *left, *right;
 	int left_param, right_param;
-	static char left_buf[256];
-	static char right_buf[256];
+	static char left_buf[248];
+	static char right_buf[248];
 	static char info_buf[256];
 	const char *tmp_name;
 
@@ -284,7 +284,7 @@
 	if (!parse_comparison(&value, op))
 		return 0;
 
-	snprintf(buf, sizeof(buf), value);
+	snprintf(buf, sizeof(buf), "%s", value);
 
 	p = buf;
 	if (*p++ != '$')
@@ -357,6 +357,7 @@
 {
 	compare_id = id;
 
+	set_dynamic_states(compare_id);
 	add_merge_hook(compare_id, &merge_compare_states);
 	add_split_return_callback(&print_return_comparison);
 
@@ -367,7 +368,7 @@
 {
 	link_id = id;
 
+	set_dynamic_states(link_id);
 	add_merge_hook(link_id, &merge_links);
-
 }
 
--- old/usr/src/tools/smatch/src/smatch_param_filter.c	Mon Aug  5 08:38:40 2019
+++ new/usr/src/tools/smatch/src/smatch_param_filter.c	Mon Aug  5 08:38:40 2019
@@ -148,6 +148,11 @@
 		return;
 	}
 
+	if (is_ignored_kernel_data(param_name)) {
+		insert_string(totally_filtered, (char *)sm->name);
+		return;
+	}
+
 	sql_insert_return_states(return_id, return_ranges, PARAM_FILTER, param,
 			param_name, show_rl(estate_rl(sm->state)));
 }
@@ -198,6 +203,7 @@
 {
 	my_id = id;
 
+	set_dynamic_states(my_id);
 	add_hook(&save_start_states, AFTER_DEF_HOOK);
 	add_hook(&free_start_states, AFTER_FUNC_HOOK);
 
--- old/usr/src/tools/smatch/src/smatch_param_limit.c	Mon Aug  5 08:38:40 2019
+++ new/usr/src/tools/smatch/src/smatch_param_limit.c	Mon Aug  5 08:38:40 2019
@@ -158,6 +158,9 @@
 		if (old && rl_equiv(estate_rl(old), estate_rl(state)))
 			continue;
 
+		if (is_ignored_kernel_data(param_name))
+			continue;
+
 		rl = generify_mtag_range(state);
 		sql_insert_return_states(return_id, return_ranges, PARAM_LIMIT,
 					 param, param_name, show_rl(rl));
@@ -193,6 +196,7 @@
 {
 	my_id = id;
 
+	set_dynamic_states(my_id);
 	add_hook(&save_start_states, AFTER_DEF_HOOK);
 	add_hook(&free_start_states, AFTER_FUNC_HOOK);
 
--- old/usr/src/tools/smatch/src/smatch_param_set.c	Mon Aug  5 08:38:40 2019
+++ new/usr/src/tools/smatch/src/smatch_param_set.c	Mon Aug  5 08:38:40 2019
@@ -164,7 +164,6 @@
 	struct string_list *set_list = NULL;
 	char *math_str;
 	char buf[256];
-	sval_t sval;
 
 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
 		if (!estate_rl(sm->state))
@@ -188,12 +187,13 @@
 			insert_string(&set_list, (char *)sm->name);
 			continue;
 		}
+		if (is_recursive_member(param_name)) {
+			insert_string(&set_list, (char *)sm->name);
+			continue;
+		}
 
-		if (rl_to_sval(rl, &sval)) {
+		if (is_ignored_kernel_data(param_name)) {
 			insert_string(&set_list, (char *)sm->name);
-			sql_insert_return_states(return_id, return_ranges,
-					param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
-					param, param_name, show_rl(rl));
 			continue;
 		}
 
@@ -260,6 +260,7 @@
 {
 	my_id = id;
 
+	set_dynamic_states(my_id);
 	add_extra_mod_hook(&extra_mod_hook);
 	add_hook(match_array_assignment, ASSIGNMENT_HOOK);
 	add_unmatched_state_hook(my_id, &unmatched_state);
--- old/usr/src/tools/smatch/src/smatch_param_to_mtag_data.c	Mon Aug  5 08:38:41 2019
+++ new/usr/src/tools/smatch/src/smatch_param_to_mtag_data.c	Mon Aug  5 08:38:41 2019
@@ -76,6 +76,18 @@
 	return &merged;
 }
 
+static bool is_local_var(struct expression *expr)
+{
+	struct symbol *sym;
+
+	if (!expr || expr->type != EXPR_SYMBOL)
+		return false;
+	sym = expr->symbol;
+	if (!(sym->ctype.modifiers & MOD_TOPLEVEL))
+		return true;
+	return false;
+}
+
 static void match_assign(struct expression *expr)
 {
 	struct expression *left;
@@ -88,6 +100,8 @@
 	if (expr->op != '=')
 		return;
 	left = strip_expr(expr->left);
+	if (is_local_var(left))
+		return;
 	right_sym = expr_to_sym(expr->right);
 	if (!right_sym)
 		return;
@@ -105,28 +119,6 @@
 	free_string(name);
 }
 
-#if 0
-static void save_mtag_to_map(struct expression *expr, mtag_t tag, int offset, int param, char *key, char *value)
-{
-	struct expression *arg, *gen_expr;
-	mtag_t arg_tag;
-
-	arg = get_argument_from_call_expr(expr->args, param);
-	if (!arg)
-		return;
-
-	gen_expr = gen_expression_from_key(arg, key);
-	if (!gen_expr)
-		return;
-
-	if (!get_mtag(gen_expr, &arg_tag))
-		arg_tag = 0;
-
-	if (local_debug)
-		sm_msg("finding mtag for '%s' %lld", expr_to_str(gen_expr), arg_tag);
-}
-#endif
-
 static void propogate_assignment(struct expression *expr, mtag_t tag, int offset, int param, char *key)
 {
 	struct expression *arg;
@@ -158,6 +150,7 @@
 	struct range_list *rl;
 	mtag_t arg_tag;
 	mtag_t alias;
+	int arg_offset;
 
 	arg = get_argument_from_call_expr(expr->args, param);
 	if (!arg)
@@ -174,7 +167,8 @@
 
 //	insert_mtag_data(alias, offset, rl);
 
-	if (get_mtag(gen_expr, &arg_tag))
+	// 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);
 }
 
@@ -229,6 +223,7 @@
 {
 	my_id = id;
 
+	set_dynamic_states(my_id);
 	add_hook(&match_assign, ASSIGNMENT_HOOK);
 	select_return_states_hook(MTAG_ASSIGN, &call_does_mtag_assign);
 	add_merge_hook(my_id, &merge_tag_info);
--- old/usr/src/tools/smatch/src/smatch_param_used.c	Mon Aug  5 08:38:41 2019
+++ new/usr/src/tools/smatch/src/smatch_param_used.c	Mon Aug  5 08:38:41 2019
@@ -31,7 +31,7 @@
 
 	if (!option_info)
 		return;
-	if (__in_fake_assign)
+	if (__in_fake_assign || __in_fake_parameter_assign || __in_function_def)
 		return;
 
 	arg = get_param_num_from_sym(sym);
@@ -51,7 +51,7 @@
 
 	arg_nr = get_param_num_from_sym(sym);
 	if (arg_nr >= 0)
-		set_state(my_id, name, sym, &used);
+		set_state_stree(&used_stree, my_id, name, sym, &used);
 free:
 	free_string(name);
 }
@@ -69,7 +69,12 @@
 		name = get_param_name(tmp);
 		if (!name)
 			continue;
+		if (is_recursive_member(name))
+			continue;
 
+		if (is_ignored_kernel_data(name))
+			continue;
+
 		sql_insert_return_implies(PARAM_USED, arg, name, "");
 	} END_FOR_EACH_SM(tmp);
 
--- old/usr/src/tools/smatch/src/smatch_parse_call_math.c	Mon Aug  5 08:38:41 2019
+++ new/usr/src/tools/smatch/src/smatch_parse_call_math.c	Mon Aug  5 08:38:41 2019
@@ -114,7 +114,7 @@
 		pop_rl(&rl_stack);
 }
 
-static int read_rl_from_var(struct expression *call, char *p, char **end, struct range_list **rl)
+static int read_rl_from_var(struct expression *call, const char *p, const char **end, struct range_list **rl)
 {
 	struct expression *arg;
 	struct smatch_state *state;
@@ -125,7 +125,7 @@
 	int star;
 
 	p++;
-	param = strtol(p, &p, 10);
+	param = strtol(p, (char **)&p, 10);
 
 	arg = get_argument_from_call_expr(call->args, param);
 	if (!arg)
@@ -165,7 +165,7 @@
 	return 1;
 }
 
-static int read_var_num(struct expression *call, char *p, char **end, struct range_list **rl)
+static int read_var_num(struct expression *call, const char *p, const char **end, struct range_list **rl)
 {
 	sval_t sval;
 
@@ -176,7 +176,7 @@
 		return read_rl_from_var(call, p, end, rl);
 
 	sval.type = &llong_ctype;
-	sval.value = strtoll(p, end, 10);
+	sval.value = strtoll(p, (char **)end, 10);
 	if (*end == p)
 		return 0;
 	*rl = alloc_rl(sval, sval);
@@ -183,7 +183,7 @@
 	return 1;
 }
 
-static char *read_op(char *p)
+static const char *read_op(const char *p)
 {
 	while (*p == ' ')
 		p++;
@@ -199,14 +199,14 @@
 	}
 }
 
-int parse_call_math_rl(struct expression *call, char *math, struct range_list **rl)
+int parse_call_math_rl(struct expression *call, const char *math, struct range_list **rl)
 {
 	struct range_list *tmp;
-	char *c;
+	const char *c;
 
 	/* try to implement shunting yard algorithm. */
 
-	c = (char *)math;
+	c = math;
 	while (1) {
 		if (option_debug)
 			sm_msg("parsing %s", c);
@@ -344,6 +344,16 @@
 	return format_name_sym_helper(buf, remaining, name, sym);
 }
 
+static int is_mtag_sval(sval_t sval)
+{
+	if (!is_ptr_type(sval.type))
+		return 0;
+	if (sval_cmp(sval, valid_ptr_min_sval) >= 0 &&
+	    sval_cmp(sval, valid_ptr_max_sval) <= 0)
+		return 1;
+	return 0;
+}
+
 static int format_expr_helper(char *buf, int remaining, struct expression *expr)
 {
 	sval_t sval;
@@ -380,7 +390,7 @@
 		return cur - buf;
 	}
 
-	if (get_implied_value(expr, &sval)) {
+	if (!param_was_set(expr) && get_implied_value(expr, &sval) && !is_mtag_sval(sval)) {
 		ret = snprintf(cur, remaining, "%s", sval_to_str(sval));
 		remaining -= ret;
 		if (remaining <= 0)
@@ -435,6 +445,7 @@
 	char buf[256] = "";
 	int ret;
 	int cnt = 0;
+	sval_t sval;
 
 	expr = get_assigned_expr_name_sym(name, sym);
 	if (!expr)
@@ -445,6 +456,9 @@
 			break;
 	}
 
+	if (get_implied_value(expr, &sval))
+		return NULL;
+
 	ret = format_expr_helper(buf, sizeof(buf), expr);
 	if (ret == 0)
 		return NULL;
@@ -493,7 +507,7 @@
 	while (*p) {
 		if (*p == '$') {
 			p++;
-			param = strtol(p, &p, 10);
+			param = strtol(p, (char **)&p, 10);
 			arg = get_argument_from_call_expr(call->args, param);
 			if (!arg)
 				return NULL;
@@ -643,6 +657,8 @@
 
 	my_id = id;
 
+	set_dynamic_states(my_id);
+
 	for (i = 0; i < ARRAY_SIZE(alloc_functions); i++)
 		add_function_assign_hook(alloc_functions[i].func, &match_alloc,
 				         INT_PTR(alloc_functions[i].param));
--- old/usr/src/tools/smatch/src/smatch_passes_array_size.c	Mon Aug  5 08:38:42 2019
+++ new/usr/src/tools/smatch/src/smatch_passes_array_size.c	Mon Aug  5 08:38:42 2019
@@ -39,11 +39,15 @@
 static void match_call(struct expression *expr)
 {
 	struct expression *arg;
-	struct symbol *type;
+	struct symbol *type, *arg_type;
 	int size, bytes;
 	int i, nr;
 	char buf[16];
+	char elem_count[8];
+	char byte_count[8];
 
+	snprintf(elem_count, sizeof(elem_count), "%d", ELEM_COUNT);
+	snprintf(byte_count, sizeof(byte_count), "%d", BYTE_COUNT);
 
 	i = -1;
 	FOR_EACH_PTR(expr->args, arg) {
@@ -51,12 +55,16 @@
 		type = get_type(arg);
 		if (!type || (type->type != SYM_PTR && type->type != SYM_ARRAY))
 			continue;
+		arg_type = get_arg_type(expr->fn, i);
+		if (arg_type != type)
+			continue;
+
 		size = get_array_size(arg);
 		if (size > 0) {
 			nr = find_param_eq(expr, size);
 			if (nr >= 0) {
-				snprintf(buf, sizeof(buf), "%d", nr);
-				sql_insert_caller_info(expr, ARRAYSIZE_ARG, i, buf, "");
+				snprintf(buf, sizeof(buf), "==$%d", nr);
+				sql_insert_caller_info(expr, ELEM_COUNT, i, buf, elem_count);
 				continue;
 			}
 		}
@@ -64,8 +72,8 @@
 		if (bytes > 0) {
 			nr = find_param_eq(expr, bytes);
 			if (nr >= 0) {
-				snprintf(buf, sizeof(buf), "%d", nr);
-				sql_insert_caller_info(expr, SIZEOF_ARG, i, buf, "");
+				snprintf(buf, sizeof(buf), "==$%d", nr);
+				sql_insert_caller_info(expr, BYTE_COUNT, i, buf, byte_count);
 				continue;
 			}
 		}
--- old/usr/src/tools/smatch/src/smatch_ranges.c	Mon Aug  5 08:38:42 2019
+++ new/usr/src/tools/smatch/src/smatch_ranges.c	Mon Aug  5 08:38:42 2019
@@ -26,27 +26,77 @@
 			 "permanent ranges", perm_data_range);
 __DECLARE_ALLOCATOR(struct ptr_list, rl_ptrlist);
 
+static bool is_err_ptr(sval_t sval)
+{
+	if (option_project != PROJ_KERNEL)
+		return false;
+	if (!type_is_ptr(sval.type))
+		return false;
+	if (sval.uvalue < -4095ULL)
+		return false;
+	return true;
+}
+
+static char *get_err_pointer_str(struct data_range *drange)
+{
+	static char buf[20];
+
+	/*
+	 * The kernel has error pointers where you do essentially:
+	 *
+	 * return (void *)(unsigned long)-12;
+	 *
+	 * But what I want here is to print -12 instead of the unsigned version
+	 * of that.
+	 *
+	 */
+	if (!is_err_ptr(drange->min))
+		return NULL;
+
+	if (drange->min.value == drange->max.value)
+		snprintf(buf, sizeof(buf), "(%lld)", drange->min.value);
+	else
+		snprintf(buf, sizeof(buf), "(%lld)-(%lld)", drange->min.value, drange->max.value);
+	return buf;
+}
+
 char *show_rl(struct range_list *list)
 {
+	struct data_range *prev_drange = NULL;
 	struct data_range *tmp;
-	char full[512];
+	char full[255];
+	char *p = full;
+	char *prev = full;
+	char *err_ptr;
+	int remain;
 	int i = 0;
 
 	full[0] = '\0';
-	full[sizeof(full) - 1] = '\0';
+
 	FOR_EACH_PTR(list, tmp) {
-		if (i++)
-			strncat(full, ",", 254 - strlen(full));
-		if (sval_cmp(tmp->min, tmp->max) == 0) {
-			strncat(full, sval_to_str(tmp->min), 254 - strlen(full));
-			continue;
+		remain = full + sizeof(full) - p;
+		if (remain < 48) {
+			snprintf(prev, full + sizeof(full) - prev, ",%s-%s",
+				 sval_to_str(prev_drange->min),
+				 sval_to_str(sval_type_max(prev_drange->min.type)));
+			break;
 		}
-		strncat(full, sval_to_str(tmp->min), 254 - strlen(full));
-		strncat(full, "-", 254 - strlen(full));
-		strncat(full, sval_to_str(tmp->max), 254 - strlen(full));
+		prev_drange = tmp;
+		prev = p;
+
+		err_ptr = get_err_pointer_str(tmp);
+		if (err_ptr) {
+			p += snprintf(p, remain, "%s%s", i++ ? "," : "", err_ptr);
+		} else if (sval_cmp(tmp->min, tmp->max) == 0) {
+			p += snprintf(p, remain, "%s%s", i++ ? "," : "",
+				      sval_to_str(tmp->min));
+		} else {
+			p += snprintf(p, remain, "%s%s-%s", i++ ? "," : "",
+				      sval_to_str(tmp->min),
+				      sval_to_str(tmp->max));
+		}
 	} END_FOR_EACH_PTR(tmp);
-	if (strlen(full) == sizeof(full) - 1)
-		full[sizeof(full) - 2] = '+';
+
 	return alloc_sname(full);
 }
 
@@ -79,6 +129,18 @@
 	return 0;
 }
 
+static int truncates_nicely(struct symbol *type, sval_t min, sval_t max)
+{
+	unsigned long long mask;
+	int bits = type_bits(type);
+
+	if (bits >= type_bits(min.type))
+		return 0;
+
+	mask = -1ULL << bits;
+	return (min.uvalue & mask) == (max.uvalue & mask);
+}
+
 static void add_range_t(struct symbol *type, struct range_list **rl, sval_t min, sval_t max)
 {
 	/* If we're just adding a number, cast it and add it */
@@ -93,11 +155,16 @@
 		return;
 	}
 
+	if (truncates_nicely(type, min, max)) {
+		add_range(rl, sval_cast(type, min), sval_cast(type, max));
+		return;
+	}
+
 	/*
 	 * If the range we are adding has more bits than the range type then
 	 * add the whole range type.  Eg:
 	 * 0x8000000000000000 - 0xf000000000000000 -> cast to int
-	 * This isn't totally the right thing to do.  We could be more granular.
+	 *
 	 */
 	if (sval_too_big(type, min) || sval_too_big(type, max)) {
 		add_range(rl, sval_type_min(type), sval_type_max(type));
@@ -138,10 +205,10 @@
 
 static int str_to_comparison_arg_helper(const char *str,
 		struct expression *call, int *comparison,
-		struct expression **arg, char **endp)
+		struct expression **arg, const char **endp)
 {
 	int param;
-	char *c = (char *)str;
+	const char *c = str;
 
 	if (*c != '[')
 		return 0;
@@ -171,6 +238,8 @@
 		c++;
 		c++;
 		*comparison = SPECIAL_NOTEQUAL;
+	} else if (*c == '$') {
+		*comparison = SPECIAL_EQUAL;
 	} else {
 		return 0;
 	}
@@ -179,8 +248,8 @@
 		return 0;
 	c++;
 
-	param = strtoll(c, &c, 10);
-	if (*c == ']')
+	param = strtoll(c, (char **)&c, 10);
+	if (*c == ',' || *c == ']')
 		c++; /* skip the ']' character */
 	if (endp)
 		*endp = (char *)c;
@@ -218,7 +287,7 @@
 	return str_to_comparison_arg_helper(str, call, comparison, arg, NULL);
 }
 
-static int get_val_from_key(int use_max, struct symbol *type, char *c, struct expression *call, char **endp, sval_t *sval)
+static int get_val_from_key(int use_max, struct symbol *type, const char *c, struct expression *call, const char **endp, sval_t *sval)
 {
 	struct expression *arg;
 	int comparison;
@@ -318,7 +387,7 @@
 	*rl = cast_rl(rl_type(*rl), ret_rl);
 }
 
-static struct range_list *filter_by_comparison_call(char *c, struct expression *call, char **endp, struct range_list *start_rl)
+static struct range_list *filter_by_comparison_call(const char *c, struct expression *call, const char **endp, struct range_list *start_rl)
 {
 	struct symbol *type;
 	struct expression *arg;
@@ -344,9 +413,9 @@
 	return cast_rl(rl_type(start_rl), casted_start);
 }
 
-static sval_t parse_val(int use_max, struct expression *call, struct symbol *type, char *c, char **endp)
+static sval_t parse_val(int use_max, struct expression *call, struct symbol *type, const char *c, const char **endp)
 {
-	char *start = c;
+	const char *start = c;
 	sval_t ret;
 
 	if (!strncmp(start, "max", 3)) {
@@ -398,17 +467,17 @@
 		/* this parses [==p0] comparisons */
 		get_val_from_key(1, type, start, call, &c, &ret);
 	} else if (type_positive_bits(type) == 64) {
-		ret = sval_type_val(type, strtoull(start, &c, 0));
+		ret = sval_type_val(type, strtoull(start, (char **)&c, 0));
 	} else {
-		ret = sval_type_val(type, strtoll(start, &c, 0));
+		ret = sval_type_val(type, strtoll(start, (char **)&c, 0));
 	}
 	*endp = c;
 	return ret;
 }
 
-static char *jump_to_call_math(char *value)
+static const char *jump_to_call_math(const char *value)
 {
-	char *c = value;
+	const char *c = value;
 
 	while (*c && *c != '[')
 		c++;
@@ -422,12 +491,13 @@
 	return c;
 }
 
-static void str_to_rl_helper(struct expression *call, struct symbol *type, char *str, char **endp, struct range_list **rl)
+static void str_to_rl_helper(struct expression *call, struct symbol *type, const char *str, const char **endp, struct range_list **rl)
 {
 	struct range_list *rl_tmp = NULL;
-	sval_t min, max;
-	char *c;
+	sval_t prev_min, min, max;
+	const char *c;
 
+	prev_min = sval_type_min(type);
 	min = sval_type_min(type);
 	max = sval_type_max(type);
 	c = str;
@@ -457,8 +527,12 @@
 			continue;
 		}
 		if (*c == '+') {
-			min = sval_type_max(type);
+			min = prev_min;
+			max = sval_type_max(type);
+			add_range_t(type, &rl_tmp, min, max);
 			c++;
+			if (*c == '[' || *c == '\0')
+				break;
 		}
 		if (*c != '-') {
 			sm_msg("debug XXX: trouble parsing %s c = %s", str, c);
@@ -472,8 +546,12 @@
 			max = sval_type_max(type);
 		if (*c == '+') {
 			max = sval_type_max(type);
+			add_range_t(type, &rl_tmp, min, max);
 			c++;
+			if (*c == '[' || *c == '\0')
+				break;
 		}
+		prev_min = max;
 		add_range_t(type, &rl_tmp, min, max);
 		if (*c == ')')
 			c++;
@@ -485,11 +563,11 @@
 	*endp = c;
 }
 
-static void str_to_dinfo(struct expression *call, struct symbol *type, char *value, struct data_info *dinfo)
+static void str_to_dinfo(struct expression *call, struct symbol *type, const char *value, struct data_info *dinfo)
 {
 	struct range_list *math_rl;
-	char *call_math;
-	char *c;
+	const char *call_math;
+	const char *c;
 	struct range_list *rl = NULL;
 
 	if (!type)
@@ -533,15 +611,35 @@
 	dinfo->value_ranges = rl;
 }
 
+static int rl_is_sane(struct range_list *rl)
+{
+	struct data_range *tmp;
+	struct symbol *type;
+
+	type = rl_type(rl);
+	FOR_EACH_PTR(rl, tmp) {
+		if (!sval_fits(type, tmp->min))
+			return 0;
+		if (!sval_fits(type, tmp->max))
+			return 0;
+		if (sval_cmp(tmp->min, tmp->max) > 0)
+			return 0;
+	} END_FOR_EACH_PTR(tmp);
+
+	return 1;
+}
+
 void str_to_rl(struct symbol *type, char *value, struct range_list **rl)
 {
 	struct data_info dinfo = {};
 
 	str_to_dinfo(NULL, type, value, &dinfo);
+	if (!rl_is_sane(dinfo.value_ranges))
+		dinfo.value_ranges = alloc_whole_rl(type);
 	*rl = dinfo.value_ranges;
 }
 
-void call_results_to_rl(struct expression *expr, struct symbol *type, char *value, struct range_list **rl)
+void call_results_to_rl(struct expression *expr, struct symbol *type, const char *value, struct range_list **rl)
 {
 	struct data_info dinfo = {};
 
@@ -561,6 +659,25 @@
 	return 0;
 }
 
+int is_unknown_ptr(struct range_list *rl)
+{
+	struct data_range *drange;
+	int cnt = 0;
+
+	if (is_whole_rl(rl))
+		return 1;
+
+	FOR_EACH_PTR(rl, drange) {
+		if (++cnt >= 3)
+			return 0;
+		if (sval_cmp(drange->min, valid_ptr_min_sval) == 0 &&
+		    sval_cmp(drange->max, valid_ptr_max_sval) == 0)
+			return 1;
+	} END_FOR_EACH_PTR(drange);
+
+	return 0;
+}
+
 int is_whole_rl_non_zero(struct range_list *rl)
 {
 	struct data_range *drange;
@@ -672,6 +789,55 @@
 	return alloc_rl(sval_type_min(type), sval_type_max(type));
 }
 
+static bool collapse_pointer_rl(struct range_list **rl, sval_t min, sval_t max)
+{
+	struct range_list *new_rl = NULL;
+	struct data_range *tmp;
+	static bool recurse;
+	bool ret = false;
+	int cnt = 0;
+
+	/*
+	 * With the mtag work, then we end up getting huge lists of mtags.
+	 * That seems cool, but the problem is that we can only store about
+	 * 8-10 mtags in the DB before we truncate the list.  Also the mtags
+	 * aren't really used at all so it's a waste of resources for now...
+	 * In the future, we maybe will revisit this code.
+	 *
+	 */
+
+	if (recurse)
+		return false;
+	recurse = true;
+	if (!type_is_ptr(min.type))
+		goto out;
+
+	if (ptr_list_size((struct ptr_list *)*rl) < 8)
+		goto out;
+	FOR_EACH_PTR(*rl, tmp) {
+		if (!is_err_ptr(tmp->min))
+			cnt++;
+	} END_FOR_EACH_PTR(tmp);
+	if (cnt < 8)
+		goto out;
+
+	FOR_EACH_PTR(*rl, tmp) {
+		if (sval_cmp(tmp->min, valid_ptr_min_sval) >= 0 &&
+		    sval_cmp(tmp->max, valid_ptr_max_sval) <= 0)
+			add_range(&new_rl, valid_ptr_min_sval, valid_ptr_max_sval);
+		else
+			add_range(&new_rl, tmp->min, tmp->max);
+	} END_FOR_EACH_PTR(tmp);
+
+	add_range(&new_rl, min, max);
+
+	*rl = new_rl;
+	ret = true;
+out:
+	recurse = false;
+	return ret;
+}
+
 extern int rl_ptrlist_hack;
 void add_range(struct range_list **list, sval_t min, sval_t max)
 {
@@ -712,6 +878,9 @@
 		max = sval_type_max(min.type);
 	}
 
+	if (collapse_pointer_rl(list, min, max))
+		return;
+
 	/*
 	 * FIXME:  This has a problem merging a range_list like: min-0,3-max
 	 * with a range like 1-2.  You end up with min-2,3-max instead of
@@ -1219,21 +1388,15 @@
 	return ret;
 }
 
-static int rl_is_sane(struct range_list *rl)
+int rl_fits_in_type(struct range_list *rl, struct symbol *type)
 {
-	struct data_range *tmp;
-	struct symbol *type;
-
-	type = rl_type(rl);
-	FOR_EACH_PTR(rl, tmp) {
-		if (!sval_fits(type, tmp->min))
-			return 0;
-		if (!sval_fits(type, tmp->max))
-			return 0;
-		if (sval_cmp(tmp->min, tmp->max) > 0)
-			return 0;
-	} END_FOR_EACH_PTR(tmp);
-
+	if (type_bits(rl_type(rl)) <= type_bits(type))
+		return 1;
+	if (sval_cmp(rl_max(rl), sval_type_max(type)) > 0)
+		return 0;
+	if (sval_is_negative(rl_min(rl)) &&
+	    sval_cmp(rl_min(rl), sval_type_min(type)) < 0)
+		return 0;
 	return 1;
 }
 
@@ -1310,64 +1473,76 @@
 	return ret;
 }
 
-struct range_list *rl_invert(struct range_list *orig)
+struct range_list *rl_filter(struct range_list *rl, struct range_list *filter)
 {
-	struct range_list *ret = NULL;
 	struct data_range *tmp;
-	sval_t gap_min, abs_max, sval;
 
-	if (!orig)
-		return NULL;
-	if (type_bits(rl_type(orig)) < 0)  /* void type mostly */
-		return NULL;
-
-	gap_min = sval_type_min(rl_min(orig).type);
-	abs_max = sval_type_max(rl_max(orig).type);
-
-	FOR_EACH_PTR(orig, tmp) {
-		if (sval_cmp(tmp->min, gap_min) > 0) {
-			sval = sval_type_val(tmp->min.type, tmp->min.value - 1);
-			add_range(&ret, gap_min, sval);
-		}
-		if (sval_cmp(tmp->max, abs_max) == 0)
-			return ret;
-		gap_min = sval_type_val(tmp->max.type, tmp->max.value + 1);
+	FOR_EACH_PTR(filter, tmp) {
+		rl = remove_range(rl, tmp->min, tmp->max);
 	} END_FOR_EACH_PTR(tmp);
 
-	if (sval_cmp(gap_min, abs_max) <= 0)
-		add_range(&ret, gap_min, abs_max);
-
-	return ret;
+	return rl;
 }
 
-struct range_list *rl_filter(struct range_list *rl, struct range_list *filter)
+struct range_list *do_intersection(struct range_list *one_rl, struct range_list *two_rl)
 {
-	struct data_range *tmp;
+	struct data_range *one, *two;
+	struct range_list *ret = NULL;
 
-	FOR_EACH_PTR(filter, tmp) {
-		rl = remove_range(rl, tmp->min, tmp->max);
-	} END_FOR_EACH_PTR(tmp);
 
-	return rl;
+	PREPARE_PTR_LIST(one_rl, one);
+	PREPARE_PTR_LIST(two_rl, two);
+
+	while (true) {
+		if (!one || !two)
+			break;
+		if (sval_cmp(one->max, two->min) < 0) {
+			NEXT_PTR_LIST(one);
+			continue;
+		}
+		if (sval_cmp(one->min, two->min) < 0 && sval_cmp(one->max, two->max) <= 0) {
+			add_range(&ret, two->min, one->max);
+			NEXT_PTR_LIST(one);
+			continue;
+		}
+		if (sval_cmp(one->min, two->min) >= 0 && sval_cmp(one->max, two->max) <= 0) {
+			add_range(&ret, one->min, one->max);
+			NEXT_PTR_LIST(one);
+			continue;
+		}
+		if (sval_cmp(one->min, two->min) < 0 && sval_cmp(one->max, two->max) > 0) {
+			add_range(&ret, two->min, two->max);
+			NEXT_PTR_LIST(two);
+			continue;
+		}
+		if (sval_cmp(one->min, two->max) <= 0 && sval_cmp(one->max, two->max) > 0) {
+			add_range(&ret, one->min, two->max);
+			NEXT_PTR_LIST(two);
+			continue;
+		}
+		if (sval_cmp(one->min, two->max) <= 0) {
+			sm_fatal("error calculating intersection of '%s' and '%s'", show_rl(one_rl), show_rl(two_rl));
+			return NULL;
+		}
+		NEXT_PTR_LIST(two);
+	}
+
+	FINISH_PTR_LIST(two);
+	FINISH_PTR_LIST(one);
+
+	return ret;
 }
 
 struct range_list *rl_intersection(struct range_list *one, struct range_list *two)
 {
-	struct range_list *one_orig;
-	struct range_list *two_orig;
 	struct range_list *ret;
 	struct symbol *ret_type;
 	struct symbol *small_type;
 	struct symbol *large_type;
 
-	if (!two)
+	if (!one || !two)
 		return NULL;
-	if (!one)
-		return NULL;
 
-	one_orig = one;
-	two_orig = two;
-
 	ret_type = rl_type(one);
 	small_type = rl_type(one);
 	large_type = rl_type(two);
@@ -1380,23 +1555,7 @@
 	one = cast_rl(large_type, one);
 	two = cast_rl(large_type, two);
 
-	ret = one;
-	one = rl_invert(one);
-	two = rl_invert(two);
-
-	ret = rl_filter(ret, one);
-	ret = rl_filter(ret, two);
-
-	one = cast_rl(small_type, one_orig);
-	two = cast_rl(small_type, two_orig);
-
-	one = rl_invert(one);
-	two = rl_invert(two);
-
-	ret = cast_rl(small_type, ret);
-	ret = rl_filter(ret, one);
-	ret = rl_filter(ret, two);
-
+	ret = do_intersection(one, two);
 	return cast_rl(ret_type, ret);
 }
 
@@ -1520,10 +1679,39 @@
 	return rl_union(ret, pos_pos);
 }
 
+static struct range_list *ptr_add_mult(struct range_list *left, int op, struct range_list *right)
+{
+	struct range_list *ret;
+	sval_t l_sval, r_sval, res;
+
+	/*
+	 * This function is sort of the wrong API because it takes two pointer
+	 * and adds them together.  The caller is expected to figure out
+	 * alignment.  Neither of those are the correct things to do.
+	 *
+	 * Really this function is quite bogus...
+	 */
+
+	if (rl_to_sval(left, &l_sval) && rl_to_sval(right, &r_sval)) {
+		res = sval_binop(l_sval, op, r_sval);
+		return alloc_rl(res, res);
+	}
+
+	if (rl_min(left).value != 0 || rl_max(right).value != 0) {
+		ret = alloc_rl(valid_ptr_min_sval, valid_ptr_max_sval);
+		return cast_rl(rl_type(left), ret);
+	}
+
+	return alloc_whole_rl(rl_type(left));
+}
+
 static struct range_list *handle_add_mult_rl(struct range_list *left, int op, struct range_list *right)
 {
 	sval_t min, max;
 
+	if (type_is_ptr(rl_type(left)) || type_is_ptr(rl_type(right)))
+		return ptr_add_mult(left, op, right);
+
 	if (sval_binop_overflows(rl_min(left), op, rl_min(right)))
 		return NULL;
 	min = sval_binop(rl_min(left), op, rl_min(right));
@@ -1645,27 +1833,157 @@
 	return cast_rl(rl_type(left), alloc_rl(zero, max));
 }
 
+static sval_t sval_lowest_set_bit(sval_t sval)
+{
+	sval_t ret = { .type = sval.type };
+	int i;
+
+	for (i = 0; i < 64; i++) {
+		if (sval.uvalue & 1ULL << i) {
+			ret.uvalue = (1ULL << i);
+			return ret;
+		}
+	}
+	return ret;
+}
+
+static struct range_list *handle_AND_rl_sval(struct range_list *rl, sval_t sval)
+{
+	struct range_list *known_rl;
+	sval_t zero = { 0 };
+	sval_t min;
+
+	zero.type = sval.type;
+	zero.value = 0;
+
+	if (sm_fls64(rl_max(rl).uvalue) < find_first_zero_bit(sval.uvalue) &&
+	    sm_fls64(rl_min(rl).uvalue) < find_first_zero_bit(sval.uvalue))
+		return rl;
+
+	min = sval_lowest_set_bit(sval);
+
+	if (min.value != 0) {
+		sval_t max, mod;
+
+		max = rl_max(rl);
+		mod = sval_binop(max, '%', min);
+		if (mod.value) {
+			max = sval_binop(max, '-', mod);
+			max.value++;
+			if (max.value > 0 && sval_cmp(max, rl_max(rl)) < 0)
+				rl = remove_range(rl, max, rl_max(rl));
+		}
+	}
+
+	known_rl = alloc_rl(min, sval);
+
+	rl = rl_intersection(rl, known_rl);
+	zero = rl_min(rl);
+	zero.value = 0;
+	add_range(&rl, zero, zero);
+
+	return rl;
+}
+
+static struct range_list *fudge_AND_rl(struct range_list *rl)
+{
+	struct range_list *ret;
+	sval_t min;
+
+	min = sval_lowest_set_bit(rl_min(rl));
+	ret = clone_rl(rl);
+	add_range(&ret, min, rl_min(rl));
+
+	return ret;
+}
+
 static struct range_list *handle_AND_rl(struct range_list *left, struct range_list *right)
 {
-	unsigned long long left_set, left_maybe;
-	unsigned long long right_set, right_maybe;
-	sval_t zero, max;
+	sval_t sval, zero;
+	struct range_list *rl;
 
-	return NULL;
+	if (rl_to_sval(left, &sval))
+		return handle_AND_rl_sval(right, sval);
+	if (rl_to_sval(right, &sval))
+		return handle_AND_rl_sval(left, sval);
 
-	left_set = rl_bits_always_set(left);
-	left_maybe = rl_bits_maybe_set(left);
+	left = fudge_AND_rl(left);
+	right = fudge_AND_rl(right);
 
-	right_set = rl_bits_always_set(right);
-	right_maybe = rl_bits_maybe_set(right);
+	rl = rl_intersection(left, right);
+	zero = rl_min(rl);
+	zero.value = 0;
+	add_range(&rl, zero, zero);
 
-	zero = max = rl_min(left);
-	zero.uvalue = 0;
-	max.uvalue = fls_mask((left_maybe | right_maybe) ^ (left_set & right_set));
+	return rl;
+}
 
-	return cast_rl(rl_type(left), alloc_rl(zero, max));
+static struct range_list *handle_lshift(struct range_list *left_orig, struct range_list *right_orig)
+{
+	struct range_list *left;
+	struct data_range *tmp;
+	struct range_list *ret = NULL;
+	sval_t zero = { .type = rl_type(left_orig), };
+	sval_t shift, min, max;
+	bool add_zero = false;
+
+	if (!rl_to_sval(right_orig, &shift) || sval_is_negative(shift))
+		return NULL;
+	if (shift.value == 0)
+		return left_orig;
+
+	/* Cast to unsigned for easier left shift math */
+	if (type_positive_bits(rl_type(left_orig)) < 32)
+		left = cast_rl(&uint_ctype, left_orig);
+	else if(type_positive_bits(rl_type(left_orig)) == 63)
+		left = cast_rl(&ullong_ctype, left_orig);
+	else
+		left = left_orig;
+
+	FOR_EACH_PTR(left, tmp) {
+		min = tmp->min;
+		max = tmp->max;
+
+		if (min.value == 0 || max.value > sval_type_max(max.type).uvalue >> shift.uvalue)
+			add_zero = true;
+		if (min.value == 0 && max.value == 0)
+			continue;
+		if (min.value == 0)
+			min.value = 1;
+		min = sval_binop(min, SPECIAL_LEFTSHIFT, shift);
+		max = sval_binop(max, SPECIAL_LEFTSHIFT, shift);
+		add_range(&ret, min, max);
+	} END_FOR_EACH_PTR(tmp);
+
+	if (!rl_fits_in_type(ret, rl_type(left_orig)))
+		add_zero = true;
+	ret = cast_rl(rl_type(left_orig), ret);
+	if (add_zero)
+		add_range(&ret, zero, zero);
+
+	return ret;
 }
 
+static struct range_list *handle_rshift(struct range_list *left_orig, struct range_list *right_orig)
+{
+	struct data_range *tmp;
+	struct range_list *ret = NULL;
+	sval_t shift, min, max;
+
+	if (!rl_to_sval(right_orig, &shift) || sval_is_negative(shift))
+		return NULL;
+	if (shift.value == 0)
+		return left_orig;
+
+	FOR_EACH_PTR(left_orig, tmp) {
+		min = sval_binop(tmp->min, SPECIAL_RIGHTSHIFT, shift);
+		max = sval_binop(tmp->max, SPECIAL_RIGHTSHIFT, shift);
+		add_range(&ret, min, max);
+	} END_FOR_EACH_PTR(tmp);
+
+	return ret;
+}
+
 struct range_list *rl_binop(struct range_list *left, int op, struct range_list *right)
 {
 	struct symbol *cast_type;
@@ -1712,10 +2030,10 @@
 	case '-':
 		ret = handle_sub_rl(left, right);
 		break;
-	/* FIXME:  Do the rest as well */
 	case SPECIAL_RIGHTSHIFT:
+		return handle_rshift(left, right);
 	case SPECIAL_LEFTSHIFT:
-		break;
+		return handle_lshift(left, right);
 	}
 
 	return ret;
--- old/usr/src/tools/smatch/src/smatch_real_absolute.c	Mon Aug  5 08:38:43 2019
+++ new/usr/src/tools/smatch/src/smatch_real_absolute.c	Mon Aug  5 08:38:43 2019
@@ -103,6 +103,9 @@
 	type = get_type(expr->left);
 	if (!type)
 		return;
+	if (type->type != SYM_PTR && type->type != SYM_BASETYPE &&
+	    type->type != SYM_ENUM)
+		return;
 
 	rl = cast_rl(type, rl);
 	if (is_whole_rl(rl) && !get_state_expr(my_id, expr->left))
@@ -128,6 +131,7 @@
 {
 	my_id = id;
 
+	set_dynamic_states(my_id);
 	add_pre_merge_hook(my_id, &pre_merge_hook);
 	add_unmatched_state_hook(my_id, &empty_state);
 	add_merge_hook(my_id, &merge_estates);
--- old/usr/src/tools/smatch/src/smatch_return_to_param.c	Mon Aug  5 08:38:43 2019
+++ new/usr/src/tools/smatch/src/smatch_return_to_param.c	Mon Aug  5 08:38:43 2019
@@ -56,7 +56,7 @@
 	char buf[256];
 
 	/* skip 'foo->'.  This was checked in the caller. */
-	skip = strlen(sym->ident->name) + 2;
+	skip = sym->ident->len + 2;
 
 	state = get_state(my_id, sym->ident->name, sym);
 	if (!state || !state->data)
@@ -87,46 +87,6 @@
 	return alloc_string(buf);
 }
 
-static char *map_assignment_long_to_short(struct sm_state *sm, const char *name, struct symbol *sym, struct symbol **new_sym, bool stack)
-{
-	struct expression *orig_expr;
-	struct symbol *orig_sym;
-	int len;
-	char buf[256];
-
-	orig_expr = sm->state->data;
-	if (!orig_expr)
-		return NULL;
-
-	/*
-	 * Say we have an assignment like:
-	 *     foo->bar->my_ptr = my_ptr;
-	 * We still expect the function to carry on using "my_ptr" as the
-	 * shorter name.  That's not a long to short mapping.
-	 *
-	 */
-	if (orig_expr->type == EXPR_SYMBOL)
-		return NULL;
-
-	orig_sym = expr_to_sym(orig_expr);
-	if (!orig_sym)
-		return NULL;
-	if (sym != orig_sym)
-		return NULL;
-
-	len = strlen(sm->state->name);
-	if (strncmp(name, sm->state->name, len) != 0)
-		return NULL;
-
-	if (name[len] == '.')
-		return NULL;
-	if (!stack && name[len] != '-')
-		return NULL;
-	snprintf(buf, sizeof(buf), "%s%s", sm->name, name + len);
-	*new_sym = sm->sym;
-	return alloc_string(buf);
-}
-
 /*
  * Normally, we expect people to consistently refer to variables by the shortest
  * name.  So they use "b->a" instead of "foo->bar.a" when both point to the
@@ -136,7 +96,7 @@
  * which in turn updates the longer name.
  *
  */
-static char *map_long_to_short_name_sym_helper(const char *name, struct symbol *sym, struct symbol **new_sym, bool stack)
+char *map_long_to_short_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym, bool use_stack)
 {
 	char *ret;
 	struct sm_state *sm;
@@ -145,32 +105,20 @@
 
 	FOR_EACH_SM(__get_cur_stree(), sm) {
 		if (sm->owner == my_id) {
-			ret = map_my_state_long_to_short(sm, name, sym, new_sym, stack);
-			if (ret)
+			ret = map_my_state_long_to_short(sm, name, sym, new_sym, use_stack);
+			if (ret) {
+				if (local_debug)
+					sm_msg("%s: my_state: name = '%s' sm = '%s'",
+					       __func__, name, show_sm(sm));
 				return ret;
+			}
 			continue;
 		}
-		if (sm->owner == check_assigned_expr_id) {
-			ret = map_assignment_long_to_short(sm, name, sym, new_sym, stack);
-			if (ret)
-				return ret;
-			continue;
-		}
 	} END_FOR_EACH_SM(sm);
 
 	return NULL;
 }
 
-char *map_long_to_short_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym)
-{
-	return map_long_to_short_name_sym_helper(name, sym, new_sym, 1);
-}
-
-char *map_long_to_short_name_sym_nostack(const char *name, struct symbol *sym, struct symbol **new_sym)
-{
-	return map_long_to_short_name_sym_helper(name, sym, new_sym, 0);
-}
-
 char *map_call_to_param_name_sym(struct expression *expr, struct symbol **sym)
 {
 	char *name;
@@ -280,6 +228,7 @@
 void register_return_to_param(int id)
 {
 	my_id = id;
+	set_dynamic_states(my_id);
 	add_modification_hook(my_id, &undef);
 }
 
--- old/usr/src/tools/smatch/src/smatch_returns.c	Mon Aug  5 08:38:44 2019
+++ new/usr/src/tools/smatch/src/smatch_returns.c	Mon Aug  5 08:38:44 2019
@@ -130,6 +130,7 @@
 {
 	RETURN_ID = id;
 
+	set_dynamic_states(RETURN_ID);
 	add_split_return_callback(match_return);
 }
 
--- old/usr/src/tools/smatch/src/smatch_scripts/build_kernel_data.sh	Mon Aug  5 08:38:44 2019
+++ new/usr/src/tools/smatch/src/smatch_scripts/build_kernel_data.sh	Mon Aug  5 08:38:44 2019
@@ -35,7 +35,8 @@
     fi
 fi
 
-$SCRIPT_DIR/test_kernel.sh --call-tree --info --param-mapper --spammy --data=$DATA_DIR
+BUILD_STATUS=0
+$SCRIPT_DIR/test_kernel.sh --call-tree --info --param-mapper --spammy --data=$DATA_DIR || BUILD_STATUS=$?
 
 for i in $SCRIPT_DIR/gen_* ; do
 	$i smatch_warns.txt -p=kernel
@@ -45,3 +46,4 @@
 
 $DATA_DIR/db/create_db.sh -p=kernel smatch_warns.txt
 
+exit $BUILD_STATUS
--- old/usr/src/tools/smatch/src/smatch_scripts/kpatch.sh	Mon Aug  5 08:38:44 2019
+++ new/usr/src/tools/smatch/src/smatch_scripts/kpatch.sh	Mon Aug  5 08:38:44 2019
@@ -4,7 +4,7 @@
 
 help()
 {
-    echo "Usage: $0 [--no-compile|--ammend] <filename>"
+    echo "Usage: $0 [--no-compile|--amend] <filename>"
     echo "You must be at the base of the kernel tree to run this."
     exit 1
 }
@@ -37,7 +37,7 @@
     if [[ "$1" == "--no-compile" ]] ; then
         NO_COMPILE=true
         shift
-    elif [[ "$1" == "--ammend" ]] ; then
+    elif [[ "$1" == "--amend" ]] ; then
         AMEND="--amend"
         shift
     else
@@ -53,8 +53,12 @@
 filename=$(basename $fullname)
 oname=$(echo ${fullname/.c/.o})
 
-MAIL_FILE=$TMP_DIR/${filename}.msg
+MSG_FILE=$TMP_DIR/${filename}.msg
+MAIL_FILE=$TMP_DIR/${filename}.mail
 
+# heat up the disk cache
+#git log --oneline $fullname | head -n 10 > /dev/null &
+
 echo "QC checklist"
 qc "Have you handled all the errors properly?"
 if git diff $fullname | grep ^+ | grep -qi alloc ; then
@@ -71,16 +75,30 @@
 #    make C=1 CHECK="scripts/coccicheck" $oname
 fi
 
-grepmail $fullname ~/var/mail/sent* | grep -i ^subject || echo -n ""
+for file in $(grep -l $fullname ~/var/mail/sent-*) ; do
+    grepmail $fullname $file | grep -i ^subject || echo -n ""
+done
 qc "Looks OK?"
 
-git log --oneline $fullname | head -n 10
-echo "Copy and paste one of these subjects?"
-read unused
-
 git add $fullname
-git commit --signoff $AMEND
 
+cat /dev/null > $MSG_FILE
+if [ "$AMEND" != "" ] ; then
+    git format-patch HEAD^ --stdout >> $MSG_FILE
+else
+    echo "" >> $MSG_FILE
+    echo "Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>" >> $MSG_FILE
+    echo "" >> $MSG_FILE
+    echo "# $sm_err" >> $MSG_FILE
+fi
+git log -10 --oneline $fullname | sed -e 's/^/# /' >> $MSG_FILE
+vim $MSG_FILE
+
+grep -v '^#' $MSG_FILE > $MSG_FILE.1
+mv $MSG_FILE.1 $MSG_FILE
+
+git commit $AMEND -F $MSG_FILE
+
 to_addr=$(./scripts/get_maintainer.pl -f --noroles --norolestats $fullname | head -n 1)
 cc_addr=$(./scripts/get_maintainer.pl -f --noroles --norolestats $fullname | tail -n +2 | \
     perl -ne 's/\n$/, /; print')
--- old/usr/src/tools/smatch/src/smatch_scripts/test_kernel.sh	Mon Aug  5 08:38:45 2019
+++ new/usr/src/tools/smatch/src/smatch_scripts/test_kernel.sh	Mon Aug  5 08:38:45 2019
@@ -41,6 +41,10 @@
     fi
 done
 
+# receive parameters from environment, which override
+[ -z "${SMATCH_ENV_TARGET:-}" ] || TARGET="$SMATCH_ENV_TARGET"
+[ -z "${SMATCH_ENV_BUILD_PARAM:-}" ] || BUILD_PARAM="$SMATCH_ENV_BUILD_PARAM"
+
 SCRIPT_DIR=$(dirname $0)
 if [ -e $SCRIPT_DIR/../smatch ] ; then
     cp $SCRIPT_DIR/../smatch $SCRIPT_DIR/../bak.smatch
@@ -55,9 +59,11 @@
 make clean
 find -name \*.c.smatch -exec rm \{\} \;
 make -j${NR_CPU} $ENDIAN -k CHECK="$CMD -p=kernel --file-output --succeed $*" \
-	C=1 $TARGET 2>&1 | tee $LOG
+	C=1 $BUILD_PARAM $TARGET 2>&1 | tee $LOG
+BUILD_STATUS=${PIPESTATUS[0]}
 find -name \*.c.smatch -exec cat \{\} \; -exec rm \{\} \; > $WLOG
 find -name \*.c.smatch.sql -exec cat \{\} \; -exec rm \{\} \; > $WLOG.sql
 find -name \*.c.smatch.caller_info -exec cat \{\} \; -exec rm \{\} \; > $WLOG.caller_info
 
-echo "Done.  The warnings are saved to $WLOG"
+echo "Done. Build with status $BUILD_STATUS. The warnings are saved to $WLOG"
+exit $BUILD_STATUS
--- old/usr/src/tools/smatch/src/smatch_slist.c	Mon Aug  5 08:38:45 2019
+++ new/usr/src/tools/smatch/src/smatch_slist.c	Mon Aug  5 08:38:45 2019
@@ -96,9 +96,9 @@
 	if (!a)
 		return 1;
 
-	if (a->owner > b->owner)
-		return -1;
 	if (a->owner < b->owner)
+		return -1;
+	if (a->owner > b->owner)
 		return 1;
 
 	ret = strcmp(a->name, b->name);
@@ -119,38 +119,67 @@
 	return 0;
 }
 
-static int cmp_sm_states(const struct sm_state *a, const struct sm_state *b, int preserve)
+int *dynamic_states;
+void allocate_dynamic_states_array(int num_checks)
 {
+	dynamic_states = calloc(num_checks + 1, sizeof(int));
+}
+
+void set_dynamic_states(unsigned short owner)
+{
+	dynamic_states[owner] = true;
+}
+
+bool has_dynamic_states(unsigned short owner)
+{
+	if (owner >= num_checks)
+		return false;
+	return dynamic_states[owner];
+}
+
+static int cmp_possible_sm(const struct sm_state *a, const struct sm_state *b, int preserve)
+{
 	int ret;
 
-	ret = cmp_tracker(a, b);
-	if (ret)
-		return ret;
+	if (a == b)
+		return 0;
 
-	/* todo:  add hook for smatch_extra.c */
-	if (a->state > b->state)
-		return -1;
-	if (a->state < b->state)
-		return 1;
-	/* This is obviously a massive disgusting hack but we need to preserve
-	 * the unmerged states for smatch extra because we use them in
-	 * smatch_db.c.  Meanwhile if we preserve all the other unmerged states
-	 * then it uses a lot of memory and we don't use it.  Hence this hack.
-	 *
-	 * Also sometimes even just preserving every possible SMATCH_EXTRA state
-	 * takes too much resources so we have to cap that.  Capping is probably
-	 * not often a problem in real life.
-	 */
-	if (a->owner == SMATCH_EXTRA && preserve) {
-		if (a == b)
-			return 0;
-		if (a->merged == 1 && b->merged == 0)
+	if (!has_dynamic_states(a->owner)) {
+		if (a->state > b->state)
 			return -1;
-		if (a->merged == 0)
+		if (a->state < b->state)
 			return 1;
+		return 0;
 	}
 
-	return 0;
+	if (a->owner == SMATCH_EXTRA) {
+		/*
+		 * In Smatch extra you can have borrowed implications.
+		 *
+		 * FIXME: review how borrowed implications work and if they
+		 * are the best way.  See also smatch_implied.c.
+		 *
+		 */
+		ret = cmp_tracker(a, b);
+		if (ret)
+			return ret;
+
+		/*
+		 * We want to preserve leaf states.  They're use to split
+		 * returns in smatch_db.c.
+		 *
+		 */
+		if (preserve) {
+			if (a->merged && !b->merged)
+				return -1;
+			if (!a->merged)
+				return 1;
+		}
+	}
+	if (!a->state->name || !b->state->name)
+		return 0;
+
+	return strcmp(a->state->name, b->state->name);
 }
 
 struct sm_state *alloc_sm_state(int owner, const char *name,
@@ -169,7 +198,6 @@
 	sm_state->pool = NULL;
 	sm_state->left = NULL;
 	sm_state->right = NULL;
-	sm_state->nr_children = 1;
 	sm_state->possible = NULL;
 	add_ptr_list(&sm_state->possible, sm_state);
 	return sm_state;
@@ -197,14 +225,16 @@
 {
 	struct sm_state *tmp;
 	int preserve = 1;
+	int cmp;
 
 	if (too_many_possible(to))
 		preserve = 0;
 
 	FOR_EACH_PTR(to->possible, tmp) {
-		if (cmp_sm_states(tmp, new, preserve) < 0)
+		cmp = cmp_possible_sm(tmp, new, preserve);
+		if (cmp < 0)
 			continue;
-		else if (cmp_sm_states(tmp, new, preserve) == 0) {
+		else if (cmp == 0) {
 			return;
 		} else {
 			INSERT_CURRENT(new, tmp);
@@ -214,11 +244,27 @@
 	add_ptr_list(&to->possible, new);
 }
 
-static void copy_possibles(struct sm_state *to, struct sm_state *from)
+static void copy_possibles(struct sm_state *to, struct sm_state *one, struct sm_state *two)
 {
+	struct sm_state *large = one;
+	struct sm_state *small = two;
 	struct sm_state *tmp;
 
-	FOR_EACH_PTR(from->possible, tmp) {
+	/*
+	 * We spend a lot of time copying the possible lists.  I've tried to
+	 * optimize the process a bit.
+	 *
+	 */
+
+	if (ptr_list_size((struct ptr_list *)two->possible) >
+	    ptr_list_size((struct ptr_list *)one->possible)) {
+		large = two;
+		small = one;
+	}
+
+	to->possible = clone_slist(large->possible);
+	add_possible_sm(to, to);
+	FOR_EACH_PTR(small->possible, tmp) {
 		add_possible_sm(to, tmp);
 	} END_FOR_EACH_PTR(tmp);
 }
@@ -234,8 +280,13 @@
 	return tmp;
 }
 
+static struct symbol *oom_func;
+static int oom_limit = 3000000;  /* Start with a 3GB limit */
 int out_of_memory(void)
 {
+	if (oom_func)
+		return 1;
+
 	/*
 	 * I decided to use 50M here based on trial and error.
 	 * It works out OK for the kernel and so it should work
@@ -243,6 +294,25 @@
 	 */
 	if (sm_state_counter * sizeof(struct sm_state) >= 100000000)
 		return 1;
+
+	/*
+	 * We're reading from statm to figure out how much memory we
+	 * are using.  The problem is that at the end of the function
+	 * we release the memory, so that it can be re-used but it
+	 * stays in cache, it's not released to the OS.  So then if
+	 * we allocate memory for different purposes we can easily
+	 * hit the 3GB limit on the next function, so that's why I give
+	 * the next function an extra 100MB to work with.
+	 *
+	 */
+	if (get_mem_kb() > oom_limit) {
+		oom_func = cur_func_sym;
+		final_pass++;
+		sm_perror("OOM: %luKb sm_state_count = %d", get_mem_kb(), sm_state_counter);
+		final_pass--;
+		return 1;
+	}
+
 	return 0;
 }
 
@@ -297,6 +367,10 @@
 
 	free_stack_and_strees(&all_pools);
 	sm_state_counter = 0;
+	if (oom_func) {
+		oom_limit += 100000;
+		oom_func = NULL;
+	}
 }
 
 unsigned long get_pool_count(void)
@@ -316,7 +390,6 @@
 	ret->possible = clone_slist(s->possible);
 	ret->left = s->left;
 	ret->right = s->right;
-	ret->nr_children = s->nr_children;
 	return ret;
 }
 
@@ -394,10 +467,9 @@
 	result->merged = 1;
 	result->left = one;
 	result->right = two;
-	result->nr_children = one->nr_children + two->nr_children;
-	copy_possibles(result, one);
-	copy_possibles(result, two);
 
+	copy_possibles(result, one, two);
+
 	/*
 	 * The ->line information is used by deref_check where we complain about
 	 * checking pointers that have already been dereferenced.  Let's say we
@@ -718,7 +790,7 @@
 	struct stree *implied_two = NULL;
 	AvlIter one_iter;
 	AvlIter two_iter;
-	struct sm_state *tmp_sm;
+	struct sm_state *one, *two, *res;
 
 	if (out_of_memory())
 		return;
@@ -761,28 +833,30 @@
 	for (;;) {
 		if (!one_iter.sm || !two_iter.sm)
 			break;
-		if (cmp_tracker(one_iter.sm, two_iter.sm) < 0) {
-			sm_perror(" in %s", __func__);
-			avl_iter_next(&one_iter);
-		} else if (cmp_tracker(one_iter.sm, two_iter.sm) == 0) {
-			if (add_pool && one_iter.sm != two_iter.sm) {
-				one_iter.sm->pool = implied_one;
-				if (implied_one->base_stree)
-					one_iter.sm->pool = implied_one->base_stree;
-				two_iter.sm->pool = implied_two;
-				if (implied_two->base_stree)
-					two_iter.sm->pool = implied_two->base_stree;
-			}
-			tmp_sm = merge_sm_states(one_iter.sm, two_iter.sm);
-			add_possible_sm(tmp_sm, one_iter.sm);
-			add_possible_sm(tmp_sm, two_iter.sm);
-			avl_insert(&results, tmp_sm);
-			avl_iter_next(&one_iter);
-			avl_iter_next(&two_iter);
-		} else {
-			sm_perror(" in %s", __func__);
-			avl_iter_next(&two_iter);
+
+		one = one_iter.sm;
+		two = two_iter.sm;
+
+		if (one == two) {
+			avl_insert(&results, one);
+			goto next;
 		}
+
+		if (add_pool) {
+			one->pool = implied_one;
+			if (implied_one->base_stree)
+				one->pool = implied_one->base_stree;
+			two->pool = implied_two;
+			if (implied_two->base_stree)
+				two->pool = implied_two->base_stree;
+		}
+		res = merge_sm_states(one, two);
+		add_possible_sm(res, one);
+		add_possible_sm(res, two);
+		avl_insert(&results, res);
+next:
+		avl_iter_next(&one_iter);
+		avl_iter_next(&two_iter);
 	}
 
 	free_stree(to);
--- old/usr/src/tools/smatch/src/smatch_slist.h	Mon Aug  5 08:38:46 2019
+++ new/usr/src/tools/smatch/src/smatch_slist.h	Mon Aug  5 08:38:46 2019
@@ -96,3 +96,4 @@
 
 void all_return_states_hook(void (*callback)(void));
 
+void allocate_dynamic_states_array(int num_checks);
--- old/usr/src/tools/smatch/src/smatch_statement_count.c	Mon Aug  5 08:38:46 2019
+++ new/usr/src/tools/smatch/src/smatch_statement_count.c	Mon Aug  5 08:38:46 2019
@@ -79,6 +79,7 @@
 {
 	my_id = id;
 
+	set_dynamic_states(my_id);
 	add_hook(match_statement, STMT_HOOK);
 	add_merge_hook(my_id, &merge_states);
 
--- old/usr/src/tools/smatch/src/smatch_states.c	Mon Aug  5 08:38:46 2019
+++ new/usr/src/tools/smatch/src/smatch_states.c	Mon Aug  5 08:38:46 2019
@@ -84,7 +84,7 @@
 {
 	struct sm_state *ret;
 
-	if (!name)
+	if (!name || !state)
 		return NULL;
 
 	if (read_only)
@@ -93,7 +93,7 @@
 	if (option_debug || strcmp(check_name(owner), option_debug_check) == 0) {
 		struct smatch_state *s;
 
-		s = get_state(owner, name, sym);
+		s = __get_state(owner, name, sym);
 		if (!s)
 			sm_msg("%s new [%s] '%s' %s", __func__,
 			       check_name(owner), name, show_state(state));
@@ -196,7 +196,7 @@
 	    strcmp(check_name(sm->owner), option_debug_check) == 0) {
 		struct smatch_state *s;
 
-		s = get_state(sm->owner, sm->name, sm->sym);
+		s = __get_state(sm->owner, sm->name, sm->sym);
 		if (!s)
 			sm_msg("%s new %s", __func__, show_sm(sm));
 		else
@@ -222,7 +222,7 @@
 	    strcmp(check_name(sm->owner), option_debug_check) == 0) {
 		struct smatch_state *s;
 
-		s = get_state(sm->owner, sm->name, sm->sym);
+		s = __get_state(sm->owner, sm->name, sm->sym);
 		if (!s)
 			sm_msg("%s new %s", __func__, show_sm(sm));
 		else
@@ -245,7 +245,7 @@
 	    strcmp(check_name(sm->owner), option_debug_check) == 0) {
 		struct smatch_state *s;
 
-		s = get_state(sm->owner, sm->name, sm->sym);
+		s = __get_state(sm->owner, sm->name, sm->sym);
 		if (!s)
 			sm_msg("%s new %s", __func__, show_sm(sm));
 		else
@@ -477,7 +477,7 @@
 	if (option_debug || strcmp(check_name(owner), option_debug_check) == 0) {
 		struct smatch_state *tmp;
 
-		tmp = get_state(owner, name, sym);
+		tmp = __get_state(owner, name, sym);
 		sm_msg("%s [%s] '%s'.  Was %s.  Now T:%s F:%s", __func__,
 		       check_name(owner),  name, show_state(tmp),
 		       show_state(true_state), show_state(false_state));
@@ -531,7 +531,7 @@
 	if (option_debug || strcmp(check_name(owner), option_debug_check) == 0) {
 		struct smatch_state *tmp;
 
-		tmp = get_state(owner, name, sym);
+		tmp = __get_state(owner, name, sym);
 		sm_msg("%s [%s] '%s'.  Was %s.  Now T:%s F:%s", __func__,
 		       check_name(owner),  name, show_state(tmp),
 		       show_state(true_sm ? true_sm->state : NULL),
@@ -789,7 +789,6 @@
 {
 	struct stree *old_false, *old_true;
 
-	__use_cond_stack(&cond_false_stack);
 	old_false = pop_stree(&cond_false_stack);
 	old_true = pop_stree(&cond_true_stack);
 	push_stree(&cond_false_stack, old_true);
--- old/usr/src/tools/smatch/src/smatch_stored_conditions.c	Mon Aug  5 08:38:47 2019
+++ new/usr/src/tools/smatch/src/smatch_stored_conditions.c	Mon Aug  5 08:38:47 2019
@@ -238,11 +238,14 @@
 void register_stored_conditions(int id)
 {
 	my_id = id;
+	set_dynamic_states(my_id);
 }
 
 void register_stored_conditions_links(int id)
 {
 	link_id = id;
+	db_ignore_states(link_id);
+	set_dynamic_states(link_id);
 	add_merge_hook(link_id, &merge_links);
 	add_modification_hook(link_id, &match_link_modify);
 }
--- old/usr/src/tools/smatch/src/smatch_string_list.c	Mon Aug  5 08:38:47 2019
+++ new/usr/src/tools/smatch/src/smatch_string_list.c	Mon Aug  5 08:38:47 2019
@@ -20,14 +20,16 @@
 int list_has_string(struct string_list *str_list, const char *str)
 {
 	char *tmp;
+	int cmp;
 
 	if (!str)
 		return 0;
 
 	FOR_EACH_PTR(str_list, tmp) {
-		if (strcmp(tmp, str) < 0)
+		cmp = strcmp(tmp, str);
+		if (cmp < 0)
 			continue;
-		if (strcmp(tmp, str) == 0)
+		if (cmp == 0)
 			return 1;
 		return 0;
 	} END_FOR_EACH_PTR(tmp);
@@ -34,23 +36,26 @@
 	return 0;
 }
 
-void insert_string(struct string_list **str_list, const char *_new)
+int insert_string(struct string_list **str_list, const char *_new)
 {
 	char *new = (char *)_new;
 	char *tmp;
+	int cmp;
 
 	FOR_EACH_PTR(*str_list, tmp) {
-		if (strcmp(tmp, new) < 0)
+		cmp = strcmp(tmp, new);
+		if (cmp < 0)
 			continue;
-		else if (strcmp(tmp, new) == 0) {
-			return;
+		else if (cmp == 0) {
+			return 0;
 		} else {
 			INSERT_CURRENT(alloc_string(new), tmp);
-			return;
+			return 1;
 		}
 	} END_FOR_EACH_PTR(tmp);
 	new = alloc_string(new);
 	add_ptr_list(str_list, new);
+	return 1;
 }
 
 struct string_list *clone_str_list(struct string_list *orig)
--- old/usr/src/tools/smatch/src/smatch_strlen.c	Mon Aug  5 08:38:47 2019
+++ new/usr/src/tools/smatch/src/smatch_strlen.c	Mon Aug  5 08:38:47 2019
@@ -333,6 +333,8 @@
 {
 	my_strlen_id = id;
 
+	set_dynamic_states(my_strlen_id);
+
 	add_unmatched_state_hook(my_strlen_id, &unmatched_strlen_state);
 
 	select_caller_info_hook(set_param_strlen, STR_LEN);
@@ -354,6 +356,7 @@
 void register_strlen_equiv(int id)
 {
 	my_equiv_id = id;
+	set_dynamic_states(my_equiv_id);
 	add_function_assign_hook("strlen", &match_strlen, NULL);
 	add_modification_hook(my_equiv_id, &set_strlen_equiv_undefined);
 }
--- old/usr/src/tools/smatch/src/smatch_struct_assignment.c	Mon Aug  5 08:38:48 2019
+++ new/usr/src/tools/smatch/src/smatch_struct_assignment.c	Mon Aug  5 08:38:48 2019
@@ -445,6 +445,23 @@
 	__struct_members_copy(COPY_MEMCPY, expr, remove_addr(dest), remove_addr(src));
 }
 
+static void match_memdup(const char *fn, struct expression *call_expr,
+			struct expression *expr, void *_unused)
+{
+	struct expression *left, *right, *arg;
+
+	if (!expr || expr->type != EXPR_ASSIGNMENT)
+		return;
+
+	left = strip_expr(expr->left);
+	right = strip_expr(expr->right);
+
+	if (right->type != EXPR_CALL)
+		return;
+	arg = get_argument_from_call_expr(right->args, 0);
+	__struct_members_copy(COPY_MEMCPY, expr, left, arg);
+}
+
 static void match_memcpy_unknown(const char *fn, struct expression *expr, void *_arg)
 {
 	struct expression *dest;
@@ -548,6 +565,9 @@
 	add_function_hook("__memcpy", &match_memcpy, INT_PTR(0));
 	add_function_hook("__memmove", &match_memcpy, INT_PTR(0));
 
+	if (option_project == PROJ_KERNEL)
+		return_implies_state_sval("kmemdup", valid_ptr_min_sval, valid_ptr_max_sval, &match_memdup, NULL);
+
 	add_function_hook("sscanf", &match_sscanf, NULL);
 
 	add_hook(&unop_expr, OP_HOOK);
--- old/usr/src/tools/smatch/src/smatch_sval.c	Mon Aug  5 08:38:48 2019
+++ new/usr/src/tools/smatch/src/smatch_sval.c	Mon Aug  5 08:38:48 2019
@@ -67,7 +67,7 @@
 	sval_t ret;
 
 	if (!type)
-		type = &int_ctype;
+		type = &llong_ctype;
 
 	ret.type = type;
 	ret.value = val;
@@ -94,6 +94,8 @@
 
 int sval_unsigned(sval_t sval)
 {
+	if (is_ptr_type(sval.type))
+		return true;
 	return type_unsigned(sval.type);
 }
 
@@ -231,7 +233,7 @@
 {
 	if (sval_is_negative(sval) && type_unsigned(type))
 		return 1;
-	if (type_signed(type) &&  sval_unsigned(sval))
+	if (type_signed(type) && sval_unsigned(sval))
 		return 0;
 	if (type_signed(sval.type) &&
 	    sval.value < sval_type_min(type).value)
@@ -458,6 +460,8 @@
 		}
 	}
 
+	if (op == '-')
+		ret.type = ssize_t_ctype;
 	return ret;
 }
 
@@ -583,15 +587,34 @@
 	return sval_binop_overflows(left, op, right);
 }
 
-unsigned long long fls_mask(unsigned long long uvalue)
+int find_first_zero_bit(unsigned long long uvalue)
 {
-	unsigned long long high_bit = 0;
+	int i;
 
+	for (i = 0; i < 64; i++) {
+		if (!(uvalue & (1ULL << i)))
+			return i;
+	}
+	return i;
+}
+
+int sm_fls64(unsigned long long uvalue)
+{
+	int high_bit = 0;
+
 	while (uvalue) {
 		uvalue >>= 1;
 		high_bit++;
 	}
 
+	return high_bit;
+}
+
+unsigned long long fls_mask(unsigned long long uvalue)
+{
+	int high_bit = 0;
+
+	high_bit = sm_fls64(uvalue);
 	if (high_bit == 0)
 		return 0;
 
@@ -607,6 +630,8 @@
 {
 	char buf[30];
 
+	if (sval_is_ptr(sval) && sval.value == valid_ptr_max)
+		return "ptr_max";
 	if (sval_unsigned(sval) && sval.value == ULLONG_MAX)
 		return "u64max";
 	if (sval_unsigned(sval) && sval.value == UINT_MAX)
@@ -638,6 +663,22 @@
 	return alloc_sname(buf);
 }
 
+const char *sval_to_str_or_err_ptr(sval_t sval)
+{
+	char buf[12];
+
+	if (option_project != PROJ_KERNEL ||
+	    !is_ptr_type(sval.type))
+		return sval_to_str(sval);
+
+	if (sval.uvalue >= -4905ULL) {
+		snprintf(buf, sizeof(buf), "(%lld)", sval.value);
+		return alloc_sname(buf);
+	}
+
+	return sval_to_str(sval);
+}
+
 const char *sval_to_numstr(sval_t sval)
 {
 	char buf[30];
--- old/usr/src/tools/smatch/src/smatch_type.c	Mon Aug  5 08:38:49 2019
+++ new/usr/src/tools/smatch/src/smatch_type.c	Mon Aug  5 08:38:49 2019
@@ -29,6 +29,8 @@
 
 	if (!sym)
 		return NULL;
+	if (sym->type == SYM_BASETYPE)
+		return sym;
 	ret = get_base_type(sym);
 	if (!ret)
 		return NULL;
@@ -75,6 +77,10 @@
 	if (!right)
 		return NULL;
 
+	if (expr->op == '-' &&
+	    (is_ptr_type(left) && is_ptr_type(right)))
+		return ssize_t_ctype;
+
 	if (left->type == SYM_PTR || left->type == SYM_ARRAY)
 		return left;
 	if (right->type == SYM_PTR || right->type == SYM_ARRAY)
@@ -269,6 +275,9 @@
 	case EXPR_LOGICAL:
 		ret = &int_ctype;
 		break;
+	case EXPR_OFFSETOF:
+		ret = &ulong_ctype;
+		break;
 	default:
 		return NULL;
 	}
@@ -283,17 +292,11 @@
 static struct symbol *get_final_type_helper(struct expression *expr)
 {
 	/*
-	 * I'm not totally positive I understand types...
+	 * The problem is that I wrote a bunch of Smatch to think that
+	 * you could do get_type() on an expression and it would give
+	 * you what the comparison was type promoted to.  This is wrong
+	 * but fixing it is a big of work...  Hence this horrible hack.
 	 *
-	 * So, when you're doing pointer math, and you do a subtraction, then
-	 * the sval_binop() and whatever need to know the type of the pointer
-	 * so they can figure out the alignment.  But the result is going to be
-	 * and ssize_t.  So get_operation_type() gives you the pointer type
-	 * and get_type() gives you ssize_t.
-	 *
-	 * Most of the time the operation type and the final type are the same
-	 * but this just handles the few places where they are different.
-	 *
 	 */
 
 	expr = strip_parens(expr);
@@ -300,22 +303,9 @@
 	if (!expr)
 		return NULL;
 
-	switch (expr->type) {
-	case EXPR_COMPARE:
+	if (expr->type == EXPR_COMPARE)
 		return &int_ctype;
-	case EXPR_BINOP: {
-		struct symbol *left, *right;
 
-		if (expr->op != '-')
-			return NULL;
-
-		left = get_type(expr->left);
-		right = get_type(expr->right);
-		if (type_is_ptr(left) || type_is_ptr(right))
-			return ssize_t_ctype;
-		}
-	}
-
 	return NULL;
 }
 
@@ -397,16 +387,7 @@
 
 int is_pointer(struct expression *expr)
 {
-	struct symbol *sym;
-
-	sym = get_type(expr);
-	if (!sym)
-		return 0;
-	if (sym == &string_ctype)
-		return 0;
-	if (sym->type == SYM_PTR)
-		return 1;
-	return 0;
+	return type_is_ptr(get_type(expr));
 }
 
 int returns_pointer(struct symbol *sym)
@@ -442,7 +423,7 @@
 		base_type = &llong_ctype;
 	ret.type = base_type;
 
-	if (type_unsigned(base_type)) {
+	if (type_unsigned(base_type) || is_ptr_type(base_type)) {
 		ret.value = 0;
 		return ret;
 	}
@@ -604,7 +585,7 @@
 
 	if (strncmp(name, ".", 1) == 0)
 		name += 1;
-	if (strncmp(name, "->", 2) == 0)
+	else if (strncmp(name, "->", 2) == 0)
 		name += 2;
 
 	FOR_EACH_PTR(symbol_list, tmp) {
@@ -619,10 +600,12 @@
 		if (strcmp(tmp->ident->name, name) == 0)
 			return tmp;
 
-		chunk_len = strlen(tmp->ident->name);
+		chunk_len = tmp->ident->len;
 		if (strncmp(tmp->ident->name, name, chunk_len) == 0 &&
 		    (name[chunk_len] == '.' || name[chunk_len] == '-')) {
 			sub = get_real_base_type(tmp);
+			if (sub->type == SYM_PTR)
+				sub = get_real_base_type(sub);
 			return get_member_from_string(sub->symbol_list, name + chunk_len);
 		}
 
@@ -741,7 +724,7 @@
 		return snprintf(buf, size, "<unknown>");
 
 	if (type->type == SYM_BASETYPE) {
-		return snprintf(buf, size, base_type_str(type));
+		return snprintf(buf, size, "%s", base_type_str(type));
 	} else if (type->type == SYM_PTR) {
 		type = get_real_base_type(type);
 		n = type_str_helper(buf, size, type);
@@ -795,6 +778,8 @@
 		if (n > size)
 			return n;
 		return n + snprintf(buf + n, size - n, "}");
+	} else if (type->type == SYM_ENUM) {
+		return snprintf(buf, size, "enum %s", type->ident ? type->ident->name : "<unknown>");
 	} else {
 		return snprintf(buf, size, "<type %d>", type->type);
 	}
--- old/usr/src/tools/smatch/src/smatch_type_val.c	Mon Aug  5 08:38:49 2019
+++ new/usr/src/tools/smatch/src/smatch_type_val.c	Mon Aug  5 08:38:49 2019
@@ -396,9 +396,6 @@
 		return;
 
 	type = get_type(expr->left);
-	if (type && type->type == SYM_STRUCT)
-		return;
-
 	member = get_member_name(expr->left);
 	if (!member)
 		return;
--- old/usr/src/tools/smatch/src/smatch_untracked_param.c	Mon Aug  5 08:38:49 2019
+++ new/usr/src/tools/smatch/src/smatch_untracked_param.c	Mon Aug  5 08:38:49 2019
@@ -39,10 +39,12 @@
 static int tracked;
 
 STATE(untracked);
+STATE(lost);
 
 typedef void (untracked_hook)(struct expression *call, int param);
 DECLARE_PTR_LIST(untracked_hook_list, untracked_hook *);
 static struct untracked_hook_list *untracked_hooks;
+static struct untracked_hook_list *lost_hooks;
 
 struct int_stack *tracked_stack;
 
@@ -62,13 +64,46 @@
 	} END_FOR_EACH_PTR(fn);
 }
 
+void add_lost_param_hook(void (func)(struct expression *call, int param))
+{
+	untracked_hook **p = malloc(sizeof(untracked_hook *));
+	*p = func;
+	add_ptr_list(&lost_hooks, p);
+}
+
+static void call_lost_callbacks(struct expression *expr, int param)
+{
+	untracked_hook **fn;
+
+	FOR_EACH_PTR(lost_hooks, fn) {
+		(*fn)(expr, param);
+	} END_FOR_EACH_PTR(fn);
+}
+
 static void assume_tracked(struct expression *call_expr, int param, char *key, char *value)
 {
 	tracked = 1;
 }
 
-void mark_untracked(struct expression *expr, int param, const char *key, const char *value)
+static char *get_array_from_key(struct expression *expr, int param, const char *key, struct symbol **sym)
 {
+	struct expression *arg;
+
+	arg = get_argument_from_call_expr(expr->args, param);
+	if (!arg)
+		return NULL;
+	if (arg->type != EXPR_PREOP || arg->op != '&')
+		return NULL;
+	arg = arg->unop;
+	if (!is_array(arg))
+		return NULL;
+	arg = get_array_base(arg);
+
+	return expr_to_var_sym(arg, sym);
+}
+
+static void mark_untracked_lost(struct expression *expr, int param, const char *key, int type)
+{
 	char *name;
 	struct symbol *sym;
 
@@ -78,15 +113,31 @@
 		return;
 
 	name = return_state_to_var_sym(expr, param, key, &sym);
-	if (!name || !sym)
-		goto free;
+	if (!name || !sym) {
+		name = get_array_from_key(expr, param, key, &sym);
+		if (!name || !sym)
+			goto free;
+	}
 
+	if (type == LOST_PARAM)
+		call_lost_callbacks(expr, param);
 	call_untracked_callbacks(expr, param);
 	set_state(my_id, name, sym, &untracked);
 free:
 	free_string(name);
+
 }
 
+void mark_untracked(struct expression *expr, int param, const char *key, const char *value)
+{
+	mark_untracked_lost(expr, param, key, UNTRACKED_PARAM);
+}
+
+void mark_lost(struct expression *expr, int param, const char *key, const char *value)
+{
+	mark_untracked_lost(expr, param, key, LOST_PARAM);
+}
+
 static int lost_in_va_args(struct expression *expr)
 {
 	struct symbol *fn;
@@ -133,7 +184,8 @@
 	} END_FOR_EACH_PTR(arg);
 }
 
-void mark_all_params_untracked(int return_id, char *return_ranges, struct expression *expr)
+
+static void mark_all_params(int return_id, char *return_ranges, int type)
 {
 	struct symbol *arg;
 	int param;
@@ -145,14 +197,27 @@
 		if (!arg->ident)
 			continue;
 		sql_insert_return_states(return_id, return_ranges,
-					 UNTRACKED_PARAM, param, "$", "");
+					 type, param, "$", "");
 	} END_FOR_EACH_PTR(arg);
 }
 
+
+void mark_all_params_untracked(int return_id, char *return_ranges, struct expression *expr)
+{
+	mark_all_params(return_id, return_ranges, UNTRACKED_PARAM);
+}
+
+void mark_all_params_lost(int return_id, char *return_ranges, struct expression *expr)
+{
+	mark_all_params(return_id, return_ranges, LOST_PARAM);
+}
+
 static void print_untracked_params(int return_id, char *return_ranges, struct expression *expr)
 {
+	struct sm_state *sm;
 	struct symbol *arg;
 	int param;
+	int type;
 
 	param = -1;
 	FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
@@ -160,12 +225,21 @@
 
 		if (!arg->ident)
 			continue;
-		if (!get_state(my_id, arg->ident->name, arg) &&
-		    !__bail_on_rest_of_function)  /* hairy functions are untrackable */
+
+		if (__bail_on_rest_of_function) {
+			/* hairy functions are lost */
+			type = LOST_PARAM;
+		} else if ((sm = get_sm_state(my_id, arg->ident->name, arg))) {
+			if (slist_has_state(sm->possible, &lost))
+				type = LOST_PARAM;
+			else
+				type = UNTRACKED_PARAM;
+		} else {
 			continue;
+		}
 
 		sql_insert_return_states(return_id, return_ranges,
-					 UNTRACKED_PARAM, param, "$", "");
+					 type, param, "$", "");
 	} END_FOR_EACH_PTR(arg);
 }
 
@@ -237,6 +311,7 @@
 
 	select_return_states_hook(INTERNAL, &assume_tracked);
 	select_return_states_hook(UNTRACKED_PARAM, &mark_untracked);
+	select_return_states_hook(LOST_PARAM, &mark_lost);
 	add_hook(&match_after_call, FUNCTION_CALL_HOOK_AFTER_DB);
 
 	add_split_return_callback(&print_untracked_params);
--- old/usr/src/tools/smatch/src/validation/sm_bitwise1.c	Mon Aug  5 08:38:50 2019
+++ new/usr/src/tools/smatch/src/validation/sm_bitwise1.c	Mon Aug  5 08:38:50 2019
@@ -17,6 +17,6 @@
 sm_bitwise1.c:6 test() implied: x & 1 = '0-1'
 sm_bitwise1.c:7 test() implied: x & 2 = '0,2'
 sm_bitwise1.c:8 test() implied: x & ~(255) = '0,256-4294967040'
-sm_bitwise1.c:9 test() implied: x & ~(255) = '0-4294967040'
+sm_bitwise1.c:9 test() implied: x & ~(255) = '0,256-4294967040'
  * check-output-end
  */
--- old/usr/src/tools/smatch/src/validation/sm_equiv1.c	Mon Aug  5 08:38:50 2019
+++ new/usr/src/tools/smatch/src/validation/sm_equiv1.c	Mon Aug  5 08:38:50 2019
@@ -30,11 +30,11 @@
  * check-output-start
 sm_equiv1.c:13 func() one = 1
 sm_equiv1.c:14 func() two = 1
-sm_equiv1.c:16 func() one = s64min-s64max
-sm_equiv1.c:17 func() two = s64min-s64max
+sm_equiv1.c:16 func() one = 0-u64max
+sm_equiv1.c:17 func() two = 0-u64max
 sm_equiv1.c:19 func() one = 2
 sm_equiv1.c:20 func() two = 2
-sm_equiv1.c:22 func() one = s64min-s64max
-sm_equiv1.c:23 func() two = s64min-s64max
+sm_equiv1.c:22 func() one = 0-u64max
+sm_equiv1.c:23 func() two = 0-u64max
  * check-output-end
  */
--- old/usr/src/tools/smatch/src/validation/sm_implied.c	Mon Aug  5 08:38:50 2019
+++ new/usr/src/tools/smatch/src/validation/sm_implied.c	Mon Aug  5 08:38:50 2019
@@ -26,5 +26,6 @@
  *
  * check-output-start
 sm_implied.c:20 func() error: potentially dereferencing uninitialized 'aa'.
+sm_implied.c:20 func() error: potentially dereferencing uninitialized 'aa'.
  * check-output-end
  */
--- old/usr/src/tools/smatch/src/validation/sm_implied10.c	Mon Aug  5 08:38:51 2019
+++ new/usr/src/tools/smatch/src/validation/sm_implied10.c	Mon Aug  5 08:38:51 2019
@@ -11,7 +11,7 @@
 	else
 		__smatch_value("y");
 
-	if (({int test2 = !!(offset >= 10 || x[offset] == 1); frob(); frob(); frob(); test2;}))
+	if (({int test2 = !!(offset >= 10u || x[offset] == 1); frob(); frob(); frob(); test2;}))
 		__smatch_value("offset");
 	else
 		__smatch_value("offset");
@@ -22,9 +22,9 @@
  * check-command: smatch -I.. -m64 sm_implied10.c
  *
  * check-output-start
-sm_implied10.c:10 func() y = 0,4096-2117777777777777777
-sm_implied10.c:12 func() y = 4096-2117777777777777777
-sm_implied10.c:15 func() offset = 0-s32max
+sm_implied10.c:10 func() y = 0,4096-ptr_max
+sm_implied10.c:12 func() y = 4096-ptr_max
+sm_implied10.c:15 func() offset = s32min-s32max
 sm_implied10.c:17 func() offset = 0-9
  * check-output-end
  */
--- old/usr/src/tools/smatch/src/validation/sm_implied11.c	Mon Aug  5 08:38:51 2019
+++ new/usr/src/tools/smatch/src/validation/sm_implied11.c	Mon Aug  5 08:38:51 2019
@@ -29,6 +29,6 @@
  * check-command: smatch -I.. -m64 sm_implied11.c
  *
  * check-output-start
-sm_implied11.c:25 ad_agg_selection_logic() implied: foo = '0,4096-2117777777777777777'
+sm_implied11.c:25 ad_agg_selection_logic() implied: foo = '0,4096-ptr_max'
  * check-output-end
  */
--- old/usr/src/tools/smatch/src/validation/sm_implied12.c	Mon Aug  5 08:38:51 2019
+++ new/usr/src/tools/smatch/src/validation/sm_implied12.c	Mon Aug  5 08:38:51 2019
@@ -33,6 +33,6 @@
  * check-command: smatch -I.. -m64 sm_implied12.c
  *
  * check-output-start
-sm_implied12.c:28 ad_agg_selection_logic() implied: foo = '0,4096-2117777777777777777'
+sm_implied12.c:28 ad_agg_selection_logic() implied: foo = '0,4096-ptr_max'
  * check-output-end
  */
--- old/usr/src/tools/smatch/src/validation/sm_implied2.c	Mon Aug  5 08:38:52 2019
+++ new/usr/src/tools/smatch/src/validation/sm_implied2.c	Mon Aug  5 08:38:52 2019
@@ -37,5 +37,6 @@
  *
  * check-output-start
 sm_implied2.c:28 func() error: potentially dereferencing uninitialized 'aa'.
+sm_implied2.c:28 func() error: potentially dereferencing uninitialized 'aa'.
  * check-output-end
  */
--- old/usr/src/tools/smatch/src/validation/sm_implied5.c	Mon Aug  5 08:38:52 2019
+++ new/usr/src/tools/smatch/src/validation/sm_implied5.c	Mon Aug  5 08:38:52 2019
@@ -23,5 +23,6 @@
  *
  * check-output-start
 sm_implied5.c:18 func() error: potentially dereferencing uninitialized 'aa'.
+sm_implied5.c:18 func() error: potentially dereferencing uninitialized 'aa'.
  * check-output-end
  */
--- old/usr/src/tools/smatch/src/validation/sm_memory.c	Mon Aug  5 08:38:53 2019
+++ new/usr/src/tools/smatch/src/validation/sm_memory.c	Mon Aug  5 08:38:53 2019
@@ -30,6 +30,5 @@
  *
  * check-output-start
 sm_memory.c:22 func() warn: possible memory leak of 'ac'
-sm_memory.c:22 func() error: memory leak of 'ac'
  * check-output-end
  */
--- old/usr/src/tools/smatch/src/validation/sm_null_deref.c	Mon Aug  5 08:38:53 2019
+++ new/usr/src/tools/smatch/src/validation/sm_null_deref.c	Mon Aug  5 08:38:53 2019
@@ -44,6 +44,7 @@
  *
  * check-output-start
 sm_null_deref.c:18 func() error: potentially dereferencing uninitialized 'aa'.
+sm_null_deref.c:18 func() error: potentially dereferencing uninitialized 'aa'.
 sm_null_deref.c:23 func() error: we previously assumed 'a' could be null (see line 20)
 sm_null_deref.c:25 func() warn: variable dereferenced before check 'a' (see line 23)
 sm_null_deref.c:30 func() error: we previously assumed 'b' could be null (see line 25)
--- old/usr/src/tools/smatch/src/validation/sm_select5.c	Mon Aug  5 08:38:54 2019
+++ new/usr/src/tools/smatch/src/validation/sm_select5.c	Mon Aug  5 08:38:54 2019
@@ -25,7 +25,7 @@
  *
  * check-output-start
 sm_select5.c:15 test() implied: ret = '(-12)'
-sm_select5.c:16 test() implied: a = 's32min-s32max'
+sm_select5.c:16 test() implied: a = 's32min-(-1),4-s32max'
 sm_select5.c:18 test() implied: a = '0-3'
  * check-output-end
  */
--- old/usr/src/uts/i86pc/unix/Makefile	Mon Aug  5 08:38:54 2019
+++ new/usr/src/uts/i86pc/unix/Makefile	Mon Aug  5 08:38:54 2019
@@ -21,7 +21,7 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# Copyright (c) 2018, Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
 # Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
 
 #
@@ -149,6 +149,9 @@
 $(OBJS_DIR)/zutil.o := SMOFF += indenting
 $(OBJS_DIR)/bootrd_cpio.o := SMOFF += allocating_enough_data
 
+# too hairy
+$(OBJS_DIR)/inflate.o := SMATCH=off
+
 #
 #	Default build targets.
 #
--- old/usr/src/uts/intel/emlxs/Makefile	Mon Aug  5 08:38:54 2019
+++ new/usr/src/uts/intel/emlxs/Makefile	Mon Aug  5 08:38:54 2019
@@ -22,7 +22,7 @@
 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 # Copyright (c) 2011 Bayard G. Bell. All rights reserved.
-# Copyright (c) 2018, Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
 #
 # This makefile drives the production of the emlxs driver kernel module.
 #
@@ -36,7 +36,6 @@
 #
 MODULE		= emlxs
 OBJECTS		= $(EMLXS_OBJS:%=$(OBJS_DIR)/%)
-LINTS		= $(EMLXS_OBJS:%.o=$(LINTS_DIR)/%.ln)
 ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
 CONF_SRCDIR	= $(UTSBASE)/common/io/fibre-channel/fca/emlxs
 
@@ -50,7 +49,6 @@
 #	Define targets
 #
 ALL_TARGET	= $(BINARY) $(SRC_CONFILE)
-LINT_TARGET	= $(MODULE).lint
 INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
 
 EMLXS_FLAGS             = -DEMLXS_I386
@@ -60,7 +58,6 @@
 EMLXS_CFLAGS            = $(EMLXS_FLAGS)
 EMLXS_LFLAGS            = $(EMLXS_FLAGS)
 CFLAGS	                += $(EMLXS_CFLAGS) -DEMLXS_ARCH=\"$(CLASS)\"
-LINTTAGS                += $(EMLXS_LFLAGS) -DEMLXS_ARCH=\"$(CLASS)\"
 
 
 #
@@ -82,21 +79,12 @@
 LDFLAGS		+= -dy -Nmisc/md5 -Nmisc/sha1
 LDFLAGS		+= -Nmisc/bignum -Nmisc/fctl
 
-#
-# For now, disable these lint checks; maintainers should endeavor
-# to investigate and remove these for maximum lint coverage.
-#
-LINTTAGS	+= -erroff=E_BAD_PTR_CAST_ALIGN
-LINTTAGS	+= -erroff=E_STATIC_UNUSED
-LINTTAGS	+= -erroff=E_ASSIGN_NARROW_CONV
-LINTTAGS	+= -erroff=E_SUSPICIOUS_COMPARISON
-LINTTAGS	+= -erroff=E_INCONS_VAL_TYPE_DECL2
-
 CERRWARN	+= -_gcc=-Wno-parentheses
 CERRWARN	+= -_gcc=-Wno-unused-label
 CERRWARN	+= -_gcc=-Wno-uninitialized
 
-SMOFF += indenting,deref_check,all_func_returns
+# needs work
+SMOFF += indenting,deref_check,all_func_returns,index_overflow
 
 # seems definitely wrong
 $(OBJS_DIR)/emlxs_fcf.o := SMOFF += logical_instead_of_bitwise
@@ -114,12 +102,6 @@
 
 clobber:	$(CLOBBER_DEPS)
 
-lint:		$(LINT_DEPS)
-
-modlintlib:	$(MODLINTLIB_DEPS)
-
-clean.lint:	$(CLEAN_LINT_DEPS)
-
 install:	$(INSTALL_DEPS)
 
 #
--- old/usr/src/uts/intel/genunix/Makefile	Mon Aug  5 08:38:55 2019
+++ new/usr/src/uts/intel/genunix/Makefile	Mon Aug  5 08:38:55 2019
@@ -23,14 +23,8 @@
 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# Copyright (c) 2018, Joyent, Inc.
-
+# Copyright 2019 Joyent, Inc.
 #
-#	This makefile drives the production of the generic
-#	unix kernel module.
-#
-#	x86 implementation architecture dependent
-#
 
 #
 #	Path to the base of the uts directory tree (usually /usr/src/uts).
@@ -46,9 +40,6 @@
 OBJECTS		= $(GENUNIX_OBJS:%=$(OBJS_DIR)/%) \
 		  $(NOT_YET_KMODS:%=$(OBJS_DIR)/%)
 
-LINTS		= $(GENUNIX_OBJS:%.o=$(LINTS_DIR)/%.ln) \
-		  $(NOT_YET_KMODS:%.o=$(LINTS_DIR)/%.ln)
-
 ROOTMODULE	= $(ROOT_KERN_DIR)/$(MODULE)
 
 LIBGEN		= $(OBJS_DIR)/libgenunix.so
@@ -63,7 +54,6 @@
 #	Define targets
 #
 ALL_TARGET	= $(LIBGEN) $(GENUNIX)
-LINT_TARGET	= $(MODULE).lint
 INSTALL_TARGET	= $(LIBGEN) $(GENUNIX) $(ROOTMODULE)
 
 #
@@ -88,18 +78,6 @@
 
 CPPFLAGS	+= -I$(UTSBASE)/i86pc
 
-#
-# For now, disable these lint checks; maintainers should endeavor
-# to investigate and remove these for maximum lint coverage.
-# Please do not carry these forward to new Makefiles.
-#
-LINTTAGS	+= -erroff=E_SUSPICIOUS_COMPARISON
-LINTTAGS	+= -erroff=E_BAD_PTR_CAST_ALIGN
-LINTTAGS	+= -erroff=E_SUPPRESSION_DIRECTIVE_UNUSED
-LINTTAGS	+= -erroff=E_STATIC_UNUSED
-LINTTAGS	+= -erroff=E_PTRDIFF_OVERFLOW
-LINTTAGS	+= -erroff=E_ASSIGN_NARROW_CONV
-
 CERRWARN	+= -_gcc=-Wno-unused-label
 CERRWARN	+= -_gcc=-Wno-unused-variable
 CERRWARN	+= -_gcc=-Wno-unused-value
@@ -111,6 +89,9 @@
 CERRWARN	+= -_gcc=-Wno-clobbered
 CERRWARN	+= -_gcc=-Wno-empty-body
 
+# very hairy
+$(OBJS_DIR)/u8_textprep.o := SMATCH=off
+
 # false positives
 SMOFF += index_overflow
 $(OBJS_DIR)/seg_vn.o := SMOFF += deref_check
@@ -130,12 +111,6 @@
 $(OBJS_DIR)/acl_common.o := SMOFF += or_vs_and
 
 #
-# Ensure that lint sees 'struct cpu' containing a fully declared
-# embedded 'struct machcpu'
-#
-LINTFLAGS	+= -D_MACHDEP -I../../i86pc
-
-#
 #	Default build targets.
 #
 .KEEP_STATE:
@@ -148,12 +123,6 @@
 
 clobber:	$(CLOBBER_DEPS)
 
-lint:		$(LINT_DEPS)
-
-modlintlib:	$(MODLINTLIB_DEPS)
-
-clean.lint:	$(CLEAN_LINT_DEPS)
-
 install:	$(INSTALL_DEPS)
 
 # Due to what seems to be an issue in GCC 4 generated DWARF containing
@@ -190,8 +159,3 @@
 include	$(UTSBASE)/i86pc/Makefile.workarounds
 
 ALL_DEFS += $(WORKAROUND_DEFS)
-
-#
-# Override.
-#
-$(MODULE).lint := GEN_LINT_LIB =
--- old/usr/src/uts/intel/mega_sas/Makefile	Mon Aug  5 08:38:55 2019
+++ new/usr/src/uts/intel/mega_sas/Makefile	Mon Aug  5 08:38:55 2019
@@ -21,15 +21,8 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# Copyright (c) 2018, Joyent, Inc.
-
+# Copyright 2019 Joyent, Inc.
 #
-# uts/intel/mega_sas/Makefile
-#
-#	This makefile drives the production of the mega_sas driver kernel module.
-#
-#	intel implementation architecture dependent
-#
 
 #
 #	Path to the base of the uts directory tree (usually /usr/src/uts).
@@ -41,7 +34,6 @@
 #
 MODULE		= mega_sas
 OBJECTS		= $(MEGA_SAS_OBJS:%=$(OBJS_DIR)/%)
-LINTS		= $(MEGA_SAS_OBJS:%.o=$(LINTS_DIR)/%.ln)
 ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
 CONF_SRCDIR     = $(UTSBASE)/common/io/mega_sas
 
@@ -54,11 +46,10 @@
 #	Define targets
 #
 ALL_TARGET	= $(BINARY) $(CONFMOD)
-LINT_TARGET	= $(MODULE).lint
 INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
 
 #
-# 	Kernel Module Dependencies
+#	Kernel Module Dependencies
 #
 LDFLAGS		+= -dy -Nmisc/scsi
 
@@ -65,7 +56,7 @@
 CERRWARN	+= -_gcc=-Wno-uninitialized
 
 # needs work
-$(OBJS_DIR)/megaraid_sas.o := SMOFF += snprintf_overflow,all_func_returns
+$(OBJS_DIR)/megaraid_sas.o := SMOFF += snprintf_overflow,all_func_returns,index_overflow
 
 #
 #	Default build targets.
@@ -80,12 +71,6 @@
 
 clobber:	$(CLOBBER_DEPS)
 
-lint:		$(LINT_DEPS)
-
-modlintlib:	$(MODLINTLIB_DEPS)
-
-clean.lint:	$(CLEAN_LINT_DEPS)
-
 install:	$(INSTALL_DEPS)
 
 #
--- old/usr/src/uts/intel/simnet/Makefile	Mon Aug  5 08:38:56 2019
+++ new/usr/src/uts/intel/simnet/Makefile	Mon Aug  5 08:38:56 2019
@@ -22,7 +22,10 @@
 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
+# Copyright 2019 Joyent, Inc.
 #
+
+#
 # Path to the base of the uts directory tree (usually /usr/src/uts).
 #
 UTSBASE	= ../..
@@ -32,7 +35,6 @@
 #
 MODULE		= simnet
 OBJECTS		= $(SIMNET_OBJS:%=$(OBJS_DIR)/%)
-LINTS		= $(SIMNET_OBJS:%.o=$(LINTS_DIR)/%.ln)
 ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
 CONF_SRCDIR	= $(UTSBASE)/common/io/$(MODULE)
 
@@ -45,7 +47,6 @@
 # Define targets
 #
 ALL_TARGET	= $(BINARY) $(SRC_CONFILE)
-LINT_TARGET	= $(MODULE).lint
 INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
 
 #
@@ -56,6 +57,9 @@
 
 CERRWARN	+= -_gcc=-Wno-switch
 
+# needs work
+$(OBJS_DIR)/simnet.o := SMOFF += index_overflow
+
 #
 #	Default build targets.
 #
@@ -69,12 +73,6 @@
 
 clobber:	$(CLOBBER_DEPS)
 
-lint:		$(LINT_DEPS)
-
-modlintlib:	$(MODLINTLIB_DEPS)
-
-clean.lint:	$(CLEAN_LINT_DEPS)
-
 install:	$(INSTALL_DEPS)
 
 #
--- old/usr/src/uts/intel/spppcomp/Makefile	Mon Aug  5 08:38:56 2019
+++ new/usr/src/uts/intel/spppcomp/Makefile	Mon Aug  5 08:38:56 2019
@@ -25,7 +25,8 @@
 # Use is subject to license terms.
 # Copyright (c) 2011 Bayard G. Bell. All rights reserved.
 #
-# Copyright (c) 2018, Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
+#
 
 #
 #	Path to the base of the uts directory tree (usually /usr/src/uts).
@@ -37,7 +38,6 @@
 #
 MODULE		= spppcomp
 OBJECTS		= $(SPPPCOMP_OBJS:%=$(OBJS_DIR)/%)
-LINTS           = $(SPPPCOMP_OBJS:%.o=$(LINTS_DIR)/%.ln)
 ROOTMODULE	= $(USR_STRMOD_DIR)/$(MODULE)
 
 #
@@ -49,7 +49,6 @@
 #	Define targets
 #
 ALL_TARGET	= $(BINARY)
-LINT_TARGET     = $(MODULE).lint
 INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
 
 #
@@ -57,25 +56,16 @@
 #
 CPPFLAGS	+= -DINTERNAL_BUILD -DSOL2 -DMUX_FRAME
 
-# 
+#
 #	Additional compiler definitions
-# 
+#
 INC_PATH	+= -I$(UTSBASE)/common/io/ppp/common
 
-#
-# For now, disable these lint checks; maintainers should endeavor
-# to investigate and remove these for maximum lint coverage.
-# Please do not carry these forward to new Makefiles.
-#
-LINTTAGS	+= -erroff=E_BAD_PTR_CAST_ALIGN
-LINTTAGS	+= -erroff=E_PTRDIFF_OVERFLOW
-LINTTAGS	+= -erroff=E_ASSIGN_NARROW_CONV
-
 CERRWARN	+= -_gcc=-Wno-parentheses
 CERRWARN	+= -_gcc=-Wno-uninitialized
 
 # needs work
-SMOFF += indenting
+SMOFF += indenting,index_overflow
 
 #
 # Depends on sppp
@@ -95,17 +85,8 @@
 
 clobber:	$(CLOBBER_DEPS)
 
-lint:		$(LINT_DEPS)
-
-modlintlib:	$(MODLINTLIB_DEPS)
-
-clean.lint:	$(CLEAN_LINT_DEPS)
-
 install:	$(INSTALL_DEPS)
 
-$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/ppp/spppcomp/%.c
-	@($(LHEAD) $(LINT.c) $< $(LTAIL))
-
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/ppp/spppcomp/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
--- old/usr/src/uts/intel/xge/Makefile	Mon Aug  5 08:38:56 2019
+++ new/usr/src/uts/intel/xge/Makefile	Mon Aug  5 08:38:56 2019
@@ -23,7 +23,8 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# Copyright (c) 2018, Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
+#
 
 #
 #	Paths to the base of the uts directory trees
@@ -35,7 +36,6 @@
 #
 MODULE		= xge
 OBJECTS		= $(XGE_HAL_OBJS:%=$(OBJS_DIR)/%) $(XGE_OBJS:%=$(OBJS_DIR)/%)
-LINTS		= $(XGE_HAL_OBJS:%.o=$(LINTS_DIR)/%.ln) $(XGE_OBJS:%.o=$(LINTS_DIR)/%.ln)
 ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
 
 #
@@ -47,7 +47,6 @@
 #	Define targets
 #
 ALL_TARGET	= $(BINARY)
-LINT_TARGET	= $(MODULE).lint
 INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
 
 #
@@ -74,7 +73,7 @@
 #TRACE_CFLAGS = -DXGE_DEBUG_MODULE_MASK=0xffffffff \
 #		-DXGE_DEBUG_TRACE_MASK=0xffffffff \
 #		-DXGE_DEBUG_ERR_MASK=0xffffffff
-TRACE_CFLAGS 	= -DXGE_DEBUG_MODULE_MASK=0x00003010 \
+TRACE_CFLAGS	= -DXGE_DEBUG_MODULE_MASK=0x00003010 \
 		-DXGE_DEBUG_TRACE_MASK=0x00000000  \
 		-DXGE_DEBUG_ERR_MASK=0x00003010
 
@@ -91,19 +90,6 @@
 #
 LDFLAGS		+=  -dy -N misc/mac -N drv/ip
 
-#	Lint flag
-#
-LINTFLAGS	+= $(XGE_CFLAGS) -Xc99=%all
-
-#
-# For now, disable these lint checks; maintainers should endeavor
-# to investigate and remove these for maximum lint coverage.
-# Please do not carry these forward to new Makefiles.
-#
-LINTTAGS	+= -erroff=E_BAD_PTR_CAST_ALIGN
-LINTTAGS	+= -erroff=E_STATIC_UNUSED
-LINTTAGS	+= -erroff=E_PTRDIFF_OVERFLOW
-
 CERRWARN	+= -_gcc=-Wno-parentheses
 CERRWARN	+= -_gcc=-Wno-unused-variable
 CERRWARN	+= -_gcc=-Wno-unused-label
@@ -111,7 +97,10 @@
 CERRWARN	+= -_gcc=-Wno-uninitialized
 
 # needs work
-SMOFF += indenting,all_func_returns,no_if_block
+SMOFF += indenting
+SMOFF += all_func_returns
+SMOFF += no_if_block
+SMOFF += allocating_enough_data
 
 #
 #
@@ -127,12 +116,6 @@
 
 clobber:	$(CLOBBER_DEPS)
 
-lint:		$(LINT_DEPS)
-
-modlintlib:	$(MODLINTLIB_DEPS)
-
-clean.lint:	$(CLEAN_LINT_DEPS)
-
 install:	$(INSTALL_DEPS)
 
 #
