/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 | 7.46M | { |
23 | 7.46M | 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 | 7.46M | ctx->loop_detection = (uint64_t *)gs_alloc_bytes(ctx->memory, INITIAL_LOOP_TRACKER_SIZE * sizeof (uint64_t), "allocate loop tracking array"); |
29 | 7.46M | if (ctx->loop_detection == NULL) |
30 | 0 | return_error(gs_error_VMerror); |
31 | | |
32 | 7.46M | ctx->loop_detection_entries = 0; |
33 | 7.46M | ctx->loop_detection_size = INITIAL_LOOP_TRACKER_SIZE; |
34 | 7.46M | return 0; |
35 | 7.46M | } |
36 | | |
37 | | static int pdfi_free_loop_detector(pdf_context *ctx) |
38 | 7.46M | { |
39 | 7.46M | 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 | 7.46M | if (ctx->loop_detection != NULL) |
44 | 7.46M | gs_free_object(ctx->memory, ctx->loop_detection, "Free array for loop tracking"); |
45 | 7.46M | ctx->loop_detection_entries = 0; |
46 | 7.46M | ctx->loop_detection_size = 0; |
47 | 7.46M | ctx->loop_detection = NULL; |
48 | | |
49 | 7.46M | return 0; |
50 | 7.46M | } |
51 | | |
52 | | static int pdfi_loop_detector_add_object_unchecked(pdf_context *ctx, uint64_t object) |
53 | 23.4M | { |
54 | 23.4M | 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 | 23.4M | if (ctx->loop_detection_entries == ctx->loop_detection_size) { |
60 | 20 | 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 | 20 | if (ctx->loop_detection_entries > 1000) { |
67 | 0 | return_error(gs_error_Fatal); |
68 | 0 | } |
69 | | |
70 | 20 | 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 | 20 | if (New == NULL) { |
72 | 0 | return_error(gs_error_VMerror); |
73 | 0 | } |
74 | 20 | memcpy(New, ctx->loop_detection, ctx->loop_detection_entries * sizeof(uint64_t)); |
75 | 20 | gs_free_object(ctx->memory, ctx->loop_detection, "Free array for loop tracking"); |
76 | 20 | ctx->loop_detection_size += INITIAL_LOOP_TRACKER_SIZE; |
77 | 20 | ctx->loop_detection = New; |
78 | 20 | } |
79 | 23.4M | ctx->loop_detection[ctx->loop_detection_entries++] = object; |
80 | | |
81 | 23.4M | return 0; |
82 | 23.4M | } |
83 | | |
84 | | int pdfi_loop_detector_add_object(pdf_context *ctx, uint64_t object) |
85 | 8.47M | { |
86 | 8.47M | 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 | 8.47M | return pdfi_loop_detector_add_object_unchecked(ctx, object); |
91 | 8.47M | } |
92 | | |
93 | | bool pdfi_loop_detector_check_object(pdf_context *ctx, uint64_t object) |
94 | 6.06M | { |
95 | 6.06M | int i = 0; |
96 | | |
97 | 6.06M | if (ctx->loop_detection == NULL) { |
98 | 3 | dbgmprintf(ctx->memory, "Attempt to use loop detector without initialising it\n"); |
99 | 3 | return 0; |
100 | 3 | } |
101 | | |
102 | 16.2M | for (i=0;i < ctx->loop_detection_entries;i++) { |
103 | 10.2M | if (ctx->loop_detection[i] == object) { |
104 | 9.38k | char info_string[256]; |
105 | 9.38k | gs_snprintf(info_string, sizeof(info_string), "Error! circular reference to object %"PRIu64" detected.\n", object); |
106 | 9.38k | pdfi_set_error(ctx, 0, NULL, E_PDF_CIRCULARREF, "pdfi_loop_detector_check_object", info_string); |
107 | 9.38k | return true; |
108 | 9.38k | } |
109 | 10.2M | } |
110 | 6.05M | return false; |
111 | 6.06M | } |
112 | | |
113 | | int pdfi_loop_detector_mark(pdf_context *ctx) |
114 | 15.0M | { |
115 | 15.0M | int code = 0; |
116 | | |
117 | 15.0M | if (ctx->loop_detection == NULL) { |
118 | 7.46M | code = pdfi_init_loop_detector(ctx); |
119 | 7.46M | if (code < 0) |
120 | 0 | return code; |
121 | 7.46M | } |
122 | | |
123 | 15.0M | return pdfi_loop_detector_add_object_unchecked(ctx, 0); |
124 | 15.0M | } |
125 | | |
126 | | int pdfi_loop_detector_cleartomark(pdf_context *ctx) |
127 | 15.0M | { |
128 | 15.0M | 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 | 23.4M | while (ctx->loop_detection[--ctx->loop_detection_entries] != 0) { |
134 | 8.47M | ctx->loop_detection[ctx->loop_detection_entries] = 0; |
135 | 8.47M | } |
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 | 15.0M | if (ctx->loop_detection_entries == 0) |
143 | 7.46M | pdfi_free_loop_detector(ctx); |
144 | 15.0M | return 0; |
145 | 15.0M | } |