blob: c8d5f73190545551360a49bf1cfffa25e0b0c8b4 [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
sewardjc7529c32002-04-16 01:55:18 +0000266 && a < vg_cgbs[i].start
267 + vg_cgbs[i].size
268 + VG_AR_CLIENT_REDZONE_SZB) {
sewardjde4a1d02002-03-22 01:27:54 +0000269 ai->akind = UserG;
270 ai->blksize = vg_cgbs[i].size;
271 ai->rwoffset = (Int)(a) - (Int)(vg_cgbs[i].start);
272 ai->lastchange = vg_cgbs[i].where;
273 return True;
274 }
275 }
276 return False;
277}
278
279
280void VG_(delete_client_stack_blocks_following_ESP_change) ( void )
281{
282 Addr newESP;
sewardjde4a1d02002-03-22 01:27:54 +0000283 newESP = VG_(baseBlock)[VGOFF_(m_esp)];
sewardjc7529c32002-04-16 01:55:18 +0000284 while (vg_csb_used > 0
285 && vg_csbs[vg_csb_used-1].start + vg_csbs[vg_csb_used-1].size
286 <= newESP) {
sewardjde4a1d02002-03-22 01:27:54 +0000287 vg_csb_used--;
288 vg_csb_discards++;
289 if (VG_(clo_verbosity) > 2)
290 VG_(printf)("discarding stack block %p for %d\n",
sewardj9a199dc2002-04-14 13:01:38 +0000291 (void*)vg_csbs[vg_csb_used].start,
292 vg_csbs[vg_csb_used].size);
sewardjde4a1d02002-03-22 01:27:54 +0000293 }
294}
295
296
sewardj8c824512002-04-14 04:16:48 +0000297UInt VG_(handle_client_request) ( ThreadState* tst, UInt* arg_block )
sewardjde4a1d02002-03-22 01:27:54 +0000298{
sewardj2e93c502002-04-12 11:12:52 +0000299 Int i;
300 Bool ok;
301 Addr bad_addr;
302 UInt* arg = arg_block;
sewardjde4a1d02002-03-22 01:27:54 +0000303
304 if (VG_(clo_verbosity) > 2)
305 VG_(printf)("client request: code %d, addr %p, len %d\n",
sewardj9a199dc2002-04-14 13:01:38 +0000306 arg[0], (void*)arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000307
sewardj2e93c502002-04-12 11:12:52 +0000308 switch (arg[0]) {
309 case VG_USERREQ__MAKE_NOACCESS: /* make no access */
sewardjc7529c32002-04-16 01:55:18 +0000310 if (!VG_(clo_instrument))
311 return 0;
sewardjde4a1d02002-03-22 01:27:54 +0000312 i = vg_alloc_client_block();
313 /* VG_(printf)("allocated %d %p\n", i, vg_cgbs); */
314 vg_cgbs[i].kind = CG_NoAccess;
sewardj2e93c502002-04-12 11:12:52 +0000315 vg_cgbs[i].start = arg[1];
316 vg_cgbs[i].size = arg[2];
sewardj8c824512002-04-14 04:16:48 +0000317 vg_cgbs[i].where
318 = VG_(get_ExeContext) ( False, tst->m_eip, tst->m_ebp );
sewardj2e93c502002-04-12 11:12:52 +0000319 VGM_(make_noaccess) ( arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000320 return i;
sewardj2e93c502002-04-12 11:12:52 +0000321 case VG_USERREQ__MAKE_WRITABLE: /* make writable */
sewardjc7529c32002-04-16 01:55:18 +0000322 if (!VG_(clo_instrument))
323 return 0;
sewardjde4a1d02002-03-22 01:27:54 +0000324 i = vg_alloc_client_block();
325 vg_cgbs[i].kind = CG_Writable;
sewardj2e93c502002-04-12 11:12:52 +0000326 vg_cgbs[i].start = arg[1];
327 vg_cgbs[i].size = arg[2];
sewardj8c824512002-04-14 04:16:48 +0000328 vg_cgbs[i].where
329 = VG_(get_ExeContext) ( False, tst->m_eip, tst->m_ebp );
sewardj2e93c502002-04-12 11:12:52 +0000330 VGM_(make_writable) ( arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000331 return i;
sewardj2e93c502002-04-12 11:12:52 +0000332 case VG_USERREQ__MAKE_READABLE: /* make readable */
sewardjc7529c32002-04-16 01:55:18 +0000333 if (!VG_(clo_instrument))
334 return 0;
sewardjde4a1d02002-03-22 01:27:54 +0000335 i = vg_alloc_client_block();
336 vg_cgbs[i].kind = CG_Readable;
sewardj2e93c502002-04-12 11:12:52 +0000337 vg_cgbs[i].start = arg[1];
338 vg_cgbs[i].size = arg[2];
sewardj8c824512002-04-14 04:16:48 +0000339 vg_cgbs[i].where
340 = VG_(get_ExeContext) ( False, tst->m_eip, tst->m_ebp );
sewardj2e93c502002-04-12 11:12:52 +0000341 VGM_(make_readable) ( arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000342 return i;
343
sewardj2e93c502002-04-12 11:12:52 +0000344 case VG_USERREQ__CHECK_WRITABLE: /* check writable */
sewardjc7529c32002-04-16 01:55:18 +0000345 if (!VG_(clo_instrument))
346 return 0;
sewardj2e93c502002-04-12 11:12:52 +0000347 ok = VGM_(check_writable) ( arg[1], arg[2], &bad_addr );
sewardjde4a1d02002-03-22 01:27:54 +0000348 if (!ok)
sewardj8c824512002-04-14 04:16:48 +0000349 VG_(record_user_err) ( tst, bad_addr, True );
sewardjde4a1d02002-03-22 01:27:54 +0000350 return ok ? (UInt)NULL : bad_addr;
sewardj2e93c502002-04-12 11:12:52 +0000351 case VG_USERREQ__CHECK_READABLE: /* check readable */
sewardjc7529c32002-04-16 01:55:18 +0000352 if (!VG_(clo_instrument))
353 return 0;
sewardj2e93c502002-04-12 11:12:52 +0000354 ok = VGM_(check_readable) ( arg[1], arg[2], &bad_addr );
sewardjde4a1d02002-03-22 01:27:54 +0000355 if (!ok)
sewardj8c824512002-04-14 04:16:48 +0000356 VG_(record_user_err) ( tst, bad_addr, False );
sewardjde4a1d02002-03-22 01:27:54 +0000357 return ok ? (UInt)NULL : bad_addr;
358
sewardj2e93c502002-04-12 11:12:52 +0000359 case VG_USERREQ__DISCARD: /* discard */
sewardjc7529c32002-04-16 01:55:18 +0000360 if (!VG_(clo_instrument))
361 return 0;
sewardjde4a1d02002-03-22 01:27:54 +0000362 if (vg_cgbs == NULL
sewardj2e93c502002-04-12 11:12:52 +0000363 || arg[2] >= vg_cgb_used || vg_cgbs[arg[2]].kind == CG_NotInUse)
sewardjde4a1d02002-03-22 01:27:54 +0000364 return 1;
sewardj2e93c502002-04-12 11:12:52 +0000365 vg_assert(arg[2] >= 0 && arg[2] < vg_cgb_used);
366 vg_cgbs[arg[2]].kind = CG_NotInUse;
sewardjde4a1d02002-03-22 01:27:54 +0000367 vg_cgb_discards++;
368 return 0;
369
sewardj2e93c502002-04-12 11:12:52 +0000370 case VG_USERREQ__MAKE_NOACCESS_STACK: /* make noaccess stack block */
sewardjc7529c32002-04-16 01:55:18 +0000371 if (!VG_(clo_instrument))
372 return 0;
sewardj8c824512002-04-14 04:16:48 +0000373 vg_add_client_stack_block ( tst, arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000374 return 0;
375
sewardj2e93c502002-04-12 11:12:52 +0000376 case VG_USERREQ__RUNNING_ON_VALGRIND:
377 return 1;
378
379 case VG_USERREQ__DO_LEAK_CHECK:
sewardjc7529c32002-04-16 01:55:18 +0000380 if (!VG_(clo_instrument))
381 return 0;
sewardj2e93c502002-04-12 11:12:52 +0000382 VG_(detect_memory_leaks)();
383 return 0; /* return value is meaningless */
384
sewardjde4a1d02002-03-22 01:27:54 +0000385 default:
386 VG_(message)(Vg_UserMsg,
sewardj2e93c502002-04-12 11:12:52 +0000387 "Warning: unknown client request code %d", arg[0]);
sewardjde4a1d02002-03-22 01:27:54 +0000388 return 1;
389 }
390}
391
392
393/*--------------------------------------------------------------------*/
394/*--- end vg_clientperms.c ---*/
395/*--------------------------------------------------------------------*/