|
From: Julian S. <se...@so...> - 2020-04-17 17:26:03
|
https://sourceware.org/git/gitweb.cgi?p=valgrind.git;h=40187fcd61ee877f78701c46a74ac1dadbe65b3d commit 40187fcd61ee877f78701c46a74ac1dadbe65b3d Author: Julian Seward <js...@ac...> Date: Fri Apr 17 19:23:26 2020 +0200 Remove the exp-sgcheck tool. It only ever worked on x86 and amd64, and even on those it had a high false positive rate and was slow. Everything it does, ASan can do faster, better, and on more architectures. So there's no reason to keep this tool any more. Diff: --- Makefile.am | 2 - configure.ac | 5 - docs/xml/FAQ.xml | 4 - docs/xml/manual-core.xml | 4 +- docs/xml/manual.xml | 2 - docs/xml/valgrind-manpage.xml | 9 - exp-sgcheck.supp | 20 - exp-sgcheck/Makefile.am | 109 - exp-sgcheck/docs/sg-manual.xml | 327 - exp-sgcheck/h_intercepts.c | 440 -- exp-sgcheck/h_main.c | 722 --- exp-sgcheck/h_main.h | 80 - exp-sgcheck/pc_common.c | 814 --- exp-sgcheck/pc_common.h | 76 - exp-sgcheck/pc_main.c | 158 - exp-sgcheck/sg_main.c | 2569 -------- exp-sgcheck/sg_main.h | 76 - exp-sgcheck/tests/Makefile.am | 69 - exp-sgcheck/tests/bad_percentify.c | 647 -- .../tests/bad_percentify.stderr.exp-glibc28-amd64 | 30 - exp-sgcheck/tests/bad_percentify.stdout.exp | 3 - exp-sgcheck/tests/bad_percentify.vgtest | 2 - exp-sgcheck/tests/filter_add | 8 - exp-sgcheck/tests/filter_stderr | 38 - exp-sgcheck/tests/filter_suppgen | 11 - exp-sgcheck/tests/globalerr.c | 15 - .../tests/globalerr.stderr.exp-gcc491-amd64 | 17 - .../tests/globalerr.stderr.exp-glibc28-amd64 | 17 - exp-sgcheck/tests/globalerr.stdout.exp | 0 exp-sgcheck/tests/globalerr.vgtest | 2 - exp-sgcheck/tests/hackedbz2.c | 6537 -------------------- .../tests/hackedbz2.stderr.exp-glibc28-amd64 | 17 - exp-sgcheck/tests/hackedbz2.stdout.exp | 70 - exp-sgcheck/tests/hackedbz2.vgtest | 2 - exp-sgcheck/tests/hsg.c | 48 - exp-sgcheck/tests/hsg.stderr.exp | 116 - exp-sgcheck/tests/hsg.stdout.exp | 1 - exp-sgcheck/tests/hsg.vgtest | 4 - exp-sgcheck/tests/is_arch_supported | 15 - exp-sgcheck/tests/preen_invars.c | 52 - .../tests/preen_invars.stderr.exp-glibc28-amd64 | 9 - exp-sgcheck/tests/preen_invars.stdout.exp | 1 - exp-sgcheck/tests/preen_invars.vgtest | 2 - exp-sgcheck/tests/preen_invars_so.c | 12 - exp-sgcheck/tests/stackerr.c | 53 - exp-sgcheck/tests/stackerr.stderr.exp-glibc27-x86 | 28 - .../tests/stackerr.stderr.exp-glibc28-amd64 | 28 - exp-sgcheck/tests/stackerr.stdout.exp | 0 exp-sgcheck/tests/stackerr.vgtest | 3 - helgrind/tests/annotate_hbefore.c | 3 +- solaris/valgrind.p5m | 4 - tests/check_headers_and_includes | 1 - 52 files changed, 3 insertions(+), 13279 deletions(-) diff --git a/Makefile.am b/Makefile.am index 242b38a142..08db834019 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,7 +15,6 @@ TOOLS = \ none EXP_TOOLS = \ - exp-sgcheck \ exp-bbv # Put docs last because building the HTML is slow and we want to get @@ -45,7 +44,6 @@ SUPP_FILES = \ glibc-2.34567-NPTL-helgrind.supp \ glibc-2.2-LinuxThreads-helgrind.supp \ glibc-2.X-drd.supp \ - exp-sgcheck.supp \ darwin9.supp darwin9-drd.supp \ darwin10.supp darwin10-drd.supp \ darwin11.supp darwin12.supp darwin13.supp darwin14.supp darwin15.supp \ diff --git a/configure.ac b/configure.ac index 3336329e9c..9e6ed71387 100755 --- a/configure.ac +++ b/configure.ac @@ -1166,9 +1166,6 @@ if test "$VGCONF_OS" != "solaris"; then # add the suppressions antidisirregardless. DEFAULT_SUPP="xfree-4.supp ${DEFAULT_SUPP}" DEFAULT_SUPP="xfree-3.supp ${DEFAULT_SUPP}" - - # Add glibc and X11 suppressions for exp-sgcheck - DEFAULT_SUPP="exp-sgcheck.supp ${DEFAULT_SUPP}" fi @@ -4853,8 +4850,6 @@ AC_CONFIG_FILES([ none/tests/x86-darwin/Makefile none/tests/amd64-solaris/Makefile none/tests/x86-solaris/Makefile - exp-sgcheck/Makefile - exp-sgcheck/tests/Makefile exp-bbv/Makefile exp-bbv/tests/Makefile exp-bbv/tests/x86/Makefile diff --git a/docs/xml/FAQ.xml b/docs/xml/FAQ.xml index d0d917edce..b124606282 100644 --- a/docs/xml/FAQ.xml +++ b/docs/xml/FAQ.xml @@ -483,10 +483,6 @@ int main(void) <para>Unfortunately, Memcheck doesn't do bounds checking on global or stack arrays. We'd like to, but it's just not possible to do in a reasonable way that fits with how Memcheck works. Sorry.</para> - - <para>However, the experimental tool SGcheck can detect errors like - this. Run Valgrind with the <option>--tool=exp-sgcheck</option> option - to try it, but be aware that it is not as robust as Memcheck.</para> </answer> </qandaentry> diff --git a/docs/xml/manual-core.xml b/docs/xml/manual-core.xml index 8d24616a0e..72730ec7bb 100644 --- a/docs/xml/manual-core.xml +++ b/docs/xml/manual-core.xml @@ -613,7 +613,7 @@ in most cases. We group the available options by rough categories.</para> <listitem> <para>Run the Valgrind tool called <varname>toolname</varname>, e.g. memcheck, cachegrind, callgrind, helgrind, drd, massif, - dhat, lackey, none, exp-sgcheck, exp-bbv, etc.</para> + dhat, lackey, none, exp-bbv, etc.</para> </listitem> </varlistentry> @@ -2562,7 +2562,7 @@ need to use them.</para> malloc related functions, using the synonym <varname>somalloc</varname>. This synonym is usable for all tools doing standard replacement of malloc related functions - (e.g. memcheck, helgrind, drd, massif, dhat, exp-sgcheck). + (e.g. memcheck, helgrind, drd, massif, dhat). </para> <itemizedlist> diff --git a/docs/xml/manual.xml b/docs/xml/manual.xml index b90514935a..da37102d00 100644 --- a/docs/xml/manual.xml +++ b/docs/xml/manual.xml @@ -42,8 +42,6 @@ xmlns:xi="http://www.w3.org/2001/XInclude" /> <xi:include href="../../none/docs/nl-manual.xml" parse="xml" xmlns:xi="http://www.w3.org/2001/XInclude" /> - <xi:include href="../../exp-sgcheck/docs/sg-manual.xml" parse="xml" - xmlns:xi="http://www.w3.org/2001/XInclude" /> <xi:include href="../../exp-bbv/docs/bbv-manual.xml" parse="xml" xmlns:xi="http://www.w3.org/2001/XInclude" /> diff --git a/docs/xml/valgrind-manpage.xml b/docs/xml/valgrind-manpage.xml index 7498c614c4..332ca4884c 100644 --- a/docs/xml/valgrind-manpage.xml +++ b/docs/xml/valgrind-manpage.xml @@ -202,15 +202,6 @@ system: <filename>&vg-docs-path;</filename>, or online: -<refsect1 id="sgcheck-options"> -<title>SGcheck Options</title> - -<xi:include href="../../exp-sgcheck/docs/sg-manual.xml" - xpointer="sg.opts.list" - xmlns:xi="http://www.w3.org/2001/XInclude" /> - -</refsect1> - <refsect1 id="bbv-options"> <title>BBV Options</title> diff --git a/exp-sgcheck.supp b/exp-sgcheck.supp deleted file mode 100644 index b2d4aebfd9..0000000000 --- a/exp-sgcheck.supp +++ /dev/null @@ -1,20 +0,0 @@ -{ - ld-2.X possibly applying relocations - exp-sgcheck:SorG - obj:*/*lib*/ld-2.*so* - obj:*/*lib*/ld-2.*so* -} - -# I'm pretty sure this is a false positive caused by the sg_ stuff -{ - glibc realpath false positive - exp-sgcheck:SorG - fun:realpath - fun:* -} - -{ - I think this is glibc's ultra optimised getenv doing 2 byte reads - exp-sgcheck:SorG - fun:getenv -} diff --git a/exp-sgcheck/Makefile.am b/exp-sgcheck/Makefile.am deleted file mode 100644 index 8927ff630b..0000000000 --- a/exp-sgcheck/Makefile.am +++ /dev/null @@ -1,109 +0,0 @@ -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 = $(LTO_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 = $(LTO_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 - diff --git a/exp-sgcheck/docs/sg-manual.xml b/exp-sgcheck/docs/sg-manual.xml deleted file mode 100644 index c03e77811d..0000000000 --- a/exp-sgcheck/docs/sg-manual.xml +++ /dev/null @@ -1,327 +0,0 @@ -<?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> - -<para>It is important to note that the association is done between - a <emphasis>binary instruction</emphasis> and an array, the - <emphasis>first time</emphasis> this binary instruction accesses an - array during a function call. When the same instruction is executed - again during the same function call, then SGCheck might report a - problem, if these further executions are not accessing the same - array. This technique causes several limitations in SGCheck, see - <xref linkend="sg-manual.limitations"/>. -</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> - <para>It also means that errors will not be found in an instruction - executed only once (e.g. because this instruction is not in a loop, - or the loop is executed only once).</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> diff --git a/exp-sgcheck/h_intercepts.c b/exp-sgcheck/h_intercepts.c deleted file mode 100644 index 6fe8680317..0000000000 --- a/exp-sgcheck/h_intercepts.c +++ /dev/null @@ -1,440 +0,0 @@ - -/*--------------------------------------------------------------------*/ -/*--- 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-2017 Nicholas Nethercote - nj...@va... - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. - - 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 ---*/ -/*--------------------------------------------------------------------*/ diff --git a/exp-sgcheck/h_main.c b/exp-sgcheck/h_main.c deleted file mode 100644 index 64cac3a323..0000000000 --- a/exp-sgcheck/h_main.c +++ /dev/null @@ -1,722 +0,0 @@ - -/*--------------------------------------------------------------------*/ -/*--- 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-2017 Nicholas Nethercote - nj...@va... - - Valgrind-3.X port: - - Copyright (C) 2008-2017 OpenWorks Ltd - in...@op... - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see <http://www.gnu.org/licenses/>. - - 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_(... [truncated message content] |