blob: e9ecbc420c9282bc99c0afec4bdd426b11d6bc1e [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
sewardjde4a1d02002-03-22 01:27:54 +000013
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation; either version 2 of the
17 License, or (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful, but
20 WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
27 02111-1307, USA.
28
29 The GNU General Public License is contained in the file LICENSE.
30*/
31
32#include "vg_include.h"
33#include "vg_constants.h"
34
sewardj2e93c502002-04-12 11:12:52 +000035#include "valgrind.h" /* for VG_USERREQ__* */
36
sewardjde4a1d02002-03-22 01:27:54 +000037
38/*------------------------------------------------------------*/
39/*--- General client block management. ---*/
40/*------------------------------------------------------------*/
41
42/* This is managed as an expanding array of client block descriptors.
43 Indices of live descriptors are issued to the client, so it can ask
44 to free them later. Therefore we cannot slide live entries down
45 over dead ones. Instead we must use free/inuse flags and scan for
46 an empty slot at allocation time. This in turn means allocation is
47 relatively expensive, so we hope this does not happen too often.
48*/
49
50typedef
51 enum { CG_NotInUse, CG_NoAccess, CG_Writable, CG_Readable }
52 CGenBlockKind;
53
54typedef
55 struct {
56 Addr start;
57 UInt size;
58 ExeContext* where;
59 CGenBlockKind kind;
60 }
61 CGenBlock;
62
63/* This subsystem is self-initialising. */
64static UInt vg_cgb_size = 0;
65static UInt vg_cgb_used = 0;
66static CGenBlock* vg_cgbs = NULL;
67
68/* Stats for this subsystem. */
69static UInt vg_cgb_used_MAX = 0; /* Max in use. */
70static UInt vg_cgb_allocs = 0; /* Number of allocs. */
71static UInt vg_cgb_discards = 0; /* Number of discards. */
72static UInt vg_cgb_search = 0; /* Number of searches. */
73
74
75static
76Int vg_alloc_client_block ( void )
77{
78 Int i, sz_new;
79 CGenBlock* cgbs_new;
80
81 vg_cgb_allocs++;
82
83 for (i = 0; i < vg_cgb_used; i++) {
84 vg_cgb_search++;
85 if (vg_cgbs[i].kind == CG_NotInUse)
86 return i;
87 }
88
89 /* Not found. Try to allocate one at the end. */
90 if (vg_cgb_used < vg_cgb_size) {
91 vg_cgb_used++;
92 return vg_cgb_used-1;
93 }
94
95 /* Ok, we have to allocate a new one. */
96 vg_assert(vg_cgb_used == vg_cgb_size);
97 sz_new = (vg_cgbs == NULL) ? 10 : (2 * vg_cgb_size);
98
99 cgbs_new = VG_(malloc)( VG_AR_PRIVATE, sz_new * sizeof(CGenBlock) );
100 for (i = 0; i < vg_cgb_used; i++)
101 cgbs_new[i] = vg_cgbs[i];
102
103 if (vg_cgbs != NULL)
104 VG_(free)( VG_AR_PRIVATE, vg_cgbs );
105 vg_cgbs = cgbs_new;
106
107 vg_cgb_size = sz_new;
108 vg_cgb_used++;
109 if (vg_cgb_used > vg_cgb_used_MAX)
110 vg_cgb_used_MAX = vg_cgb_used;
111 return vg_cgb_used-1;
112}
113
114
115/*------------------------------------------------------------*/
116/*--- Stack block management. ---*/
117/*------------------------------------------------------------*/
118
119/* This is managed as an expanding array of CStackBlocks. They are
120 packed up against the left-hand end of the array, with no holes.
121 They are kept sorted by the start field, with the [0] having the
122 highest value. This means it's pretty cheap to put new blocks at
123 the end, corresponding to stack pushes, since the additions put
124 blocks on in what is presumably fairly close to strictly descending
125 order. If this assumption doesn't hold the performance
126 consequences will be horrible.
127
128 When the client's %ESP jumps back upwards as the result of a RET
129 insn, we shrink the array backwards from the end, in a
130 guaranteed-cheap linear scan.
131*/
132
133typedef
134 struct {
135 Addr start;
136 UInt size;
137 ExeContext* where;
138 }
139 CStackBlock;
140
141/* This subsystem is self-initialising. */
142static UInt vg_csb_size = 0;
143static UInt vg_csb_used = 0;
144static CStackBlock* vg_csbs = NULL;
145
146/* Stats for this subsystem. */
147static UInt vg_csb_used_MAX = 0; /* Max in use. */
148static UInt vg_csb_allocs = 0; /* Number of allocs. */
149static UInt vg_csb_discards = 0; /* Number of discards. */
150static UInt vg_csb_swaps = 0; /* Number of searches. */
151
152static
sewardj8c824512002-04-14 04:16:48 +0000153void vg_add_client_stack_block ( ThreadState* tst, Addr aa, UInt sz )
sewardjde4a1d02002-03-22 01:27:54 +0000154{
155 UInt i, sz_new;
156 CStackBlock* csbs_new;
157 vg_csb_allocs++;
158
159 /* Ensure there is space for a new block. */
160
161 if (vg_csb_used >= vg_csb_size) {
162
163 /* No; we have to expand the array. */
164 vg_assert(vg_csb_used == vg_csb_size);
165
166 sz_new = (vg_csbs == NULL) ? 10 : (2 * vg_csb_size);
167
168 csbs_new = VG_(malloc)( VG_AR_PRIVATE, sz_new * sizeof(CStackBlock) );
169 for (i = 0; i < vg_csb_used; i++)
170 csbs_new[i] = vg_csbs[i];
171
172 if (vg_csbs != NULL)
173 VG_(free)( VG_AR_PRIVATE, vg_csbs );
174 vg_csbs = csbs_new;
175
176 vg_csb_size = sz_new;
177 }
178
179 /* Ok, we can use [vg_csb_used]. */
180 vg_csbs[vg_csb_used].start = aa;
181 vg_csbs[vg_csb_used].size = sz;
sewardj8c824512002-04-14 04:16:48 +0000182 /* Actually running a thread at this point. */
183 vg_csbs[vg_csb_used].where
184 = VG_(get_ExeContext) ( False, tst->m_eip, tst->m_ebp );
sewardjde4a1d02002-03-22 01:27:54 +0000185 vg_csb_used++;
186
187 if (vg_csb_used > vg_csb_used_MAX)
188 vg_csb_used_MAX = vg_csb_used;
189
190 vg_assert(vg_csb_used <= vg_csb_size);
191
192 /* VG_(printf)("acsb %p %d\n", aa, sz); */
193 VGM_(make_noaccess) ( aa, sz );
194
195 /* And make sure that they are in descending order of address. */
196 i = vg_csb_used;
197 while (i > 0 && vg_csbs[i-1].start < vg_csbs[i].start) {
198 CStackBlock tmp = vg_csbs[i-1];
199 vg_csbs[i-1] = vg_csbs[i];
200 vg_csbs[i] = tmp;
201 vg_csb_swaps++;
202 }
203
204# if 1
205 for (i = 1; i < vg_csb_used; i++)
206 vg_assert(vg_csbs[i-1].start >= vg_csbs[i].start);
207# endif
208}
209
210
211/*------------------------------------------------------------*/
212/*--- Externally visible functions. ---*/
213/*------------------------------------------------------------*/
214
215void VG_(show_client_block_stats) ( void )
216{
217 VG_(message)(Vg_DebugMsg,
218 "general CBs: %d allocs, %d discards, %d maxinuse, %d search",
219 vg_cgb_allocs, vg_cgb_discards, vg_cgb_used_MAX, vg_cgb_search
220 );
221 VG_(message)(Vg_DebugMsg,
222 " stack CBs: %d allocs, %d discards, %d maxinuse, %d swap",
223 vg_csb_allocs, vg_csb_discards, vg_csb_used_MAX, vg_csb_swaps
224 );
225}
226
227
228Bool VG_(client_perm_maybe_describe)( Addr a, AddrInfo* ai )
229{
230 Int i;
231 /* VG_(printf)("try to identify %d\n", a); */
232
233 /* First see if it's a stack block. We do two passes, one exact
234 and one with a bit of slop, so as to try and get the most
235 accurate fix. */
236 for (i = 0; i < vg_csb_used; i++) {
237 if (vg_csbs[i].start <= a
238 && a < vg_csbs[i].start + vg_csbs[i].size) {
239 ai->akind = UserS;
240 ai->blksize = vg_csbs[i].size;
241 ai->rwoffset = (Int)(a) - (Int)(vg_csbs[i].start);
242 ai->lastchange = vg_csbs[i].where;
243 return True;
244 }
245 }
246
247 /* No exact match on the stack. Re-do the stack scan with a bit of
248 slop. */
249 for (i = 0; i < vg_csb_used; i++) {
250 if (vg_csbs[i].start - 8 <= a
251 && a < vg_csbs[i].start + vg_csbs[i].size + 8) {
252 ai->akind = UserS;
253 ai->blksize = vg_csbs[i].size;
254 ai->rwoffset = (Int)(a) - (Int)(vg_csbs[i].start);
255 ai->lastchange = vg_csbs[i].where;
256 return True;
257 }
258 }
259
260 /* No match on the stack. Perhaps it's a general block ? */
261 for (i = 0; i < vg_cgb_used; i++) {
262 if (vg_cgbs[i].kind == CG_NotInUse)
263 continue;
264 if (vg_cgbs[i].start - VG_AR_CLIENT_REDZONE_SZB <= a
sewardjc7529c32002-04-16 01:55:18 +0000265 && a < vg_cgbs[i].start
266 + vg_cgbs[i].size
267 + VG_AR_CLIENT_REDZONE_SZB) {
sewardjde4a1d02002-03-22 01:27:54 +0000268 ai->akind = UserG;
269 ai->blksize = vg_cgbs[i].size;
270 ai->rwoffset = (Int)(a) - (Int)(vg_cgbs[i].start);
271 ai->lastchange = vg_cgbs[i].where;
272 return True;
273 }
274 }
275 return False;
276}
277
278
279void VG_(delete_client_stack_blocks_following_ESP_change) ( void )
280{
281 Addr newESP;
sewardjde4a1d02002-03-22 01:27:54 +0000282 newESP = VG_(baseBlock)[VGOFF_(m_esp)];
sewardjc7529c32002-04-16 01:55:18 +0000283 while (vg_csb_used > 0
284 && vg_csbs[vg_csb_used-1].start + vg_csbs[vg_csb_used-1].size
285 <= newESP) {
sewardjde4a1d02002-03-22 01:27:54 +0000286 vg_csb_used--;
287 vg_csb_discards++;
288 if (VG_(clo_verbosity) > 2)
289 VG_(printf)("discarding stack block %p for %d\n",
sewardj9a199dc2002-04-14 13:01:38 +0000290 (void*)vg_csbs[vg_csb_used].start,
291 vg_csbs[vg_csb_used].size);
sewardjde4a1d02002-03-22 01:27:54 +0000292 }
293}
294
295
sewardj8c824512002-04-14 04:16:48 +0000296UInt VG_(handle_client_request) ( ThreadState* tst, UInt* arg_block )
sewardjde4a1d02002-03-22 01:27:54 +0000297{
sewardj2e93c502002-04-12 11:12:52 +0000298 Int i;
299 Bool ok;
300 Addr bad_addr;
301 UInt* arg = arg_block;
sewardjde4a1d02002-03-22 01:27:54 +0000302
303 if (VG_(clo_verbosity) > 2)
304 VG_(printf)("client request: code %d, addr %p, len %d\n",
sewardj9a199dc2002-04-14 13:01:38 +0000305 arg[0], (void*)arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000306
sewardj2e93c502002-04-12 11:12:52 +0000307 switch (arg[0]) {
308 case VG_USERREQ__MAKE_NOACCESS: /* make no access */
sewardjc7529c32002-04-16 01:55:18 +0000309 if (!VG_(clo_instrument))
310 return 0;
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 */
sewardjc7529c32002-04-16 01:55:18 +0000321 if (!VG_(clo_instrument))
322 return 0;
sewardjde4a1d02002-03-22 01:27:54 +0000323 i = vg_alloc_client_block();
324 vg_cgbs[i].kind = CG_Writable;
sewardj2e93c502002-04-12 11:12:52 +0000325 vg_cgbs[i].start = arg[1];
326 vg_cgbs[i].size = arg[2];
sewardj8c824512002-04-14 04:16:48 +0000327 vg_cgbs[i].where
328 = VG_(get_ExeContext) ( False, tst->m_eip, tst->m_ebp );
sewardj2e93c502002-04-12 11:12:52 +0000329 VGM_(make_writable) ( arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000330 return i;
sewardj2e93c502002-04-12 11:12:52 +0000331 case VG_USERREQ__MAKE_READABLE: /* make readable */
sewardjc7529c32002-04-16 01:55:18 +0000332 if (!VG_(clo_instrument))
333 return 0;
sewardjde4a1d02002-03-22 01:27:54 +0000334 i = vg_alloc_client_block();
335 vg_cgbs[i].kind = CG_Readable;
sewardj2e93c502002-04-12 11:12:52 +0000336 vg_cgbs[i].start = arg[1];
337 vg_cgbs[i].size = arg[2];
sewardj8c824512002-04-14 04:16:48 +0000338 vg_cgbs[i].where
339 = VG_(get_ExeContext) ( False, tst->m_eip, tst->m_ebp );
sewardj2e93c502002-04-12 11:12:52 +0000340 VGM_(make_readable) ( arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000341 return i;
342
sewardj2e93c502002-04-12 11:12:52 +0000343 case VG_USERREQ__CHECK_WRITABLE: /* check writable */
sewardjc7529c32002-04-16 01:55:18 +0000344 if (!VG_(clo_instrument))
345 return 0;
sewardj2e93c502002-04-12 11:12:52 +0000346 ok = VGM_(check_writable) ( arg[1], arg[2], &bad_addr );
sewardjde4a1d02002-03-22 01:27:54 +0000347 if (!ok)
sewardj8c824512002-04-14 04:16:48 +0000348 VG_(record_user_err) ( tst, bad_addr, True );
sewardjde4a1d02002-03-22 01:27:54 +0000349 return ok ? (UInt)NULL : bad_addr;
sewardj2e93c502002-04-12 11:12:52 +0000350 case VG_USERREQ__CHECK_READABLE: /* check readable */
sewardjc7529c32002-04-16 01:55:18 +0000351 if (!VG_(clo_instrument))
352 return 0;
sewardj2e93c502002-04-12 11:12:52 +0000353 ok = VGM_(check_readable) ( arg[1], arg[2], &bad_addr );
sewardjde4a1d02002-03-22 01:27:54 +0000354 if (!ok)
sewardj8c824512002-04-14 04:16:48 +0000355 VG_(record_user_err) ( tst, bad_addr, False );
sewardjde4a1d02002-03-22 01:27:54 +0000356 return ok ? (UInt)NULL : bad_addr;
357
sewardj2e93c502002-04-12 11:12:52 +0000358 case VG_USERREQ__DISCARD: /* discard */
sewardjc7529c32002-04-16 01:55:18 +0000359 if (!VG_(clo_instrument))
360 return 0;
sewardjde4a1d02002-03-22 01:27:54 +0000361 if (vg_cgbs == NULL
sewardj2e93c502002-04-12 11:12:52 +0000362 || arg[2] >= vg_cgb_used || vg_cgbs[arg[2]].kind == CG_NotInUse)
sewardjde4a1d02002-03-22 01:27:54 +0000363 return 1;
sewardj2e93c502002-04-12 11:12:52 +0000364 vg_assert(arg[2] >= 0 && arg[2] < vg_cgb_used);
365 vg_cgbs[arg[2]].kind = CG_NotInUse;
sewardjde4a1d02002-03-22 01:27:54 +0000366 vg_cgb_discards++;
367 return 0;
368
sewardj2e93c502002-04-12 11:12:52 +0000369 case VG_USERREQ__MAKE_NOACCESS_STACK: /* make noaccess stack block */
sewardjc7529c32002-04-16 01:55:18 +0000370 if (!VG_(clo_instrument))
371 return 0;
sewardj8c824512002-04-14 04:16:48 +0000372 vg_add_client_stack_block ( tst, arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000373 return 0;
374
sewardj9650c992002-04-16 03:44:31 +0000375 /* Is handled by the scheduler as a trivial request, for
376 performance reasons. */
377 /*
sewardj2e93c502002-04-12 11:12:52 +0000378 case VG_USERREQ__RUNNING_ON_VALGRIND:
379 return 1;
sewardj9650c992002-04-16 03:44:31 +0000380 */
sewardj2e93c502002-04-12 11:12:52 +0000381
382 case VG_USERREQ__DO_LEAK_CHECK:
sewardjc7529c32002-04-16 01:55:18 +0000383 if (!VG_(clo_instrument))
384 return 0;
sewardj2e93c502002-04-12 11:12:52 +0000385 VG_(detect_memory_leaks)();
386 return 0; /* return value is meaningless */
387
sewardj18d75132002-05-16 11:06:21 +0000388 case VG_USERREQ__DISCARD_TRANSLATIONS:
389 VG_(invalidate_translations)( arg[1], arg[2] );
390 return 0; /* return value is meaningless */
391
sewardjde4a1d02002-03-22 01:27:54 +0000392 default:
393 VG_(message)(Vg_UserMsg,
sewardj2e93c502002-04-12 11:12:52 +0000394 "Warning: unknown client request code %d", arg[0]);
sewardjde4a1d02002-03-22 01:27:54 +0000395 return 1;
396 }
397}
398
399
400/*--------------------------------------------------------------------*/
401/*--- end vg_clientperms.c ---*/
402/*--------------------------------------------------------------------*/