blob: 8594a3eee58f374a15961cd47657fcae95621082 [file] [log] [blame]
sewardjde4a1d02002-03-22 01:27:54 +00001
2/*--------------------------------------------------------------------*/
3/*--- For when the client advises Valgrind about permissions. ---*/
4/*--- vg_clientperms.c ---*/
5/*--------------------------------------------------------------------*/
6
7/*
8 This file is part of Valgrind, an x86 protected-mode emulator
9 designed for debugging and profiling binaries on x86-Unixes.
10
11 Copyright (C) 2000-2002 Julian Seward
12 jseward@acm.org
13 Julian_Seward@muraroa.demon.co.uk
14
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation; either version 2 of the
18 License, or (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
28 02111-1307, USA.
29
30 The GNU General Public License is contained in the file LICENSE.
31*/
32
33#include "vg_include.h"
34#include "vg_constants.h"
35
sewardj2e93c502002-04-12 11:12:52 +000036#include "valgrind.h" /* for VG_USERREQ__* */
37
sewardjde4a1d02002-03-22 01:27:54 +000038
39/*------------------------------------------------------------*/
40/*--- General client block management. ---*/
41/*------------------------------------------------------------*/
42
43/* This is managed as an expanding array of client block descriptors.
44 Indices of live descriptors are issued to the client, so it can ask
45 to free them later. Therefore we cannot slide live entries down
46 over dead ones. Instead we must use free/inuse flags and scan for
47 an empty slot at allocation time. This in turn means allocation is
48 relatively expensive, so we hope this does not happen too often.
49*/
50
51typedef
52 enum { CG_NotInUse, CG_NoAccess, CG_Writable, CG_Readable }
53 CGenBlockKind;
54
55typedef
56 struct {
57 Addr start;
58 UInt size;
59 ExeContext* where;
60 CGenBlockKind kind;
61 }
62 CGenBlock;
63
64/* This subsystem is self-initialising. */
65static UInt vg_cgb_size = 0;
66static UInt vg_cgb_used = 0;
67static CGenBlock* vg_cgbs = NULL;
68
69/* Stats for this subsystem. */
70static UInt vg_cgb_used_MAX = 0; /* Max in use. */
71static UInt vg_cgb_allocs = 0; /* Number of allocs. */
72static UInt vg_cgb_discards = 0; /* Number of discards. */
73static UInt vg_cgb_search = 0; /* Number of searches. */
74
75
76static
77Int vg_alloc_client_block ( void )
78{
79 Int i, sz_new;
80 CGenBlock* cgbs_new;
81
82 vg_cgb_allocs++;
83
84 for (i = 0; i < vg_cgb_used; i++) {
85 vg_cgb_search++;
86 if (vg_cgbs[i].kind == CG_NotInUse)
87 return i;
88 }
89
90 /* Not found. Try to allocate one at the end. */
91 if (vg_cgb_used < vg_cgb_size) {
92 vg_cgb_used++;
93 return vg_cgb_used-1;
94 }
95
96 /* Ok, we have to allocate a new one. */
97 vg_assert(vg_cgb_used == vg_cgb_size);
98 sz_new = (vg_cgbs == NULL) ? 10 : (2 * vg_cgb_size);
99
100 cgbs_new = VG_(malloc)( VG_AR_PRIVATE, sz_new * sizeof(CGenBlock) );
101 for (i = 0; i < vg_cgb_used; i++)
102 cgbs_new[i] = vg_cgbs[i];
103
104 if (vg_cgbs != NULL)
105 VG_(free)( VG_AR_PRIVATE, vg_cgbs );
106 vg_cgbs = cgbs_new;
107
108 vg_cgb_size = sz_new;
109 vg_cgb_used++;
110 if (vg_cgb_used > vg_cgb_used_MAX)
111 vg_cgb_used_MAX = vg_cgb_used;
112 return vg_cgb_used-1;
113}
114
115
116/*------------------------------------------------------------*/
117/*--- Stack block management. ---*/
118/*------------------------------------------------------------*/
119
120/* This is managed as an expanding array of CStackBlocks. They are
121 packed up against the left-hand end of the array, with no holes.
122 They are kept sorted by the start field, with the [0] having the
123 highest value. This means it's pretty cheap to put new blocks at
124 the end, corresponding to stack pushes, since the additions put
125 blocks on in what is presumably fairly close to strictly descending
126 order. If this assumption doesn't hold the performance
127 consequences will be horrible.
128
129 When the client's %ESP jumps back upwards as the result of a RET
130 insn, we shrink the array backwards from the end, in a
131 guaranteed-cheap linear scan.
132*/
133
134typedef
135 struct {
136 Addr start;
137 UInt size;
138 ExeContext* where;
139 }
140 CStackBlock;
141
142/* This subsystem is self-initialising. */
143static UInt vg_csb_size = 0;
144static UInt vg_csb_used = 0;
145static CStackBlock* vg_csbs = NULL;
146
147/* Stats for this subsystem. */
148static UInt vg_csb_used_MAX = 0; /* Max in use. */
149static UInt vg_csb_allocs = 0; /* Number of allocs. */
150static UInt vg_csb_discards = 0; /* Number of discards. */
151static UInt vg_csb_swaps = 0; /* Number of searches. */
152
153static
sewardj8c824512002-04-14 04:16:48 +0000154void vg_add_client_stack_block ( ThreadState* tst, Addr aa, UInt sz )
sewardjde4a1d02002-03-22 01:27:54 +0000155{
156 UInt i, sz_new;
157 CStackBlock* csbs_new;
158 vg_csb_allocs++;
159
160 /* Ensure there is space for a new block. */
161
162 if (vg_csb_used >= vg_csb_size) {
163
164 /* No; we have to expand the array. */
165 vg_assert(vg_csb_used == vg_csb_size);
166
167 sz_new = (vg_csbs == NULL) ? 10 : (2 * vg_csb_size);
168
169 csbs_new = VG_(malloc)( VG_AR_PRIVATE, sz_new * sizeof(CStackBlock) );
170 for (i = 0; i < vg_csb_used; i++)
171 csbs_new[i] = vg_csbs[i];
172
173 if (vg_csbs != NULL)
174 VG_(free)( VG_AR_PRIVATE, vg_csbs );
175 vg_csbs = csbs_new;
176
177 vg_csb_size = sz_new;
178 }
179
180 /* Ok, we can use [vg_csb_used]. */
181 vg_csbs[vg_csb_used].start = aa;
182 vg_csbs[vg_csb_used].size = sz;
sewardj8c824512002-04-14 04:16:48 +0000183 /* Actually running a thread at this point. */
184 vg_csbs[vg_csb_used].where
185 = VG_(get_ExeContext) ( False, tst->m_eip, tst->m_ebp );
sewardjde4a1d02002-03-22 01:27:54 +0000186 vg_csb_used++;
187
188 if (vg_csb_used > vg_csb_used_MAX)
189 vg_csb_used_MAX = vg_csb_used;
190
191 vg_assert(vg_csb_used <= vg_csb_size);
192
193 /* VG_(printf)("acsb %p %d\n", aa, sz); */
194 VGM_(make_noaccess) ( aa, sz );
195
196 /* And make sure that they are in descending order of address. */
197 i = vg_csb_used;
198 while (i > 0 && vg_csbs[i-1].start < vg_csbs[i].start) {
199 CStackBlock tmp = vg_csbs[i-1];
200 vg_csbs[i-1] = vg_csbs[i];
201 vg_csbs[i] = tmp;
202 vg_csb_swaps++;
203 }
204
205# if 1
206 for (i = 1; i < vg_csb_used; i++)
207 vg_assert(vg_csbs[i-1].start >= vg_csbs[i].start);
208# endif
209}
210
211
212/*------------------------------------------------------------*/
213/*--- Externally visible functions. ---*/
214/*------------------------------------------------------------*/
215
216void VG_(show_client_block_stats) ( void )
217{
218 VG_(message)(Vg_DebugMsg,
219 "general CBs: %d allocs, %d discards, %d maxinuse, %d search",
220 vg_cgb_allocs, vg_cgb_discards, vg_cgb_used_MAX, vg_cgb_search
221 );
222 VG_(message)(Vg_DebugMsg,
223 " stack CBs: %d allocs, %d discards, %d maxinuse, %d swap",
224 vg_csb_allocs, vg_csb_discards, vg_csb_used_MAX, vg_csb_swaps
225 );
226}
227
228
229Bool VG_(client_perm_maybe_describe)( Addr a, AddrInfo* ai )
230{
231 Int i;
232 /* VG_(printf)("try to identify %d\n", a); */
233
234 /* First see if it's a stack block. We do two passes, one exact
235 and one with a bit of slop, so as to try and get the most
236 accurate fix. */
237 for (i = 0; i < vg_csb_used; i++) {
238 if (vg_csbs[i].start <= a
239 && a < vg_csbs[i].start + vg_csbs[i].size) {
240 ai->akind = UserS;
241 ai->blksize = vg_csbs[i].size;
242 ai->rwoffset = (Int)(a) - (Int)(vg_csbs[i].start);
243 ai->lastchange = vg_csbs[i].where;
244 return True;
245 }
246 }
247
248 /* No exact match on the stack. Re-do the stack scan with a bit of
249 slop. */
250 for (i = 0; i < vg_csb_used; i++) {
251 if (vg_csbs[i].start - 8 <= a
252 && a < vg_csbs[i].start + vg_csbs[i].size + 8) {
253 ai->akind = UserS;
254 ai->blksize = vg_csbs[i].size;
255 ai->rwoffset = (Int)(a) - (Int)(vg_csbs[i].start);
256 ai->lastchange = vg_csbs[i].where;
257 return True;
258 }
259 }
260
261 /* No match on the stack. Perhaps it's a general block ? */
262 for (i = 0; i < vg_cgb_used; i++) {
263 if (vg_cgbs[i].kind == CG_NotInUse)
264 continue;
265 if (vg_cgbs[i].start - VG_AR_CLIENT_REDZONE_SZB <= a
266 && a < vg_cgbs[i].start + vg_cgbs[i].size + VG_AR_CLIENT_REDZONE_SZB) {
267 ai->akind = UserG;
268 ai->blksize = vg_cgbs[i].size;
269 ai->rwoffset = (Int)(a) - (Int)(vg_cgbs[i].start);
270 ai->lastchange = vg_cgbs[i].where;
271 return True;
272 }
273 }
274 return False;
275}
276
277
278void VG_(delete_client_stack_blocks_following_ESP_change) ( void )
279{
280 Addr newESP;
281 if (!VG_(clo_client_perms)) return;
282 newESP = VG_(baseBlock)[VGOFF_(m_esp)];
283 while (vg_csb_used > 0 &&
284 vg_csbs[vg_csb_used-1].start + vg_csbs[vg_csb_used-1].size <= newESP) {
285 vg_csb_used--;
286 vg_csb_discards++;
287 if (VG_(clo_verbosity) > 2)
288 VG_(printf)("discarding stack block %p for %d\n",
289 vg_csbs[vg_csb_used].start, vg_csbs[vg_csb_used].size);
290 }
291}
292
293
sewardj8c824512002-04-14 04:16:48 +0000294UInt VG_(handle_client_request) ( ThreadState* tst, UInt* arg_block )
sewardjde4a1d02002-03-22 01:27:54 +0000295{
sewardj2e93c502002-04-12 11:12:52 +0000296 Int i;
297 Bool ok;
298 Addr bad_addr;
299 UInt* arg = arg_block;
sewardjde4a1d02002-03-22 01:27:54 +0000300
301 if (VG_(clo_verbosity) > 2)
302 VG_(printf)("client request: code %d, addr %p, len %d\n",
sewardj2e93c502002-04-12 11:12:52 +0000303 arg[0], arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000304
305 vg_assert(VG_(clo_client_perms));
306 vg_assert(VG_(clo_instrument));
307
sewardj2e93c502002-04-12 11:12:52 +0000308 switch (arg[0]) {
309 case VG_USERREQ__MAKE_NOACCESS: /* make no access */
sewardjde4a1d02002-03-22 01:27:54 +0000310 i = vg_alloc_client_block();
311 /* VG_(printf)("allocated %d %p\n", i, vg_cgbs); */
312 vg_cgbs[i].kind = CG_NoAccess;
sewardj2e93c502002-04-12 11:12:52 +0000313 vg_cgbs[i].start = arg[1];
314 vg_cgbs[i].size = arg[2];
sewardj8c824512002-04-14 04:16:48 +0000315 vg_cgbs[i].where
316 = VG_(get_ExeContext) ( False, tst->m_eip, tst->m_ebp );
sewardj2e93c502002-04-12 11:12:52 +0000317 VGM_(make_noaccess) ( arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000318 return i;
sewardj2e93c502002-04-12 11:12:52 +0000319 case VG_USERREQ__MAKE_WRITABLE: /* make writable */
sewardjde4a1d02002-03-22 01:27:54 +0000320 i = vg_alloc_client_block();
321 vg_cgbs[i].kind = CG_Writable;
sewardj2e93c502002-04-12 11:12:52 +0000322 vg_cgbs[i].start = arg[1];
323 vg_cgbs[i].size = arg[2];
sewardj8c824512002-04-14 04:16:48 +0000324 vg_cgbs[i].where
325 = VG_(get_ExeContext) ( False, tst->m_eip, tst->m_ebp );
sewardj2e93c502002-04-12 11:12:52 +0000326 VGM_(make_writable) ( arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000327 return i;
sewardj2e93c502002-04-12 11:12:52 +0000328 case VG_USERREQ__MAKE_READABLE: /* make readable */
sewardjde4a1d02002-03-22 01:27:54 +0000329 i = vg_alloc_client_block();
330 vg_cgbs[i].kind = CG_Readable;
sewardj2e93c502002-04-12 11:12:52 +0000331 vg_cgbs[i].start = arg[1];
332 vg_cgbs[i].size = arg[2];
sewardj8c824512002-04-14 04:16:48 +0000333 vg_cgbs[i].where
334 = VG_(get_ExeContext) ( False, tst->m_eip, tst->m_ebp );
sewardj2e93c502002-04-12 11:12:52 +0000335 VGM_(make_readable) ( arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000336 return i;
337
sewardj2e93c502002-04-12 11:12:52 +0000338 case VG_USERREQ__CHECK_WRITABLE: /* check writable */
339 ok = VGM_(check_writable) ( arg[1], arg[2], &bad_addr );
sewardjde4a1d02002-03-22 01:27:54 +0000340 if (!ok)
sewardj8c824512002-04-14 04:16:48 +0000341 VG_(record_user_err) ( tst, bad_addr, True );
sewardjde4a1d02002-03-22 01:27:54 +0000342 return ok ? (UInt)NULL : bad_addr;
sewardj2e93c502002-04-12 11:12:52 +0000343 case VG_USERREQ__CHECK_READABLE: /* check readable */
344 ok = VGM_(check_readable) ( arg[1], arg[2], &bad_addr );
sewardjde4a1d02002-03-22 01:27:54 +0000345 if (!ok)
sewardj8c824512002-04-14 04:16:48 +0000346 VG_(record_user_err) ( tst, bad_addr, False );
sewardjde4a1d02002-03-22 01:27:54 +0000347 return ok ? (UInt)NULL : bad_addr;
348
sewardj2e93c502002-04-12 11:12:52 +0000349 case VG_USERREQ__DISCARD: /* discard */
sewardjde4a1d02002-03-22 01:27:54 +0000350 if (vg_cgbs == NULL
sewardj2e93c502002-04-12 11:12:52 +0000351 || arg[2] >= vg_cgb_used || vg_cgbs[arg[2]].kind == CG_NotInUse)
sewardjde4a1d02002-03-22 01:27:54 +0000352 return 1;
sewardj2e93c502002-04-12 11:12:52 +0000353 vg_assert(arg[2] >= 0 && arg[2] < vg_cgb_used);
354 vg_cgbs[arg[2]].kind = CG_NotInUse;
sewardjde4a1d02002-03-22 01:27:54 +0000355 vg_cgb_discards++;
356 return 0;
357
sewardj2e93c502002-04-12 11:12:52 +0000358 case VG_USERREQ__MAKE_NOACCESS_STACK: /* make noaccess stack block */
sewardj8c824512002-04-14 04:16:48 +0000359 vg_add_client_stack_block ( tst, arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000360 return 0;
361
sewardj2e93c502002-04-12 11:12:52 +0000362 case VG_USERREQ__RUNNING_ON_VALGRIND:
363 return 1;
364
365 case VG_USERREQ__DO_LEAK_CHECK:
366 VG_(detect_memory_leaks)();
367 return 0; /* return value is meaningless */
368
sewardjde4a1d02002-03-22 01:27:54 +0000369 default:
370 VG_(message)(Vg_UserMsg,
sewardj2e93c502002-04-12 11:12:52 +0000371 "Warning: unknown client request code %d", arg[0]);
sewardjde4a1d02002-03-22 01:27:54 +0000372 return 1;
373 }
374}
375
376
377/*--------------------------------------------------------------------*/
378/*--- end vg_clientperms.c ---*/
379/*--------------------------------------------------------------------*/