shithub: jbig2

Download patch

ref: f9d37c7cf6a23a1c28015eebf53f2421c497b179
parent: 26e1f48057927c16b4fe200486236ae66a89860f
author: Sebastian Rasmussen <sebras@gmail.com>
date: Sun Sep 15 11:52:26 EDT 2019

jbig2dec: Add command line option to limit memory usage.

--- a/jbig2dec.c
+++ b/jbig2dec.c
@@ -65,6 +65,7 @@
     SHA1_CTX *hash_ctx;
     char *output_filename;
     jbig2dec_format output_format;
+    size_t memory_limit;
 } jbig2dec_params_t;
 
 typedef struct {
@@ -75,9 +76,118 @@
     long repeats;
 } jbig2dec_error_callback_state_t;
 
+typedef struct {
+    Jbig2Allocator super;
+    Jbig2Ctx *ctx;
+    size_t memory_limit;
+    size_t memory_used;
+    size_t memory_peak;
+} jbig2dec_allocator_t;
+
 static int print_version(void);
 static int print_usage(void);
 
+#define ALIGNMENT 16
+#define KBYTE 1024
+#define MBYTE (1024 * KBYTE)
+
+static void *jbig2dec_alloc(Jbig2Allocator *allocator_, size_t size)
+{
+    jbig2dec_allocator_t *allocator = (jbig2dec_allocator_t *) allocator_;
+    void *ptr;
+
+    if (size == 0)
+        return NULL;
+    if (size > allocator->memory_limit - ALIGNMENT - allocator->memory_used)
+        return NULL;
+
+    ptr = malloc(size + ALIGNMENT);
+    if (ptr == NULL)
+        return NULL;
+    memcpy(ptr, &size, sizeof(size));
+    allocator->memory_used += size + ALIGNMENT;
+
+    if (allocator->memory_used > allocator->memory_peak) {
+        allocator->memory_peak = allocator->memory_used;
+
+        if (allocator->ctx) {
+            size_t limit_mb = allocator->memory_limit / MBYTE;
+            size_t peak_mb = allocator->memory_peak / MBYTE;
+            jbig2_error(allocator->ctx, JBIG2_SEVERITY_DEBUG, -1, "memory: limit: %lu Mbyte peak usage: %lu Mbyte", limit_mb, peak_mb);
+        }
+    }
+
+    return (unsigned char *) ptr + ALIGNMENT;
+}
+
+static void jbig2dec_free(Jbig2Allocator *allocator_, void *p)
+{
+    jbig2dec_allocator_t *allocator = (jbig2dec_allocator_t *) allocator_;
+    size_t size;
+
+    if (p == NULL)
+        return;
+
+    memcpy(&size, (unsigned char *) p - ALIGNMENT, sizeof(size));
+    allocator->memory_used -= size + ALIGNMENT;
+    free((unsigned char *) p - ALIGNMENT);
+}
+
+static void *jbig2dec_realloc(Jbig2Allocator *allocator_, void *p, size_t size)
+{
+    jbig2dec_allocator_t *allocator = (jbig2dec_allocator_t *) allocator_;
+    unsigned char *oldp = p ? (unsigned char *) p - ALIGNMENT : NULL;
+
+    if (size > SIZE_MAX - ALIGNMENT)
+        return NULL;
+
+    if (oldp == NULL)
+    {
+        if (size == 0)
+            return NULL;
+        if (size > allocator->memory_limit - ALIGNMENT - allocator->memory_used)
+            return NULL;
+
+        p = malloc(size + ALIGNMENT);
+    }
+    else
+    {
+        size_t oldsize;
+        memcpy(&oldsize, oldp, sizeof(oldsize));
+
+        if (size == 0)
+        {
+            allocator->memory_used -= oldsize + ALIGNMENT;
+            free(oldp);
+            return NULL;
+        }
+
+        if (size > allocator->memory_limit - allocator->memory_used + oldsize)
+            return NULL;
+
+        p = realloc(oldp, size + ALIGNMENT);
+        if (p == NULL)
+            return NULL;
+
+        allocator->memory_used -= oldsize + ALIGNMENT;
+    }
+
+    memcpy(p, &size, sizeof(size));
+    allocator->memory_used += size + ALIGNMENT;
+
+    if (allocator->memory_used > allocator->memory_peak) {
+        allocator->memory_peak = allocator->memory_used;
+
+        if (allocator->ctx) {
+            size_t limit_mb = allocator->memory_limit / MBYTE;
+            size_t peak_mb = allocator->memory_peak / MBYTE;
+            jbig2_error(allocator->ctx, JBIG2_SEVERITY_DEBUG, -1, "memory: limit: %lu Mbyte peak usage: %lu Mbyte", limit_mb, peak_mb);
+        }
+    }
+
+    return (unsigned char *) p + ALIGNMENT;
+}
+
 /* page hashing functions */
 static void
 hash_init(jbig2dec_params_t *params)
