Commit message: None
CVE: CVE-2016-9601
CWE: CWE-119
Vulnerability description: ghostscript before version 9.21 is vulnerable to a heap based buffer overflow that was found in the ghostscript jbig2_decode_gray_scale_image function which is used to decode halftone segments in a JBIG2 image. A document (PostScript or PDF) with an embedded, specially crafted, jbig2 image could trigger a segmentation fault in ghostscript.

Project: ghostscript

jbig2_default_error(void *data, const char *msg, Jbig2Severity severity, int32_t seg_idx)
{
    /* report only fatal errors by default */
    if (severity == JBIG2_SEVERITY_FATAL) {
        fprintf(stderr, "jbig2 decoder FATAL ERROR: %s", msg);
        if (seg_idx != -1)
            fprintf(stderr, " (segment 0x%02x)", seg_idx);
        fprintf(stderr, "\n");
        fflush(stderr);
    }

    return 0;
}

Project: ghostscript

jbig2_decode_generic_template1_TPGDON(Jbig2Ctx *ctx,
                                      Jbig2Segment *segment,
                                      const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats)
{
    const int GBW = image->width;
    const int GBH = image->height;
    uint32_t CONTEXT;
    int x, y;
    bool bit;
    int LTP = 0;

    for (y = 0; y < GBH; y++) {
        bit = jbig2_arith_decode(as, &GB_stats[0x0795]);
        if (bit < 0)
                    return -1;
        LTP ^= bit;
        if (!LTP) {
            for (x = 0; x < GBW; x++) {
                CONTEXT = jbig2_image_get_pixel(image, x - 1, y);
                CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1;
                CONTEXT |= jbig2_image_get_pixel(image, x - 3, y) << 2;
                CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 3;
                CONTEXT |= jbig2_image_get_pixel(image, x + 2, y - 1) << 4;
                CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 5;
                CONTEXT |= jbig2_image_get_pixel(image, x, y - 1) << 6;
                CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 7;
                CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 8;
                CONTEXT |= jbig2_image_get_pixel(image, x + 2, y - 2) << 9;
                CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 2) << 10;
                CONTEXT |= jbig2_image_get_pixel(image, x, y - 2) << 11;
                CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 2) << 12;
                bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]);
                if (bit < 0)
                            return -1;
                jbig2_image_set_pixel(image, x, y, bit);
            }
        } else {
            copy_prev_row(image, y);
        }
    }

    return 0;
}

Project: ghostscript

jbig2_dump_huffman_state(Jbig2HuffmanState *hs)
{
    fprintf(stderr, "huffman state %08x %08x offset %d.%d\n", hs->this_word, hs->next_word, hs->offset, hs->offset_bits);
}

Project: ghostscript

jbig2_huffman_new(Jbig2Ctx *ctx, Jbig2WordStream *ws)
{
    Jbig2HuffmanState *result = NULL;

    result = jbig2_new(ctx, Jbig2HuffmanState, 1);

    if (result != NULL) {
        result->offset = 0;
        result->offset_bits = 0;
        result->offset_limit = 0;
        result->ws = ws;
        result->ctx = ctx;
        result->this_word = huff_get_next_word(result, 0);
        result->next_word = huff_get_next_word(result, 4);
    } else {
        jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate new huffman coding state");
    }

    return result;
}

Project: ghostscript

main(int argc, char **argv)
{
    Jbig2Ctx *ctx;
    Jbig2HuffmanTable *tables[5];
    Jbig2HuffmanState *hs;
    Jbig2WordStream ws;
    bool oob;
    int32_t code;

    ctx = jbig2_ctx_new(NULL, 0, NULL, NULL, NULL);

    tables[0] = NULL;
    tables[1] = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_A);
    tables[2] = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_B);
    tables[3] = NULL;
    tables[4] = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_D);
    ws.get_next_word = test_get_word;
    hs = jbig2_huffman_new(ctx, &ws);

    printf("testing jbig2 huffmann decoding...");
    printf("\t(should be 8 5 (oob) 8)\n");

    {
        int i;
        int sequence_length = sizeof(test_tabindex);

        for (i = 0; i < sequence_length; i++) {
            code = jbig2_huffman_get(hs, tables[test_tabindex[i]], &oob);
            if (oob)
                printf("(oob) ");
            else
                printf("%d ", code);
        }
    }

    printf("\n");

    jbig2_ctx_free(ctx);

    return 0;
}

Project: ghostscript

