Coverage Report

Created: 2026-08-08 08:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/pdf/pdf_loop_detect.c
Line
Count
Source
1
/* Copyright (C) 2018-2026 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
/* code for handling circular references */
17
18
#include "pdf_int.h"
19
#include "pdf_loop_detect.h"
20
21
static int pdfi_init_loop_detector(pdf_context *ctx)
22
6.71M
{
23
6.71M
    if (ctx->loop_detection) {
24
0
        dbgmprintf(ctx->memory, "Attempt to initialise loop detector while one is in operation\n");
25
0
        return_error(gs_error_unknownerror);
26
0
    }
27
28
6.71M
    ctx->loop_detection = (uint64_t *)gs_alloc_bytes(ctx->memory, INITIAL_LOOP_TRACKER_SIZE * sizeof (uint64_t), "allocate loop tracking array");
29
6.71M
    if (ctx->loop_detection == NULL)
30
0
        return_error(gs_error_VMerror);
31
32
6.71M
    ctx->loop_detection_entries = 0;
33
6.71M
    ctx->loop_detection_size = INITIAL_LOOP_TRACKER_SIZE;
34
6.71M
    return 0;
35
6.71M
}
36
37
static int pdfi_free_loop_detector(pdf_context *ctx)
38
6.71M
{
39
6.71M
    if (ctx->loop_detection == NULL) {
40
0
        dbgmprintf(ctx->memory, "Attempt to free loop detector without initialising it\n");
41
0
        return 0;
42
0
    }
43
6.71M
    if (ctx->loop_detection != NULL)
44
6.71M
        gs_free_object(ctx->memory, ctx->loop_detection, "Free array for loop tracking");
45
6.71M
    ctx->loop_detection_entries = 0;
46
6.71M
    ctx->loop_detection_size = 0;
47
6.71M
    ctx->loop_detection = NULL;
48
49
6.71M
    return 0;
50
6.71M
}
51
52
static int pdfi_loop_detector_add_object_unchecked(pdf_context *ctx, uint64_t object)
53
21.1M
{
54
21.1M
    if (ctx->loop_detection == NULL) {
55
0
        dbgmprintf(ctx->memory, "Attempt to use loop detector without initialising it\n");
56
0
        return 0;
57
0
    }
58
59
21.1M
    if (ctx->loop_detection_entries == ctx->loop_detection_size) {
60
19
        uint64_t *New;
61
62
        /* 1000 is an arbitrary limit, it is intended to ensure we don't process files where objects are nested so deeply
63
         * that processing them leads to a C exec stack overflow. This allows objects to be nested 500 deep, with a mark
64
         * (to clear the object) for each one which really ought to be more than adequate.
65
         */
66
19
        if (ctx->loop_detection_entries > 1000) {
67
0
            return_error(gs_error_Fatal);
68
0
        }
69
70
19
        New = (uint64_t *)gs_alloc_bytes(ctx->memory, (size_t)(ctx->loop_detection_size + INITIAL_LOOP_TRACKER_SIZE) * (size_t)sizeof (uint64_t), "re-allocate loop tracking array");
71
19
        if (New == NULL) {
72
0
            return_error(gs_error_VMerror);
73
0
        }
74
19
        memcpy(New, ctx->loop_detection, ctx->loop_detection_entries * sizeof(uint64_t));
75
19
        gs_free_object(ctx->memory, ctx->loop_detection, "Free array for loop tracking");
76
19
        ctx->loop_detection_size += INITIAL_LOOP_TRACKER_SIZE;
77
19
        ctx->loop_detection = New;
78
19
    }
79
21.1M
    ctx->loop_detection[ctx->loop_detection_entries++] = object;
80
81
21.1M
    return 0;
82
21.1M
}
83
84
int pdfi_loop_detector_add_object(pdf_context *ctx, uint64_t object)
85
7.65M
{
86
7.65M
    if (object == 0) {
87
0
        dbgmprintf(ctx->memory, "Attempt to add an object number of 0 to the loop detection\n");
88
0
        return 0;
89
0
    }
90
7.65M
    return pdfi_loop_detector_add_object_unchecked(ctx, object);
91
7.65M
}
92
93
bool pdfi_loop_detector_check_object(pdf_context *ctx, uint64_t object)
94
5.65M
{
95
5.65M
    int i = 0;
96
97
5.65M
    if (ctx->loop_detection == NULL) {
98
2
        dbgmprintf(ctx->memory, "Attempt to use loop detector without initialising it\n");
99
2
        return 0;
100
2
    }
101
102
15.1M
    for (i=0;i < ctx->loop_detection_entries;i++) {
103
9.53M
        if (ctx->loop_detection[i] == object) {
104
8.32k
            char info_string[256];
105
8.32k
            gs_snprintf(info_string, sizeof(info_string), "Error! circular reference to object %"PRIu64" detected.\n", object);
106
8.32k
            pdfi_set_error(ctx, 0, NULL, E_PDF_CIRCULARREF, "pdfi_loop_detector_check_object", info_string);
107
8.32k
            return true;
108
8.32k
        }
109
9.53M
    }
110
5.64M
    return false;
111
5.65M
}
112
113
int pdfi_loop_detector_mark(pdf_context *ctx)
114
13.4M
{
115
13.4M
    int code = 0;
116
117
13.4M
    if (ctx->loop_detection == NULL) {
118
6.71M
        code = pdfi_init_loop_detector(ctx);
119
6.71M
        if (code < 0)
120
0
            return code;
121
6.71M
    }
122
123
13.4M
    return pdfi_loop_detector_add_object_unchecked(ctx, 0);
124
13.4M
}
125
126
int pdfi_loop_detector_cleartomark(pdf_context *ctx)
127
13.4M
{
128
13.4M
    if (ctx->loop_detection == NULL) {
129
0
        dbgmprintf(ctx->memory, "Attempt to use loop detector without initialising it\n");
130
0
        return 0;
131
0
    }
132
133
21.1M
    while (ctx->loop_detection[--ctx->loop_detection_entries] != 0) {
134
7.65M
        ctx->loop_detection[ctx->loop_detection_entries] = 0;
135
7.65M
    }
136
    /* FIXME - potential optimisation
137
     * Instead of freeing the loop detection array every tiome we are done with it
138
     * and then reallocating a new one next time we need one, we could just keep
139
     * the existing (empty) array. I suspect this would provide a small performance
140
     * improvement.
141
     */
142
13.4M
    if (ctx->loop_detection_entries == 0)
143
6.71M
        pdfi_free_loop_detector(ctx);
144
13.4M
    return 0;
145
13.4M
}