1 /* Copyright (C) 1995-2000, 2004 artofcode LLC. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: ilocate.c,v 1.14 2005/10/12 11:05:11 leonardo Exp $ */
18 /* Object locating and validating for Ghostscript memory manager */
19 #include "ghost.h"
20 #include "memory_.h"
21 #include "ierrors.h"
22 #include "gsexit.h"
23 #include "gsstruct.h"
24 #include "iastate.h"
25 #include "idict.h"
26 #include "igc.h" /* for gc_state_t and gcst_get_memory_ptr() */
27 #include "igcstr.h" /* for prototype */
28 #include "iname.h"
29 #include "ipacked.h"
30 #include "isstate.h"
31 #include "iutil.h" /* for packed_get */
32 #include "ivmspace.h"
33 #include "store.h"
34
35 /* ================ Locating ================ */
36
37 /* Locate a pointer in the chunks of a space being collected. */
38 /* This is only used for string garbage collection and for debugging. */
39 chunk_t *
gc_locate(const void * ptr,gc_state_t * gcst)40 gc_locate(const void *ptr, gc_state_t * gcst)
41 {
42 const gs_ref_memory_t *mem;
43 const gs_ref_memory_t *other;
44
45 if (chunk_locate(ptr, &gcst->loc))
46 return gcst->loc.cp;
47 mem = gcst->loc.memory;
48
49 /*
50 * Try the stable allocator of this space, or, if the current memory
51 * is the stable one, the non-stable allocator of this space.
52 */
53
54 if ((other = (const gs_ref_memory_t *)mem->stable_memory) != mem ||
55 (other = gcst->spaces_indexed[mem->space >> r_space_shift]) != mem
56 ) {
57 gcst->loc.memory = other;
58 gcst->loc.cp = 0;
59 if (chunk_locate(ptr, &gcst->loc))
60 return gcst->loc.cp;
61 }
62
63 /*
64 * Try the other space, if there is one, including its stable allocator
65 * and all save levels. (If the original space is system space, try
66 * local space.)
67 */
68
69 if (gcst->space_local != gcst->space_global) {
70 gcst->loc.memory = other =
71 (mem->space == avm_local ? gcst->space_global : gcst->space_local);
72 gcst->loc.cp = 0;
73 if (chunk_locate(ptr, &gcst->loc))
74 return gcst->loc.cp;
75 /* Try its stable allocator. */
76 if (other->stable_memory != (const gs_memory_t *)other) {
77 gcst->loc.memory = (gs_ref_memory_t *)other->stable_memory;
78 gcst->loc.cp = 0;
79 if (chunk_locate(ptr, &gcst->loc))
80 return gcst->loc.cp;
81 gcst->loc.memory = other;
82 }
83 /* Try other save levels of this space. */
84 while (gcst->loc.memory->saved != 0) {
85 gcst->loc.memory = &gcst->loc.memory->saved->state;
86 gcst->loc.cp = 0;
87 if (chunk_locate(ptr, &gcst->loc))
88 return gcst->loc.cp;
89 }
90 }
91
92 /*
93 * Try system space. This is simpler because it isn't subject to
94 * save/restore and doesn't have a separate stable allocator.
95 */
96
97 if (mem != gcst->space_system) {
98 gcst->loc.memory = gcst->space_system;
99 gcst->loc.cp = 0;
100 if (chunk_locate(ptr, &gcst->loc))
101 return gcst->loc.cp;
102 }
103
104 /*
105 * Try other save levels of the initial space, or of global space if the
106 * original space was system space. In the latter case, try all
107 * levels, and its stable allocator.
108 */
109
110 switch (mem->space) {
111 default: /* system */
112 other = gcst->space_global;
113 if (other->stable_memory != (const gs_memory_t *)other) {
114 gcst->loc.memory = (gs_ref_memory_t *)other->stable_memory;
115 gcst->loc.cp = 0;
116 if (chunk_locate(ptr, &gcst->loc))
117 return gcst->loc.cp;
118 }
119 gcst->loc.memory = other;
120 break;
121 case avm_global:
122 gcst->loc.memory = gcst->space_global;
123 break;
124 case avm_local:
125 gcst->loc.memory = gcst->space_local;
126 break;
127 }
128 for (;;) {
129 if (gcst->loc.memory != mem) { /* don't do twice */
130 gcst->loc.cp = 0;
131 if (chunk_locate(ptr, &gcst->loc))
132 return gcst->loc.cp;
133 }
134 if (gcst->loc.memory->saved == 0)
135 break;
136 gcst->loc.memory = &gcst->loc.memory->saved->state;
137 }
138
139 /* Restore locator to a legal state and report failure. */
140
141 gcst->loc.memory = mem;
142 gcst->loc.cp = 0;
143 return 0;
144 }
145
146 /* ================ Debugging ================ */
147
148 #ifdef DEBUG
149
150 /* Define the structure for temporarily saving allocator state. */
151 typedef struct alloc_temp_save_s {
152 chunk_t cc;
153 uint rsize;
154 ref rlast;
155 } alloc_temp_save_t;
156 /* Temporarily save the state of an allocator. */
157 private void
alloc_temp_save(alloc_temp_save_t * pats,gs_ref_memory_t * mem)158 alloc_temp_save(alloc_temp_save_t *pats, gs_ref_memory_t *mem)
159 {
160 chunk_t *pcc = mem->pcc;
161 obj_header_t *rcur = mem->cc.rcur;
162
163 if (pcc != 0) {
164 pats->cc = *pcc;
165 *pcc = mem->cc;
166 }
167 if (rcur != 0) {
168 pats->rsize = rcur[-1].o_size;
169 rcur[-1].o_size = mem->cc.rtop - (byte *) rcur;
170 /* Create the final ref, reserved for the GC. */
171 pats->rlast = ((ref *) mem->cc.rtop)[-1];
172 make_mark((ref *) mem->cc.rtop - 1);
173 }
174 }
175 /* Restore the temporarily saved state. */
176 private void
alloc_temp_restore(alloc_temp_save_t * pats,gs_ref_memory_t * mem)177 alloc_temp_restore(alloc_temp_save_t *pats, gs_ref_memory_t *mem)
178 {
179 chunk_t *pcc = mem->pcc;
180 obj_header_t *rcur = mem->cc.rcur;
181
182 if (rcur != 0) {
183 rcur[-1].o_size = pats->rsize;
184 ((ref *) mem->cc.rtop)[-1] = pats->rlast;
185 }
186 if (pcc != 0)
187 *pcc = pats->cc;
188 }
189
190 /* Validate the contents of an allocator. */
191 void
ialloc_validate_spaces(const gs_dual_memory_t * dmem)192 ialloc_validate_spaces(const gs_dual_memory_t * dmem)
193 {
194 int i;
195 gc_state_t state;
196 alloc_temp_save_t
197 save[countof(dmem->spaces_indexed)],
198 save_stable[countof(dmem->spaces_indexed)];
199 gs_ref_memory_t *mem;
200
201 state.spaces = dmem->spaces;
202 state.loc.memory = state.space_local;
203 state.loc.cp = 0;
204
205 /* Save everything we need to reset temporarily. */
206
207 for (i = 0; i < countof(save); i++)
208 if ((mem = dmem->spaces_indexed[i]) != 0) {
209 alloc_temp_save(&save[i], mem);
210 if (mem->stable_memory != (gs_memory_t *)mem)
211 alloc_temp_save(&save_stable[i],
212 (gs_ref_memory_t *)mem->stable_memory);
213 }
214
215 /* Validate memory. */
216
217 for (i = 0; i < countof(save); i++)
218 if ((mem = dmem->spaces_indexed[i]) != 0) {
219 ialloc_validate_memory(mem, &state);
220 if (mem->stable_memory != (gs_memory_t *)mem)
221 ialloc_validate_memory((gs_ref_memory_t *)mem->stable_memory,
222 &state);
223 }
224
225 /* Undo temporary changes. */
226
227 for (i = 0; i < countof(save); i++)
228 if ((mem = dmem->spaces_indexed[i]) != 0) {
229 if (mem->stable_memory != (gs_memory_t *)mem)
230 alloc_temp_restore(&save_stable[i],
231 (gs_ref_memory_t *)mem->stable_memory);
232 alloc_temp_restore(&save[i], mem);
233 }
234 }
235 void
ialloc_validate_memory(const gs_ref_memory_t * mem,gc_state_t * gcst)236 ialloc_validate_memory(const gs_ref_memory_t * mem, gc_state_t * gcst)
237 {
238 const gs_ref_memory_t *smem;
239 int level;
240
241 for (smem = mem, level = 0; smem != 0;
242 smem = &smem->saved->state, --level
243 ) {
244 const chunk_t *cp;
245 int i;
246
247 if_debug3('6', "[6]validating memory 0x%lx, space %d, level %d\n",
248 (ulong) mem, mem->space, level);
249 /* Validate chunks. */
250 for (cp = smem->cfirst; cp != 0; cp = cp->cnext)
251 ialloc_validate_chunk(cp, gcst);
252 /* Validate freelists. */
253 for (i = 0; i < num_freelists; ++i) {
254 uint free_size = i << log2_obj_align_mod;
255 const obj_header_t *pfree;
256
257 for (pfree = mem->freelists[i]; pfree != 0;
258 pfree = *(const obj_header_t * const *)pfree
259 ) {
260 uint size = pfree[-1].o_size;
261
262 if (pfree[-1].o_type != &st_free) {
263 lprintf3("Non-free object 0x%lx(%u) on freelist %i!\n",
264 (ulong) pfree, size, i);
265 break;
266 }
267 if ((i == LARGE_FREELIST_INDEX && size < max_freelist_size) ||
268 (i != LARGE_FREELIST_INDEX &&
269 (size < free_size - obj_align_mask || size > free_size))) {
270 lprintf3("Object 0x%lx(%u) size wrong on freelist %i!\n",
271 (ulong) pfree, size, i);
272 break;
273 }
274 }
275 }
276 };
277 }
278
279 /* Check the validity of an object's size. */
280 inline private bool
object_size_valid(const obj_header_t * pre,uint size,const chunk_t * cp)281 object_size_valid(const obj_header_t * pre, uint size, const chunk_t * cp)
282 {
283 return (pre->o_alone ? (const byte *)pre == cp->cbase :
284 size <= cp->ctop - (const byte *)(pre + 1));
285 }
286
287 /* Validate all the objects in a chunk. */
288 #if IGC_PTR_STABILITY_CHECK
289 void ialloc_validate_pointer_stability(const obj_header_t * ptr_from,
290 const obj_header_t * ptr_to);
291 private void ialloc_validate_ref(const ref *, gc_state_t *, const obj_header_t *pre_fr);
292 private void ialloc_validate_ref_packed(const ref_packed *, gc_state_t *, const obj_header_t *pre_fr);
293 #else
294 private void ialloc_validate_ref(const ref *, gc_state_t *);
295 private void ialloc_validate_ref_packed(const ref_packed *, gc_state_t *);
296 #endif
297 void
ialloc_validate_chunk(const chunk_t * cp,gc_state_t * gcst)298 ialloc_validate_chunk(const chunk_t * cp, gc_state_t * gcst)
299 {
300 if_debug_chunk('6', "[6]validating chunk", cp);
301 SCAN_CHUNK_OBJECTS(cp);
302 DO_ALL
303 if (pre->o_type == &st_free) {
304 if (!object_size_valid(pre, size, cp))
305 lprintf3("Bad free object 0x%lx(%lu), in chunk 0x%lx!\n",
306 (ulong) (pre + 1), (ulong) size, (ulong) cp);
307 } else
308 ialloc_validate_object(pre + 1, cp, gcst);
309 if_debug3('7', " [7]validating %s(%lu) 0x%lx\n",
310 struct_type_name_string(pre->o_type),
311 (ulong) size, (ulong) pre);
312 if (pre->o_type == &st_refs) {
313 const ref_packed *rp = (const ref_packed *)(pre + 1);
314 const char *end = (const char *)rp + size;
315
316 while ((const char *)rp < end) {
317 # if IGC_PTR_STABILITY_CHECK
318 ialloc_validate_ref_packed(rp, gcst, pre);
319 # else
320 ialloc_validate_ref_packed(rp, gcst);
321 # endif
322 rp = packed_next(rp);
323 }
324 } else {
325 struct_proc_enum_ptrs((*proc)) = pre->o_type->enum_ptrs;
326 uint index = 0;
327 enum_ptr_t eptr;
328 gs_ptr_type_t ptype;
329
330 if (proc != gs_no_struct_enum_ptrs)
331 for (; (ptype = (*proc) (gcst_get_memory_ptr(gcst),
332 pre + 1, size, index, &eptr,
333 pre->o_type, gcst)) != 0; ++index) {
334 if (eptr.ptr == 0)
335 DO_NOTHING;
336 else if (ptype == ptr_struct_type) {
337 ialloc_validate_object(eptr.ptr, NULL, gcst);
338 # if IGC_PTR_STABILITY_CHECK
339 ialloc_validate_pointer_stability(pre,
340 (const obj_header_t *)eptr.ptr - 1);
341 # endif
342 } else if (ptype == ptr_ref_type)
343 # if IGC_PTR_STABILITY_CHECK
344 ialloc_validate_ref_packed(eptr.ptr, gcst, pre);
345 # else
346 ialloc_validate_ref_packed(eptr.ptr, gcst);
347 # endif
348 }
349 }
350 END_OBJECTS_SCAN
351 }
352 /* Validate a ref. */
353 #if IGC_PTR_STABILITY_CHECK
354 private void
ialloc_validate_ref_packed(const ref_packed * rp,gc_state_t * gcst,const obj_header_t * pre_fr)355 ialloc_validate_ref_packed(const ref_packed * rp, gc_state_t * gcst, const obj_header_t *pre_fr)
356 {
357 const gs_memory_t *cmem = gcst->spaces.memories.named.system->stable_memory;
358
359 if (r_is_packed(rp)) {
360 ref unpacked;
361
362 packed_get(cmem, rp, &unpacked);
363 ialloc_validate_ref(&unpacked, gcst, pre_fr);
364 } else {
365 ialloc_validate_ref((const ref *)rp, gcst, pre_fr);
366 }
367 }
368 #else
369 private void
ialloc_validate_ref_packed(const ref_packed * rp,gc_state_t * gcst)370 ialloc_validate_ref_packed(const ref_packed * rp, gc_state_t * gcst)
371 {
372 const gs_memory_t *cmem = gcst->spaces.memories.named.system->stable_memory;
373
374 if (r_is_packed(rp)) {
375 ref unpacked;
376
377 packed_get(cmem, rp, &unpacked);
378 ialloc_validate_ref(&unpacked, gcst);
379 } else {
380 ialloc_validate_ref((const ref *)rp, gcst);
381 }
382 }
383 #endif
384 private void
ialloc_validate_ref(const ref * pref,gc_state_t * gcst,const obj_header_t * pre_fr)385 ialloc_validate_ref(const ref * pref, gc_state_t * gcst
386 # if IGC_PTR_STABILITY_CHECK
387 , const obj_header_t *pre_fr
388 # endif
389 )
390 {
391 const void *optr;
392 const ref *rptr;
393 const char *tname;
394 uint size;
395 const gs_memory_t *cmem = gcst->spaces.memories.named.system->stable_memory;
396
397 if (!gs_debug_c('?'))
398 return; /* no check */
399 if (r_space(pref) == avm_foreign)
400 return;
401 switch (r_type(pref)) {
402 case t_file:
403 optr = pref->value.pfile;
404 goto cks;
405 case t_device:
406 optr = pref->value.pdevice;
407 goto cks;
408 case t_fontID:
409 case t_struct:
410 case t_astruct:
411 optr = pref->value.pstruct;
412 cks: if (optr != 0) {
413 ialloc_validate_object(optr, NULL, gcst);
414 # if IGC_PTR_STABILITY_CHECK
415 ialloc_validate_pointer_stability(pre_fr,
416 (const obj_header_t *)optr - 1);
417 # endif
418 }
419 break;
420 case t_name:
421 if (name_index_ptr(cmem, name_index(cmem, pref)) != pref->value.pname) {
422 lprintf3("At 0x%lx, bad name %u, pname = 0x%lx\n",
423 (ulong) pref, (uint)name_index(cmem, pref),
424 (ulong) pref->value.pname);
425 break;
426 } {
427 ref sref;
428
429 name_string_ref(cmem, pref, &sref);
430 if (r_space(&sref) != avm_foreign &&
431 !gc_locate(sref.value.const_bytes, gcst)
432 ) {
433 lprintf4("At 0x%lx, bad name %u, pname = 0x%lx, string 0x%lx not in any chunk\n",
434 (ulong) pref, (uint) r_size(pref),
435 (ulong) pref->value.pname,
436 (ulong) sref.value.const_bytes);
437 }
438 }
439 break;
440 case t_string:
441 if (r_size(pref) != 0 && !gc_locate(pref->value.bytes, gcst))
442 lprintf3("At 0x%lx, string ptr 0x%lx[%u] not in any chunk\n",
443 (ulong) pref, (ulong) pref->value.bytes,
444 (uint) r_size(pref));
445 break;
446 case t_array:
447 if (r_size(pref) == 0)
448 break;
449 rptr = pref->value.refs;
450 size = r_size(pref);
451 tname = "array";
452 cka: if (!gc_locate(rptr, gcst)) {
453 lprintf3("At 0x%lx, %s 0x%lx not in any chunk\n",
454 (ulong) pref, tname, (ulong) rptr);
455 break;
456 } {
457 uint i;
458
459 for (i = 0; i < size; ++i) {
460 const ref *elt = rptr + i;
461
462 if (r_is_packed(elt))
463 lprintf5("At 0x%lx, %s 0x%lx[%u] element %u is not a ref\n",
464 (ulong) pref, tname, (ulong) rptr, size, i);
465 }
466 }
467 break;
468 case t_shortarray:
469 case t_mixedarray:
470 if (r_size(pref) == 0)
471 break;
472 optr = pref->value.packed;
473 if (!gc_locate(optr, gcst))
474 lprintf2("At 0x%lx, packed array 0x%lx not in any chunk\n",
475 (ulong) pref, (ulong) optr);
476 break;
477 case t_dictionary:
478 {
479 const dict *pdict = pref->value.pdict;
480
481 if (!r_has_type(&pdict->values, t_array) ||
482 !r_is_array(&pdict->keys) ||
483 !r_has_type(&pdict->count, t_integer) ||
484 !r_has_type(&pdict->maxlength, t_integer)
485 )
486 lprintf2("At 0x%lx, invalid dict 0x%lx\n",
487 (ulong) pref, (ulong) pdict);
488 rptr = (const ref *)pdict;
489 }
490 size = sizeof(dict) / sizeof(ref);
491 tname = "dict";
492 goto cka;
493 }
494 }
495
496 #if IGC_PTR_STABILITY_CHECK
497 /* Validate an pointer stability. */
498 void
ialloc_validate_pointer_stability(const obj_header_t * ptr_fr,const obj_header_t * ptr_to)499 ialloc_validate_pointer_stability(const obj_header_t * ptr_fr,
500 const obj_header_t * ptr_to)
501 {
502 static const char *sn[] = {"undef", "undef", "system", "undef",
503 "global_stable", "global", "local_stable", "local"};
504
505 if (ptr_fr->d.o.space_id < ptr_to->d.o.space_id) {
506 const char *sn_fr = (ptr_fr->d.o.space_id < count_of(sn)
507 ? sn[ptr_fr->d.o.space_id] : "unknown");
508 const char *sn_to = (ptr_to->d.o.space_id < count_of(sn)
509 ? sn[ptr_to->d.o.space_id] : "unknown");
510
511 lprintf6("Reference to a less stable object 0x%lx<%s> "
512 "in the space \'%s\' from 0x%lx<%s> in the space \'%s\' !\n",
513 (ulong) ptr_to, ptr_to->d.o.t.type->sname, sn_to,
514 (ulong) ptr_fr, ptr_fr->d.o.t.type->sname, sn_fr);
515 }
516 }
517 #endif
518
519 /* Validate an object. */
520 void
ialloc_validate_object(const obj_header_t * ptr,const chunk_t * cp,gc_state_t * gcst)521 ialloc_validate_object(const obj_header_t * ptr, const chunk_t * cp,
522 gc_state_t * gcst)
523 {
524 const obj_header_t *pre = ptr - 1;
525 ulong size = pre_obj_contents_size(pre);
526 gs_memory_type_ptr_t otype = pre->o_type;
527 const char *oname;
528
529 if (!gs_debug_c('?'))
530 return; /* no check */
531 if (cp == 0 && gcst != 0) {
532 gc_state_t st;
533
534 st = *gcst; /* no side effects! */
535 if (!(cp = gc_locate(pre, &st))) {
536 lprintf1("Object 0x%lx not in any chunk!\n",
537 (ulong) ptr);
538 return; /*gs_abort(); */
539 }
540 }
541 if (otype == &st_free) {
542 lprintf3("Reference to free object 0x%lx(%lu), in chunk 0x%lx!\n",
543 (ulong) ptr, (ulong) size, (ulong) cp);
544 gs_abort(gcst->heap);
545 }
546 if ((cp != 0 && !object_size_valid(pre, size, cp)) ||
547 otype->ssize == 0 ||
548 size % otype->ssize != 0 ||
549 (oname = struct_type_name_string(otype),
550 *oname < 33 || *oname > 126)
551 ) {
552 lprintf2("Bad object 0x%lx(%lu),\n",
553 (ulong) ptr, (ulong) size);
554 dprintf2(" ssize = %u, in chunk 0x%lx!\n",
555 otype->ssize, (ulong) cp);
556 gs_abort(gcst->heap);
557 }
558 }
559
560 #else /* !DEBUG */
561
562 void
ialloc_validate_spaces(const gs_dual_memory_t * dmem)563 ialloc_validate_spaces(const gs_dual_memory_t * dmem)
564 {
565 }
566
567 void
ialloc_validate_memory(const gs_ref_memory_t * mem,gc_state_t * gcst)568 ialloc_validate_memory(const gs_ref_memory_t * mem, gc_state_t * gcst)
569 {
570 }
571
572 void
ialloc_validate_chunk(const chunk_t * cp,gc_state_t * gcst)573 ialloc_validate_chunk(const chunk_t * cp, gc_state_t * gcst)
574 {
575 }
576
577 void
ialloc_validate_object(const obj_header_t * ptr,const chunk_t * cp,gc_state_t * gcst)578 ialloc_validate_object(const obj_header_t * ptr, const chunk_t * cp,
579 gc_state_t * gcst)
580 {
581 }
582
583 #endif /* (!)DEBUG */
584