Commit message: PS interpreter - check the Implementation of a Pattern before use

Bug #700141 "Type confusion in setpattern"

As the bug thread says, we were not checking that the Implementation
of a pattern dictionary was a structure type, leading to a crash when
we tried to treat it as one.

Here we make the st_pattern1_instance and st_pattern2_instance
structures public definitions and in zsetcolor we check the object
stored under the Implementation key in the supplied dictionary to see if
its a t_struct or t_astruct type, and if it is that its a
st_pattern1_instance or st_pattern2_instance structure.

If either check fails we throw a typecheck error.

We need to make the st_pattern1_instance and st_pattern2_instance
definitions public as they are defined in the graphics library and we
need to check in the interpreter.
CVE: CVE-2018-19134
CWE: CWE-704
Vulnerability description: In Artifex Ghostscript through 9.25, the setpattern operator did not properly validate certain types. A specially crafted PostScript document could exploit this to crash Ghostscript or, possibly, execute arbitrary code in the context of the Ghostscript process. This is a type confusion issue because of failure to check whether the Implementation of a pattern dictionary was a structure type.

Project: ghostpdl
File: gsptype2.c

gx_dc_pattern2_load(gx_device_color *pdevc, const gs_gstate *ignore_pgs,
                    gx_device *ignore_dev, gs_color_select_t ignore_select)
{
    return 0;
}

Project: ghostpdl
File: gsptype2.c

gx_dc_is_pattern2_color(const gx_device_color *pdevc)
{
    return pdevc->type == &gx_dc_pattern2;
}

Project: ghostpdl
File: gsptype2.c

gx_dc_pattern2_equal(const gx_device_color * pdevc1,
                     const gx_device_color * pdevc2)
{
    return pdevc2->type == pdevc1->type &&
        pdevc1->ccolor.pattern == pdevc2->ccolor.pattern;
}

Project: ghostpdl
File: gsptype2.c

static ENUM_PTRS_BEGIN(pattern2_instance_enum_ptrs) {
    if (index < st_pattern2_template_max_ptrs) {
        gs_ptr_type_t ptype =
            ENUM_SUPER_ELT(gs_pattern2_instance_t, st_pattern2_template,
                           templat, 0);

        if (ptype)
            return ptype;
        return ENUM_OBJ(NULL);  /* don't stop early */
    }
    ENUM_PREFIX(st_pattern_instance, st_pattern2_template_max_ptrs);
}

Project: ghostpdl
File: gsptype2.c

gs_pattern2_set_shfill(gs_client_color * pcc)
{
    gs_pattern2_instance_t *pinst;

    if (pcc->pattern->type != &gs_pattern2_type)
        return_error(gs_error_unregistered); /* Must not happen. */
    pinst = (gs_pattern2_instance_t *)pcc->pattern;
    pinst->shfill = true;
    return 0;
}

Project: ghostpdl
File: gsptype2.c

static RELOC_PTRS_BEGIN(pattern2_instance_reloc_ptrs) {
    RELOC_PREFIX(st_pattern_instance);
    RELOC_SUPER(gs_pattern2_instance_t, st_pattern2_template, templat);
} RELOC_PTRS_END

Project: ghostpdl
File: gsptype2.c

gs_pattern2_get_pattern(const gs_pattern_instance_t *pinst)
{
    return (const gs_pattern_template_t *)
        &((const gs_pattern2_instance_t *)pinst)->templat;
}

Project: ghostpdl
File: gsptype2.c

