summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'pdf/pdf_xref.c')
-rw-r--r--pdf/pdf_xref.c156
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, " ");