1
0
mirror of https://github.com/ioacademy-jikim/debugging synced 2026-08-30 09:19:28 +00:00

first commit

This commit is contained in:
jikim
2015-12-13 22:34:58 +09:00
commit 0b589c7986
9455 changed files with 4350134 additions and 0 deletions
@@ -0,0 +1,130 @@
exp_sgcheck_x86_linux-h_main.o: h_main.c /usr/include/stdc-predef.h \
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h \
../include/pub_tool_libcbase.h ../include/pub_tool_basics.h \
../include/pub_tool_libcprint.h ../include/pub_tool_libcassert.h \
../include/pub_tool_mallocfree.h ../include/pub_tool_execontext.h \
../include/pub_tool_hashtable.h ../include/pub_tool_tooliface.h \
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
../VEX/pub/libvex_ir.h ../include/pub_tool_replacemalloc.h \
../include/pub_tool_options.h ../include/pub_tool_aspacemgr.h \
../include/pub_tool_vki.h ../include/vki/vki-linux.h \
../include/vki/vki-posixtypes-x86-linux.h ../include/vki/vki-x86-linux.h \
../include/vki/vki-linux-drm.h ../include/vki/vki-xen.h \
../include/vki/vki-xen-x86.h ../include/vki/vki-xen-domctl.h \
../include/vki/vki-xen-sysctl.h ../include/vki/vki-xen-mmuext.h \
../include/vki/vki-xen-schedop.h ../include/vki/vki-xen-memory.h \
../include/vki/vki-xen-evtchn.h ../include/vki/vki-xen-gnttab.h \
../include/vki/vki-xen-version.h ../include/vki/vki-xen-hvm.h \
../include/vki/vki-xen-tmem.h ../include/vki/vki-xen-xsm.h \
../include/vki/vki-xen-physdev.h ../include/pub_tool_machine.h \
../include/pub_tool_debuginfo.h ../include/pub_tool_xarray.h \
../include/pub_tool_threadstate.h ../include/pub_tool_oset.h \
../include/pub_tool_vkiscnums.h ../include/pub_tool_vkiscnums_asm.h \
../include/vki/vki-scnums-x86-linux.h ../include/pub_tool_wordfm.h \
../include/pub_tool_xarray.h pc_common.h h_main.h sg_main.h
/usr/include/stdc-predef.h:
../include/pub_tool_basics.h:
../VEX/pub/libvex_basictypes.h:
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
../include/pub_tool_libcbase.h:
../include/pub_tool_basics.h:
../include/pub_tool_libcprint.h:
../include/pub_tool_libcassert.h:
../include/pub_tool_mallocfree.h:
../include/pub_tool_execontext.h:
../include/pub_tool_hashtable.h:
../include/pub_tool_tooliface.h:
../include/pub_tool_errormgr.h:
../include/pub_tool_execontext.h:
../VEX/pub/libvex.h:
../VEX/pub/libvex_basictypes.h:
../VEX/pub/libvex_ir.h:
../include/pub_tool_replacemalloc.h:
../include/pub_tool_options.h:
../include/pub_tool_aspacemgr.h:
../include/pub_tool_vki.h:
../include/vki/vki-linux.h:
../include/vki/vki-posixtypes-x86-linux.h:
../include/vki/vki-x86-linux.h:
../include/vki/vki-linux-drm.h:
../include/vki/vki-xen.h:
../include/vki/vki-xen-x86.h:
../include/vki/vki-xen-domctl.h:
../include/vki/vki-xen-sysctl.h:
../include/vki/vki-xen-mmuext.h:
../include/vki/vki-xen-schedop.h:
../include/vki/vki-xen-memory.h:
../include/vki/vki-xen-evtchn.h:
../include/vki/vki-xen-gnttab.h:
../include/vki/vki-xen-version.h:
../include/vki/vki-xen-hvm.h:
../include/vki/vki-xen-tmem.h:
../include/vki/vki-xen-xsm.h:
../include/vki/vki-xen-physdev.h:
../include/pub_tool_machine.h:
../include/pub_tool_debuginfo.h:
../include/pub_tool_xarray.h:
../include/pub_tool_threadstate.h:
../include/pub_tool_oset.h:
../include/pub_tool_vkiscnums.h:
../include/pub_tool_vkiscnums_asm.h:
../include/vki/vki-scnums-x86-linux.h:
../include/pub_tool_wordfm.h:
../include/pub_tool_xarray.h:
pc_common.h:
h_main.h:
sg_main.h:
@@ -0,0 +1,61 @@
exp_sgcheck_x86_linux-pc_common.o: pc_common.c /usr/include/stdc-predef.h \
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h \
../include/pub_tool_libcbase.h ../include/pub_tool_basics.h \
../include/pub_tool_libcprint.h ../include/pub_tool_xarray.h \
../include/pub_tool_mallocfree.h ../include/pub_tool_libcassert.h \
../include/pub_tool_options.h ../VEX/pub/libvex.h \
../VEX/pub/libvex_basictypes.h ../VEX/pub/libvex_ir.h \
../include/pub_tool_replacemalloc.h ../include/pub_tool_execontext.h \
../include/pub_tool_tooliface.h ../include/pub_tool_errormgr.h \
../include/pub_tool_execontext.h ../include/pub_tool_threadstate.h \
../include/pub_tool_debuginfo.h ../include/pub_tool_xarray.h pc_common.h \
h_main.h
/usr/include/stdc-predef.h:
../include/pub_tool_basics.h:
../VEX/pub/libvex_basictypes.h:
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
../include/pub_tool_libcbase.h:
../include/pub_tool_basics.h:
../include/pub_tool_libcprint.h:
../include/pub_tool_xarray.h:
../include/pub_tool_mallocfree.h:
../include/pub_tool_libcassert.h:
../include/pub_tool_options.h:
../VEX/pub/libvex.h:
../VEX/pub/libvex_basictypes.h:
../VEX/pub/libvex_ir.h:
../include/pub_tool_replacemalloc.h:
../include/pub_tool_execontext.h:
../include/pub_tool_tooliface.h:
../include/pub_tool_errormgr.h:
../include/pub_tool_execontext.h:
../include/pub_tool_threadstate.h:
../include/pub_tool_debuginfo.h:
../include/pub_tool_xarray.h:
pc_common.h:
h_main.h:
@@ -0,0 +1,45 @@
exp_sgcheck_x86_linux-pc_main.o: pc_main.c /usr/include/stdc-predef.h \
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h \
../include/pub_tool_libcassert.h ../include/pub_tool_basics.h \
../include/pub_tool_libcprint.h ../include/pub_tool_execontext.h \
../include/pub_tool_tooliface.h ../include/pub_tool_errormgr.h \
../include/pub_tool_execontext.h ../VEX/pub/libvex.h \
../VEX/pub/libvex_basictypes.h ../VEX/pub/libvex_ir.h \
../include/pub_tool_options.h sg_main.h pc_common.h h_main.h
/usr/include/stdc-predef.h:
../include/pub_tool_basics.h:
../VEX/pub/libvex_basictypes.h:
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
../include/pub_tool_libcassert.h:
../include/pub_tool_basics.h:
../include/pub_tool_libcprint.h:
../include/pub_tool_execontext.h:
../include/pub_tool_tooliface.h:
../include/pub_tool_errormgr.h:
../include/pub_tool_execontext.h:
../VEX/pub/libvex.h:
../VEX/pub/libvex_basictypes.h:
../VEX/pub/libvex_ir.h:
../include/pub_tool_options.h:
sg_main.h:
pc_common.h:
h_main.h:
@@ -0,0 +1,61 @@
exp_sgcheck_x86_linux-sg_main.o: sg_main.c /usr/include/stdc-predef.h \
../include/pub_tool_basics.h ../VEX/pub/libvex_basictypes.h \
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h \
../include/pub_tool_libcbase.h ../include/pub_tool_basics.h \
../include/pub_tool_libcassert.h ../include/pub_tool_libcprint.h \
../include/pub_tool_tooliface.h ../include/pub_tool_errormgr.h \
../include/pub_tool_execontext.h ../VEX/pub/libvex.h \
../VEX/pub/libvex_basictypes.h ../VEX/pub/libvex_ir.h \
../include/pub_tool_wordfm.h ../include/pub_tool_xarray.h \
../include/pub_tool_threadstate.h ../include/pub_tool_mallocfree.h \
../include/pub_tool_machine.h ../include/pub_tool_debuginfo.h \
../include/pub_tool_xarray.h ../include/pub_tool_options.h pc_common.h \
sg_main.h
/usr/include/stdc-predef.h:
../include/pub_tool_basics.h:
../VEX/pub/libvex_basictypes.h:
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
../include/pub_tool_libcbase.h:
../include/pub_tool_basics.h:
../include/pub_tool_libcassert.h:
../include/pub_tool_libcprint.h:
../include/pub_tool_tooliface.h:
../include/pub_tool_errormgr.h:
../include/pub_tool_execontext.h:
../VEX/pub/libvex.h:
../VEX/pub/libvex_basictypes.h:
../VEX/pub/libvex_ir.h:
../include/pub_tool_wordfm.h:
../include/pub_tool_xarray.h:
../include/pub_tool_threadstate.h:
../include/pub_tool_mallocfree.h:
../include/pub_tool_machine.h:
../include/pub_tool_debuginfo.h:
../include/pub_tool_xarray.h:
../include/pub_tool_options.h:
pc_common.h:
sg_main.h:
@@ -0,0 +1,41 @@
vgpreload_exp_sgcheck_x86_linux_so-h_intercepts.o: h_intercepts.c \
/usr/include/stdc-predef.h ../include/pub_tool_basics.h \
../VEX/pub/libvex_basictypes.h \
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h \
../include/pub_tool_hashtable.h ../include/pub_tool_basics.h \
../include/pub_tool_redir.h ../config.h ../include/pub_tool_tooliface.h \
../include/pub_tool_errormgr.h ../include/pub_tool_execontext.h \
../VEX/pub/libvex.h ../VEX/pub/libvex_basictypes.h \
../VEX/pub/libvex_ir.h ../include/pub_tool_clreq.h ../include/valgrind.h
/usr/include/stdc-predef.h:
../include/pub_tool_basics.h:
../VEX/pub/libvex_basictypes.h:
/usr/lib/gcc/i686-linux-gnu/5/include/stdarg.h:
../include/pub_tool_hashtable.h:
../include/pub_tool_basics.h:
../include/pub_tool_redir.h:
../config.h:
../include/pub_tool_tooliface.h:
../include/pub_tool_errormgr.h:
../include/pub_tool_execontext.h:
../VEX/pub/libvex.h:
../VEX/pub/libvex_basictypes.h:
../VEX/pub/libvex_ir.h:
../include/pub_tool_clreq.h:
../include/valgrind.h:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,109 @@
include $(top_srcdir)/Makefile.tool.am
EXTRA_DIST = docs/sg-manual.xml
#----------------------------------------------------------------------------
# Headers, etc
#----------------------------------------------------------------------------
noinst_HEADERS = \
h_main.h \
pc_common.h \
sg_main.h
#----------------------------------------------------------------------------
# exp-sgcheck-<platform>
#----------------------------------------------------------------------------
noinst_PROGRAMS = exp-sgcheck-@VGCONF_ARCH_PRI@-@VGCONF_OS@
if VGCONF_HAVE_PLATFORM_SEC
noinst_PROGRAMS += exp-sgcheck-@VGCONF_ARCH_SEC@-@VGCONF_OS@
endif
EXP_PTRCHECK_SOURCES_COMMON = \
h_main.c \
pc_common.c \
pc_main.c \
sg_main.c
exp_sgcheck_@VGCONF_ARCH_PRI@_@VGCONF_OS@_SOURCES = \
$(EXP_PTRCHECK_SOURCES_COMMON)
exp_sgcheck_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CPPFLAGS = \
$(AM_CPPFLAGS_@VGCONF_PLATFORM_PRI_CAPS@)
exp_sgcheck_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CFLAGS = \
$(AM_CFLAGS_@VGCONF_PLATFORM_PRI_CAPS@)
exp_sgcheck_@VGCONF_ARCH_PRI@_@VGCONF_OS@_DEPENDENCIES = \
$(TOOL_DEPENDENCIES_@VGCONF_PLATFORM_PRI_CAPS@)
exp_sgcheck_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDADD = \
$(TOOL_LDADD_@VGCONF_PLATFORM_PRI_CAPS@)
exp_sgcheck_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDFLAGS = \
$(TOOL_LDFLAGS_@VGCONF_PLATFORM_PRI_CAPS@)
exp_sgcheck_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LINK = \
$(top_builddir)/coregrind/link_tool_exe_@VGCONF_OS@ \
@VALT_LOAD_ADDRESS_PRI@ \
$(LINK) \
$(exp_sgcheck_@VGCONF_ARCH_PRI@_@VGCONF_OS@_CFLAGS) \
$(exp_sgcheck_@VGCONF_ARCH_PRI@_@VGCONF_OS@_LDFLAGS)
if VGCONF_HAVE_PLATFORM_SEC
exp_sgcheck_@VGCONF_ARCH_SEC@_@VGCONF_OS@_SOURCES = \
$(EXP_PTRCHECK_SOURCES_COMMON)
exp_sgcheck_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CPPFLAGS = \
$(AM_CPPFLAGS_@VGCONF_PLATFORM_SEC_CAPS@)
exp_sgcheck_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CFLAGS = \
$(AM_CFLAGS_@VGCONF_PLATFORM_SEC_CAPS@)
exp_sgcheck_@VGCONF_ARCH_SEC@_@VGCONF_OS@_DEPENDENCIES = \
$(TOOL_DEPENDENCIES_@VGCONF_PLATFORM_SEC_CAPS@)
exp_sgcheck_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDADD = \
$(TOOL_LDADD_@VGCONF_PLATFORM_SEC_CAPS@)
exp_sgcheck_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDFLAGS = \
$(TOOL_LDFLAGS_@VGCONF_PLATFORM_SEC_CAPS@)
exp_sgcheck_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LINK = \
$(top_builddir)/coregrind/link_tool_exe_@VGCONF_OS@ \
@VALT_LOAD_ADDRESS_SEC@ \
$(LINK) \
$(exp_sgcheck_@VGCONF_ARCH_SEC@_@VGCONF_OS@_CFLAGS) \
$(exp_sgcheck_@VGCONF_ARCH_SEC@_@VGCONF_OS@_LDFLAGS)
endif
#----------------------------------------------------------------------------
# vgpreload_exp-sgcheck-<platform>.so
#----------------------------------------------------------------------------
noinst_PROGRAMS += vgpreload_exp-sgcheck-@VGCONF_ARCH_PRI@-@VGCONF_OS@.so
if VGCONF_HAVE_PLATFORM_SEC
noinst_PROGRAMS += vgpreload_exp-sgcheck-@VGCONF_ARCH_SEC@-@VGCONF_OS@.so
endif
if VGCONF_OS_IS_DARWIN
noinst_DSYMS = $(noinst_PROGRAMS)
endif
VGPRELOAD_EXP_PTRCHECK_SOURCES_COMMON = h_intercepts.c
vgpreload_exp_sgcheck_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_SOURCES = \
$(VGPRELOAD_EXP_PTRCHECK_SOURCES_COMMON)
vgpreload_exp_sgcheck_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_CPPFLAGS = \
$(AM_CPPFLAGS_@VGCONF_PLATFORM_PRI_CAPS@)
vgpreload_exp_sgcheck_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_CFLAGS = \
$(AM_CFLAGS_PSO_@VGCONF_PLATFORM_PRI_CAPS@) -O2
vgpreload_exp_sgcheck_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_DEPENDENCIES = \
$(LIBREPLACEMALLOC_@VGCONF_PLATFORM_PRI_CAPS@)
vgpreload_exp_sgcheck_@VGCONF_ARCH_PRI@_@VGCONF_OS@_so_LDFLAGS = \
$(PRELOAD_LDFLAGS_@VGCONF_PLATFORM_PRI_CAPS@) \
$(LIBREPLACEMALLOC_LDFLAGS_@VGCONF_PLATFORM_PRI_CAPS@)
if VGCONF_HAVE_PLATFORM_SEC
vgpreload_exp_sgcheck_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_SOURCES = \
$(VGPRELOAD_EXP_PTRCHECK_SOURCES_COMMON)
vgpreload_exp_sgcheck_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_CPPFLAGS = \
$(AM_CPPFLAGS_@VGCONF_PLATFORM_SEC_CAPS@)
vgpreload_exp_sgcheck_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_CFLAGS = \
$(AM_CFLAGS_PSO_@VGCONF_PLATFORM_SEC_CAPS@) -O2
vgpreload_exp_sgcheck_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_DEPENDENCIES = \
$(LIBREPLACEMALLOC_@VGCONF_PLATFORM_SEC_CAPS@)
vgpreload_exp_sgcheck_@VGCONF_ARCH_SEC@_@VGCONF_OS@_so_LDFLAGS = \
$(PRELOAD_LDFLAGS_@VGCONF_PLATFORM_SEC_CAPS@) \
$(LIBREPLACEMALLOC_LDFLAGS_@VGCONF_PLATFORM_SEC_CAPS@)
endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,316 @@
<?xml version="1.0"?> <!-- -*- sgml -*- -->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[ <!ENTITY % vg-entities SYSTEM "../../docs/xml/vg-entities.xml"> %vg-entities; ]>
<chapter id="sg-manual"
xreflabel="SGCheck: an experimental stack and global array overrun detector">
<title>SGCheck: an experimental stack and global array overrun detector</title>
<para>To use this tool, you must specify
<option>--tool=exp-sgcheck</option> on the Valgrind
command line.</para>
<sect1 id="sg-manual.overview" xreflabel="Overview">
<title>Overview</title>
<para>SGCheck is a tool for finding overruns of stack and global
arrays. It works by using a heuristic approach derived from an
observation about the likely forms of stack and global array accesses.
</para>
</sect1>
<sect1 id="sg-manual.options" xreflabel="SGCheck Command-line Options">
<title>SGCheck Command-line Options</title>
<para id="sg.opts.list">There are no SGCheck-specific command-line options at present.</para>
<!--
<para>SGCheck-specific command-line options are:</para>
<variablelist id="sg.opts.list">
</variablelist>
-->
</sect1>
<sect1 id="sg-manual.how-works.sg-checks"
xreflabel="How SGCheck Works">
<title>How SGCheck Works</title>
<para>When a source file is compiled
with <option>-g</option>, the compiler attaches DWARF3
debugging information which describes the location of all stack and
global arrays in the file.</para>
<para>Checking of accesses to such arrays would then be relatively
simple, if the compiler could also tell us which array (if any) each
memory referencing instruction was supposed to access. Unfortunately
the DWARF3 debugging format does not provide a way to represent such
information, so we have to resort to a heuristic technique to
approximate it. The key observation is that
<emphasis>
if a memory referencing instruction accesses inside a stack or
global array once, then it is highly likely to always access that
same array</emphasis>.</para>
<para>To see how this might be useful, consider the following buggy
fragment:</para>
<programlisting><![CDATA[
{ int i, a[10]; // both are auto vars
for (i = 0; i <= 10; i++)
a[i] = 42;
}
]]></programlisting>
<para>At run time we will know the precise address
of <computeroutput>a[]</computeroutput> on the stack, and so we can
observe that the first store resulting from <computeroutput>a[i] =
42</computeroutput> writes <computeroutput>a[]</computeroutput>, and
we will (correctly) assume that that instruction is intended always to
access <computeroutput>a[]</computeroutput>. Then, on the 11th
iteration, it accesses somewhere else, possibly a different local,
possibly an un-accounted for area of the stack (eg, spill slot), so
SGCheck reports an error.</para>
<para>There is an important caveat.</para>
<para>Imagine a function such as <function>memcpy</function>, which is used
to read and write many different areas of memory over the lifetime of the
program. If we insist that the read and write instructions in its memory
copying loop only ever access one particular stack or global variable, we
will be flooded with errors resulting from calls to
<function>memcpy</function>.</para>
<para>To avoid this problem, SGCheck instantiates fresh likely-target
records for each entry to a function, and discards them on exit. This
allows detection of cases where (e.g.) <function>memcpy</function>
overflows its source or destination buffers for any specific call, but
does not carry any restriction from one call to the next. Indeed,
multiple threads may make multiple simultaneous calls to
(e.g.) <function>memcpy</function> without mutual interference.</para>
</sect1>
<sect1 id="sg-manual.cmp-w-memcheck"
xreflabel="Comparison with Memcheck">
<title>Comparison with Memcheck</title>
<para>SGCheck and Memcheck are complementary: their capabilities do
not overlap. Memcheck performs bounds checks and use-after-free
checks for heap arrays. It also finds uses of uninitialised values
created by heap or stack allocations. But it does not perform bounds
checking for stack or global arrays.</para>
<para>SGCheck, on the other hand, does do bounds checking for stack or
global arrays, but it doesn't do anything else.</para>
</sect1>
<sect1 id="sg-manual.limitations"
xreflabel="Limitations">
<title>Limitations</title>
<para>This is an experimental tool, which relies rather too heavily on some
not-as-robust-as-I-would-like assumptions on the behaviour of correct
programs. There are a number of limitations which you should be aware
of.</para>
<itemizedlist>
<listitem>
<para>False negatives (missed errors): it follows from the
description above (<xref linkend="sg-manual.how-works.sg-checks"/>)
that the first access by a memory referencing instruction to a
stack or global array creates an association between that
instruction and the array, which is checked on subsequent accesses
by that instruction, until the containing function exits. Hence,
the first access by an instruction to an array (in any given
function instantiation) is not checked for overrun, since SGCheck
uses that as the "example" of how subsequent accesses should
behave.</para>
</listitem>
<listitem>
<para>False positives (false errors): similarly, and more serious,
it is clearly possible to write legitimate pieces of code which
break the basic assumption upon which the checking algorithm
depends. For example:</para>
<programlisting><![CDATA[
{ int a[10], b[10], *p, i;
for (i = 0; i < 10; i++) {
p = /* arbitrary condition */ ? &a[i] : &b[i];
*p = 42;
}
}
]]></programlisting>
<para>In this case the store sometimes
accesses <computeroutput>a[]</computeroutput> and
sometimes <computeroutput>b[]</computeroutput>, but in no cases is
the addressed array overrun. Nevertheless the change in target
will cause an error to be reported.</para>
<para>It is hard to see how to get around this problem. The only
mitigating factor is that such constructions appear very rare, at
least judging from the results using the tool so far. Such a
construction appears only once in the Valgrind sources (running
Valgrind on Valgrind) and perhaps two or three times for a start
and exit of Firefox. The best that can be done is to suppress the
errors.</para>
</listitem>
<listitem>
<para>Performance: SGCheck has to read all of
the DWARF3 type and variable information on the executable and its
shared objects. This is computationally expensive and makes
startup quite slow. You can expect debuginfo reading time to be in
the region of a minute for an OpenOffice sized application, on a
2.4 GHz Core 2 machine. Reading this information also requires a
lot of memory. To make it viable, SGCheck goes to considerable
trouble to compress the in-memory representation of the DWARF3
data, which is why the process of reading it appears slow.</para>
</listitem>
<listitem>
<para>Performance: SGCheck runs slower than Memcheck. This is
partly due to a lack of tuning, but partly due to algorithmic
difficulties. The
stack and global checks can sometimes require a number of range
checks per memory access, and these are difficult to short-circuit,
despite considerable efforts having been made. A
redesign and reimplementation could potentially make it much faster.
</para>
</listitem>
<listitem>
<para>Coverage: Stack and global checking is fragile. If a shared
object does not have debug information attached, then SGCheck will
not be able to determine the bounds of any stack or global arrays
defined within that shared object, and so will not be able to check
accesses to them. This is true even when those arrays are accessed
from some other shared object which was compiled with debug
info.</para>
<para>At the moment SGCheck accepts objects lacking debuginfo
without comment. This is dangerous as it causes SGCheck to
silently skip stack and global checking for such objects. It would
be better to print a warning in such circumstances.</para>
</listitem>
<listitem>
<para>Coverage: SGCheck does not check whether the areas read
or written by system calls do overrun stack or global arrays. This
would be easy to add.</para>
</listitem>
<listitem>
<para>Platforms: the stack/global checks won't work properly on
PowerPC, ARM or S390X platforms, only on X86 and AMD64 targets.
That's because the stack and global checking requires tracking
function calls and exits reliably, and there's no obvious way to do
it on ABIs that use a link register for function returns.
</para>
</listitem>
<listitem>
<para>Robustness: related to the previous point. Function
call/exit tracking for X86 and AMD64 is believed to work properly
even in the presence of longjmps within the same stack (although
this has not been tested). However, code which switches stacks is
likely to cause breakage/chaos.</para>
</listitem>
</itemizedlist>
</sect1>
<sect1 id="sg-manual.todo-user-visible"
xreflabel="Still To Do: User-visible Functionality">
<title>Still To Do: User-visible Functionality</title>
<itemizedlist>
<listitem>
<para>Extend system call checking to work on stack and global arrays.</para>
</listitem>
<listitem>
<para>Print a warning if a shared object does not have debug info
attached, or if, for whatever reason, debug info could not be
found, or read.</para>
</listitem>
<listitem>
<para>Add some heuristic filtering that removes obvious false
positives. This would be easy to do. For example, an access
transition from a heap to a stack object almost certainly isn't a
bug and so should not be reported to the user.</para>
</listitem>
</itemizedlist>
</sect1>
<sect1 id="sg-manual.todo-implementation"
xreflabel="Still To Do: Implementation Tidying">
<title>Still To Do: Implementation Tidying</title>
<para>Items marked CRITICAL are considered important for correctness:
non-fixage of them is liable to lead to crashes or assertion failures
in real use.</para>
<itemizedlist>
<listitem>
<para> sg_main.c: Redesign and reimplement the basic checking
algorithm. It could be done much faster than it is -- the current
implementation isn't very good.
</para>
</listitem>
<listitem>
<para> sg_main.c: Improve the performance of the stack / global
checks by doing some up-front filtering to ignore references in
areas which "obviously" can't be stack or globals. This will
require using information that m_aspacemgr knows about the address
space layout.</para>
</listitem>
<listitem>
<para>sg_main.c: fix compute_II_hash to make it a bit more sensible
for ppc32/64 targets (except that sg_ doesn't work on ppc32/64
targets, so this is a bit academic at the moment).</para>
</listitem>
</itemizedlist>
</sect1>
</chapter>
Binary file not shown.
@@ -0,0 +1,442 @@
/*--------------------------------------------------------------------*/
/*--- Ptrcheck: a pointer-use checker. pc_intercepts.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Ptrcheck, a Valgrind tool for checking pointer
use in programs.
Copyright (C) 2003-2015 Nicholas Nethercote
njn@valgrind.org
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
/* Nothing actually in here. However it appears this file is needed
to make malloc intercepting work. (jrs, 2 july 08 -- not sure about
that).
*/
#include "pub_tool_basics.h"
#include "pub_tool_hashtable.h"
#include "pub_tool_redir.h"
#include "pub_tool_tooliface.h"
#include "pub_tool_clreq.h"
/* The following intercepts are copied verbatim from
memcheck/mc_replace_strmem.c. If you copy more in, please keep
them in the same order as in mc_replace_strmem.c. */
#define STRRCHR(soname, fnname) \
char* VG_REPLACE_FUNCTION_ZU(soname,fnname)( const char* s, int c ); \
char* VG_REPLACE_FUNCTION_ZU(soname,fnname)( const char* s, int c ) \
{ \
HChar ch = (HChar)c; \
const HChar* p = s; \
const HChar* last = NULL; \
while (True) { \
if (*p == ch) last = p; \
if (*p == 0) return CONST_CAST(HChar *,last); \
p++; \
} \
}
// Apparently rindex() is the same thing as strrchr()
STRRCHR(VG_Z_LIBC_SONAME, strrchr)
STRRCHR(VG_Z_LIBC_SONAME, rindex)
#if defined(VGO_linux)
STRRCHR(VG_Z_LIBC_SONAME, __GI_strrchr)
STRRCHR(VG_Z_LD_LINUX_SO_2, rindex)
#elif defined(VGO_darwin)
STRRCHR(VG_Z_DYLD, strrchr)
STRRCHR(VG_Z_DYLD, rindex)
#elif defined(VGO_solaris)
STRRCHR(VG_Z_LD_SO_1, strrchr)
#endif
#define STRCHR(soname, fnname) \
char* VG_REPLACE_FUNCTION_ZU(soname,fnname) ( const char* s, int c ); \
char* VG_REPLACE_FUNCTION_ZU(soname,fnname) ( const char* s, int c ) \
{ \
HChar ch = (HChar)c ; \
const HChar* p = s; \
while (True) { \
if (*p == ch) return CONST_CAST(HChar *,p); \
if (*p == 0) return NULL; \
p++; \
} \
}
// Apparently index() is the same thing as strchr()
STRCHR(VG_Z_LIBC_SONAME, strchr)
STRCHR(VG_Z_LIBC_SONAME, index)
#if defined(VGO_linux)
STRCHR(VG_Z_LIBC_SONAME, __GI_strchr)
STRCHR(VG_Z_LD_LINUX_SO_2, strchr)
STRCHR(VG_Z_LD_LINUX_SO_2, index)
STRCHR(VG_Z_LD_LINUX_X86_64_SO_2, strchr)
STRCHR(VG_Z_LD_LINUX_X86_64_SO_2, index)
#elif defined(VGO_darwin)
STRCHR(VG_Z_DYLD, strchr)
STRCHR(VG_Z_DYLD, index)
#elif defined(VGO_solaris)
STRCHR(VG_Z_LD_SO_1, strchr)
#endif
#define STRNLEN(soname, fnname) \
SizeT VG_REPLACE_FUNCTION_ZU(soname,fnname) ( const char* str, SizeT n ); \
SizeT VG_REPLACE_FUNCTION_ZU(soname,fnname) ( const char* str, SizeT n ) \
{ \
SizeT i = 0; \
while (i < n && str[i] != 0) i++; \
return i; \
}
STRNLEN(VG_Z_LIBC_SONAME, strnlen)
// Note that this replacement often doesn't get used because gcc inlines
// calls to strlen() with its own built-in version. This can be very
// confusing if you aren't expecting it. Other small functions in this file
// may also be inline by gcc.
#define STRLEN(soname, fnname) \
SizeT VG_REPLACE_FUNCTION_ZU(soname,fnname)( const char* str ); \
SizeT VG_REPLACE_FUNCTION_ZU(soname,fnname)( const char* str ) \
{ \
SizeT i = 0; \
while (str[i] != 0) i++; \
return i; \
}
STRLEN(VG_Z_LIBC_SONAME, strlen)
#if defined(VGO_linux)
STRLEN(VG_Z_LIBC_SONAME, __GI_strlen)
STRLEN(VG_Z_LD_LINUX_SO_2, strlen)
STRLEN(VG_Z_LD_LINUX_X86_64_SO_2, strlen)
STRLEN(VG_Z_LD_SO_1, strlen)
#elif defined(VGO_solaris)
STRLEN(VG_Z_LD_SO_1, strlen)
#endif
#define STRCPY(soname, fnname) \
char* VG_REPLACE_FUNCTION_ZU(soname, fnname) ( char* dst, const char* src ); \
char* VG_REPLACE_FUNCTION_ZU(soname, fnname) ( char* dst, const char* src ) \
{ \
HChar* dst_orig = dst; \
\
while (*src) *dst++ = *src++; \
*dst = 0; \
\
return dst_orig; \
}
STRCPY(VG_Z_LIBC_SONAME, strcpy)
#if defined(VGO_linux)
STRCPY(VG_Z_LIBC_SONAME, __GI_strcpy)
#elif defined(VGO_darwin)
STRCPY(VG_Z_DYLD, strcpy)
#elif defined(VGO_solaris)
STRCPY(VG_Z_LD_SO_1, strcpy)
#endif
#define STRNCMP(soname, fnname) \
int VG_REPLACE_FUNCTION_ZU(soname,fnname) \
( const char* s1, const char* s2, SizeT nmax ); \
int VG_REPLACE_FUNCTION_ZU(soname,fnname) \
( const char* s1, const char* s2, SizeT nmax ) \
{ \
SizeT n = 0; \
while (True) { \
if (n >= nmax) return 0; \
if (*s1 == 0 && *s2 == 0) return 0; \
if (*s1 == 0) return -1; \
if (*s2 == 0) return 1; \
\
if (*(const unsigned char*)s1 < *(const unsigned char*)s2) return -1; \
if (*(const unsigned char*)s1 > *(const unsigned char*)s2) return 1; \
\
s1++; s2++; n++; \
} \
}
STRNCMP(VG_Z_LIBC_SONAME, strncmp)
#if defined(VGO_linux)
STRNCMP(VG_Z_LIBC_SONAME, __GI_strncmp)
#elif defined(VGO_darwin)
STRNCMP(VG_Z_DYLD, strncmp)
#endif
#define STRCMP(soname, fnname) \
int VG_REPLACE_FUNCTION_ZU(soname,fnname) \
( const char* s1, const char* s2 ); \
int VG_REPLACE_FUNCTION_ZU(soname,fnname) \
( const char* s1, const char* s2 ) \
{ \
register UChar c1; \
register UChar c2; \
while (True) { \
c1 = *(const UChar *)s1; \
c2 = *(const UChar *)s2; \
if (c1 != c2) break; \
if (c1 == 0) break; \
s1++; s2++; \
} \
if ((UChar)c1 < (UChar)c2) return -1; \
if ((UChar)c1 > (UChar)c2) return 1; \
return 0; \
}
STRCMP(VG_Z_LIBC_SONAME, strcmp)
#if defined(VGO_linux)
STRCMP(VG_Z_LIBC_SONAME, __GI_strcmp)
STRCMP(VG_Z_LD_LINUX_X86_64_SO_2, strcmp)
STRCMP(VG_Z_LD64_SO_1, strcmp)
#elif defined(VGO_solaris)
STRCMP(VG_Z_LD_SO_1, strcmp)
#endif
#define MEMCHR(soname, fnname) \
void* VG_REPLACE_FUNCTION_ZU(soname,fnname) (const void *s, int c, SizeT n); \
void* VG_REPLACE_FUNCTION_ZU(soname,fnname) (const void *s, int c, SizeT n) \
{ \
SizeT i; \
UChar c0 = (UChar)c; \
const UChar* p = s; \
for (i = 0; i < n; i++) \
if (p[i] == c0) return CONST_CAST(void *,&p[i]); \
return NULL; \
}
MEMCHR(VG_Z_LIBC_SONAME, memchr)
#if defined(VGO_darwin)
MEMCHR(VG_Z_DYLD, memchr)
#endif
#define MEMCPY(soname, fnname) \
void* VG_REPLACE_FUNCTION_ZU(soname,fnname) \
( void *dst, const void *src, SizeT len ); \
void* VG_REPLACE_FUNCTION_ZU(soname,fnname) \
( void *dst, const void *src, SizeT len ) \
{ \
const Addr WS = sizeof(UWord); /* 8 or 4 */ \
const Addr WM = WS - 1; /* 7 or 3 */ \
\
if (len > 0) { \
if (dst < src) { \
\
/* Copying backwards. */ \
SizeT n = len; \
Addr d = (Addr)dst; \
Addr s = (Addr)src; \
\
if (((s^d) & WM) == 0) { \
/* s and d have same UWord alignment. */ \
/* Pull up to a UWord boundary. */ \
while ((s & WM) != 0 && n >= 1) \
{ *(UChar*)d = *(UChar*)s; s += 1; d += 1; n -= 1; } \
/* Copy UWords. */ \
while (n >= WS) \
{ *(UWord*)d = *(UWord*)s; s += WS; d += WS; n -= WS; } \
if (n == 0) \
return dst; \
} \
if (((s|d) & 1) == 0) { \
/* Both are 16-aligned; copy what we can thusly. */ \
while (n >= 2) \
{ *(UShort*)d = *(UShort*)s; s += 2; d += 2; n -= 2; } \
} \
/* Copy leftovers, or everything if misaligned. */ \
while (n >= 1) \
{ *(UChar*)d = *(UChar*)s; s += 1; d += 1; n -= 1; } \
\
} else if (dst > src) { \
\
SizeT n = len; \
Addr d = ((Addr)dst) + n; \
Addr s = ((Addr)src) + n; \
\
/* Copying forwards. */ \
if (((s^d) & WM) == 0) { \
/* s and d have same UWord alignment. */ \
/* Back down to a UWord boundary. */ \
while ((s & WM) != 0 && n >= 1) \
{ s -= 1; d -= 1; *(UChar*)d = *(UChar*)s; n -= 1; } \
/* Copy UWords. */ \
while (n >= WS) \
{ s -= WS; d -= WS; *(UWord*)d = *(UWord*)s; n -= WS; } \
if (n == 0) \
return dst; \
} \
if (((s|d) & 1) == 0) { \
/* Both are 16-aligned; copy what we can thusly. */ \
while (n >= 2) \
{ s -= 2; d -= 2; *(UShort*)d = *(UShort*)s; n -= 2; } \
} \
/* Copy leftovers, or everything if misaligned. */ \
while (n >= 1) \
{ s -= 1; d -= 1; *(UChar*)d = *(UChar*)s; n -= 1; } \
\
} \
} \
\
return dst; \
}
MEMCPY(VG_Z_LIBC_SONAME, memcpy)
#if defined(VGO_linux)
MEMCPY(VG_Z_LD_SO_1, memcpy) /* ld.so.1 */
MEMCPY(VG_Z_LD64_SO_1, memcpy) /* ld64.so.1 */
#elif defined(VGO_solaris)
MEMCPY(VG_Z_LD_SO_1, memcpy)
#endif
/* Copy SRC to DEST, returning the address of the terminating '\0' in
DEST. (minor variant of strcpy) */
#define STPCPY(soname, fnname) \
char* VG_REPLACE_FUNCTION_ZU(soname,fnname) ( char* dst, const char* src ); \
char* VG_REPLACE_FUNCTION_ZU(soname,fnname) ( char* dst, const char* src ) \
{ \
while (*src) *dst++ = *src++; \
*dst = 0; \
\
return dst; \
}
STPCPY(VG_Z_LIBC_SONAME, stpcpy)
#if defined(VGO_linux)
STPCPY(VG_Z_LD_LINUX_SO_2, stpcpy)
STPCPY(VG_Z_LD_LINUX_X86_64_SO_2, stpcpy)
#endif
/* Find the first occurrence of C in S. */
#define GLIBC232_RAWMEMCHR(soname, fnname) \
void* VG_REPLACE_FUNCTION_ZU(soname,fnname) (const void* s, int c_in); \
void* VG_REPLACE_FUNCTION_ZU(soname,fnname) (const void* s, int c_in) \
{ \
UChar c = (UChar)c_in; \
const UChar* char_ptr = s; \
while (1) { \
if (*char_ptr == c) return CONST_CAST(void *,char_ptr); \
char_ptr++; \
} \
}
GLIBC232_RAWMEMCHR(VG_Z_LIBC_SONAME, rawmemchr)
#if defined (VGO_linux)
GLIBC232_RAWMEMCHR(VG_Z_LIBC_SONAME, __GI___rawmemchr)
#endif
#define STRSTR(soname, fnname) \
char* VG_REPLACE_FUNCTION_ZU(soname,fnname) \
(const char* haystack, const char* needle); \
char* VG_REPLACE_FUNCTION_ZU(soname,fnname) \
(const char* haystack, const char* needle) \
{ \
const HChar* h = haystack; \
const HChar* n = needle; \
\
/* find the length of n, not including terminating zero */ \
UWord nlen = 0; \
while (n[nlen]) nlen++; \
\
/* if n is the empty string, match immediately. */ \
if (nlen == 0) return CONST_CAST(HChar *,h); \
\
/* assert(nlen >= 1); */ \
HChar n0 = n[0]; \
\
while (1) { \
const HChar hh = *h; \
if (hh == 0) return NULL; \
if (hh != n0) { h++; continue; } \
\
UWord i; \
for (i = 0; i < nlen; i++) { \
if (n[i] != h[i]) \
break; \
} \
/* assert(i >= 0 && i <= nlen); */ \
if (i == nlen) \
return CONST_CAST(HChar *,h); \
\
h++; \
} \
}
#if defined(VGO_linux)
STRSTR(VG_Z_LIBC_SONAME, strstr)
#elif defined(VGO_solaris)
STRSTR(VG_Z_LIBC_SONAME, strstr)
#endif
#define STRPBRK(soname, fnname) \
char* VG_REPLACE_FUNCTION_ZU(soname,fnname) \
(const char* sV, const char* acceptV); \
char* VG_REPLACE_FUNCTION_ZU(soname,fnname) \
(const char* sV, const char* acceptV) \
{ \
const HChar* s = sV; \
const HChar* accept = acceptV; \
\
/* find the length of 'accept', not including terminating zero */ \
UWord nacc = 0; \
while (accept[nacc]) nacc++; \
\
/* if n is the empty string, fail immediately. */ \
if (nacc == 0) return NULL; \
\
/* assert(nacc >= 1); */ \
while (1) { \
UWord i; \
HChar sc = *s; \
if (sc == 0) \
break; \
for (i = 0; i < nacc; i++) { \
if (sc == accept[i]) \
return CONST_CAST(HChar *,s); \
} \
s++; \
} \
\
return NULL; \
}
#if defined(VGO_linux)
STRPBRK(VG_Z_LIBC_SONAME, strpbrk)
#elif defined(VGO_solaris)
STRPBRK(VG_Z_LIBC_SONAME, strpbrk)
#endif
/*--------------------------------------------------------------------*/
/*--- end pc_intercepts.c ---*/
/*--------------------------------------------------------------------*/
@@ -0,0 +1,730 @@
/*--------------------------------------------------------------------*/
/*--- Ptrcheck: a pointer-use checker. ---*/
/*--- This file checks heap accesses. ---*/
/*--- h_main.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Ptrcheck, a Valgrind tool for checking pointer
use in programs.
Initial version (Annelid):
Copyright (C) 2003-2015 Nicholas Nethercote
njn@valgrind.org
Valgrind-3.X port:
Copyright (C) 2008-2015 OpenWorks Ltd
info@open-works.co.uk
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
#include "pub_tool_basics.h"
#include "pub_tool_libcbase.h"
#include "pub_tool_libcprint.h"
#include "pub_tool_libcassert.h"
#include "pub_tool_mallocfree.h"
#include "pub_tool_execontext.h"
#include "pub_tool_hashtable.h"
#include "pub_tool_tooliface.h"
#include "pub_tool_replacemalloc.h"
#include "pub_tool_options.h"
#include "pub_tool_execontext.h"
#include "pub_tool_aspacemgr.h" // VG_(am_shadow_malloc)
#include "pub_tool_vki.h" // VKI_MAX_PAGE_SIZE
#include "pub_tool_machine.h" // VG_({get,set}_shadow_regs_area) et al
#include "pub_tool_debuginfo.h" // VG_(get_fnname)
#include "pub_tool_threadstate.h" // VG_(get_running_tid)
#include "pub_tool_oset.h"
#include "pub_tool_vkiscnums.h"
#include "pub_tool_machine.h"
#include "pub_tool_wordfm.h"
#include "pub_tool_xarray.h"
#include "pc_common.h"
//#include "h_list.h"
#include "h_main.h"
#include "sg_main.h" // sg_instrument_*, and struct _SGEnv
/*------------------------------------------------------------*/
/*--- Debug/trace options ---*/
/*------------------------------------------------------------*/
static ULong stats__client_mallocs = 0;
static ULong stats__client_frees = 0;
static ULong stats__segs_allocd = 0;
static ULong stats__segs_recycled = 0;
//////////////////////////////////////////////////////////////
// //
// Segments low level storage //
// //
//////////////////////////////////////////////////////////////
// NONPTR, UNKNOWN, BOTTOM defined in h_main.h since
// pc_common.c needs to see them, for error processing
// we only start recycling segs when this many exist
#define N_FREED_SEGS (1 * 1000 * 1000)
struct _Seg {
Addr addr;
SizeT szB; /* may be zero */
ExeContext* ec; /* where malloc'd or freed */
/* When 1, indicates block is in use. Otherwise, used to form a
linked list of freed blocks, running from oldest freed block to
the most recently freed block. */
struct _Seg* nextfree;
};
// Determines if 'a' is before, within, or after seg's range. Sets 'cmp' to
// -1/0/1 accordingly. Sets 'n' to the number of bytes before/within/after.
void Seg__cmp(Seg* seg, Addr a, Int* cmp, UWord* n)
{
if (a < seg->addr) {
*cmp = -1;
*n = seg->addr - a;
} else if (a < seg->addr + seg->szB && seg->szB > 0) {
*cmp = 0;
*n = a - seg->addr;
} else {
*cmp = 1;
*n = a - (seg->addr + seg->szB);
}
}
/*inline*/ Bool Seg__is_freed(Seg* seg)
{
if (!is_known_segment(seg))
return False;
else
return seg->nextfree != (Seg*)1;
}
ExeContext* Seg__where(Seg* seg)
{
tl_assert(is_known_segment(seg));
return seg->ec;
}
SizeT Seg__size(Seg* seg)
{
tl_assert(is_known_segment(seg));
return seg->szB;
}
Addr Seg__addr(Seg* seg)
{
tl_assert(is_known_segment(seg));
return seg->addr;
}
#define N_SEGS_PER_GROUP 10000
typedef
struct _SegGroup {
struct _SegGroup* admin;
UWord nextfree; /* 0 .. N_SEGS_PER_GROUP */
Seg segs[N_SEGS_PER_GROUP];
}
SegGroup;
static SegGroup* group_list = NULL;
static UWord nFreeSegs = 0;
static Seg* freesegs_youngest = NULL;
static Seg* freesegs_oldest = NULL;
static SegGroup* new_SegGroup ( void ) {
SegGroup* g = VG_(malloc)("pc.h_main.nTG.1", sizeof(SegGroup));
VG_(memset)(g, 0, sizeof(*g));
return g;
}
/* Get a completely new Seg */
static Seg* new_Seg ( void )
{
Seg* teg;
SegGroup* g;
if (group_list == NULL) {
g = new_SegGroup();
g->admin = NULL;
group_list = g;
}
tl_assert(group_list->nextfree <= N_SEGS_PER_GROUP);
if (group_list->nextfree == N_SEGS_PER_GROUP) {
g = new_SegGroup();
g->admin = group_list;
group_list = g;
}
tl_assert(group_list->nextfree < N_SEGS_PER_GROUP);
teg = &group_list->segs[ group_list->nextfree ];
group_list->nextfree++;
stats__segs_allocd++;
return teg;
}
static Seg* get_Seg_for_malloc ( void )
{
Seg* seg;
if (nFreeSegs < N_FREED_SEGS) {
seg = new_Seg();
seg->nextfree = (Seg*)1;
return seg;
}
/* else recycle the oldest Seg in the free list */
tl_assert(freesegs_youngest);
tl_assert(freesegs_oldest);
tl_assert(freesegs_youngest != freesegs_oldest);
seg = freesegs_oldest;
freesegs_oldest = seg->nextfree;
nFreeSegs--;
seg->nextfree = (Seg*)1;
stats__segs_recycled++;
return seg;
}
static void set_Seg_freed ( Seg* seg )
{
tl_assert(seg);
tl_assert(!Seg__is_freed(seg));
if (nFreeSegs == 0) {
tl_assert(freesegs_oldest == NULL);
tl_assert(freesegs_youngest == NULL);
seg->nextfree = NULL;
freesegs_youngest = seg;
freesegs_oldest = seg;
nFreeSegs++;
} else {
tl_assert(freesegs_youngest);
tl_assert(freesegs_oldest);
if (nFreeSegs == 1) {
tl_assert(freesegs_youngest == freesegs_oldest);
} else {
tl_assert(freesegs_youngest != freesegs_oldest);
}
tl_assert(freesegs_youngest->nextfree == NULL);
tl_assert(seg != freesegs_youngest && seg != freesegs_oldest);
seg->nextfree = NULL;
freesegs_youngest->nextfree = seg;
freesegs_youngest = seg;
nFreeSegs++;
}
}
static WordFM* addr_to_seg_map = NULL; /* GuestAddr -> Seg* */
static void addr_to_seg_map_ENSURE_INIT ( void )
{
if (UNLIKELY(addr_to_seg_map == NULL)) {
addr_to_seg_map = VG_(newFM)( VG_(malloc), "pc.h_main.attmEI.1",
VG_(free), NULL/*unboxedcmp*/ );
}
}
static Seg* find_Seg_by_addr ( Addr ga )
{
UWord keyW, valW;
addr_to_seg_map_ENSURE_INIT();
if (VG_(lookupFM)( addr_to_seg_map, &keyW, &valW, (UWord)ga )) {
tl_assert(keyW == ga);
return (Seg*)valW;
} else {
return NULL;
}
}
static void bind_addr_to_Seg ( Addr ga, Seg* seg )
{
Bool b;
addr_to_seg_map_ENSURE_INIT();
b = VG_(addToFM)( addr_to_seg_map, (UWord)ga, (UWord)seg );
tl_assert(!b); /* else ga is already bound */
}
static void unbind_addr_from_Seg ( Addr ga )
{
Bool b;
UWord keyW, valW;
addr_to_seg_map_ENSURE_INIT();
b = VG_(delFromFM)( addr_to_seg_map, &keyW, &valW, (UWord)ga );
tl_assert(b); /* else ga was not already bound */
tl_assert(keyW == ga);
tl_assert(valW != 0);
}
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
// Returns the added heap segment
static Seg* add_new_segment ( ThreadId tid, Addr p, SizeT size )
{
Seg* seg = get_Seg_for_malloc();
tl_assert(seg != (Seg*)1); /* since we're using 1 as a special value */
seg->addr = p;
seg->szB = size;
seg->ec = VG_(record_ExeContext)( tid, 0/*first_ip_delta*/ );
tl_assert(!Seg__is_freed(seg));
bind_addr_to_Seg(p, seg);
return seg;
}
static
void* alloc_and_new_mem_heap ( ThreadId tid,
SizeT size, SizeT alignment, Bool is_zeroed )
{
Addr p;
if ( ((SSizeT)size) < 0) return NULL;
p = (Addr)VG_(cli_malloc)(alignment, size);
if (is_zeroed) VG_(memset)((void*)p, 0, size);
add_new_segment( tid, p, size );
stats__client_mallocs++;
return (void*)p;
}
static void die_and_free_mem_heap ( ThreadId tid, Seg* seg )
{
// Empty and free the actual block
tl_assert(!Seg__is_freed(seg));
VG_(cli_free)( (void*)seg->addr );
// Remember where freed
seg->ec = VG_(record_ExeContext)( tid, 0/*first_ip_delta*/ );
set_Seg_freed(seg);
unbind_addr_from_Seg( seg->addr );
stats__client_frees++;
}
static void handle_free_heap( ThreadId tid, void* p )
{
Seg* seg = find_Seg_by_addr( (Addr)p );
if (!seg) {
/* freeing a block that wasn't malloc'd. Ignore. */
return;
}
die_and_free_mem_heap( tid, seg );
}
/*------------------------------------------------------------*/
/*--- malloc() et al replacements ---*/
/*------------------------------------------------------------*/
void* h_replace_malloc ( ThreadId tid, SizeT n )
{
return alloc_and_new_mem_heap ( tid, n, VG_(clo_alignment),
/*is_zeroed*/False );
}
void* h_replace___builtin_new ( ThreadId tid, SizeT n )
{
return alloc_and_new_mem_heap ( tid, n, VG_(clo_alignment),
/*is_zeroed*/False );
}
void* h_replace___builtin_vec_new ( ThreadId tid, SizeT n )
{
return alloc_and_new_mem_heap ( tid, n, VG_(clo_alignment),
/*is_zeroed*/False );
}
void* h_replace_memalign ( ThreadId tid, SizeT align, SizeT n )
{
return alloc_and_new_mem_heap ( tid, n, align,
/*is_zeroed*/False );
}
void* h_replace_calloc ( ThreadId tid, SizeT nmemb, SizeT size1 )
{
return alloc_and_new_mem_heap ( tid, nmemb*size1, VG_(clo_alignment),
/*is_zeroed*/True );
}
void h_replace_free ( ThreadId tid, void* p )
{
// Should arguably check here if p.vseg matches the segID of the
// pointed-to block... unfortunately, by this stage, we don't know what
// p.vseg is, because we don't know the address of p (the p here is a
// copy, and we've lost the address of its source). To do so would
// require passing &p in, which would require rewriting part of
// vg_replace_malloc.c... argh.
//
// However, Memcheck does free checking, and will catch almost all
// violations this checking would have caught. (Would only miss if we
// unluckily passed an unrelated pointer to the very start of a heap
// block that was unrelated to that block. This is very unlikely!) So
// we haven't lost much.
handle_free_heap(tid, p);
}
void h_replace___builtin_delete ( ThreadId tid, void* p )
{
handle_free_heap(tid, p);
}
void h_replace___builtin_vec_delete ( ThreadId tid, void* p )
{
handle_free_heap(tid, p);
}
void* h_replace_realloc ( ThreadId tid, void* p_old, SizeT new_size )
{
Seg* seg;
/* First try and find the block. */
seg = find_Seg_by_addr( (Addr)p_old );
if (!seg)
return NULL;
tl_assert(seg->addr == (Addr)p_old);
if (new_size <= seg->szB) {
/* new size is smaller: allocate, copy from old to new */
Addr p_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_size);
VG_(memcpy)((void*)p_new, p_old, new_size);
/* Free old memory */
die_and_free_mem_heap( tid, seg );
/* This has to be after die_and_free_mem_heap, otherwise the
former succeeds in shorting out the new block, not the
old, in the case when both are on the same list. */
add_new_segment ( tid, p_new, new_size );
return (void*)p_new;
} else {
/* new size is bigger: allocate, copy from old to new */
Addr p_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_size);
VG_(memcpy)((void*)p_new, p_old, seg->szB);
/* Free old memory */
die_and_free_mem_heap( tid, seg );
/* This has to be after die_and_free_mem_heap, otherwise the
former succeeds in shorting out the new block, not the old,
in the case when both are on the same list. NB jrs
2008-Sept-11: not sure if this comment is valid/correct any
more -- I suspect not. */
add_new_segment ( tid, p_new, new_size );
return (void*)p_new;
}
}
SizeT h_replace_malloc_usable_size ( ThreadId tid, void* p )
{
Seg* seg = find_Seg_by_addr( (Addr)p );
// There may be slop, but pretend there isn't because only the asked-for
// area will have been shadowed properly.
return ( seg ? seg->szB : 0 );
}
/*--------------------------------------------------------------------*/
/*--- Instrumentation ---*/
/*--------------------------------------------------------------------*/
/* The h_ instrumenter that follows is complex, since it deals with
shadow value computation.
It also needs to generate instrumentation for the sg_ side of
things. That's relatively straightforward. However, rather than
confuse the code herein any further, we simply delegate the problem
to sg_main.c, by using the four functions
sg_instrument_{init,fini,IRStmt,final_jump}. These four completely
abstractify the sg_ instrumentation. See comments in sg_main.c's
instrumentation section for further details. */
/* Carries info about a particular tmp. The tmp's number is not
recorded, as this is implied by (equal to) its index in the tmpMap
in PCEnv. The tmp's type is also not recorded, as this is present
in PCEnv.sb->tyenv.
When .kind is NonShad, .shadow may give the identity of the temp
currently holding the associated shadow value, or it may be
IRTemp_INVALID if code to compute the shadow has not yet been
emitted.
When .kind is Shad tmp holds a shadow value, and so .shadow must be
IRTemp_INVALID, since it is illogical for a shadow tmp itself to be
shadowed.
*/
typedef
enum { NonShad=1, Shad=2 }
TempKind;
typedef
struct {
TempKind kind;
IRTemp shadow;
}
TempMapEnt;
/* Carries around state during Ptrcheck instrumentation. */
typedef
struct {
/* MODIFIED: the superblock being constructed. IRStmts are
added. */
IRSB* sb;
Bool trace;
/* MODIFIED: a table [0 .. #temps_in_sb-1] which gives the
current kind and possibly shadow temps for each temp in the
IRSB being constructed. Note that it does not contain the
type of each tmp. If you want to know the type, look at the
relevant entry in sb->tyenv. It follows that at all times
during the instrumentation process, the valid indices for
tmpMap and sb->tyenv are identical, being 0 .. N-1 where N is
total number of NonShad and Shad temps allocated so far.
The reason for this strange split (types in one place, all
other info in another) is that we need the types to be
attached to sb so as to make it possible to do
"typeOfIRExpr(mce->bb->tyenv, ...)" at various places in the
instrumentation process.
Note that only integer temps of the guest word size are
shadowed, since it is impossible (or meaningless) to hold a
pointer in any other type of temp. */
XArray* /* of TempMapEnt */ qmpMap;
/* READONLY: the host word type. Needed for constructing
arguments of type 'HWord' to be passed to helper functions.
Ity_I32 or Ity_I64 only. */
IRType hWordTy;
/* READONLY: the guest word type, Ity_I32 or Ity_I64 only. */
IRType gWordTy;
/* READONLY: the guest state size, so we can generate shadow
offsets correctly. */
Int guest_state_sizeB;
}
PCEnv;
/* SHADOW TMP MANAGEMENT. Shadow tmps are allocated lazily (on
demand), as they are encountered. This is for two reasons.
(1) (less important reason): Many original tmps are unused due to
initial IR optimisation, and we do not want to spaces in tables
tracking them.
Shadow IRTemps are therefore allocated on demand. pce.tmpMap is a
table indexed [0 .. n_types-1], which gives the current shadow for
each original tmp, or INVALID_IRTEMP if none is so far assigned.
It is necessary to support making multiple assignments to a shadow
-- specifically, after testing a shadow for definedness, it needs
to be made defined. But IR's SSA property disallows this.
(2) (more important reason): Therefore, when a shadow needs to get
a new value, a new temporary is created, the value is assigned to
that, and the tmpMap is updated to reflect the new binding.
A corollary is that if the tmpMap maps a given tmp to
IRTemp_INVALID and we are hoping to read that shadow tmp, it means
there's a read-before-write error in the original tmps. The IR
sanity checker should catch all such anomalies, however.
*/
/* Create a new IRTemp of type 'ty' and kind 'kind', and add it to
both the table in pce->sb and to our auxiliary mapping. Note that
newTemp may cause pce->tmpMap to resize, hence previous results
from VG_(indexXA)(pce->tmpMap) are invalidated. */
static IRTemp newTemp ( PCEnv* pce, IRType ty, TempKind kind )
{
Word newIx;
TempMapEnt ent;
IRTemp tmp = newIRTemp(pce->sb->tyenv, ty);
ent.kind = kind;
ent.shadow = IRTemp_INVALID;
newIx = VG_(addToXA)( pce->qmpMap, &ent );
tl_assert(newIx == (Word)tmp);
return tmp;
}
/*------------------------------------------------------------*/
/*--- Constructing IR fragments ---*/
/*------------------------------------------------------------*/
/* add stmt to a bb */
static /*inline*/ void stmt ( HChar cat, PCEnv* pce, IRStmt* st ) {
if (pce->trace) {
VG_(printf)(" %c: ", cat);
ppIRStmt(st);
VG_(printf)("\n");
}
addStmtToIRSB(pce->sb, st);
}
static IRTemp for_sg__newIRTemp_cb ( IRType ty, void* opaque )
{
PCEnv* pce = (PCEnv*)opaque;
return newTemp( pce, ty, NonShad );
}
IRSB* h_instrument ( VgCallbackClosure* closure,
IRSB* sbIn,
const VexGuestLayout* layout,
const VexGuestExtents* vge,
const VexArchInfo* archinfo_host,
IRType gWordTy, IRType hWordTy )
{
Bool verboze = 0||False;
Int i /*, j*/;
PCEnv pce;
struct _SGEnv* sgenv;
if (gWordTy != hWordTy) {
/* We don't currently support this case. */
VG_(tool_panic)("host/guest word size mismatch");
}
/* Check we're not completely nuts */
tl_assert(sizeof(UWord) == sizeof(void*));
tl_assert(sizeof(Word) == sizeof(void*));
tl_assert(sizeof(Addr) == sizeof(void*));
tl_assert(sizeof(ULong) == 8);
tl_assert(sizeof(Long) == 8);
tl_assert(sizeof(Addr) == sizeof(void*));
tl_assert(sizeof(UInt) == 4);
tl_assert(sizeof(Int) == 4);
/* Set up the running environment. Both .sb and .tmpMap are
modified as we go along. Note that tmps are added to both
.sb->tyenv and .tmpMap together, so the valid index-set for
those two arrays should always be identical. */
VG_(memset)(&pce, 0, sizeof(pce));
pce.sb = deepCopyIRSBExceptStmts(sbIn);
pce.trace = verboze;
pce.hWordTy = hWordTy;
pce.gWordTy = gWordTy;
pce.guest_state_sizeB = layout->total_sizeB;
pce.qmpMap = VG_(newXA)( VG_(malloc), "pc.h_instrument.1", VG_(free),
sizeof(TempMapEnt));
for (i = 0; i < sbIn->tyenv->types_used; i++) {
TempMapEnt ent;
ent.kind = NonShad;
ent.shadow = IRTemp_INVALID;
VG_(addToXA)( pce.qmpMap, &ent );
}
tl_assert( VG_(sizeXA)( pce.qmpMap ) == sbIn->tyenv->types_used );
/* Also set up for the sg_ instrumenter. See comments at the top
of this instrumentation section for details. The two parameters
constitute a closure, which sg_ can use to correctly generate
new IRTemps as needed. */
sgenv = sg_instrument_init( for_sg__newIRTemp_cb,
(void*)&pce );
/* Copy verbatim any IR preamble preceding the first IMark */
i = 0;
while (i < sbIn->stmts_used && sbIn->stmts[i]->tag != Ist_IMark) {
IRStmt* st = sbIn->stmts[i];
tl_assert(st);
tl_assert(isFlatIRStmt(st));
stmt( 'C', &pce, sbIn->stmts[i] );
i++;
}
/* Iterate over the remaining stmts to generate instrumentation. */
tl_assert(sbIn->stmts_used > 0);
tl_assert(i >= 0);
tl_assert(i < sbIn->stmts_used);
tl_assert(sbIn->stmts[i]->tag == Ist_IMark);
for (/*use current i*/; i < sbIn->stmts_used; i++) {
/* generate sg_ instrumentation for this stmt */
sg_instrument_IRStmt( sgenv, pce.sb, sbIn->stmts[i],
layout, gWordTy, hWordTy );
stmt( 'C', &pce, sbIn->stmts[i] );
}
/* generate sg_ instrumentation for the final jump */
sg_instrument_final_jump( sgenv, pce.sb, sbIn->next, sbIn->jumpkind,
layout, gWordTy, hWordTy );
/* and finalise .. */
sg_instrument_fini( sgenv );
/* If this fails, there's been some serious snafu with tmp management,
that should be investigated. */
tl_assert( VG_(sizeXA)( pce.qmpMap ) == pce.sb->tyenv->types_used );
VG_(deleteXA)( pce.qmpMap );
return pce.sb;
}
/*--------------------------------------------------------------------*/
/*--- Finalisation ---*/
/*--------------------------------------------------------------------*/
void h_fini ( Int exitcode )
{
if (VG_(clo_verbosity) == 1 && !VG_(clo_xml)) {
VG_(message)(Vg_UserMsg,
"For counts of detected and suppressed errors, "
"rerun with: -v\n");
}
if (VG_(clo_stats)) {
VG_(message)(Vg_DebugMsg,
" h_: %'10llu client allocs, %'10llu client frees\n",
stats__client_mallocs, stats__client_frees);
VG_(message)(Vg_DebugMsg,
" h_: %'10llu Segs allocd, %'10llu Segs recycled\n",
stats__segs_allocd, stats__segs_recycled);
}
}
/*--------------------------------------------------------------------*/
/*--- end h_main.c ---*/
/*--------------------------------------------------------------------*/
@@ -0,0 +1,82 @@
/*--------------------------------------------------------------------*/
/*--- Ptrcheck: a pointer-use checker. ---*/
/*--- Exports for heap access checking. ---*/
/*--- h_main.h ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Ptrcheck, a Valgrind tool for checking pointer
use in programs.
Copyright (C) 2003-2015 Nicholas Nethercote
njn@valgrind.org
Copyright (C) 2008-2015 OpenWorks Ltd
info@open-works.co.uk
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
#ifndef __H_MAIN_H
#define __H_MAIN_H
// Choose values that couldn't possibly be pointers
#define NONPTR ((Seg*)0xA1)
#define UNKNOWN ((Seg*)0xB2)
#define BOTTOM ((Seg*)0xC3)
static inline Bool is_known_segment(Seg* teg) {
return (UNKNOWN != teg && BOTTOM != teg && NONPTR != teg);
// better? teg <= BOTTOM
}
void Seg__cmp(Seg* seg, Addr a, Int* cmp, UWord* n);
Bool Seg__is_freed(Seg* seg);
ExeContext* Seg__where(Seg* seg);
SizeT Seg__size(Seg* seg);
Addr Seg__addr(Seg* seg);
void h_pre_clo_init ( void );
void h_post_clo_init ( void );
void h_fini ( Int exitcode );
void* h_replace_malloc ( ThreadId tid, SizeT n );
void* h_replace___builtin_new ( ThreadId tid, SizeT n );
void* h_replace___builtin_vec_new ( ThreadId tid, SizeT n );
void* h_replace_memalign ( ThreadId tid, SizeT align, SizeT n );
void* h_replace_calloc ( ThreadId tid, SizeT nmemb, SizeT size1 );
void h_replace_free ( ThreadId tid, void* p );
void h_replace___builtin_delete ( ThreadId tid, void* p );
void h_replace___builtin_vec_delete ( ThreadId tid, void* p );
void* h_replace_realloc ( ThreadId tid, void* p_old, SizeT new_size );
SizeT h_replace_malloc_usable_size ( ThreadId tid, void* p );
/* Note that this also does the sg_ instrumentation. */
IRSB* h_instrument ( VgCallbackClosure* closure,
IRSB* sbIn,
const VexGuestLayout* layout,
const VexGuestExtents* vge,
const VexArchInfo* archinfo_host,
IRType gWordTy, IRType hWordTy );
#endif
/*--------------------------------------------------------------------*/
/*--- end h_main.h ---*/
/*--------------------------------------------------------------------*/
@@ -0,0 +1,811 @@
/*--------------------------------------------------------------------*/
/*--- Ptrcheck: a pointer-use checker. ---*/
/*--- Provides stuff shared between sg_ and h_ subtools. ---*/
/*--- pc_common.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Ptrcheck, a Valgrind tool for checking pointer
use in programs.
Copyright (C) 2008-2015 OpenWorks Ltd
info@open-works.co.uk
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
Neither the names of the U.S. Department of Energy nor the
University of California nor the names of its contributors may be
used to endorse or promote products derived from this software
without prior written permission.
*/
#include "pub_tool_basics.h"
#include "pub_tool_libcbase.h"
#include "pub_tool_libcprint.h"
#include "pub_tool_xarray.h"
#include "pub_tool_mallocfree.h"
#include "pub_tool_libcassert.h"
#include "pub_tool_options.h"
#include "pub_tool_replacemalloc.h"
#include "pub_tool_execontext.h"
#include "pub_tool_tooliface.h" // CorePart
#include "pub_tool_threadstate.h" // VG_(get_running_tid)
#include "pub_tool_debuginfo.h"
#include "pc_common.h" // self, & Seg
#include "h_main.h" // NONPTR, BOTTOM, UNKNOWN
//////////////////////////////////////////////////////////////
// //
// Command line options //
// //
//////////////////////////////////////////////////////////////
Bool h_clo_partial_loads_ok = True; /* user visible */
/* Bool h_clo_lossage_check = False; */ /* dev flag only */
Bool sg_clo_enable_sg_checks = True; /* user visible */
Bool pc_process_cmd_line_options(const HChar* arg)
{
if VG_BOOL_CLO(arg, "--partial-loads-ok", h_clo_partial_loads_ok) {}
/* else if VG_BOOL_CLO(arg, "--lossage-check", h_clo_lossage_check) {} */
else if VG_BOOL_CLO(arg, "--enable-sg-checks", sg_clo_enable_sg_checks) {}
else
return VG_(replacement_malloc_process_cmd_line_option)(arg);
return True;
}
void pc_print_usage(void)
{
VG_(printf)(
" --partial-loads-ok=no|yes same as for Memcheck [yes]\n"
" --enable-sg-checks=no|yes enable stack & global array checking? [yes]\n"
);
}
void pc_print_debug_usage(void)
{
VG_(printf)(
" (none)\n"
//" --lossage-check=no|yes gather stats for quality control [no]\n"
);
}
//////////////////////////////////////////////////////////////
// //
// Error management -- storage //
// //
//////////////////////////////////////////////////////////////
/* What kind of error it is. */
typedef
enum {
XE_SorG=1202, // sg: stack or global array inconsistency
XE_Heap, // h: mismatched ptr/addr segments on load/store
XE_Arith, // h: bad arithmetic between two segment pointers
XE_SysParam // h: block straddling >1 segment passed to syscall
}
XErrorTag;
typedef
enum {
XS_SorG=2021,
XS_Heap,
XS_Arith,
XS_SysParam
}
XSuppTag;
typedef
struct {
XErrorTag tag;
union {
struct {
Addr addr;
SSizeT sszB; /* -ve is write, +ve is read */
HChar expect[128];
HChar actual[128];
HChar delta[32]; // text showing relation to expected
} SorG;
struct {
Addr addr;
SSizeT sszB; /* -ve is write, +ve is read */
Seg* vseg;
XArray* descr1; /* XArray* of HChar */
XArray* descr2; /* XArray* of HChar */
const HChar* datasym;
PtrdiffT datasymoff;
} Heap;
struct {
Seg* seg1;
Seg* seg2;
const HChar* opname; // user-understandable text name
} Arith;
struct {
CorePart part;
Addr lo;
Addr hi;
Seg* seglo;
Seg* seghi;
} SysParam;
} XE;
}
XError;
void sg_record_error_SorG ( ThreadId tid,
Addr addr, SSizeT sszB,
HChar* expect, HChar* actual, HChar* delta )
{
XError xe;
VG_(memset)(&xe, 0, sizeof(xe));
xe.tag = XE_SorG;
xe.XE.SorG.addr = addr;
xe.XE.SorG.sszB = sszB;
VG_(strncpy)( &xe.XE.SorG.expect[0],
expect, sizeof(xe.XE.SorG.expect) );
VG_(strncpy)( &xe.XE.SorG.actual[0],
actual, sizeof(xe.XE.SorG.actual) );
VG_(strncpy)( &xe.XE.SorG.delta[0],
delta, sizeof(xe.XE.SorG.delta) );
xe.XE.SorG.expect[ sizeof(xe.XE.SorG.expect)-1 ] = 0;
xe.XE.SorG.actual[ sizeof(xe.XE.SorG.actual)-1 ] = 0;
xe.XE.SorG.delta[ sizeof(xe.XE.SorG.delta)-1 ] = 0;
VG_(maybe_record_error)( tid, XE_SorG, 0, NULL, &xe );
}
void h_record_heap_error( Addr a, SizeT size, Seg* vseg, Bool is_write )
{
XError xe;
tl_assert(size > 0);
VG_(memset)(&xe, 0, sizeof(xe));
xe.tag = XE_Heap;
xe.XE.Heap.addr = a;
xe.XE.Heap.sszB = is_write ? -size : size;
xe.XE.Heap.vseg = vseg;
VG_(maybe_record_error)( VG_(get_running_tid)(), XE_Heap,
/*a*/0, /*str*/NULL, /*extra*/(void*)&xe);
}
void h_record_arith_error( Seg* seg1, Seg* seg2, HChar* opname )
{
XError xe;
VG_(memset)(&xe, 0, sizeof(xe));
xe.tag = XE_Arith;
xe.XE.Arith.seg1 = seg1;
xe.XE.Arith.seg2 = seg2;
xe.XE.Arith.opname = opname;
VG_(maybe_record_error)( VG_(get_running_tid)(), XE_Arith,
/*a*/0, /*str*/NULL, /*extra*/(void*)&xe);
}
void h_record_sysparam_error( ThreadId tid, CorePart part, const HChar* s,
Addr lo, Addr hi, Seg* seglo, Seg* seghi )
{
XError xe;
VG_(memset)(&xe, 0, sizeof(xe));
xe.tag = XE_SysParam;
xe.XE.SysParam.part = part;
xe.XE.SysParam.lo = lo;
xe.XE.SysParam.hi = hi;
xe.XE.SysParam.seglo = seglo;
xe.XE.SysParam.seghi = seghi;
VG_(maybe_record_error)( tid, XE_SysParam, /*a*/(Addr)0, /*str*/s,
/*extra*/(void*)&xe);
}
Bool pc_eq_Error ( VgRes res, const Error* e1, const Error* e2 )
{
XError *xe1, *xe2;
tl_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2));
//tl_assert(VG_(get_error_string)(e1) == NULL);
//tl_assert(VG_(get_error_string)(e2) == NULL);
xe1 = (XError*)VG_(get_error_extra)(e1);
xe2 = (XError*)VG_(get_error_extra)(e2);
tl_assert(xe1);
tl_assert(xe2);
if (xe1->tag != xe2->tag)
return False;
switch (xe1->tag) {
case XE_SorG:
return //xe1->XE.SorG.addr == xe2->XE.SorG.addr
//&&
xe1->XE.SorG.sszB == xe2->XE.SorG.sszB
&& 0 == VG_(strncmp)( &xe1->XE.SorG.expect[0],
&xe2->XE.SorG.expect[0],
sizeof(xe1->XE.SorG.expect) )
&& 0 == VG_(strncmp)( &xe1->XE.SorG.actual[0],
&xe2->XE.SorG.actual[0],
sizeof(xe1->XE.SorG.actual) );
case XE_Heap:
case XE_Arith:
case XE_SysParam:
return True;
default:
VG_(tool_panic)("eq_Error: unrecognised error kind");
}
}
//////////////////////////////////////////////////////////////
// //
// Error management -- printing //
// //
//////////////////////////////////////////////////////////////
/* This is the "this error is due to be printed shortly; so have a
look at it any print any preamble you want" function. Which, in
Ptrcheck, we don't use. Hence a no-op.
*/
void pc_before_pp_Error ( const Error* err ) {
}
/* Do a printf-style operation on either the XML or normal output
channel, depending on the setting of VG_(clo_xml).
*/
static void emit_WRK ( const HChar* format, va_list vargs )
{
if (VG_(clo_xml)) {
VG_(vprintf_xml)(format, vargs);
} else {
VG_(vmessage)(Vg_UserMsg, format, vargs);
}
}
static void emit ( const HChar* format, ... ) PRINTF_CHECK(1, 2);
static void emit ( const HChar* format, ... )
{
va_list vargs;
va_start(vargs, format);
emit_WRK(format, vargs);
va_end(vargs);
}
static void emiN ( const HChar* format, ... ) /* With NO FORMAT CHECK */
{
va_list vargs;
va_start(vargs, format);
emit_WRK(format, vargs);
va_end(vargs);
}
static const HChar* readwrite(SSizeT sszB)
{
return ( sszB < 0 ? "write" : "read" );
}
static Word Word__abs ( Word w ) {
return w < 0 ? -w : w;
}
void pc_pp_Error ( const Error* err )
{
const Bool xml = VG_(clo_xml); /* a shorthand, that's all */
XError *xe = (XError*)VG_(get_error_extra)(err);
tl_assert(xe);
if (xml)
emit( " <kind>%s</kind>\n", pc_get_error_name(err));
switch (VG_(get_error_kind)(err)) {
//----------------------------------------------------------
case XE_SorG:
if (xml) {
emit( " <what>Invalid %s of size %ld</what>\n",
xe->XE.SorG.sszB < 0 ? "write" : "read",
Word__abs(xe->XE.SorG.sszB) );
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
emit( " <auxwhat>Address %#lx expected vs actual:</auxwhat>\n",
xe->XE.SorG.addr );
emiN( " <auxwhat>Expected: %pS</auxwhat>\n",
&xe->XE.SorG.expect[0] );
emiN( " <auxwhat>Actual: %pS</auxwhat>\n",
&xe->XE.SorG.actual[0] );
} else {
emit( "Invalid %s of size %ld\n",
xe->XE.SorG.sszB < 0 ? "write" : "read",
Word__abs(xe->XE.SorG.sszB) );
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
emit( " Address %#lx expected vs actual:\n", xe->XE.SorG.addr );
emit( " Expected: %s\n", &xe->XE.SorG.expect[0] );
emit( " Actual: %s\n", &xe->XE.SorG.actual[0] );
if (xe->XE.SorG.delta[0] != 0)
emit(" Actual: is %s Expected\n", &xe->XE.SorG.delta[0]);
}
break;
//----------------------------------------------------------
case XE_Heap: {
const HChar *place, *legit, *how_invalid;
Addr a = xe->XE.Heap.addr;
Seg* vseg = xe->XE.Heap.vseg;
tl_assert(is_known_segment(vseg) || NONPTR == vseg);
if (NONPTR == vseg) {
// Access via a non-pointer
if (xml) {
emit( " <what>Invalid %s of size %ld</what>\n",
readwrite(xe->XE.Heap.sszB),
Word__abs(xe->XE.Heap.sszB) );
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
emit( " <auxwhat>Address %#lx is not derived from "
"any known block</auxwhat>\n", a );
} else {
emit( "Invalid %s of size %ld\n",
readwrite(xe->XE.Heap.sszB),
Word__abs(xe->XE.Heap.sszB) );
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
emit( " Address %#lx is not derived from "
"any known block\n", a );
}
} else {
// Access via a pointer, but outside its range.
Int cmp;
UWord miss_size;
Seg__cmp(vseg, a, &cmp, &miss_size);
if (cmp < 0) place = "before";
else if (cmp == 0) place = "inside";
else place = "after";
how_invalid = ( ( Seg__is_freed(vseg) && 0 != cmp )
? "Doubly-invalid" : "Invalid" );
legit = ( Seg__is_freed(vseg) ? "once-" : "" );
if (xml) {
emit( " <what>%s %s of size %ld</what>\n",
how_invalid,
readwrite(xe->XE.Heap.sszB),
Word__abs(xe->XE.Heap.sszB) );
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
emit( " <auxwhat>Address %#lx is %lu bytes %s "
"the accessing pointer's</auxwhat>\n",
a, miss_size, place );
emit( " <auxwhat>%slegitimate range, "
"a block of size %lu %s</auxwhat>\n",
legit, Seg__size(vseg),
Seg__is_freed(vseg) ? "free'd" : "alloc'd" );
VG_(pp_ExeContext)(Seg__where(vseg));
} else {
emit( "%s %s of size %ld\n",
how_invalid,
readwrite(xe->XE.Heap.sszB),
Word__abs(xe->XE.Heap.sszB) );
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
emit( " Address %#lx is %lu bytes %s the accessing pointer's\n",
a, miss_size, place );
emit( " %slegitimate range, a block of size %lu %s\n",
legit, Seg__size(vseg),
Seg__is_freed(vseg) ? "free'd" : "alloc'd" );
VG_(pp_ExeContext)(Seg__where(vseg));
}
}
/* If we have a better description of the address, show it.
Note that in XML mode, it will already by nicely wrapped up
in tags, either <auxwhat> or <xauxwhat>, so we can just emit
it verbatim. */
if (xml) {
if (xe->XE.Heap.descr1)
emiN( " %pS\n",
(HChar*)VG_(indexXA)( xe->XE.Heap.descr1, 0 ) );
if (xe->XE.Heap.descr2)
emiN( " %pS\n",
(HChar*)VG_(indexXA)( xe->XE.Heap.descr2, 0 ) );
if (xe->XE.Heap.datasym[0] != 0)
emiN( " <auxwhat>Address 0x%llx is %llu bytes "
"inside data symbol \"%pS\"</auxwhat>\n",
(ULong)xe->XE.Heap.addr,
(ULong)xe->XE.Heap.datasymoff,
xe->XE.Heap.datasym );
} else {
if (xe->XE.Heap.descr1)
emit( " %s\n",
(HChar*)VG_(indexXA)( xe->XE.Heap.descr1, 0 ) );
if (xe->XE.Heap.descr2)
emit( " %s\n",
(HChar*)VG_(indexXA)( xe->XE.Heap.descr2, 0 ) );
if (xe->XE.Heap.datasym[0] != 0)
emit( " Address 0x%llx is %llu bytes "
"inside data symbol \"%s\"\n",
(ULong)xe->XE.Heap.addr,
(ULong)xe->XE.Heap.datasymoff,
xe->XE.Heap.datasym );
}
break;
}
//----------------------------------------------------------
case XE_Arith: {
Seg* seg1 = xe->XE.Arith.seg1;
Seg* seg2 = xe->XE.Arith.seg2;
const HChar* which;
tl_assert(BOTTOM != seg1);
tl_assert(BOTTOM != seg2 && UNKNOWN != seg2);
if (xml) {
emit( " <what>Invalid arguments to %s</what>\n",
xe->XE.Arith.opname );
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
if (seg1 != seg2) {
if (NONPTR == seg1) {
emit( " <auxwhat>First arg not a pointer</auxwhat>\n" );
} else if (UNKNOWN == seg1) {
emit( " <auxwhat>First arg may be a pointer</auxwhat>\n" );
} else {
emit( " <auxwhat>First arg derived from address %#lx of "
"%lu-byte block alloc'd</auxwhat>\n",
Seg__addr(seg1), Seg__size(seg1) );
VG_(pp_ExeContext)(Seg__where(seg1));
}
which = "Second arg";
} else {
which = "Both args";
}
if (NONPTR == seg2) {
emit( " <auxwhat>%s not a pointer</auxwhat>\n", which );
} else {
emit( " <auxwhat>%s derived from address %#lx of "
"%lu-byte block alloc'd</auxwhat>\n",
which, Seg__addr(seg2), Seg__size(seg2) );
VG_(pp_ExeContext)(Seg__where(seg2));
}
} else {
emit( "Invalid arguments to %s\n",
xe->XE.Arith.opname );
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
if (seg1 != seg2) {
if (NONPTR == seg1) {
emit( " First arg not a pointer\n" );
} else if (UNKNOWN == seg1) {
emit( " First arg may be a pointer\n" );
} else {
emit( " First arg derived from address %#lx of "
"%lu-byte block alloc'd\n",
Seg__addr(seg1), Seg__size(seg1) );
VG_(pp_ExeContext)(Seg__where(seg1));
}
which = "Second arg";
} else {
which = "Both args";
}
if (NONPTR == seg2) {
emit( " %s not a pointer\n", which );
} else {
emit( " %s derived from address %#lx of "
"%lu-byte block alloc'd\n",
which, Seg__addr(seg2), Seg__size(seg2) );
VG_(pp_ExeContext)(Seg__where(seg2));
}
}
break;
}
//----------------------------------------------------------
case XE_SysParam: {
Addr lo = xe->XE.SysParam.lo;
Addr hi = xe->XE.SysParam.hi;
Seg* seglo = xe->XE.SysParam.seglo;
Seg* seghi = xe->XE.SysParam.seghi;
const HChar* s = VG_(get_error_string) (err);
const HChar* what;
tl_assert(BOTTOM != seglo && BOTTOM != seghi);
if (Vg_CoreSysCall == xe->XE.SysParam.part)
what = "Syscall param ";
else VG_(tool_panic)("bad CorePart");
if (seglo == seghi) {
// freed block
tl_assert(is_known_segment(seglo));
tl_assert(Seg__is_freed(seglo)); // XXX what if it's now recycled?
if (xml) {
emit( " <what>%s%s contains unaddressable byte(s)</what>\n",
what, s );
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
emit( " <auxwhat>Address %#lx is %lu bytes inside a "
"%lu-byte block free'd</auxwhat>\n",
lo, lo-Seg__addr(seglo), Seg__size(seglo) );
VG_(pp_ExeContext)(Seg__where(seglo));
} else {
emit( " %s%s contains unaddressable byte(s)\n",
what, s );
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
emit( " Address %#lx is %lu bytes inside a "
"%lu-byte block free'd\n",
lo, lo-Seg__addr(seglo), Seg__size(seglo) );
VG_(pp_ExeContext)(Seg__where(seglo));
}
} else {
// mismatch
if (xml) {
emit( " <what>%s%s is non-contiguous</what>\n",
what, s );
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
if (UNKNOWN == seglo) {
emit( " <auxwhat>First byte is "
"not inside a known block</auxwhat>\n" );
} else {
emit( " <auxwhat>First byte (%#lx) is %lu bytes inside a "
"%lu-byte block alloc'd</auxwhat>\n",
lo, lo-Seg__addr(seglo), Seg__size(seglo) );
VG_(pp_ExeContext)(Seg__where(seglo));
}
if (UNKNOWN == seghi) {
emit( " <auxwhat>Last byte is "
"not inside a known block</auxwhat>\n" );
} else {
emit( " <auxwhat>Last byte (%#lx) is %lu bytes inside a "
"%lu-byte block alloc'd</auxwhat>\n",
hi, hi-Seg__addr(seghi), Seg__size(seghi) );
VG_(pp_ExeContext)(Seg__where(seghi));
}
} else {
emit( "%s%s is non-contiguous\n",
what, s );
VG_(pp_ExeContext)( VG_(get_error_where)(err) );
if (UNKNOWN == seglo) {
emit( " First byte is not inside a known block\n" );
} else {
emit( " First byte (%#lx) is %lu bytes inside a "
"%lu-byte block alloc'd\n",
lo, lo-Seg__addr(seglo), Seg__size(seglo) );
VG_(pp_ExeContext)(Seg__where(seglo));
}
if (UNKNOWN == seghi) {
emit( " Last byte is not inside a known block\n" );
} else {
emit( " Last byte (%#lx) is %lu bytes inside a "
"%lu-byte block alloc'd\n",
hi, hi-Seg__addr(seghi), Seg__size(seghi) );
VG_(pp_ExeContext)(Seg__where(seghi));
}
}
}
break;
}
default:
VG_(tool_panic)("pp_Error: unrecognised error kind");
}
}
UInt pc_update_Error_extra ( const Error* err )
{
XError *xe = (XError*)VG_(get_error_extra)(err);
tl_assert(xe);
switch (xe->tag) {
case XE_SorG:
break;
case XE_Heap: {
Bool have_descr;
xe->XE.Heap.datasymoff = 0;
xe->XE.Heap.datasym = NULL;
tl_assert(!xe->XE.Heap.descr1);
tl_assert(!xe->XE.Heap.descr2);
xe->XE.Heap.descr1
= VG_(newXA)( VG_(malloc), "pc.update_extra.Heap.descr1",
VG_(free), sizeof(HChar) );
xe->XE.Heap.descr2
= VG_(newXA)( VG_(malloc), "pc.update_extra.Heap.descr1",
VG_(free), sizeof(HChar) );
xe->XE.Heap.datasymoff = 0;
have_descr
= VG_(get_data_description)( xe->XE.Heap.descr1,
xe->XE.Heap.descr2,
xe->XE.Heap.addr );
/* If there's nothing in descr1/2, free it. Why is it safe to
to VG_(indexXA) at zero here? Because
VG_(get_data_description) guarantees to zero terminate
descr1/2 regardless of the outcome of the call. So there's
always at least one element in each XA after the call.
*/
if (0 == VG_(strlen)( VG_(indexXA)( xe->XE.Heap.descr1, 0 ))
|| !have_descr) {
VG_(deleteXA)( xe->XE.Heap.descr1 );
xe->XE.Heap.descr1 = NULL;
}
if (0 == VG_(strlen)( VG_(indexXA)( xe->XE.Heap.descr2, 0 ))
|| !have_descr) {
VG_(deleteXA)( xe->XE.Heap.descr2 );
xe->XE.Heap.descr2 = NULL;
}
/* If Dwarf3 info produced nothing useful, see at least if
we can fish something useful out of the ELF symbol info. */
if (!have_descr) {
const HChar *name;
if (VG_(get_datasym_and_offset)(
xe->XE.Heap.addr, &name,
&xe->XE.Heap.datasymoff )
) {
xe->XE.Heap.datasym =
VG_(strdup)("pc.update_extra.Heap.datasym", name);
}
}
break;
}
case XE_Arith:
break;
case XE_SysParam:
break;
default:
VG_(tool_panic)("update_extra");
}
return sizeof(XError);
}
Bool pc_is_recognised_suppression ( const HChar* name, Supp *su )
{
SuppKind skind;
if (VG_STREQ(name, "SorG")) skind = XS_SorG;
else if (VG_STREQ(name, "Heap")) skind = XS_Heap;
else if (VG_STREQ(name, "Arith")) skind = XS_Arith;
else if (VG_STREQ(name, "SysParam")) skind = XS_SysParam;
else
return False;
VG_(set_supp_kind)(su, skind);
return True;
}
Bool pc_read_extra_suppression_info ( Int fd, HChar** bufpp,
SizeT* nBufp, Int* lineno,
Supp* su )
{
Bool eof;
if (VG_(get_supp_kind)(su) == XS_SysParam) {
eof = VG_(get_line) ( fd, bufpp, nBufp, lineno );
if (eof) return False;
VG_(set_supp_string)(su, VG_(strdup)("pc.common.presi.1", *bufpp));
}
return True;
}
Bool pc_error_matches_suppression (const Error* err, const Supp* su)
{
ErrorKind ekind = VG_(get_error_kind)(err);
switch (VG_(get_supp_kind)(su)) {
case XS_SorG: return ekind == XE_SorG;
case XS_Heap: return ekind == XE_Heap;
case XS_Arith: return ekind == XE_Arith;
case XS_SysParam: return ekind == XE_SysParam;
default:
VG_(printf)("Error:\n"
" unknown suppression type %d\n",
VG_(get_supp_kind)(su));
VG_(tool_panic)("unknown suppression type in "
"pc_error_matches_suppression");
}
}
const HChar* pc_get_error_name ( const Error* err )
{
XError *xe = (XError*)VG_(get_error_extra)(err);
tl_assert(xe);
switch (xe->tag) {
case XE_SorG: return "SorG";
case XE_Heap: return "Heap";
case XE_Arith: return "Arith";
case XE_SysParam: return "SysParam";
default: VG_(tool_panic)("get_error_name: unexpected type");
}
}
SizeT pc_get_extra_suppression_info ( const Error* err,
/*OUT*/HChar* buf, Int nBuf )
{
ErrorKind ekind = VG_(get_error_kind )(err);
tl_assert(buf);
tl_assert(nBuf >= 1);
if (XE_SysParam == ekind) {
const HChar* errstr = VG_(get_error_string)(err);
tl_assert(errstr);
return VG_(snprintf)(buf, nBuf, "%s", errstr);
} else {
buf[0] = '\0';
return 0;
}
}
SizeT pc_print_extra_suppression_use ( const Supp* su,
/*OUT*/HChar* buf, Int nBuf )
{
tl_assert(nBuf >= 1);
buf[0] = '\0';
return 0;
}
void pc_update_extra_suppression_use (const Error* err, const Supp* su)
{
return;
}
/*--------------------------------------------------------------------*/
/*--- end pc_common.c ---*/
/*--------------------------------------------------------------------*/
@@ -0,0 +1,78 @@
/*--------------------------------------------------------------------*/
/*--- Ptrcheck: a pointer-use checker. ---*/
/*--- Exports for stuff shared between sg_ and h_ subtools. ---*/
/*--- pc_common.h ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Ptrcheck, a Valgrind tool for checking pointer
use in programs.
Copyright (C) 2008-2015 OpenWorks Ltd
info@open-works.co.uk
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
#ifndef __PC_COMMON_H
#define __PC_COMMON_H
typedef struct _Seg Seg; /* abstract every except in h_main.c */
void sg_record_error_SorG ( ThreadId tid,
Addr addr, SSizeT sszB,
HChar* expect, HChar* actual, HChar* delta );
void h_record_heap_error( Addr a, SizeT size, Seg* vseg, Bool is_write );
void h_record_arith_error( Seg* seg1, Seg* seg2, HChar* opname );
void h_record_sysparam_error( ThreadId tid, CorePart part, const HChar* s,
Addr lo, Addr hi, Seg* seglo, Seg* seghi );
Bool pc_eq_Error ( VgRes res, const Error* e1, const Error* e2 );
void pc_before_pp_Error ( const Error* err );
void pc_pp_Error ( const Error* err );
UInt pc_update_Error_extra ( const Error* err );
Bool pc_is_recognised_suppression ( const HChar* name, Supp *su );
Bool pc_read_extra_suppression_info ( Int fd, HChar** bufpp,
SizeT* nBufp, Int* lineno, Supp* su );
Bool pc_error_matches_suppression (const Error* err, const Supp* su);
const HChar* pc_get_error_name ( const Error* err );
SizeT pc_get_extra_suppression_info ( const Error* err,
/*OUT*/HChar* buf, Int nBuf );
SizeT pc_print_extra_suppression_use ( const Supp* su,
/*OUT*/HChar* buf, Int nBuf );
void pc_update_extra_suppression_use (const Error* err, const Supp* su);
extern Bool h_clo_partial_loads_ok;
/* extern Bool h_clo_lossage_check; */
extern Bool sg_clo_enable_sg_checks;
Bool pc_process_cmd_line_options(const HChar* arg);
void pc_print_usage(void);
void pc_print_debug_usage(void);
#endif
/*--------------------------------------------------------------------*/
/*--- end pc_common.h ---*/
/*--------------------------------------------------------------------*/
@@ -0,0 +1,164 @@
/*--------------------------------------------------------------------*/
/*--- Ptrcheck: a pointer-use checker. ---*/
/*--- This file coordinates the h_ and sg_ subtools. ---*/
/*--- pc_main.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Ptrcheck, a Valgrind tool for checking pointer
use in programs.
Copyright (C) 2008-2015 OpenWorks Ltd
info@open-works.co.uk
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
Neither the names of the U.S. Department of Energy nor the
University of California nor the names of its contributors may be
used to endorse or promote products derived from this software
without prior written permission.
*/
#include "pub_tool_basics.h"
#include "pub_tool_libcassert.h"
#include "pub_tool_libcprint.h"
#include "pub_tool_execontext.h"
#include "pub_tool_tooliface.h"
#include "pub_tool_options.h"
#include "sg_main.h"
#include "pc_common.h"
#include "h_main.h"
//////////////////////////////////////////////////////////////
// //
// main //
// //
//////////////////////////////////////////////////////////////
static void pc_pre_clo_init(void)
{
#if defined(VGO_darwin)
// This makes the (all-failing) regtests run much faster.
VG_(printf)("SGCheck doesn't work on Darwin yet, sorry.\n");
VG_(exit)(1);
#endif
#if defined(VGA_s390x)
/* fixs390: to be done. */
VG_(printf)("SGCheck doesn't work on s390x yet, sorry.\n");
VG_(exit)(1);
#endif
#if defined(VGA_ppc32) || defined(VGA_ppc64be) || defined(VGA_ppc64le)
VG_(printf)("SGCheck doesn't work on PPC yet, sorry.\n");
VG_(exit)(1);
#endif
#if defined(VGA_arm) || defined(VGA_arm64)
VG_(printf)("SGCheck doesn't work on ARM yet, sorry.\n");
VG_(exit)(1);
#endif
#if defined(VGA_mips32) || defined(VGA_mips64)
VG_(printf)("SGCheck doesn't work on MIPS yet, sorry.\n");
VG_(exit)(1);
#endif
#if defined(VGA_tilegx)
VG_(printf)("SGCheck doesn't work on TileGx yet, sorry.\n");
VG_(exit)(1);
#endif
// Can't change the name until we change the names in suppressions
// too.
VG_(details_name) ("exp-sgcheck");
VG_(details_version) (NULL);
VG_(details_description) ("a stack and global array "
"overrun detector");
VG_(details_copyright_author)(
"Copyright (C) 2003-2015, and GNU GPL'd, by OpenWorks Ltd et al.");
VG_(details_bug_reports_to) (VG_BUGS_TO);
VG_(details_avg_translation_sizeB) ( 496 );
VG_(basic_tool_funcs) (sg_post_clo_init,
h_instrument,
sg_fini);
VG_(needs_malloc_replacement)( h_replace_malloc,
h_replace___builtin_new,
h_replace___builtin_vec_new,
h_replace_memalign,
h_replace_calloc,
h_replace_free,
h_replace___builtin_delete,
h_replace___builtin_vec_delete,
h_replace_realloc,
h_replace_malloc_usable_size,
0 /* no need for client heap redzones */ );
VG_(needs_var_info) ();
VG_(needs_core_errors) ();
VG_(needs_tool_errors) (pc_eq_Error,
pc_before_pp_Error,
pc_pp_Error,
True,/*show TIDs for errors*/
pc_update_Error_extra,
pc_is_recognised_suppression,
pc_read_extra_suppression_info,
pc_error_matches_suppression,
pc_get_error_name,
pc_get_extra_suppression_info,
pc_print_extra_suppression_use,
pc_update_extra_suppression_use);
VG_(needs_xml_output) ();
//VG_(needs_syscall_wrapper)( h_pre_syscall,
// h_post_syscall );
VG_(needs_command_line_options)( pc_process_cmd_line_options,
pc_print_usage,
pc_print_debug_usage );
VG_(track_die_mem_stack) ( sg_die_mem_stack );
VG_(track_pre_thread_ll_create) ( sg_pre_thread_ll_create );
VG_(track_pre_thread_first_insn)( sg_pre_thread_first_insn );
VG_(track_new_mem_mmap) ( sg_new_mem_mmap );
VG_(track_new_mem_startup) ( sg_new_mem_startup);
VG_(track_die_mem_munmap) ( sg_die_mem_munmap );
/* Really we ought to give handlers for these, to
check that syscalls don't read across array boundaries. */
/*
VG_(track_pre_mem_read) ( NULL );
VG_(track_pre_mem_read_asciiz) ( NULL );
VG_(track_pre_mem_write) ( NULL );
*/
sg_pre_clo_init();
VG_(clo_vex_control).iropt_unroll_thresh = 0;
VG_(clo_vex_control).guest_chase_thresh = 0;
}
VG_DETERMINE_INTERFACE_VERSION(pc_pre_clo_init)
/*--------------------------------------------------------------------*/
/*--- end pc_main.c ---*/
/*--------------------------------------------------------------------*/
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,78 @@
/*--------------------------------------------------------------------*/
/*--- Ptrcheck: a pointer-use checker. ---*/
/*--- Exports for stack and global access checking. ---*/
/*--- sg_main.h ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Ptrcheck, a Valgrind tool for checking pointer
use in programs.
Copyright (C) 2008-2015 OpenWorks Ltd
info@open-works.co.uk
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file COPYING.
*/
#ifndef __SG_MAIN_H
#define __SG_MAIN_H
void sg_pre_clo_init ( void );
void sg_post_clo_init ( void );
void sg_fini(Int exitcode);
void sg_die_mem_stack ( Addr old_SP, SizeT len );
void sg_pre_thread_ll_create ( ThreadId parent, ThreadId child );
void sg_pre_thread_first_insn ( ThreadId tid );
void sg_new_mem_mmap( Addr a, SizeT len,
Bool rr, Bool ww, Bool xx, ULong di_handle );
void sg_new_mem_startup( Addr a, SizeT len,
Bool rr, Bool ww, Bool xx, ULong di_handle );
void sg_die_mem_munmap ( Addr a, SizeT len );
/* These really ought to be moved elsewhere, so that we don't have to
include this file in h_main.c. See comments in sg_main.c and
h_main.c for what this is about. */
struct _SGEnv; /* abstract export */
struct _SGEnv* sg_instrument_init ( IRTemp (*newIRTemp_cb)(IRType,void*),
void* newIRTemp_opaque );
void sg_instrument_fini ( struct _SGEnv * env );
void sg_instrument_IRStmt ( /*MOD*/struct _SGEnv * env,
/*MOD*/IRSB* sbOut,
IRStmt* st,
const VexGuestLayout* layout,
IRType gWordTy, IRType hWordTy );
void sg_instrument_final_jump ( /*MOD*/struct _SGEnv * env,
/*MOD*/IRSB* sbOut,
IRExpr* next,
IRJumpKind jumpkind,
const VexGuestLayout* layout,
IRType gWordTy, IRType hWordTy );
#endif
/*--------------------------------------------------------------------*/
/*--- end sg_main.h ---*/
/*--------------------------------------------------------------------*/
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
@@ -0,0 +1 @@
# dummy
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,69 @@
include $(top_srcdir)/Makefile.tool-tests.am
dist_noinst_SCRIPTS = filter_stderr filter_add filter_suppgen
EXTRA_DIST = \
is_arch_supported \
bad_percentify.vgtest bad_percentify.c \
bad_percentify.stdout.exp bad_percentify.stderr.exp-glibc28-amd64 \
globalerr.vgtest globalerr.stdout.exp \
globalerr.stderr.exp-glibc28-amd64 \
globalerr.stderr.exp-gcc491-amd64 \
hackedbz2.vgtest hackedbz2.stdout.exp \
hackedbz2.stderr.exp-glibc28-amd64 \
hsg.vgtest hsg.stdout.exp hsg.stderr.exp \
preen_invars.vgtest preen_invars.stdout.exp \
preen_invars.stderr.exp-glibc28-amd64 \
stackerr.vgtest stackerr.stdout.exp \
stackerr.stderr.exp-glibc28-amd64 stackerr.stderr.exp-glibc27-x86
check_PROGRAMS = \
bad_percentify \
globalerr hackedbz2 \
hsg \
preen_invars preen_invars_so.so \
stackerr
# DDD: not sure if these ones should work on Darwin or not... if not, should
# be moved into x86-linux/.
#if ! VGCONF_OS_IS_DARWIN
# check_PROGRAMS += \
# ccc
#endif
AM_CFLAGS += $(AM_FLAG_M3264_PRI)
AM_CXXFLAGS += $(AM_FLAG_M3264_PRI)
# To make it a bit more realistic, build hackedbz2.c with at
# least some optimisation.
hackedbz2_CFLAGS = $(AM_CFLAGS) -O -Wno-inline
globalerr_CFLAGS = $(AM_CFLAGS) @FLAG_W_NO_UNINITIALIZED@
# C ones
#pth_create_LDADD = -lpthread
# C++ ones
#ccc_SOURCES = ccc.cpp
# Build shared object for preen_invars
preen_invars_DEPENDENCIES = preen_invars_so.so
if VGCONF_OS_IS_DARWIN
preen_invars_LDADD = -ldl
preen_invars_LDFLAGS = $(AM_FLAG_M3264_PRI)
else
preen_invars_LDADD = -ldl
preen_invars_LDFLAGS = $(AM_FLAG_M3264_PRI) \
-Wl,-rpath,$(top_builddir)/memcheck/tests
endif
preen_invars_so_so_CFLAGS = $(AM_CFLAGS) -fpic
if VGCONF_OS_IS_DARWIN
preen_invars_so_so_LDFLAGS = -fpic $(AM_FLAG_M3264_PRI) -dynamic \
-dynamiclib -all_load
else
preen_invars_so_so_LDFLAGS = -fpic $(AM_FLAG_M3264_PRI) -shared \
-Wl,-soname -Wl,preen_invars_so.so
endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,647 @@
/* This demonstrates a stack overrun bug that exp-ptrcheck found while
running Valgrind itself (self hosting). As at 12 Sept 08 this bug
is still in Valgrind. */
#include <stdio.h>
#include <assert.h>
#include <stdarg.h>
typedef unsigned long long int ULong;
typedef signed long long int Long;
typedef unsigned int UInt;
typedef signed int Int;
typedef signed char Char;
typedef char HChar;
typedef unsigned long UWord;
typedef signed long Word;
typedef unsigned char Bool;
#define True ((Bool)1)
#define False ((Bool)0)
#define VG_(_str) VG_##_str
/* ---------------------------------------------------------------------
vg_sprintf, copied from m_libcprint.c
------------------------------------------------------------------ */
UInt
VG_(debugLog_vprintf) (
void(*send)(HChar,void*),
void* send_arg2,
const HChar* format,
va_list vargs
);
/* ---------------------------------------------------------------------
printf() and friends
------------------------------------------------------------------ */
typedef
struct { Int fd; Bool is_socket; }
OutputSink;
OutputSink VG_(log_output_sink) = { 2, False }; /* 2 = stderr */
/* Do the low-level send of a message to the logging sink. */
static
void send_bytes_to_logging_sink ( OutputSink* sink, HChar* msg, Int nbytes )
{
fwrite(msg, 1, nbytes, stdout);
fflush(stdout);
}
/* --------- printf --------- */
typedef
struct {
HChar buf[512];
Int buf_used;
OutputSink* sink;
}
printf_buf_t;
// Adds a single char to the buffer. When the buffer gets sufficiently
// full, we write its contents to the logging sink.
static void add_to__printf_buf ( HChar c, void *p )
{
printf_buf_t *b = (printf_buf_t *)p;
if (b->buf_used > sizeof(b->buf) - 2 ) {
send_bytes_to_logging_sink( b->sink, b->buf, b->buf_used );
b->buf_used = 0;
}
b->buf[b->buf_used++] = c;
b->buf[b->buf_used] = 0;
assert(b->buf_used < sizeof(b->buf));
}
__attribute__((noinline))
static UInt vprintf_to_buf ( printf_buf_t* b,
const HChar *format, va_list vargs )
{
UInt ret = 0;
if (b->sink->fd >= 0 || b->sink->fd == -2) {
ret = VG_(debugLog_vprintf)
( add_to__printf_buf, b, format, vargs );
}
return ret;
}
__attribute__((noinline))
static UInt vprintf_WRK ( OutputSink* sink,
const HChar *format, va_list vargs )
{
printf_buf_t myprintf_buf
= { "", 0, sink };
UInt ret;
ret = vprintf_to_buf(&myprintf_buf, format, vargs);
// Write out any chars left in the buffer.
if (myprintf_buf.buf_used > 0) {
send_bytes_to_logging_sink( myprintf_buf.sink,
myprintf_buf.buf,
myprintf_buf.buf_used );
}
return ret;
}
__attribute__((noinline))
UInt VG_(vprintf) ( const HChar *format, va_list vargs )
{
return vprintf_WRK( &VG_(log_output_sink), format, vargs );
}
__attribute__((noinline))
UInt VG_(printf) ( const HChar *format, ... )
{
UInt ret;
va_list vargs;
va_start(vargs, format);
ret = VG_(vprintf)(format, vargs);
va_end(vargs);
return ret;
}
static Bool toBool ( Int x ) {
Int r = (x == 0) ? False : True;
return (Bool)r;
}
__attribute__((noinline))
static Int local_strlen ( const HChar* str )
{
Int i = 0;
while (str[i] != 0) i++;
return i;
}
__attribute__((noinline))
static HChar local_toupper ( HChar c )
{
if (c >= 'a' && c <= 'z')
return c + ('A' - 'a');
else
return c;
}
/*------------------------------------------------------------*/
/*--- A simple, generic, vprintf implementation. ---*/
/*------------------------------------------------------------*/
/* -----------------------------------------------
Distantly derived from:
vprintf replacement for Checker.
Copyright 1993, 1994, 1995 Tristan Gingold
Written September 1993 Tristan Gingold
Tristan Gingold, 8 rue Parmentier, F-91120 PALAISEAU, FRANCE
(Checker itself was GPL'd.)
----------------------------------------------- */
/* Some flags. */
#define VG_MSG_SIGNED 1 /* The value is signed. */
#define VG_MSG_ZJUSTIFY 2 /* Must justify with '0'. */
#define VG_MSG_LJUSTIFY 4 /* Must justify on the left. */
#define VG_MSG_PAREN 8 /* Parenthesize if present (for %y) */
#define VG_MSG_COMMA 16 /* Add commas to numbers (for %d, %u) */
#define VG_MSG_ALTFORMAT 32 /* Convert the value to alternate format */
/* Copy a string into the buffer. */
static __attribute__((noinline))
UInt myvprintf_str ( void(*send)(HChar,void*),
void* send_arg2,
Int flags,
Int width,
HChar* str,
Bool capitalise )
{
# define MAYBE_TOUPPER(ch) (capitalise ? local_toupper(ch) : (ch))
UInt ret = 0;
Int i, extra;
Int len = local_strlen(str);
if (width == 0) {
ret += len;
for (i = 0; i < len; i++)
send(MAYBE_TOUPPER(str[i]), send_arg2);
return ret;
}
if (len > width) {
ret += width;
for (i = 0; i < width; i++)
send(MAYBE_TOUPPER(str[i]), send_arg2);
return ret;
}
extra = width - len;
if (flags & VG_MSG_LJUSTIFY) {
ret += extra;
for (i = 0; i < extra; i++)
send(' ', send_arg2);
}
ret += len;
for (i = 0; i < len; i++)
send(MAYBE_TOUPPER(str[i]), send_arg2);
if (!(flags & VG_MSG_LJUSTIFY)) {
ret += extra;
for (i = 0; i < extra; i++)
send(' ', send_arg2);
}
# undef MAYBE_TOUPPER
return ret;
}
/* Copy a string into the buffer, escaping bad XML chars. */
static
UInt myvprintf_str_XML_simplistic ( void(*send)(HChar,void*),
void* send_arg2,
HChar* str )
{
UInt ret = 0;
Int i;
Int len = local_strlen(str);
HChar* alt;
for (i = 0; i < len; i++) {
switch (str[i]) {
case '&': alt = "&amp;"; break;
case '<': alt = "&lt;"; break;
case '>': alt = "&gt;"; break;
default: alt = NULL;
}
if (alt) {
while (*alt) {
send(*alt, send_arg2);
ret++;
alt++;
}
} else {
send(str[i], send_arg2);
ret++;
}
}
return ret;
}
/* Write P into the buffer according to these args:
* If SIGN is true, p is a signed.
* BASE is the base.
* If WITH_ZERO is true, '0' must be added.
* WIDTH is the width of the field.
*/
static
UInt myvprintf_int64 ( void(*send)(HChar,void*),
void* send_arg2,
Int flags,
Int base,
Int width,
Bool capitalised,
ULong p )
{
HChar buf[40];
Int ind = 0;
Int i, nc = 0;
Bool neg = False;
HChar* digits = capitalised ? "0123456789ABCDEF" : "0123456789abcdef";
UInt ret = 0;
if (base < 2 || base > 16)
return ret;
if ((flags & VG_MSG_SIGNED) && (Long)p < 0) {
p = - (Long)p;
neg = True;
}
if (p == 0)
buf[ind++] = '0';
else {
while (p > 0) {
if (flags & VG_MSG_COMMA && 10 == base &&
0 == (ind-nc) % 3 && 0 != ind)
{
buf[ind++] = ',';
nc++;
}
buf[ind++] = digits[p % base];
p /= base;
}
}
if (neg)
buf[ind++] = '-';
if (width > 0 && !(flags & VG_MSG_LJUSTIFY)) {
for(; ind < width; ind++) {
/* assert(ind < 39); */
if (ind > 39) {
buf[39] = 0;
break;
}
buf[ind] = (flags & VG_MSG_ZJUSTIFY) ? '0': ' ';
}
}
/* Reverse copy to buffer. */
ret += ind;
for (i = ind -1; i >= 0; i--) {
send(buf[i], send_arg2);
}
if (width > 0 && (flags & VG_MSG_LJUSTIFY)) {
for(; ind < width; ind++) {
ret++;
/* Never pad with zeroes on RHS -- changes the value! */
send(' ', send_arg2);
}
}
return ret;
}
/* A simple vprintf(). */
/* EXPORTED */
__attribute__((noinline))
UInt
VG_(debugLog_vprintf) (
void(*send)(HChar,void*),
void* send_arg2,
const HChar* format,
va_list vargs
)
{
UInt ret = 0;
Int i;
Int flags;
Int width;
Int n_ls = 0;
Bool is_long, caps;
/* We assume that vargs has already been initialised by the
caller, using va_start, and that the caller will similarly
clean up with va_end.
*/
for (i = 0; format[i] != 0; i++) {
if (format[i] != '%') {
send(format[i], send_arg2);
ret++;
continue;
}
i++;
/* A '%' has been found. Ignore a trailing %. */
if (format[i] == 0)
break;
if (format[i] == '%') {
/* '%%' is replaced by '%'. */
send('%', send_arg2);
ret++;
continue;
}
flags = 0;
n_ls = 0;
width = 0; /* length of the field. */
while (1) {
switch (format[i]) {
case '(':
flags |= VG_MSG_PAREN;
break;
case ',':
case '\'':
/* If ',' or '\'' follows '%', commas will be inserted. */
flags |= VG_MSG_COMMA;
break;
case '-':
/* If '-' follows '%', justify on the left. */
flags |= VG_MSG_LJUSTIFY;
break;
case '0':
/* If '0' follows '%', pads will be inserted. */
flags |= VG_MSG_ZJUSTIFY;
break;
case '#':
/* If '#' follows '%', alternative format will be used. */
flags |= VG_MSG_ALTFORMAT;
break;
default:
goto parse_fieldwidth;
}
i++;
}
parse_fieldwidth:
/* Compute the field length. */
while (format[i] >= '0' && format[i] <= '9') {
width *= 10;
width += format[i++] - '0';
}
while (format[i] == 'l') {
i++;
n_ls++;
}
// %d means print a 32-bit integer.
// %ld means print a word-size integer.
// %lld means print a 64-bit integer.
if (0 == n_ls) { is_long = False; }
else if (1 == n_ls) { is_long = ( sizeof(void*) == sizeof(Long) ); }
else { is_long = True; }
switch (format[i]) {
case 'o': /* %o */
if (flags & VG_MSG_ALTFORMAT) {
ret += 2;
send('0',send_arg2);
}
if (is_long)
ret += myvprintf_int64(send, send_arg2, flags, 8, width, False,
(ULong)(va_arg (vargs, ULong)));
else
ret += myvprintf_int64(send, send_arg2, flags, 8, width, False,
(ULong)(va_arg (vargs, UInt)));
break;
case 'd': /* %d */
flags |= VG_MSG_SIGNED;
if (is_long)
ret += myvprintf_int64(send, send_arg2, flags, 10, width, False,
(ULong)(va_arg (vargs, Long)));
else
ret += myvprintf_int64(send, send_arg2, flags, 10, width, False,
(ULong)(va_arg (vargs, Int)));
break;
case 'u': /* %u */
if (is_long)
ret += myvprintf_int64(send, send_arg2, flags, 10, width, False,
(ULong)(va_arg (vargs, ULong)));
else
ret += myvprintf_int64(send, send_arg2, flags, 10, width, False,
(ULong)(va_arg (vargs, UInt)));
break;
case 'p':
if (format[i+1] == 'S') {
i++;
/* %pS, like %s but escaping chars for XML safety */
/* Note: simplistic; ignores field width and flags */
char *str = va_arg (vargs, char *);
if (str == (char*) 0)
str = "(null)";
ret += myvprintf_str_XML_simplistic(send, send_arg2, str);
} else {
/* %p */
ret += 2;
send('0',send_arg2);
send('x',send_arg2);
ret += myvprintf_int64(send, send_arg2, flags, 16, width, True,
(ULong)((UWord)va_arg (vargs, void *)));
}
break;
case 'x': /* %x */
case 'X': /* %X */
caps = toBool(format[i] == 'X');
if (flags & VG_MSG_ALTFORMAT) {
ret += 2;
send('0',send_arg2);
send('x',send_arg2);
}
if (is_long)
ret += myvprintf_int64(send, send_arg2, flags, 16, width, caps,
(ULong)(va_arg (vargs, ULong)));
else
ret += myvprintf_int64(send, send_arg2, flags, 16, width, caps,
(ULong)(va_arg (vargs, UInt)));
break;
case 'c': /* %c */
ret++;
send(va_arg (vargs, int), send_arg2);
break;
case 's': case 'S': { /* %s */
char *str = va_arg (vargs, char *);
if (str == (char*) 0) str = "(null)";
ret += myvprintf_str(send, send_arg2,
flags, width, str, format[i]=='S');
break;
}
// case 'y': { /* %y - print symbol */
// Addr a = va_arg(vargs, Addr);
//
//
//
// HChar *name;
// if (VG_(get_fnname_w_offset)(a, &name)) {
// HChar buf[1 + VG_strlen(name) + 1 + 1];
// if (flags & VG_MSG_PAREN) {
// VG_(sprintf)(str, "(%s)", name):
// } else {
// VG_(sprintf)(str, "%s", name):
// }
// ret += myvprintf_str(send, flags, width, buf, 0);
// }
// break;
// }
default:
break;
}
}
return ret;
}
static void add_to__sprintf_buf ( HChar c, void *p )
{
HChar** b = p;
*(*b)++ = c;
}
UInt VG_(vsprintf) ( HChar* buf, const HChar *format, va_list vargs )
{
Int ret;
HChar* sprintf_ptr = buf;
ret = VG_(debugLog_vprintf)
( add_to__sprintf_buf, &sprintf_ptr, format, vargs );
add_to__sprintf_buf('\0', &sprintf_ptr);
assert(local_strlen(buf) == ret);
return ret;
}
UInt VG_(sprintf) ( HChar* buf, const HChar *format, ... )
{
UInt ret;
va_list vargs;
va_start(vargs,format);
ret = VG_(vsprintf)(buf, format, vargs);
va_end(vargs);
return ret;
}
/* ---------------------------------------------------------------------
percentify()
------------------------------------------------------------------ */
/* This part excerpted from coregrind/m_libcbase.c */
// Percentify n/m with d decimal places. Includes the '%' symbol at the end.
// Right justifies in 'buf'.
__attribute__((noinline))
void VG_percentify(ULong n, ULong m, UInt d, Int n_buf, HChar buf[])
{
Int i, len, space;
ULong p1;
HChar fmt[32];
if (m == 0) {
// Have to generate the format string in order to be flexible about
// the width of the field.
VG_(sprintf)(fmt, "%%-%ds", n_buf);
// fmt is now "%<n_buf>s" where <d> is 1,2,3...
VG_(sprintf)(buf, fmt, "--%");
return;
}
p1 = (100*n) / m;
if (d == 0) {
VG_(sprintf)(buf, "%lld%%", p1);
} else {
ULong p2;
UInt ex;
switch (d) {
case 1: ex = 10; break;
case 2: ex = 100; break;
case 3: ex = 1000; break;
default: assert(0);
/* was: VG_(tool_panic)("Currently can only handle 3 decimal places"); */
}
p2 = ((100*n*ex) / m) % ex;
// Have to generate the format string in order to be flexible about
// the width of the post-decimal-point part.
VG_(sprintf)(fmt, "%%lld.%%0%dlld%%%%", d);
// fmt is now "%lld.%0<d>lld%%" where <d> is 1,2,3...
VG_(sprintf)(buf, fmt, p1, p2);
}
len = local_strlen(buf);
space = n_buf - len;
if (space < 0) space = 0; /* Allow for v. small field_width */
i = len;
/* Right justify in field */
for ( ; i >= 0; i--) buf[i + space] = buf[i];
for (i = 0; i < space; i++) buf[i] = ' ';
}
/*------------------------------------------------------------*/
/*--- Stats ---*/
/*------------------------------------------------------------*/
/* This part excerpted from coregrind/m_translate.c */
static UInt n_SP_updates_fast = 0;
static UInt n_SP_updates_generic_known = 0;
static UInt n_SP_updates_generic_unknown = 0;
__attribute__((noinline))
void VG_print_translation_stats ( void )
{
HChar buf[6];
UInt n_SP_updates = n_SP_updates_fast + n_SP_updates_generic_known
+ n_SP_updates_generic_unknown;
VG_percentify(n_SP_updates_fast, n_SP_updates, 1, 6, buf);
VG_(printf)(
"translate: fast SP updates identified: %'u (%s)\n",
n_SP_updates_fast, buf );
VG_percentify(n_SP_updates_generic_known, n_SP_updates, 1, 6, buf);
VG_(printf)(
"translate: generic_known SP updates identified: %'u (%s)\n",
n_SP_updates_generic_known, buf );
VG_percentify(n_SP_updates_generic_unknown, n_SP_updates, 1, 6, buf);
VG_(printf)(
"translate: generic_unknown SP updates identified: %'u (%s)\n",
n_SP_updates_generic_unknown, buf );
}
int main ( void )
{
VG_print_translation_stats();
return 0;
}
@@ -0,0 +1,30 @@
Invalid read of size 1
at 0x........: local_strlen (bad_percentify.c:138)
by 0x........: VG_vsprintf (bad_percentify.c:535)
by 0x........: VG_sprintf (bad_percentify.c:545)
by 0x........: VG_percentify (bad_percentify.c:572)
by 0x........: VG_print_translation_stats (bad_percentify.c:625)
by 0x........: main (bad_percentify.c:645)
Address 0x........ expected vs actual:
Expected: stack array "buf" of size 6 in frame 4 back from here
Actual: unknown
Actual: is 0 after Expected
Invalid read of size 1
at 0x........: local_strlen (bad_percentify.c:138)
by 0x........: myvprintf_str (bad_percentify.c:187)
by 0x........: VG_debugLog_vprintf (bad_percentify.c:490)
by 0x........: vprintf_to_buf (bad_percentify.c:89)
by 0x........: vprintf_WRK (bad_percentify.c:102)
by 0x........: VG_vprintf (bad_percentify.c:115)
by 0x........: VG_printf (bad_percentify.c:124)
by 0x........: VG_print_translation_stats (bad_percentify.c:626)
by 0x........: main (bad_percentify.c:645)
Address 0x........ expected vs actual:
Expected: stack array "buf" of size 6 in frame 7 back from here
Actual: unknown
Actual: is 0 after Expected
ERROR SUMMARY: 6 errors from 2 contexts (suppressed: 0 from 0)
@@ -0,0 +1,3 @@
translate: fast SP updates identified: 0 ( --%)
translate: generic_known SP updates identified: 0 ( --%)
translate: generic_unknown SP updates identified: 0 ( --%)
@@ -0,0 +1,2 @@
prereq: ./is_arch_supported && (../../tests/os_test linux || ../../tests/os_test solaris)
prog: bad_percentify
@@ -0,0 +1,8 @@
#! /bin/sh
dir=`dirname $0`
$dir/filter_stderr |
# Anonymise "before" distances (if greater than 9 bytes)
sed "s/Address 0x........ is [0-9][0-9]\+ bytes /Address 0x........ is ... bytes /"
@@ -0,0 +1,38 @@
#! /bin/sh
dir=`dirname $0`
$dir/../../tests/filter_stderr_basic |
# Anonymise addresses
$dir/../../tests/filter_addresses |
# Anonymise paths like "(in /foo/bar/libc-baz.so)"
sed "s/(in \/.*libc.*)$/(in \/...libc...)/" |
sed "s/(in \/.*libpthread.*)$/(in \/...libpthread...)/" |
# Anonymise paths like "__libc_start_main (../foo/bar/libc-quux.c:129)"
sed "s/__libc_\(.*\) (.*)$/__libc_\1 (...libc...)/" |
# Remove preambly stuff; also postambly stuff
sed \
-e "/^exp-sgcheck, a stack and global array overrun detector$/d" \
-e "/^NOTE: This is an Experimental-Class Valgrind Tool$/d" \
-e "/^Copyright (C) 2003-201., and GNU GPL'd, by OpenWorks Ltd et al.$/d" \
-e "/^For counts of detected and suppressed errors, rerun with: -v$/d" |
# Tidy up in cases where glibc (+ libdl + libpthread + ld) have
# been built with debugging information, hence source locs are present.
sed \
-e "s/ vfprintf (.*)/ .../" \
-e "s/ vsprintf (.*)/ .../" \
-e "s/ sprintf (.*)/ .../" \
-e "s/ printf (.*)/ .../" \
-e "s/ strdup (.*)/ .../" \
-e "s/(pthread_key_create.c:[0-9]*)/(in \/...libpthread...)/" \
-e "s/(genops.c:[0-9]*)/(in \/...libc...)/" \
-e "s/(syscall-template.S:[0-9]*)/(in \/...libc...)/" |
# Anonymise line numbers in h_intercepts.c.
sed "s/h_intercepts.c:[0-9]*/h_intercepts.c:.../"
@@ -0,0 +1,11 @@
#! /bin/sh
dir=`dirname $0`
$dir/filter_stderr |
# Anonymise "obj:" path
sed "s/obj:.*\/annelid\/tests\/supp/obj:*\/annelid\/tests\/supp/"
@@ -0,0 +1,15 @@
#include <stdio.h>
short a[7];
static short b[7];
int main ( void )
{
int i;
short sum;
for (i = 0; i < 7+1; i++) {
sum += a[i] * b[i];
}
return 1 & ((unsigned int)sum / 1000000);
}
@@ -0,0 +1,17 @@
Invalid read of size 2
at 0x........: main (globalerr.c:12)
Address 0x........ expected vs actual:
Expected: global array "a" of size 14 in object with soname "NONE"
Actual: unknown
Actual: is 0 after Expected
Invalid read of size 2
at 0x........: main (globalerr.c:12)
Address 0x........ expected vs actual:
Expected: global array "b" of size 14 in object with soname "NONE"
Actual: global array "a" of size 14 in object with soname "NONE"
Actual: is 0 after Expected
ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
@@ -0,0 +1,17 @@
Invalid read of size 2
at 0x........: main (globalerr.c:12)
Address 0x........ expected vs actual:
Expected: global array "a" of size 14 in object with soname "NONE"
Actual: unknown
Actual: is 0 after Expected
Invalid read of size 2
at 0x........: main (globalerr.c:12)
Address 0x........ expected vs actual:
Expected: global array "b" of size 14 in object with soname "NONE"
Actual: unknown
Actual: is 0 after Expected
ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
@@ -0,0 +1,2 @@
prereq: ./is_arch_supported && (../../tests/os_test linux || ../../tests/os_test solaris)
prog: globalerr
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,17 @@
Invalid read of size 1
at 0x........: vex_strlen (hackedbz2.c:1006)
by 0x........: add_to_myprintf_buf (hackedbz2.c:1284)
by 0x........: vex_printf (hackedbz2.c:1155)
by 0x........: BZ2_compressBlock (hackedbz2.c:4039)
by 0x........: handle_compress (hackedbz2.c:4761)
by 0x........: BZ2_bzCompress (hackedbz2.c:4831)
by 0x........: BZ2_bzBuffToBuffCompress (hackedbz2.c:5638)
by 0x........: main (hackedbz2.c:6484)
Address 0x........ expected vs actual:
Expected: global array "myprintf_buf" of size 70 in object with soname "NONE"
Actual: unknown
Actual: is 0 after Expected
ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
@@ -0,0 +1,70 @@
22323 bytes read
block 1: crc = 0xA212ABF8, combined CRC = 0xA212ABF8, size = 22373
too repetitive; using fallback sorting algorithm
22373 in block, 13504 after MTF & 1-2 coding, 79+2 syms in use
pass 1: size is 17143, grp uses are 38 62 2 92 6 71
pass 2: size is 6506, grp uses are 28 71 0 86 9 77
pass 3: size is 6479, grp uses are 26 70 0 81 11 83
pass 4: size is 6469, grp uses are 26 69 0 74 17 85
bytes: mapping 19, selectors 66, code lengths 134, codes 6465
final combined CRC = 0xA212ABF8
6710 after compression
bit 0 -5 DATA_ERROR_MAGIC
bit 1 -5 DATA_ERROR_MAGIC
bit 2 -5 DATA_ERROR_MAGIC
bit 3 -5 DATA_ERROR_MAGIC
bit 4 -5 DATA_ERROR_MAGIC
bit 5 -5 DATA_ERROR_MAGIC
bit 6 -5 DATA_ERROR_MAGIC
bit 7 -5 DATA_ERROR_MAGIC
bit 8 -5 DATA_ERROR_MAGIC
bit 9 -5 DATA_ERROR_MAGIC
bit 10 -5 DATA_ERROR_MAGIC
bit 11 -5 DATA_ERROR_MAGIC
bit 12 -5 DATA_ERROR_MAGIC
bit 13 -5 DATA_ERROR_MAGIC
bit 14 -5 DATA_ERROR_MAGIC
bit 15 -5 DATA_ERROR_MAGIC
bit 16 -5 DATA_ERROR_MAGIC
bit 17 -5 DATA_ERROR_MAGIC
bit 18 -5 DATA_ERROR_MAGIC
bit 19 -5 DATA_ERROR_MAGIC
bit 20 -5 DATA_ERROR_MAGIC
bit 21 -5 DATA_ERROR_MAGIC
bit 22 -5 DATA_ERROR_MAGIC
bit 23 -5 DATA_ERROR_MAGIC
bit 24 0 OK really ok!
bit 25 -5 DATA_ERROR_MAGIC
bit 26 -5 DATA_ERROR_MAGIC
bit 27 0 OK really ok!
bit 28 -5 DATA_ERROR_MAGIC
bit 29 -5 DATA_ERROR_MAGIC
bit 30 -5 DATA_ERROR_MAGIC
bit 31 -5 DATA_ERROR_MAGIC
bit 32 -4 DATA_ERROR
bit 33 -4 DATA_ERROR
bit 34 -4 DATA_ERROR
bit 35 -4 DATA_ERROR
bit 2412 -4 DATA_ERROR
bit 4789 -4 DATA_ERROR
bit 7166 -4 DATA_ERROR
bit 9543 -4 DATA_ERROR
bit 11920 -4 DATA_ERROR
bit 14297 -4 DATA_ERROR
bit 16674 -4 DATA_ERROR
bit 19051 -4 DATA_ERROR
bit 21428 -4 DATA_ERROR
bit 23805 -4 DATA_ERROR
bit 26182 -4 DATA_ERROR
bit 28559 -4 DATA_ERROR
bit 30936 -4 DATA_ERROR
bit 33313 -4 DATA_ERROR
bit 35690 -4 DATA_ERROR
bit 38067 -4 DATA_ERROR
bit 40444 -4 DATA_ERROR
bit 42821 -4 DATA_ERROR
bit 45198 -4 DATA_ERROR
bit 47575 -4 DATA_ERROR
bit 49952 -4 DATA_ERROR
bit 52329 -4 DATA_ERROR
all ok
@@ -0,0 +1,2 @@
prereq: ./is_arch_supported && (../../tests/os_test linux || ../../tests/os_test solaris)
prog: hackedbz2
@@ -0,0 +1,48 @@
/* A simple test to demonstrate heap, stack, and global overrun
detection. */
#include <stdio.h>
#include <stdlib.h>
short ga[100];
__attribute__((noinline))
int addup_wrongly ( short* arr )
{
int sum = 0, i;
for (i = 0; i <= 100; i++)
sum += (int)arr[i];
return sum;
}
__attribute__((noinline))
int do_other_stuff ( void )
{
short la[100];
return 123 + addup_wrongly(la);
}
__attribute__((noinline))
int do_stupid_malloc_stuff ( void )
{
int sum = 0;
unsigned char* duh = malloc(100 * sizeof(char));
sum += duh[-1];
free(duh);
sum += duh[50];
return sum;
}
int main ( void )
{
long s = addup_wrongly(ga);
s += do_other_stuff();
s += do_stupid_malloc_stuff();
if (s == 123456789) {
fprintf(stdout, "well, i never!\n");
} else {
fprintf(stdout, "boringly as expected\n");
}
return 0;
}
@@ -0,0 +1,116 @@
<?xml version="1.0"?>
<valgrindoutput>
<protocolversion>4</protocolversion>
<protocoltool>exp-sgcheck</protocoltool>
<preamble>
<line>...</line>
<line>...</line>
<line>...</line>
<line>...</line>
<line>...</line>
</preamble>
<pid>...</pid>
<ppid>...</ppid>
<tool>exp-sgcheck</tool>
<args>
<vargv>...</vargv>
<argv>
<exe>./hsg</exe>
</argv>
</args>
<status>
<state>RUNNING</state>
<time>...</time>
</status>
<error>
<unique>0x........</unique>
<tid>...</tid>
<kind>SorG</kind>
<what>Invalid read of size 2</what>
<stack>
<frame>
<ip>0x........</ip>
<obj>...</obj>
<fn>addup_wrongly</fn>
<dir>...</dir>
<file>hsg.c</file>
<line>...</line>
</frame>
<frame>
<ip>0x........</ip>
<obj>...</obj>
<fn>main</fn>
<dir>...</dir>
<file>hsg.c</file>
<line>...</line>
</frame>
</stack>
<auxwhat>Address 0x........ expected vs actual:</auxwhat>
<auxwhat>Expected: global array "ga" of size 200 in object with soname "NONE"</auxwhat>
<auxwhat>Actual: unknown</auxwhat>
</error>
<error>
<unique>0x........</unique>
<tid>...</tid>
<kind>SorG</kind>
<what>Invalid read of size 2</what>
<stack>
<frame>
<ip>0x........</ip>
<obj>...</obj>
<fn>addup_wrongly</fn>
<dir>...</dir>
<file>hsg.c</file>
<line>...</line>
</frame>
<frame>
<ip>0x........</ip>
<obj>...</obj>
<fn>do_other_stuff</fn>
<dir>...</dir>
<file>hsg.c</file>
<line>...</line>
</frame>
<frame>
<ip>0x........</ip>
<obj>...</obj>
<fn>main</fn>
<dir>...</dir>
<file>hsg.c</file>
<line>...</line>
</frame>
</stack>
<auxwhat>Address 0x........ expected vs actual:</auxwhat>
<auxwhat>Expected: stack array "la" of size 200 in frame 1 back from here</auxwhat>
<auxwhat>Actual: unknown</auxwhat>
</error>
<status>
<state>FINISHED</state>
<time>...</time>
</status>
<errorcounts>
<pair>
<count>...</count>
<unique>0x........</unique>
</pair>
<pair>
<count>...</count>
<unique>0x........</unique>
</pair>
</errorcounts>
<suppcounts>...</suppcounts>
</valgrindoutput>
@@ -0,0 +1 @@
boringly as expected
@@ -0,0 +1,4 @@
prereq: ./is_arch_supported && (../../tests/os_test linux || ../../tests/os_test solaris)
prog: hsg
vgopts: --xml=yes --xml-fd=2 --log-file=/dev/null
stderr_filter: ../../memcheck/tests/filter_xml
@@ -0,0 +1,15 @@
#!/bin/sh
#
# Not all architectures are supported by exp-ptr. Currently, PowerPC, s390x,
# MIPS and ARM are not supported and will fail these tests as follows:
# WARNING: exp-ptrcheck on <blah> platforms: stack and global array
# WARNING: checking is not currently supported. Only heap checking is
# WARNING: supported.
#
# So we use this script to prevent these tests from running on unsupported
# architectures.
case `uname -m` in
ppc*|arm*|s390x|mips*|tilegx) exit 1;;
*) exit 0;;
esac
@@ -0,0 +1,52 @@
#include <stdio.h>
#include <assert.h>
#include <dlfcn.h>
/* see comments in preen_invar_so.c for explanation of this */
int main ( void )
{
int i, r, sum = 0;
char* im_a_global_array;
void* hdl = dlopen("./preen_invars_so.so", RTLD_NOW);
assert(hdl);
im_a_global_array = dlsym(hdl, "im_a_global_array");
assert(im_a_global_array);
/* printf("%p %p\n", im_a_global_array, me_too_me_too); */
/* poke around in the global array, so as to cause exp-ptrcheck
to generate an Inv_Global invar for it. */
for (i = 10/*ERROR*/; i >= 0; i--) {
sum += im_a_global_array[i];
}
/* iterating 10 .. 0 causes an Unknown->Global transition at i = 9.
We do it this way in order that at the end of a loop, there is a
Global invar in place for the memory read in the loop, so that
the subsequent dlclose (hence munmap) causes it to get preened.
Unfortunately there's nothing to show that the preen was
successful or happened at all. The only way to see is from the
-v output:
--686-- sg_: 251 Invars preened, of which 1 changed
It's the "1 changed" bit which is significant.
*/
/* let's hope gcc is not clever enough to optimise this away, since
if it does, then it will also nuke the preceding loop, and
thereby render this test program useless. */
if (sum & 1) printf("%s bar %d\n", "foo", sum & 1); else
printf("foo %s %d\n", "bar", 1 - (sum & 1));
/* Now close (== unmap) the array, so that exp-ptrcheck has to check
its collection of Inv_Global invars, and remove this one from
it. */
r = dlclose(hdl);
assert(r == 0);
return 0;
}
@@ -0,0 +1,9 @@
Invalid read of size 1
at 0x........: main (preen_invars.c:22)
Address 0x........ expected vs actual:
Expected: unknown
Actual: global array "im_a_global_arr" of size 10 in object with soname "preen_invars_so"
ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
@@ -0,0 +1 @@
foo bar 1
@@ -0,0 +1,2 @@
prereq: ./is_arch_supported && (../../tests/os_test linux || ../../tests/os_test solaris)
prog: preen_invars
@@ -0,0 +1,12 @@
/* This file contains a global array. It is compiled into a .so,
which is dlopened by preen_invar.c. That then accesses the global
array, hence generating Inv_Global invariants in sg_main.c.
preen_invar.c then dlcloses this object, causing it to get
unmapped; and we then need to be sure that the Inv_Global is
removed by preen_Invars (or, at least, that the system doesn't
crash..). */
char im_a_global_array[10];
@@ -0,0 +1,53 @@
/* Check basic stack overflow detection.
It's difficult to get consistent behaviour across all platforms.
For example, x86 w/ gcc-4.3.1 gives
Expected: stack array "a" in frame 2 back from here
Actual: stack array "beforea" in frame 2 back from here
whereas amd64 w/ gcc-4.3.1 gives
Expected: stack array "a" in frame 2 back from here
Actual: unknown
This happens because on x86 the arrays are placed on the
stack without holes in between, but not so for amd64. I don't
know why.
*/
#include <stdio.h>
__attribute__((noinline)) void foo ( long* sa, int n )
{
int i;
for (i = 0; i < n; i++)
sa[i] = 0;
}
__attribute__((noinline)) void bar ( long* sa, int n )
{
foo(sa, n);
}
int main ( void )
{
int i;
long beforea[3];
long a[7];
long aftera[3];
bar(a, 7+1); /* generates error */
bar(a, 7+0); /* generates no error */
for (i = 0; i < 7+1; i++) {
a[i] = 0;
}
{char beforebuf[8];
char buf[8];
char afterbuf[8];
sprintf(buf, "%d", 123456789);
return 1 & ((a[4] + beforea[1] + aftera[1] + beforebuf[1]
+ buf[2] + afterbuf[3]) / 100000) ;
}
}
@@ -0,0 +1,28 @@
Invalid write of size 4
at 0x........: foo (stackerr.c:27)
by 0x........: bar (stackerr.c:32)
by 0x........: main (stackerr.c:41)
Address 0x........ expected vs actual:
Expected: stack array "a" of size 28 in frame 2 back from here
Actual: stack array "beforea" of size 12 in frame 2 back from here
Actual: is 0 after Expected
Invalid write of size 4
at 0x........: main (stackerr.c:44)
Address 0x........ expected vs actual:
Expected: stack array "a" of size 28 in this frame
Actual: stack array "beforea" of size 12 in this frame
Actual: is 0 after Expected
Invalid write of size 1
at 0x........: _IO_default_xsputn (in /...libc...)
by 0x........: ...
by 0x........: ...
Address 0x........ expected vs actual:
Expected: stack array "buf" of size 8 in frame 4 back from here
Actual: stack array "beforebuf" of size 8 in frame 4 back from here
Actual: is 0 after Expected
ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
@@ -0,0 +1,28 @@
Invalid write of size 8
at 0x........: foo (stackerr.c:27)
by 0x........: bar (stackerr.c:32)
by 0x........: main (stackerr.c:41)
Address 0x........ expected vs actual:
Expected: stack array "a" of size 56 in frame 2 back from here
Actual: unknown
Actual: is 0 after Expected
Invalid write of size 8
at 0x........: main (stackerr.c:44)
Address 0x........ expected vs actual:
Expected: stack array "a" of size 56 in this frame
Actual: unknown
Actual: is 0 after Expected
Invalid write of size 1
at 0x........: _IO_default_xsputn (in /...libc...)
by 0x........: ...
by 0x........: ...
Address 0x........ expected vs actual:
Expected: stack array "buf" of size 8 in frame 4 back from here
Actual: unknown
Actual: is 0 after Expected
ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
@@ -0,0 +1,3 @@
prereq: ./is_arch_supported && (../../tests/os_test linux || ../../tests/os_test solaris)
vgopts: --num-callers=3
prog: stackerr