Coverage Report

Created: 2025-06-10 07:17

/src/ghostpdl/psi/ialloc.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2025 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* Memory allocator for Ghostscript interpreter */
18
#include "gx.h"
19
#include "memory_.h"
20
#include "gsexit.h"
21
#include "ierrors.h"
22
#include "gsstruct.h"
23
#include "iref.h"   /* must precede iastate.h */
24
#include "iastate.h"
25
#include "igc.h"    /* for gs_gc_reclaim */
26
#include "ipacked.h"
27
#include "iutil.h"
28
#include "ivmspace.h"
29
#include "store.h"
30
31
/*
32
 * Define global and local instances.
33
 */
34
public_st_gs_dual_memory();
35
36
/* Initialize the allocator */
37
int
38
ialloc_init(gs_dual_memory_t *dmem, gs_memory_t * rmem, uint clump_size,
39
            bool level2)
40
9.56k
{
41
9.56k
    gs_ref_memory_t *ilmem = ialloc_alloc_state(rmem, clump_size);
42
9.56k
    gs_ref_memory_t *ilmem_stable = ialloc_alloc_state(rmem, clump_size);
43
9.56k
    gs_ref_memory_t *igmem = 0;
44
9.56k
    gs_ref_memory_t *igmem_stable = 0;
45
9.56k
    gs_ref_memory_t *ismem = ialloc_alloc_state(rmem, clump_size);
46
9.56k
    int i;
47
48
9.56k
    if (ilmem == 0 || ilmem_stable == 0 || ismem == 0)
49
0
        goto fail;
50
9.56k
    ilmem->stable_memory = (gs_memory_t *)ilmem_stable;
51
9.56k
    if (level2) {
52
9.56k
        igmem = ialloc_alloc_state(rmem, clump_size);
53
9.56k
        igmem_stable = ialloc_alloc_state(rmem, clump_size);
54
9.56k
        if (igmem == 0 || igmem_stable == 0)
55
0
            goto fail;
56
9.56k
        igmem->stable_memory = (gs_memory_t *)igmem_stable;
57
9.56k
    } else
58
0
        igmem = ilmem, igmem_stable = ilmem_stable;
59
47.8k
    for (i = 0; i < countof(dmem->spaces_indexed); i++)
60
38.2k
        dmem->spaces_indexed[i] = 0;
61
9.56k
    dmem->space_local = ilmem;
62
9.56k
    dmem->space_global = igmem;
63
9.56k
    dmem->space_system = ismem;
64
9.56k
    dmem->spaces.vm_reclaim = gs_gc_reclaim; /* real GC */
65
9.56k
    dmem->reclaim = 0;    /* no interpreter GC yet */
66
    /* Level 1 systems have only local VM. */
67
9.56k
    igmem->space = avm_global;
68
9.56k
    igmem_stable->space = avm_global;
69
9.56k
    ilmem->space = avm_local; /* overrides if ilmem == igmem */
70
9.56k
    ilmem_stable->space = avm_local; /* ditto */
71
9.56k
    ismem->space = avm_system;
72
#   if IGC_PTR_STABILITY_CHECK
73
    igmem->space_id = (i_vm_global << 1) + 1;
74
    igmem_stable->space_id = i_vm_global << 1;
75
    ilmem->space_id = (i_vm_local << 1) + 1;  /* overrides if ilmem == igmem */
76
    ilmem_stable->space_id = i_vm_local << 1; /* ditto */
77
    ismem->space_id = (i_vm_system << 1);
78
#   endif
79
9.56k
    ialloc_set_space(dmem, avm_global);
80
9.56k
    return 0;
81
0
 fail:
82
0
    ialloc_free_state(igmem_stable);
83
0
    ialloc_free_state(igmem);
84
0
    ialloc_free_state(ismem);
85
0
    ialloc_free_state(ilmem_stable);
86
0
    ialloc_free_state(ilmem);
87
0
    return_error(gs_error_VMerror);
88
9.56k
}
89
90
/* Free the allocator */
91
void
92
ialloc_finit(gs_dual_memory_t *mem)
93
9.56k
{
94
9.56k
    if (mem != NULL) {
95
9.56k
        gs_ref_memory_t *ilmem = mem->space_local;
96
9.56k
        gs_ref_memory_t *igmem = mem->space_global;
97
9.56k
        gs_ref_memory_t *ismem = mem->space_system;
98
99
9.56k
        if (ilmem != NULL) {
100
9.56k
            gs_ref_memory_t *ilmem_stable = (gs_ref_memory_t *)(ilmem->stable_memory);
101
9.56k
            gs_memory_free_all((gs_memory_t *)ilmem_stable, FREE_ALL_EVERYTHING, "ialloc_finit");
102
9.56k
            gs_memory_free_all((gs_memory_t *)ilmem, FREE_ALL_EVERYTHING, "ialloc_finit");
103
9.56k
        }
104
105
9.56k
        if (igmem != NULL) {
106
9.56k
            gs_ref_memory_t *igmem_stable = (gs_ref_memory_t *)(igmem->stable_memory);
107
9.56k
            gs_memory_free_all((gs_memory_t *)igmem_stable, FREE_ALL_EVERYTHING, "ialloc_finit");
108
9.56k
            gs_memory_free_all((gs_memory_t *)igmem, FREE_ALL_EVERYTHING, "ialloc_finit");
109
9.56k
        }
110
111
9.56k
        if (ismem != NULL)
112
9.56k
            gs_memory_free_all((gs_memory_t *)ismem, FREE_ALL_EVERYTHING, "ialloc_finit");
113
9.56k
     }
114
9.56k
}
115
116
/* ================ Local/global VM ================ */
117
118
/* Get the space attribute of an allocator */
119
uint
120
imemory_space(const gs_ref_memory_t * iimem)
121
59.3M
{
122
59.3M
    return iimem->space;
123
59.3M
}
124
125
/* Select the allocation space. */
126
void
127
ialloc_set_space(gs_dual_memory_t * dmem, uint space)
128
30.4M
{
129
30.4M
    gs_ref_memory_t *mem = dmem->spaces_indexed[space >> r_space_shift];
130
131
30.4M
    dmem->current = mem;
132
30.4M
    dmem->current_space = mem->space;
133
30.4M
}
134
135
/* Get the l_new attribute of a current allocator. */
136
/* (A copy of the new_mask in the gs_dual_memory_t.) */
137
uint
138
imemory_new_mask(const gs_ref_memory_t *imem)
139
60.7M
{
140
60.7M
    return imem->new_mask;
141
60.7M
}
142
143
/* Get the save level of an allocator. */
144
int
145
imemory_save_level(const gs_ref_memory_t *imem)
146
1.03M
{
147
1.03M
    return imem->save_level;
148
1.03M
}
149
150
/* Reset the requests. */
151
void
152
ialloc_reset_requested(gs_dual_memory_t * dmem)
153
192k
{
154
192k
    dmem->space_system->gc_status.requested = 0;
155
192k
    dmem->space_global->gc_status.requested = 0;
156
192k
    dmem->space_local->gc_status.requested = 0;
157
192k
}
158
159
/* ================ Refs ================ */
160
161
#ifdef DEBUG
162
static int
163
ialloc_trace_space(const gs_ref_memory_t *imem)
164
{
165
    return imem->space + (imem->stable_memory == (const gs_memory_t *)imem);
166
}
167
#endif
168
169
/* Register a ref root. */
170
int
171
gs_register_ref_root(gs_memory_t *mem, gs_gc_root_t **root,
172
                     void **pp, client_name_t cname)
