/*--------------------------------------------------------------------*/ /*--- Management of memory error messages. ---*/ /*--- mc_errcontext.c ---*/ /*--------------------------------------------------------------------*/ /* This file is part of MemCheck, a heavyweight Valgrind skin for detecting memory errors. Copyright (C) 2000-2002 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 "mc_include.h" /*------------------------------------------------------------*/ /*--- Comparing and printing errors ---*/ /*------------------------------------------------------------*/ void SK_(pp_SkinError) ( Error* err, void (*pp_ExeContext)(void) ) { MemCheckError* err_extra = VG_(get_error_extra)(err); switch (VG_(get_error_kind)(err)) { case CoreMemErr: if (err_extra->isWrite) { VG_(message)(Vg_UserMsg, "%s contains unaddressable byte(s)", VG_(get_error_string)(err)); } else { VG_(message)(Vg_UserMsg, "%s contains uninitialised or unaddressable byte(s)", VG_(get_error_string)(err)); } pp_ExeContext(); break; case ValueErr: if (err_extra->size == 0) { VG_(message)( Vg_UserMsg, "Conditional jump or move depends on uninitialised value(s)"); } else { VG_(message)(Vg_UserMsg, "Use of uninitialised value of size %d", err_extra->size); } pp_ExeContext(); break; case AddrErr: switch (err_extra->axskind) { case ReadAxs: VG_(message)(Vg_UserMsg, "Invalid read of size %d", err_extra->size ); break; case WriteAxs: VG_(message)(Vg_UserMsg, "Invalid write of size %d", err_extra->size ); break; case ExecAxs: VG_(message)(Vg_UserMsg, "Jump to the invalid address " "stated on the next line"); break; default: VG_(skin_panic)("SK_(pp_SkinError)(axskind)"); } pp_ExeContext(); MC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo); break; case FreeErr: VG_(message)(Vg_UserMsg,"Invalid free() / delete / delete[]"); /* fall through */ case FreeMismatchErr: if (VG_(get_error_kind)(err) == FreeMismatchErr) VG_(message)(Vg_UserMsg, "Mismatched free() / delete / delete []"); pp_ExeContext(); MC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo); break; case ParamErr: if (err_extra->isWrite) { VG_(message)(Vg_UserMsg, "Syscall param %s contains unaddressable byte(s)", VG_(get_error_string)(err)); } else { VG_(message)(Vg_UserMsg, "Syscall param %s contains uninitialised or " "unaddressable byte(s)", VG_(get_error_string)(err)); } pp_ExeContext(); MC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo); break; case UserErr: if (err_extra->isWrite) { VG_(message)(Vg_UserMsg, "Unaddressable byte(s) found during client check request"); } else { VG_(message)(Vg_UserMsg, "Uninitialised or " "unaddressable byte(s) found during client check request"); } pp_ExeContext(); MC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo); break; default: VG_(printf)("Error:\n unknown MemCheck error code %d\n", VG_(get_error_kind)(err)); VG_(skin_panic)("unknown error code in SK_(pp_SkinError)"); } } /*------------------------------------------------------------*/ /*--- Recording errors ---*/ /*------------------------------------------------------------*/ /* Describe an address as best you can, for error messages, putting the result in ai. */ static void mc_describe_addr ( Addr a, AddrInfo* ai ) { ShadowChunk* sc; Bool ok; ThreadId tid; /* Nested functions, yeah. Need the lexical scoping of 'a'. */ /* Closure for searching thread stacks */ Bool addr_is_in_bounds(Addr stack_min, Addr stack_max) { return (stack_min <= a && a <= stack_max); } /* Closure for searching malloc'd and free'd lists */ Bool addr_is_in_block(ShadowChunk *sh_ch) { return VG_(addr_is_in_block) ( a, VG_(get_sc_data)(sh_ch), VG_(get_sc_size)(sh_ch) ); } /* Perhaps it's a user-def'd block ? */ ok = MC_(client_perm_maybe_describe)( a, ai ); if (ok) return; /* Perhaps it's on a thread's stack? */ tid = VG_(any_matching_thread_stack)(addr_is_in_bounds); if (tid != VG_INVALID_THREADID) { ai->akind = Stack; ai->stack_tid = tid; return; } /* Search for a recently freed block which might bracket it. */ sc = MC_(any_matching_freed_ShadowChunks)(addr_is_in_block); if (NULL != sc) { ai->akind = Freed; ai->blksize = VG_(get_sc_size)(sc); ai->rwoffset = (Int)(a) - (Int)(VG_(get_sc_data)(sc)); ai->lastchange = (ExeContext*)( VG_(get_sc_extra)(sc, 0) ); return; } /* Search for a currently malloc'd block which might bracket it. */ sc = VG_(any_matching_mallocd_ShadowChunks)(addr_is_in_block); if (NULL != sc) { ai->akind = Mallocd; ai->blksize = VG_(get_sc_size)(sc); ai->rwoffset = (Int)(a) - (Int)(VG_(get_sc_data)(sc)); ai->lastchange = (ExeContext*)( VG_(get_sc_extra)(sc, 0) ); return; } /* Clueless ... */ ai->akind = Unknown; return; } /* Creates a copy of the `extra' part, updates the copy with address info if necessary, and returns the copy. */ void* SK_(dup_extra_and_update)( Error* err ) { MemCheckError* extra; MemCheckError* new_extra = NULL; extra = ((MemCheckError*)VG_(get_error_extra)(err)); if (extra != NULL) { new_extra = VG_(malloc)(sizeof(MemCheckError)); *new_extra = *extra; if (new_extra->addrinfo.akind == Undescribed) mc_describe_addr ( VG_(get_error_address)(err), &(new_extra->addrinfo) ); } return new_extra; } /* This one called from generated code. */ void MC_(record_value_error) ( Int size ) { MemCheckError err_extra; MC_(clear_MemCheckError)( &err_extra ); err_extra.size = size; VG_(maybe_record_error)( NULL, ValueErr, /*addr*/0, /*s*/NULL, &err_extra ); } /* This one called from non-generated code */ void MC_(record_user_error) ( ThreadState* tst, Addr a, Bool isWrite ) { MemCheckError err_extra; sk_assert(NULL != tst); MC_(clear_MemCheckError)( &err_extra ); err_extra.addrinfo.akind = Undescribed; err_extra.isWrite = isWrite; VG_(maybe_record_error)( tst, UserErr, a, /*s*/NULL, &err_extra ); } /*------------------------------------------------------------*/ /*--- Suppressions ---*/ /*------------------------------------------------------------*/ #define STREQ(s1,s2) (s1 != NULL && s2 != NULL \ && VG_(strcmp)((s1),(s2))==0) Bool SK_(recognised_suppression) ( Char* name, Supp* su ) { SuppKind skind; if (STREQ(name, "Param")) skind = ParamSupp; else if (STREQ(name, "CoreMem")) skind = CoreMemSupp; else if (STREQ(name, "Value0")) skind = Value0Supp; /* backwards compat */ else if (STREQ(name, "Cond")) skind = Value0Supp; else if (STREQ(name, "Value1")) skind = Value1Supp; else if (STREQ(name, "Value2")) skind = Value2Supp; else if (STREQ(name, "Value4")) skind = Value4Supp; else if (STREQ(name, "Value8")) skind = Value8Supp; else if (STREQ(name, "Addr1")) skind = Addr1Supp; else if (STREQ(name, "Addr2")) skind = Addr2Supp; else if (STREQ(name, "Addr4")) skind = Addr4Supp; else if (STREQ(name, "Addr8")) skind = Addr8Supp; else if (STREQ(name, "Free")) skind = FreeSupp; else if (STREQ(name, "Leak")) skind = LeakSupp; else return False; VG_(set_supp_kind)(su, skind); return True; } # undef STREQ /*--------------------------------------------------------------------*/ /*--- end mc_errcontext.c ---*/ /*--------------------------------------------------------------------*/