Coverage Report

Created: 2025-11-16 07:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/sjbig2.c
Line
Count
Source
1
/* Copyright (C) 2001-2023 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
/* jbig2decode filter implementation -- hooks in libjbig2dec */
18
19
#include "stdint_.h"
20
#include "memory_.h"
21
#include "stdio_.h" /* sprintf() for debug output */
22
23
#include "gserrors.h"
24
#include "gdebug.h"
25
#include "strimpl.h"
26
#include "sjbig2.h"
27
#include <limits.h>                     /* UINT_MAX */
28
29
/* stream implementation */
30
31
/* The /JBIG2Decode filter is a fairly memory intensive one to begin with,
32
   particularly in the initial jbig2dec library implementation. Furthermore,
33
   as a PDF 1.4 feature, we can assume a fairly large (host-level) machine.
34
   We therefore dispense with the normal Ghostscript memory discipline and
35
   let the library allocate all its resources on the heap. The pointers to
36
   these are not enumerated and so will not be garbage collected. We rely
37
   on our release() proc being called to deallocate state.
38
 */
39
40
private_st_jbig2decode_state(); /* creates a gc object for our state, defined in sjbig2.h */
41
42
/* error callback for jbig2 decoder */
43
static void
44
s_jbig2decode_error(void *callback_data, const char *msg, Jbig2Severity severity,
45
               uint32_t seg_idx)