173
172k
{
174
172k
    return gs_register_root(mem, root, ptr_ref_type, pp, cname);
175
172k
}
176
177
/*
178
 * As noted in iastate.h, every run of refs has an extra ref at the end
179
 * to hold relocation information for the garbage collector;
180
 * since sizeof(ref) % obj_align_mod == 0, we never need to
181
 * allocate any additional padding space at the end of the block.
182
 */
183
184
/* Allocate an array of refs. */
185
int
186
gs_alloc_ref_array(gs_ref_memory_t * mem, ref * parr, uint attrs,
187
                   uint num_refs, client_name_t cname)
188
109M
{
189
109M
    ref *obj;
190
109M
    int i;
191
192
    /* If we're allocating a run of refs already, */
193
    /* and we aren't about to overflow the maximum run length, use it. */
194
109M
    if (mem->cc && mem->cc->has_refs == true && mem->cc->rtop == mem->cc->cbot &&
195
109M
        num_refs < (mem->cc->ctop - mem->cc->cbot) / sizeof(ref) &&
196
109M
        mem->cc->rtop - (byte *) mem->cc->rcur + num_refs * sizeof(ref) <
197
107M
        max_size_st_refs
198
109M
        ) {
199
96.5M
        ref *end;
200
201
96.5M
        obj = (ref *) mem->cc->rtop - 1;    /* back up over last ref */
202
96.5M
        if_debug4m('A', (const gs_memory_t *)mem, "[a%d:+$ ]%s(%u) = "PRI_INTPTR"\n",
203
96.5M
                   ialloc_trace_space(mem), client_name_string(cname),
204
96.5M
                   num_refs, (intptr_t)obj);
205
96.5M
        mem->cc->rcur[-1].o_size += num_refs * sizeof(ref);
206
96.5M
        end = (ref *) (mem->cc->rtop = mem->cc->cbot +=
207
96.5M
                       num_refs * sizeof(ref));
208
96.5M
        make_mark(end - 1);
209
96.5M
    } else {
210
        /*
211
         * Allocate a new run.  We have to distinguish 3 cases:
212
         *      - Same clump: cc unchanged, end == cc->cbot.
213
         *      - Large clump: cc unchanged, end != cc->cbot.
214
         *      - New clump: cc changed.
215
         */
216
12.8M
        clump_t *cc;
217
12.8M
        ref *end;
218
12.8M
        alloc_change_t *cp = 0;
219
12.8M
        int code = 0;
220
221
12.8M
        if ((gs_memory_t *)mem != mem->stable_memory) {
222
12.4M
            code = alloc_save_change_alloc(mem, "gs_alloc_ref_array", &cp);
223
12.4M
            if (code < 0)
224
0
                return code;
225
12.4M
        }
226
        /* The save state allocation above may have moved mem->cc */
227
12.8M
        cc = mem->cc;
228
12.8M
        obj = gs_alloc_struct_array((gs_memory_t *) mem, num_refs + 1,
229
12.8M
                                    ref, &st_refs, cname);
230
12.8M
        if (obj == 0) {
231
            /* We don't have to alloc_save_remove() because the change
232
               object hasn't been attached to the allocator yet.
233
             */
234
21
            gs_free_object((gs_memory_t *) mem, cp, "gs_alloc_ref_array");
235
21
            return_error(gs_error_VMerror);
236
21
        }
237
        /* Set the terminating ref now. */
238
12.8M
        end = (ref *) obj + num_refs;
239
12.8M
        make_mark(end);
240
        /* Set has_refs in the clump. */
241
12.8M
        if (mem->cc && (mem->cc != cc || mem->cc->cbot == (byte *) (end + 1))) {
242
            /* Ordinary clump. */
243
12.3M
            mem->cc->rcur = (obj_header_t *) obj;
244
12.3M
            mem->cc->rtop = (byte *) (end + 1);
245
12.3M
            mem->cc->has_refs = true;
246
12.3M
        } else {
247
            /* Large clump. */
248
            /* This happens only for very large arrays, */
249
            /* so it doesn't need to be cheap. */
250
450k
            clump_locator_t cl;
251
252
450k
            cl.memory = mem;
253
450k
            cl.cp = mem->root;
254
            /* clump_locate_ptr() should *never* fail here */
255
450k
            if (clump_locate_ptr(obj, &cl)) {
256
450k
                cl.cp->has_refs = true;
257
450k
            }
258
0
            else {
259
0
                gs_abort((gs_memory_t *) mem);
260
0
            }
261
450k
        }
262
12.8M
        if (cp) {
263
1.09M
            mem->changes = cp;
264
1.09M
            cp->where = (ref_packed *)obj;
265
1.09M
        }
266
12.8M
    }
267
3.27G
    for (i = 0; i < num_refs; i++) {
268
3.16G
        make_null(&(obj[i]));
269
3.16G
    }
270
109M
    make_array(parr, attrs | mem->space, num_refs, obj);
271
109M
    return 0;
272
109M
}
273
274
/* Resize an array of refs.  Currently this is only implemented */
275
/* for shrinking, not for growing. */
276
int
277
gs_resize_ref_array(gs_ref_memory_t * mem, ref * parr,
278
                    uint new_num_refs, client_name_t cname)
