mirror of
https://github.com/michaelrsweet/pdfio.git
synced 2024-12-26 13:28:22 +01:00
Fix a few stack/buffer overflow bugs discovered by Bart, Steffan, and Mark from
the Radboud University NL (thanks!) - Add depth argument to all value read functions that recurse - Add depth argument to page tree loading code - Validate xref stream sizes individually to avoid out-of-bounds access to local xref buffer.
This commit is contained in:
parent
ec8e900ea5
commit
a431d7806f
@ -2,6 +2,12 @@ Changes in PDFio
|
|||||||
================
|
================
|
||||||
|
|
||||||
|
|
||||||
|
v1.0rc1 (Month DD, YYYY)
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
- Fixed a few stack/buffer overflow bugs discovered via fuzzing.
|
||||||
|
|
||||||
|
|
||||||
v1.0b2 (November 7, 2021)
|
v1.0b2 (November 7, 2021)
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
2
Makefile
2
Makefile
@ -17,7 +17,7 @@ CC = cc
|
|||||||
CFLAGS =
|
CFLAGS =
|
||||||
CODESIGN_IDENTITY = Developer ID
|
CODESIGN_IDENTITY = Developer ID
|
||||||
#COMMONFLAGS = -Os -g
|
#COMMONFLAGS = -Os -g
|
||||||
COMMONFLAGS = -O0 -g
|
COMMONFLAGS = -O0 -g -fsanitize=address
|
||||||
CPPFLAGS = '-DPDFIO_VERSION="$(VERSION)"'
|
CPPFLAGS = '-DPDFIO_VERSION="$(VERSION)"'
|
||||||
DESTDIR = $(DSTROOT)
|
DESTDIR = $(DSTROOT)
|
||||||
DSO = cc
|
DSO = cc
|
||||||
|
@ -575,7 +575,8 @@ _pdfioArrayGetValue(pdfio_array_t *a, // I - Array
|
|||||||
pdfio_array_t * // O - New array
|
pdfio_array_t * // O - New array
|
||||||
_pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file
|
_pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file
|
||||||
pdfio_obj_t *obj, // I - Object, if any
|
pdfio_obj_t *obj, // I - Object, if any
|
||||||
_pdfio_token_t *tb) // I - Token buffer/stack
|
_pdfio_token_t *tb, // I - Token buffer/stack
|
||||||
|
size_t depth) // I - Depth of array
|
||||||
{
|
{
|
||||||
pdfio_array_t *array; // New array
|
pdfio_array_t *array; // New array
|
||||||
char token[8192]; // Token from file
|
char token[8192]; // Token from file
|
||||||
@ -599,7 +600,7 @@ _pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
|
|
||||||
// Push the token and decode the value...
|
// Push the token and decode the value...
|
||||||
_pdfioTokenPush(tb, token);
|
_pdfioTokenPush(tb, token);
|
||||||
if (!_pdfioValueRead(pdf, obj, tb, &value))
|
if (!_pdfioValueRead(pdf, obj, tb, &value, depth))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// PDFIO_DEBUG("_pdfioArrayRead(%p): Appending ", (void *)array);
|
// PDFIO_DEBUG("_pdfioArrayRead(%p): Appending ", (void *)array);
|
||||||
|
@ -473,7 +473,8 @@ _pdfioDictGetValue(pdfio_dict_t *dict, // I - Dictionary
|
|||||||
pdfio_dict_t * // O - New dictionary
|
pdfio_dict_t * // O - New dictionary
|
||||||
_pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
|
_pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
|
||||||
pdfio_obj_t *obj, // I - Object, if any
|
pdfio_obj_t *obj, // I - Object, if any
|
||||||
_pdfio_token_t *tb) // I - Token buffer/stack
|
_pdfio_token_t *tb, // I - Token buffer/stack
|
||||||
|
size_t depth) // I - Depth of dictionary
|
||||||
{
|
{
|
||||||
pdfio_dict_t *dict; // New dictionary
|
pdfio_dict_t *dict; // New dictionary
|
||||||
char key[256]; // Dictionary key
|
char key[256]; // Dictionary key
|
||||||
@ -501,7 +502,7 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Then get the next value...
|
// Then get the next value...
|
||||||
if (!_pdfioValueRead(pdf, obj, tb, &value))
|
if (!_pdfioValueRead(pdf, obj, tb, &value, depth))
|
||||||
{
|
{
|
||||||
_pdfioFileError(pdf, "Missing value for dictionary key.");
|
_pdfioFileError(pdf, "Missing value for dictionary key.");
|
||||||
break;
|
break;
|
||||||
|
23
pdfio-file.c
23
pdfio-file.c
@ -25,7 +25,7 @@ static pdfio_obj_t *add_obj(pdfio_file_t *pdf, size_t number, unsigned short gen
|
|||||||
static int compare_objmaps(_pdfio_objmap_t *a, _pdfio_objmap_t *b);
|
static int compare_objmaps(_pdfio_objmap_t *a, _pdfio_objmap_t *b);
|
||||||
static int compare_objs(pdfio_obj_t **a, pdfio_obj_t **b);
|
static int compare_objs(pdfio_obj_t **a, pdfio_obj_t **b);
|
||||||
static bool load_obj_stream(pdfio_obj_t *obj);
|
static bool load_obj_stream(pdfio_obj_t *obj);
|
||||||
static bool load_pages(pdfio_file_t *pdf, pdfio_obj_t *obj);
|
static bool load_pages(pdfio_file_t *pdf, pdfio_obj_t *obj, size_t depth);
|
||||||
static bool load_xref(pdfio_file_t *pdf, off_t xref_offset, pdfio_password_cb_t password_cb, void *password_data);
|
static bool load_xref(pdfio_file_t *pdf, off_t xref_offset, pdfio_password_cb_t password_cb, void *password_data);
|
||||||
static bool write_catalog(pdfio_file_t *pdf);
|
static bool write_catalog(pdfio_file_t *pdf);
|
||||||
static bool write_pages(pdfio_file_t *pdf);
|
static bool write_pages(pdfio_file_t *pdf);
|
||||||
@ -1312,7 +1312,7 @@ load_obj_stream(pdfio_obj_t *obj) // I - Object to load
|
|||||||
// Read the objects themselves...
|
// Read the objects themselves...
|
||||||
for (cur_obj = 0; cur_obj < num_objs; cur_obj ++)
|
for (cur_obj = 0; cur_obj < num_objs; cur_obj ++)
|
||||||
{
|
{
|
||||||
if (!_pdfioValueRead(obj->pdf, obj, &tb, &(objs[cur_obj]->value)))
|
if (!_pdfioValueRead(obj->pdf, obj, &tb, &(objs[cur_obj]->value), 0))
|
||||||
{
|
{
|
||||||
pdfioStreamClose(st);
|
pdfioStreamClose(st);
|
||||||
return (false);
|
return (false);
|
||||||
@ -1332,7 +1332,8 @@ load_obj_stream(pdfio_obj_t *obj) // I - Object to load
|
|||||||
|
|
||||||
static bool // O - `true` on success, `false` on error
|
static bool // O - `true` on success, `false` on error
|
||||||
load_pages(pdfio_file_t *pdf, // I - PDF file
|
load_pages(pdfio_file_t *pdf, // I - PDF file
|
||||||
pdfio_obj_t *obj) // I - Page object
|
pdfio_obj_t *obj, // I - Page object
|
||||||
|
size_t depth) // I - Depth of page tree
|
||||||
{
|
{
|
||||||
pdfio_dict_t *dict; // Page object dictionary
|
pdfio_dict_t *dict; // Page object dictionary
|
||||||
const char *type; // Node type
|
const char *type; // Node type
|
||||||
@ -1364,9 +1365,15 @@ load_pages(pdfio_file_t *pdf, // I - PDF file
|
|||||||
size_t i, // Looping var
|
size_t i, // Looping var
|
||||||
num_kids; // Number of elements in array
|
num_kids; // Number of elements in array
|
||||||
|
|
||||||
|
if (depth >= PDFIO_MAX_DEPTH)
|
||||||
|
{
|
||||||
|
_pdfioFileError(pdf, "Depth of pages objects too great to load.");
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0, num_kids = pdfioArrayGetSize(kids); i < num_kids; i ++)
|
for (i = 0, num_kids = pdfioArrayGetSize(kids); i < num_kids; i ++)
|
||||||
{
|
{
|
||||||
if (!load_pages(pdf, pdfioArrayGetObj(kids, i)))
|
if (!load_pages(pdf, pdfioArrayGetObj(kids, i), depth + 1))
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1496,7 +1503,7 @@ load_xref(
|
|||||||
|
|
||||||
_pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, pdf);
|
_pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, pdf);
|
||||||
|
|
||||||
if (!_pdfioValueRead(pdf, obj, &tb, &trailer))
|
if (!_pdfioValueRead(pdf, obj, &tb, &trailer, 0))
|
||||||
{
|
{
|
||||||
_pdfioFileError(pdf, "Unable to read cross-reference stream dictionary.");
|
_pdfioFileError(pdf, "Unable to read cross-reference stream dictionary.");
|
||||||
return (false);
|
return (false);
|
||||||
@ -1537,7 +1544,7 @@ load_xref(
|
|||||||
w_2 = w[0];
|
w_2 = w[0];
|
||||||
w_3 = w[0] + w[1];
|
w_3 = w[0] + w[1];
|
||||||
|
|
||||||
if (w[1] == 0 || w[2] > 2 || w_total > sizeof(buffer))
|
if (w[1] == 0 || w[2] > 2 || w[0] > sizeof(buffer) || w[1] > sizeof(buffer) || w[2] > sizeof(buffer) || w_total > sizeof(buffer))
|
||||||
{
|
{
|
||||||
_pdfioFileError(pdf, "Cross-reference stream has invalid W key.");
|
_pdfioFileError(pdf, "Cross-reference stream has invalid W key.");
|
||||||
return (false);
|
return (false);
|
||||||
@ -1751,7 +1758,7 @@ load_xref(
|
|||||||
|
|
||||||
_pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, pdf);
|
_pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, pdf);
|
||||||
|
|
||||||
if (!_pdfioValueRead(pdf, NULL, &tb, &trailer))
|
if (!_pdfioValueRead(pdf, NULL, &tb, &trailer, 0))
|
||||||
{
|
{
|
||||||
_pdfioFileError(pdf, "Unable to read trailer dictionary.");
|
_pdfioFileError(pdf, "Unable to read trailer dictionary.");
|
||||||
return (false);
|
return (false);
|
||||||
@ -1803,7 +1810,7 @@ load_xref(
|
|||||||
|
|
||||||
PDFIO_DEBUG("load_xref: Root=%p(%lu)\n", pdf->root_obj, (unsigned long)pdf->root_obj->number);
|
PDFIO_DEBUG("load_xref: Root=%p(%lu)\n", pdf->root_obj, (unsigned long)pdf->root_obj->number);
|
||||||
|
|
||||||
return (load_pages(pdf, pdfioDictGetObj(pdfioObjGetDict(pdf->root_obj), "Pages")));
|
return (load_pages(pdf, pdfioDictGetObj(pdfioObjGetDict(pdf->root_obj), "Pages"), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -412,7 +412,7 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
|
|||||||
// Then grab the object value...
|
// Then grab the object value...
|
||||||
_pdfioTokenInit(&tb, obj->pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, obj->pdf);
|
_pdfioTokenInit(&tb, obj->pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, obj->pdf);
|
||||||
|
|
||||||
if (!_pdfioValueRead(obj->pdf, obj, &tb, &obj->value))
|
if (!_pdfioValueRead(obj->pdf, obj, &tb, &obj->value, 0))
|
||||||
{
|
{
|
||||||
_pdfioFileError(obj->pdf, "Unable to read value for object %lu.", (unsigned long)obj->number);
|
_pdfioFileError(obj->pdf, "Unable to read value for object %lu.", (unsigned long)obj->number);
|
||||||
return (false);
|
return (false);
|
||||||
|
@ -116,6 +116,8 @@
|
|||||||
// Types and constants...
|
// Types and constants...
|
||||||
//
|
//
|
||||||
|
|
||||||
|
# define PDFIO_MAX_DEPTH 32 // Maximum nesting depth for values
|
||||||
|
|
||||||
typedef enum _pdfio_mode_e // Read/write mode
|
typedef enum _pdfio_mode_e // Read/write mode
|
||||||
{
|
{
|
||||||
_PDFIO_MODE_READ, // Read a PDF file
|
_PDFIO_MODE_READ, // Read a PDF file
|
||||||
@ -341,7 +343,7 @@ struct _pdfio_stream_s // Stream
|
|||||||
extern void _pdfioArrayDebug(pdfio_array_t *a, FILE *fp) _PDFIO_INTERNAL;
|
extern void _pdfioArrayDebug(pdfio_array_t *a, FILE *fp) _PDFIO_INTERNAL;
|
||||||
extern void _pdfioArrayDelete(pdfio_array_t *a) _PDFIO_INTERNAL;
|
extern void _pdfioArrayDelete(pdfio_array_t *a) _PDFIO_INTERNAL;
|
||||||
extern _pdfio_value_t *_pdfioArrayGetValue(pdfio_array_t *a, size_t n) _PDFIO_INTERNAL;
|
extern _pdfio_value_t *_pdfioArrayGetValue(pdfio_array_t *a, size_t n) _PDFIO_INTERNAL;
|
||||||
extern pdfio_array_t *_pdfioArrayRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts) _PDFIO_INTERNAL;
|
extern pdfio_array_t *_pdfioArrayRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts, size_t depth) _PDFIO_INTERNAL;
|
||||||
extern bool _pdfioArrayWrite(pdfio_array_t *a, pdfio_obj_t *obj) _PDFIO_INTERNAL;
|
extern bool _pdfioArrayWrite(pdfio_array_t *a, pdfio_obj_t *obj) _PDFIO_INTERNAL;
|
||||||
|
|
||||||
extern void _pdfioCryptoAESInit(_pdfio_aes_t *ctx, const uint8_t *key, size_t keylen, const uint8_t *iv) _PDFIO_INTERNAL;
|
extern void _pdfioCryptoAESInit(_pdfio_aes_t *ctx, const uint8_t *key, size_t keylen, const uint8_t *iv) _PDFIO_INTERNAL;
|
||||||
@ -365,7 +367,7 @@ extern void _pdfioDictClear(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNA
|
|||||||
extern void _pdfioDictDebug(pdfio_dict_t *dict, FILE *fp) _PDFIO_INTERNAL;
|
extern void _pdfioDictDebug(pdfio_dict_t *dict, FILE *fp) _PDFIO_INTERNAL;
|
||||||
extern void _pdfioDictDelete(pdfio_dict_t *dict) _PDFIO_INTERNAL;
|
extern void _pdfioDictDelete(pdfio_dict_t *dict) _PDFIO_INTERNAL;
|
||||||
extern _pdfio_value_t *_pdfioDictGetValue(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;
|
extern _pdfio_value_t *_pdfioDictGetValue(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;
|
||||||
extern pdfio_dict_t *_pdfioDictRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts) _PDFIO_INTERNAL;
|
extern pdfio_dict_t *_pdfioDictRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts, size_t depth) _PDFIO_INTERNAL;
|
||||||
extern bool _pdfioDictSetValue(pdfio_dict_t *dict, const char *key, _pdfio_value_t *value) _PDFIO_INTERNAL;
|
extern bool _pdfioDictSetValue(pdfio_dict_t *dict, const char *key, _pdfio_value_t *value) _PDFIO_INTERNAL;
|
||||||
extern bool _pdfioDictWrite(pdfio_dict_t *dict, pdfio_obj_t *obj, off_t *length) _PDFIO_INTERNAL;
|
extern bool _pdfioDictWrite(pdfio_dict_t *dict, pdfio_obj_t *obj, off_t *length) _PDFIO_INTERNAL;
|
||||||
|
|
||||||
@ -405,7 +407,7 @@ extern bool _pdfioTokenRead(_pdfio_token_t *tb, char *buffer, size_t bufsize);
|
|||||||
extern _pdfio_value_t *_pdfioValueCopy(pdfio_file_t *pdfdst, _pdfio_value_t *vdst, pdfio_file_t *pdfsrc, _pdfio_value_t *vsrc) _PDFIO_INTERNAL;
|
extern _pdfio_value_t *_pdfioValueCopy(pdfio_file_t *pdfdst, _pdfio_value_t *vdst, pdfio_file_t *pdfsrc, _pdfio_value_t *vsrc) _PDFIO_INTERNAL;
|
||||||
extern void _pdfioValueDebug(_pdfio_value_t *v, FILE *fp) _PDFIO_INTERNAL;
|
extern void _pdfioValueDebug(_pdfio_value_t *v, FILE *fp) _PDFIO_INTERNAL;
|
||||||
extern void _pdfioValueDelete(_pdfio_value_t *v) _PDFIO_INTERNAL;
|
extern void _pdfioValueDelete(_pdfio_value_t *v) _PDFIO_INTERNAL;
|
||||||
extern _pdfio_value_t *_pdfioValueRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts, _pdfio_value_t *v) _PDFIO_INTERNAL;
|
extern _pdfio_value_t *_pdfioValueRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts, _pdfio_value_t *v, size_t depth) _PDFIO_INTERNAL;
|
||||||
extern bool _pdfioValueWrite(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_value_t *v, off_t *length) _PDFIO_INTERNAL;
|
extern bool _pdfioValueWrite(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_value_t *v, off_t *length) _PDFIO_INTERNAL;
|
||||||
|
|
||||||
#endif // !PDFIO_PRIVATE_H
|
#endif // !PDFIO_PRIVATE_H
|
||||||
|
@ -196,7 +196,8 @@ _pdfio_value_t * // O - Value or `NULL` on error/EOF
|
|||||||
_pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
_pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
||||||
pdfio_obj_t *obj, // I - Object, if any
|
pdfio_obj_t *obj, // I - Object, if any
|
||||||
_pdfio_token_t *tb, // I - Token buffer/stack
|
_pdfio_token_t *tb, // I - Token buffer/stack
|
||||||
_pdfio_value_t *v) // I - Value
|
_pdfio_value_t *v, // I - Value
|
||||||
|
size_t depth) // I - Depth of value
|
||||||
{
|
{
|
||||||
char token[32768]; // Token buffer
|
char token[32768]; // Token buffer
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@ -226,15 +227,27 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
if (!strcmp(token, "["))
|
if (!strcmp(token, "["))
|
||||||
{
|
{
|
||||||
// Start of array
|
// Start of array
|
||||||
|
if (depth >= PDFIO_MAX_DEPTH)
|
||||||
|
{
|
||||||
|
_pdfioFileError(pdf, "Too many nested arrays.");
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
v->type = PDFIO_VALTYPE_ARRAY;
|
v->type = PDFIO_VALTYPE_ARRAY;
|
||||||
if ((v->value.array = _pdfioArrayRead(pdf, obj, tb)) == NULL)
|
if ((v->value.array = _pdfioArrayRead(pdf, obj, tb, depth + 1)) == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
else if (!strcmp(token, "<<"))
|
else if (!strcmp(token, "<<"))
|
||||||
{
|
{
|
||||||
// Start of dictionary
|
// Start of dictionary
|
||||||
|
if (depth >= PDFIO_MAX_DEPTH)
|
||||||
|
{
|
||||||
|
_pdfioFileError(pdf, "Too many nested dictionaries.");
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
v->type = PDFIO_VALTYPE_DICT;
|
v->type = PDFIO_VALTYPE_DICT;
|
||||||
if ((v->value.dict = _pdfioDictRead(pdf, obj, tb)) == NULL)
|
if ((v->value.dict = _pdfioDictRead(pdf, obj, tb, depth + 1)) == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
else if (!strncmp(token, "(D:", 3))
|
else if (!strncmp(token, "(D:", 3))
|
||||||
|
Loading…
Reference in New Issue
Block a user