main(int argc, char **argv)
{
    Jbig2Ctx *ctx = jbig2_ctx_new(NULL, 0, NULL, NULL, NULL);
    int i;

    for (i = 0; i < countof(tests); i++) {
        Jbig2HuffmanTable *table;
        Jbig2HuffmanState *hs;
        test_stream_t st;
        int32_t code;
        bool oob;
        int j;

        st.ws.get_next_word = test_get_word;
        st.h = &tests[i];
        printf("testing Standard Huffman table %s: ", st.h->name);
        table = jbig2_build_huffman_table(ctx, st.h->params);
        if (table == NULL) {
            printf("jbig2_build_huffman_table() returned NULL!\n");
        } else {
            /* jbig2_dump_huffman_table(table); */
            hs = jbig2_huffman_new(ctx, &st.ws);
            if (hs == NULL) {
                printf("jbig2_huffman_new() returned NULL!\n");
            } else {
                for (j = 0; j < st.h->output_len; j++) {
                    printf("%d...", st.h->output[j]);
                    code = jbig2_huffman_get(hs, table, &oob);
                    if (code == st.h->output[j] && !oob) {
                        printf("ok, ");
                    } else {
                        int need_comma = 0;

                        printf("NG(");
                        if (code != st.h->output[j]) {
                            printf("%d", code);
                            need_comma = 1;
                        }
                        if (oob) {
                            if (need_comma)
                                printf(",");
                            printf("OOB");
                        }
                        printf("), ");
                    }
                }
                if (st.h->params->HTOOB) {
                    printf("OOB...");
                    code = jbig2_huffman_get(hs, table, &oob);
                    if (oob) {
                        printf("ok");
                    } else {
                        printf("NG(%d)", code);
                    }
                }
                printf("\n");
                jbig2_huffman_free(ctx, hs);
            }
            jbig2_release_huffman_table(ctx, table);
        }
    }
    jbig2_ctx_free(ctx);
    return 0;
}

Project: ghostscript

test_get_word(Jbig2WordStream *self, int offset, uint32_t *word)
{
    /* assume test_stream[] is at least 4 bytes */
    if (offset + 3 > sizeof(test_stream))
        return -1;
    *word = ((test_stream[offset] << 24) | (test_stream[offset + 1] << 16) | (test_stream[offset + 2] << 8) | (test_stream[offset + 3]));
    return 0;
}

Project: ghostscript

jbig2_end_of_page(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data)
{
    uint32_t page_number = ctx->pages[ctx->current_page].number;

    if (segment->page_association != page_number) {
        jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number,
                    "end of page marker for page %d doesn't match current page number %d", segment->page_association, page_number);
    }

    jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "end of page %d", page_number);

    jbig2_complete_page(ctx);

#ifdef OUTPUT_PBM
    jbig2_image_write_pbm(ctx->pages[ctx->current_page].image, stdout);
#endif

    return 0;
}

Project: ghostscript

jbig2_parse_segment(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data)
{
    jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number,
                "Segment %d, flags=%x, type=%d, data_length=%d", segment->number, segment->flags, segment->flags & 63, segment->data_length);
    switch (segment->flags & 63) {
    case 0:
        return jbig2_symbol_dictionary(ctx, segment, segment_data);
    case 4:                    /* intermediate text region */
    case 6:                    /* immediate text region */
    case 7:                    /* immediate lossless text region */
        return jbig2_text_region(ctx, segment, segment_data);
    case 16:
        return jbig2_pattern_dictionary(ctx, segment, segment_data);
    case 20:                   /* intermediate halftone region */
    case 22:                   /* immediate halftone region */
    case 23:                   /* immediate lossless halftone region */
        return jbig2_halftone_region(ctx, segment, segment_data);
    case 36:
        return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unhandled segment type 'intermediate generic region'");
    case 38:                   /* immediate generic region */
    case 39:                   /* immediate lossless generic region */
        return jbig2_immediate_generic_region(ctx, segment, segment_data);
    case 40:                   /* intermediate generic refinement region */
    case 42:                   /* immediate generic refinement region */
    case 43:                   /* immediate lossless generic refinement region */
        return jbig2_refinement_region(ctx, segment, segment_data);
    case 48:
        return jbig2_page_info(ctx, segment, segment_data);
    case 49:
        return jbig2_end_of_page(ctx, segment, segment_data);
    case 50:
        return jbig2_end_of_stripe(ctx, segment, segment_data);
    case 51:
        ctx->state = JBIG2_FILE_EOF;
        return jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "end of file");
    case 52:
        return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unhandled segment type 'profile'");
    case 53:                   /* user-supplied huffman table */
        return jbig2_table(ctx, segment, segment_data);
    case 62:
        return jbig2_parse_extension_segment(ctx, segment, segment_data);
    default:
        jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unknown segment type %d", segment->flags & 63);
    }
    return 0;
}