blob: 15f3a3d2ad88875c9ec10a2f5ef3c317b5b064c5 [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",
sewardj9a199dc2002-04-14 13:01:38 +0000289 (void*)vg_csbs[vg_csb_used].start,
290 vg_csbs[vg_csb_used].size);
sewardjde4a1d02002-03-22 01:27:54 +0000291 }
292}
293
294
sewardj8c824512002-04-14 04:16:48 +0000295UInt VG_(handle_client_request) ( ThreadState* tst, UInt* arg_block )
sewardjde4a1d02002-03-22 01:27:54 +0000296{
sewardj2e93c502002-04-12 11:12:52 +0000297 Int i;
298 Bool ok;
299 Addr bad_addr;
300 UInt* arg = arg_block;
sewardjde4a1d02002-03-22 01:27:54 +0000301
302 if (VG_(clo_verbosity) > 2)
303 VG_(printf)("client request: code %d, addr %p, len %d\n",
sewardj9a199dc2002-04-14 13:01:38 +0000304 arg[0], (void*)arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000305
306 vg_assert(VG_(clo_client_perms));
307 vg_assert(VG_(clo_instrument));
308
sewardj2e93c502002-04-12 11:12:52 +0000309 switch (arg[0]) {
310 case VG_USERREQ__MAKE_NOACCESS: /* make no access */
sewardjde4a1d02002-03-22 01:27:54 +0000311 i = vg_alloc_client_block();
312 /* VG_(printf)("allocated %d %p\n", i, vg_cgbs); */
313 vg_cgbs[i].kind = CG_NoAccess;
sewardj2e93c502002-04-12 11:12:52 +0000314 vg_cgbs[i].start = arg[1];
315 vg_cgbs[i].size = arg[2];
sewardj8c824512002-04-14 04:16:48 +0000316 vg_cgbs[i].where
317 = VG_(get_ExeContext) ( False, tst->m_eip, tst->m_ebp );
sewardj2e93c502002-04-12 11:12:52 +0000318 VGM_(make_noaccess) ( arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000319 return i;
sewardj2e93c502002-04-12 11:12:52 +0000320 case VG_USERREQ__MAKE_WRITABLE: /* make writable */
sewardjde4a1d02002-03-22 01:27:54 +0000321 i = vg_alloc_client_block();
322 vg_cgbs[i].kind = CG_Writable;
sewardj2e93c502002-04-12 11:12:52 +0000323 vg_cgbs[i].start = arg[1];
324 vg_cgbs[i].size = arg[2];
sewardj8c824512002-04-14 04:16:48 +0000325 vg_cgbs[i].where
326 = VG_(get_ExeContext) ( False, tst->m_eip, tst->m_ebp );
sewardj2e93c502002-04-12 11:12:52 +0000327 VGM_(make_writable) ( arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000328 return i;
sewardj2e93c502002-04-12 11:12:52 +0000329 case VG_USERREQ__MAKE_READABLE: /* make readable */
sewardjde4a1d02002-03-22 01:27:54 +0000330 i = vg_alloc_client_block();
331 vg_cgbs[i].kind = CG_Readable;
sewardj2e93c502002-04-12 11:12:52 +0000332 vg_cgbs[i].start = arg[1];
333 vg_cgbs[i].size = arg[2];
sewardj8c824512002-04-14 04:16:48 +0000334 vg_cgbs[i].where
335 = VG_(get_ExeContext) ( False, tst->m_eip, tst->m_ebp );
sewardj2e93c502002-04-12 11:12:52 +0000336 VGM_(make_readable) ( arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000337 return i;
338
sewardj2e93c502002-04-12 11:12:52 +0000339 case VG_USERREQ__CHECK_WRITABLE: /* check writable */
340 ok = VGM_(check_writable) ( arg[1], arg[2], &bad_addr );
sewardjde4a1d02002-03-22 01:27:54 +0000341 if (!ok)
sewardj8c824512002-04-14 04:16:48 +0000342 VG_(record_user_err) ( tst, bad_addr, True );
sewardjde4a1d02002-03-22 01:27:54 +0000343 return ok ? (UInt)NULL : bad_addr;
sewardj2e93c502002-04-12 11:12:52 +0000344 case VG_USERREQ__CHECK_READABLE: /* check readable */
345 ok = VGM_(check_readable) ( arg[1], arg[2], &bad_addr );
sewardjde4a1d02002-03-22 01:27:54 +0000346 if (!ok)
sewardj8c824512002-04-14 04:16:48 +0000347 VG_(record_user_err) ( tst, bad_addr, False );
sewardjde4a1d02002-03-22 01:27:54 +0000348 return ok ? (UInt)NULL : bad_addr;
349
sewardj2e93c502002-04-12 11:12:52 +0000350 case VG_USERREQ__DISCARD: /* discard */
sewardjde4a1d02002-03-22 01:27:54 +0000351 if (vg_cgbs == NULL
sewardj2e93c502002-04-12 11:12:52 +0000352 || arg[2] >= vg_cgb_used || vg_cgbs[arg[2]].kind == CG_NotInUse)
sewardjde4a1d02002-03-22 01:27:54 +0000353 return 1;
sewardj2e93c502002-04-12 11:12:52 +0000354 vg_assert(arg[2] >= 0 && arg[2] < vg_cgb_used);
355 vg_cgbs[arg[2]].kind = CG_NotInUse;
sewardjde4a1d02002-03-22 01:27:54 +0000356 vg_cgb_discards++;
357 return 0;
358
sewardj2e93c502002-04-12 11:12:52 +0000359 case VG_USERREQ__MAKE_NOACCESS_STACK: /* make noaccess stack block */
sewardj8c824512002-04-14 04:16:48 +0000360 vg_add_client_stack_block ( tst, arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000361 return 0;
362
sewardj2e93c502002-04-12 11:12:52 +0000363 case VG_USERREQ__RUNNING_ON_VALGRIND:
364 return 1;
365
366 case VG_USERREQ__DO_LEAK_CHECK:
367 VG_(detect_memory_leaks)();
368 return 0; /* return value is meaningless */
369
sewardjde4a1d02002-03-22 01:27:54 +0000370 default:
371 VG_(message)(Vg_UserMsg,
sewardj2e93c502002-04-12 11:12:52 +0000372 "Warning: unknown client request code %d", arg[0]);
sewardjde4a1d02002-03-22 01:27:54 +0000373 return 1;
374 }
375}
376
377
378/*--------------------------------------------------------------------*/
379/*--- end vg_clientperms.c ---*/
380/*--------------------------------------------------------------------*/