279
2.54k
{
280
2.54k
    uint old_num_refs = r_size(parr);
281
2.54k
    uint diff;
282
2.54k
    ref *obj = parr->value.refs;
283
284
2.54k
    if (new_num_refs > old_num_refs || !r_has_type(parr, t_array))
285
0
        return_error(gs_error_Fatal);
286
2.54k
    diff = old_num_refs - new_num_refs;
287
    /* Check for LIFO.  See gs_free_ref_array for more details. */
288
2.54k
    if (mem->cc && mem->cc->rtop == mem->cc->cbot &&
289
2.54k
        (byte *) (obj + (old_num_refs + 1)) == mem->cc->rtop
290
2.54k
        ) {
291
        /* Shorten the refs object. */
292
24
        ref *end = (ref *) (mem->cc->cbot = mem->cc->rtop -=
293
24
                            diff * sizeof(ref));
294
295
24
        if_debug4m('A', (const gs_memory_t *)mem, "[a%d:<$ ]%s(%u) "PRI_INTPTR"\n",
296
24
                   ialloc_trace_space(mem), client_name_string(cname), diff,
297
24
                   (intptr_t)obj);
298
24
        mem->cc->rcur[-1].o_size -= diff * sizeof(ref);
299
24
        make_mark(end - 1);
300
2.52k
    } else {
301
        /* Punt. */
302
2.52k
        if_debug4m('A', (const gs_memory_t *)mem, "[a%d:<$#]%s(%u) "PRI_INTPTR"\n",
303
2.52k
                   ialloc_trace_space(mem), client_name_string(cname), diff,
304
2.52k
                   (intptr_t)obj);
305
2.52k
        mem->lost.refs += diff * sizeof(ref);
306
2.52k
    }
307
2.54k
    r_set_size(parr, new_num_refs);
308
2.54k
    return 0;
309
2.54k
}
310
311
/* Deallocate an array of refs.  Only do this if LIFO, or if */
312
/* the array occupies an entire clump by itself. */
313
void
314
gs_free_ref_array(gs_ref_memory_t * mem, ref * parr, client_name_t cname)
315
4.71M
{
316
4.71M
    uint num_refs = r_size(parr);
317
4.71M
    ref *obj = parr->value.refs;
318
319
    /*
320
     * Compute the storage size of the array, and check for LIFO
321
     * freeing or a separate clump.  Note that the array might be packed;
322
     * for the moment, if it's anything but a t_array, punt.
323
     * The +1s are for the extra ref for the GC.
324
     */
325
4.71M
    if (!r_has_type(parr, t_array))
326
4.71M
        DO_NOTHING;    /* don't look for special cases */
327
1.89M
    else if (mem->cc && mem->cc->rtop == mem->cc->cbot &&
328
1.89M
             (byte *) (obj + (num_refs + 1)) == mem->cc->rtop
329
1.89M
        ) {
330
9
        if ((obj_header_t *) obj == mem->cc->rcur) {
331
            /* Deallocate the entire refs object. */
332
8
            if ((gs_memory_t *)mem != mem->stable_memory)
333
6
                alloc_save_remove(mem, (ref_packed *)obj, "gs_free_ref_array");
334
8
            gs_free_object((gs_memory_t *) mem, obj, cname);
335
8
            mem->cc->rcur = 0;
336
8
            mem->cc->rtop = 0;
337
8
        } else {
338
            /* Deallocate it at the end of the refs object. */
339
1
            if_debug4m('A', (const gs_memory_t *)mem, "[a%d:-$ ]%s(%u) "PRI_INTPTR"\n",
340
1
                       ialloc_trace_space(mem), client_name_string(cname),
341
1
                       num_refs, (intptr_t)obj);
342
1
            mem->cc->rcur[-1].o_size -= num_refs * sizeof(ref);
343
1
            mem->cc->rtop = mem->cc->cbot = (byte *) (obj + 1);
344
1
            make_mark(obj);
345
1
        }
346
9
        return;
347
1.89M
    } else if (num_refs >= (mem->large_size / ARCH_SIZEOF_REF - 1)) {
348
        /* See if this array has a clump all to itself. */
349
        /* We only make this check when freeing very large objects, */
350
        /* so it doesn't need to be cheap. */
351
318k
        clump_locator_t cl;
352
353
318k
        cl.memory = mem;
354
318k
        cl.cp = mem->root;
355
318k
        if (clump_locate_ptr(obj, &cl) &&
356
318k
            obj == (ref *) ((obj_header_t *) (cl.cp->cbase) + 1) &&
357
318k
            (byte *) (obj + (num_refs + 1)) == cl.cp->cend
358
318k
            ) {
359
            /* Free the clump. */
360
318k
            if_debug4m('a', (const gs_memory_t *)mem, "[a%d:-$L]%s(%u) "PRI_INTPTR"\n",
361
318k
                       ialloc_trace_space(mem), client_name_string(cname),
362
318k
                       num_refs, (intptr_t)obj);
363
318k
            if ((gs_memory_t *)mem != mem->stable_memory) {
364
1.76k
                alloc_save_remove(mem, (ref_packed *)obj, "gs_free_ref_array");
365
1.76k
            }
366
318k
            alloc_free_clump(cl.cp, mem);
367
318k
            return;
368
318k
        }
369
318k
    }
370
    /* Punt, but fill the array with nulls so that there won't be */
371
    /* dangling references to confuse the garbage collector. */
372
4.71M
    if_debug4m('A', (const gs_memory_t *)mem, "[a%d:-$#]%s(%u) "PRI_INTPTR"\n",
373
4.39M
               ialloc_trace_space(mem), client_name_string(cname), num_refs,
374
4.39M
               (intptr_t)obj);
375
4.39M
    {
376
4.39M
        uint size;
377
378
4.39M
        switch (r_type(parr)) {
379
2.81M
            case t_shortarray:
380
2.81M
                size = num_refs * sizeof(ref_packed);
381
2.81M
                break;
382
0
            case t_mixedarray:{
383
                /* We have to parse the array to compute the storage size. */
384
0
                uint i = 0;
385
0
                const ref_packed *p = parr->value.packed;
386
387
0
                for (; i < num_refs; ++i)
388
0
                    p = packed_next(p);
389
0
                size = (const byte *)p - (const byte *)parr->value.packed;
390
0
                break;
391
0
            }
392
1.57M
            case t_array:
393
1.57M
                size = num_refs * sizeof(ref);
394
1.57M
                break;
395
0
            default:
396
0
                if_debug3('A', "Unknown type 0x%x in free_ref_array(%u,"PRI_INTPTR")!",
397
0
                         r_type(parr), num_refs, (intptr_t)obj);
398
0
                return;
399
4.39M
        }
400
        /*
401
         * If there are any leftover packed elements, we don't
402
         * worry about them, since they can't be dangling references.
403
         */
404
4.39M
        refset_null_new(obj, size / sizeof(ref), 0);
405
4.39M
        mem->lost.refs += size;
406
4.39M
    }
407
4.39M
}
408
409
/* Allocate a string ref. */
410
int
411
gs_alloc_string_ref(gs_ref_memory_t * mem, ref * psref,
412
                    uint attrs, uint nbytes, client_name_t cname)
413
3.88k
{
414
3.88k
    byte *str = gs_alloc_string((gs_memory_t *) mem, nbytes, cname);
415
416
3.88k
    if (str == 0)
417
0
        return_error(gs_error_VMerror);
418
3.88k
    make_string(psref, attrs | mem->space, nbytes, str);
419
3.88k
    return 0;
420
3.88k
}