46
16.3k
{
47
16.3k
    s_jbig2_callback_data_t *error_data = (s_jbig2_callback_data_t *)callback_data;
48
16.3k
    const char *type;
49
16.3k
    char segment[22];
50
51
16.3k
    switch (severity) {
52
8.88k
        case JBIG2_SEVERITY_DEBUG:
53
8.88k
            type = "DEBUG"; break;;
54
5.31k
        case JBIG2_SEVERITY_INFO:
55
5.31k
            type = "info"; break;;
56
1.71k
        case JBIG2_SEVERITY_WARNING:
57
1.71k
            type = "WARNING"; break;;
58
390
        case JBIG2_SEVERITY_FATAL:
59
390
            type = "FATAL ERROR decoding image:";
60
            /* pass the fatal error upstream if possible */
61
390
            if (error_data != NULL) error_data->error = gs_error_ioerror;
62
390
            break;;
63
0
        default: type = "unknown message:"; break;;
64
16.3k
    }
65
16.3k
    if (seg_idx == JBIG2_UNKNOWN_SEGMENT_NUMBER) segment[0] = '\0';
66
13.8k
    else gs_snprintf(segment, sizeof(segment), "(segment 0x%02x)", seg_idx);
67
68
16.3k
    if (error_data)
69
16.3k
    {
70
16.3k
        char *message;
71
16.3k
        int len;
72
73
16.3k
        len = snprintf(NULL, 0, "jbig2dec %s %s %s", type, msg, segment);
74
16.3k
        if (len < 0)
75
0
            return;
76
77
16.3k
        message = (char *)gs_alloc_bytes(error_data->memory, len + 1, "sjbig2decode_error(message)");
78
16.3k
        if (message == NULL)
79
0
            return;
80
81
16.3k
        len = snprintf(message, len + 1, "jbig2dec %s %s %s", type, msg, segment);
82
16.3k
        if (len < 0)
83
0
        {
84
0
            gs_free_object(error_data->memory, message, "s_jbig2decode_error(message)");
85
0
            return;
86
0
        }
87
88
16.3k
        if (error_data->last_message != NULL && strcmp(message, error_data->last_message)) {
89
14.8k
            if (error_data->repeats > 1)
90
41
            {
91
41
                if (error_data->severity == JBIG2_SEVERITY_FATAL || error_data->severity == JBIG2_SEVERITY_WARNING) {
92
38
                    dmlprintf1(error_data->memory, "jbig2dec last message repeated %ld times\n", error_data->repeats);
93
38
                } else {
94
3
                    if_debug1m('w', error_data->memory, "[w] jbig2dec last message repeated %ld times\n", error_data->repeats);
95
3
                }
96
41
            }
97
14.8k
            gs_free_object(error_data->memory, error_data->last_message, "s_jbig2decode_error(last_message)");
98
14.8k
            if (severity == JBIG2_SEVERITY_FATAL || severity == JBIG2_SEVERITY_WARNING) {
99
1.89k
                dmlprintf1(error_data->memory, "%s\n", message);
100
13.0k
            } else {
101
13.0k
                if_debug1m('w', error_data->memory, "[w] %s\n", message);
102
13.0k
            }
103
14.8k
            error_data->last_message = message;
104
14.8k
            error_data->severity = severity;
105
14.8k
            error_data->type = type;
106
14.8k
            error_data->repeats = 0;
107
14.8k
        }
108
1.40k
        else if (error_data->last_message != NULL) {
109
211
            error_data->repeats++;
110
211
            if (error_data->repeats % 1000000 == 0)
111
0
            {
112
0
                if (error_data->severity == JBIG2_SEVERITY_FATAL || error_data->severity == JBIG2_SEVERITY_WARNING) {
113
0
                    dmlprintf1(error_data->memory, "jbig2dec last message repeated %ld times so far\n", error_data->repeats);
114
0
                } else {
115
0
                    if_debug1m('w', error_data->memory, "[w] jbig2dec last message repeated %ld times so far\n", error_data->repeats);
116
0
                }
117
0
            }
118
211
            gs_free_object(error_data->memory, message, "s_jbig2decode_error(message)");
119
211
        }
120
1.19k
        else if (error_data->last_message == NULL) {
121
1.19k
            if (severity == JBIG2_SEVERITY_FATAL || severity == JBIG2_SEVERITY_WARNING) {
122
13
                dmlprintf1(error_data->memory, "%s\n", message);
123
1.17k
            } else {
124
1.17k
                if_debug1m('w', error_data->memory, "[w] %s\n", message);
125
1.17k
            }
126
1.19k
            error_data->last_message = message;
127
1.19k
            error_data->severity = severity;
128
1.19k
            error_data->type = type;
129
1.19k
            error_data->repeats = 0;
130
1.19k
        }
131
16.3k
    }
132
0
    else
133
0
    {
134
/*
135
        FIXME s_jbig2_callback_data_t should be updated so that jbig2_ctx_new is not called
136
        with a NULL argument (see jbig2.h) and we never reach here with a NULL state
137
*/
138
0
        if (severity == JBIG2_SEVERITY_FATAL) {
139
0
            dlprintf3("jbig2dec %s %s %s\n", type, msg, segment);
140
0
        } else {
141
0
            if_debug3('w', "[w] jbig2dec %s %s %s\n", type, msg, segment);
142
0
        }
143
0
    }
144
16.3k
}
145
146
static void
147
s_jbig2decode_flush_errors(void *callback_data)
148
1.19k
{
149
1.19k
    s_jbig2_callback_data_t *error_data = (s_jbig2_callback_data_t *)callback_data;
150
151
1.19k
    if (error_data == NULL)
152
0
        return;
153
154
1.19k
    if (error_data->last_message != NULL) {
155
1.19k
        if (error_data->repeats > 1)
156
0
        {
157
0
            if (error_data->severity == JBIG2_SEVERITY_FATAL || error_data->severity == JBIG2_SEVERITY_WARNING) {
158
0
                dmlprintf1(error_data->memory, "jbig2dec last message repeated %ld times\n", error_data->repeats);
159
0
            } else {
160
0
                if_debug1m('w', error_data->memory, "[w] jbig2dec last message repeated %ld times\n", error_data->repeats);
161
0
            }
162
0
        }
163
1.19k
        gs_free_object(error_data->memory, error_data->last_message, "s_jbig2decode_error(last_message)");
164
1.19k
        error_data->last_message = NULL;
165
1.19k
        error_data->repeats = 0;
166
1.19k
    }
167
1.19k
}
168
169
/* invert the bits in a buffer */
170
/* jbig2 and postscript have different senses of what pixel
171
   value is black, so we must invert the image */
