blob: 5f19e4b9c28b262f3366068bca307eda979eb432 [file] [log] [blame]
/*--------------------------------------------------------------------*/
/*--- For when the client advises Valgrind about permissions. ---*/
/*--- vg_clientperms.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Valgrind, an x86 protected-mode emulator
designed for debugging and profiling binaries on x86-Unixes.
Copyright (C) 2000-2002 Julian Seward
jseward@acm.org
Julian_Seward@muraroa.demon.co.uk
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
The GNU General Public License is contained in the file LICENSE.
*/
#include "vg_include.h"
#include "vg_constants.h"
/*------------------------------------------------------------*/
/*--- General client block management. ---*/
/*------------------------------------------------------------*/
/* This is managed as an expanding array of client block descriptors.
Indices of live descriptors are issued to the client, so it can ask
to free them later. Therefore we cannot slide live entries down
over dead ones. Instead we must use free/inuse flags and scan for
an empty slot at allocation time. This in turn means allocation is
relatively expensive, so we hope this does not happen too often.
*/
typedef
enum { CG_NotInUse, CG_NoAccess, CG_Writable, CG_Readable }
CGenBlockKind;
typedef
struct {
Addr start;
UInt size;
ExeContext* where;
CGenBlockKind kind;
}
CGenBlock;
/* This subsystem is self-initialising. */
static UInt vg_cgb_size = 0;
static UInt vg_cgb_used = 0;
static CGenBlock* vg_cgbs = NULL;
/* Stats for this subsystem. */
static UInt vg_cgb_used_MAX = 0; /* Max in use. */
static UInt vg_cgb_allocs = 0; /* Number of allocs. */
static UInt vg_cgb_discards = 0; /* Number of discards. */
static UInt vg_cgb_search = 0; /* Number of searches. */
static
Int vg_alloc_client_block ( void )
{
Int i, sz_new;
CGenBlock* cgbs_new;
vg_cgb_allocs++;
for (i = 0; i < vg_cgb_used; i++) {
vg_cgb_search++;
if (vg_cgbs[i].kind == CG_NotInUse)
return i;
}
/* Not found. Try to allocate one at the end. */
if (vg_cgb_used < vg_cgb_size) {
vg_cgb_used++;
return vg_cgb_used-1;
}
/* Ok, we have to allocate a new one. */
vg_assert(vg_cgb_used == vg_cgb_size);
sz_new = (vg_cgbs == NULL) ? 10 : (2 * vg_cgb_size);
cgbs_new = VG_(malloc)( VG_AR_PRIVATE, sz_new * sizeof(CGenBlock) );
for (i = 0; i < vg_cgb_used; i++)
cgbs_new[i] = vg_cgbs[i];
if (vg_cgbs != NULL)
VG_(free)( VG_AR_PRIVATE, vg_cgbs );
vg_cgbs = cgbs_new;
vg_cgb_size = sz_new;
vg_cgb_used++;
if (vg_cgb_used > vg_cgb_used_MAX)
vg_cgb_used_MAX = vg_cgb_used;
return vg_cgb_used-1;
}
/*------------------------------------------------------------*/
/*--- Stack block management. ---*/
/*------------------------------------------------------------*/
/* This is managed as an expanding array of CStackBlocks. They are
packed up against the left-hand end of the array, with no holes.
They are kept sorted by the start field, with the [0] having the
highest value. This means it's pretty cheap to put new blocks at
the end, corresponding to stack pushes, since the additions put
blocks on in what is presumably fairly close to strictly descending
order. If this assumption doesn't hold the performance
consequences will be horrible.
When the client's %ESP jumps back upwards as the result of a RET
insn, we shrink the array backwards from the end, in a
guaranteed-cheap linear scan.
*/
typedef
struct {
Addr start;
UInt size;
ExeContext* where;
}
CStackBlock;
/* This subsystem is self-initialising. */
static UInt vg_csb_size = 0;
static UInt vg_csb_used = 0;
static CStackBlock* vg_csbs = NULL;
/* Stats for this subsystem. */
static UInt vg_csb_used_MAX = 0; /* Max in use. */
static UInt vg_csb_allocs = 0; /* Number of allocs. */
static UInt vg_csb_discards = 0; /* Number of discards. */
static UInt vg_csb_swaps = 0; /* Number of searches. */
static
void vg_add_client_stack_block ( Addr aa, UInt sz )
{
UInt i, sz_new;
CStackBlock* csbs_new;
vg_csb_allocs++;
/* Ensure there is space for a new block. */
if (vg_csb_used >= vg_csb_size) {
/* No; we have to expand the array. */
vg_assert(vg_csb_used == vg_csb_size);
sz_new = (vg_csbs == NULL) ? 10 : (2 * vg_csb_size);
csbs_new = VG_(malloc)( VG_AR_PRIVATE, sz_new * sizeof(CStackBlock) );
for (i = 0; i < vg_csb_used; i++)
csbs_new[i] = vg_csbs[i];
if (vg_csbs != NULL)
VG_(free)( VG_AR_PRIVATE, vg_csbs );
vg_csbs = csbs_new;
vg_csb_size = sz_new;
}
/* Ok, we can use [vg_csb_used]. */
vg_csbs[vg_csb_used].start = aa;
vg_csbs[vg_csb_used].size = sz;
vg_csbs[vg_csb_used].where = VG_(get_ExeContext) ( False );
vg_csb_used++;
if (vg_csb_used > vg_csb_used_MAX)
vg_csb_used_MAX = vg_csb_used;
vg_assert(vg_csb_used <= vg_csb_size);
/* VG_(printf)("acsb %p %d\n", aa, sz); */
VGM_(make_noaccess) ( aa, sz );
/* And make sure that they are in descending order of address. */
i = vg_csb_used;
while (i > 0 && vg_csbs[i-1].start < vg_csbs[i].start) {
CStackBlock tmp = vg_csbs[i-1];
vg_csbs[i-1] = vg_csbs[i];
vg_csbs[i] = tmp;
vg_csb_swaps++;
}
# if 1
for (i = 1; i < vg_csb_used; i++)
vg_assert(vg_csbs[i-1].start >= vg_csbs[i].start);
# endif
}
/*------------------------------------------------------------*/
/*--- Externally visible functions. ---*/
/*------------------------------------------------------------*/
void VG_(show_client_block_stats) ( void )
{
VG_(message)(Vg_DebugMsg,
"general CBs: %d allocs, %d discards, %d maxinuse, %d search",
vg_cgb_allocs, vg_cgb_discards, vg_cgb_used_MAX, vg_cgb_search
);
VG_(message)(Vg_DebugMsg,
" stack CBs: %d allocs, %d discards, %d maxinuse, %d swap",
vg_csb_allocs, vg_csb_discards, vg_csb_used_MAX, vg_csb_swaps
);
}
Bool VG_(client_perm_maybe_describe)( Addr a, AddrInfo* ai )
{
Int i;
/* VG_(printf)("try to identify %d\n", a); */
/* First see if it's a stack block. We do two passes, one exact
and one with a bit of slop, so as to try and get the most
accurate fix. */
for (i = 0; i < vg_csb_used; i++) {
if (vg_csbs[i].start <= a
&& a < vg_csbs[i].start + vg_csbs[i].size) {
ai->akind = UserS;
ai->blksize = vg_csbs[i].size;
ai->rwoffset = (Int)(a) - (Int)(vg_csbs[i].start);
ai->lastchange = vg_csbs[i].where;
return True;
}
}
/* No exact match on the stack. Re-do the stack scan with a bit of
slop. */
for (i = 0; i < vg_csb_used; i++) {
if (vg_csbs[i].start - 8 <= a
&& a < vg_csbs[i].start + vg_csbs[i].size + 8) {
ai->akind = UserS;
ai->blksize = vg_csbs[i].size;
ai->rwoffset = (Int)(a) - (Int)(vg_csbs[i].start);
ai->lastchange = vg_csbs[i].where;
return True;
}
}
/* No match on the stack. Perhaps it's a general block ? */
for (i = 0; i < vg_cgb_used; i++) {
if (vg_cgbs[i].kind == CG_NotInUse)
continue;
if (vg_cgbs[i].start - VG_AR_CLIENT_REDZONE_SZB <= a
&& a < vg_cgbs[i].start + vg_cgbs[i].size + VG_AR_CLIENT_REDZONE_SZB) {
ai->akind = UserG;
ai->blksize = vg_cgbs[i].size;
ai->rwoffset = (Int)(a) - (Int)(vg_cgbs[i].start);
ai->lastchange = vg_cgbs[i].where;
return True;
}
}
return False;
}
void VG_(delete_client_stack_blocks_following_ESP_change) ( void )
{
Addr newESP;
if (!VG_(clo_client_perms)) return;
newESP = VG_(baseBlock)[VGOFF_(m_esp)];
while (vg_csb_used > 0 &&
vg_csbs[vg_csb_used-1].start + vg_csbs[vg_csb_used-1].size <= newESP) {
vg_csb_used--;
vg_csb_discards++;
if (VG_(clo_verbosity) > 2)
VG_(printf)("discarding stack block %p for %d\n",
vg_csbs[vg_csb_used].start, vg_csbs[vg_csb_used].size);
}
}
UInt VG_(handle_client_request) ( UInt code, Addr aa, UInt nn )
{
Int i;
Bool ok;
Addr bad_addr;
if (VG_(clo_verbosity) > 2)
VG_(printf)("client request: code %d, addr %p, len %d\n",
code, aa, nn );
vg_assert(VG_(clo_client_perms));
vg_assert(VG_(clo_instrument));
switch (code) {
case 1001: /* make no access */
i = vg_alloc_client_block();
/* VG_(printf)("allocated %d %p\n", i, vg_cgbs); */
vg_cgbs[i].kind = CG_NoAccess;
vg_cgbs[i].start = aa;
vg_cgbs[i].size = nn;
vg_cgbs[i].where = VG_(get_ExeContext) ( False );
VGM_(make_noaccess) ( aa, nn );
return i;
case 1002: /* make writable */
i = vg_alloc_client_block();
vg_cgbs[i].kind = CG_Writable;
vg_cgbs[i].start = aa;
vg_cgbs[i].size = nn;
vg_cgbs[i].where = VG_(get_ExeContext) ( False );
VGM_(make_writable) ( aa, nn );
return i;
case 1003: /* make readable */
i = vg_alloc_client_block();
vg_cgbs[i].kind = CG_Readable;
vg_cgbs[i].start = aa;
vg_cgbs[i].size = nn;
vg_cgbs[i].where = VG_(get_ExeContext) ( False );
VGM_(make_readable) ( aa, nn );
return i;
case 2002: /* check writable */
ok = VGM_(check_writable) ( aa, nn, &bad_addr );
if (!ok)
VG_(record_user_err) ( bad_addr, True );
return ok ? (UInt)NULL : bad_addr;
case 2003: /* check readable */
ok = VGM_(check_readable) ( aa, nn, &bad_addr );
if (!ok)
VG_(record_user_err) ( bad_addr, False );
return ok ? (UInt)NULL : bad_addr;
case 2004: /* discard */
if (vg_cgbs == NULL
|| nn >= vg_cgb_used || vg_cgbs[nn].kind == CG_NotInUse)
return 1;
vg_assert(nn >= 0 && nn < vg_cgb_used);
vg_cgbs[nn].kind = CG_NotInUse;
vg_cgb_discards++;
return 0;
case 3001: /* make noaccess stack block */
vg_add_client_stack_block ( aa, nn );
return 0;
default:
VG_(message)(Vg_UserMsg,
"Warning: unknown client request code %d", code);
return 1;
}
}
/*--------------------------------------------------------------------*/
/*--- end vg_clientperms.c ---*/
/*--------------------------------------------------------------------*/