diff options
author | John Levon <john.levon@joyent.com> | 2019-07-24 13:20:02 -0700 |
---|---|---|
committer | John Levon <john.levon@joyent.com> | 2019-08-12 08:25:47 -0700 |
commit | efe51d0cc2398b9ac179568b63a44e4bf295b8e2 (patch) | |
tree | 7cfc305912bf3c9efe1a234885b96678a89af2a5 /usr/src | |
parent | cc7e66e6be0e280179bd43f44f08e442d9839a8f (diff) | |
download | illumos-joyent-efe51d0cc2398b9ac179568b63a44e4bf295b8e2.tar.gz |
11506 smatch resync
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Toomas Soome <tsoome@me.com>
Approved by: Robert Mustacchi <rm@joyent.com>
Diffstat (limited to 'usr/src')
155 files changed, 5977 insertions, 2762 deletions
diff --git a/usr/src/Makefile.smatch b/usr/src/Makefile.smatch index 27f302cb47..4f20c0497b 100644 --- a/usr/src/Makefile.smatch +++ b/usr/src/Makefile.smatch @@ -28,7 +28,7 @@ SMATCH_ARGS += -Wno-external-function-has-definition 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=%) diff --git a/usr/src/boot/lib/libstand/Makefile.inc b/usr/src/boot/lib/libstand/Makefile.inc index de07d9a0e9..6c46fdeefd 100644 --- a/usr/src/boot/lib/libstand/Makefile.inc +++ b/usr/src/boot/lib/libstand/Makefile.inc @@ -11,6 +11,7 @@ # # Copyright 2016 Toomas Soome <tsoome@me.com> +# Copyright 2019 Joyent, Inc. # # @@ -85,6 +86,9 @@ _bzlib.o _crctable.o _decompress.o _huffman.o _randtable.o bzipfs.o \ := 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 diff --git a/usr/src/boot/sys/boot/Makefile.inc b/usr/src/boot/sys/boot/Makefile.inc index 777aa87b32..6b13275062 100644 --- a/usr/src/boot/sys/boot/Makefile.inc +++ b/usr/src/boot/sys/boot/Makefile.inc @@ -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 @@ CFLAGS += $(CCNOAUTOINLINE) $(CCNOREORDER) $(CSTD_GNU99) 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 @@ SMATCH_amd64= -_smatch=-D__amd64 -_smatch=-D__amd64__ #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 diff --git a/usr/src/boot/sys/boot/efi/libefi/i386/Makefile b/usr/src/boot/sys/boot/efi/libefi/i386/Makefile index 3d1c95feed..cc749255bd 100644 --- a/usr/src/boot/sys/boot/efi/libefi/i386/Makefile +++ b/usr/src/boot/sys/boot/efi/libefi/i386/Makefile @@ -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 @@ include ../Makefile.com CFLAGS += -m32 +# false positive only with a 64-bit smatch +SMOFF += uninitialized + CLEANFILES += machine x86 $(OBJS): machine x86 diff --git a/usr/src/boot/sys/boot/libstand/Makefile.com b/usr/src/boot/sys/boot/libstand/Makefile.com index 135944335c..07598ccf90 100644 --- a/usr/src/boot/sys/boot/libstand/Makefile.com +++ b/usr/src/boot/sys/boot/libstand/Makefile.com @@ -11,6 +11,7 @@ # # Copyright 2016 Toomas Soome <tsoome@me.com> +# Copyright 2019 Joyent, Inc. # include $(SRC)/Makefile.master @@ -27,6 +28,15 @@ include $(ZFSSRC)/Makefile.inc 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) diff --git a/usr/src/cmd/ls/Makefile.com b/usr/src/cmd/ls/Makefile.com index cc3ca0ad81..c31f661cf3 100644 --- a/usr/src/cmd/ls/Makefile.com +++ b/usr/src/cmd/ls/Makefile.com @@ -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 @@ $(XPG6) := CFLAGS64 += -DXPG4 -DXPG6 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 @@ include ../../Makefile.targ $(POST_PROCESS) %.xpg6: ../%.c - $(LINK.c) -o $@ $< $(LDLIBS) + $(LINK.c) -o $@ $< $(LDLIBS) $(POST_PROCESS) %: ../%.c diff --git a/usr/src/cmd/sgs/libld/Makefile.com b/usr/src/cmd/sgs/libld/Makefile.com index 2119ecec29..b007c46bad 100644 --- a/usr/src/cmd/sgs/libld/Makefile.com +++ b/usr/src/cmd/sgs/libld/Makefile.com @@ -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 @@ DYNFLAGS += $(VERSREF) $(CC_USE_PROTO) '-R$$ORIGIN' native:= DYNFLAGS += $(CONVLIBDIR) +# too hairy +pics/sections32.o := SMATCH=off +pics/sections64.o := SMATCH=off + BLTDEFS = msg.h BLTDATA = msg.c BLTMESG = $(SGSMSGDIR)/libld diff --git a/usr/src/cmd/svc/configd/Makefile b/usr/src/cmd/svc/configd/Makefile index 9617a53f79..419ca6248b 100644 --- a/usr/src/cmd/svc/configd/Makefile +++ b/usr/src/cmd/svc/configd/Makefile @@ -24,6 +24,8 @@ # # Copyright 2015 RackTop Systems. # +# Copyright 2019 Joyent, Inc. +# MYPROG = svc.configd MYOBJS = \ @@ -60,9 +62,12 @@ CERRWARN += -_gcc=-Wno-unused-label 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 @@ clean: FRC clobber: -lint: lint_SRCS - -lint_SRCS: - include ../../Makefile.targ FRC: diff --git a/usr/src/cmd/syseventd/modules/sysevent_conf_mod/Makefile b/usr/src/cmd/syseventd/modules/sysevent_conf_mod/Makefile index 377c91df68..94cba19b73 100644 --- a/usr/src/cmd/syseventd/modules/sysevent_conf_mod/Makefile +++ b/usr/src/cmd/syseventd/modules/sysevent_conf_mod/Makefile @@ -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 @@ CPPFLAGS += -I ../../daemons/syseventconfd CERRWARN += -_gcc=-Wno-uninitialized +# strange smatch false positive +SMOFF += allocating_enough_data + .KEEP_STATE: all: $(DYNLIB) -install: all \ +install: all \ $(ROOTLIBSYSEVENTDIR) \ $(ROOTLIBDIR) \ $(ROOTLIBS) diff --git a/usr/src/common/ficl/vm.c b/usr/src/common/ficl/vm.c index e6db0e3a73..dc3f40202f 100644 --- a/usr/src/common/ficl/vm.c +++ b/usr/src/common/ficl/vm.c @@ -47,6 +47,10 @@ * SUCH DAMAGE. */ +/* + * Copyright 2019 Joyent, Inc. + */ + #include "ficl.h" #if FICL_ROBUST >= 2 @@ -2165,8 +2169,8 @@ ficlVmGetWordToPad(ficlVm *vm) 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)); diff --git a/usr/src/lib/libpctx/Makefile.com b/usr/src/lib/libpctx/Makefile.com index c9d50e434c..1cd8757729 100644 --- a/usr/src/lib/libpctx/Makefile.com +++ b/usr/src/lib/libpctx/Makefile.com @@ -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 @@ OBJECTS = libpctx.o # 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 diff --git a/usr/src/lib/libumem/Makefile.com b/usr/src/lib/libumem/Makefile.com index 70bbb164a7..504a8beb38 100644 --- a/usr/src/lib/libumem/Makefile.com +++ b/usr/src/lib/libumem/Makefile.com @@ -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_standalone = $(LINKTEST_OBJ) 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_standalone = -DUMEM_STANDALONE 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 @@ CFLAGS += $(CFLAGS_$(CURTYPE)) $(CFLAGS_common) 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) diff --git a/usr/src/tools/smatch/Makefile b/usr/src/tools/smatch/Makefile index 075cb12458..4079d9542e 100644 --- a/usr/src/tools/smatch/Makefile +++ b/usr/src/tools/smatch/Makefile @@ -13,14 +13,14 @@ # # 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. # PROG = smatch -SPARSE_VERSION = 0.5.1-il-3 +SPARSE_VERSION = 0.5.1-il-4 include ../Makefile.tools @@ -28,7 +28,7 @@ include ../Makefile.tools 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 @@ OBJS += smatch_flow.o smatch_conditions.o smatch_slist.o smatch_states.o \ 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 \ diff --git a/usr/src/tools/smatch/src/Documentation/sparse-README.txt b/usr/src/tools/smatch/src/Documentation/sparse-README.txt index 144d27459d..033ae15d5d 100644 --- a/usr/src/tools/smatch/src/Documentation/sparse-README.txt +++ b/usr/src/tools/smatch/src/Documentation/sparse-README.txt @@ -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. diff --git a/usr/src/tools/smatch/src/Makefile b/usr/src/tools/smatch/src/Makefile index d77fd2b2da..c1423afbbf 100644 --- a/usr/src/tools/smatch/src/Makefile +++ b/usr/src/tools/smatch/src/Makefile @@ -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_FILES=smatch_flow.o smatch_conditions.o smatch_slist.o smatch_states.o \ 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 \ diff --git a/usr/src/tools/smatch/src/check_64bit_shift.c b/usr/src/tools/smatch/src/check_64bit_shift.c index 89b1c23d3f..66c0bbd2b8 100644 --- a/usr/src/tools/smatch/src/check_64bit_shift.c +++ b/usr/src/tools/smatch/src/check_64bit_shift.c @@ -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 @@ void check_64bit_shift(int id) my_id = id; add_hook(&match_shift_assignment, ASSIGNMENT_HOOK); + add_hook(&match_shift_mask, BINOP_HOOK); } diff --git a/usr/src/tools/smatch/src/check_buffer_too_small_for_struct.c b/usr/src/tools/smatch/src/check_buffer_too_small_for_struct.c index 06b69d89fa..2e470ba7a3 100644 --- a/usr/src/tools/smatch/src/check_buffer_too_small_for_struct.c +++ b/usr/src/tools/smatch/src/check_buffer_too_small_for_struct.c @@ -26,6 +26,8 @@ static void match_assign(struct expression *expr) 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 @@ static void match_assign(struct expression *expr) 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 @@ static void match_dereferences(struct expression *expr) char *name; struct expression *size_expr; sval_t min_size; + int limit_type; if (expr->type != EXPR_PREOP) return; @@ -79,9 +88,11 @@ static void match_dereferences(struct expression *expr) 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)) diff --git a/usr/src/tools/smatch/src/check_cmn_err.c b/usr/src/tools/smatch/src/check_cmn_err.c new file mode 100644 index 0000000000..1063efeb47 --- /dev/null +++ b/usr/src/tools/smatch/src/check_cmn_err.c @@ -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); +} diff --git a/usr/src/tools/smatch/src/check_debug.c b/usr/src/tools/smatch/src/check_debug.c index 0defd2bfad..155e62535c 100644 --- a/usr/src/tools/smatch/src/check_debug.c +++ b/usr/src/tools/smatch/src/check_debug.c @@ -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_print_implied_max(const char *fn, struct expression *expr, voi 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 @@ static void match_buf_size(const char *fn, struct expression *expr, void *info) int elements, bytes; char *name; char buf[256] = ""; + int limit_type; int n; sval_t sval; @@ -392,7 +398,7 @@ static void match_buf_size(const char *fn, struct expression *expr, void *info) 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 @@ static void match_buf_size(const char *fn, struct expression *expr, void *info) 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 @@ static void match_local_debug_off(const char *fn, struct expression *expr, void 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 @@ static void match_mtag(const char *fn, struct expression *expr, void *info) 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 @@ static void match_mtag_data_offset(const char *fn, struct expression *expr, void 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 @@ void check_debug(int id) 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 @@ void check_debug(int id) 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); diff --git a/usr/src/tools/smatch/src/check_debug.h b/usr/src/tools/smatch/src/check_debug.h index 8e6a97f4e4..52a71a619b 100644 --- a/usr/src/tools/smatch/src/check_debug.h +++ b/usr/src/tools/smatch/src/check_debug.h @@ -74,4 +74,6 @@ static inline void __smatch_exit(void){} 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 diff --git a/usr/src/tools/smatch/src/check_dma_mapping_error.c b/usr/src/tools/smatch/src/check_dma_mapping_error.c index a786813cd5..10662db373 100644 --- a/usr/src/tools/smatch/src/check_dma_mapping_error.c +++ b/usr/src/tools/smatch/src/check_dma_mapping_error.c @@ -32,6 +32,12 @@ static void ok_to_use(struct sm_state *sm, struct expression *mod_expr) 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); } diff --git a/usr/src/tools/smatch/src/check_err_ptr_deref.c b/usr/src/tools/smatch/src/check_err_ptr_deref.c index 50a79cd3ad..814d6469d3 100644 --- a/usr/src/tools/smatch/src/check_err_ptr_deref.c +++ b/usr/src/tools/smatch/src/check_err_ptr_deref.c @@ -222,7 +222,7 @@ void check_err_ptr_deref(int id) 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); diff --git a/usr/src/tools/smatch/src/check_free_strict.c b/usr/src/tools/smatch/src/check_free_strict.c index dd551cba52..4da29cced5 100644 --- a/usr/src/tools/smatch/src/check_free_strict.c +++ b/usr/src/tools/smatch/src/check_free_strict.c @@ -291,6 +291,38 @@ free: 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 @@ void check_free_strict(int id) add_pre_merge_hook(my_id, &pre_merge_hook); select_return_states_hook(PARAM_FREED, &set_param_freed); + add_untracked_param_hook(&match_untracked); } diff --git a/usr/src/tools/smatch/src/check_held_dev.c b/usr/src/tools/smatch/src/check_held_dev.c index 43f61d7712..c776e1a653 100644 --- a/usr/src/tools/smatch/src/check_held_dev.c +++ b/usr/src/tools/smatch/src/check_held_dev.c @@ -113,7 +113,7 @@ static void register_returns_held_funcs(void) 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); diff --git a/usr/src/tools/smatch/src/check_kernel.c b/usr/src/tools/smatch/src/check_kernel.c index 98a4342fe7..69c9a37555 100644 --- a/usr/src/tools/smatch/src/check_kernel.c +++ b/usr/src/tools/smatch/src/check_kernel.c @@ -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 @@ static void match_param_valid_ptr(const char *fn, struct expression *call_expr, 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 @@ static void match_param_err_or_null(const char *fn, struct expression *call_expr 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 @@ static void match_not_err(const char *fn, struct expression *call_expr, { 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 @@ static void match_err(const char *fn, struct expression *call_expr, { 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 @@ static void match__read_once_size(const char *fn, struct expression *call, __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); diff --git a/usr/src/tools/smatch/src/check_kernel_printf.c b/usr/src/tools/smatch/src/check_kernel_printf.c index c85dac1e20..8992a83fd7 100644 --- a/usr/src/tools/smatch/src/check_kernel_printf.c +++ b/usr/src/tools/smatch/src/check_kernel_printf.c @@ -521,6 +521,15 @@ static void dentry_file(const char *fmt, struct symbol *type, struct symbol *bas 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 @@ pointer(const char *fmt, struct expression *arg, int vaidx) 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; diff --git a/usr/src/tools/smatch/src/check_list.h b/usr/src/tools/smatch/src/check_list.h index 0c6ab0cd5f..6e0a0d7a8c 100644 --- a/usr/src/tools/smatch/src/check_list.h +++ b/usr/src/tools/smatch/src/check_list.h @@ -53,8 +53,11 @@ CK(register_type_links) 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) 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_dma_mapping_error) 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 @@ CK(check_wine_WtoA) /* illumos specific */ CK(check_all_func_returns) +CK(check_cmn_err) #include "check_list_local.h" diff --git a/usr/src/tools/smatch/src/check_locking.c b/usr/src/tools/smatch/src/check_locking.c index 06cc7a597a..81fe9dda75 100644 --- a/usr/src/tools/smatch/src/check_locking.c +++ b/usr/src/tools/smatch/src/check_locking.c @@ -49,6 +49,7 @@ enum return_type { ret_any, ret_non_zero, ret_zero, + ret_one, ret_negative, ret_positive, }; @@ -151,18 +152,18 @@ static struct lock_info kernel_lock_table[] = { {"__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 @@ static struct lock_info kernel_lock_table[] = { {"__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 @@ static struct lock_info kernel_lock_table[] = { {"_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,16 +248,30 @@ static struct lock_info kernel_lock_table[] = { {"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}, {"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 @@ static struct lock_info kernel_lock_table[] = { {"_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 @@ static struct lock_info kernel_lock_table[] = { {"__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 @@ static struct lock_info kernel_lock_table[] = { {"_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 @@ static void pre_merge_hook(struct sm_state *sm) 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 @@ static void do_lock(const char *name) 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,12 +767,15 @@ static void print_inconsistent_returns(struct tracker *lock, 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! */ 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 @@ static void register_lock(int index) 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 @@ static void register_lock(int index) } 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); } } diff --git a/usr/src/tools/smatch/src/check_macro_side_effects.c b/usr/src/tools/smatch/src/check_macro_side_effects.c index 29c92b0e4b..ef4e7de4eb 100644 --- a/usr/src/tools/smatch/src/check_macro_side_effects.c +++ b/usr/src/tools/smatch/src/check_macro_side_effects.c @@ -30,9 +30,12 @@ static struct smatch_state *alloc_my_state(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; @@ -160,6 +163,7 @@ void check_macro_side_effects(int id) if (!option_spammy) return; + set_dynamic_states(my_id); add_hook(&match_unop, OP_HOOK); add_hook(&match_stmt, STMT_HOOK); register_ignored_macros(); diff --git a/usr/src/tools/smatch/src/check_missing_break.c b/usr/src/tools/smatch/src/check_missing_break.c index fb30518131..4b6b0c032d 100644 --- a/usr/src/tools/smatch/src/check_missing_break.c +++ b/usr/src/tools/smatch/src/check_missing_break.c @@ -177,6 +177,7 @@ void check_missing_break(int id) if (!option_spammy) return; + set_dynamic_states(my_id); add_unmatched_state_hook(my_id, &unmatched_state); add_merge_hook(my_id, &merge_hook); diff --git a/usr/src/tools/smatch/src/check_no_return.c b/usr/src/tools/smatch/src/check_no_return.c index 57c6afdf38..3971ecce92 100644 --- a/usr/src/tools/smatch/src/check_no_return.c +++ b/usr/src/tools/smatch/src/check_no_return.c @@ -16,6 +16,7 @@ */ #include "smatch.h" +#include "smatch_slist.h" static int my_id; static int returned; @@ -32,6 +33,8 @@ static void match_func_end(struct symbol *sym) { 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; diff --git a/usr/src/tools/smatch/src/check_nospec.c b/usr/src/tools/smatch/src/check_nospec.c index a4d5baef89..a2aea00dfe 100644 --- a/usr/src/tools/smatch/src/check_nospec.c +++ b/usr/src/tools/smatch/src/check_nospec.c @@ -96,6 +96,7 @@ static void struct_member_callback(struct expression *call, int param, char *pri 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 @@ static void returned_struct_members(int return_id, char *return_ranges, struct e 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 @@ static void match_barrier(struct statement *stmt) 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 @@ static void db_returns_barrier(struct expression *expr, int param, char *key, ch 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 @@ void check_nospec(int id) 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); diff --git a/usr/src/tools/smatch/src/check_off_by_one_relative.c b/usr/src/tools/smatch/src/check_off_by_one_relative.c index f6f6e6fac7..2249288e51 100644 --- a/usr/src/tools/smatch/src/check_off_by_one_relative.c +++ b/usr/src/tools/smatch/src/check_off_by_one_relative.c @@ -33,19 +33,23 @@ static void array_check(struct expression *expr) struct expression *size; struct expression *offset; char *array_str, *offset_str; + int limit_type; expr = strip_expr(expr); if (!is_array(expr)) 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 @@ static void array_check(struct expression *expr) 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 @@ static void array_check_data_info(struct expression *expr) 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); diff --git a/usr/src/tools/smatch/src/check_precedence.c b/usr/src/tools/smatch/src/check_precedence.c index 3ee062295c..a7220ab8b5 100644 --- a/usr/src/tools/smatch/src/check_precedence.c +++ b/usr/src/tools/smatch/src/check_precedence.c @@ -120,6 +120,16 @@ static void match_mask(struct expression *expr) 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 @@ void check_precedence(int id) 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); } diff --git a/usr/src/tools/smatch/src/check_return_cast.c b/usr/src/tools/smatch/src/check_return_cast.c index 5a87a6e27f..d0237e4584 100644 --- a/usr/src/tools/smatch/src/check_return_cast.c +++ b/usr/src/tools/smatch/src/check_return_cast.c @@ -28,6 +28,9 @@ static void match_return(struct expression *ret_value) 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)) diff --git a/usr/src/tools/smatch/src/check_rosenberg.c b/usr/src/tools/smatch/src/check_rosenberg.c index bde603a706..7a03ee488d 100644 --- a/usr/src/tools/smatch/src/check_rosenberg.c +++ b/usr/src/tools/smatch/src/check_rosenberg.c @@ -382,6 +382,7 @@ void check_rosenberg2(int id) return; my_member_id = id; + set_dynamic_states(my_member_id); add_extra_mod_hook(&extra_mod_hook); } diff --git a/usr/src/tools/smatch/src/check_shift_to_zero.c b/usr/src/tools/smatch/src/check_shift_to_zero.c index 57cc3cbb84..019b06fb75 100644 --- a/usr/src/tools/smatch/src/check_shift_to_zero.c +++ b/usr/src/tools/smatch/src/check_shift_to_zero.c @@ -35,6 +35,8 @@ static void match_binop(struct expression *expr) 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); } diff --git a/usr/src/tools/smatch/src/check_snprintf.c b/usr/src/tools/smatch/src/check_snprintf.c index 162e07c6db..43f5131787 100644 --- a/usr/src/tools/smatch/src/check_snprintf.c +++ b/usr/src/tools/smatch/src/check_snprintf.c @@ -79,6 +79,7 @@ void check_snprintf(int id) 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); diff --git a/usr/src/tools/smatch/src/check_spectre.c b/usr/src/tools/smatch/src/check_spectre.c index e35527b5e4..014235daef 100644 --- a/usr/src/tools/smatch/src/check_spectre.c +++ b/usr/src/tools/smatch/src/check_spectre.c @@ -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 @@ static void array_check(struct expression *expr) 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 @@ static void array_check(struct expression *expr) 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); diff --git a/usr/src/tools/smatch/src/check_spectre_second_half.c b/usr/src/tools/smatch/src/check_spectre_second_half.c new file mode 100644 index 0000000000..158fc81da2 --- /dev/null +++ b/usr/src/tools/smatch/src/check_spectre_second_half.c @@ -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); +} diff --git a/usr/src/tools/smatch/src/check_string_len.c b/usr/src/tools/smatch/src/check_string_len.c index 3d6fd7f105..f8c102ae01 100644 --- a/usr/src/tools/smatch/src/check_string_len.c +++ b/usr/src/tools/smatch/src/check_string_len.c @@ -35,13 +35,13 @@ struct param_info { 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 @@ static int handle_format(struct expression *call, char **pp, int *arg_nr) 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 @@ out_no_arg: 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 @@ int get_formatted_string_size(struct expression *call, int arg) 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 @@ int get_formatted_string_size(struct expression *call, int arg) } } - 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 @@ static void match_not_limited(const char *fn, struct expression *call, void *inf 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; diff --git a/usr/src/tools/smatch/src/check_syscall_arg_type.c b/usr/src/tools/smatch/src/check_syscall_arg_type.c index 1beb27f33c..dbabaae679 100644 --- a/usr/src/tools/smatch/src/check_syscall_arg_type.c +++ b/usr/src/tools/smatch/src/check_syscall_arg_type.c @@ -161,6 +161,7 @@ void check_syscall_arg_type(int id) 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); } diff --git a/usr/src/tools/smatch/src/check_testing_index_after_use.c b/usr/src/tools/smatch/src/check_testing_index_after_use.c index 18b0fcffe6..b04ceece5d 100644 --- a/usr/src/tools/smatch/src/check_testing_index_after_use.c +++ b/usr/src/tools/smatch/src/check_testing_index_after_use.c @@ -35,29 +35,12 @@ static void delete(struct sm_state *sm, struct expression *mod_expr) 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 @@ static void array_check(struct expression *expr) 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 @@ static void match_condition(struct expression *expr) 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); diff --git a/usr/src/tools/smatch/src/check_uninitialized.c b/usr/src/tools/smatch/src/check_uninitialized.c index 9cf28f3612..97d8d51f54 100644 --- a/usr/src/tools/smatch/src/check_uninitialized.c +++ b/usr/src/tools/smatch/src/check_uninitialized.c @@ -95,6 +95,41 @@ static void match_assign(struct expression *expr) 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 @@ static void match_dereferences(struct expression *expr) { char *name; - if (parse_error) + if (implications_off || parse_error) return; if (expr->type != EXPR_PREOP) @@ -135,7 +170,7 @@ static void match_condition(struct expression *expr) { char *name; - if (parse_error) + if (implications_off || parse_error) return; if (is_impossible_path()) @@ -265,7 +300,7 @@ static void match_symbol(struct expression *expr) { char *name; - if (parse_error) + if (implications_off || parse_error) return; if (is_impossible_path()) @@ -352,6 +387,7 @@ void check_uninitialized(int id) 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); diff --git a/usr/src/tools/smatch/src/check_unwind.c b/usr/src/tools/smatch/src/check_unwind.c index 9fa6d0cfe2..b29b8362f6 100644 --- a/usr/src/tools/smatch/src/check_unwind.c +++ b/usr/src/tools/smatch/src/check_unwind.c @@ -194,21 +194,21 @@ void check_unwind(int id) 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 @@ void check_unwind(int id) add_function_hook("misc_deregister", &match_release, INT_PTR(0)); release_function_indicator("misc_deregister"); - add_hook(&match_return, RETURN_HOOK); } diff --git a/usr/src/tools/smatch/src/check_wine_WtoA.c b/usr/src/tools/smatch/src/check_wine_WtoA.c index 4ef4529231..f074471bd7 100644 --- a/usr/src/tools/smatch/src/check_wine_WtoA.c +++ b/usr/src/tools/smatch/src/check_wine_WtoA.c @@ -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 diff --git a/usr/src/tools/smatch/src/check_zero_to_err_ptr.c b/usr/src/tools/smatch/src/check_zero_to_err_ptr.c index 8d5984a183..618f24abcc 100644 --- a/usr/src/tools/smatch/src/check_zero_to_err_ptr.c +++ b/usr/src/tools/smatch/src/check_zero_to_err_ptr.c @@ -74,15 +74,48 @@ static int next_line_checks_IS_ERR(struct expression *call, struct expression *a 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; } @@ -90,7 +123,9 @@ 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); @@ -109,17 +144,19 @@ static void match_err_ptr(const char *fn, struct expression *expr, void *data) 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); } diff --git a/usr/src/tools/smatch/src/evaluate.c b/usr/src/tools/smatch/src/evaluate.c index 8f07d08cf5..14abc8fa5c 100644 --- a/usr/src/tools/smatch/src/evaluate.c +++ b/usr/src/tools/smatch/src/evaluate.c @@ -1946,7 +1946,7 @@ static struct symbol *evaluate_preop(struct expression *expr) 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; diff --git a/usr/src/tools/smatch/src/expression.h b/usr/src/tools/smatch/src/expression.h index 78813602b8..214313786d 100644 --- a/usr/src/tools/smatch/src/expression.h +++ b/usr/src/tools/smatch/src/expression.h @@ -261,6 +261,7 @@ struct token *assignment_expression(struct token *token, struct expression **tre 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 *); diff --git a/usr/src/tools/smatch/src/graph.c b/usr/src/tools/smatch/src/graph.c index 22c90be936..8cbc220273 100644 --- a/usr/src/tools/smatch/src/graph.c +++ b/usr/src/tools/smatch/src/graph.c @@ -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> diff --git a/usr/src/tools/smatch/src/smatch.c b/usr/src/tools/smatch/src/smatch.c index 450caeef40..e0fb1bd818 100644 --- a/usr/src/tools/smatch/src/smatch.c +++ b/usr/src/tools/smatch/src/smatch.c @@ -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_call_tree = 0; 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; @@ -231,8 +231,6 @@ void parse_args(int *argcp, char ***argvp) 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 @@ static char *get_data_dir(char *arg0) int main(int argc, char **argv) { + struct string_list *filelist = NULL; int i; reg_func func; @@ -343,8 +342,12 @@ int main(int argc, char **argv) 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 @@ int main(int argc, char **argv) func(i); } - smatch(argc, argv); + smatch(filelist); free_string(data_dir); if (option_succeed) diff --git a/usr/src/tools/smatch/src/smatch.h b/usr/src/tools/smatch/src/smatch.h index 285321b039..3b94e6577d 100644 --- a/usr/src/tools/smatch/src/smatch.h +++ b/usr/src/tools/smatch/src/smatch.h @@ -79,8 +79,6 @@ struct sm_state { 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 @@ struct constraint { }; 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 add_macro_assign_hook_extra(const char *look_for, func_hook *call_back, 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 char *trace_variable; 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 append(char *dest, const char *data, int buff_len); 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_fuzzy_max(struct expression *expr, sval_t *max); 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 ms_since(struct timeval *start); 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_assign; 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 @@ extern struct statement *__next_stmt; 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_select_assigns(struct expression *expr); 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 @@ int assume(struct expression *expr); 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,79 +589,83 @@ struct data_range { #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}, }; 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 @@ enum info_type { PARAM_VALUE = 1001, BUF_SIZE = 1002, - USER_DATA = 1003, CAPPED_DATA = 1004, RETURN_VALUE = 1005, DEREFERENCE = 1006, @@ -795,6 +810,7 @@ enum info_type { PARAM_FREED = 1013, DATA_SOURCE = 1014, FUZZY_MAX = 1015, + HARD_MAX = 2015, STR_LEN = 1016, ARRAY_LEN = 1017, CAPABLE = 1018, @@ -803,6 +819,7 @@ enum info_type { CASTED_CALL = 1021, TYPE_LINK = 1022, UNTRACKED_PARAM = 1023, + LOST_PARAM = 2023, CULL_PATH = 1024, PARAM_SET = 1025, PARAM_USED = 1026, @@ -812,14 +829,15 @@ enum info_type { CONSTRAINT = 1031, PASSES_TYPE = 1032, CONSTRAINT_REQUIRED = 1033, + BIT_INFO = 1034, NOSPEC = 1035, NOSPEC_WB = 1036, STMT_CNT = 1037, 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 @@ enum info_type { 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_param_name(struct sm_state *sm); 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_copy_constraint_required(const char *new_limit, const char *old_limit); 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 @@ int is_capped_var_sym(const char *name, struct symbol *sym); /* 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 combine_comparisons(int left_compare, int right_compare); 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_preop(sval_t sval, int op); 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 @@ struct sm_state *stored_condition_implication_hook(struct expression *expr, /* 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 store_link(int link_id, const char *name, struct symbol *sym, const char *l 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 @@ char *get_required_constraint(const char *data_str); /* 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 */ - return bits_in_pointer; - if (type->type == SYM_ARRAY) + if (type_is_ptr(type)) 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 @@ static inline int type_positive_bits(struct symbol *type) { 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; diff --git a/usr/src/tools/smatch/src/smatch_about_fn_ptr_arg.c b/usr/src/tools/smatch/src/smatch_about_fn_ptr_arg.c index 347833f7d2..cbf0355603 100644 --- a/usr/src/tools/smatch/src/smatch_about_fn_ptr_arg.c +++ b/usr/src/tools/smatch/src/smatch_about_fn_ptr_arg.c @@ -139,7 +139,7 @@ static char *get_data_member(char *fn_member, struct expression *expr, struct sy } 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); } diff --git a/usr/src/tools/smatch/src/smatch_address.c b/usr/src/tools/smatch/src/smatch_address.c index 5c48e05fdb..539492a023 100644 --- a/usr/src/tools/smatch/src/smatch_address.c +++ b/usr/src/tools/smatch/src/smatch_address.c @@ -62,22 +62,53 @@ static bool is_non_null_array(struct expression *expr) 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 @@ int get_member_offset_from_deref(struct expression *expr) 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 @@ int get_member_offset_from_deref(struct expression *expr) 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 @@ static void add_offset_to_pointer(struct range_list **rl, int offset) if (offset == 0) return; + if (is_unknown_ptr(orig)) + return; + /* * This function doesn't necessarily work how you might expect... * @@ -164,16 +183,6 @@ static void add_offset_to_pointer(struct range_list **rl, int offset) 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,52 +202,94 @@ static struct range_list *where_allocated_rl(struct symbol *sym) 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; } diff --git a/usr/src/tools/smatch/src/smatch_array_values.c b/usr/src/tools/smatch/src/smatch_array_values.c index 7627cebc2f..3f9f73383e 100644 --- a/usr/src/tools/smatch/src/smatch_array_values.c +++ b/usr/src/tools/smatch/src/smatch_array_values.c @@ -163,7 +163,7 @@ static void match_assign(struct expression *expr) 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 @@ static void match_assign(struct expression *expr) 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); diff --git a/usr/src/tools/smatch/src/smatch_assigned_expr.c b/usr/src/tools/smatch/src/smatch_assigned_expr.c index b0c2c4f767..188577e42c 100644 --- a/usr/src/tools/smatch/src/smatch_assigned_expr.c +++ b/usr/src/tools/smatch/src/smatch_assigned_expr.c @@ -28,8 +28,12 @@ int check_assigned_expr_id; 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 @@ struct expression *get_assigned_expr_name_sym(const char *name, struct symbol *s 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 @@ static void match_assignment(struct expression *expr) 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 @@ static void record_param_assignment(struct expression *expr, int param, char *ke 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 @@ free: 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(int id) 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); } diff --git a/usr/src/tools/smatch/src/smatch_bits.c b/usr/src/tools/smatch/src/smatch_bits.c new file mode 100644 index 0000000000..0492203836 --- /dev/null +++ b/usr/src/tools/smatch/src/smatch_bits.c @@ -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); +} diff --git a/usr/src/tools/smatch/src/smatch_buf_comparison.c b/usr/src/tools/smatch/src/smatch_buf_comparison.c index d142817a99..f86b0774e6 100644 --- a/usr/src/tools/smatch/src/smatch_buf_comparison.c +++ b/usr/src/tools/smatch/src/smatch_buf_comparison.c @@ -29,24 +29,18 @@ static int size_id; 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 @@ static void match_link_modify(struct sm_state *sm, struct expression *mod_expr) 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,20 +140,21 @@ static int bytes_per_element(struct expression *expr) 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; 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) { struct expression *tmp; struct sm_state *sm; + int limit_type = ELEM_COUNT; sval_t sval; int cnt = 0; @@ -159,11 +189,17 @@ static void match_alloc_helper(struct expression *pointer, struct expression *si 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 @@ static void match_calloc(const char *fn, struct expression *expr, void *_start_a 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 @@ static void match_calloc(const char *fn, struct expression *expr, void *_start_a 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,15 +268,18 @@ static void array_check(struct expression *expr) struct expression *size; struct expression *offset; char *array_str, *offset_str; + int limit_type; expr = strip_expr(expr); if (!is_array(expr)) 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,20 +364,30 @@ int db_var_is_array_limit(struct expression *array, const char *name, struct var 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 @@ static void array_check_data_info(struct expression *expr) 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 @@ static void add_allocation_function(const char *func, void *call_back, int param 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,16 +566,19 @@ static void match_call(struct expression *call) struct expression *arg; char *compare; int param; + char buf[5]; + int limit_type; param = -1; FOR_EACH_PTR(call->args, arg) { 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 @@ static void set_param_compare(const char *array_name, struct symbol *array_sym, 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 @@ static void munge_start_states(struct statement *stmt) 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 @@ void register_buf_comparison(int id) 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); } diff --git a/usr/src/tools/smatch/src/smatch_buf_size.c b/usr/src/tools/smatch/src/smatch_buf_size.c index 0c7f34947b..b7bad47254 100644 --- a/usr/src/tools/smatch/src/smatch_buf_size.c +++ b/usr/src/tools/smatch/src/smatch_buf_size.c @@ -330,8 +330,6 @@ static int get_bytes_from_address(struct expression *expr) 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 @@ struct range_list *get_array_size_bytes_rl(struct expression *expr) 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 @@ struct range_list *get_array_size_bytes_rl(struct expression *expr) 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_alloc(const char *fn, struct expression *expr, void *_size_arg 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 @@ void register_buf_size(int id) { 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 @@ void register_buf_size(int id) 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); diff --git a/usr/src/tools/smatch/src/smatch_capped.c b/usr/src/tools/smatch/src/smatch_capped.c index a42303d4b7..1b25c4795e 100644 --- a/usr/src/tools/smatch/src/smatch_capped.c +++ b/usr/src/tools/smatch/src/smatch_capped.c @@ -42,7 +42,7 @@ static struct smatch_state *unmatched_state(struct sm_state *sm) { 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 @@ static int is_capped_macro(struct expression *expr) int is_capped(struct expression *expr) { + struct symbol *type; sval_t dummy; expr = strip_expr(expr); @@ -77,6 +78,14 @@ int is_capped(struct expression *expr) 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 @@ int is_capped(struct expression *expr) 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 @@ void set_param_capped_data(const char *name, struct symbol *sym, char *key, char 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 @@ static void match_condition(struct expression *expr) 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 @@ static void struct_member_callback(struct expression *call, int param, char *pri 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 @@ static void print_return_implies_capped(int return_id, char *return_ranges, stru 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 @@ static void print_return_implies_capped(int return_id, char *return_ranges, stru 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; diff --git a/usr/src/tools/smatch/src/smatch_comparison.c b/usr/src/tools/smatch/src/smatch_comparison.c index 51b4a45487..44e6717b2c 100644 --- a/usr/src/tools/smatch/src/smatch_comparison.c +++ b/usr/src/tools/smatch/src/smatch_comparison.c @@ -641,7 +641,7 @@ static void save_link_var_sym(const char *var, struct symbol *sym, const char *l 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 @@ static void match_inc(struct sm_state *sm) 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 @@ static void match_inc(struct sm_state *sm) } 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 @@ static void match_dec(struct sm_state *sm) 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 @@ static void match_dec(struct sm_state *sm) } 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 @@ static void match_inc_dec(struct sm_state *sm, struct expression *mod_expr) */ 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 int is_self_assign(struct expression *expr) 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 @@ static void match_modify(struct sm_state *sm, struct expression *mod_expr) ((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 @@ int get_comparison_strings(const char *one, const char *two) 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: 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 @@ static int split_op_param_key(char *value, int *op, int *param, char **key) 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 @@ static void free_data(struct symbol *sym) 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_late(int id) 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(int id) 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); } diff --git a/usr/src/tools/smatch/src/smatch_conditions.c b/usr/src/tools/smatch/src/smatch_conditions.c index 79f89bdd56..3127b13f46 100644 --- a/usr/src/tools/smatch/src/smatch_conditions.c +++ b/usr/src/tools/smatch/src/smatch_conditions.c @@ -420,6 +420,8 @@ static void split_conditions(struct expression *expr) * 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; } diff --git a/usr/src/tools/smatch/src/smatch_constraints.c b/usr/src/tools/smatch/src/smatch_constraints.c index b026e90383..7350cfe096 100644 --- a/usr/src/tools/smatch/src/smatch_constraints.c +++ b/usr/src/tools/smatch/src/smatch_constraints.c @@ -196,6 +196,7 @@ char *get_constraint_str(struct expression *expr) { char *name; + expr = strip_expr(expr); if (!expr) return NULL; if (expr->type == EXPR_CALL) @@ -341,9 +342,8 @@ free_data: 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 @@ static void handle_comparison(struct expression *left, int op, struct expression 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 @@ static void handle_comparison(struct expression *left, int op, struct expression 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 @@ void register_constraints(int id) { my_id = id; + set_dynamic_states(my_id); add_merge_hook(my_id, &merge_func); add_hook(&match_condition, CONDITION_HOOK); diff --git a/usr/src/tools/smatch/src/smatch_constraints_required.c b/usr/src/tools/smatch/src/smatch_constraints_required.c index 3708938586..8b8009bf80 100644 --- a/usr/src/tools/smatch/src/smatch_constraints_required.c +++ b/usr/src/tools/smatch/src/smatch_constraints_required.c @@ -281,14 +281,17 @@ free_data: 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 @@ void register_constraints_required(int id) { 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); diff --git a/usr/src/tools/smatch/src/smatch_container_of.c b/usr/src/tools/smatch/src/smatch_container_of.c index 7399726917..a832aee15b 100644 --- a/usr/src/tools/smatch/src/smatch_container_of.c +++ b/usr/src/tools/smatch/src/smatch_container_of.c @@ -112,7 +112,7 @@ static int get_container_offset(struct symbol *sym) 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 @@ static void process_states(void) 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 @@ static void returns_container_of(struct expression *expr, int param, char *key, 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; + + 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; + } + return cnt; +} - i = 0; - while (true) { - if (!one[i] || !two[i]) { - on_end = true; - break; - } - if (one[i] != two[i]) - break; - i++; +static struct expression *get_partial_deref(struct expression *expr, int cnt) +{ + while (--cnt >= 0) { + if (!expr || expr->type != EXPR_DEREF) + return expr; + expr = expr->deref; + if (expr->type == EXPR_PREOP && expr->op == '*') + expr = expr->unop; } - if (i == 0) - return 0; - i--; - while (i > 0 && (one[i] == '>' || one[i] == '-' || one[i] == '.')) { - on_end = true; - i--; + return expr; +} + +static int partial_deref_to_offset_str(struct expression *expr, int cnt, char op, char *buf, int size) +{ + int n, offset; + + if (cnt == 0) + return snprintf(buf, size, "%c0", op); + + n = 0; + while (--cnt >= 0) { + offset = get_member_offset_from_deref(expr); + if (offset < 0) + return -1; + n += snprintf(buf + n, size - n, "%c%d", op, offset); + if (expr->type != EXPR_DEREF) + return -1; + expr = expr->deref; + if (expr->type == EXPR_PREOP && expr->op == '*') + expr = expr->unop; } - if (!on_end) - return 0; - return i + 1; + return n; } -static int build_offset_str(struct expression *expr, const char *name, - int shared, char *buf, int size, int op) +static char *get_shared_str(struct expression *container, struct expression *expr) { - int chop = 0; - int offset; - int i; + struct expression *one, *two; + int cont, exp, min, ret, n; + static char buf[48]; - i = shared; - while (name[i]) { - if (name[i] == '.' || name[i] == '-') - chop++; - i++; + cont = get_deref_count(container); + exp = get_deref_count(expr); + if (cont < 0 || exp < 0) + return NULL; + + min = (cont < exp) ? cont : exp; + while (min >= 0) { + one = get_partial_deref(container, cont - min); + two = get_partial_deref(expr, exp - min); + if (expr_equiv(one, two)) + goto found; + min--; } - // FIXME: Handle more chops - if (chop > 1) - return 0; + return NULL; - if (chop == 0) { - offset = 0; - } else { - offset = get_member_offset_from_deref(expr); - if (offset < 0) - return 0; +found: + ret = partial_deref_to_offset_str(container, cont - min, '-', buf, sizeof(buf)); + if (ret < 0) + return NULL; + n = ret; + ret = partial_deref_to_offset_str(expr, exp - min, '+', buf + ret, sizeof(buf) - ret); + if (ret < 0) + return NULL; + n += ret; + if (n >= sizeof(buf)) + return NULL; + + return buf; +} + +char *get_container_name(struct expression *container, struct expression *expr) +{ + struct symbol *container_sym, *sym; + struct expression *tmp; + static char buf[64]; + char *shared; + bool star; + int cnt; + + container_sym = expr_to_sym(container); + sym = expr_to_sym(expr); + if (container_sym && container_sym == sym) + goto found; + + cnt = 0; + while ((tmp = get_assigned_expr(expr))) { + expr = tmp; + if (cnt++ > 3) + break; + } + + cnt = 0; + while ((tmp = get_assigned_expr(container))) { + container = tmp; + if (cnt++ > 3) + break; } - snprintf(buf, size, "%c%d", (op == '+') ? '+' : '-', offset); - return 1; +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 @@ static void match_call(struct expression *call) * 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 @@ static struct stree *load_tag_info_sym(mtag_t tag, struct symbol *arg, int arg_o 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 @@ found: 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 @@ void register_container_of(int id) 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); } diff --git a/usr/src/tools/smatch/src/smatch_data/db/apply_return_fixes.sh b/usr/src/tools/smatch/src/smatch_data/db/apply_return_fixes.sh new file mode 100755 index 0000000000..c663a0d214 --- /dev/null +++ b/usr/src/tools/smatch/src/smatch_data/db/apply_return_fixes.sh @@ -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 + diff --git a/usr/src/tools/smatch/src/smatch_data/db/create_db.sh b/usr/src/tools/smatch/src/smatch_data/db/create_db.sh index 42c1524d15..37ff637650 100755 --- a/usr/src/tools/smatch/src/smatch_data/db/create_db.sh +++ b/usr/src/tools/smatch/src/smatch_data/db/create_db.sh @@ -44,15 +44,12 @@ if [ "$PROJ" != "" ] ; then 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 diff --git a/usr/src/tools/smatch/src/smatch_data/db/delete_too_common_fn_ptr.sh b/usr/src/tools/smatch/src/smatch_data/db/delete_too_common_fn_ptr.sh new file mode 100755 index 0000000000..5f050f3c59 --- /dev/null +++ b/usr/src/tools/smatch/src/smatch_data/db/delete_too_common_fn_ptr.sh @@ -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 diff --git a/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh b/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh index 0a323cf374..8a52d2585f 100755 --- a/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh +++ b/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh @@ -23,18 +23,8 @@ delete from caller_info where function = '(struct irq_router)->set' and type != 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 @@ delete from return_states where function='sprintf' and type = 8017; /* 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 function = 'nf_tables_newexpr' and type = 8017 and 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 @@ insert into return_states values ('faked', 'rw_verify_area', 0, 2, '(-4095)-(-1) 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 @@ delete from return_states where function = 'bitmap_allocate_region' and return = /* 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 @@ update caller_info set value = 4096 where caller='kernfs_file_direct_read' and f 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 @@ delete from caller_info where function = '(struct i2c_algorithm)->master_xfer' a /* 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 @@ echo "select distinct file, function from function_ptr where ptr='(struct rtl_ha | 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 diff --git a/usr/src/tools/smatch/src/smatch_data/db/function_ptr.schema b/usr/src/tools/smatch/src/smatch_data/db/function_ptr.schema index 20227b24e9..3ad777de4c 100644 --- a/usr/src/tools/smatch/src/smatch_data/db/function_ptr.schema +++ b/usr/src/tools/smatch/src/smatch_data/db/function_ptr.schema @@ -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) +); + diff --git a/usr/src/tools/smatch/src/smatch_data/db/init_constraints.pl b/usr/src/tools/smatch/src/smatch_data/db/init_constraints.pl index d1db7d03a8..e2bc868553 100755 --- a/usr/src/tools/smatch/src/smatch_data/db/init_constraints.pl +++ b/usr/src/tools/smatch/src/smatch_data/db/init_constraints.pl @@ -46,6 +46,10 @@ sub load_manual_constraints($$) my $project = shift; my $dir = dirname($full_path); + if ($project =~ /^$/) { + return; + } + open(FILE, "$dir/$project.constraints"); while (<FILE>) { s/\n//; diff --git a/usr/src/tools/smatch/src/smatch_data/db/init_constraints_required.pl b/usr/src/tools/smatch/src/smatch_data/db/init_constraints_required.pl index e7a4057544..15ce19b2c2 100755 --- a/usr/src/tools/smatch/src/smatch_data/db/init_constraints_required.pl +++ b/usr/src/tools/smatch/src/smatch_data/db/init_constraints_required.pl @@ -34,6 +34,10 @@ sub load_manual_constraints($$) my $dir = dirname($full_path); my ($data, $op, $limit); + if ($project =~ /^$/) { + return; + } + open(FILE, "$dir/$project.constraints_required"); while (<FILE>) { ($data, $op, $limit) = split(/,/); diff --git a/usr/src/tools/smatch/src/smatch_data/db/kernel.return_fixes b/usr/src/tools/smatch/src/smatch_data/db/kernel.return_fixes index 03d2e00d05..66fe421030 100644 --- a/usr/src/tools/smatch/src/smatch_data/db/kernel.return_fixes +++ b/usr/src/tools/smatch/src/smatch_data/db/kernel.return_fixes @@ -11,7 +11,9 @@ scnprintf 0-s32max 0-s32max[<$1] 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,16 +21,35 @@ simple_write_to_buffer s64min-s64max 0-s32max[<=$1] 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] __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] diff --git a/usr/src/tools/smatch/src/smatch_data/db/smdb.py b/usr/src/tools/smatch/src/smatch_data/db/smdb.py index 1dcad2e94e..c0b310b13b 100755 --- a/usr/src/tools/smatch/src/smatch_data/db/smdb.py +++ b/usr/src/tools/smatch/src/smatch_data/db/smdb.py @@ -17,6 +17,7 @@ except sqlite3.Error, e: 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 @@ db_types = { 0: "INTERNAL", 104: "PARAM_FILTER", 1001: "PARAM_VALUE", 1002: "BUF_SIZE", - 1003: "USER_DATA", 1004: "CAPPED_DATA", 1005: "RETURN_VALUE", 1006: "DEREFERENCE", @@ -79,7 +79,22 @@ db_types = { 0: "INTERNAL", 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 @@ def txt_to_val(txt): 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: 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 @@ elif sys.argv[1] == "call_info": 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) diff --git a/usr/src/tools/smatch/src/smatch_data/db/vim_smdb b/usr/src/tools/smatch/src/smatch_data/db/vim_smdb index 5760066110..c169c9c5d9 100755 --- a/usr/src/tools/smatch/src/smatch_data/db/vim_smdb +++ b/usr/src/tools/smatch/src/smatch_data/db/vim_smdb @@ -25,7 +25,7 @@ fi next=$(($i + 1)) rm -f $DIR/$next -rm $DIR/.${i}.swp +rm -f $DIR/.${i}.swp smdb $* > $DIR/$i echo "$DIR/$i" > $DIR/cur diff --git a/usr/src/tools/smatch/src/smatch_data/illumos_kernel.skipped_functions b/usr/src/tools/smatch/src/smatch_data/illumos_kernel.skipped_functions index 452d7f5871..3617b9b806 100644 --- a/usr/src/tools/smatch/src/smatch_data/illumos_kernel.skipped_functions +++ b/usr/src/tools/smatch/src/smatch_data/illumos_kernel.skipped_functions @@ -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 diff --git a/usr/src/tools/smatch/src/smatch_data/illumos_user.skipped_functions b/usr/src/tools/smatch/src/smatch_data/illumos_user.skipped_functions index ce1c807d97..f82ff4f38a 100644 --- a/usr/src/tools/smatch/src/smatch_data/illumos_user.skipped_functions +++ b/usr/src/tools/smatch/src/smatch_data/illumos_user.skipped_functions @@ -24,8 +24,12 @@ sqliteVdbeExec AslCompilerparse /* cmd/fs.d/autofs */ nfsmount +/* cmd/mdb */ +iob_doprnt /* cmd/pppd */ lcp_nakci +/* cmd/cmd-crypto */ +execute_cmd /* generated code */ ipf_yyparse diff --git a/usr/src/tools/smatch/src/smatch_data/kernel.allocation_funcs_gfp.remove b/usr/src/tools/smatch/src/smatch_data/kernel.allocation_funcs_gfp.remove index 78c093f6f8..4e99631377 100644 --- a/usr/src/tools/smatch/src/smatch_data/kernel.allocation_funcs_gfp.remove +++ b/usr/src/tools/smatch/src/smatch_data/kernel.allocation_funcs_gfp.remove @@ -1,2 +1,3 @@ +acquire_group acquire_group 2 acquire_group X diff --git a/usr/src/tools/smatch/src/smatch_data/kernel.ignore_casted_params b/usr/src/tools/smatch/src/smatch_data/kernel.ignore_casted_params new file mode 100644 index 0000000000..4981799f70 --- /dev/null +++ b/usr/src/tools/smatch/src/smatch_data/kernel.ignore_casted_params @@ -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 diff --git a/usr/src/tools/smatch/src/smatch_data/kernel.ignore_side_effects b/usr/src/tools/smatch/src/smatch_data/kernel.ignore_side_effects index e94fa1e605..b4237017c0 100644 --- a/usr/src/tools/smatch/src/smatch_data/kernel.ignore_side_effects +++ b/usr/src/tools/smatch/src/smatch_data/kernel.ignore_side_effects @@ -53,12 +53,16 @@ RADEON_WAIT_UNTIL_IDLE 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 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 diff --git a/usr/src/tools/smatch/src/smatch_data/kernel.ignore_uninitialized_param b/usr/src/tools/smatch/src/smatch_data/kernel.ignore_uninitialized_param index f76216cfff..16825e753a 100644 --- a/usr/src/tools/smatch/src/smatch_data/kernel.ignore_uninitialized_param +++ b/usr/src/tools/smatch/src/smatch_data/kernel.ignore_uninitialized_param @@ -102,3 +102,10 @@ read_current_timer 0 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 diff --git a/usr/src/tools/smatch/src/smatch_data/kernel.ignored_warnings b/usr/src/tools/smatch/src/smatch_data/kernel.ignored_warnings new file mode 100644 index 0000000000..758066bc22 --- /dev/null +++ b/usr/src/tools/smatch/src/smatch_data/kernel.ignored_warnings @@ -0,0 +1 @@ +check_shift_to_zero overflows_type diff --git a/usr/src/tools/smatch/src/smatch_data/kernel.no_inline_functions b/usr/src/tools/smatch/src/smatch_data/kernel.no_inline_functions index 149bc0deaa..388824e15f 100644 --- a/usr/src/tools/smatch/src/smatch_data/kernel.no_inline_functions +++ b/usr/src/tools/smatch/src/smatch_data/kernel.no_inline_functions @@ -10,3 +10,5 @@ __arch_hweight32 __arch_hweight64 __write_once_size atomic_set +atomic_read +notifier_to_errno diff --git a/usr/src/tools/smatch/src/smatch_data/kernel.no_return_funcs.add b/usr/src/tools/smatch/src/smatch_data/kernel.no_return_funcs.add new file mode 100644 index 0000000000..db33514776 --- /dev/null +++ b/usr/src/tools/smatch/src/smatch_data/kernel.no_return_funcs.add @@ -0,0 +1,2 @@ +YY_FATAL_ERROR +malformed_line diff --git a/usr/src/tools/smatch/src/smatch_data/kernel.silenced_functions b/usr/src/tools/smatch/src/smatch_data/kernel.silenced_functions index e23ef2635f..6ff3b95a86 100644 --- a/usr/src/tools/smatch/src/smatch_data/kernel.silenced_functions +++ b/usr/src/tools/smatch/src/smatch_data/kernel.silenced_functions @@ -4,7 +4,6 @@ atomic_inc_and_test 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 diff --git a/usr/src/tools/smatch/src/smatch_data_source.c b/usr/src/tools/smatch/src/smatch_data_source.c index 7ada921cdb..e63c27a89f 100644 --- a/usr/src/tools/smatch/src/smatch_data_source.c +++ b/usr/src/tools/smatch/src/smatch_data_source.c @@ -24,12 +24,14 @@ static int my_id; static char *get_source_parameter(struct expression *expr) { struct expression *tmp; + const char *param_name; struct symbol *sym; char *name; int param; char *ret = NULL; char buf[32]; int cnt = 0; + bool modified = false; tmp = expr; while ((tmp = get_assigned_expr(tmp))) { @@ -48,10 +50,14 @@ static char *get_source_parameter(struct expression *expr) 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: diff --git a/usr/src/tools/smatch/src/smatch_db.c b/usr/src/tools/smatch/src/smatch_db.c index e9e1eab24b..d96c9ace99 100644 --- a/usr/src/tools/smatch/src/smatch_db.c +++ b/usr/src/tools/smatch/src/smatch_db.c @@ -79,7 +79,7 @@ char *escape_newlines(const char *str) 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_caller_info(struct expression *call, int type, 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 @@ void sql_save_constraint(const char *con) 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 @@ void sql_insert_mtag_about(mtag_t tag, const char *left_name, const char *right_ 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 @@ static char *show_offset(int offset) 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 @@ static void print_struct_members(struct expression *call, struct expression *exp 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 @@ static char *get_next_ptr_name(void) 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 @@ static void match_call_implies(struct symbol *sym) 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 @@ static void call_return_state_hooks_conditional(struct expression *expr) { 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 @@ static void call_return_state_hooks_conditional(struct expression *expr) __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 @@ static int ptr_in_list(struct sm_state *sm, struct state_list *slist) 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 @@ static int split_possible_helper(struct sm_state *sm, struct expression *expr) 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 @@ static int split_possible_helper(struct sm_state *sm, struct expression *expr) 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 @@ static int split_possible_helper(struct sm_state *sm, struct expression *expr) 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 @@ static int call_return_state_hooks_split_possible(struct expression *expr) 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 @@ static int split_positive_from_negative(struct expression *expr) 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 @@ static int split_positive_from_negative(struct expression *expr) 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 @@ static int split_positive_from_negative(struct expression *expr) 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 @@ static int split_positive_from_negative(struct expression *expr) 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 @@ static int call_return_state_hooks_split_null_non_null(struct expression *expr) 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 @@ static void call_return_state_hooks(struct expression *expr) 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 @@ static int save_cache_data(void *_table, int argc, char **argv, char **azColName 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 @@ static void register_return_replacements(void) 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 @@ char *return_state_to_var_sym(struct expression *expr, int param, const char *ke 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 *get_variable_from_key(struct expression *arg, const char *key, struct symb { char buf[256]; char *tmp; + bool add_star = false; if (!arg) return NULL; @@ -2450,19 +2452,25 @@ char *get_variable_from_key(struct expression *arg, const char *key, struct symb } } + 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); } 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 @@ const char *state_name_to_param_name(const char *state_name, const char *param_n { 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; } diff --git a/usr/src/tools/smatch/src/smatch_equiv.c b/usr/src/tools/smatch/src/smatch_equiv.c index e00b6f2a6a..4e99c37a83 100644 --- a/usr/src/tools/smatch/src/smatch_equiv.c +++ b/usr/src/tools/smatch/src/smatch_equiv.c @@ -102,21 +102,6 @@ struct related_list *get_shared_relations(struct related_list *one, 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 @@ static void add_related(struct related_list **rlist, const char *name, struct sy .sym = sym }; - debug_addition(*rlist, name); - FOR_EACH_PTR(*rlist, rel) { if (cmp_relation(rel, &tmp) < 0) continue; @@ -212,19 +195,28 @@ void set_related(struct smatch_state *estate, struct related_list *rlist) */ 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) - right_sm = set_state_expr(SMATCH_EXTRA, right, alloc_estate_whole(get_type(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) goto free; @@ -233,12 +225,24 @@ void set_equiv(struct expression *left, struct expression *right) 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; diff --git a/usr/src/tools/smatch/src/smatch_estate.c b/usr/src/tools/smatch/src/smatch_estate.c index 61a8fd347a..533cd2d9e0 100644 --- a/usr/src/tools/smatch/src/smatch_estate.c +++ b/usr/src/tools/smatch/src/smatch_estate.c @@ -43,11 +43,16 @@ struct smatch_state *merge_estates(struct smatch_state *s1, struct smatch_state 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 @@ int estate_get_hard_max(struct smatch_state *state, sval_t *sval) 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 @@ int estates_equiv(struct smatch_state *one, struct smatch_state *two) 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 @@ struct smatch_state *clone_estate(struct smatch_state *state) 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 @@ struct smatch_state *get_implied_estate(struct expression *expr) 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. diff --git a/usr/src/tools/smatch/src/smatch_expressions.c b/usr/src/tools/smatch/src/smatch_expressions.c index 9900342b89..a0d05ed8cb 100644 --- a/usr/src/tools/smatch/src/smatch_expressions.c +++ b/usr/src/tools/smatch/src/smatch_expressions.c @@ -165,7 +165,7 @@ struct expression *string_expression(char *str) 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 @@ struct expression *gen_expression_from_key(struct expression *arg, const char *k 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; } diff --git a/usr/src/tools/smatch/src/smatch_extra.c b/usr/src/tools/smatch/src/smatch_extra.c index 0633cffc6d..c756182355 100644 --- a/usr/src/tools/smatch/src/smatch_extra.c +++ b/usr/src/tools/smatch/src/smatch_extra.c @@ -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 @@ static char *get_pointed_at(const char *name, struct symbol *sym, struct symbol 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; - - *new_sym = NULL; - - if (!sym || !sym->ident) - return NULL; - - ret = get_pointed_at(name, sym, new_sym); - if (ret) - return ret; + char *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 @@ void set_extra_nomod(const char *name, struct symbol *sym, struct expression *ex 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 @@ static struct sm_state *handle_canonical_while_count_down(struct statement *loop { 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 @@ static struct sm_state *handle_canonical_while_count_down(struct statement *loop 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 @@ static struct sm_state *handle_canonical_for_inc(struct expression *iter_expr, 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 @@ static struct sm_state *handle_canonical_for_inc(struct expression *iter_expr, 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 @@ static struct sm_state *handle_canonical_for_inc(struct expression *iter_expr, } 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,13 +669,14 @@ static struct sm_state *handle_canonical_for_dec(struct expression *iter_expr, 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; 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 @@ void __extra_pre_loop_hook_after(struct sm_state *sm, 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 @@ void __extra_pre_loop_hook_after(struct sm_state *sm, 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 @@ static struct smatch_state *unmatched_state(struct sm_state *sm) } 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 @@ static void match_function_call(struct expression *expr) } 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 @@ static int values_fit_type(struct expression *left, struct expression *right) 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 @@ static void match_vanilla_assign(struct expression *left, struct expression *rig 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: 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 @@ static void match_assign(struct expression *expr) 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 @@ static void match_assign(struct expression *expr) 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 @@ static void match_assign(struct expression *expr) 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 @@ static void check_dereference(struct expression *expr) 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 @@ static int handle_postop_inc(struct expression *left, int op, struct expression 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 @@ static int handle_postop_inc(struct expression *left, int op, struct expression 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 @@ bool is_impossible_variable(struct expression *expr) 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,18 +1504,18 @@ static void handle_comparison(struct symbol *type, struct expression *left, int 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 '>': 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 @@ static int is_simple_math(struct expression *expr) 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,29 +1796,8 @@ static int match_func_comparison(struct expression *expr) { 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; - } - 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; - } + if (left->type == EXPR_CALL || right->type == EXPR_CALL) { function_comparison(left, expr->op, right); return 1; } @@ -1772,6 +1841,10 @@ static int handle_integer_overflow_test(struct expression *expr) 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 @@ static sval_t get_high_mask(sval_t known) 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 @@ static void handle_AND_condition(struct expression *expr) { 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 @@ static void handle_MOD_condition(struct expression *expr) 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 @@ static void handle_MOD_condition(struct expression *expr) /* 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 @@ void __extra_match_condition(struct expression *expr) 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 @@ static int param_used_callback(void *found, int argc, char **argv, char **azColN 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 @@ static int filter_unused_kzalloc_info(struct expression *call, int param, char * 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(¶m_used_callback, &found, @@ -2246,50 +2371,68 @@ struct range_list *intersect_with_real_abs_expr(struct expression *expr, struct 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 @@ static void db_limited_after(void) 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 @@ static void db_param_limit_filter(struct expression *expr, int param, char *key, 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,17 +2506,20 @@ static void db_param_limit_filter(struct expression *expr, int param, char *key, 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; 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,27 +2529,28 @@ static void db_param_limit_filter(struct expression *expr, int param, char *key, 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); 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)); 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_filter(struct expression *expr, int param, char *key, char 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 @@ static void db_param_add_set(struct expression *expr, int param, char *key, char 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 @@ static void db_param_set(struct expression *expr, int param, char *key, char *va 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 @@ static void match_call_info(struct expression *expr) 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 @@ static void match_call_info(struct expression *expr) 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 match_call_info(struct expression *expr) 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++; + } + + snprintf(fullname, 256, "%s%s%s", add_star ? "*" : "", name, key + 1); - type = get_member_type_from_key(symbol_expression(sym), key); + 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 @@ static void set_param_value(const char *name, struct symbol *sym, char *key, cha 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,13 +2751,31 @@ static void set_param_hard_max(const char *name, struct symbol *sym, char *key, 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; 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 @@ void register_smatch_extra(int id) { 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 @@ static void match_link_modify(struct sm_state *sm, struct expression *mod_expr) void register_smatch_extra_links(int id) { link_id = id; + set_dynamic_states(link_id); } void register_smatch_extra_late(int id) diff --git a/usr/src/tools/smatch/src/smatch_extra.h b/usr/src/tools/smatch/src/smatch_extra.h index d48cdf1f79..6877053f86 100644 --- a/usr/src/tools/smatch/src/smatch_extra.h +++ b/usr/src/tools/smatch/src/smatch_extra.h @@ -30,6 +30,7 @@ struct data_info { 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 @@ struct range_list *rl_one(void); 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 ranges_equiv(struct data_range *one, struct data_range *two); 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 @@ sval_t rl_max(struct range_list *rl); 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_rl(struct range_list *rl); 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 @@ int estate_has_hard_max(struct smatch_state *state); 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); diff --git a/usr/src/tools/smatch/src/smatch_flow.c b/usr/src/tools/smatch/src/smatch_flow.c index 5c4d64bc02..11a70bdd70 100644 --- a/usr/src/tools/smatch/src/smatch_flow.c +++ b/usr/src/tools/smatch/src/smatch_flow.c @@ -76,30 +76,23 @@ int option_two_passes = 0; 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 @@ void __split_expr(struct expression *expr) break; case EXPR_OFFSETOF: case EXPR_ALIGNOF: - evaluate_expression(expr); break; case EXPR_CONDITIONAL: case EXPR_SELECT: @@ -878,9 +870,14 @@ int time_parsing_function(void) 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 @@ static void open_output_files(char *base_file) 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 @@ void smatch(int argc, char **argv) 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 @@ void smatch(int argc, char **argv) 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) diff --git a/usr/src/tools/smatch/src/smatch_function_hooks.c b/usr/src/tools/smatch/src/smatch_function_hooks.c index 9aa51c319b..983befeac1 100644 --- a/usr/src/tools/smatch/src/smatch_function_hooks.c +++ b/usr/src/tools/smatch/src/smatch_function_hooks.c @@ -139,6 +139,16 @@ void return_implies_state(const char *look_for, long long start, long long end, 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 @@ static int assign_ranged_funcs(const char *fn, struct expression *expr, 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 @@ static int assign_ranged_funcs(const char *fn, struct expression *expr, 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 void store_return_state(struct db_callback_info *db_info, const char *ret 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 @@ static bool fake_a_param_assignment(struct expression *expr, const char *return_ 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 @@ static bool fake_a_param_assignment(struct expression *expr, const char *return_ 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 @@ static void set_return_state(struct expression *expr, struct db_callback_info *d 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 @@ static int db_compare_callback(void *_info, int argc, char **argv, char **azColN 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 @@ static int db_compare_callback(void *_info, int argc, char **argv, char **azColN 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 @@ static int db_compare_callback(void *_info, int argc, char **argv, char **azColN 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 @@ static int db_compare_callback(void *_info, int argc, char **argv, char **azColN 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 @@ static int db_compare_callback(void *_info, int argc, char **argv, char **azColN 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 @@ static void compare_db_return_states_callbacks(struct expression *left, int comp __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 @@ static void compare_db_return_states_callbacks(struct expression *left, int comp __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 @@ static void call_ranged_return_hooks(struct db_callback_info *db_info) 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 @@ static int db_assign_return_states_callback(void *_info, int argc, char **argv, 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 @@ static int db_assign_return_states_callback(void *_info, int argc, char **argv, 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 @@ static int db_assign_return_states_callback(void *_info, int argc, char **argv, 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,17 +898,17 @@ static int db_assign_return_states_callback(void *_info, int argc, char **argv, 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); 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 @@ static int db_return_states_assign(struct expression *expr) } 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 @@ static int db_return_states_callback(void *_info, int argc, char **argv, char ** 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 @@ static int db_return_states_callback(void *_info, int argc, char **argv, char ** 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 @@ static int db_return_states_callback(void *_info, int argc, char **argv, char ** 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); } diff --git a/usr/src/tools/smatch/src/smatch_function_ptrs.c b/usr/src/tools/smatch/src/smatch_function_ptrs.c index 9a897f9cb4..e8b616425b 100644 --- a/usr/src/tools/smatch/src/smatch_function_ptrs.c +++ b/usr/src/tools/smatch/src/smatch_function_ptrs.c @@ -141,6 +141,9 @@ char *get_fnptr_name(struct expression *expr) { 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 @@ static void match_returns_function_pointer(struct expression *expr) 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 @@ void register_function_ptrs(int id) 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); diff --git a/usr/src/tools/smatch/src/smatch_helper.c b/usr/src/tools/smatch/src/smatch_helper.c index 81d4e2dd78..81125ad308 100644 --- a/usr/src/tools/smatch/src/smatch_helper.c +++ b/usr/src/tools/smatch/src/smatch_helper.c @@ -81,14 +81,26 @@ struct smatch_state *alloc_state_str(const char *name) 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,16 +178,16 @@ static void __get_variable_from_expr(struct symbol **sym_ptr, char *buf, 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 == '&') { 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 @@ char *expr_to_chunk_helper(struct expression *expr, struct symbol **sym, struct 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 @@ char *get_member_name(struct expression *expr) 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 @@ int invert_op(int op) 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; diff --git a/usr/src/tools/smatch/src/smatch_ignore.c b/usr/src/tools/smatch/src/smatch_ignore.c index eb06a0512a..fa2b564bab 100644 --- a/usr/src/tools/smatch/src/smatch_ignore.c +++ b/usr/src/tools/smatch/src/smatch_ignore.c @@ -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 @@ int is_ignored_expr(int owner, struct expression *expr) 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 @@ static void clear_ignores(void) 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(); } diff --git a/usr/src/tools/smatch/src/smatch_imaginary_absolute.c b/usr/src/tools/smatch/src/smatch_imaginary_absolute.c index 6532dfc397..3b401bd965 100644 --- a/usr/src/tools/smatch/src/smatch_imaginary_absolute.c +++ b/usr/src/tools/smatch/src/smatch_imaginary_absolute.c @@ -53,6 +53,8 @@ static void reset(struct sm_state *sm, struct expression *mod_expr) 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 @@ void register_imaginary_absolute(int id) { 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); diff --git a/usr/src/tools/smatch/src/smatch_implied.c b/usr/src/tools/smatch/src/smatch_implied.c index ae5ff1dc4c..86f682f851 100644 --- a/usr/src/tools/smatch/src/smatch_implied.c +++ b/usr/src/tools/smatch/src/smatch_implied.c @@ -65,9 +65,11 @@ #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(): @@ -87,7 +89,7 @@ static struct range_list *tmp_range_list(struct symbol *type, long long num) 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,18 +145,18 @@ static int create_fake_history(struct sm_state *sm, int comparison, struct range 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)); 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 @@ static void do_compare(struct sm_state *sm, int comparison, struct range_list *r 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 @@ static void __separate_pools(struct sm_state *sm, int comparison, struct range_l 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 @@ static void __separate_pools(struct sm_state *sm, int comparison, struct range_l 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 @@ static void separate_pools(struct sm_state *sm, int comparison, struct range_lis { 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); + gettimeofday(&start_time, NULL); + __separate_pools(sm, comparison, rl, true_stack, &maybe_stack, false_stack, checked, mixed, sm, &start_time); - if (option_debug) { + if (implied_debug) { struct sm_state *sm; FOR_EACH_PTR(*true_stack, sm) { @@ -356,7 +350,8 @@ static void separate_pools(struct sm_state *sm, int comparison, struct range_lis } 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 @@ static int sm_in_keep_leafs(struct sm_state *sm, const struct state_list *keep_g 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 @@ static int taking_too_long(void) * 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 @@ struct sm_state *filter_pools(struct sm_state *sm, 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 @@ struct sm_state *filter_pools(struct sm_state *sm, 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 @@ static struct stree *filter_stack(struct sm_state *gate_sm, 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) - continue; - if (sm_in_keep_leafs(tmp, keep_stack)) + if (!tmp->merged || 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 @@ static void separate_and_filter(struct sm_state *sm, int comparison, struct rang 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 @@ static void separate_and_filter(struct sm_state *sm, int comparison, struct rang *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 @@ static int handled_by_stored_conditions(struct expression *expr, 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 @@ static void get_tf_states(struct expression *expr, 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 @@ void param_limit_implications(struct expression *expr, int param, char *key, cha 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 @@ int assume(struct expression *expr) 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--; diff --git a/usr/src/tools/smatch/src/smatch_integer_overflow.c b/usr/src/tools/smatch/src/smatch_integer_overflow.c new file mode 100644 index 0000000000..e164598aff --- /dev/null +++ b/usr/src/tools/smatch/src/smatch_integer_overflow.c @@ -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); +} diff --git a/usr/src/tools/smatch/src/smatch_kernel_user_data.c b/usr/src/tools/smatch/src/smatch_kernel_user_data.c index 709958a666..dac5bf5208 100644 --- a/usr/src/tools/smatch/src/smatch_kernel_user_data.c +++ b/usr/src/tools/smatch/src/smatch_kernel_user_data.c @@ -32,7 +32,7 @@ static int my_call_id; 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,8 +45,11 @@ static const char * kstr_funcs[] = { 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); @@ -84,6 +87,7 @@ static void pre_merge_hook(struct sm_state *sm) { struct smatch_state *user; struct smatch_state *extra; + struct smatch_state *state; struct range_list *rl; sval_t dummy; sval_t sval_100; @@ -91,52 +95,90 @@ static void pre_merge_hook(struct sm_state *sm) sval_100.value = 100; sval_100.type = &int_ctype; - user = get_state(my_id, sm->name, sm->sym); - if (!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()); + user = __get_state(my_id, sm->name, sm->sym); + if (!user || !estate_rl(user)) return; - } - extra = get_state(SMATCH_EXTRA, sm->name, sm->sym); - if (!extra || !estate_rl(extra)) + extra = __get_state(SMATCH_EXTRA, sm->name, sm->sym); + if (!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) @@ -347,6 +389,22 @@ static int is_skb_data(struct expression *expr) 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 @@ int points_to_user_data(struct expression *expr) 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 @@ int points_to_user_data(struct expression *expr) 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 @@ static void set_points_to_user_data(struct expression *expr) 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 @@ free: 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_eq_noteq(struct expression *expr) } } -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 @@ static void match_condition(struct expression *expr) 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 @@ static int get_user_macro_rl(struct expression *expr, struct range_list **rl) 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 we_pass_user_data(struct expression *call) 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 @@ struct stree *get_user_stree(void) 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 @@ static struct range_list *var_user_rl(struct expression *expr) 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 @@ found: 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 @@ int get_user_rl(struct expression *expr, struct range_list **rl) 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 @@ int get_user_rl_var_sym(const char *name, struct symbol *sym, struct range_list 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 @@ static void struct_member_callback(struct expression *call, int param, char *pri 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 @@ static void struct_member_callback(struct expression *call, int param, char *pri 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 @@ static void match_syscall_definition(struct symbol *sym) } 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 @@ static void set_to_user_data(struct expression *expr, char *key, char *value) 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 @@ static void returns_param_user_data(struct expression *expr, int param, char *ke 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 @@ static void returns_param_user_data_set(struct expression *expr, int param, char 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 @@ static void returns_param_user_data_set(struct expression *expr, int param, char 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,19 +1327,21 @@ static void param_set_to_user_data(int return_id, char *return_ranges, struct ex 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); 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 @@ static void param_set_to_user_data(int return_id, char *return_ranges, struct ex 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 @@ static void param_set_to_user_data(int return_id, char *return_ranges, struct ex 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); + } + + /* + * 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: 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 @@ static void match_inline_end(struct expression *expr) 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 @@ void register_kernel_user_data2(int id) 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 @@ void register_kernel_user_data2(int id) 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(¶m_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 @@ void register_kernel_user_data3(int id) return; select_caller_info_hook(set_called, INTERNAL); } - diff --git a/usr/src/tools/smatch/src/smatch_links.c b/usr/src/tools/smatch/src/smatch_links.c index c24f0c220b..ba467224e0 100644 --- a/usr/src/tools/smatch/src/smatch_links.c +++ b/usr/src/tools/smatch/src/smatch_links.c @@ -102,6 +102,7 @@ void set_up_link_functions(int id, int link_id) 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 diff --git a/usr/src/tools/smatch/src/smatch_local_values.c b/usr/src/tools/smatch/src/smatch_local_values.c index cffc206612..da0fcc4055 100644 --- a/usr/src/tools/smatch/src/smatch_local_values.c +++ b/usr/src/tools/smatch/src/smatch_local_values.c @@ -234,6 +234,7 @@ void register_local_values(int id) 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); diff --git a/usr/src/tools/smatch/src/smatch_math.c b/usr/src/tools/smatch/src/smatch_math.c index 04d3c0a4b4..9fb3429dd5 100644 --- a/usr/src/tools/smatch/src/smatch_math.c +++ b/usr/src/tools/smatch/src/smatch_math.c @@ -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 @@ enum { 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 @@ static struct range_list *last_stmt_rl(struct statement *stmt, int implied, int 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 bool handle_ampersand_rl(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res, sval_t *res_sval) +{ + return handle_address(expr, implied, recurse_cnt, res, res_sval); } -static struct range_list *handle_negate_rl(struct expression *expr, int implied, int *recurse_cnt) +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)) - return rl_zero(); - if (known_condition_false(expr->unop)) - return rl_one(); + 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 @@ static int handle_offset_subtraction(struct expression *expr) 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 @@ static struct range_list *handle_subtract_rl(struct expression *expr, int implie 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 @@ static struct range_list *handle_subtract_rl(struct expression *expr, int implie /* 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 @@ static struct range_list *handle_subtract_rl(struct expression *expr, int implie 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,129 +420,75 @@ static struct range_list *handle_subtract_rl(struct expression *expr, int implie } 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; 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); -} - -static sval_t sval_lowest_set_bit(sval_t sval) -{ - 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; + *res = alloc_rl(sval_cast(right.type, zero), right); + return true; } -static struct range_list *handle_bitwise_AND(struct expression *expr, int implied, int *recurse_cnt) +static bool handle_bitwise_AND(struct expression *expr, int implied, int *recurse_cnt, struct range_list **res) { 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; - - 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); - } - } + if (!get_rl_internal(expr->left, implied, recurse_cnt, &left_rl)) + left_rl = alloc_whole_rl(type); + left_rl = cast_rl(type, left_rl); 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; - - *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); - } - } + 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; - 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 @@ static struct range_list *use_rl_binop(struct expression *expr, int implied, int 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,74 +613,92 @@ static struct range_list *handle_implied_binop(struct range_list *left_rl, int o 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) @@ -662,19 +727,25 @@ static int do_comparison(struct expression *expr) 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 @@ static struct range_list *handle_comparison_rl(struct expression *expr, int impl 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,39 +791,47 @@ static struct range_list *handle_logical_rl(struct expression *expr, int implied 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; } 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,79 +843,81 @@ static struct range_list *handle_conditional_rl(struct expression *expr, int imp 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); __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; 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; 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,54 +961,67 @@ struct range_list *var_to_absolute_rl(struct expression *expr) 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; 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)) - return alloc_rl(sval, sval); + if (get_mtag_sval(expr, &sval)) { + *res_sval = sval; + return true; + } 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 @@ static struct range_list *handle_variable(struct expression *expr, int implied, 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,35 +1054,37 @@ static struct range_list *handle_variable(struct expression *expr, int implied, * 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; 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 @@ static sval_t handle_sizeof(struct expression *expr) 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 }; - - if (implied == RL_EXACT) - return NULL; + struct range_list *rl; 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 @@ static struct range_list *handle__builtin_choose_expr(struct expression *expr, i 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 @@ static struct range_list *handle_call_rl(struct expression *expr, int implied, i 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 @@ void clear_math_cache(void) 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_value(struct expression *expr, sval_t *sval) 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_min(struct expression *expr, sval_t *sval) 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_max(struct expression *expr, sval_t *sval) 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 @@ static int get_absolute_rl_internal(struct expression *expr, struct range_list * 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_absolute_rl(struct expression *expr, struct range_list **rl) 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 @@ int custom_get_absolute_rl(struct expression *expr, 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_implied_rl_var_sym(const char *var, struct symbol *sym, struct range_lis 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 @@ int get_fuzzy_min(struct expression *expr, sval_t *sval) { 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 @@ int get_fuzzy_max(struct expression *expr, sval_t *sval) { 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 @@ int get_absolute_min(struct expression *expr, sval_t *sval) { 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 @@ int get_absolute_max(struct expression *expr, sval_t *sval) { 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 @@ int implied_condition_false(struct expression *expr) 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; -} diff --git a/usr/src/tools/smatch/src/smatch_mem_tracker.c b/usr/src/tools/smatch/src/smatch_mem_tracker.c index 31081139f1..c10ee90587 100644 --- a/usr/src/tools/smatch/src/smatch_mem_tracker.c +++ b/usr/src/tools/smatch/src/smatch_mem_tracker.c @@ -22,7 +22,7 @@ static int my_id; 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,14 +30,24 @@ static void match_end_func(struct symbol *sym) 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) diff --git a/usr/src/tools/smatch/src/smatch_modification_hooks.c b/usr/src/tools/smatch/src/smatch_modification_hooks.c index b4f9e62fe3..07a32cdfbc 100644 --- a/usr/src/tools/smatch/src/smatch_modification_hooks.c +++ b/usr/src/tools/smatch/src/smatch_modification_hooks.c @@ -52,9 +52,12 @@ static struct smatch_state *alloc_my_state(struct expression *expr, struct smatc 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 @@ static void db_param_add(struct expression *expr, int param, char *key, char *va 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 @@ void register_modification_hooks(int id) { 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)); diff --git a/usr/src/tools/smatch/src/smatch_mtag.c b/usr/src/tools/smatch/src/smatch_mtag.c index c499c8f923..b30e51bc2d 100644 --- a/usr/src/tools/smatch/src/smatch_mtag.c +++ b/usr/src/tools/smatch/src/smatch_mtag.c @@ -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 @@ static mtag_t str_to_tag(const char *str) 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 @@ int get_toplevel_mtag(struct symbol *sym, mtag_t *tag) 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; - - /* - * 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. - * - */ + char buf[256]; - if (!get_mtag(expr->unop, &container_tag)) - return 0; + if (!sym || !sym->ident) + return false; - offset = get_member_offset_from_deref(expr); - if (offset < 0) - return 0; + if (get_toplevel_mtag(sym, tag)) + return true; - if (!mtag_map_select_tag(container_tag, -offset, &member_tag)) - return 0; + if (get_param_num_from_sym(sym) >= 0) + return false; - *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 @@ static void global_variable(struct symbol *sym) (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,107 +232,35 @@ static int get_array_mtag_offset(struct expression *expr, mtag_t *tag, int *offs 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; - - *tag = sval.uvalue & ~MTAG_OFFSET_MASK; - *offset = sval.uvalue & MTAG_OFFSET_MASK; - return 1; -} - -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 (!rl_to_sval(rl, &sval)) + return rl; + if (sval.type->type != SYM_PTR || sval.uvalue != MTAG_SEED) + return rl; - 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; + 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); } int create_mtag_alias(mtag_t tag, struct expression *expr, mtag_t *new) @@ -415,10 +290,45 @@ int create_mtag_alias(mtag_t tag, struct expression *expr, mtag_t *new) 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 @@ int expr_to_mtag_offset(struct expression *expr, mtag_t *tag, int *offset) 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) + 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; + + 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); + + if (get_implied_mtag_offset(expr, tag, &tmp_offset)) { + // FIXME: look it up recursively? + if (tmp_offset) + return 0; + return 1; + } return 0; - return get_mtag(expr->deref, tag); + } 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); } - - if (get_implied_mtag_offset(expr, tag, offset)) - return 1; - - return get_mtag(expr, 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,81 +399,43 @@ int get_mtag_sval(struct expression *expr, sval_t *sval) 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;" * */ 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; - expr = strip_expr(expr->unop); - - if (!expr_to_mtag_offset(expr, &tag, &offset)) + return 0; +found: + if (offset >= MTAG_OFFSET_MASK) return 0; - if (offset > MTAG_OFFSET_MASK) - offset = MTAG_OFFSET_MASK; -found: sval->type = type; sval->uvalue = tag | offset; 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 @@ void register_mtag(int id) * 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); } diff --git a/usr/src/tools/smatch/src/smatch_mtag_data.c b/usr/src/tools/smatch/src/smatch_mtag_data.c index 0396730390..801542373b 100644 --- a/usr/src/tools/smatch/src/smatch_mtag_data.c +++ b/usr/src/tools/smatch/src/smatch_mtag_data.c @@ -36,11 +36,9 @@ static int save_rl(void *_rl, int argc, char **argv, char **azColName) 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 @@ static int is_kernel_param(const char *name) 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 insert_mtag_data(sval_t sval, struct range_list *rl) 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 @@ void update_mtag_data(struct expression *expr) } 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 @@ static void match_global_assign(struct expression *expr) } 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 @@ static int get_vals(void *_db_info, int argc, char **argv, char **azColName) } 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 @@ static int get_rl_from_mtag_sval(sval_t sval, struct symbol *type, struct range_ } } - 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 @@ static int get_rl_from_mtag_sval(sval_t sval, struct symbol *type, struct range_ 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 @@ static void clear_cache(struct symbol *sym) 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) diff --git a/usr/src/tools/smatch/src/smatch_mtag_map.c b/usr/src/tools/smatch/src/smatch_mtag_map.c index 8384a9908f..4ca0e00c08 100644 --- a/usr/src/tools/smatch/src/smatch_mtag_map.c +++ b/usr/src/tools/smatch/src/smatch_mtag_map.c @@ -29,8 +29,9 @@ static int my_id; 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 @@ static void match_assign(struct expression *expr) 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; + + if (!expr_to_mtag_offset(left, &left_tag, &offset)) return; - sql_insert_mtag_map(right_tag, -offset, left_tag); + sql_insert_mtag_map(sval.uvalue, -offset, left_tag); } void register_mtag_map(int id) diff --git a/usr/src/tools/smatch/src/smatch_nul_terminator.c b/usr/src/tools/smatch/src/smatch_nul_terminator.c index 845c75661f..86a3dbd4fb 100644 --- a/usr/src/tools/smatch/src/smatch_nul_terminator.c +++ b/usr/src/tools/smatch/src/smatch_nul_terminator.c @@ -247,6 +247,38 @@ bool is_nul_terminated(struct expression *expr) 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 @@ void register_nul_terminator(int id) 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) diff --git a/usr/src/tools/smatch/src/smatch_param_compare_limit.c b/usr/src/tools/smatch/src/smatch_param_compare_limit.c index 1539e06249..bca4c89a55 100644 --- a/usr/src/tools/smatch/src/smatch_param_compare_limit.c +++ b/usr/src/tools/smatch/src/smatch_param_compare_limit.c @@ -180,8 +180,8 @@ static void print_return_comparison(int return_id, char *return_ranges, struct e 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 @@ static int split_op_param_key(char *value, int *op, int *param, char **key) 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 @@ void register_param_compare_limit(int id) { 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 @@ void register_param_compare_limit_links(int id) { link_id = id; + set_dynamic_states(link_id); add_merge_hook(link_id, &merge_links); - } diff --git a/usr/src/tools/smatch/src/smatch_param_filter.c b/usr/src/tools/smatch/src/smatch_param_filter.c index 01a9920f57..9b5a04b268 100644 --- a/usr/src/tools/smatch/src/smatch_param_filter.c +++ b/usr/src/tools/smatch/src/smatch_param_filter.c @@ -148,6 +148,11 @@ static void print_one_mod_param(int return_id, char *return_ranges, 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 @@ void register_param_filter(int id) { my_id = id; + set_dynamic_states(my_id); add_hook(&save_start_states, AFTER_DEF_HOOK); add_hook(&free_start_states, AFTER_FUNC_HOOK); diff --git a/usr/src/tools/smatch/src/smatch_param_limit.c b/usr/src/tools/smatch/src/smatch_param_limit.c index 6ed3259b40..3ef8afc3a7 100644 --- a/usr/src/tools/smatch/src/smatch_param_limit.c +++ b/usr/src/tools/smatch/src/smatch_param_limit.c @@ -158,6 +158,9 @@ static void print_return_value_param(int return_id, char *return_ranges, struct 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 @@ void register_param_limit(int id) { my_id = id; + set_dynamic_states(my_id); add_hook(&save_start_states, AFTER_DEF_HOOK); add_hook(&free_start_states, AFTER_FUNC_HOOK); diff --git a/usr/src/tools/smatch/src/smatch_param_set.c b/usr/src/tools/smatch/src/smatch_param_set.c index 21456057ce..44b6e3e659 100644 --- a/usr/src/tools/smatch/src/smatch_param_set.c +++ b/usr/src/tools/smatch/src/smatch_param_set.c @@ -164,7 +164,6 @@ static void print_return_value_param(int return_id, char *return_ranges, struct 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 @@ static void print_return_value_param(int return_id, char *return_ranges, struct 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 @@ void register_param_set(int id) { 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); diff --git a/usr/src/tools/smatch/src/smatch_param_to_mtag_data.c b/usr/src/tools/smatch/src/smatch_param_to_mtag_data.c index ec647317e7..a4b000f647 100644 --- a/usr/src/tools/smatch/src/smatch_param_to_mtag_data.c +++ b/usr/src/tools/smatch/src/smatch_param_to_mtag_data.c @@ -76,6 +76,18 @@ struct smatch_state *merge_tag_info(struct smatch_state *s1, struct smatch_state 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 @@ static void match_assign(struct expression *expr) 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 @@ static void match_assign(struct expression *expr) 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 @@ static void assign_to_alias(struct expression *expr, int param, mtag_t tag, int 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 @@ static void assign_to_alias(struct expression *expr, int param, mtag_t tag, int // 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 @@ void register_param_to_mtag_data(int id) { 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); diff --git a/usr/src/tools/smatch/src/smatch_param_used.c b/usr/src/tools/smatch/src/smatch_param_used.c index 80d390d359..e81bd8358a 100644 --- a/usr/src/tools/smatch/src/smatch_param_used.c +++ b/usr/src/tools/smatch/src/smatch_param_used.c @@ -31,7 +31,7 @@ static void get_state_hook(int owner, const char *name, struct symbol *sym) 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 @@ static void set_param_used(struct expression *call, struct expression *arg, char 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,6 +69,11 @@ static void process_states(void) 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); diff --git a/usr/src/tools/smatch/src/smatch_parse_call_math.c b/usr/src/tools/smatch/src/smatch_parse_call_math.c index a84622d941..5f3ed679a4 100644 --- a/usr/src/tools/smatch/src/smatch_parse_call_math.c +++ b/usr/src/tools/smatch/src/smatch_parse_call_math.c @@ -114,7 +114,7 @@ static void rl_discard_stacks(void) 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 @@ static int read_rl_from_var(struct expression *call, char *p, char **end, struct 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 @@ static int read_rl_from_var(struct expression *call, char *p, char **end, struct 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,14 +176,14 @@ static int read_var_num(struct expression *call, char *p, char **end, struct ran 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); return 1; } -static char *read_op(char *p) +static const char *read_op(const char *p) { while (*p == ' ') p++; @@ -199,14 +199,14 @@ static char *read_op(char *p) } } -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 @@ static int format_call_to_param_mapping(char *buf, int remaining, struct express 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 @@ static int format_expr_helper(char *buf, int remaining, struct expression *expr) 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 *get_value_in_terms_of_parameter_math_var_sym(const char *name, struct symb 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 @@ char *get_value_in_terms_of_parameter_math_var_sym(const char *name, struct symb 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 @@ static char *swap_format(struct expression *call, char *format) 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 @@ void register_parse_call_math(int id) 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)); diff --git a/usr/src/tools/smatch/src/smatch_passes_array_size.c b/usr/src/tools/smatch/src/smatch_passes_array_size.c index cf69c8cdc5..fa17ec3e3b 100644 --- a/usr/src/tools/smatch/src/smatch_passes_array_size.c +++ b/usr/src/tools/smatch/src/smatch_passes_array_size.c @@ -39,11 +39,15 @@ static int find_param_eq(struct expression *expr, int size) 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 @@ static void match_call(struct expression *expr) 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 @@ static void match_call(struct expression *expr) 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; } } diff --git a/usr/src/tools/smatch/src/smatch_ranges.c b/usr/src/tools/smatch/src/smatch_ranges.c index fbc93fec7e..d534328476 100644 --- a/usr/src/tools/smatch/src/smatch_ranges.c +++ b/usr/src/tools/smatch/src/smatch_ranges.c @@ -26,27 +26,77 @@ __DO_ALLOCATOR(struct data_range, sizeof(struct data_range), __alignof__(struct "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; + } + 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)); } - 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)); } END_FOR_EACH_PTR(tmp); - if (strlen(full) == sizeof(full) - 1) - full[sizeof(full) - 2] = '+'; + return alloc_sname(full); } @@ -79,6 +129,18 @@ static int sval_too_big(struct symbol *type, sval_t sval) 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 @@ static void add_range_t(struct symbol *type, struct range_list **rl, sval_t min, 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 void add_range_t(struct symbol *type, struct range_list **rl, sval_t min, 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 @@ static int str_to_comparison_arg_helper(const char *str, c++; c++; *comparison = SPECIAL_NOTEQUAL; + } else if (*c == '$') { + *comparison = SPECIAL_EQUAL; } else { return 0; } @@ -179,8 +248,8 @@ static int str_to_comparison_arg_helper(const char *str, 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 @@ int str_to_comparison_arg(const char *str, struct expression *call, int *compari 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 @@ void filter_by_comparison(struct range_list **rl, int comparison, struct range_l *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 @@ static struct range_list *filter_by_comparison_call(char *c, struct expression * 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 @@ static sval_t parse_val(int use_max, struct expression *call, struct symbol *typ /* 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 @@ static char *jump_to_call_math(char *value) 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 @@ static void str_to_rl_helper(struct expression *call, struct symbol *type, char 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 @@ static void str_to_rl_helper(struct expression *call, struct symbol *type, char 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 @@ static void str_to_rl_helper(struct expression *call, struct symbol *type, char *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 @@ cast: 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 @@ int is_whole_rl(struct range_list *rl) 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 @@ struct range_list *alloc_whole_rl(struct symbol *type) 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 @@ void add_range(struct range_list **list, sval_t min, sval_t max) 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 @@ struct range_list *rl_truncate_cast(struct symbol *type, struct range_list *rl) 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 @@ struct range_list *cast_rl(struct symbol *type, struct range_list *rl) 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) - return NULL; - if (!one) + if (!one || !two) 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 @@ struct range_list *rl_intersection(struct range_list *one, struct range_list *tw 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 @@ static struct range_list *handle_divide_rl(struct range_list *left, struct range 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,25 +1833,155 @@ static struct range_list *handle_XOR_rl(struct range_list *left, struct range_li 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) @@ -1712,10 +2030,10 @@ struct range_list *rl_binop(struct range_list *left, int op, struct range_list * 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; diff --git a/usr/src/tools/smatch/src/smatch_real_absolute.c b/usr/src/tools/smatch/src/smatch_real_absolute.c index 96cf6b4aa7..dd6c5581dd 100644 --- a/usr/src/tools/smatch/src/smatch_real_absolute.c +++ b/usr/src/tools/smatch/src/smatch_real_absolute.c @@ -103,6 +103,9 @@ static void match_assign(struct expression *expr) 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 @@ void register_real_absolute(int id) { 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); diff --git a/usr/src/tools/smatch/src/smatch_return_to_param.c b/usr/src/tools/smatch/src/smatch_return_to_param.c index 220d24f1f9..ec8e3a9cc5 100644 --- a/usr/src/tools/smatch/src/smatch_return_to_param.c +++ b/usr/src/tools/smatch/src/smatch_return_to_param.c @@ -56,7 +56,7 @@ char *map_call_to_other_name_sym(const char *name, struct symbol *sym, struct sy 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 @@ static char *map_my_state_long_to_short(struct sm_state *sm, const char *name, s 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 @@ static char *map_assignment_long_to_short(struct sm_state *sm, const char *name, * 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,15 +105,13 @@ static char *map_long_to_short_name_sym_helper(const char *name, struct symbol * 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) - return ret; - continue; - } - if (sm->owner == check_assigned_expr_id) { - ret = map_assignment_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; } } END_FOR_EACH_SM(sm); @@ -161,16 +119,6 @@ static char *map_long_to_short_name_sym_helper(const char *name, struct symbol * 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 @@ free: void register_return_to_param(int id) { my_id = id; + set_dynamic_states(my_id); add_modification_hook(my_id, &undef); } diff --git a/usr/src/tools/smatch/src/smatch_returns.c b/usr/src/tools/smatch/src/smatch_returns.c index 5c7157cf1f..e04406f225 100644 --- a/usr/src/tools/smatch/src/smatch_returns.c +++ b/usr/src/tools/smatch/src/smatch_returns.c @@ -130,6 +130,7 @@ void register_returns_early(int id) { RETURN_ID = id; + set_dynamic_states(RETURN_ID); add_split_return_callback(match_return); } diff --git a/usr/src/tools/smatch/src/smatch_scripts/build_kernel_data.sh b/usr/src/tools/smatch/src/smatch_scripts/build_kernel_data.sh index cacf065e0a..9f470738e6 100755 --- a/usr/src/tools/smatch/src/smatch_scripts/build_kernel_data.sh +++ b/usr/src/tools/smatch/src/smatch_scripts/build_kernel_data.sh @@ -35,7 +35,8 @@ if [ ! -e smatch_db.sqlite ] ; then 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 @@ mv ${PROJECT}.* $DATA_DIR $DATA_DIR/db/create_db.sh -p=kernel smatch_warns.txt +exit $BUILD_STATUS diff --git a/usr/src/tools/smatch/src/smatch_scripts/kpatch.sh b/usr/src/tools/smatch/src/smatch_scripts/kpatch.sh index 1dcbd93061..42b2f31bb9 100755 --- a/usr/src/tools/smatch/src/smatch_scripts/kpatch.sh +++ b/usr/src/tools/smatch/src/smatch_scripts/kpatch.sh @@ -4,7 +4,7 @@ TMP_DIR=/tmp 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 @@ while true ; do if [[ "$1" == "--no-compile" ]] ; then NO_COMPILE=true shift - elif [[ "$1" == "--ammend" ]] ; then + elif [[ "$1" == "--amend" ]] ; then AMEND="--amend" shift else @@ -53,7 +53,11 @@ fullname=$1 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?" @@ -71,15 +75,29 @@ if [ "$NO_COMPILE" != "true" ] ; then # 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 | \ diff --git a/usr/src/tools/smatch/src/smatch_scripts/test_kernel.sh b/usr/src/tools/smatch/src/smatch_scripts/test_kernel.sh index 2beaf88e0c..b31c61047c 100755 --- a/usr/src/tools/smatch/src/smatch_scripts/test_kernel.sh +++ b/usr/src/tools/smatch/src/smatch_scripts/test_kernel.sh @@ -41,6 +41,10 @@ while true ; do 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 @@ fi 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 diff --git a/usr/src/tools/smatch/src/smatch_slist.c b/usr/src/tools/smatch/src/smatch_slist.c index ab0ca19d64..ef00465593 100644 --- a/usr/src/tools/smatch/src/smatch_slist.c +++ b/usr/src/tools/smatch/src/smatch_slist.c @@ -96,9 +96,9 @@ int cmp_tracker(const struct sm_state *a, const struct sm_state *b) 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 @@ int cmp_tracker(const struct sm_state *a, const struct sm_state *b) 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 @@ struct sm_state *alloc_sm_state(int owner, const char *name, 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 @@ void add_possible_sm(struct sm_state *to, struct sm_state *new) { 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 @@ void add_possible_sm(struct sm_state *to, struct sm_state *new) 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 @@ char *alloc_sname(const char *str) 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 @@ int out_of_memory(void) */ 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 @@ void free_every_single_sm_state(void) 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 @@ struct sm_state *clone_sm(struct sm_state *s) ret->possible = clone_slist(s->possible); ret->left = s->left; ret->right = s->right; - ret->nr_children = s->nr_children; return ret; } @@ -394,9 +467,8 @@ struct sm_state *merge_sm_states(struct sm_state *one, struct sm_state *two) 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 @@ -718,7 +790,7 @@ static void __merge_stree(struct stree **to, struct stree *stree, int add_pool) 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 @@ static void __merge_stree(struct stree **to, struct stree *stree, int add_pool) 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); diff --git a/usr/src/tools/smatch/src/smatch_slist.h b/usr/src/tools/smatch/src/smatch_slist.h index 358756b25d..f08ed46332 100644 --- a/usr/src/tools/smatch/src/smatch_slist.h +++ b/usr/src/tools/smatch/src/smatch_slist.h @@ -96,3 +96,4 @@ void overwrite_stree(struct stree *from, struct stree **to); void all_return_states_hook(void (*callback)(void)); +void allocate_dynamic_states_array(int num_checks); diff --git a/usr/src/tools/smatch/src/smatch_statement_count.c b/usr/src/tools/smatch/src/smatch_statement_count.c index 1bc6383479..282deacb1c 100644 --- a/usr/src/tools/smatch/src/smatch_statement_count.c +++ b/usr/src/tools/smatch/src/smatch_statement_count.c @@ -79,6 +79,7 @@ void register_statement_count(int id) { my_id = id; + set_dynamic_states(my_id); add_hook(match_statement, STMT_HOOK); add_merge_hook(my_id, &merge_states); diff --git a/usr/src/tools/smatch/src/smatch_states.c b/usr/src/tools/smatch/src/smatch_states.c index c7a9c85d2b..d3656dff5a 100644 --- a/usr/src/tools/smatch/src/smatch_states.c +++ b/usr/src/tools/smatch/src/smatch_states.c @@ -84,7 +84,7 @@ struct sm_state *set_state(int owner, const char *name, struct symbol *sym, stru { struct sm_state *ret; - if (!name) + if (!name || !state) return NULL; if (read_only) @@ -93,7 +93,7 @@ struct sm_state *set_state(int owner, const char *name, struct symbol *sym, stru 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 @@ void __set_sm(struct sm_state *sm) 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 @@ void __set_sm_cur_stree(struct sm_state *sm) 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 @@ void __set_sm_fake_stree(struct sm_state *sm) 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 @@ void set_true_false_states(int owner, const char *name, struct symbol *sym, 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 @@ void __set_true_false_sm(struct sm_state *true_sm, struct sm_state *false_sm) 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 @@ void __negate_cond_stacks(void) { 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); diff --git a/usr/src/tools/smatch/src/smatch_stored_conditions.c b/usr/src/tools/smatch/src/smatch_stored_conditions.c index 07f436d416..925b86d4b1 100644 --- a/usr/src/tools/smatch/src/smatch_stored_conditions.c +++ b/usr/src/tools/smatch/src/smatch_stored_conditions.c @@ -238,11 +238,14 @@ struct expression_list *get_conditions(struct expression *expr) 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); } diff --git a/usr/src/tools/smatch/src/smatch_string_list.c b/usr/src/tools/smatch/src/smatch_string_list.c index 832ef7b938..cf8e21ca00 100644 --- a/usr/src/tools/smatch/src/smatch_string_list.c +++ b/usr/src/tools/smatch/src/smatch_string_list.c @@ -20,37 +20,42 @@ 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); 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) diff --git a/usr/src/tools/smatch/src/smatch_strlen.c b/usr/src/tools/smatch/src/smatch_strlen.c index 145d32ad39..b69faf4bad 100644 --- a/usr/src/tools/smatch/src/smatch_strlen.c +++ b/usr/src/tools/smatch/src/smatch_strlen.c @@ -333,6 +333,8 @@ void register_strlen(int id) { 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(int id) 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); } diff --git a/usr/src/tools/smatch/src/smatch_struct_assignment.c b/usr/src/tools/smatch/src/smatch_struct_assignment.c index 414f5af770..7fcd631e3e 100644 --- a/usr/src/tools/smatch/src/smatch_struct_assignment.c +++ b/usr/src/tools/smatch/src/smatch_struct_assignment.c @@ -445,6 +445,23 @@ static void match_memcpy(const char *fn, struct expression *expr, void *_arg) __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 @@ void register_struct_assignment(int id) 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); diff --git a/usr/src/tools/smatch/src/smatch_sval.c b/usr/src/tools/smatch/src/smatch_sval.c index a3a5cc7a02..6fe29cdab7 100644 --- a/usr/src/tools/smatch/src/smatch_sval.c +++ b/usr/src/tools/smatch/src/smatch_sval.c @@ -67,7 +67,7 @@ sval_t sval_type_val(struct symbol *type, long long val) sval_t ret; if (!type) - type = &int_ctype; + type = &llong_ctype; ret.type = type; ret.value = val; @@ -94,6 +94,8 @@ int sval_is_ptr(sval_t sval) int sval_unsigned(sval_t sval) { + if (is_ptr_type(sval.type)) + return true; return type_unsigned(sval.type); } @@ -231,7 +233,7 @@ int sval_too_low(struct symbol *type, sval_t sval) { 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 @@ static sval_t ptr_binop(struct symbol *type, sval_t left, int op, sval_t right) } } + if (op == '-') + ret.type = ssize_t_ctype; return ret; } @@ -583,15 +587,34 @@ int sval_binop_overflows_no_sign(sval_t left, int op, sval_t right) 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 @@ const char *sval_to_str(sval_t sval) { 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 @@ const char *sval_to_str(sval_t sval) 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]; diff --git a/usr/src/tools/smatch/src/smatch_type.c b/usr/src/tools/smatch/src/smatch_type.c index 23763b9f13..fca1fb8bac 100644 --- a/usr/src/tools/smatch/src/smatch_type.c +++ b/usr/src/tools/smatch/src/smatch_type.c @@ -29,6 +29,8 @@ struct symbol *get_real_base_type(struct symbol *sym) if (!sym) return NULL; + if (sym->type == SYM_BASETYPE) + return sym; ret = get_base_type(sym); if (!ret) return NULL; @@ -75,6 +77,10 @@ static struct symbol *get_binop_type(struct expression *expr) 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 @@ static struct symbol *get_type_helper(struct expression *expr) case EXPR_LOGICAL: ret = &int_ctype; break; + case EXPR_OFFSETOF: + ret = &ulong_ctype; + break; default: return NULL; } @@ -283,16 +292,10 @@ static struct symbol *get_type_helper(struct expression *expr) static struct symbol *get_final_type_helper(struct expression *expr) { /* - * I'm not totally positive I understand types... - * - * 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. + * 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. * */ @@ -300,21 +303,8 @@ static struct symbol *get_final_type_helper(struct expression *expr) 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 returns_unsigned(struct symbol *sym) 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 @@ sval_t sval_type_min(struct symbol *base_type) 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 @@ static struct symbol *get_member_from_string(struct symbol_list *symbol_list, co 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 @@ static struct symbol *get_member_from_string(struct symbol_list *symbol_list, co 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 @@ static int type_str_helper(char *buf, int size, struct symbol *type) 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 @@ static int type_str_helper(char *buf, int size, struct symbol *type) 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); } diff --git a/usr/src/tools/smatch/src/smatch_type_val.c b/usr/src/tools/smatch/src/smatch_type_val.c index 6baa2f50dd..89be79e679 100644 --- a/usr/src/tools/smatch/src/smatch_type_val.c +++ b/usr/src/tools/smatch/src/smatch_type_val.c @@ -396,9 +396,6 @@ static void match_assign_value(struct expression *expr) return; type = get_type(expr->left); - if (type && type->type == SYM_STRUCT) - return; - member = get_member_name(expr->left); if (!member) return; diff --git a/usr/src/tools/smatch/src/smatch_untracked_param.c b/usr/src/tools/smatch/src/smatch_untracked_param.c index 7f07d9894d..d6ff0e7010 100644 --- a/usr/src/tools/smatch/src/smatch_untracked_param.c +++ b/usr/src/tools/smatch/src/smatch_untracked_param.c @@ -39,10 +39,12 @@ static int my_id; 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,12 +64,45 @@ static void call_untracked_callbacks(struct expression *expr, int param) } 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,13 +113,29 @@ void mark_untracked(struct expression *expr, int param, const char *key, const c 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) @@ -133,7 +184,8 @@ static void match_after_call(struct expression *expr) } 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 @@ void mark_all_params_untracked(int return_id, char *return_ranges, struct expres 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 @@ static void print_untracked_params(int return_id, char *return_ranges, struct ex 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 @@ void register_untracked_param(int id) 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); diff --git a/usr/src/tools/smatch/src/validation/sm_bitwise1.c b/usr/src/tools/smatch/src/validation/sm_bitwise1.c index e36da4b075..dbd2f873bf 100644 --- a/usr/src/tools/smatch/src/validation/sm_bitwise1.c +++ b/usr/src/tools/smatch/src/validation/sm_bitwise1.c @@ -17,6 +17,6 @@ void test(void) 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 */ diff --git a/usr/src/tools/smatch/src/validation/sm_equiv1.c b/usr/src/tools/smatch/src/validation/sm_equiv1.c index 8bd7e57500..b8db9507eb 100644 --- a/usr/src/tools/smatch/src/validation/sm_equiv1.c +++ b/usr/src/tools/smatch/src/validation/sm_equiv1.c @@ -30,11 +30,11 @@ int func(void) * 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 */ diff --git a/usr/src/tools/smatch/src/validation/sm_implied.c b/usr/src/tools/smatch/src/validation/sm_implied.c index 9e318effa5..957ed4223f 100644 --- a/usr/src/tools/smatch/src/validation/sm_implied.c +++ b/usr/src/tools/smatch/src/validation/sm_implied.c @@ -26,5 +26,6 @@ x: * * 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 */ diff --git a/usr/src/tools/smatch/src/validation/sm_implied10.c b/usr/src/tools/smatch/src/validation/sm_implied10.c index b52f01195f..d699d9883b 100644 --- a/usr/src/tools/smatch/src/validation/sm_implied10.c +++ b/usr/src/tools/smatch/src/validation/sm_implied10.c @@ -11,7 +11,7 @@ void func(int *y) 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 @@ void func(int *y) * 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 */ diff --git a/usr/src/tools/smatch/src/validation/sm_implied11.c b/usr/src/tools/smatch/src/validation/sm_implied11.c index 5a8b02b5c3..7536fed355 100644 --- a/usr/src/tools/smatch/src/validation/sm_implied11.c +++ b/usr/src/tools/smatch/src/validation/sm_implied11.c @@ -29,6 +29,6 @@ static void ad_agg_selection_logic(void) * 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 */ diff --git a/usr/src/tools/smatch/src/validation/sm_implied12.c b/usr/src/tools/smatch/src/validation/sm_implied12.c index ad9e49487d..a35f1b9c6b 100644 --- a/usr/src/tools/smatch/src/validation/sm_implied12.c +++ b/usr/src/tools/smatch/src/validation/sm_implied12.c @@ -33,6 +33,6 @@ static void ad_agg_selection_logic(void) * 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 */ diff --git a/usr/src/tools/smatch/src/validation/sm_implied2.c b/usr/src/tools/smatch/src/validation/sm_implied2.c index c25e3567c2..b40aa811fc 100644 --- a/usr/src/tools/smatch/src/validation/sm_implied2.c +++ b/usr/src/tools/smatch/src/validation/sm_implied2.c @@ -37,5 +37,6 @@ void func (void) * * 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 */ diff --git a/usr/src/tools/smatch/src/validation/sm_implied5.c b/usr/src/tools/smatch/src/validation/sm_implied5.c index 09b4a13ac9..0fe213537c 100644 --- a/usr/src/tools/smatch/src/validation/sm_implied5.c +++ b/usr/src/tools/smatch/src/validation/sm_implied5.c @@ -23,5 +23,6 @@ void func (void) * * 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 */ diff --git a/usr/src/tools/smatch/src/validation/sm_memory.c b/usr/src/tools/smatch/src/validation/sm_memory.c index 145b1c0b49..402c23fcbc 100644 --- a/usr/src/tools/smatch/src/validation/sm_memory.c +++ b/usr/src/tools/smatch/src/validation/sm_memory.c @@ -30,6 +30,5 @@ void func (void) * * 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 */ diff --git a/usr/src/tools/smatch/src/validation/sm_null_deref.c b/usr/src/tools/smatch/src/validation/sm_null_deref.c index df399e6cc5..5734d21a5b 100644 --- a/usr/src/tools/smatch/src/validation/sm_null_deref.c +++ b/usr/src/tools/smatch/src/validation/sm_null_deref.c @@ -44,6 +44,7 @@ static void func (void) * * 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) diff --git a/usr/src/tools/smatch/src/validation/sm_select5.c b/usr/src/tools/smatch/src/validation/sm_select5.c index cce93b30c2..05e3dec8db 100644 --- a/usr/src/tools/smatch/src/validation/sm_select5.c +++ b/usr/src/tools/smatch/src/validation/sm_select5.c @@ -25,7 +25,7 @@ void test(void) * * 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 */ diff --git a/usr/src/uts/i86pc/unix/Makefile b/usr/src/uts/i86pc/unix/Makefile index 9eedb1bf67..fbd7976548 100644 --- a/usr/src/uts/i86pc/unix/Makefile +++ b/usr/src/uts/i86pc/unix/Makefile @@ -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)/fmsmb.o := SMOFF += indenting $(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. # diff --git a/usr/src/uts/intel/emlxs/Makefile b/usr/src/uts/intel/emlxs/Makefile index 4910ca9fdd..e29f1762ca 100644 --- a/usr/src/uts/intel/emlxs/Makefile +++ b/usr/src/uts/intel/emlxs/Makefile @@ -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 @@ COMMON_BASE = ../../../common # 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 @@ include ../Makefile.$(ARCHDIR) # 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_FLAGS += -DMACH=\"$(MACH)\" EMLXS_CFLAGS = $(EMLXS_FLAGS) EMLXS_LFLAGS = $(EMLXS_FLAGS) CFLAGS += $(EMLXS_CFLAGS) -DEMLXS_ARCH=\"$(CLASS)\" -LINTTAGS += $(EMLXS_LFLAGS) -DEMLXS_ARCH=\"$(CLASS)\" # @@ -82,21 +79,12 @@ INC_PATH += -I$(UTSBASE)/common/sys/fibre-channel/ulp 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 @@ clean: $(CLEAN_DEPS) clobber: $(CLOBBER_DEPS) -lint: $(LINT_DEPS) - -modlintlib: $(MODLINTLIB_DEPS) - -clean.lint: $(CLEAN_LINT_DEPS) - install: $(INSTALL_DEPS) # diff --git a/usr/src/uts/intel/genunix/Makefile b/usr/src/uts/intel/genunix/Makefile index 04a0279b01..58a50d801a 100644 --- a/usr/src/uts/intel/genunix/Makefile +++ b/usr/src/uts/intel/genunix/Makefile @@ -23,13 +23,7 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# Copyright (c) 2018, Joyent, Inc. - -# -# This makefile drives the production of the generic -# unix kernel module. -# -# x86 implementation architecture dependent +# Copyright 2019 Joyent, Inc. # # @@ -46,9 +40,6 @@ GENUNIX = $(OBJS_DIR)/$(MODULE) 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 @@ include $(UTSBASE)/intel/Makefile.intel # Define targets # ALL_TARGET = $(LIBGEN) $(GENUNIX) -LINT_TARGET = $(MODULE).lint INSTALL_TARGET = $(LIBGEN) $(GENUNIX) $(ROOTMODULE) # @@ -88,18 +78,6 @@ CPPFLAGS += -I$(SRC)/uts/common/fs/zfs 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-uninitialized 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)/timers.o := SMOFF += signed_integer_overflow_check $(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 @@ clean: $(CLEAN_DEPS) 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)/intel/Makefile.targ include $(UTSBASE)/i86pc/Makefile.workarounds ALL_DEFS += $(WORKAROUND_DEFS) - -# -# Override. -# -$(MODULE).lint := GEN_LINT_LIB = diff --git a/usr/src/uts/intel/mega_sas/Makefile b/usr/src/uts/intel/mega_sas/Makefile index 3011ffa0f9..916b2350ab 100644 --- a/usr/src/uts/intel/mega_sas/Makefile +++ b/usr/src/uts/intel/mega_sas/Makefile @@ -21,14 +21,7 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# Copyright (c) 2018, Joyent, Inc. - -# -# uts/intel/mega_sas/Makefile -# -# This makefile drives the production of the mega_sas driver kernel module. -# -# intel implementation architecture dependent +# Copyright 2019 Joyent, Inc. # # @@ -41,7 +34,6 @@ UTSBASE = ../.. # 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,18 +46,17 @@ include $(UTSBASE)/intel/Makefile.intel # 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 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 @@ clean: $(CLEAN_DEPS) clobber: $(CLOBBER_DEPS) -lint: $(LINT_DEPS) - -modlintlib: $(MODLINTLIB_DEPS) - -clean.lint: $(CLEAN_LINT_DEPS) - install: $(INSTALL_DEPS) # diff --git a/usr/src/uts/intel/simnet/Makefile b/usr/src/uts/intel/simnet/Makefile index c3f41026a3..c056a38128 100644 --- a/usr/src/uts/intel/simnet/Makefile +++ b/usr/src/uts/intel/simnet/Makefile @@ -22,6 +22,9 @@ # 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). # @@ -32,7 +35,6 @@ UTSBASE = ../.. # 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 @@ include $(UTSBASE)/intel/Makefile.intel # Define targets # ALL_TARGET = $(BINARY) $(SRC_CONFILE) -LINT_TARGET = $(MODULE).lint INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) # @@ -56,6 +57,9 @@ LDFLAGS += -dy -Ndrv/dld -Nmisc/mac -Nmisc/dls -Ndrv/random CERRWARN += -_gcc=-Wno-switch +# needs work +$(OBJS_DIR)/simnet.o := SMOFF += index_overflow + # # Default build targets. # @@ -69,12 +73,6 @@ clean: $(CLEAN_DEPS) clobber: $(CLOBBER_DEPS) -lint: $(LINT_DEPS) - -modlintlib: $(MODLINTLIB_DEPS) - -clean.lint: $(CLEAN_LINT_DEPS) - install: $(INSTALL_DEPS) # diff --git a/usr/src/uts/intel/spppcomp/Makefile b/usr/src/uts/intel/spppcomp/Makefile index 1d3d2b3918..30f6bb387f 100644 --- a/usr/src/uts/intel/spppcomp/Makefile +++ b/usr/src/uts/intel/spppcomp/Makefile @@ -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 @@ UTSBASE = ../.. # MODULE = spppcomp OBJECTS = $(SPPPCOMP_OBJS:%=$(OBJS_DIR)/%) -LINTS = $(SPPPCOMP_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(USR_STRMOD_DIR)/$(MODULE) # @@ -49,7 +49,6 @@ include $(UTSBASE)/intel/Makefile.intel # Define targets # ALL_TARGET = $(BINARY) -LINT_TARGET = $(MODULE).lint INSTALL_TARGET = $(BINARY) $(ROOTMODULE) # @@ -57,25 +56,16 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) # 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. +# Additional compiler definitions # -LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN -LINTTAGS += -erroff=E_PTRDIFF_OVERFLOW -LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV +INC_PATH += -I$(UTSBASE)/common/io/ppp/common CERRWARN += -_gcc=-Wno-parentheses CERRWARN += -_gcc=-Wno-uninitialized # needs work -SMOFF += indenting +SMOFF += indenting,index_overflow # # Depends on sppp @@ -95,17 +85,8 @@ clean: $(CLEAN_DEPS) 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) diff --git a/usr/src/uts/intel/xge/Makefile b/usr/src/uts/intel/xge/Makefile index 1c9b0e2e2b..c0407c1984 100644 --- a/usr/src/uts/intel/xge/Makefile +++ b/usr/src/uts/intel/xge/Makefile @@ -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 @@ UTSBASE = ../.. # 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 @@ include $(UTSBASE)/intel/Makefile.intel # Define targets # ALL_TARGET = $(BINARY) -LINT_TARGET = $(MODULE).lint INSTALL_TARGET = $(BINARY) $(ROOTMODULE) # @@ -74,7 +73,7 @@ HAL_CFLAGS = -DXGE_HAL_USE_MGMT_AUX #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 @@ CFLAGS64 += $(XGE_CFLAGS) -xO4 -xcrossfile # 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-empty-body 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 @@ clean: $(CLEAN_DEPS) clobber: $(CLOBBER_DEPS) -lint: $(LINT_DEPS) - -modlintlib: $(MODLINTLIB_DEPS) - -clean.lint: $(CLEAN_LINT_DEPS) - install: $(INSTALL_DEPS) # |