172
static void
173
s_jbig2decode_invert_buffer(unsigned char *buf, size_t length)
174
365k
{
175
365k
    size_t i;
176
177
749M
    for (i = 0; i < length; i++)
178
748M
        *buf++ ^= 0xFF;
179
365k
}
180
181
typedef struct {
182
        Jbig2Allocator allocator;
183
        gs_memory_t *mem;
184
} s_jbig2decode_allocator_t;
185
186
static void *s_jbig2decode_alloc(Jbig2Allocator *_allocator, size_t size)
187
4.89M
{
188
4.89M
        s_jbig2decode_allocator_t *allocator = (s_jbig2decode_allocator_t *) _allocator;
189
4.89M
        if (size > UINT_MAX)
190
2
            return NULL;
191
4.89M
        return gs_alloc_bytes(allocator->mem, size, "s_jbig2decode_alloc");
192
4.89M
}
193
194
static void s_jbig2decode_free(Jbig2Allocator *_allocator, void *p)
195
4.89M
{
196
4.89M
        s_jbig2decode_allocator_t *allocator = (s_jbig2decode_allocator_t *) _allocator;
197
4.89M
        gs_free_object(allocator->mem, p, "s_jbig2decode_free");
198
4.89M
}
199
200
static void *s_jbig2decode_realloc(Jbig2Allocator *_allocator, void *p, size_t size)
201
0
{
202
0
        s_jbig2decode_allocator_t *allocator = (s_jbig2decode_allocator_t *) _allocator;
203
0
        if (size > UINT_MAX)
204
0
            return NULL;
205
0
        return gs_resize_object(allocator->mem, p, size, "s_jbig2decode_realloc");
206
0
}
207
208
/* parse a globals stream packed into a gs_bytestring for us by the postscript
209
   layer and stuff the resulting context into a pointer for use in later decoding */
210
int
211
s_jbig2decode_make_global_data(gs_memory_t *mem, byte *data, uint length, void **result)
212
0
{
213
0
    Jbig2Ctx *ctx = NULL;
214
0
    int code;
215
0
    s_jbig2decode_allocator_t *allocator;
216
217
    /* the cvision encoder likes to include empty global streams */
218
0
    if (length == 0) {
219
0
        if_debug0('w', "[w] ignoring zero-length jbig2 global stream.\n");
220
0
        *result = NULL;
221
0
        return 0;
222
0
    }
223
224
0
    allocator = (s_jbig2decode_allocator_t *) gs_alloc_bytes(mem,
225
0
            sizeof (s_jbig2decode_allocator_t), "s_jbig2_make_global_data");
226
0
    if (allocator == NULL) {
227
0
        *result = NULL;
228
0
        return_error(gs_error_VMerror);
229
0
    }
230
231
0
    allocator->allocator.alloc = s_jbig2decode_alloc;
232
0
    allocator->allocator.free = s_jbig2decode_free;
233
0
    allocator->allocator.realloc = s_jbig2decode_realloc;
234
0
    allocator->mem = mem;
235
236
    /* allocate a context with which to parse our global segments */
237
0
    ctx = jbig2_ctx_new((Jbig2Allocator *) allocator, JBIG2_OPTIONS_EMBEDDED,
238
0
                            NULL, s_jbig2decode_error, NULL);
239
0
    if (ctx == NULL) {
240
0
        gs_free_object(mem, allocator, "s_jbig2_make_global_data");
241
0
        return_error(gs_error_VMerror);
242
0
    }
243
244
    /* parse the global bitstream */
245
0
    code = jbig2_data_in(ctx, data, length);
246
0
    if (code) {
247
        /* error parsing the global stream */
248
0
        allocator = (s_jbig2decode_allocator_t *) jbig2_ctx_free(ctx);
249
0
        gs_free_object(allocator->mem, allocator, "s_jbig2_make_global_data");
250
0
        *result = NULL;
251
0
        return_error(gs_error_ioerror);
252
0
    }
253
254
    /* canonize and store our global state */
255
0
    *result = jbig2_make_global_ctx(ctx);
256
257
0
    return 0; /* todo: check for allocation failure */
258
0
}
259
260
/* release a global ctx pointer */
261
void
262
s_jbig2decode_free_global_data(void *data)
263
0
{
264
0
    Jbig2GlobalCtx *global_ctx = (Jbig2GlobalCtx*)data;
265
0
    s_jbig2decode_allocator_t *allocator;
266
267
0
    allocator = (s_jbig2decode_allocator_t *) jbig2_global_ctx_free(global_ctx);
268
269
0
    gs_free_object(allocator->mem, allocator, "s_jbig2decode_free_global_data");
270
0
}
271
272
/* store a global ctx pointer in our state structure.
273
 * If "gd" is NULL, then this library must free the global context.
274
 * If not-NULL, then it will be memory managed by caller, for example,
275
 * garbage collected in the case of the PS interpreter.
276
 * Currently gpdf will use NULL, and the PDF implemented in the gs interpreter would use
277
 * the garbage collection.
278
 */