gx_dc_pattern2_is_rectangular_cell(const gx_device_color * pdevc, gx_device * pdev, gs_fixed_rect *rect)
{
    if (gx_dc_is_pattern2_color(pdevc) && gx_dc_pattern2_color_has_bbox(pdevc) &&
            (*dev_proc(pdev, dev_spec_op))(pdev, gxdso_pattern_shading_area, NULL, 0) == 0) {
        gs_pattern2_instance_t *pinst = (gs_pattern2_instance_t *)pdevc->ccolor.pattern;
        const gs_shading_t *psh = pinst->templat.Shading;
        gs_fixed_point p, q;

        if (is_xxyy(&ctm_only(pinst->saved)))
            if (psh->params.have_BBox) {
                int code = gs_point_transform2fixed(&pinst->saved->ctm,
                            psh->params.BBox.p.x, psh->params.BBox.p.y, &p);
                if (code < 0)
                    return code;
                code = gs_point_transform2fixed(&pinst->saved->ctm,
                            psh->params.BBox.q.x, psh->params.BBox.q.y, &q);
                if (code < 0)
                    return code;
                if (p.x > q.x) {
                    p.x ^= q.x; q.x ^= p.x; p.x ^= q.x;
                }
                if (p.y > q.y) {
                    p.y ^= q.y; q.y ^= p.y; p.y ^= q.y;
                }
                rect->p = p;
                rect->q = q;
                return 1;
            }
    }
    return 0;
}

Project: ghostpdl
File: gsptype2.c

gx_dc_pattern2_get_dev_halftone(const gx_device_color * pdevc)
{
    return ((gs_pattern2_instance_t *)pdevc->ccolor.pattern)->saved->dev_ht;
}

Project: ghostpdl
File: gsptype2.c

bool gx_dc_pattern2_has_background(const gx_device_color *pdevc)
{
    gs_pattern2_instance_t * pinst;
    const gs_shading_t *Shading;

    if (pdevc->type != &gx_dc_pattern2)
        return false;
    pinst = (gs_pattern2_instance_t *)pdevc->ccolor.pattern;
    Shading = pinst->templat.Shading;
    return !pinst->shfill && Shading->params.Background != NULL;
}

Project: ghostpdl
File: gsptype2.c

gx_dc_shading_path_add_box(gx_path *ppath, const gx_device_color * pdevc)
{
    gs_pattern2_instance_t *pinst = (gs_pattern2_instance_t *)pdevc->ccolor.pattern;
    const gs_shading_t *psh = pinst->templat.Shading;

    if (!psh->params.have_BBox)
        return_error(gs_error_unregistered); /* Do not call in this case. */
    else {
        gs_gstate *pgs = pinst->saved;

        return gs_shading_path_add_box(ppath, &psh->params.BBox, &pgs->ctm);
    }
}

Project: ghostpdl
File: gsptype2.c

gx_dc_pattern2_fill_rectangle(const gx_device_color * pdevc, int x, int y,
                              int w, int h, gx_device * dev,
                              gs_logical_operation_t lop,
                              const gx_rop_source_t * source)
{
    if (dev_proc(dev, dev_spec_op)(dev, gxdso_pattern_is_cpath_accum, NULL, 0)) {
        /* Performing a conversion of imagemask into a clipping path.
           Fall back to the device procedure. */
        return dev_proc(dev, fill_rectangle)(dev, x, y, w, h, (gx_color_index)0/*any*/);
    } else {
        gs_fixed_rect rect;
        gs_pattern2_instance_t *pinst =
            (gs_pattern2_instance_t *)pdevc->ccolor.pattern;

        rect.p.x = int2fixed(x);
        rect.p.y = int2fixed(y);
        rect.q.x = int2fixed(x + w);
        rect.q.y = int2fixed(y + h);
        return gs_shading_do_fill_rectangle(pinst->templat.Shading, &rect, dev,
                                    (gs_gstate *)pinst->saved, !pinst->shfill);
    }
}

Project: ghostpdl
File: gsptype2.c

gs_pattern2_uses_base_space(const gs_pattern_template_t *ptemp)
{
    return false;
}

Project: ghostpdl
File: gsptype2.c

