blob: f6351d371494f08e0535c98bdfa7741b23f5cabf [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
154void vg_add_client_stack_block ( Addr aa, UInt sz )
155{
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;
183 vg_csbs[vg_csb_used].where = VG_(get_ExeContext) ( False );
184 vg_csb_used++;
185
186 if (vg_csb_used > vg_csb_used_MAX)
187 vg_csb_used_MAX = vg_csb_used;
188
189 vg_assert(vg_csb_used <= vg_csb_size);
190
191 /* VG_(printf)("acsb %p %d\n", aa, sz); */
192 VGM_(make_noaccess) ( aa, sz );
193
194 /* And make sure that they are in descending order of address. */
195 i = vg_csb_used;
196 while (i > 0 && vg_csbs[i-1].start < vg_csbs[i].start) {
197 CStackBlock tmp = vg_csbs[i-1];
198 vg_csbs[i-1] = vg_csbs[i];
199 vg_csbs[i] = tmp;
200 vg_csb_swaps++;
201 }
202
203# if 1
204 for (i = 1; i < vg_csb_used; i++)
205 vg_assert(vg_csbs[i-1].start >= vg_csbs[i].start);
206# endif
207}
208
209
210/*------------------------------------------------------------*/
211/*--- Externally visible functions. ---*/
212/*------------------------------------------------------------*/
213
214void VG_(show_client_block_stats) ( void )
215{
216 VG_(message)(Vg_DebugMsg,
217 "general CBs: %d allocs, %d discards, %d maxinuse, %d search",
218 vg_cgb_allocs, vg_cgb_discards, vg_cgb_used_MAX, vg_cgb_search
219 );
220 VG_(message)(Vg_DebugMsg,
221 " stack CBs: %d allocs, %d discards, %d maxinuse, %d swap",
222 vg_csb_allocs, vg_csb_discards, vg_csb_used_MAX, vg_csb_swaps
223 );
224}
225
226
227Bool VG_(client_perm_maybe_describe)( Addr a, AddrInfo* ai )
228{
229 Int i;
230 /* VG_(printf)("try to identify %d\n", a); */
231
232 /* First see if it's a stack block. We do two passes, one exact
233 and one with a bit of slop, so as to try and get the most
234 accurate fix. */
235 for (i = 0; i < vg_csb_used; i++) {
236 if (vg_csbs[i].start <= a
237 && a < vg_csbs[i].start + vg_csbs[i].size) {
238 ai->akind = UserS;
239 ai->blksize = vg_csbs[i].size;
240 ai->rwoffset = (Int)(a) - (Int)(vg_csbs[i].start);
241 ai->lastchange = vg_csbs[i].where;
242 return True;
243 }
244 }
245
246 /* No exact match on the stack. Re-do the stack scan with a bit of
247 slop. */
248 for (i = 0; i < vg_csb_used; i++) {
249 if (vg_csbs[i].start - 8 <= a
250 && a < vg_csbs[i].start + vg_csbs[i].size + 8) {
251 ai->akind = UserS;
252 ai->blksize = vg_csbs[i].size;
253 ai->rwoffset = (Int)(a) - (Int)(vg_csbs[i].start);
254 ai->lastchange = vg_csbs[i].where;
255 return True;
256 }
257 }
258
259 /* No match on the stack. Perhaps it's a general block ? */
260 for (i = 0; i < vg_cgb_used; i++) {
261 if (vg_cgbs[i].kind == CG_NotInUse)
262 continue;
263 if (vg_cgbs[i].start - VG_AR_CLIENT_REDZONE_SZB <= a
264 && a < vg_cgbs[i].start + vg_cgbs[i].size + VG_AR_CLIENT_REDZONE_SZB) {
265 ai->akind = UserG;
266 ai->blksize = vg_cgbs[i].size;
267 ai->rwoffset = (Int)(a) - (Int)(vg_cgbs[i].start);
268 ai->lastchange = vg_cgbs[i].where;
269 return True;
270 }
271 }
272 return False;
273}
274
275
276void VG_(delete_client_stack_blocks_following_ESP_change) ( void )
277{
278 Addr newESP;
279 if (!VG_(clo_client_perms)) return;
280 newESP = VG_(baseBlock)[VGOFF_(m_esp)];
281 while (vg_csb_used > 0 &&
282 vg_csbs[vg_csb_used-1].start + vg_csbs[vg_csb_used-1].size <= newESP) {
283 vg_csb_used--;
284 vg_csb_discards++;
285 if (VG_(clo_verbosity) > 2)
286 VG_(printf)("discarding stack block %p for %d\n",
287 vg_csbs[vg_csb_used].start, vg_csbs[vg_csb_used].size);
288 }
289}
290
291
sewardj2e93c502002-04-12 11:12:52 +0000292UInt VG_(handle_client_request) ( UInt* arg_block )
sewardjde4a1d02002-03-22 01:27:54 +0000293{
sewardj2e93c502002-04-12 11:12:52 +0000294 Int i;
295 Bool ok;
296 Addr bad_addr;
297 UInt* arg = arg_block;
sewardjde4a1d02002-03-22 01:27:54 +0000298
299 if (VG_(clo_verbosity) > 2)
300 VG_(printf)("client request: code %d, addr %p, len %d\n",
sewardj2e93c502002-04-12 11:12:52 +0000301 arg[0], arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000302
303 vg_assert(VG_(clo_client_perms));
304 vg_assert(VG_(clo_instrument));
305
sewardj2e93c502002-04-12 11:12:52 +0000306 switch (arg[0]) {
307 case VG_USERREQ__MAKE_NOACCESS: /* make no access */
sewardjde4a1d02002-03-22 01:27:54 +0000308 i = vg_alloc_client_block();
309 /* VG_(printf)("allocated %d %p\n", i, vg_cgbs); */
310 vg_cgbs[i].kind = CG_NoAccess;
sewardj2e93c502002-04-12 11:12:52 +0000311 vg_cgbs[i].start = arg[1];
312 vg_cgbs[i].size = arg[2];
sewardjde4a1d02002-03-22 01:27:54 +0000313 vg_cgbs[i].where = VG_(get_ExeContext) ( False );
sewardj2e93c502002-04-12 11:12:52 +0000314 VGM_(make_noaccess) ( arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000315 return i;
sewardj2e93c502002-04-12 11:12:52 +0000316 case VG_USERREQ__MAKE_WRITABLE: /* make writable */
sewardjde4a1d02002-03-22 01:27:54 +0000317 i = vg_alloc_client_block();
318 vg_cgbs[i].kind = CG_Writable;
sewardj2e93c502002-04-12 11:12:52 +0000319 vg_cgbs[i].start = arg[1];
320 vg_cgbs[i].size = arg[2];
sewardjde4a1d02002-03-22 01:27:54 +0000321 vg_cgbs[i].where = VG_(get_ExeContext) ( False );
sewardj2e93c502002-04-12 11:12:52 +0000322 VGM_(make_writable) ( arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000323 return i;
sewardj2e93c502002-04-12 11:12:52 +0000324 case VG_USERREQ__MAKE_READABLE: /* make readable */
sewardjde4a1d02002-03-22 01:27:54 +0000325 i = vg_alloc_client_block();
326 vg_cgbs[i].kind = CG_Readable;
sewardj2e93c502002-04-12 11:12:52 +0000327 vg_cgbs[i].start = arg[1];
328 vg_cgbs[i].size = arg[2];
sewardjde4a1d02002-03-22 01:27:54 +0000329 vg_cgbs[i].where = VG_(get_ExeContext) ( False );
sewardj2e93c502002-04-12 11:12:52 +0000330 VGM_(make_readable) ( arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000331 return i;
332
sewardj2e93c502002-04-12 11:12:52 +0000333 case VG_USERREQ__CHECK_WRITABLE: /* check writable */
334 ok = VGM_(check_writable) ( arg[1], arg[2], &bad_addr );
sewardjde4a1d02002-03-22 01:27:54 +0000335 if (!ok)
336 VG_(record_user_err) ( bad_addr, True );
337 return ok ? (UInt)NULL : bad_addr;
sewardj2e93c502002-04-12 11:12:52 +0000338 case VG_USERREQ__CHECK_READABLE: /* check readable */
339 ok = VGM_(check_readable) ( arg[1], arg[2], &bad_addr );
sewardjde4a1d02002-03-22 01:27:54 +0000340 if (!ok)
341 VG_(record_user_err) ( bad_addr, False );
342 return ok ? (UInt)NULL : bad_addr;
343
sewardj2e93c502002-04-12 11:12:52 +0000344 case VG_USERREQ__DISCARD: /* discard */
sewardjde4a1d02002-03-22 01:27:54 +0000345 if (vg_cgbs == NULL
sewardj2e93c502002-04-12 11:12:52 +0000346 || arg[2] >= vg_cgb_used || vg_cgbs[arg[2]].kind == CG_NotInUse)
sewardjde4a1d02002-03-22 01:27:54 +0000347 return 1;
sewardj2e93c502002-04-12 11:12:52 +0000348 vg_assert(arg[2] >= 0 && arg[2] < vg_cgb_used);
349 vg_cgbs[arg[2]].kind = CG_NotInUse;
sewardjde4a1d02002-03-22 01:27:54 +0000350 vg_cgb_discards++;
351 return 0;
352
sewardj2e93c502002-04-12 11:12:52 +0000353 case VG_USERREQ__MAKE_NOACCESS_STACK: /* make noaccess stack block */
354 vg_add_client_stack_block ( arg[1], arg[2] );
sewardjde4a1d02002-03-22 01:27:54 +0000355 return 0;
356
sewardj2e93c502002-04-12 11:12:52 +0000357 case VG_USERREQ__RUNNING_ON_VALGRIND:
358 return 1;
359
360 case VG_USERREQ__DO_LEAK_CHECK:
361 VG_(detect_memory_leaks)();
362 return 0; /* return value is meaningless */
363
sewardjde4a1d02002-03-22 01:27:54 +0000364 default:
365 VG_(message)(Vg_UserMsg,
sewardj2e93c502002-04-12 11:12:52 +0000366 "Warning: unknown client request code %d", arg[0]);
sewardjde4a1d02002-03-22 01:27:54 +0000367 return 1;
368 }
369}
370
371
372/*--------------------------------------------------------------------*/
373/*--- end vg_clientperms.c ---*/
374/*--------------------------------------------------------------------*/