279
int
280
s_jbig2decode_set_global_data(stream_state *ss, s_jbig2_global_data_t *gd, void *global_ctx)
281
1.19k
{
282
1.19k
    stream_jbig2decode_state *state = (stream_jbig2decode_state*)ss;
283
1.19k
    state->global_struct = gd;
284
1.19k
    state->global_ctx = global_ctx;
285
1.19k
    return 0;
286
1.19k
}
287
288
/* initialize the steam.
289
   this involves allocating the context structures, and
290
   initializing the global context from the /JBIG2Globals object reference
291
 */
292
static int
293
s_jbig2decode_init(stream_state * ss)
294
1.19k
{
295
1.19k
    stream_jbig2decode_state *const state = (stream_jbig2decode_state *) ss;
296
1.19k
    Jbig2GlobalCtx *global_ctx = state->global_ctx; /* may be NULL */
297
1.19k
    int code = 0;
298
1.19k
    s_jbig2decode_allocator_t *allocator = NULL;
299
300
1.19k
    state->callback_data = (s_jbig2_callback_data_t *)gs_alloc_bytes(
301
1.19k
                                                ss->memory->non_gc_memory,
302
1.19k
                                                sizeof(s_jbig2_callback_data_t),
303
1.19k
            "s_jbig2decode_init(callback_data)");
304
1.19k
    if (state->callback_data) {
305
1.19k
        state->callback_data->memory = ss->memory->non_gc_memory;
306
1.19k
        state->callback_data->error = 0;
307
1.19k
        state->callback_data->last_message = NULL;
308
1.19k
        state->callback_data->repeats = 0;
309
310
1.19k
        allocator = (s_jbig2decode_allocator_t *) gs_alloc_bytes(ss->memory->non_gc_memory, sizeof (s_jbig2decode_allocator_t), "s_jbig2decode_init(allocator)");
311
1.19k
        if (allocator == NULL) {
312
0
                s_jbig2decode_error(state->callback_data, "failed to allocate custom jbig2dec allocator", JBIG2_SEVERITY_FATAL, -1);
313
0
        }
314
1.19k
        else {
315
1.19k
                allocator->allocator.alloc = s_jbig2decode_alloc;
316
1.19k
                allocator->allocator.free = s_jbig2decode_free;
317
1.19k
                allocator->allocator.realloc = s_jbig2decode_realloc;
318
1.19k
                allocator->mem = ss->memory->non_gc_memory;
319
320
                /* initialize the decoder with the parsed global context if any */
321
1.19k
                state->decode_ctx = jbig2_ctx_new((Jbig2Allocator *) allocator, JBIG2_OPTIONS_EMBEDDED,
322
1.19k
                             global_ctx, s_jbig2decode_error, state->callback_data);
323
324
1.19k
                if (state->decode_ctx == NULL) {
325
0
                        gs_free_object(allocator->mem, allocator, "s_jbig2decode_release");
326
0
                }
327
328
1.19k
        }
329
330
1.19k
        code = state->callback_data->error;
331
1.19k
    }
332
0
    else {
333
0
        code = gs_error_VMerror;
334
0
    }
335
1.19k
    state->image = 0;
336
337
338
1.19k
    return_error (code);
339
1.19k
}
340
341
/* process a section of the input and return any decoded data.
342
   see strimpl.h for return codes.
343
 */
344
static int
345
s_jbig2decode_process(stream_state * ss, stream_cursor_read * pr,
346
                  stream_cursor_write * pw, bool last)