gx_dc_pattern2_color_has_bbox(const gx_device_color * pdevc)
{
    gs_pattern2_instance_t *pinst = (gs_pattern2_instance_t *)pdevc->ccolor.pattern;
    const gs_shading_t *psh = pinst->templat.Shading;

    return psh->params.have_BBox;
}

Project: ghostpdl
File: gsptype2.c

gx_dc_pattern2_shade_bbox_transform2fixed(const gs_rect * rect, const gs_gstate * pgs,
                           gs_fixed_rect * rfixed)
{
    gs_rect dev_rect;
    int code = gs_bbox_transform(rect, &ctm_only(pgs), &dev_rect);

    if (code >= 0) {
        rfixed->p.x = float2fixed(dev_rect.p.x);
        rfixed->p.y = float2fixed(dev_rect.p.y);
        rfixed->q.x = float2fixed(dev_rect.q.x);
        rfixed->q.y = float2fixed(dev_rect.q.y);
    }
    return code;
}

Project: ghostpdl
File: gsptype2.c

gs_pattern2_remap_color(const gs_client_color * pc, const gs_color_space * pcs,
                        gx_device_color * pdc, const gs_gstate * pgs,
                        gx_device * dev, gs_color_select_t select)
{
    /* We don't do any actual color mapping now. */
    pdc->type = &gx_dc_pattern2;
    pdc->ccolor = *pc;
    pdc->ccolor_valid = true;
    return 0;
}

Project: ghostpdl
File: gsptype2.c

gs_pattern2_set_color(const gs_client_color * pcc, gs_gstate * pgs)
{
    gs_pattern2_instance_t * pinst = (gs_pattern2_instance_t *)pcc->pattern;
    gs_color_space * pcs = pinst->templat.Shading->params.ColorSpace;
    int code;
    uchar k, num_comps;

    pinst->saved->overprint_mode = pgs->overprint_mode;
    pinst->saved->overprint = pgs->overprint;

    num_comps = pgs->device->color_info.num_components;
    for (k = 0; k < num_comps; k++) {
        pgs->color_component_map.color_map[k] =
            pinst->saved->color_component_map.color_map[k];
    }
    code = pcs->type->set_overprint(pcs, pgs);
    return code;
}

Project: ghostpdl
File: gsptype2.c

gx_dc_pattern2_get_color_space(const gx_device_color * pdevc)
{
    gs_pattern2_instance_t *pinst =
        (gs_pattern2_instance_t *)pdevc->ccolor.pattern;
    const gs_shading_t *psh = pinst->templat.Shading;

    return psh->params.ColorSpace;
}

Project: ghostpdl
File: gsptype2.c

gx_dc_pattern2_clip_with_bbox(const gx_device_color * pdevc, gx_device * pdev,
                              gx_clip_path *cpath_local, const gx_clip_path **ppcpath1)
{
    if (gx_dc_is_pattern2_color(pdevc) && gx_dc_pattern2_color_has_bbox(pdevc) &&
            (*dev_proc(pdev, dev_spec_op))(pdev, gxdso_pattern_shading_area, NULL, 0) == 0) {
        gs_pattern2_instance_t *pinst = (gs_pattern2_instance_t *)pdevc->ccolor.pattern;
        gx_path box_path;
        gs_memory_t *mem = (*ppcpath1 != NULL ? (*ppcpath1)->path.memory : pdev->memory);
        int code;

        gx_path_init_local(&box_path, mem);
        code = gx_dc_shading_path_add_box(&box_path, pdevc);
        if (code != gs_error_limitcheck) {
            /* Ignore huge BBox causing limitcheck - bug 689027. */
            if (code >= 0) {
                gx_cpath_init_local_shared(cpath_local, *ppcpath1, mem);
                code = gx_cpath_intersect(cpath_local, &box_path, gx_rule_winding_number, (gs_gstate *)pinst->saved);
                if (code < 0) {
                    gx_path_free(&box_path, "gx_default_fill_path(path_bbox)");
                    return code;
                }
                *ppcpath1 = cpath_local;
            }
        }
        gx_path_free(&box_path, "gx_default_fill_path(path_bbox)");
    }
    return 0;
}

