/src/ghostpdl/pdf/pdf_loop_detect.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2018-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 | | /* 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 | 8.71M | { |
23 | 8.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 | 8.71M | ctx->loop_detection = (uint64_t *)gs_alloc_bytes(ctx->memory, INITIAL_LOOP_TRACKER_SIZE * sizeof (uint64_t), "allocate loop tracking array"); |
29 | 8.71M | if (ctx->loop_detection == NULL) |
30 | 0 | return_error(gs_error_VMerror); |
31 | | |
32 | 8.71M | ctx->loop_detection_entries = 0; |
33 | 8.71M | ctx->loop_detection_size = INITIAL_LOOP_TRACKER_SIZE; |
34 | 8.71M | return 0; |
35 | 8.71M | } |
36 | | |
37 | | static int pdfi_free_loop_detector(pdf_context *ctx) |
38 | 8.71M | { |
39 | 8.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 | 8.71M | if (ctx->loop_detection != NULL) |
44 | 8.71M | gs_free_object(ctx->memory, ctx->loop_detection, "Free array for loop tracking"); |
45 | 8.71M | ctx->loop_detection_entries = 0; |
46 | 8.71M | ctx->loop_detection_size = 0; |
47 | 8.71M | ctx->loop_detection = NULL; |
48 | | |
49 | 8.71M | return 0; |
50 | 8.71M | } |
51 | | |
52 | | static int pdfi_loop_detector_add_object_unchecked(pdf_context *ctx, uint64_t object) |
53 | 27.2M | { |
54 | 27.2M | 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 | 27.2M | if (ctx->loop_detection_entries == ctx->loop_detection_size) { |
60 | 18 | uint64_t *New; |
61 | | |
62 | 18 | New = (uint64_t *)gs_alloc_bytes(ctx->memory, (ctx->loop_detection_size + INITIAL_LOOP_TRACKER_SIZE) * sizeof (uint64_t), "re-allocate loop tracking array"); |
63 | 18 | if (New == NULL) { |
64 | 0 | return_error(gs_error_VMerror); |
65 | 0 | } |
66 | 18 | memcpy(New, ctx->loop_detection, ctx->loop_detection_entries * sizeof(uint64_t)); |
67 | 18 | gs_free_object(ctx->memory, ctx->loop_detection, "Free array for loop tracking"); |
68 | 18 | ctx->loop_detection_size += INITIAL_LOOP_TRACKER_SIZE; |
69 | 18 | ctx->loop_detection = New; |
70 | 18 | } |
71 | 27.2M | ctx->loop_detection[ctx->loop_detection_entries++] = object; |
72 | 27.2M | return 0; |
73 | 27.2M | } |
74 | | |
75 | | int pdfi_loop_detector_add_object(pdf_context *ctx, uint64_t object) |
76 | 10.3M | { |
77 | 10.3M | if (object == 0) { |
78 | 0 | dbgmprintf(ctx->memory, "Attempt to add an object number of 0 to the loop detection\n"); |
79 | 0 | return 0; |
80 | 0 | } |
81 | 10.3M | return pdfi_loop_detector_add_object_unchecked(ctx, object); |
82 | 10.3M | } |
83 | | |
84 | | bool pdfi_loop_detector_check_object(pdf_context *ctx, uint64_t object) |
85 | 8.27M | { |
86 | 8.27M | int i = 0; |
87 | | |
88 | 8.27M | if (ctx->loop_detection == NULL) { |
89 | 8 | dbgmprintf(ctx->memory, "Attempt to use loop detector without initialising it\n"); |
90 | 8 | return 0; |
91 | 8 | } |
92 | | |
93 | 22.8M | for (i=0;i < ctx->loop_detection_entries;i++) { |
94 | 14.6M | if (ctx->loop_detection[i] == object) { |
95 | 37.3k | char info_string[256]; |
96 | 37.3k | gs_snprintf(info_string, sizeof(info_string), "Error! circular reference to object %"PRIu64" detected.\n", object); |
97 | 37.3k | pdfi_set_error(ctx, 0, NULL, E_PDF_CIRCULARREF, "pdfi_loop_detector_check_object", info_string); |
98 | 37.3k | return true; |
99 | 37.3k | } |
100 | 14.6M | } |
101 | 8.23M | return false; |
102 | 8.27M | } |
103 | | |
104 | | int pdfi_loop_detector_mark(pdf_context *ctx) |
105 | 16.8M | { |
106 | 16.8M | int code = 0; |
107 | | |
108 | 16.8M | if (ctx->loop_detection == NULL) { |
109 | 8.71M | code = pdfi_init_loop_detector(ctx); |
110 | 8.71M | if (code < 0) |
111 | 0 | return code; |
112 | 8.71M | } |
113 | | |
114 | 16.8M | return pdfi_loop_detector_add_object_unchecked(ctx, 0); |
115 | 16.8M | } |
116 | | |
117 | | int pdfi_loop_detector_cleartomark(pdf_context *ctx) |
118 | 16.8M | { |
119 | 16.8M | if (ctx->loop_detection == NULL) { |
120 | 0 | dbgmprintf(ctx->memory, "Attempt to use loop detector without initialising it\n"); |
121 | 0 | return 0; |
122 | 0 | } |
123 | | |
124 | 27.2M | while (ctx->loop_detection[--ctx->loop_detection_entries] != 0) { |
125 | 10.3M | ctx->loop_detection[ctx->loop_detection_entries] = 0; |
126 | 10.3M | } |
127 | | /* FIXME - potential optimisation |
128 | | * Instead of freeing the loop detection array every tiome we are done with it |
129 | | * and then reallocating a new one next time we need one, we could just keep |
130 | | * the existing (empty) array. I suspect this would provide a small performance |
131 | | * improvement. |
132 | | */ |
133 | 16.8M | if (ctx->loop_detection_entries == 0) |
134 | 8.71M | pdfi_free_loop_detector(ctx); |
135 | 16.8M | return 0; |
136 | 16.8M | } |