From e31c26d7a0fad2cd935d597d1dabd38f7d8941bc Mon Sep 17 00:00:00 2001 From: Robin Watts Date: Tue, 15 Mar 2016 18:51:15 +0000 Subject: Tweak fz_try/catch to fix overflow case. In the current code, when we hit the fz_try(), we check to see if we are about to overflow the exception stack. If we are, we throw. This does NOT throw to the fz_catch following the try, but rather to the enclosing fz_always/fz_catch blocks. This can cause leaks, as it's very hard to code correctly. It would be a far nicer behaviour to have a failure in fz_try() cause us to throw to the immediately following fz_always/fz_catch. This commit achieves that. --- include/mupdf/fitz/context.h | 18 +++++++++++------- source/fitz/error.c | 41 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/include/mupdf/fitz/context.h b/include/mupdf/fitz/context.h index c2b08898..db5d5d75 100644 --- a/include/mupdf/fitz/context.h +++ b/include/mupdf/fitz/context.h @@ -55,23 +55,27 @@ void fz_var_imp(void *); #define fz_try(ctx) \ {{{ \ - if (fz_setjmp(fz_push_try(ctx)->buffer) == 0)\ - { do { + if (fz_push_try(ctx)) {\ + if (fz_setjmp((ctx)->error->top->buffer) == 0)\ + { do {\ #define fz_always(ctx) \ - } while (0); \ + } while (0); \ + } \ } \ if (ctx->error->top->code < 3) \ { \ - ctx->error->top->code++; \ - do { \ + { \ + ctx->error->top->code++; \ + do { \ #define fz_catch(ctx) \ - } while(0); \ + } while(0); \ + } \ } }}} \ if ((ctx->error->top--)->code > 1) -fz_error_stack_slot *fz_push_try(fz_context *ctx); +int fz_push_try(fz_context *ctx); FZ_NORETURN void fz_throw(fz_context *ctx, int errcode, const char *, ...) __printflike(3, 4); FZ_NORETURN void fz_rethrow(fz_context *ctx); FZ_NORETURN void fz_rethrow_message(fz_context *ctx, const char *fmt, ...) __printflike(2, 3); diff --git a/source/fitz/error.c b/source/fitz/error.c index a99a3f83..f3c5973d 100644 --- a/source/fitz/error.c +++ b/source/fitz/error.c @@ -100,16 +100,47 @@ FZ_NORETURN static void throw(fz_context *ctx) } } -fz_error_stack_slot *fz_push_try(fz_context *ctx) +/* Only called when we hit the bottom of the exception stack. + * Do the same as fz_throw, but don't actually throw. */ +static int fz_fake_throw(fz_context *ctx, int code, const char *fmt, ...) +{ + va_list args; + ctx->error->errcode = code; + va_start(args, fmt); + vsnprintf(ctx->error->message, sizeof ctx->error->message, fmt, args); + va_end(args); + + if (code != FZ_ERROR_ABORT) + { + fz_flush_warnings(ctx); + fprintf(stderr, "error: %s\n", ctx->error->message); + LOGE("error: %s\n", ctx->error->message); +#ifdef USE_OUTPUT_DEBUG_STRING + OutputDebugStringA("error: "); + OutputDebugStringA(ctx->error->message); + OutputDebugStringA("\n"); +#endif + } + + /* We need to arrive in the always/catch block as if throw + * had taken place. */ + ctx->error->top++; + ctx->error->top->code = 2; + return 0; +} + +int fz_push_try(fz_context *ctx) { /* If we would overflow the exception stack, throw an exception instead - * of entering the try block. */ - if (ctx->error->top + 1 >= ctx->error->stack + nelem(ctx->error->stack)) - fz_throw(ctx, FZ_ERROR_GENERIC, "exception stack overflow!"); + * of entering the try block. We assume that we always have room for + * 1 extra level on the stack here - i.e. we throw the error on us + * starting to use the last level. */ + if (ctx->error->top + 2 >= ctx->error->stack + nelem(ctx->error->stack)) + return fz_fake_throw(ctx, FZ_ERROR_GENERIC, "exception stack overflow!"); ctx->error->top++; ctx->error->top->code = 0; - return ctx->error->top; + return 1; } int fz_caught(fz_context *ctx) -- cgit v1.2.3