Project: ghostpdl
File: gsptype2.c

gs_pattern2_init(gs_pattern2_template_t * ppat)
{
    gs_pattern_common_init((gs_pattern_template_t *)ppat, &gs_pattern2_type);
}

Project: ghostpdl
File: gsptype2.c

gx_dc_pattern2_get_bbox(const gx_device_color * pdevc, gs_fixed_rect *bbox)
{
    gs_pattern2_instance_t *pinst =
        (gs_pattern2_instance_t *)pdevc->ccolor.pattern;
    int code;

    if (!pinst->templat.Shading->params.have_BBox)
        return 0;
    code = gx_dc_pattern2_shade_bbox_transform2fixed(
                &pinst->templat.Shading->params.BBox, (gs_gstate *)pinst->saved, bbox);
    if (code < 0)
        return code;
    return 1;
}

Project: ghostpdl
File: gsptype2.c

gx_dc_pattern2_can_overlap(const gx_device_color *pdevc)
{
    gs_pattern2_instance_t * pinst;

    if (pdevc->type != &gx_dc_pattern2)
        return false;
    pinst = (gs_pattern2_instance_t *)pdevc->ccolor.pattern;
    switch (pinst->templat.Shading->head.type) {
        case 3: case 6: case 7:
            return true;
        default:
            return false;
    }
}

Project: ghostpdl
File: gsptype2.c

gx_dc_pattern2_clip_with_bbox_simple(const gx_device_color * pdevc, gx_device * pdev,
                              gx_clip_path *cpath_local)
{
    int code = 0;

    if (gx_dc_is_pattern2_color(pdevc) && gx_dc_pattern2_color_has_bbox(pdevc) &&
            (*dev_proc(pdev, dev_spec_op))(pdev, gxdso_pattern_shading_area, NULL, 0) == 0) {
        gs_pattern2_instance_t *pinst = (gs_pattern2_instance_t *)pdevc->ccolor.pattern;
        gx_path box_path;
        gs_memory_t *mem = cpath_local->path.memory;

        gx_path_init_local(&box_path, mem);
        code = gx_dc_shading_path_add_box(&box_path, pdevc);
        if (code == gs_error_limitcheck) {
            /* Ignore huge BBox - bug 689027. */
            code = 0;
        } else if (code >= 0) {
            code = gx_cpath_intersect(cpath_local, &box_path, gx_rule_winding_number, (gs_gstate *)pinst->saved);
        }
        gx_path_free(&box_path, "gx_default_fill_path(path_bbox)");
    }
    return code;
}

Project: ghostpdl
File: gsptype2.c

gs_pattern2_make_pattern(gs_client_color * pcc,
                         const gs_pattern_template_t * pcp,
                         const gs_matrix * pmat, gs_gstate * pgs,
                         gs_memory_t * mem)
{
    const gs_pattern2_template_t *ptemp =
        (const gs_pattern2_template_t *)pcp;
    int code = gs_make_pattern_common(pcc, pcp, pmat, pgs, mem,
                                      &st_pattern2_instance);
    gs_pattern2_instance_t *pinst;

    if (code < 0)
        return code;
    pinst = (gs_pattern2_instance_t *)pcc->pattern;
    pinst->templat = *ptemp;
    pinst->shfill = false;
    return 0;
}

Project: ghostpdl
File: gsptype2.c

gx_dc_pattern2_save_dc(
    const gx_device_color * pdevc,
    gx_device_color_saved * psdc )
{
    gs_pattern2_instance_t * pinst = (gs_pattern2_instance_t *)pdevc->ccolor.pattern;

    psdc->type = pdevc->type;
    psdc->colors.pattern2.id = pinst->pattern_id;
    psdc->colors.pattern2.shfill = pinst->shfill;
}