diff options
author | Robin Watts <robin.watts@artifex.com> | 2016-09-22 13:44:45 +0100 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2016-09-22 13:52:41 +0100 |
commit | 1e03c06456d997435019fb3526fa2d4be7dbc6ec (patch) | |
tree | 6cbe8ea587af3682fd4340d802fcfcc538d6e106 | |
parent | fdf71862fe929b4560e9f632d775c50313d6ef02 (diff) | |
download | mupdf-1e03c06456d997435019fb3526fa2d4be7dbc6ec.tar.xz |
Bug 697015: Avoid object references vanishing during repair.
A PDF repair can be triggered 'just in time', when we encounter
a problem in the file. The idea is that this can happen without
the enclosing code being aware of it.
Thus the enclosing code may be holding 'borrowed' references
(such as those returned by pdf_dict_get()) at the time when the
repair is triggered. We are therefore at pains to ensure that
the repair does not replace any objects that exist already, so
that the calling code will not have these references unexpectedly
invalidated.
The sole exception to this is when we replace the 'Length' fields
in stream dictionaries with the actual lengths. Bug 697015 shows
exactly this situation causing a reference to become invalid.
The solution implemented here is to add an 'orphan list' to the
document, where we put these (hopefully few, small) objects. These
orphans are kept around until the document is closed.
-rw-r--r-- | include/mupdf/pdf/document.h | 4 | ||||
-rw-r--r-- | include/mupdf/pdf/object.h | 1 | ||||
-rw-r--r-- | source/pdf/pdf-object.c | 31 | ||||
-rw-r--r-- | source/pdf/pdf-repair.c | 28 | ||||
-rw-r--r-- | source/pdf/pdf-xref.c | 6 |
5 files changed, 63 insertions, 7 deletions
diff --git a/include/mupdf/pdf/document.h b/include/mupdf/pdf/document.h index aabf05f9..0078c4a9 100644 --- a/include/mupdf/pdf/document.h +++ b/include/mupdf/pdf/document.h @@ -269,6 +269,10 @@ struct pdf_document_s fz_hash_table *images; fz_hash_table *fonts; } resources; + + int orphans_max; + int orphans_count; + pdf_obj **orphans; }; /* diff --git a/include/mupdf/pdf/object.h b/include/mupdf/pdf/object.h index 5bc3dcaf..bf574551 100644 --- a/include/mupdf/pdf/object.h +++ b/include/mupdf/pdf/object.h @@ -110,6 +110,7 @@ pdf_obj *pdf_dict_gets(fz_context *ctx, pdf_obj *dict, const char *key); pdf_obj *pdf_dict_getsa(fz_context *ctx, pdf_obj *dict, const char *key, const char *abbrev); void pdf_dict_put(fz_context *ctx, pdf_obj *dict, pdf_obj *key, pdf_obj *val); void pdf_dict_put_drop(fz_context *ctx, pdf_obj *dict, pdf_obj *key, pdf_obj *val); +void pdf_dict_get_put_drop(fz_context *ctx, pdf_obj *dict, pdf_obj *key, pdf_obj *val, pdf_obj **old_val); void pdf_dict_puts(fz_context *ctx, pdf_obj *dict, const char *key, pdf_obj *val); void pdf_dict_puts_drop(fz_context *ctx, pdf_obj *dict, const char *key, pdf_obj *val); void pdf_dict_putp(fz_context *ctx, pdf_obj *dict, const char *path, pdf_obj *val); diff --git a/source/pdf/pdf-object.c b/source/pdf/pdf-object.c index b4e33f3c..1c19ba4f 100644 --- a/source/pdf/pdf-object.c +++ b/source/pdf/pdf-object.c @@ -1265,11 +1265,14 @@ pdf_dict_geta(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *abbrev) return pdf_dict_get(ctx, obj, abbrev); } -void -pdf_dict_put(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *val) +static void +pdf_dict_get_put(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *val, pdf_obj **old_val) { int i; + if (old_val) + *old_val = NULL; + RESOLVE(obj); if (!OBJ_IS_DICT(obj)) fz_throw(ctx, FZ_ERROR_GENERIC, "not a dict (%s)", pdf_objkindstr(obj)); @@ -1295,7 +1298,10 @@ pdf_dict_put(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *val) { pdf_obj *d = DICT(obj)->items[i].v; DICT(obj)->items[i].v = pdf_keep_obj(ctx, val); - pdf_drop_obj(ctx, d); + if (old_val) + *old_val = d; + else + pdf_drop_obj(ctx, d); } } else @@ -1316,10 +1322,27 @@ pdf_dict_put(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *val) } void +pdf_dict_put(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *val) +{ + pdf_dict_get_put(ctx, obj, key, val, NULL); +} + +void pdf_dict_put_drop(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *val) { fz_try(ctx) - pdf_dict_put(ctx, obj, key, val); + pdf_dict_get_put(ctx, obj, key, val, NULL); + fz_always(ctx) + pdf_drop_obj(ctx, val); + fz_catch(ctx) + fz_rethrow(ctx); +} + +void +pdf_dict_get_put_drop(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *val, pdf_obj **old_val) +{ + fz_try(ctx) + pdf_dict_get_put(ctx, obj, key, val, old_val); fz_always(ctx) pdf_drop_obj(ctx, val); fz_catch(ctx) diff --git a/source/pdf/pdf-repair.c b/source/pdf/pdf-repair.c index 690bf15a..167f6097 100644 --- a/source/pdf/pdf-repair.c +++ b/source/pdf/pdf-repair.c @@ -260,6 +260,27 @@ pdf_repair_obj_stm(fz_context *ctx, pdf_document *doc, int stm_num) } } +static void +orphan_object(fz_context *ctx, pdf_document *doc, pdf_obj *obj) +{ + if (doc->orphans_count == doc->orphans_max) + { + int new_max = (doc->orphans_max ? doc->orphans_max*2 : 32); + + fz_try(ctx) + { + doc->orphans = fz_resize_array(ctx, doc->orphans, new_max, sizeof(*doc->orphans)); + doc->orphans_max = new_max; + } + fz_catch(ctx) + { + pdf_drop_obj(ctx, obj); + fz_rethrow(ctx); + } + } + doc->orphans[doc->orphans_count++] = obj; +} + void pdf_repair_xref(fz_context *ctx, pdf_document *doc) { @@ -528,12 +549,13 @@ pdf_repair_xref(fz_context *ctx, pdf_document *doc) /* correct stream length for unencrypted documents */ if (!encrypt && list[i].stm_len >= 0) { + pdf_obj *old_obj = NULL; dict = pdf_load_object(ctx, doc, list[i].num); length = pdf_new_int(ctx, doc, list[i].stm_len); - pdf_dict_put(ctx, dict, PDF_NAME_Length, length); - pdf_drop_obj(ctx, length); - + pdf_dict_get_put_drop(ctx, dict, PDF_NAME_Length, length, &old_obj); + if (old_obj) + orphan_object(ctx, doc, old_obj); pdf_drop_obj(ctx, dict); } } diff --git a/source/pdf/pdf-xref.c b/source/pdf/pdf-xref.c index 7d21775a..0cf20d4c 100644 --- a/source/pdf/pdf-xref.c +++ b/source/pdf/pdf-xref.c @@ -1620,6 +1620,12 @@ pdf_drop_document_imp(fz_context *ctx, pdf_document *doc) pdf_drop_resource_tables(ctx, doc); + for (i = 0; i < doc->orphans_count; i++) + { + pdf_drop_obj(ctx, doc->orphans[i]); + } + fz_free(ctx, doc->orphans); + fz_free(ctx, doc); } fz_always(ctx) |