@@ -155,9 +265,10 @@
     };
     int option_idx = 1;
     int option;
+    int ret;
 
     while (1) {
-        option = getopt_long(argc, argv, "Vh?qv:do:t:e", long_options, &option_idx);
+        option = getopt_long(argc, argv, "Vh?qv:do:t:eM:", long_options, &option_idx);
         if (option == -1)
             break;
 
@@ -200,9 +311,14 @@
         case 'e':
             params->embedded = 1;
             break;
+        case 'M':
+            ret = sscanf(optarg, "%zu", &params->memory_limit);
+            if (ret != 1)
+                fprintf(stderr, "could not parse memory limit argument\n");
+            break;
         default:
             if (!params->verbose)
-                fprintf(stdout, "unrecognized option: -%c\n", option);
+                fprintf(stderr, "unrecognized option: -%c\n", option);
             break;
         }
     }
@@ -238,6 +354,7 @@
             "    -V --version    program name and version information\n"
             "    -m --hash       print a hash of the decoded document\n"
             "    -e --embedded   expect embedded bit stream without file header\n"
+            "    -M <limit>      memory limit expressed in bytes\n"
             "    -o <file>\n"
             "    --output <file> send decoded output to <file>\n"
             "                    Defaults to the the input with a different\n"
@@ -428,11 +545,13 @@
 int
 main(int argc, char **argv)
 {
-    FILE *f = NULL, *f_page = NULL;
-    Jbig2Ctx *ctx = NULL;
-    uint8_t buf[4096];
     jbig2dec_params_t params;
     jbig2dec_error_callback_state_t error_callback_state;
+    jbig2dec_allocator_t allocator_;
+    jbig2dec_allocator_t *allocator = &allocator_;
+    Jbig2Ctx *ctx = NULL;
+    FILE *f = NULL, *f_page = NULL;
+    uint8_t buf[4096];
     int filearg;
     int result = 1;
     int code;
@@ -444,6 +563,7 @@
     params.output_filename = NULL;
     params.output_format = jbig2dec_format_none;
     params.embedded = 0;
+    params.memory_limit = 0;
 
     filearg = parse_options(argc, argv, &params);
 
@@ -496,7 +616,20 @@
         error_callback_state.last_message = NULL;
         error_callback_state.repeats = 0;
 
-        ctx = jbig2_ctx_new(NULL, (Jbig2Options)(f_page != NULL || params.embedded ? JBIG2_OPTIONS_EMBEDDED : 0), NULL, error_callback, &error_callback_state);
+        if (params.memory_limit == 0)
+            allocator = NULL;
+        else
+        {
+            allocator->super.alloc = jbig2dec_alloc;
+            allocator->super.free = jbig2dec_free;
+            allocator->super.realloc = jbig2dec_realloc;
+            allocator->ctx = NULL;
+            allocator->memory_limit = params.memory_limit;
+            allocator->memory_used = 0;
+            allocator->memory_peak = 0;
+        }
+
+        ctx = jbig2_ctx_new((Jbig2Allocator *) allocator, (Jbig2Options)(f_page != NULL || params.embedded ? JBIG2_OPTIONS_EMBEDDED : 0), NULL, error_callback, &error_callback_state);
         if (ctx == NULL) {
             fclose(f);
             if (f_page)
@@ -504,6 +637,9 @@
             goto cleanup;
         }
 
+        if (allocator != NULL)
+            allocator->ctx = ctx;
+
         /* pull the whole file/global stream into memory */
         for (;;) {
             int n_bytes = fread(buf, 1, sizeof(buf), f);
@@ -519,8 +655,11 @@
         if (f_page != NULL) {
             Jbig2GlobalCtx *global_ctx = jbig2_make_global_ctx(ctx);
 
-            ctx = jbig2_ctx_new(NULL, JBIG2_OPTIONS_EMBEDDED, global_ctx, error_callback, &error_callback_state);
+            ctx = jbig2_ctx_new((Jbig2Allocator *) allocator, JBIG2_OPTIONS_EMBEDDED, global_ctx, error_callback, &error_callback_state);
             if (ctx != NULL) {
+                if (allocator != NULL)
+                    allocator->ctx = ctx;
+
                 for (;;) {
                     int n_bytes = fread(buf, 1, sizeof(buf), f_page);
 
@@ -596,6 +735,12 @@
         }
 
     }                           /* end params.mode switch */
+
+    if (allocator != NULL && allocator->ctx != NULL) {
+        size_t limit_mb = allocator->memory_limit / MBYTE;
+        size_t peak_mb = allocator->memory_peak / MBYTE;
+        jbig2_error(allocator->ctx, JBIG2_SEVERITY_DEBUG, -1, "memory: limit: %lu Mbyte peak usage: %lu Mbyte", limit_mb, peak_mb);
+    }
 
     /* fin */
     result = 0;