347
374k
{
348
374k
    stream_jbig2decode_state *const state = (stream_jbig2decode_state *) ss;
349
374k
    Jbig2Image *image = state->image;
350
374k
    size_t in_size = pr->limit - pr->ptr;
351
374k
    size_t out_size = pw->limit - pw->ptr;
352
374k
    int status = 0;
353
354
    /* there will only be a single page image,
355
       so pass all data in before looking for any output.
356
       note that the gs stream library expects offset-by-one
357
       indexing of the buffers, while jbig2dec uses normal 0 indexes */
358
374k
    if (in_size > 0) {
359
        /* pass all available input to the decoder */
360
8.85k
        jbig2_data_in(state->decode_ctx, pr->ptr + 1, in_size);
361
8.85k
        pr->ptr += in_size;
362
        /* simulate end-of-page segment */
363
8.85k
        if (last == 1) {
364
1.17k
            jbig2_complete_page(state->decode_ctx);
365
1.17k
        }
366
        /* handle fatal decoding errors reported through our callback */
367
8.85k
        if (state->callback_data->error) return state->callback_data->error;
368
365k
    } else {
369
        /* Ran out of input, try and terminate cleanly */
370
365k
        if (last == 1) {
371
365k
            jbig2_complete_page(state->decode_ctx);
372
365k
        }
373
365k
    }
374
374k
    if (out_size > 0) {
375
374k
        if (image == NULL) {
376
            /* see if a page image in available */
377
9.36k
            image = jbig2_page_out(state->decode_ctx);
378
9.36k
            if (image != NULL) {
379
819
                state->image = image;
380
819
                state->offset = 0;
381
819
            }
382
9.36k
        }
383
374k
        if (image != NULL) {
384
            /* copy data out of the decoded image, if any */
385
365k
            size_t image_size = (size_t)image->height*image->stride;
386
365k
            size_t usable = min(image_size - state->offset, out_size);
387
365k
            memcpy(pw->ptr + 1, image->data + state->offset, usable);
388
365k
            s_jbig2decode_invert_buffer(pw->ptr + 1, usable);
389
365k
            state->offset += usable;
390
365k
            pw->ptr += usable;
391
365k
            status = (state->offset < image_size) ? 1 : 0;
392
365k
        }
393
374k
    }
394
395
374k
    return status;
396
374k
}
397
398
/* stream release.
399
   free all our decoder state.
400
 */
401
static void
402
s_jbig2decode_release(stream_state *ss)
403
2.39k
{
404
2.39k
    stream_jbig2decode_state *const state = (stream_jbig2decode_state *) ss;
405
406
2.39k
    if (state->decode_ctx) {
407
1.19k
        s_jbig2decode_allocator_t *allocator = NULL;
408
409
1.19k
        if (state->image) jbig2_release_page(state->decode_ctx, state->image);
410
1.19k
  state->image = NULL;
411
1.19k
        s_jbig2decode_flush_errors(state->callback_data);
412
1.19k
        allocator = (s_jbig2decode_allocator_t *) jbig2_ctx_free(state->decode_ctx);
413
1.19k
  state->decode_ctx = NULL;
414
415
1.19k
        gs_free_object(allocator->mem, allocator, "s_jbig2decode_release");
416
1.19k
    }
417
2.39k
    if (state->callback_data) {
418
1.19k
        gs_memory_t *mem = state->callback_data->memory;
419
1.19k
        gs_free_object(state->callback_data->memory, state->callback_data->last_message, "s_jbig2decode_release(message)");
420
1.19k
        gs_free_object(mem, state->callback_data, "s_jbig2decode_release(callback_data)");
421
1.19k
  state->callback_data = NULL;
422
1.19k
    }
423
2.39k
    if (state->global_struct != NULL) {
424
        /* the interpreter calls jbig2decode_free_global_data() separately */
425
2.39k
    } else {
426
        /* We are responsible for freeing global context */
427
2.39k
        if (state->global_ctx) {
428
0
            s_jbig2decode_free_global_data(state->global_ctx);
429
0
            state->global_ctx = NULL;
430
0
        }
431
2.39k
    }
432
2.39k
}
433
434
void
435
s_jbig2decode_finalize(const gs_memory_t *cmem, void *vptr)
436
1.19k
{
437
1.19k
    (void)cmem;
438
439
1.19k
    s_jbig2decode_release((stream_state *)vptr);
440
1.19k
}
441
442
/* set stream defaults.
443
   this hook exists to avoid confusing the gc with bogus
444
   pointers. we use it similarly just to NULL all the pointers.
445
   (could just be done in _init?)
446
 */
447
static void
448
s_jbig2decode_set_defaults(stream_state *ss)
449
1.19k
{
450
1.19k
    stream_jbig2decode_state *const state = (stream_jbig2decode_state *) ss;
451
452
    /* state->global_ctx is not owned by us */
453
1.19k
    state->global_struct = NULL;
454
1.19k
    state->global_ctx = NULL;
455
1.19k
    state->decode_ctx = NULL;
456
1.19k
    state->image = NULL;
457
1.19k
    state->offset = 0;
458
    state->callback_data = NULL;
459
1.19k
}
460
461
/* stream template */
462
const stream_template s_jbig2decode_template = {
463
    &st_jbig2decode_state,
464
    s_jbig2decode_init,
465
    s_jbig2decode_process,
466
    1, 1, /* min in and out buffer sizes we can handle --should be ~32k,64k for efficiency? */
467
    s_jbig2decode_release,
468
    s_jbig2decode_set_defaults
469
};