diff options
Diffstat (limited to 'pdf/pdf_xref.c')
-rw-r--r-- | pdf/pdf_xref.c | 156 |
1 files changed, 101 insertions, 55 deletions
diff --git a/pdf/pdf_xref.c b/pdf/pdf_xref.c index 27c1501e..7e611130 100644 --- a/pdf/pdf_xref.c +++ b/pdf/pdf_xref.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Artifex Software, Inc. +/* Copyright (C) 2018-2022 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or @@ -175,8 +175,10 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd code = pdfi_dict_get_int(ctx, sdict, "Size", &size); if (code < 0) return code; + if (size < 1) + return 0; - if (size < 0) + if (size < 0 || size > floor((double)ARCH_MAX_SIZE_T / (double)sizeof(xref_entry))) return_error(gs_error_rangecheck); /* If this is the first xref stream then allocate the xref table and store the trailer */ @@ -197,7 +199,7 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd ctx->xref_table->type = PDF_XREF_TABLE; ctx->xref_table->xref_size = size; #if REFCNT_DEBUG - ctx->xref_table->UID = ctx->UID++; + ctx->xref_table->UID = ctx->ref_UID++; dmprintf1(ctx->memory, "Allocated xref table with UID %"PRIi64"\n", ctx->xref_table->UID); #endif pdfi_countup(ctx->xref_table); @@ -205,6 +207,9 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd ctx->Trailer = sdict; pdfi_countup(sdict); } else { + if (size > ctx->xref_table->xref_size) + return_error(gs_error_rangecheck); + code = pdfi_merge_dicts(ctx, ctx->Trailer, sdict); if (code < 0) { if (code == gs_error_VMerror || ctx->args.pdfstoponerror) @@ -321,7 +326,7 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd for (i=0;i < pdfi_array_size(a);i+=2){ code = pdfi_array_get_int(ctx, a, (uint64_t)i, &start); - if (code < 0) { + if (code < 0 || start < 0) { pdfi_countdown(a); pdfi_close_file(ctx, XRefStrm); pdfi_countdown(ctx->xref_table); @@ -332,7 +337,6 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd code = pdfi_array_get_int(ctx, a, (uint64_t)i+1, &end); if (code < 0) { pdfi_countdown(a); - pdfi_countdown(start); pdfi_close_file(ctx, XRefStrm); pdfi_countdown(ctx->xref_table); ctx->xref_table = NULL; @@ -390,6 +394,8 @@ static int pdfi_process_xref_stream(pdf_context *ctx, pdf_stream *stream_obj, pd code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); if (code < 0) return code; + if (code == 0) + return_error(gs_error_syntaxerror); if (((pdf_obj *)ctx->stack_top[-1])->type == PDF_KEYWORD && ((pdf_keyword *)ctx->stack_top[-1])->key == TOKEN_XREF) { /* Read old-style xref table */ @@ -412,7 +418,7 @@ static int pdfi_read_xref_stream_dict(pdf_context *ctx, pdf_c_stream *s) /* Its an integer, lets try for index gen obj as a XRef stream */ code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); - if (code < 0) + if (code <= 0) return(pdfi_repair_file(ctx)); if (((pdf_obj *)ctx->stack_top[-1])->type != PDF_INT) { @@ -426,6 +432,10 @@ static int pdfi_read_xref_stream_dict(pdf_context *ctx, pdf_c_stream *s) pdfi_pop(ctx, 1); return code; } + if (code == 0) { + pdfi_pop(ctx, 1); + return_error(gs_error_syntaxerror); + } if (((pdf_obj *)ctx->stack_top[-1])->type != PDF_KEYWORD) { /* Second element is not an integer, not a valid xref */ @@ -449,10 +459,10 @@ static int pdfi_read_xref_stream_dict(pdf_context *ctx, pdf_c_stream *s) do { code = pdfi_read_token(ctx, ctx->main_stream, obj_num, gen_num); - if (code < 0) + if (code <= 0) return pdfi_repair_file(ctx); - if (((pdf_obj *)ctx->stack_top[-1])->type == PDF_KEYWORD) { + if (pdfi_count_stack(ctx) >= 2 && ((pdf_obj *)ctx->stack_top[-1])->type == PDF_KEYWORD) { keyword = (pdf_keyword *)ctx->stack_top[-1]; if (keyword->key == TOKEN_STREAM) { pdf_dict *dict; @@ -491,7 +501,7 @@ static int pdfi_read_xref_stream_dict(pdf_context *ctx, pdf_c_stream *s) /* TODO: Not positive this will actually have a length -- just use 0 */ char extra_info[gp_file_name_sizeof]; - gs_sprintf(extra_info, "Xref Stream object %u missing mandatory keyword /Length\n", obj_num); + gs_snprintf(extra_info, sizeof(extra_info), "Xref Stream object %u missing mandatory keyword /Length\n", obj_num); pdfi_set_error(ctx, 0, NULL, E_PDF_BADSTREAM, "pdfi_read_xref_stream_dict", extra_info); code = 0; Length = 0; @@ -525,36 +535,49 @@ static int pdfi_read_xref_stream_dict(pdf_context *ctx, pdf_c_stream *s) static int skip_to_digit(pdf_context *ctx, pdf_c_stream *s, unsigned int limit) { - byte c; - int bytes, read = 0; + int c, read = 0; do { - bytes = pdfi_read_bytes(ctx, &c, 1, 1, s); - if (bytes == 0) + c = pdfi_read_byte(ctx, s); + if (c < 0) return_error(gs_error_ioerror); - if (c >= 0x30 && c <= 0x39) { - pdfi_unread(ctx, s, &c, 1); - break; + if (c >= '0' && c <= '9') { + pdfi_unread_byte(ctx, s, (byte)c); + return read; } - read += bytes; - }while (read < limit); + read++; + } while (read < limit); + return read; } -static int read_digits(pdf_context *ctx, pdf_c_stream *s, byte *Buffer, unsigned int limit) +static int read_digits(pdf_context *ctx, pdf_c_stream *s, byte *Buffer, int limit) { - int bytes, read = 0; + int c, read = 0; + + /* Since the "limit" is a value calculated by the caller, + it's easier to check it in one place (here) than before + every call. + */ + if (limit <= 0) + return_error(gs_error_syntaxerror); + + /* We assume that Buffer always has limit+1 bytes available, so we can + * safely terminate it. */ do { - bytes = pdfi_read_bytes(ctx, &Buffer[read], 1, 1, s); - if (bytes == 0) + c = pdfi_read_byte(ctx, s); + if (c < 0) return_error(gs_error_ioerror); - if (Buffer[read] < 0x30 || Buffer[read] > 0x39) { - pdfi_unread(ctx, s, &Buffer[read], 1); + if (c < '0' || c > '9') { + pdfi_unread_byte(ctx, s, c); break; } - read += bytes; - }while (read < limit); + *Buffer++ = (byte)c; + read++; + } while (read < limit); + *Buffer = 0; + return read; } @@ -562,9 +585,8 @@ static int read_digits(pdf_context *ctx, pdf_c_stream *s, byte *Buffer, unsigned static int read_xref_entry_slow(pdf_context *ctx, pdf_c_stream *s, gs_offset_t *offset, uint32_t *generation_num, unsigned char *free) { byte Buffer[20]; - int code, read = 0, bytes; + int c, code, read = 0; - memset(Buffer, 0x00, 20); /* First off, find a number. If we don't find one, and read 20 bytes, throw an error */ code = skip_to_digit(ctx, s, 20); if (code < 0) @@ -575,7 +597,6 @@ static int read_xref_entry_slow(pdf_context *ctx, pdf_c_stream *s, gs_offset_t * code = read_digits(ctx, s, (byte *)&Buffer, (read > 10 ? 20 - read : 10)); if (code < 0) return code; - Buffer[code] = 0x00; read += code; *offset = atol((const char *)Buffer); @@ -587,23 +608,22 @@ static int read_xref_entry_slow(pdf_context *ctx, pdf_c_stream *s, gs_offset_t * read += code; /* and read it */ - code = read_digits(ctx, s, (byte *)&Buffer, (read > 15 ? 20 - read : 5)); + code = read_digits(ctx, s, (byte *)&Buffer, (read > 15 ? 20 - read : 5)); if (code < 0) return code; - Buffer[code] = 0x00; read += code; *generation_num = atol((const char *)Buffer); do { - bytes = pdfi_read_bytes(ctx, &Buffer[0], 1, 1, s); - if (bytes == 0) + c = pdfi_read_byte(ctx, s); + if (c < 0) return_error(gs_error_ioerror); - read += bytes; - if (Buffer[0] == 0x09 || Buffer[0] == 0x20) + read ++; + if (c == 0x09 || c == 0x20) continue; - if (Buffer[0] == 'n' || Buffer[0] == 'f') { - *free = Buffer[0]; + if (c == 'n' || c == 'f') { + *free = (unsigned char)c; break; } else { return_error(gs_error_syntaxerror); @@ -613,9 +633,11 @@ static int read_xref_entry_slow(pdf_context *ctx, pdf_c_stream *s, gs_offset_t * return_error(gs_error_syntaxerror); do { - bytes = pdfi_read_bytes(ctx, &Buffer[0], 1, 1, s); - read += bytes; - if (Buffer[0] == 0x20 || Buffer[0] == 0x09 || Buffer[0] == 0x0d || Buffer[0] == 0x0a) + c = pdfi_read_byte(ctx, s); + if (c < 0) + return_error(gs_error_syntaxerror); + read++; + if (c == 0x20 || c == 0x09 || c == 0x0d || c == 0x0a) continue; } while (read < 20); return 0; @@ -626,7 +648,7 @@ static int write_offset(byte *B, gs_offset_t o, unsigned int g, unsigned char fr byte b[20], *ptr = B; int index = 0; - gs_sprintf((char *)b, "%"PRId64"", o); + gs_snprintf((char *)b, sizeof(b), "%"PRIdOFFSET"", o); if (strlen((const char *)b) > 10) return_error(gs_error_rangecheck); for(index=0;index < 10 - strlen((const char *)b); index++) { @@ -636,7 +658,7 @@ static int write_offset(byte *B, gs_offset_t o, unsigned int g, unsigned char fr ptr += strlen((const char *)b); *ptr++ = 0x20; - gs_sprintf((char *)b, "%d", g); + gs_snprintf((char *)b, sizeof(b), "%d", g); if (strlen((const char *)b) > 5) return_error(gs_error_rangecheck); for(index=0;index < 5 - strlen((const char *)b);index++) { @@ -682,6 +704,11 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio return_error(gs_error_typecheck); } + if (((pdf_num *)o)->value.i < 0) { + pdfi_pop(ctx, 1); + return_error(gs_error_rangecheck); + } + *section_start = start = ((pdf_num *)o)->value.i; code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); @@ -689,6 +716,10 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio pdfi_pop(ctx, 1); return code; } + if (code == 0) { + pdfi_pop(ctx, 1); + return_error(gs_error_syntaxerror); + } o = ctx->stack_top[-1]; if (o->type != PDF_INT) { @@ -696,6 +727,14 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio pdfi_pop(ctx, 2); return_error(gs_error_typecheck); } + + /* Zero sized xref sections are valid; see the file attached to + * bug 704947 for an example. */ + if (((pdf_num *)o)->value.i < 0) { + pdfi_pop(ctx, 2); + return_error(gs_error_rangecheck); + } + *section_size = size = ((pdf_num *)o)->value.i; pdfi_pop(ctx, 2); @@ -716,7 +755,7 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio return_error(gs_error_VMerror); } #if REFCNT_DEBUG - ctx->xref_table->UID = ctx->UID++; + ctx->xref_table->UID = ctx->ref_UID++; dmprintf1(ctx->memory, "Allocated xref table with UID %"PRIi64"\n", ctx->xref_table->UID); #endif @@ -746,7 +785,7 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio return_error(gs_error_ioerror); j = 19; while (Buffer[j] != 0x0D && Buffer[j] != 0x0A) { - pdfi_unread(ctx, s, (byte *)&Buffer[j], 1); + pdfi_unread_byte(ctx, s, (byte)Buffer[j]); if (--j < 0) { dmprintf(ctx->memory, "Invalid xref entry, line terminator missing.\n"); code = read_xref_entry_slow(ctx, s, &off, &gen, &free); @@ -755,6 +794,7 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio code = write_offset((byte *)Buffer, off, gen, free); if (code < 0) return code; + j = 19; break; } } @@ -762,7 +802,7 @@ static int read_xref_section(pdf_context *ctx, pdf_c_stream *s, uint64_t *sectio if (entry->object_num != 0) continue; - if (sscanf(Buffer, "%ld %d %c", &entry->u.uncompressed.offset, &entry->u.uncompressed.generation_num, &free) != 3) { + if (sscanf(Buffer, "%"PRIdOFFSET" %d %c", &entry->u.uncompressed.offset, &entry->u.uncompressed.generation_num, &free) != 3) { dmprintf(ctx->memory, "Invalid xref entry, incorrect format.\n"); pdfi_unread(ctx, s, (byte *)Buffer, 20); code = read_xref_entry_slow(ctx, s, &off, &gen, &free); @@ -801,8 +841,8 @@ static int read_xref(pdf_context *ctx, pdf_c_stream *s) if (code < 0) return code; - if (section_start + section_size > max_obj) - max_obj = section_start + section_size; + if (section_size > 0 && section_start + section_size - 1 > max_obj) + max_obj = section_start + section_size - 1; if (ctx->stack_top - o > 0) { k = (pdf_keyword *)ctx->stack_top[-1]; @@ -850,7 +890,7 @@ static int read_xref(pdf_context *ctx, pdf_c_stream *s) pdfi_pop(ctx, 1); return code; } - if (size < 0) { + if (size < 0 || size > floor((double)ARCH_MAX_SIZE_T / (double)sizeof(xref_entry))) { pdfi_pop(ctx, 1); return_error(gs_error_rangecheck); } @@ -862,7 +902,7 @@ static int read_xref(pdf_context *ctx, pdf_c_stream *s) } memset(ctx->xref_table, 0x00, sizeof(xref_table_t)); #if REFCNT_DEBUG - ctx->xref_table->UID = ctx->UID++; + ctx->xref_table->UID = ctx->ref_UID++; dmprintf1(ctx->memory, "Allocated xref table with UID %"PRIi64"\n", ctx->xref_table->UID); #endif @@ -931,6 +971,10 @@ static int read_xref(pdf_context *ctx, pdf_c_stream *s) return code; } + /* This can happen if pdfi_read_xref_stream tries to repair a broken PDF file */ + if (d != ctx->Trailer) + d = ctx->Trailer; + pdfi_loop_detector_cleartomark(ctx); } @@ -976,6 +1020,8 @@ static int read_xref(pdf_context *ctx, pdf_c_stream *s) code = pdfi_read_token(ctx, ctx->main_stream, 0, 0); if (code < 0) return(code); + if (code == 0) + return_error(gs_error_syntaxerror); if (((pdf_obj *)ctx->stack_top[-1])->type == PDF_KEYWORD && ((pdf_keyword *)ctx->stack_top[-1])->key == TOKEN_XREF) { /* Read old-style xref table */ @@ -1057,21 +1103,21 @@ int pdfi_read_xref(pdf_context *ctx) entry = &ctx->xref_table->xref[i]; if(entry->compressed) { dmprintf(ctx->memory, "*"); - gs_sprintf(Buffer, "%ld", entry->object_num); + gs_snprintf(Buffer, sizeof(Buffer), "%"PRId64"", entry->object_num); j = 10 - strlen(Buffer); while(j--) { dmprintf(ctx->memory, " "); } dmprintf1(ctx->memory, "%s ", Buffer); - gs_sprintf(Buffer, "%ld", entry->u.compressed.compressed_stream_num); + gs_snprintf(Buffer, sizeof(Buffer), "%ld", entry->u.compressed.compressed_stream_num); j = 10 - strlen(Buffer); while(j--) { dmprintf(ctx->memory, " "); } dmprintf1(ctx->memory, "%s ", Buffer); - gs_sprintf(Buffer, "%ld", entry->u.compressed.object_index); + gs_snprintf(Buffer, sizeof(Buffer), "%ld", entry->u.compressed.object_index); j = 10 - strlen(Buffer); while(j--) { dmprintf(ctx->memory, " "); @@ -1081,21 +1127,21 @@ int pdfi_read_xref(pdf_context *ctx) else { dmprintf(ctx->memory, " "); - gs_sprintf(Buffer, "%ld", entry->object_num); + gs_snprintf(Buffer, sizeof(Buffer), "%ld", entry->object_num); j = 10 - strlen(Buffer); while(j--) { dmprintf(ctx->memory, " "); } dmprintf1(ctx->memory, "%s ", Buffer); - gs_sprintf(Buffer, "%ld", entry->u.uncompressed.offset); + gs_snprintf(Buffer, sizeof(Buffer), "%"PRIdOFFSET"", entry->u.uncompressed.offset); j = 10 - strlen(Buffer); while(j--) { dmprintf(ctx->memory, " "); } dmprintf1(ctx->memory, "%s ", Buffer); - gs_sprintf(Buffer, "%ld", entry->u.uncompressed.generation_num); + gs_snprintf(Buffer, sizeof(Buffer), "%ld", entry->u.uncompressed.generation_num); j = 10 - strlen(Buffer); while(j--) { dmprintf(ctx->memory, " "); |