From: Julian Seward Date: Fri, 2 May 2008 10:33:15 +0000 (+0000) Subject: Move the error management code for Memcheck into a new file, X-Git-Tag: svn/VALGRIND_3_4_0~651 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4d1716321e00b2a0f9eb6dcab5fb29b7c6f7d00f;p=thirdparty%2Fvalgrind.git Move the error management code for Memcheck into a new file, mc_errors.c, as it is relatively self contained. This reduces the size of mc_main.c by about 1350 lines. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@7986 --- diff --git a/memcheck/Makefile.am b/memcheck/Makefile.am index f1ec930ad0..8414da40f9 100644 --- a/memcheck/Makefile.am +++ b/memcheck/Makefile.am @@ -81,7 +81,8 @@ MEMCHECK_SOURCES_COMMON = \ mc_malloc_wrappers.c \ mc_main.c \ mc_translate.c \ - mc_machine.c + mc_machine.c \ + mc_errors.c memcheck_x86_linux_SOURCES = $(MEMCHECK_SOURCES_COMMON) memcheck_x86_linux_CPPFLAGS = $(AM_CPPFLAGS_X86_LINUX) diff --git a/memcheck/mc_errors.c b/memcheck/mc_errors.c new file mode 100644 index 0000000000..8db04503ff --- /dev/null +++ b/memcheck/mc_errors.c @@ -0,0 +1,1366 @@ + +/*--------------------------------------------------------------------*/ +/*--- Management, printing, etc, of errors and suppressions. ---*/ +/*--- mc_errors.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors. + + Copyright (C) 2000-2008 Julian Seward + jseward@acm.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. +*/ + +#include "pub_tool_basics.h" +#include "pub_tool_aspacemgr.h" +#include "pub_tool_hashtable.h" // For mc_include.h +#include "pub_tool_libcbase.h" +#include "pub_tool_libcassert.h" +#include "pub_tool_libcprint.h" +#include "pub_tool_machine.h" +#include "pub_tool_mallocfree.h" +#include "pub_tool_options.h" +#include "pub_tool_oset.h" +#include "pub_tool_replacemalloc.h" +#include "pub_tool_tooliface.h" +#include "pub_tool_threadstate.h" +#include "pub_tool_oset.h" +#include "pub_tool_debuginfo.h" // VG_(get_dataname_and_offset) + +#include "mc_include.h" +#include "memcheck.h" /* for client requests */ + + +/*------------------------------------------------------------*/ +/*--- Error types ---*/ +/*------------------------------------------------------------*/ + +/* See comment in mc_include.h */ +Bool MC_(any_value_errors) = False; + + +// Different kinds of blocks. +typedef enum { + Block_Mallocd = 111, + Block_Freed, + Block_Mempool, + Block_MempoolChunk, + Block_UserG +} BlockKind; + +/* ------------------ Addresses -------------------- */ + +/* The classification of a faulting address. */ +typedef + enum { + Addr_Undescribed, // as-yet unclassified + Addr_Unknown, // classification yielded nothing useful + Addr_Block, // in malloc'd/free'd block + Addr_Stack, // on a thread's stack + Addr_DataSym, // in a global data sym + Addr_Variable, // variable described by the debug info + Addr_SectKind // last-ditch classification attempt + } + AddrTag; + +typedef + struct _AddrInfo + AddrInfo; + +struct _AddrInfo { + AddrTag tag; + union { + // As-yet unclassified. + struct { } Undescribed; + + // On a stack. + struct { + ThreadId tid; // Which thread's stack? + } Stack; + + // This covers heap blocks (normal and from mempools) and user-defined + // blocks. + struct { + BlockKind block_kind; + Char* block_desc; // "block", "mempool" or user-defined + SizeT block_szB; + OffT rwoffset; + ExeContext* lastchange; + } Block; + + // In a global .data symbol. This holds the first 63 chars of + // the variable's (zero terminated), plus an offset. + struct { + Char name[128]; + OffT offset; + } DataSym; + + // Is described by Dwarf debug info. Arbitrary strings. Must + // be the same length. + struct { + Char descr1[96]; + Char descr2[96]; + } Variable; + + // Could only narrow it down to be the PLT/GOT/etc of a given + // object. Better than nothing, perhaps. + struct { + Char objname[128]; + VgSectKind kind; + } SectKind; + + // Classification yielded nothing useful. + struct { } Unknown; + + } Addr; +}; + +/* ------------------ Errors ----------------------- */ + +/* What kind of error it is. */ +typedef + enum { + Err_Value, + Err_Cond, + Err_CoreMem, + Err_Addr, + Err_Jump, + Err_RegParam, + Err_MemParam, + Err_User, + Err_Free, + Err_FreeMismatch, + Err_Overlap, + Err_Leak, + Err_IllegalMempool, + } + MC_ErrorTag; + + +typedef struct _MC_Error MC_Error; + +struct _MC_Error { + // Nb: we don't need the tag here, as it's stored in the Error type! Yuk. + //MC_ErrorTag tag; + + union { + // Use of an undefined value: + // - as a pointer in a load or store + // - as a jump target + struct { + SizeT szB; // size of value in bytes + // Origin info + UInt otag; // origin tag + ExeContext* origin_ec; // filled in later + } Value; + + // Use of an undefined value in a conditional branch or move. + struct { + // Origin info + UInt otag; // origin tag + ExeContext* origin_ec; // filled in later + } Cond; + + // Addressability error in core (signal-handling) operation. + // It would be good to get rid of this error kind, merge it with + // another one somehow. + struct { + } CoreMem; + + // Use of an unaddressable memory location in a load or store. + struct { + Bool isWrite; // read or write? + SizeT szB; // not used for exec (jump) errors + Bool maybe_gcc; // True if just below %esp -- could be a gcc bug + AddrInfo ai; + } Addr; + + // Jump to an unaddressable memory location. + struct { + AddrInfo ai; + } Jump; + + // System call register input contains undefined bytes. + struct { + // Origin info + UInt otag; // origin tag + ExeContext* origin_ec; // filled in later + } RegParam; + + // System call memory input contains undefined/unaddressable bytes + struct { + Bool isAddrErr; // Addressability or definedness error? + AddrInfo ai; + // Origin info + UInt otag; // origin tag + ExeContext* origin_ec; // filled in later + } MemParam; + + // Problem found from a client request like CHECK_MEM_IS_ADDRESSABLE. + struct { + Bool isAddrErr; // Addressability or definedness error? + AddrInfo ai; + // Origin info + UInt otag; // origin tag + ExeContext* origin_ec; // filled in later + } User; + + // Program tried to free() something that's not a heap block (this + // covers double-frees). */ + struct { + AddrInfo ai; + } Free; + + // Program allocates heap block with one function + // (malloc/new/new[]/custom) and deallocates with not the matching one. + struct { + AddrInfo ai; + } FreeMismatch; + + // Call to strcpy, memcpy, etc, with overlapping blocks. + struct { + Addr src; // Source block + Addr dst; // Destination block + Int szB; // Size in bytes; 0 if unused. + } Overlap; + + // A memory leak. + struct { + UInt n_this_record; + UInt n_total_records; + LossRecord* lossRecord; + } Leak; + + // A memory pool error. + struct { + AddrInfo ai; + } IllegalMempool; + + } Err; +}; + + +/*------------------------------------------------------------*/ +/*--- Printing errors ---*/ +/*------------------------------------------------------------*/ + +static void mc_pp_AddrInfo ( Addr a, AddrInfo* ai, Bool maybe_gcc ) +{ + HChar* xpre = VG_(clo_xml) ? " " : " "; + HChar* xpost = VG_(clo_xml) ? "" : ""; + + switch (ai->tag) { + case Addr_Unknown: + if (maybe_gcc) { + VG_(message)(Vg_UserMsg, + "%sAddress 0x%llx is just below the stack ptr. " + "To suppress, use: --workaround-gcc296-bugs=yes%s", + xpre, (ULong)a, xpost + ); + } else { + VG_(message)(Vg_UserMsg, + "%sAddress 0x%llx " + "is not stack'd, malloc'd or (recently) free'd%s", + xpre, (ULong)a, xpost); + } + break; + + case Addr_Stack: + VG_(message)(Vg_UserMsg, + "%sAddress 0x%llx is on thread %d's stack%s", + xpre, (ULong)a, ai->Addr.Stack.tid, xpost); + break; + + case Addr_Block: { + SizeT block_szB = ai->Addr.Block.block_szB; + OffT rwoffset = ai->Addr.Block.rwoffset; + SizeT delta; + const Char* relative; + + if (rwoffset < 0) { + delta = (SizeT)(-rwoffset); + relative = "before"; + } else if (rwoffset >= block_szB) { + delta = rwoffset - block_szB; + relative = "after"; + } else { + delta = rwoffset; + relative = "inside"; + } + VG_(message)(Vg_UserMsg, + "%sAddress 0x%lx is %,lu bytes %s a %s of size %,lu %s%s", + xpre, + a, delta, relative, ai->Addr.Block.block_desc, + block_szB, + ai->Addr.Block.block_kind==Block_Mallocd ? "alloc'd" + : ai->Addr.Block.block_kind==Block_Freed ? "free'd" + : "client-defined", + xpost); + VG_(pp_ExeContext)(ai->Addr.Block.lastchange); + break; + } + + case Addr_DataSym: + VG_(message)(Vg_UserMsg, + "%sAddress 0x%llx is %llu bytes " + "inside data symbol \"%t\"%s", + xpre, + (ULong)a, + (ULong)ai->Addr.DataSym.offset, + ai->Addr.DataSym.name, + xpost); + break; + + case Addr_Variable: + if (ai->Addr.Variable.descr1[0] != '\0') + VG_(message)(Vg_UserMsg, "%s%s%s", + xpre, ai->Addr.Variable.descr1, xpost); + if (ai->Addr.Variable.descr2[0] != '\0') + VG_(message)(Vg_UserMsg, "%s%s%s", + xpre, ai->Addr.Variable.descr2, xpost); + break; + + case Addr_SectKind: + VG_(message)(Vg_UserMsg, + "%sAddress 0x%llx is in the %t segment of %t%s", + xpre, + (ULong)a, + VG_(pp_SectKind)(ai->Addr.SectKind.kind), + ai->Addr.SectKind.objname, + xpost); + break; + + default: + VG_(tool_panic)("mc_pp_AddrInfo"); + } +} + +static const HChar* str_leak_lossmode ( Reachedness lossmode ) +{ + const HChar *loss = "?"; + switch (lossmode) { + case Unreached: loss = "definitely lost"; break; + case IndirectLeak: loss = "indirectly lost"; break; + case Interior: loss = "possibly lost"; break; + case Proper: loss = "still reachable"; break; + } + return loss; +} + +static const HChar* xml_leak_kind ( Reachedness lossmode ) +{ + const HChar *loss = "?"; + switch (lossmode) { + case Unreached: loss = "Leak_DefinitelyLost"; break; + case IndirectLeak: loss = "Leak_IndirectlyLost"; break; + case Interior: loss = "Leak_PossiblyLost"; break; + case Proper: loss = "Leak_StillReachable"; break; + } + return loss; +} + +static void mc_pp_msg( Char* xml_name, Error* err, const HChar* format, ... ) +{ + HChar* xpre = VG_(clo_xml) ? " " : ""; + HChar* xpost = VG_(clo_xml) ? "" : ""; + Char buf[256]; + va_list vargs; + + if (VG_(clo_xml)) + VG_(message)(Vg_UserMsg, " %s", xml_name); + // Stick xpre and xpost on the front and back of the format string. + VG_(snprintf)(buf, 256, "%s%s%s", xpre, format, xpost); + va_start(vargs, format); + VG_(vmessage) ( Vg_UserMsg, buf, vargs ); + va_end(vargs); + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); +} + +static void mc_pp_origin ( ExeContext* ec, UInt okind ) +{ + HChar* src = NULL; + HChar* xpre = VG_(clo_xml) ? " " : " "; + HChar* xpost = VG_(clo_xml) ? "" : ""; + tl_assert(ec); + + switch (okind) { + case MC_OKIND_STACK: src = " by a stack allocation"; break; + case MC_OKIND_HEAP: src = " by a heap allocation"; break; + case MC_OKIND_USER: src = " by a client request"; break; + case MC_OKIND_UNKNOWN: src = ""; break; + } + tl_assert(src); /* guards against invalid 'okind' */ + + if (VG_(clo_xml)) { + VG_(message)(Vg_UserMsg, " "); + } + + VG_(message)(Vg_UserMsg, "%sUninitialised value was created%s%s", + xpre, src, xpost); + VG_(pp_ExeContext)( ec ); + if (VG_(clo_xml)) { + VG_(message)(Vg_UserMsg, " "); + } +} + +void MC_(pp_Error) ( Error* err ) +{ + MC_Error* extra = VG_(get_error_extra)(err); + + switch (VG_(get_error_kind)(err)) { + case Err_CoreMem: { + /* What the hell *is* a CoreMemError? jrs 2005-May-18 */ + /* As of 2006-Dec-14, it's caused by unaddressable bytes in a + signal handler frame. --njn */ + mc_pp_msg("CoreMemError", err, + "%s contains unaddressable byte(s)", + VG_(get_error_string)(err)); + break; + } + + case Err_Value: + MC_(any_value_errors) = True; + if (1 || extra->Err.Value.otag == 0) { + mc_pp_msg("UninitValue", err, + "Use of uninitialised value of size %d", + extra->Err.Value.szB); + } else { + mc_pp_msg("UninitValue", err, + "Use of uninitialised value of size %d (otag %u)", + extra->Err.Value.szB, extra->Err.Value.otag); + } + if (extra->Err.Value.origin_ec) + mc_pp_origin( extra->Err.Value.origin_ec, + extra->Err.Value.otag & 3 ); + break; + + case Err_Cond: + MC_(any_value_errors) = True; + if (1 || extra->Err.Cond.otag == 0) { + mc_pp_msg("UninitCondition", err, + "Conditional jump or move depends" + " on uninitialised value(s)"); + } else { + mc_pp_msg("UninitCondition", err, + "Conditional jump or move depends" + " on uninitialised value(s) (otag %u)", + extra->Err.Cond.otag); + } + if (extra->Err.Cond.origin_ec) + mc_pp_origin( extra->Err.Cond.origin_ec, + extra->Err.Cond.otag & 3 ); + break; + + case Err_RegParam: + MC_(any_value_errors) = True; + mc_pp_msg("SyscallParam", err, + "Syscall param %s contains uninitialised byte(s)", + VG_(get_error_string)(err)); + if (extra->Err.RegParam.origin_ec) + mc_pp_origin( extra->Err.RegParam.origin_ec, + extra->Err.RegParam.otag & 3 ); + break; + + case Err_MemParam: + if (!extra->Err.MemParam.isAddrErr) + MC_(any_value_errors) = True; + mc_pp_msg("SyscallParam", err, + "Syscall param %s points to %s byte(s)", + VG_(get_error_string)(err), + ( extra->Err.MemParam.isAddrErr + ? "unaddressable" : "uninitialised" )); + mc_pp_AddrInfo(VG_(get_error_address)(err), + &extra->Err.MemParam.ai, False); + if (extra->Err.MemParam.origin_ec && !extra->Err.MemParam.isAddrErr) + mc_pp_origin( extra->Err.MemParam.origin_ec, + extra->Err.MemParam.otag & 3 ); + break; + + case Err_User: + if (!extra->Err.User.isAddrErr) + MC_(any_value_errors) = True; + mc_pp_msg("ClientCheck", err, + "%s byte(s) found during client check request", + ( extra->Err.User.isAddrErr + ? "Unaddressable" : "Uninitialised" )); + mc_pp_AddrInfo(VG_(get_error_address)(err), &extra->Err.User.ai, + False); + if (extra->Err.User.origin_ec && !extra->Err.User.isAddrErr) + mc_pp_origin( extra->Err.User.origin_ec, + extra->Err.User.otag & 3 ); + break; + + case Err_Free: + mc_pp_msg("InvalidFree", err, + "Invalid free() / delete / delete[]"); + mc_pp_AddrInfo(VG_(get_error_address)(err), + &extra->Err.Free.ai, False); + break; + + case Err_FreeMismatch: + mc_pp_msg("MismatchedFree", err, + "Mismatched free() / delete / delete []"); + mc_pp_AddrInfo(VG_(get_error_address)(err), + &extra->Err.FreeMismatch.ai, False); + break; + + case Err_Addr: + if (extra->Err.Addr.isWrite) { + mc_pp_msg("InvalidWrite", err, + "Invalid write of size %d", + extra->Err.Addr.szB); + } else { + mc_pp_msg("InvalidRead", err, + "Invalid read of size %d", + extra->Err.Addr.szB); + } + mc_pp_AddrInfo(VG_(get_error_address)(err), &extra->Err.Addr.ai, + extra->Err.Addr.maybe_gcc); + break; + + case Err_Jump: + mc_pp_msg("InvalidJump", err, + "Jump to the invalid address stated on the next line"); + mc_pp_AddrInfo(VG_(get_error_address)(err), &extra->Err.Jump.ai, + False); + break; + + case Err_Overlap: + if (extra->Err.Overlap.szB == 0) + mc_pp_msg("Overlap", err, + "Source and destination overlap in %s(%p, %p)", + VG_(get_error_string)(err), + extra->Err.Overlap.dst, extra->Err.Overlap.src); + else + mc_pp_msg("Overlap", err, + "Source and destination overlap in %s(%p, %p, %d)", + VG_(get_error_string)(err), + extra->Err.Overlap.dst, extra->Err.Overlap.src, + extra->Err.Overlap.szB); + break; + + case Err_IllegalMempool: + mc_pp_msg("InvalidMemPool", err, + "Illegal memory pool address"); + mc_pp_AddrInfo(VG_(get_error_address)(err), + &extra->Err.IllegalMempool.ai, False); + break; + + case Err_Leak: { + HChar* xpre = VG_(clo_xml) ? " " : ""; + HChar* xpost = VG_(clo_xml) ? "" : ""; + UInt n_this_record = extra->Err.Leak.n_this_record; + UInt n_total_records = extra->Err.Leak.n_total_records; + LossRecord* l = extra->Err.Leak.lossRecord; + + if (VG_(clo_xml)) { + VG_(message)(Vg_UserMsg, " %t", + xml_leak_kind(l->loss_mode)); + } else { + VG_(message)(Vg_UserMsg, ""); + } + + if (l->indirect_bytes) { + VG_(message)(Vg_UserMsg, + "%s%,lu (%,lu direct, %,lu indirect) bytes in %,u blocks" + " are %s in loss record %,u of %,u%s", + xpre, + l->total_bytes + l->indirect_bytes, + l->total_bytes, l->indirect_bytes, l->num_blocks, + str_leak_lossmode(l->loss_mode), n_this_record, n_total_records, + xpost + ); + if (VG_(clo_xml)) { + // Nb: don't put commas in these XML numbers + VG_(message)(Vg_UserMsg, " %lu", + l->total_bytes + l->indirect_bytes); + VG_(message)(Vg_UserMsg, " %u", + l->num_blocks); + } + } else { + VG_(message)( + Vg_UserMsg, + "%s%,lu bytes in %,u blocks are %s in loss record %,u of %,u%s", + xpre, + l->total_bytes, l->num_blocks, + str_leak_lossmode(l->loss_mode), n_this_record, n_total_records, + xpost + ); + if (VG_(clo_xml)) { + VG_(message)(Vg_UserMsg, " %d", + l->total_bytes); + VG_(message)(Vg_UserMsg, " %d", + l->num_blocks); + } + } + VG_(pp_ExeContext)(l->allocated_at); + break; + } + + default: + VG_(printf)("Error:\n unknown Memcheck error code %d\n", + VG_(get_error_kind)(err)); + VG_(tool_panic)("unknown error code in mc_pp_Error)"); + } +} + +/*------------------------------------------------------------*/ +/*--- Recording errors ---*/ +/*------------------------------------------------------------*/ + +/* These many bytes below %ESP are considered addressible if we're + doing the --workaround-gcc296-bugs hack. */ +#define VG_GCC296_BUG_STACK_SLOP 1024 + +/* Is this address within some small distance below %ESP? Used only + for the --workaround-gcc296-bugs kludge. */ +static Bool is_just_below_ESP( Addr esp, Addr aa ) +{ + if (esp > aa && (esp - aa) <= VG_GCC296_BUG_STACK_SLOP) + return True; + else + return False; +} + +/* --- Called from generated and non-generated code --- */ + +void MC_(record_address_error) ( ThreadId tid, Addr a, Int szB, + Bool isWrite ) +{ + MC_Error extra; + Bool just_below_esp; + + if (MC_(in_ignored_range)(a)) + return; + +# if defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) + /* AIX zero-page handling. On AIX, reads from page zero are, + bizarrely enough, legitimate. Writes to page zero aren't, + though. Since memcheck can't distinguish reads from writes, the + best we can do is to 'act normal' and mark the A bits in the + normal way as noaccess, but then hide any reads from that page + that get reported here. */ + if ((!isWrite) && a >= 0 && a < 4096 && a+szB <= 4096) + return; + + /* Appalling AIX hack. It suppresses reads done by glink + fragments. Getting rid of this would require figuring out + somehow where the referenced data areas are (and their + sizes). */ + if ((!isWrite) && szB == sizeof(Word)) { + UInt i1, i2; + UInt* pc = (UInt*)VG_(get_IP)(tid); + if (sizeof(Word) == 4) { + i1 = 0x800c0000; /* lwz r0,0(r12) */ + i2 = 0x804c0004; /* lwz r2,4(r12) */ + } else { + i1 = 0xe80c0000; /* ld r0,0(r12) */ + i2 = 0xe84c0008; /* ld r2,8(r12) */ + } + if (pc[0] == i1 && pc[1] == i2) return; + if (pc[0] == i2 && pc[-1] == i1) return; + } +# endif + + just_below_esp = is_just_below_ESP( VG_(get_SP)(tid), a ); + + /* If this is caused by an access immediately below %ESP, and the + user asks nicely, we just ignore it. */ + if (MC_(clo_workaround_gcc296_bugs) && just_below_esp) + return; + + extra.Err.Addr.isWrite = isWrite; + extra.Err.Addr.szB = szB; + extra.Err.Addr.maybe_gcc = just_below_esp; + extra.Err.Addr.ai.tag = Addr_Undescribed; + VG_(maybe_record_error)( tid, Err_Addr, a, /*s*/NULL, &extra ); +} + +void MC_(record_value_error) ( ThreadId tid, Int szB, UInt otag ) +{ + MC_Error extra; + tl_assert( MC_(clo_mc_level) >= 2 ); + if (otag > 0) + tl_assert( MC_(clo_mc_level) == 3 ); + extra.Err.Value.szB = szB; + extra.Err.Value.otag = otag; + extra.Err.Value.origin_ec = NULL; /* Filled in later */ + VG_(maybe_record_error)( tid, Err_Value, /*addr*/0, /*s*/NULL, &extra ); +} + +void MC_(record_cond_error) ( ThreadId tid, UInt otag ) +{ + MC_Error extra; + tl_assert( MC_(clo_mc_level) >= 2 ); + if (otag > 0) + tl_assert( MC_(clo_mc_level) == 3 ); + extra.Err.Cond.otag = otag; + extra.Err.Cond.origin_ec = NULL; /* Filled in later */ + VG_(maybe_record_error)( tid, Err_Cond, /*addr*/0, /*s*/NULL, &extra ); +} + +/* --- Called from non-generated code --- */ + +/* This is for memory errors in pthread functions, as opposed to pthread API + errors which are found by the core. */ +void MC_(record_core_mem_error) ( ThreadId tid, Bool isAddrErr, Char* msg ) +{ + VG_(maybe_record_error)( tid, Err_CoreMem, /*addr*/0, msg, /*extra*/NULL ); +} + +void MC_(record_regparam_error) ( ThreadId tid, Char* msg, UInt otag ) +{ + MC_Error extra; + tl_assert(VG_INVALID_THREADID != tid); + if (otag > 0) + tl_assert( MC_(clo_mc_level) == 3 ); + extra.Err.RegParam.otag = otag; + extra.Err.RegParam.origin_ec = NULL; /* Filled in later */ + VG_(maybe_record_error)( tid, Err_RegParam, /*addr*/0, msg, &extra ); +} + +void MC_(record_memparam_error) ( ThreadId tid, Addr a, + Bool isAddrErr, Char* msg, UInt otag ) +{ + MC_Error extra; + tl_assert(VG_INVALID_THREADID != tid); + if (!isAddrErr) + tl_assert( MC_(clo_mc_level) >= 2 ); + if (otag != 0) { + tl_assert( MC_(clo_mc_level) == 3 ); + tl_assert( !isAddrErr ); + } + extra.Err.MemParam.isAddrErr = isAddrErr; + extra.Err.MemParam.ai.tag = Addr_Undescribed; + extra.Err.MemParam.otag = otag; + extra.Err.MemParam.origin_ec = NULL; /* Filled in later */ + VG_(maybe_record_error)( tid, Err_MemParam, a, msg, &extra ); +} + +void MC_(record_jump_error) ( ThreadId tid, Addr a ) +{ + MC_Error extra; + tl_assert(VG_INVALID_THREADID != tid); + extra.Err.Jump.ai.tag = Addr_Undescribed; + VG_(maybe_record_error)( tid, Err_Jump, a, /*s*/NULL, &extra ); +} + +void MC_(record_free_error) ( ThreadId tid, Addr a ) +{ + MC_Error extra; + tl_assert(VG_INVALID_THREADID != tid); + extra.Err.Free.ai.tag = Addr_Undescribed; + VG_(maybe_record_error)( tid, Err_Free, a, /*s*/NULL, &extra ); +} + +void MC_(record_freemismatch_error) ( ThreadId tid, MC_Chunk* mc ) +{ + MC_Error extra; + AddrInfo* ai = &extra.Err.FreeMismatch.ai; + tl_assert(VG_INVALID_THREADID != tid); + ai->tag = Addr_Block; + ai->Addr.Block.block_kind = Block_Mallocd; // Nb: Not 'Block_Freed' + ai->Addr.Block.block_desc = "block"; + ai->Addr.Block.block_szB = mc->szB; + ai->Addr.Block.rwoffset = 0; + ai->Addr.Block.lastchange = mc->where; + VG_(maybe_record_error)( tid, Err_FreeMismatch, mc->data, /*s*/NULL, + &extra ); +} + +void MC_(record_illegal_mempool_error) ( ThreadId tid, Addr a ) +{ + MC_Error extra; + tl_assert(VG_INVALID_THREADID != tid); + extra.Err.IllegalMempool.ai.tag = Addr_Undescribed; + VG_(maybe_record_error)( tid, Err_IllegalMempool, a, /*s*/NULL, &extra ); +} + +void MC_(record_overlap_error) ( ThreadId tid, Char* function, + Addr src, Addr dst, SizeT szB ) +{ + MC_Error extra; + tl_assert(VG_INVALID_THREADID != tid); + extra.Err.Overlap.src = src; + extra.Err.Overlap.dst = dst; + extra.Err.Overlap.szB = szB; + VG_(maybe_record_error)( + tid, Err_Overlap, /*addr*/0, /*s*/function, &extra ); +} + +Bool MC_(record_leak_error) ( ThreadId tid, UInt n_this_record, + UInt n_total_records, LossRecord* lossRecord, + Bool print_record ) +{ + MC_Error extra; + extra.Err.Leak.n_this_record = n_this_record; + extra.Err.Leak.n_total_records = n_total_records; + extra.Err.Leak.lossRecord = lossRecord; + return + VG_(unique_error) ( tid, Err_Leak, /*Addr*/0, /*s*/NULL, &extra, + lossRecord->allocated_at, print_record, + /*allow_GDB_attach*/False, /*count_error*/False ); +} + +void MC_(record_user_error) ( ThreadId tid, Addr a, + Bool isAddrErr, UInt otag ) +{ + MC_Error extra; + if (otag != 0) { + tl_assert(!isAddrErr); + tl_assert( MC_(clo_mc_level) == 3 ); + } + if (!isAddrErr) { + tl_assert( MC_(clo_mc_level) >= 2 ); + } + tl_assert(VG_INVALID_THREADID != tid); + extra.Err.User.isAddrErr = isAddrErr; + extra.Err.User.ai.tag = Addr_Undescribed; + extra.Err.User.otag = otag; + extra.Err.User.origin_ec = NULL; /* Filled in later */ + VG_(maybe_record_error)( tid, Err_User, a, /*s*/NULL, &extra ); +} + +/*------------------------------------------------------------*/ +/*--- Other error operations ---*/ +/*------------------------------------------------------------*/ + +/* Compare error contexts, to detect duplicates. Note that if they + are otherwise the same, the faulting addrs and associated rwoffsets + are allowed to be different. */ +Bool MC_(eq_Error) ( VgRes res, Error* e1, Error* e2 ) +{ + MC_Error* extra1 = VG_(get_error_extra)(e1); + MC_Error* extra2 = VG_(get_error_extra)(e2); + + /* Guaranteed by calling function */ + tl_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2)); + + switch (VG_(get_error_kind)(e1)) { + case Err_CoreMem: { + Char *e1s, *e2s; + e1s = VG_(get_error_string)(e1); + e2s = VG_(get_error_string)(e2); + if (e1s == e2s) return True; + if (VG_STREQ(e1s, e2s)) return True; + return False; + } + + case Err_RegParam: + return VG_STREQ(VG_(get_error_string)(e1), VG_(get_error_string)(e2)); + + // Perhaps we should also check the addrinfo.akinds for equality. + // That would result in more error reports, but only in cases where + // a register contains uninitialised bytes and points to memory + // containing uninitialised bytes. Currently, the 2nd of those to be + // detected won't be reported. That is (nearly?) always the memory + // error, which is good. + case Err_MemParam: + if (!VG_STREQ(VG_(get_error_string)(e1), + VG_(get_error_string)(e2))) return False; + // fall through + case Err_User: + return ( extra1->Err.User.isAddrErr == extra2->Err.User.isAddrErr + ? True : False ); + + case Err_Free: + case Err_FreeMismatch: + case Err_Jump: + case Err_IllegalMempool: + case Err_Overlap: + case Err_Cond: + return True; + + case Err_Addr: + return ( extra1->Err.Addr.szB == extra2->Err.Addr.szB + ? True : False ); + + case Err_Value: + return ( extra1->Err.Value.szB == extra2->Err.Value.szB + ? True : False ); + + case Err_Leak: + VG_(tool_panic)("Shouldn't get Err_Leak in mc_eq_Error,\n" + "since it's handled with VG_(unique_error)()!"); + + default: + VG_(printf)("Error:\n unknown error code %d\n", + VG_(get_error_kind)(e1)); + VG_(tool_panic)("unknown error code in mc_eq_Error"); + } +} + +/* Function used when searching MC_Chunk lists */ +static Bool addr_is_in_MC_Chunk(MC_Chunk* mc, Addr a) +{ + // Nb: this is not quite right! It assumes that the heap block has + // a redzone of size MC_MALLOC_REDZONE_SZB. That's true for malloc'd + // blocks, but not necessarily true for custom-alloc'd blocks. So + // in some cases this could result in an incorrect description (eg. + // saying "12 bytes after block A" when really it's within block B. + // Fixing would require adding redzone size to MC_Chunks, though. + return VG_(addr_is_in_block)( a, mc->data, mc->szB, + MC_MALLOC_REDZONE_SZB ); +} + +// Forward declaration +static Bool client_block_maybe_describe( Addr a, AddrInfo* ai ); + + +/* Describe an address as best you can, for error messages, + putting the result in ai. */ +static void describe_addr ( Addr a, /*OUT*/AddrInfo* ai ) +{ + MC_Chunk* mc; + ThreadId tid; + Addr stack_min, stack_max; + VgSectKind sect; + + tl_assert(Addr_Undescribed == ai->tag); + + /* Perhaps it's a user-def'd block? */ + if (client_block_maybe_describe( a, ai )) { + return; + } + /* Search for a recently freed block which might bracket it. */ + mc = MC_(get_freed_list_head)(); + while (mc) { + if (addr_is_in_MC_Chunk(mc, a)) { + ai->tag = Addr_Block; + ai->Addr.Block.block_kind = Block_Freed; + ai->Addr.Block.block_desc = "block"; + ai->Addr.Block.block_szB = mc->szB; + ai->Addr.Block.rwoffset = (Int)a - (Int)mc->data; + ai->Addr.Block.lastchange = mc->where; + return; + } + mc = mc->next; + } + /* Search for a currently malloc'd block which might bracket it. */ + VG_(HT_ResetIter)(MC_(malloc_list)); + while ( (mc = VG_(HT_Next)(MC_(malloc_list))) ) { + if (addr_is_in_MC_Chunk(mc, a)) { + ai->tag = Addr_Block; + ai->Addr.Block.block_kind = Block_Mallocd; + ai->Addr.Block.block_desc = "block"; + ai->Addr.Block.block_szB = mc->szB; + ai->Addr.Block.rwoffset = (Int)a - (Int)mc->data; + ai->Addr.Block.lastchange = mc->where; + return; + } + } + /* Perhaps the variable type/location data describes it? */ + tl_assert(sizeof(ai->Addr.Variable.descr1) + == sizeof(ai->Addr.Variable.descr2)); + VG_(memset)( &ai->Addr.Variable.descr1, + 0, sizeof(ai->Addr.Variable.descr1)); + VG_(memset)( &ai->Addr.Variable.descr2, + 0, sizeof(ai->Addr.Variable.descr2)); + if (VG_(get_data_description)( + &ai->Addr.Variable.descr1[0], + &ai->Addr.Variable.descr2[0], + sizeof(ai->Addr.Variable.descr1)-1, + a )) { + ai->tag = Addr_Variable; + tl_assert( ai->Addr.Variable.descr1 + [ sizeof(ai->Addr.Variable.descr1)-1 ] == 0); + tl_assert( ai->Addr.Variable.descr2 + [ sizeof(ai->Addr.Variable.descr2)-1 ] == 0); + return; + } + /* Have a look at the low level data symbols - perhaps it's in + there. */ + VG_(memset)( &ai->Addr.DataSym.name, + 0, sizeof(ai->Addr.DataSym.name)); + if (VG_(get_datasym_and_offset)( + a, &ai->Addr.DataSym.name[0], + sizeof(ai->Addr.DataSym.name)-1, + &ai->Addr.DataSym.offset )) { + ai->tag = Addr_DataSym; + tl_assert( ai->Addr.DataSym.name + [ sizeof(ai->Addr.DataSym.name)-1 ] == 0); + return; + } + /* Perhaps it's on a thread's stack? */ + VG_(thread_stack_reset_iter)(&tid); + while ( VG_(thread_stack_next)(&tid, &stack_min, &stack_max) ) { + if (stack_min - VG_STACK_REDZONE_SZB <= a && a <= stack_max) { + ai->tag = Addr_Stack; + ai->Addr.Stack.tid = tid; + return; + } + } + /* last ditch attempt at classification */ + tl_assert( sizeof(ai->Addr.SectKind.objname) > 4 ); + VG_(memset)( &ai->Addr.SectKind.objname, + 0, sizeof(ai->Addr.SectKind.objname)); + VG_(strcpy)( ai->Addr.SectKind.objname, "???" ); + sect = VG_(seginfo_sect_kind)( &ai->Addr.SectKind.objname[0], + sizeof(ai->Addr.SectKind.objname)-1, a); + if (sect != Vg_SectUnknown) { + ai->tag = Addr_SectKind; + ai->Addr.SectKind.kind = sect; + tl_assert( ai->Addr.SectKind.objname + [ sizeof(ai->Addr.SectKind.objname)-1 ] == 0); + return; + } + /* Clueless ... */ + ai->tag = Addr_Unknown; + return; +} + +/* Fill in *origin_ec as specified by otag, or NULL it out if otag + does not refer to a known origin. */ +static void update_origin ( /*OUT*/ExeContext** origin_ec, + UInt otag ) +{ + UInt ecu = otag & ~3; + *origin_ec = NULL; + if (VG_(is_plausible_ECU)(ecu)) { + *origin_ec = VG_(get_ExeContext_from_ECU)( ecu ); + } +} + +/* Updates the copy with address info if necessary (but not for all errors). */ +UInt MC_(update_Error_extra)( Error* err ) +{ + MC_Error* extra = VG_(get_error_extra)(err); + + switch (VG_(get_error_kind)(err)) { + // These ones don't have addresses associated with them, and so don't + // need any updating. + case Err_CoreMem: + //case Err_Value: + //case Err_Cond: + case Err_Overlap: + // For Err_Leaks the returned size does not matter -- they are always + // shown with VG_(unique_error)() so they 'extra' not copied. But + // we make it consistent with the others. + case Err_Leak: + return sizeof(MC_Error); + + // For value errors, get the ExeContext corresponding to the + // origin tag. Note that it is a kludge to assume that + // a length-1 trace indicates a stack origin. FIXME. + case Err_Value: + update_origin( &extra->Err.Value.origin_ec, + extra->Err.Value.otag ); + return sizeof(MC_Error); + case Err_Cond: + update_origin( &extra->Err.Cond.origin_ec, + extra->Err.Cond.otag ); + return sizeof(MC_Error); + case Err_RegParam: + update_origin( &extra->Err.RegParam.origin_ec, + extra->Err.RegParam.otag ); + return sizeof(MC_Error); + + // These ones always involve a memory address. + case Err_Addr: + describe_addr ( VG_(get_error_address)(err), + &extra->Err.Addr.ai ); + return sizeof(MC_Error); + case Err_MemParam: + describe_addr ( VG_(get_error_address)(err), + &extra->Err.MemParam.ai ); + update_origin( &extra->Err.MemParam.origin_ec, + extra->Err.MemParam.otag ); + return sizeof(MC_Error); + case Err_Jump: + describe_addr ( VG_(get_error_address)(err), + &extra->Err.Jump.ai ); + return sizeof(MC_Error); + case Err_User: + describe_addr ( VG_(get_error_address)(err), + &extra->Err.User.ai ); + update_origin( &extra->Err.User.origin_ec, + extra->Err.User.otag ); + return sizeof(MC_Error); + case Err_Free: + describe_addr ( VG_(get_error_address)(err), + &extra->Err.Free.ai ); + return sizeof(MC_Error); + case Err_IllegalMempool: + describe_addr ( VG_(get_error_address)(err), + &extra->Err.IllegalMempool.ai ); + return sizeof(MC_Error); + + // Err_FreeMismatches have already had their address described; this is + // possible because we have the MC_Chunk on hand when the error is + // detected. However, the address may be part of a user block, and if so + // we override the pre-determined description with a user block one. + case Err_FreeMismatch: { + tl_assert(extra && Block_Mallocd == + extra->Err.FreeMismatch.ai.Addr.Block.block_kind); + (void)client_block_maybe_describe( VG_(get_error_address)(err), + &extra->Err.FreeMismatch.ai ); + return sizeof(MC_Error); + } + + default: VG_(tool_panic)("mc_update_extra: bad errkind"); + } +} + +// FIXME: does this perhaps want to live somewhere else +// in this file? +static Bool client_block_maybe_describe( Addr a, + /*OUT*/AddrInfo* ai ) +{ + UInt i; + + CGenBlock* cgbs = NULL; + UWord cgb_used = 0; + MC_(get_ClientBlock_array)( &cgbs, &cgb_used ); + if (cgbs == NULL) + tl_assert(cgb_used == 0); + + /* Perhaps it's a general block ? */ + for (i = 0; i < cgb_used; i++) { + if (cgbs[i].start == 0 && cgbs[i].size == 0) + continue; + // Use zero as the redzone for client blocks. + if (VG_(addr_is_in_block)(a, cgbs[i].start, cgbs[i].size, 0)) { + /* OK - maybe it's a mempool, too? */ + MC_Mempool* mp = VG_(HT_lookup)(MC_(mempool_list), + (UWord)cgbs[i].start); + if (mp != NULL) { + if (mp->chunks != NULL) { + MC_Chunk* mc; + VG_(HT_ResetIter)(mp->chunks); + while ( (mc = VG_(HT_Next)(mp->chunks)) ) { + if (addr_is_in_MC_Chunk(mc, a)) { + ai->tag = Addr_Block; + ai->Addr.Block.block_kind = Block_MempoolChunk; + ai->Addr.Block.block_desc = "block"; + ai->Addr.Block.block_szB = mc->szB; + ai->Addr.Block.rwoffset = (Int)a - (Int)mc->data; + ai->Addr.Block.lastchange = mc->where; + return True; + } + } + } + ai->tag = Addr_Block; + ai->Addr.Block.block_kind = Block_Mempool; + ai->Addr.Block.block_desc = "mempool"; + ai->Addr.Block.block_szB = cgbs[i].size; + ai->Addr.Block.rwoffset = (Int)(a) - (Int)(cgbs[i].start); + ai->Addr.Block.lastchange = cgbs[i].where; + return True; + } + ai->tag = Addr_Block; + ai->Addr.Block.block_kind = Block_UserG; + ai->Addr.Block.block_desc = cgbs[i].desc; + ai->Addr.Block.block_szB = cgbs[i].size; + ai->Addr.Block.rwoffset = (Int)(a) - (Int)(cgbs[i].start); + ai->Addr.Block.lastchange = cgbs[i].where; + return True; + } + } + return False; +} + + +/*------------------------------------------------------------*/ +/*--- Suppressions ---*/ +/*------------------------------------------------------------*/ + +typedef + enum { + ParamSupp, // Bad syscall params + UserSupp, // Errors arising from client-request checks + CoreMemSupp, // Memory errors in core (pthread ops, signal handling) + + // Undefined value errors of given size + Value1Supp, Value2Supp, Value4Supp, Value8Supp, Value16Supp, + + // Undefined value error in conditional. + CondSupp, + + // Unaddressable read/write attempt at given size + Addr1Supp, Addr2Supp, Addr4Supp, Addr8Supp, Addr16Supp, + + JumpSupp, // Jump to unaddressable target + FreeSupp, // Invalid or mismatching free + OverlapSupp, // Overlapping blocks in memcpy(), strcpy(), etc + LeakSupp, // Something to be suppressed in a leak check. + MempoolSupp, // Memory pool suppression. + } + MC_SuppKind; + +Bool MC_(is_recognised_suppression) ( Char* name, Supp* su ) +{ + SuppKind skind; + + if (VG_STREQ(name, "Param")) skind = ParamSupp; + else if (VG_STREQ(name, "User")) skind = UserSupp; + else if (VG_STREQ(name, "CoreMem")) skind = CoreMemSupp; + else if (VG_STREQ(name, "Addr1")) skind = Addr1Supp; + else if (VG_STREQ(name, "Addr2")) skind = Addr2Supp; + else if (VG_STREQ(name, "Addr4")) skind = Addr4Supp; + else if (VG_STREQ(name, "Addr8")) skind = Addr8Supp; + else if (VG_STREQ(name, "Addr16")) skind = Addr16Supp; + else if (VG_STREQ(name, "Jump")) skind = JumpSupp; + else if (VG_STREQ(name, "Free")) skind = FreeSupp; + else if (VG_STREQ(name, "Leak")) skind = LeakSupp; + else if (VG_STREQ(name, "Overlap")) skind = OverlapSupp; + else if (VG_STREQ(name, "Mempool")) skind = MempoolSupp; + else if (VG_STREQ(name, "Cond")) skind = CondSupp; + else if (VG_STREQ(name, "Value0")) skind = CondSupp; /* backwards compat */ + else if (VG_STREQ(name, "Value1")) skind = Value1Supp; + else if (VG_STREQ(name, "Value2")) skind = Value2Supp; + else if (VG_STREQ(name, "Value4")) skind = Value4Supp; + else if (VG_STREQ(name, "Value8")) skind = Value8Supp; + else if (VG_STREQ(name, "Value16")) skind = Value16Supp; + else + return False; + + VG_(set_supp_kind)(su, skind); + return True; +} + +Bool MC_(read_extra_suppression_info) ( Int fd, Char* buf, + Int nBuf, Supp *su ) +{ + Bool eof; + + if (VG_(get_supp_kind)(su) == ParamSupp) { + eof = VG_(get_line) ( fd, buf, nBuf ); + if (eof) return False; + VG_(set_supp_string)(su, VG_(strdup)(buf)); + } + return True; +} + +Bool MC_(error_matches_suppression) ( Error* err, Supp* su ) +{ + Int su_szB; + MC_Error* extra = VG_(get_error_extra)(err); + ErrorKind ekind = VG_(get_error_kind )(err); + + switch (VG_(get_supp_kind)(su)) { + case ParamSupp: + return ((ekind == Err_RegParam || ekind == Err_MemParam) + && VG_STREQ(VG_(get_error_string)(err), + VG_(get_supp_string)(su))); + + case UserSupp: + return (ekind == Err_User); + + case CoreMemSupp: + return (ekind == Err_CoreMem + && VG_STREQ(VG_(get_error_string)(err), + VG_(get_supp_string)(su))); + + case Value1Supp: su_szB = 1; goto value_case; + case Value2Supp: su_szB = 2; goto value_case; + case Value4Supp: su_szB = 4; goto value_case; + case Value8Supp: su_szB = 8; goto value_case; + case Value16Supp:su_szB =16; goto value_case; + value_case: + return (ekind == Err_Value && extra->Err.Value.szB == su_szB); + + case CondSupp: + return (ekind == Err_Cond); + + case Addr1Supp: su_szB = 1; goto addr_case; + case Addr2Supp: su_szB = 2; goto addr_case; + case Addr4Supp: su_szB = 4; goto addr_case; + case Addr8Supp: su_szB = 8; goto addr_case; + case Addr16Supp:su_szB =16; goto addr_case; + addr_case: + return (ekind == Err_Addr && extra->Err.Addr.szB == su_szB); + + case JumpSupp: + return (ekind == Err_Jump); + + case FreeSupp: + return (ekind == Err_Free || ekind == Err_FreeMismatch); + + case OverlapSupp: + return (ekind == Err_Overlap); + + case LeakSupp: + return (ekind == Err_Leak); + + case MempoolSupp: + return (ekind == Err_IllegalMempool); + + default: + VG_(printf)("Error:\n" + " unknown suppression type %d\n", + VG_(get_supp_kind)(su)); + VG_(tool_panic)("unknown suppression type in " + "MC_(error_matches_suppression)"); + } +} + +Char* MC_(get_error_name) ( Error* err ) +{ + switch (VG_(get_error_kind)(err)) { + case Err_RegParam: return "Param"; + case Err_MemParam: return "Param"; + case Err_User: return "User"; + case Err_FreeMismatch: return "Free"; + case Err_IllegalMempool: return "Mempool"; + case Err_Free: return "Free"; + case Err_Jump: return "Jump"; + case Err_CoreMem: return "CoreMem"; + case Err_Overlap: return "Overlap"; + case Err_Leak: return "Leak"; + case Err_Cond: return "Cond"; + case Err_Addr: { + MC_Error* extra = VG_(get_error_extra)(err); + switch ( extra->Err.Addr.szB ) { + case 1: return "Addr1"; + case 2: return "Addr2"; + case 4: return "Addr4"; + case 8: return "Addr8"; + case 16: return "Addr16"; + default: VG_(tool_panic)("unexpected size for Addr"); + } + } + case Err_Value: { + MC_Error* extra = VG_(get_error_extra)(err); + switch ( extra->Err.Value.szB ) { + case 1: return "Value1"; + case 2: return "Value2"; + case 4: return "Value4"; + case 8: return "Value8"; + case 16: return "Value16"; + default: VG_(tool_panic)("unexpected size for Value"); + } + } + default: VG_(tool_panic)("get_error_name: unexpected type"); + } +} + +void MC_(print_extra_suppression_info) ( Error* err ) +{ + ErrorKind ekind = VG_(get_error_kind )(err); + if (Err_RegParam == ekind || Err_MemParam == ekind) { + VG_(printf)(" %s\n", VG_(get_error_string)(err)); + } +} + + +/*--------------------------------------------------------------------*/ +/*--- end mc_errors.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h index fb0e26b5e8..ed4c789459 100644 --- a/memcheck/mc_include.h +++ b/memcheck/mc_include.h @@ -34,6 +34,10 @@ #define MC_(str) VGAPPEND(vgMemCheck_,str) + +/* This is a private header file for use only within the + memcheck/ directory. */ + /*------------------------------------------------------------*/ /*--- Tracking the heap ---*/ /*------------------------------------------------------------*/ @@ -282,14 +286,78 @@ extern void MC_(pp_LeakError)(UInt n_this_record, UInt n_total_records, /*--- Errors and suppressions ---*/ /*------------------------------------------------------------*/ -extern void MC_(record_free_error) ( ThreadId tid, Addr a ); -extern void MC_(record_illegal_mempool_error) ( ThreadId tid, Addr a ); -extern void MC_(record_freemismatch_error) ( ThreadId tid, MC_Chunk* mc ); -extern Bool MC_(record_leak_error) ( ThreadId tid, - UInt n_this_record, - UInt n_total_records, - LossRecord* lossRecord, - Bool print_record ); +/* Did we show to the user, any errors for which an uninitialised + value origin could have been collected (but wasn't) ? If yes, + then, at the end of the run, print a 1 line message advising that a + rerun with --track-origins=yes might help. */ +Bool MC_(any_value_errors); + +/* Standard functions for error and suppressions as required by the + core/tool iface */ +Bool MC_(eq_Error) ( VgRes res, Error* e1, Error* e2 ); +void MC_(pp_Error) ( Error* err ); +UInt MC_(update_Error_extra)( Error* err ); + +Bool MC_(is_recognised_suppression) ( Char* name, Supp* su ); + +Bool MC_(read_extra_suppression_info) ( Int fd, Char* buf, + Int nBuf, Supp *su ); + +Bool MC_(error_matches_suppression) ( Error* err, Supp* su ); + +void MC_(print_extra_suppression_info) ( Error* err ); + +Char* MC_(get_error_name) ( Error* err ); + +/* Recording of errors */ +void MC_(record_address_error) ( ThreadId tid, Addr a, Int szB, + Bool isWrite ); +void MC_(record_cond_error) ( ThreadId tid, UInt otag ); +void MC_(record_value_error) ( ThreadId tid, Int szB, UInt otag ); +void MC_(record_jump_error) ( ThreadId tid, Addr a ); + +void MC_(record_free_error) ( ThreadId tid, Addr a ); +void MC_(record_illegal_mempool_error) ( ThreadId tid, Addr a ); +void MC_(record_freemismatch_error) ( ThreadId tid, MC_Chunk* mc ); + +void MC_(record_overlap_error) ( ThreadId tid, Char* function, + Addr src, Addr dst, SizeT szB ); +void MC_(record_core_mem_error) ( ThreadId tid, Bool isAddrErr, Char* msg ); +void MC_(record_regparam_error) ( ThreadId tid, Char* msg, UInt otag ); +void MC_(record_memparam_error) ( ThreadId tid, Addr a, + Bool isAddrErr, Char* msg, UInt otag ); +void MC_(record_user_error) ( ThreadId tid, Addr a, + Bool isAddrErr, UInt otag ); + +Bool MC_(record_leak_error) ( ThreadId tid, + UInt n_this_record, + UInt n_total_records, + LossRecord* lossRecord, + Bool print_record ); + +/* Is this address in a user-specified "ignored range" ? */ +Bool MC_(in_ignored_range) ( Addr a ); + + +/*------------------------------------------------------------*/ +/*--- Client blocks ---*/ +/*------------------------------------------------------------*/ + +/* Describes a client block. See mc_main.c. An unused block has + start == size == 0. */ +typedef + struct { + Addr start; + SizeT size; + ExeContext* where; + Char* desc; + } + CGenBlock; + +/* Get access to the client block array. */ +void MC_(get_ClientBlock_array)( /*OUT*/CGenBlock** blocks, + /*OUT*/UWord* nBlocks ); + /*------------------------------------------------------------*/ /*--- Command line options + defaults ---*/ diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c index e4b7e13349..a89d3c49e0 100644 --- a/memcheck/mc_main.c +++ b/memcheck/mc_main.c @@ -1027,7 +1027,7 @@ typedef static IgnoreRanges ignoreRanges; -static INLINE Bool in_ignored_range ( Addr a ) +INLINE Bool MC_(in_ignored_range) ( Addr a ) { Int i; if (LIKELY(ignoreRanges.used == 0)) @@ -1137,15 +1137,6 @@ static Bool parse_ignore_ranges ( UChar* str0 ) /* --------------- Load/store slow cases. --------------- */ -// Forward declarations -static void mc_record_address_error ( ThreadId tid, Addr a, - Int size, Bool isWrite ); -static void mc_record_core_mem_error ( ThreadId tid, Bool isAddrErr, Char* s ); -static void mc_record_regparam_error ( ThreadId tid, Char* msg, UInt otag ); -static void mc_record_memparam_error ( ThreadId tid, Addr a, - Bool isAddrErr, Char* msg, UInt otag ); -static void mc_record_jump_error ( ThreadId tid, Addr a ); - static #ifndef PERF_FAST_LOADV INLINE @@ -1229,7 +1220,7 @@ ULong mc_LOADVn_slow ( Addr a, SizeT nBits, Bool bigendian ) && n_addrs_bad < VG_WORDSIZE; if (n_addrs_bad > 0 && !partial_load_exemption_applies) - mc_record_address_error( VG_(get_running_tid)(), a, szB, False ); + MC_(record_address_error)( VG_(get_running_tid)(), a, szB, False ); return vbits64; } @@ -1318,7 +1309,7 @@ void mc_STOREVn_slow ( Addr a, SizeT nBits, ULong vbytes, Bool bigendian ) /* If an address error has happened, report it. */ if (n_addrs_bad > 0) - mc_record_address_error( VG_(get_running_tid)(), a, szB, True ); + MC_(record_address_error)( VG_(get_running_tid)(), a, szB, True ); } @@ -3387,12 +3378,12 @@ void check_mem_is_addressable ( CorePart part, ThreadId tid, Char* s, if (!ok) { switch (part) { case Vg_CoreSysCall: - mc_record_memparam_error ( tid, bad_addr, - /*isAddrErr*/True, s, 0/*otag*/ ); + MC_(record_memparam_error) ( tid, bad_addr, + /*isAddrErr*/True, s, 0/*otag*/ ); break; case Vg_CoreSignal: - mc_record_core_mem_error( tid, /*isAddrErr*/True, s ); + MC_(record_core_mem_error)( tid, /*isAddrErr*/True, s ); break; default: @@ -3414,14 +3405,14 @@ void check_mem_is_defined ( CorePart part, ThreadId tid, Char* s, switch (part) { case Vg_CoreSysCall: - mc_record_memparam_error ( tid, bad_addr, isAddrErr, s, - isAddrErr ? 0 : otag ); + MC_(record_memparam_error) ( tid, bad_addr, isAddrErr, s, + isAddrErr ? 0 : otag ); break; /* If we're being asked to jump to a silly address, record an error message before potentially crashing the entire system. */ case Vg_CoreTranslate: - mc_record_jump_error( tid, bad_addr ); + MC_(record_jump_error)( tid, bad_addr ); break; default: @@ -3442,8 +3433,8 @@ void check_mem_is_defined_asciiz ( CorePart part, ThreadId tid, res = mc_is_defined_asciiz ( (Addr)str, &bad_addr, &otag ); if (MC_Ok != res) { Bool isAddrErr = ( MC_AddrErr == res ? True : False ); - mc_record_memparam_error ( tid, bad_addr, isAddrErr, s, - isAddrErr ? 0 : otag ); + MC_(record_memparam_error) ( tid, bad_addr, isAddrErr, s, + isAddrErr ? 0 : otag ); } } @@ -3561,1264 +3552,9 @@ static void mc_pre_reg_read ( CorePart part, ThreadId tid, Char* s, /* We've found some undefinedness. See if we can also find an origin for it. */ otag = mb_get_origin_for_guest_offset( tid, offset, size ); - mc_record_regparam_error ( tid, s, otag ); -} - - -/*------------------------------------------------------------*/ -/*--- Error types ---*/ -/*------------------------------------------------------------*/ - -/* Did we show to the user, any errors for which an uninitialised - value origin could have been collected (but wasn't) ? If yes, - then, at the end of the run, print a 1 line message advising that a - rerun with --track-origins=yes might help. */ -static Bool any_value_errors = False; - - -// Different kinds of blocks. -typedef enum { - Block_Mallocd = 111, - Block_Freed, - Block_Mempool, - Block_MempoolChunk, - Block_UserG -} BlockKind; - -/* ------------------ Addresses -------------------- */ - -/* The classification of a faulting address. */ -typedef - enum { - Addr_Undescribed, // as-yet unclassified - Addr_Unknown, // classification yielded nothing useful - Addr_Block, // in malloc'd/free'd block - Addr_Stack, // on a thread's stack - Addr_DataSym, // in a global data sym - Addr_Variable, // variable described by the debug info - Addr_SectKind // last-ditch classification attempt - } - AddrTag; - -typedef - struct _AddrInfo - AddrInfo; - -struct _AddrInfo { - AddrTag tag; - union { - // As-yet unclassified. - struct { } Undescribed; - - // On a stack. - struct { - ThreadId tid; // Which thread's stack? - } Stack; - - // This covers heap blocks (normal and from mempools) and user-defined - // blocks. - struct { - BlockKind block_kind; - Char* block_desc; // "block", "mempool" or user-defined - SizeT block_szB; - OffT rwoffset; - ExeContext* lastchange; - } Block; - - // In a global .data symbol. This holds the first 63 chars of - // the variable's (zero terminated), plus an offset. - struct { - Char name[128]; - OffT offset; - } DataSym; - - // Is described by Dwarf debug info. Arbitrary strings. Must - // be the same length. - struct { - Char descr1[96]; - Char descr2[96]; - } Variable; - - // Could only narrow it down to be the PLT/GOT/etc of a given - // object. Better than nothing, perhaps. - struct { - Char objname[128]; - VgSectKind kind; - } SectKind; - - // Classification yielded nothing useful. - struct { } Unknown; - - } Addr; -}; - -/* ------------------ Errors ----------------------- */ - -/* What kind of error it is. */ -typedef - enum { - Err_Value, - Err_Cond, - Err_CoreMem, - Err_Addr, - Err_Jump, - Err_RegParam, - Err_MemParam, - Err_User, - Err_Free, - Err_FreeMismatch, - Err_Overlap, - Err_Leak, - Err_IllegalMempool, - } - MC_ErrorTag; - - -typedef struct _MC_Error MC_Error; - -struct _MC_Error { - // Nb: we don't need the tag here, as it's stored in the Error type! Yuk. - //MC_ErrorTag tag; - - union { - // Use of an undefined value: - // - as a pointer in a load or store - // - as a jump target - struct { - SizeT szB; // size of value in bytes - // Origin info - UInt otag; // origin tag - ExeContext* origin_ec; // filled in later - } Value; - - // Use of an undefined value in a conditional branch or move. - struct { - // Origin info - UInt otag; // origin tag - ExeContext* origin_ec; // filled in later - } Cond; - - // Addressability error in core (signal-handling) operation. - // It would be good to get rid of this error kind, merge it with - // another one somehow. - struct { - } CoreMem; - - // Use of an unaddressable memory location in a load or store. - struct { - Bool isWrite; // read or write? - SizeT szB; // not used for exec (jump) errors - Bool maybe_gcc; // True if just below %esp -- could be a gcc bug - AddrInfo ai; - } Addr; - - // Jump to an unaddressable memory location. - struct { - AddrInfo ai; - } Jump; - - // System call register input contains undefined bytes. - struct { - // Origin info - UInt otag; // origin tag - ExeContext* origin_ec; // filled in later - } RegParam; - - // System call memory input contains undefined/unaddressable bytes - struct { - Bool isAddrErr; // Addressability or definedness error? - AddrInfo ai; - // Origin info - UInt otag; // origin tag - ExeContext* origin_ec; // filled in later - } MemParam; - - // Problem found from a client request like CHECK_MEM_IS_ADDRESSABLE. - struct { - Bool isAddrErr; // Addressability or definedness error? - AddrInfo ai; - // Origin info - UInt otag; // origin tag - ExeContext* origin_ec; // filled in later - } User; - - // Program tried to free() something that's not a heap block (this - // covers double-frees). */ - struct { - AddrInfo ai; - } Free; - - // Program allocates heap block with one function - // (malloc/new/new[]/custom) and deallocates with not the matching one. - struct { - AddrInfo ai; - } FreeMismatch; - - // Call to strcpy, memcpy, etc, with overlapping blocks. - struct { - Addr src; // Source block - Addr dst; // Destination block - Int szB; // Size in bytes; 0 if unused. - } Overlap; - - // A memory leak. - struct { - UInt n_this_record; - UInt n_total_records; - LossRecord* lossRecord; - } Leak; - - // A memory pool error. - struct { - AddrInfo ai; - } IllegalMempool; - - } Err; -}; - - -/*------------------------------------------------------------*/ -/*--- Printing errors ---*/ -/*------------------------------------------------------------*/ - -static void mc_pp_AddrInfo ( Addr a, AddrInfo* ai, Bool maybe_gcc ) -{ - HChar* xpre = VG_(clo_xml) ? " " : " "; - HChar* xpost = VG_(clo_xml) ? "" : ""; - - switch (ai->tag) { - case Addr_Unknown: - if (maybe_gcc) { - VG_(message)(Vg_UserMsg, - "%sAddress 0x%llx is just below the stack ptr. " - "To suppress, use: --workaround-gcc296-bugs=yes%s", - xpre, (ULong)a, xpost - ); - } else { - VG_(message)(Vg_UserMsg, - "%sAddress 0x%llx " - "is not stack'd, malloc'd or (recently) free'd%s", - xpre, (ULong)a, xpost); - } - break; - - case Addr_Stack: - VG_(message)(Vg_UserMsg, - "%sAddress 0x%llx is on thread %d's stack%s", - xpre, (ULong)a, ai->Addr.Stack.tid, xpost); - break; - - case Addr_Block: { - SizeT block_szB = ai->Addr.Block.block_szB; - OffT rwoffset = ai->Addr.Block.rwoffset; - SizeT delta; - const Char* relative; - - if (rwoffset < 0) { - delta = (SizeT)(-rwoffset); - relative = "before"; - } else if (rwoffset >= block_szB) { - delta = rwoffset - block_szB; - relative = "after"; - } else { - delta = rwoffset; - relative = "inside"; - } - VG_(message)(Vg_UserMsg, - "%sAddress 0x%lx is %,lu bytes %s a %s of size %,lu %s%s", - xpre, - a, delta, relative, ai->Addr.Block.block_desc, - block_szB, - ai->Addr.Block.block_kind==Block_Mallocd ? "alloc'd" - : ai->Addr.Block.block_kind==Block_Freed ? "free'd" - : "client-defined", - xpost); - VG_(pp_ExeContext)(ai->Addr.Block.lastchange); - break; - } - - case Addr_DataSym: - VG_(message)(Vg_UserMsg, - "%sAddress 0x%llx is %llu bytes " - "inside data symbol \"%t\"%s", - xpre, - (ULong)a, - (ULong)ai->Addr.DataSym.offset, - ai->Addr.DataSym.name, - xpost); - break; - - case Addr_Variable: - if (ai->Addr.Variable.descr1[0] != '\0') - VG_(message)(Vg_UserMsg, "%s%s%s", - xpre, ai->Addr.Variable.descr1, xpost); - if (ai->Addr.Variable.descr2[0] != '\0') - VG_(message)(Vg_UserMsg, "%s%s%s", - xpre, ai->Addr.Variable.descr2, xpost); - break; - - case Addr_SectKind: - VG_(message)(Vg_UserMsg, - "%sAddress 0x%llx is in the %t segment of %t%s", - xpre, - (ULong)a, - VG_(pp_SectKind)(ai->Addr.SectKind.kind), - ai->Addr.SectKind.objname, - xpost); - break; - - default: - VG_(tool_panic)("mc_pp_AddrInfo"); - } -} - -static const HChar* str_leak_lossmode ( Reachedness lossmode ) -{ - const HChar *loss = "?"; - switch (lossmode) { - case Unreached: loss = "definitely lost"; break; - case IndirectLeak: loss = "indirectly lost"; break; - case Interior: loss = "possibly lost"; break; - case Proper: loss = "still reachable"; break; - } - return loss; -} - -static const HChar* xml_leak_kind ( Reachedness lossmode ) -{ - const HChar *loss = "?"; - switch (lossmode) { - case Unreached: loss = "Leak_DefinitelyLost"; break; - case IndirectLeak: loss = "Leak_IndirectlyLost"; break; - case Interior: loss = "Leak_PossiblyLost"; break; - case Proper: loss = "Leak_StillReachable"; break; - } - return loss; -} - -static void mc_pp_msg( Char* xml_name, Error* err, const HChar* format, ... ) -{ - HChar* xpre = VG_(clo_xml) ? " " : ""; - HChar* xpost = VG_(clo_xml) ? "" : ""; - Char buf[256]; - va_list vargs; - - if (VG_(clo_xml)) - VG_(message)(Vg_UserMsg, " %s", xml_name); - // Stick xpre and xpost on the front and back of the format string. - VG_(snprintf)(buf, 256, "%s%s%s", xpre, format, xpost); - va_start(vargs, format); - VG_(vmessage) ( Vg_UserMsg, buf, vargs ); - va_end(vargs); - VG_(pp_ExeContext)( VG_(get_error_where)(err) ); -} - -static void mc_pp_origin ( ExeContext* ec, UInt okind ) -{ - HChar* src = NULL; - HChar* xpre = VG_(clo_xml) ? " " : " "; - HChar* xpost = VG_(clo_xml) ? "" : ""; - tl_assert(ec); - - switch (okind) { - case MC_OKIND_STACK: src = " by a stack allocation"; break; - case MC_OKIND_HEAP: src = " by a heap allocation"; break; - case MC_OKIND_USER: src = " by a client request"; break; - case MC_OKIND_UNKNOWN: src = ""; break; - } - tl_assert(src); /* guards against invalid 'okind' */ - - if (VG_(clo_xml)) { - VG_(message)(Vg_UserMsg, " "); - } - - VG_(message)(Vg_UserMsg, "%sUninitialised value was created%s%s", - xpre, src, xpost); - VG_(pp_ExeContext)( ec ); - if (VG_(clo_xml)) { - VG_(message)(Vg_UserMsg, " "); - } -} - -static void mc_pp_Error ( Error* err ) -{ - MC_Error* extra = VG_(get_error_extra)(err); - - switch (VG_(get_error_kind)(err)) { - case Err_CoreMem: { - /* What the hell *is* a CoreMemError? jrs 2005-May-18 */ - /* As of 2006-Dec-14, it's caused by unaddressable bytes in a - signal handler frame. --njn */ - mc_pp_msg("CoreMemError", err, - "%s contains unaddressable byte(s)", - VG_(get_error_string)(err)); - break; - } - - case Err_Value: - any_value_errors = True; - if (1 || extra->Err.Value.otag == 0) { - mc_pp_msg("UninitValue", err, - "Use of uninitialised value of size %d", - extra->Err.Value.szB); - } else { - mc_pp_msg("UninitValue", err, - "Use of uninitialised value of size %d (otag %u)", - extra->Err.Value.szB, extra->Err.Value.otag); - } - if (extra->Err.Value.origin_ec) - mc_pp_origin( extra->Err.Value.origin_ec, - extra->Err.Value.otag & 3 ); - break; - - case Err_Cond: - any_value_errors = True; - if (1 || extra->Err.Cond.otag == 0) { - mc_pp_msg("UninitCondition", err, - "Conditional jump or move depends" - " on uninitialised value(s)"); - } else { - mc_pp_msg("UninitCondition", err, - "Conditional jump or move depends" - " on uninitialised value(s) (otag %u)", - extra->Err.Cond.otag); - } - if (extra->Err.Cond.origin_ec) - mc_pp_origin( extra->Err.Cond.origin_ec, - extra->Err.Cond.otag & 3 ); - break; - - case Err_RegParam: - any_value_errors = True; - mc_pp_msg("SyscallParam", err, - "Syscall param %s contains uninitialised byte(s)", - VG_(get_error_string)(err)); - if (extra->Err.RegParam.origin_ec) - mc_pp_origin( extra->Err.RegParam.origin_ec, - extra->Err.RegParam.otag & 3 ); - break; - - case Err_MemParam: - if (!extra->Err.MemParam.isAddrErr) - any_value_errors = True; - mc_pp_msg("SyscallParam", err, - "Syscall param %s points to %s byte(s)", - VG_(get_error_string)(err), - ( extra->Err.MemParam.isAddrErr - ? "unaddressable" : "uninitialised" )); - mc_pp_AddrInfo(VG_(get_error_address)(err), - &extra->Err.MemParam.ai, False); - if (extra->Err.MemParam.origin_ec && !extra->Err.MemParam.isAddrErr) - mc_pp_origin( extra->Err.MemParam.origin_ec, - extra->Err.MemParam.otag & 3 ); - break; - - case Err_User: - if (!extra->Err.User.isAddrErr) - any_value_errors = True; - mc_pp_msg("ClientCheck", err, - "%s byte(s) found during client check request", - ( extra->Err.User.isAddrErr - ? "Unaddressable" : "Uninitialised" )); - mc_pp_AddrInfo(VG_(get_error_address)(err), &extra->Err.User.ai, - False); - if (extra->Err.User.origin_ec && !extra->Err.User.isAddrErr) - mc_pp_origin( extra->Err.User.origin_ec, - extra->Err.User.otag & 3 ); - break; - - case Err_Free: - mc_pp_msg("InvalidFree", err, - "Invalid free() / delete / delete[]"); - mc_pp_AddrInfo(VG_(get_error_address)(err), - &extra->Err.Free.ai, False); - break; - - case Err_FreeMismatch: - mc_pp_msg("MismatchedFree", err, - "Mismatched free() / delete / delete []"); - mc_pp_AddrInfo(VG_(get_error_address)(err), - &extra->Err.FreeMismatch.ai, False); - break; - - case Err_Addr: - if (extra->Err.Addr.isWrite) { - mc_pp_msg("InvalidWrite", err, - "Invalid write of size %d", - extra->Err.Addr.szB); - } else { - mc_pp_msg("InvalidRead", err, - "Invalid read of size %d", - extra->Err.Addr.szB); - } - mc_pp_AddrInfo(VG_(get_error_address)(err), &extra->Err.Addr.ai, - extra->Err.Addr.maybe_gcc); - break; - - case Err_Jump: - mc_pp_msg("InvalidJump", err, - "Jump to the invalid address stated on the next line"); - mc_pp_AddrInfo(VG_(get_error_address)(err), &extra->Err.Jump.ai, - False); - break; - - case Err_Overlap: - if (extra->Err.Overlap.szB == 0) - mc_pp_msg("Overlap", err, - "Source and destination overlap in %s(%p, %p)", - VG_(get_error_string)(err), - extra->Err.Overlap.dst, extra->Err.Overlap.src); - else - mc_pp_msg("Overlap", err, - "Source and destination overlap in %s(%p, %p, %d)", - VG_(get_error_string)(err), - extra->Err.Overlap.dst, extra->Err.Overlap.src, - extra->Err.Overlap.szB); - break; - - case Err_IllegalMempool: - mc_pp_msg("InvalidMemPool", err, - "Illegal memory pool address"); - mc_pp_AddrInfo(VG_(get_error_address)(err), - &extra->Err.IllegalMempool.ai, False); - break; - - case Err_Leak: { - HChar* xpre = VG_(clo_xml) ? " " : ""; - HChar* xpost = VG_(clo_xml) ? "" : ""; - UInt n_this_record = extra->Err.Leak.n_this_record; - UInt n_total_records = extra->Err.Leak.n_total_records; - LossRecord* l = extra->Err.Leak.lossRecord; - - if (VG_(clo_xml)) { - VG_(message)(Vg_UserMsg, " %t", - xml_leak_kind(l->loss_mode)); - } else { - VG_(message)(Vg_UserMsg, ""); - } - - if (l->indirect_bytes) { - VG_(message)(Vg_UserMsg, - "%s%,lu (%,lu direct, %,lu indirect) bytes in %,u blocks" - " are %s in loss record %,u of %,u%s", - xpre, - l->total_bytes + l->indirect_bytes, - l->total_bytes, l->indirect_bytes, l->num_blocks, - str_leak_lossmode(l->loss_mode), n_this_record, n_total_records, - xpost - ); - if (VG_(clo_xml)) { - // Nb: don't put commas in these XML numbers - VG_(message)(Vg_UserMsg, " %lu", - l->total_bytes + l->indirect_bytes); - VG_(message)(Vg_UserMsg, " %u", - l->num_blocks); - } - } else { - VG_(message)( - Vg_UserMsg, - "%s%,lu bytes in %,u blocks are %s in loss record %,u of %,u%s", - xpre, - l->total_bytes, l->num_blocks, - str_leak_lossmode(l->loss_mode), n_this_record, n_total_records, - xpost - ); - if (VG_(clo_xml)) { - VG_(message)(Vg_UserMsg, " %d", - l->total_bytes); - VG_(message)(Vg_UserMsg, " %d", - l->num_blocks); - } - } - VG_(pp_ExeContext)(l->allocated_at); - break; - } - - default: - VG_(printf)("Error:\n unknown Memcheck error code %d\n", - VG_(get_error_kind)(err)); - VG_(tool_panic)("unknown error code in mc_pp_Error)"); - } -} - -/*------------------------------------------------------------*/ -/*--- Recording errors ---*/ -/*------------------------------------------------------------*/ - -/* These many bytes below %ESP are considered addressible if we're - doing the --workaround-gcc296-bugs hack. */ -#define VG_GCC296_BUG_STACK_SLOP 1024 - -/* Is this address within some small distance below %ESP? Used only - for the --workaround-gcc296-bugs kludge. */ -static Bool is_just_below_ESP( Addr esp, Addr aa ) -{ - if (esp > aa && (esp - aa) <= VG_GCC296_BUG_STACK_SLOP) - return True; - else - return False; -} - -/* --- Called from generated and non-generated code --- */ - -static void mc_record_address_error ( ThreadId tid, Addr a, Int szB, - Bool isWrite ) -{ - MC_Error extra; - Bool just_below_esp; - - if (in_ignored_range(a)) - return; - -# if defined(VGP_ppc32_aix5) || defined(VGP_ppc64_aix5) - /* AIX zero-page handling. On AIX, reads from page zero are, - bizarrely enough, legitimate. Writes to page zero aren't, - though. Since memcheck can't distinguish reads from writes, the - best we can do is to 'act normal' and mark the A bits in the - normal way as noaccess, but then hide any reads from that page - that get reported here. */ - if ((!isWrite) && a >= 0 && a < 4096 && a+szB <= 4096) - return; - - /* Appalling AIX hack. It suppresses reads done by glink - fragments. Getting rid of this would require figuring out - somehow where the referenced data areas are (and their - sizes). */ - if ((!isWrite) && szB == sizeof(Word)) { - UInt i1, i2; - UInt* pc = (UInt*)VG_(get_IP)(tid); - if (sizeof(Word) == 4) { - i1 = 0x800c0000; /* lwz r0,0(r12) */ - i2 = 0x804c0004; /* lwz r2,4(r12) */ - } else { - i1 = 0xe80c0000; /* ld r0,0(r12) */ - i2 = 0xe84c0008; /* ld r2,8(r12) */ - } - if (pc[0] == i1 && pc[1] == i2) return; - if (pc[0] == i2 && pc[-1] == i1) return; - } -# endif - - just_below_esp = is_just_below_ESP( VG_(get_SP)(tid), a ); - - /* If this is caused by an access immediately below %ESP, and the - user asks nicely, we just ignore it. */ - if (MC_(clo_workaround_gcc296_bugs) && just_below_esp) - return; - - extra.Err.Addr.isWrite = isWrite; - extra.Err.Addr.szB = szB; - extra.Err.Addr.maybe_gcc = just_below_esp; - extra.Err.Addr.ai.tag = Addr_Undescribed; - VG_(maybe_record_error)( tid, Err_Addr, a, /*s*/NULL, &extra ); -} - -static void mc_record_value_error ( ThreadId tid, Int szB, UInt otag ) -{ - MC_Error extra; - tl_assert( MC_(clo_mc_level) >= 2 ); - if (otag > 0) - tl_assert( MC_(clo_mc_level) == 3 ); - extra.Err.Value.szB = szB; - extra.Err.Value.otag = otag; - extra.Err.Value.origin_ec = NULL; /* Filled in later */ - VG_(maybe_record_error)( tid, Err_Value, /*addr*/0, /*s*/NULL, &extra ); -} - -static void mc_record_cond_error ( ThreadId tid, UInt otag ) -{ - MC_Error extra; - tl_assert( MC_(clo_mc_level) >= 2 ); - if (otag > 0) - tl_assert( MC_(clo_mc_level) == 3 ); - extra.Err.Cond.otag = otag; - extra.Err.Cond.origin_ec = NULL; /* Filled in later */ - VG_(maybe_record_error)( tid, Err_Cond, /*addr*/0, /*s*/NULL, &extra ); -} - -/* --- Called from non-generated code --- */ - -/* This is for memory errors in pthread functions, as opposed to pthread API - errors which are found by the core. */ -static void mc_record_core_mem_error ( ThreadId tid, Bool isAddrErr, Char* msg ) -{ - VG_(maybe_record_error)( tid, Err_CoreMem, /*addr*/0, msg, /*extra*/NULL ); -} - -static void mc_record_regparam_error ( ThreadId tid, Char* msg, UInt otag ) -{ - MC_Error extra; - tl_assert(VG_INVALID_THREADID != tid); - if (otag > 0) - tl_assert( MC_(clo_mc_level) == 3 ); - extra.Err.RegParam.otag = otag; - extra.Err.RegParam.origin_ec = NULL; /* Filled in later */ - VG_(maybe_record_error)( tid, Err_RegParam, /*addr*/0, msg, &extra ); -} - -static void mc_record_memparam_error ( ThreadId tid, Addr a, - Bool isAddrErr, Char* msg, UInt otag ) -{ - MC_Error extra; - tl_assert(VG_INVALID_THREADID != tid); - if (!isAddrErr) - tl_assert( MC_(clo_mc_level) >= 2 ); - if (otag != 0) { - tl_assert( MC_(clo_mc_level) == 3 ); - tl_assert( !isAddrErr ); - } - extra.Err.MemParam.isAddrErr = isAddrErr; - extra.Err.MemParam.ai.tag = Addr_Undescribed; - extra.Err.MemParam.otag = otag; - extra.Err.MemParam.origin_ec = NULL; /* Filled in later */ - VG_(maybe_record_error)( tid, Err_MemParam, a, msg, &extra ); -} - -static void mc_record_jump_error ( ThreadId tid, Addr a ) -{ - MC_Error extra; - tl_assert(VG_INVALID_THREADID != tid); - extra.Err.Jump.ai.tag = Addr_Undescribed; - VG_(maybe_record_error)( tid, Err_Jump, a, /*s*/NULL, &extra ); -} - -void MC_(record_free_error) ( ThreadId tid, Addr a ) -{ - MC_Error extra; - tl_assert(VG_INVALID_THREADID != tid); - extra.Err.Free.ai.tag = Addr_Undescribed; - VG_(maybe_record_error)( tid, Err_Free, a, /*s*/NULL, &extra ); -} - -void MC_(record_freemismatch_error) ( ThreadId tid, MC_Chunk* mc ) -{ - MC_Error extra; - AddrInfo* ai = &extra.Err.FreeMismatch.ai; - tl_assert(VG_INVALID_THREADID != tid); - ai->tag = Addr_Block; - ai->Addr.Block.block_kind = Block_Mallocd; // Nb: Not 'Block_Freed' - ai->Addr.Block.block_desc = "block"; - ai->Addr.Block.block_szB = mc->szB; - ai->Addr.Block.rwoffset = 0; - ai->Addr.Block.lastchange = mc->where; - VG_(maybe_record_error)( tid, Err_FreeMismatch, mc->data, /*s*/NULL, - &extra ); -} - -void MC_(record_illegal_mempool_error) ( ThreadId tid, Addr a ) -{ - MC_Error extra; - tl_assert(VG_INVALID_THREADID != tid); - extra.Err.IllegalMempool.ai.tag = Addr_Undescribed; - VG_(maybe_record_error)( tid, Err_IllegalMempool, a, /*s*/NULL, &extra ); -} - -static void mc_record_overlap_error ( ThreadId tid, Char* function, - Addr src, Addr dst, SizeT szB ) -{ - MC_Error extra; - tl_assert(VG_INVALID_THREADID != tid); - extra.Err.Overlap.src = src; - extra.Err.Overlap.dst = dst; - extra.Err.Overlap.szB = szB; - VG_(maybe_record_error)( - tid, Err_Overlap, /*addr*/0, /*s*/function, &extra ); -} - -Bool MC_(record_leak_error) ( ThreadId tid, UInt n_this_record, - UInt n_total_records, LossRecord* lossRecord, - Bool print_record ) -{ - MC_Error extra; - extra.Err.Leak.n_this_record = n_this_record; - extra.Err.Leak.n_total_records = n_total_records; - extra.Err.Leak.lossRecord = lossRecord; - return - VG_(unique_error) ( tid, Err_Leak, /*Addr*/0, /*s*/NULL, &extra, - lossRecord->allocated_at, print_record, - /*allow_GDB_attach*/False, /*count_error*/False ); -} - -static void mc_record_user_error ( ThreadId tid, Addr a, - Bool isAddrErr, UInt otag ) -{ - MC_Error extra; - if (otag != 0) { - tl_assert(!isAddrErr); - tl_assert( MC_(clo_mc_level) == 3 ); - } - if (!isAddrErr) { - tl_assert( MC_(clo_mc_level) >= 2 ); - } - tl_assert(VG_INVALID_THREADID != tid); - extra.Err.User.isAddrErr = isAddrErr; - extra.Err.User.ai.tag = Addr_Undescribed; - extra.Err.User.otag = otag; - extra.Err.User.origin_ec = NULL; /* Filled in later */ - VG_(maybe_record_error)( tid, Err_User, a, /*s*/NULL, &extra ); -} - -/*------------------------------------------------------------*/ -/*--- Other error operations ---*/ -/*------------------------------------------------------------*/ - -/* Compare error contexts, to detect duplicates. Note that if they - are otherwise the same, the faulting addrs and associated rwoffsets - are allowed to be different. */ -static Bool mc_eq_Error ( VgRes res, Error* e1, Error* e2 ) -{ - MC_Error* extra1 = VG_(get_error_extra)(e1); - MC_Error* extra2 = VG_(get_error_extra)(e2); - - /* Guaranteed by calling function */ - tl_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2)); - - switch (VG_(get_error_kind)(e1)) { - case Err_CoreMem: { - Char *e1s, *e2s; - e1s = VG_(get_error_string)(e1); - e2s = VG_(get_error_string)(e2); - if (e1s == e2s) return True; - if (VG_STREQ(e1s, e2s)) return True; - return False; - } - - case Err_RegParam: - return VG_STREQ(VG_(get_error_string)(e1), VG_(get_error_string)(e2)); - - // Perhaps we should also check the addrinfo.akinds for equality. - // That would result in more error reports, but only in cases where - // a register contains uninitialised bytes and points to memory - // containing uninitialised bytes. Currently, the 2nd of those to be - // detected won't be reported. That is (nearly?) always the memory - // error, which is good. - case Err_MemParam: - if (!VG_STREQ(VG_(get_error_string)(e1), - VG_(get_error_string)(e2))) return False; - // fall through - case Err_User: - return ( extra1->Err.User.isAddrErr == extra2->Err.User.isAddrErr - ? True : False ); - - case Err_Free: - case Err_FreeMismatch: - case Err_Jump: - case Err_IllegalMempool: - case Err_Overlap: - case Err_Cond: - return True; - - case Err_Addr: - return ( extra1->Err.Addr.szB == extra2->Err.Addr.szB - ? True : False ); - - case Err_Value: - return ( extra1->Err.Value.szB == extra2->Err.Value.szB - ? True : False ); - - case Err_Leak: - VG_(tool_panic)("Shouldn't get Err_Leak in mc_eq_Error,\n" - "since it's handled with VG_(unique_error)()!"); - - default: - VG_(printf)("Error:\n unknown error code %d\n", - VG_(get_error_kind)(e1)); - VG_(tool_panic)("unknown error code in mc_eq_Error"); - } -} - -/* Function used when searching MC_Chunk lists */ -static Bool addr_is_in_MC_Chunk(MC_Chunk* mc, Addr a) -{ - // Nb: this is not quite right! It assumes that the heap block has - // a redzone of size MC_MALLOC_REDZONE_SZB. That's true for malloc'd - // blocks, but not necessarily true for custom-alloc'd blocks. So - // in some cases this could result in an incorrect description (eg. - // saying "12 bytes after block A" when really it's within block B. - // Fixing would require adding redzone size to MC_Chunks, though. - return VG_(addr_is_in_block)( a, mc->data, mc->szB, - MC_MALLOC_REDZONE_SZB ); -} - -// Forward declaration -static Bool client_perm_maybe_describe( Addr a, AddrInfo* ai ); - - -/* Describe an address as best you can, for error messages, - putting the result in ai. */ -static void describe_addr ( Addr a, /*OUT*/AddrInfo* ai ) -{ - MC_Chunk* mc; - ThreadId tid; - Addr stack_min, stack_max; - VgSectKind sect; - - tl_assert(Addr_Undescribed == ai->tag); - - /* Perhaps it's a user-def'd block? */ - if (client_perm_maybe_describe( a, ai )) { - return; - } - /* Search for a recently freed block which might bracket it. */ - mc = MC_(get_freed_list_head)(); - while (mc) { - if (addr_is_in_MC_Chunk(mc, a)) { - ai->tag = Addr_Block; - ai->Addr.Block.block_kind = Block_Freed; - ai->Addr.Block.block_desc = "block"; - ai->Addr.Block.block_szB = mc->szB; - ai->Addr.Block.rwoffset = (Int)a - (Int)mc->data; - ai->Addr.Block.lastchange = mc->where; - return; - } - mc = mc->next; - } - /* Search for a currently malloc'd block which might bracket it. */ - VG_(HT_ResetIter)(MC_(malloc_list)); - while ( (mc = VG_(HT_Next)(MC_(malloc_list))) ) { - if (addr_is_in_MC_Chunk(mc, a)) { - ai->tag = Addr_Block; - ai->Addr.Block.block_kind = Block_Mallocd; - ai->Addr.Block.block_desc = "block"; - ai->Addr.Block.block_szB = mc->szB; - ai->Addr.Block.rwoffset = (Int)a - (Int)mc->data; - ai->Addr.Block.lastchange = mc->where; - return; - } - } - /* Perhaps the variable type/location data describes it? */ - tl_assert(sizeof(ai->Addr.Variable.descr1) - == sizeof(ai->Addr.Variable.descr2)); - VG_(memset)( &ai->Addr.Variable.descr1, - 0, sizeof(ai->Addr.Variable.descr1)); - VG_(memset)( &ai->Addr.Variable.descr2, - 0, sizeof(ai->Addr.Variable.descr2)); - if (VG_(get_data_description)( - &ai->Addr.Variable.descr1[0], - &ai->Addr.Variable.descr2[0], - sizeof(ai->Addr.Variable.descr1)-1, - a )) { - ai->tag = Addr_Variable; - tl_assert( ai->Addr.Variable.descr1 - [ sizeof(ai->Addr.Variable.descr1)-1 ] == 0); - tl_assert( ai->Addr.Variable.descr2 - [ sizeof(ai->Addr.Variable.descr2)-1 ] == 0); - return; - } - /* Have a look at the low level data symbols - perhaps it's in - there. */ - VG_(memset)( &ai->Addr.DataSym.name, - 0, sizeof(ai->Addr.DataSym.name)); - if (VG_(get_datasym_and_offset)( - a, &ai->Addr.DataSym.name[0], - sizeof(ai->Addr.DataSym.name)-1, - &ai->Addr.DataSym.offset )) { - ai->tag = Addr_DataSym; - tl_assert( ai->Addr.DataSym.name - [ sizeof(ai->Addr.DataSym.name)-1 ] == 0); - return; - } - /* Perhaps it's on a thread's stack? */ - VG_(thread_stack_reset_iter)(&tid); - while ( VG_(thread_stack_next)(&tid, &stack_min, &stack_max) ) { - if (stack_min - VG_STACK_REDZONE_SZB <= a && a <= stack_max) { - ai->tag = Addr_Stack; - ai->Addr.Stack.tid = tid; - return; - } - } - /* last ditch attempt at classification */ - tl_assert( sizeof(ai->Addr.SectKind.objname) > 4 ); - VG_(memset)( &ai->Addr.SectKind.objname, - 0, sizeof(ai->Addr.SectKind.objname)); - VG_(strcpy)( ai->Addr.SectKind.objname, "???" ); - sect = VG_(seginfo_sect_kind)( &ai->Addr.SectKind.objname[0], - sizeof(ai->Addr.SectKind.objname)-1, a); - if (sect != Vg_SectUnknown) { - ai->tag = Addr_SectKind; - ai->Addr.SectKind.kind = sect; - tl_assert( ai->Addr.SectKind.objname - [ sizeof(ai->Addr.SectKind.objname)-1 ] == 0); - return; - } - /* Clueless ... */ - ai->tag = Addr_Unknown; - return; -} - -/* Fill in *origin_ec as specified by otag, or NULL it out if otag - does not refer to a known origin. */ -static void update_origin ( /*OUT*/ExeContext** origin_ec, - UInt otag ) -{ - UInt ecu = otag & ~3; - *origin_ec = NULL; - if (VG_(is_plausible_ECU)(ecu)) { - *origin_ec = VG_(get_ExeContext_from_ECU)( ecu ); - } -} - -/* Updates the copy with address info if necessary (but not for all errors). */ -static UInt mc_update_extra( Error* err ) -{ - MC_Error* extra = VG_(get_error_extra)(err); - - switch (VG_(get_error_kind)(err)) { - // These ones don't have addresses associated with them, and so don't - // need any updating. - case Err_CoreMem: - //case Err_Value: - //case Err_Cond: - case Err_Overlap: - // For Err_Leaks the returned size does not matter -- they are always - // shown with VG_(unique_error)() so they 'extra' not copied. But - // we make it consistent with the others. - case Err_Leak: - return sizeof(MC_Error); - - // For value errors, get the ExeContext corresponding to the - // origin tag. Note that it is a kludge to assume that - // a length-1 trace indicates a stack origin. FIXME. - case Err_Value: - update_origin( &extra->Err.Value.origin_ec, - extra->Err.Value.otag ); - return sizeof(MC_Error); - case Err_Cond: - update_origin( &extra->Err.Cond.origin_ec, - extra->Err.Cond.otag ); - return sizeof(MC_Error); - case Err_RegParam: - update_origin( &extra->Err.RegParam.origin_ec, - extra->Err.RegParam.otag ); - return sizeof(MC_Error); - - // These ones always involve a memory address. - case Err_Addr: - describe_addr ( VG_(get_error_address)(err), - &extra->Err.Addr.ai ); - return sizeof(MC_Error); - case Err_MemParam: - describe_addr ( VG_(get_error_address)(err), - &extra->Err.MemParam.ai ); - update_origin( &extra->Err.MemParam.origin_ec, - extra->Err.MemParam.otag ); - return sizeof(MC_Error); - case Err_Jump: - describe_addr ( VG_(get_error_address)(err), - &extra->Err.Jump.ai ); - return sizeof(MC_Error); - case Err_User: - describe_addr ( VG_(get_error_address)(err), - &extra->Err.User.ai ); - update_origin( &extra->Err.User.origin_ec, - extra->Err.User.otag ); - return sizeof(MC_Error); - case Err_Free: - describe_addr ( VG_(get_error_address)(err), - &extra->Err.Free.ai ); - return sizeof(MC_Error); - case Err_IllegalMempool: - describe_addr ( VG_(get_error_address)(err), - &extra->Err.IllegalMempool.ai ); - return sizeof(MC_Error); - - // Err_FreeMismatches have already had their address described; this is - // possible because we have the MC_Chunk on hand when the error is - // detected. However, the address may be part of a user block, and if so - // we override the pre-determined description with a user block one. - case Err_FreeMismatch: { - tl_assert(extra && Block_Mallocd == - extra->Err.FreeMismatch.ai.Addr.Block.block_kind); - (void)client_perm_maybe_describe( VG_(get_error_address)(err), - &extra->Err.FreeMismatch.ai ); - return sizeof(MC_Error); - } - - default: VG_(tool_panic)("mc_update_extra: bad errkind"); - } + MC_(record_regparam_error) ( tid, s, otag ); } -/*------------------------------------------------------------*/ -/*--- Suppressions ---*/ -/*------------------------------------------------------------*/ - -typedef - enum { - ParamSupp, // Bad syscall params - UserSupp, // Errors arising from client-request checks - CoreMemSupp, // Memory errors in core (pthread ops, signal handling) - - // Undefined value errors of given size - Value1Supp, Value2Supp, Value4Supp, Value8Supp, Value16Supp, - - // Undefined value error in conditional. - CondSupp, - - // Unaddressable read/write attempt at given size - Addr1Supp, Addr2Supp, Addr4Supp, Addr8Supp, Addr16Supp, - - JumpSupp, // Jump to unaddressable target - FreeSupp, // Invalid or mismatching free - OverlapSupp, // Overlapping blocks in memcpy(), strcpy(), etc - LeakSupp, // Something to be suppressed in a leak check. - MempoolSupp, // Memory pool suppression. - } - MC_SuppKind; - -static Bool mc_recognised_suppression ( Char* name, Supp* su ) -{ - SuppKind skind; - - if (VG_STREQ(name, "Param")) skind = ParamSupp; - else if (VG_STREQ(name, "User")) skind = UserSupp; - else if (VG_STREQ(name, "CoreMem")) skind = CoreMemSupp; - else if (VG_STREQ(name, "Addr1")) skind = Addr1Supp; - else if (VG_STREQ(name, "Addr2")) skind = Addr2Supp; - else if (VG_STREQ(name, "Addr4")) skind = Addr4Supp; - else if (VG_STREQ(name, "Addr8")) skind = Addr8Supp; - else if (VG_STREQ(name, "Addr16")) skind = Addr16Supp; - else if (VG_STREQ(name, "Jump")) skind = JumpSupp; - else if (VG_STREQ(name, "Free")) skind = FreeSupp; - else if (VG_STREQ(name, "Leak")) skind = LeakSupp; - else if (VG_STREQ(name, "Overlap")) skind = OverlapSupp; - else if (VG_STREQ(name, "Mempool")) skind = MempoolSupp; - else if (VG_STREQ(name, "Cond")) skind = CondSupp; - else if (VG_STREQ(name, "Value0")) skind = CondSupp; /* backwards compat */ - else if (VG_STREQ(name, "Value1")) skind = Value1Supp; - else if (VG_STREQ(name, "Value2")) skind = Value2Supp; - else if (VG_STREQ(name, "Value4")) skind = Value4Supp; - else if (VG_STREQ(name, "Value8")) skind = Value8Supp; - else if (VG_STREQ(name, "Value16")) skind = Value16Supp; - else - return False; - - VG_(set_supp_kind)(su, skind); - return True; -} - -static -Bool mc_read_extra_suppression_info ( Int fd, Char* buf, Int nBuf, Supp *su ) -{ - Bool eof; - - if (VG_(get_supp_kind)(su) == ParamSupp) { - eof = VG_(get_line) ( fd, buf, nBuf ); - if (eof) return False; - VG_(set_supp_string)(su, VG_(strdup)(buf)); - } - return True; -} - -static Bool mc_error_matches_suppression(Error* err, Supp* su) -{ - Int su_szB; - MC_Error* extra = VG_(get_error_extra)(err); - ErrorKind ekind = VG_(get_error_kind )(err); - - switch (VG_(get_supp_kind)(su)) { - case ParamSupp: - return ((ekind == Err_RegParam || ekind == Err_MemParam) - && VG_STREQ(VG_(get_error_string)(err), - VG_(get_supp_string)(su))); - - case UserSupp: - return (ekind == Err_User); - - case CoreMemSupp: - return (ekind == Err_CoreMem - && VG_STREQ(VG_(get_error_string)(err), - VG_(get_supp_string)(su))); - - case Value1Supp: su_szB = 1; goto value_case; - case Value2Supp: su_szB = 2; goto value_case; - case Value4Supp: su_szB = 4; goto value_case; - case Value8Supp: su_szB = 8; goto value_case; - case Value16Supp:su_szB =16; goto value_case; - value_case: - return (ekind == Err_Value && extra->Err.Value.szB == su_szB); - - case CondSupp: - return (ekind == Err_Cond); - - case Addr1Supp: su_szB = 1; goto addr_case; - case Addr2Supp: su_szB = 2; goto addr_case; - case Addr4Supp: su_szB = 4; goto addr_case; - case Addr8Supp: su_szB = 8; goto addr_case; - case Addr16Supp:su_szB =16; goto addr_case; - addr_case: - return (ekind == Err_Addr && extra->Err.Addr.szB == su_szB); - - case JumpSupp: - return (ekind == Err_Jump); - - case FreeSupp: - return (ekind == Err_Free || ekind == Err_FreeMismatch); - - case OverlapSupp: - return (ekind == Err_Overlap); - - case LeakSupp: - return (ekind == Err_Leak); - - case MempoolSupp: - return (ekind == Err_IllegalMempool); - - default: - VG_(printf)("Error:\n" - " unknown suppression type %d\n", - VG_(get_supp_kind)(su)); - VG_(tool_panic)("unknown suppression type in " - "MC_(error_matches_suppression)"); - } -} - -static Char* mc_get_error_name ( Error* err ) -{ - switch (VG_(get_error_kind)(err)) { - case Err_RegParam: return "Param"; - case Err_MemParam: return "Param"; - case Err_User: return "User"; - case Err_FreeMismatch: return "Free"; - case Err_IllegalMempool: return "Mempool"; - case Err_Free: return "Free"; - case Err_Jump: return "Jump"; - case Err_CoreMem: return "CoreMem"; - case Err_Overlap: return "Overlap"; - case Err_Leak: return "Leak"; - case Err_Cond: return "Cond"; - case Err_Addr: { - MC_Error* extra = VG_(get_error_extra)(err); - switch ( extra->Err.Addr.szB ) { - case 1: return "Addr1"; - case 2: return "Addr2"; - case 4: return "Addr4"; - case 8: return "Addr8"; - case 16: return "Addr16"; - default: VG_(tool_panic)("unexpected size for Addr"); - } - } - case Err_Value: { - MC_Error* extra = VG_(get_error_extra)(err); - switch ( extra->Err.Value.szB ) { - case 1: return "Value1"; - case 2: return "Value2"; - case 4: return "Value4"; - case 8: return "Value8"; - case 16: return "Value16"; - default: VG_(tool_panic)("unexpected size for Value"); - } - } - default: VG_(tool_panic)("get_error_name: unexpected type"); - } -} - -static void mc_print_extra_suppression_info ( Error* err ) -{ - ErrorKind ekind = VG_(get_error_kind )(err); - if (Err_RegParam == ekind || Err_MemParam == ekind) { - VG_(printf)(" %s\n", VG_(get_error_string)(err)); - } -} /*------------------------------------------------------------*/ /*--- Functions called directly from generated code: ---*/ @@ -5294,54 +4030,54 @@ void MC_(helperc_STOREV8) ( Addr a, UWord vbits8 ) /* Call these ones when an origin is available ... */ VG_REGPARM(1) void MC_(helperc_value_check0_fail_w_o) ( UWord origin ) { - mc_record_cond_error ( VG_(get_running_tid)(), (UInt)origin ); + MC_(record_cond_error) ( VG_(get_running_tid)(), (UInt)origin ); } VG_REGPARM(1) void MC_(helperc_value_check1_fail_w_o) ( UWord origin ) { - mc_record_value_error ( VG_(get_running_tid)(), 1, (UInt)origin ); + MC_(record_value_error) ( VG_(get_running_tid)(), 1, (UInt)origin ); } VG_REGPARM(1) void MC_(helperc_value_check4_fail_w_o) ( UWord origin ) { - mc_record_value_error ( VG_(get_running_tid)(), 4, (UInt)origin ); + MC_(record_value_error) ( VG_(get_running_tid)(), 4, (UInt)origin ); } VG_REGPARM(1) void MC_(helperc_value_check8_fail_w_o) ( UWord origin ) { - mc_record_value_error ( VG_(get_running_tid)(), 8, (UInt)origin ); + MC_(record_value_error) ( VG_(get_running_tid)(), 8, (UInt)origin ); } VG_REGPARM(2) void MC_(helperc_value_checkN_fail_w_o) ( HWord sz, UWord origin ) { - mc_record_value_error ( VG_(get_running_tid)(), (Int)sz, (UInt)origin ); + MC_(record_value_error) ( VG_(get_running_tid)(), (Int)sz, (UInt)origin ); } /* ... and these when an origin isn't available. */ VG_REGPARM(0) void MC_(helperc_value_check0_fail_no_o) ( void ) { - mc_record_cond_error ( VG_(get_running_tid)(), 0/*origin*/ ); + MC_(record_cond_error) ( VG_(get_running_tid)(), 0/*origin*/ ); } VG_REGPARM(0) void MC_(helperc_value_check1_fail_no_o) ( void ) { - mc_record_value_error ( VG_(get_running_tid)(), 1, 0/*origin*/ ); + MC_(record_value_error) ( VG_(get_running_tid)(), 1, 0/*origin*/ ); } VG_REGPARM(0) void MC_(helperc_value_check4_fail_no_o) ( void ) { - mc_record_value_error ( VG_(get_running_tid)(), 4, 0/*origin*/ ); + MC_(record_value_error) ( VG_(get_running_tid)(), 4, 0/*origin*/ ); } VG_REGPARM(0) void MC_(helperc_value_check8_fail_no_o) ( void ) { - mc_record_value_error ( VG_(get_running_tid)(), 8, 0/*origin*/ ); + MC_(record_value_error) ( VG_(get_running_tid)(), 8, 0/*origin*/ ); } VG_REGPARM(1) void MC_(helperc_value_checkN_fail_no_o) ( HWord sz ) { - mc_record_value_error ( VG_(get_running_tid)(), (Int)sz, 0/*origin*/ ); + MC_(record_value_error) ( VG_(get_running_tid)(), (Int)sz, 0/*origin*/ ); } @@ -5413,7 +4149,7 @@ Bool mc_is_within_valid_secondary ( Addr a ) { SecMap* sm = maybe_get_secmap_for ( a ); if (sm == NULL || sm == &sm_distinguished[SM_DIST_NOACCESS] - || in_ignored_range(a)) { + || MC_(in_ignored_range)(a)) { /* Definitely not in use. */ return False; } else { @@ -5434,7 +4170,7 @@ Bool mc_is_valid_aligned_word ( Addr a ) tl_assert(VG_IS_8_ALIGNED(a)); } if (is_mem_defined( a, sizeof(UWord), NULL, NULL) == MC_Ok - && !in_ignored_range(a)) { + && !MC_(in_ignored_range)(a)) { return True; } else { return False; @@ -5746,7 +4482,7 @@ static void mc_print_debug_usage(void) /*------------------------------------------------------------*/ -/*--- Client requests ---*/ +/*--- Client blocks ---*/ /*------------------------------------------------------------*/ /* Client block management: @@ -5761,14 +4497,7 @@ static void mc_print_debug_usage(void) An unused block has start == size == 0 */ -typedef - struct { - Addr start; - SizeT size; - ExeContext* where; - Char* desc; - } - CGenBlock; +/* type CGenBlock is defined in mc_include.h */ /* This subsystem is self-initialising. */ static UInt cgb_size = 0; @@ -5782,6 +4511,15 @@ static UInt cgb_discards = 0; /* Number of discards. */ static UInt cgb_search = 0; /* Number of searches. */ +/* Get access to the client block array. */ +void MC_(get_ClientBlock_array)( /*OUT*/CGenBlock** blocks, + /*OUT*/UWord* nBlocks ) +{ + *blocks = cgbs; + *nBlocks = cgb_used; +} + + static Int alloc_client_block ( void ) { @@ -5830,54 +4568,10 @@ static void show_client_block_stats ( void ) ); } -static Bool client_perm_maybe_describe( Addr a, AddrInfo* ai ) -{ - UInt i; - /* Perhaps it's a general block ? */ - for (i = 0; i < cgb_used; i++) { - if (cgbs[i].start == 0 && cgbs[i].size == 0) - continue; - // Use zero as the redzone for client blocks. - if (VG_(addr_is_in_block)(a, cgbs[i].start, cgbs[i].size, 0)) { - /* OK - maybe it's a mempool, too? */ - MC_Mempool* mp = VG_(HT_lookup)(MC_(mempool_list), - (UWord)cgbs[i].start); - if (mp != NULL) { - if (mp->chunks != NULL) { - MC_Chunk* mc; - VG_(HT_ResetIter)(mp->chunks); - while ( (mc = VG_(HT_Next)(mp->chunks)) ) { - if (addr_is_in_MC_Chunk(mc, a)) { - ai->tag = Addr_Block; - ai->Addr.Block.block_kind = Block_MempoolChunk; - ai->Addr.Block.block_desc = "block"; - ai->Addr.Block.block_szB = mc->szB; - ai->Addr.Block.rwoffset = (Int)a - (Int)mc->data; - ai->Addr.Block.lastchange = mc->where; - return True; - } - } - } - ai->tag = Addr_Block; - ai->Addr.Block.block_kind = Block_Mempool; - ai->Addr.Block.block_desc = "mempool"; - ai->Addr.Block.block_szB = cgbs[i].size; - ai->Addr.Block.rwoffset = (Int)(a) - (Int)(cgbs[i].start); - ai->Addr.Block.lastchange = cgbs[i].where; - return True; - } - ai->tag = Addr_Block; - ai->Addr.Block.block_kind = Block_UserG; - ai->Addr.Block.block_desc = cgbs[i].desc; - ai->Addr.Block.block_szB = cgbs[i].size; - ai->Addr.Block.rwoffset = (Int)(a) - (Int)(cgbs[i].start); - ai->Addr.Block.lastchange = cgbs[i].where; - return True; - } - } - return False; -} +/*------------------------------------------------------------*/ +/*--- Client requests ---*/ +/*------------------------------------------------------------*/ static Bool mc_handle_client_request ( ThreadId tid, UWord* arg, UWord* ret ) { @@ -5886,23 +4580,23 @@ static Bool mc_handle_client_request ( ThreadId tid, UWord* arg, UWord* ret ) Addr bad_addr; if (!VG_IS_TOOL_USERREQ('M','C',arg[0]) - && VG_USERREQ__MALLOCLIKE_BLOCK != arg[0] - && VG_USERREQ__FREELIKE_BLOCK != arg[0] - && VG_USERREQ__CREATE_MEMPOOL != arg[0] - && VG_USERREQ__DESTROY_MEMPOOL != arg[0] - && VG_USERREQ__MEMPOOL_ALLOC != arg[0] - && VG_USERREQ__MEMPOOL_FREE != arg[0] - && VG_USERREQ__MEMPOOL_TRIM != arg[0] - && VG_USERREQ__MOVE_MEMPOOL != arg[0] - && VG_USERREQ__MEMPOOL_CHANGE != arg[0] - && VG_USERREQ__MEMPOOL_EXISTS != arg[0]) + && VG_USERREQ__MALLOCLIKE_BLOCK != arg[0] + && VG_USERREQ__FREELIKE_BLOCK != arg[0] + && VG_USERREQ__CREATE_MEMPOOL != arg[0] + && VG_USERREQ__DESTROY_MEMPOOL != arg[0] + && VG_USERREQ__MEMPOOL_ALLOC != arg[0] + && VG_USERREQ__MEMPOOL_FREE != arg[0] + && VG_USERREQ__MEMPOOL_TRIM != arg[0] + && VG_USERREQ__MOVE_MEMPOOL != arg[0] + && VG_USERREQ__MEMPOOL_CHANGE != arg[0] + && VG_USERREQ__MEMPOOL_EXISTS != arg[0]) return False; switch (arg[0]) { case VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE: ok = is_mem_addressable ( arg[1], arg[2], &bad_addr ); if (!ok) - mc_record_user_error ( tid, bad_addr, /*isAddrErr*/True, 0 ); + MC_(record_user_error) ( tid, bad_addr, /*isAddrErr*/True, 0 ); *ret = ok ? (UWord)NULL : bad_addr; break; @@ -5911,9 +4605,9 @@ static Bool mc_handle_client_request ( ThreadId tid, UWord* arg, UWord* ret ) UInt otag = 0; res = is_mem_defined ( arg[1], arg[2], &bad_addr, &otag ); if (MC_AddrErr == res) - mc_record_user_error ( tid, bad_addr, /*isAddrErr*/True, 0 ); + MC_(record_user_error) ( tid, bad_addr, /*isAddrErr*/True, 0 ); else if (MC_ValueErr == res) - mc_record_user_error ( tid, bad_addr, /*isAddrErr*/False, otag ); + MC_(record_user_error) ( tid, bad_addr, /*isAddrErr*/False, otag ); *ret = ( res==MC_Ok ? (UWord)NULL : bad_addr ); break; } @@ -5929,7 +4623,8 @@ static Bool mc_handle_client_request ( ThreadId tid, UWord* arg, UWord* ret ) break; case VG_USERREQ__MAKE_MEM_UNDEFINED: - make_mem_undefined_w_tid_and_okind ( arg[1], arg[2], tid, MC_OKIND_USER ); + make_mem_undefined_w_tid_and_okind ( arg[1], arg[2], tid, + MC_OKIND_USER ); *ret = -1; break; @@ -6018,7 +4713,7 @@ static Bool mc_handle_client_request ( ThreadId tid, UWord* arg, UWord* ret ) Addr dst = (Addr) arg[2]; Addr src = (Addr) arg[3]; SizeT len = (SizeT)arg[4]; - mc_record_overlap_error(tid, s, src, dst, len); + MC_(record_overlap_error)(tid, s, src, dst, len); return True; } @@ -6474,7 +5169,6 @@ static void ocache_sarp_Clear_Origins ( Addr a, UWord len ) { /*--- Setup and finalisation ---*/ /*------------------------------------------------------------*/ - static void mc_post_clo_init ( void ) { /* If we've been asked to emit XML, mash around various other @@ -6542,7 +5236,7 @@ static void mc_fini ( Int exitcode ) } - if (any_value_errors && !VG_(clo_xml) && VG_(clo_verbosity) >= 1 + if (MC_(any_value_errors) && !VG_(clo_xml) && VG_(clo_verbosity) >= 1 && MC_(clo_mc_level) == 2) { VG_(message)(Vg_UserMsg, "Use --track-origins=yes to see where " @@ -6663,15 +5357,15 @@ static void mc_pre_clo_init(void) VG_(needs_core_errors) (); - VG_(needs_tool_errors) (mc_eq_Error, - mc_pp_Error, + VG_(needs_tool_errors) (MC_(eq_Error), + MC_(pp_Error), True,/*show TIDs for errors*/ - mc_update_extra, - mc_recognised_suppression, - mc_read_extra_suppression_info, - mc_error_matches_suppression, - mc_get_error_name, - mc_print_extra_suppression_info); + MC_(update_Error_extra), + MC_(is_recognised_suppression), + MC_(read_extra_suppression_info), + MC_(error_matches_suppression), + MC_(get_error_name), + MC_(print_extra_suppression_info)); VG_(needs_libc_freeres) (); VG_(needs_command_line_options)(mc_process_cmd_line_options, mc_print_usage, @@ -6765,5 +5459,5 @@ static void mc_pre_clo_init(void) VG_DETERMINE_INTERFACE_VERSION(mc_pre_clo_init) /*--------------------------------------------------------------------*/ -/*--- end ---*/ +/*--- end mc_main.c ---*/ /*--------------------------------------------------------------------*/