Commit message: Bug 699718(2): Improve/augment stack size checking

Improve the rebustness of the previous solution (previously it could trigger an
error when there *was* stack capacity available).

Remove redundant check: we don't need to check if the *current* stack size is
sufficient, before checking the maximum permitted stack size.

Also check the exec stack, as execstackoverflow can also cause the
Postscript call out to fail.

Lastly, in event of failure, put the LockSafetyParams flag back in the existing
device (this is only necessary because we don't enfore JOBSERVER mode).

Note: the Postscript callout (%grestorepagedevice) never pushes any dictionaries
on the dict stack - if that changes, we should check that stack, too.
CVE: None
Vulnerability description: None

Project: ghostpdl
File: zdevice2.c

z2save(i_ctx_t *i_ctx_p)
{
    if (!save_page_device(igs))
        return zsave(i_ctx_p);
    return push_callout(i_ctx_p, "%savepagedevice");
}

Project: ghostpdl
File: zdevice2.c

z2setgstate(i_ctx_t *i_ctx_p)
{
    os_ptr op = osp;
    int code;

    check_stype(*op, st_igstate_obj);
    code = restore_page_device(i_ctx_p, igs, igstate_ptr(op));
    if (code < 0) return code;
    if (code == 0)
        return zsetgstate(i_ctx_p);
    return push_callout(i_ctx_p, "%setgstatepagedevice");
}

Project: ghostpdl
File: zdevice2.c

z2gstate(i_ctx_t *i_ctx_p)
{
    if (!save_page_device(igs))
        return zgstate(i_ctx_p);
    return push_callout(i_ctx_p, "%gstatepagedevice");
}

Project: ghostpdl
File: zdevice2.c

z2grestore(i_ctx_t *i_ctx_p)
{
    int code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
    if (code < 0) return code;

    if (code == 0)
        return gs_grestore(igs);
    return push_callout(i_ctx_p, "%grestorepagedevice");
}

Project: ghostpdl
File: zdevice2.c

zcurrentshowpagecount(i_ctx_t *i_ctx_p)
{
    os_ptr op = osp;
    gx_device *dev1, *dev = gs_currentdevice(igs);

    if ((*dev_proc(dev, get_page_device))(dev) == 0) {
        push(1);
        make_false(op);
    } else {
        dev1 = (*dev_proc(dev, get_page_device))(dev);
        push(2);
        make_int(op - 1, dev1->ShowpageCount);
        make_true(op);
    }
    return 0;
}

Project: ghostpdl
File: zdevice2.c

z2grestoreall(i_ctx_t *i_ctx_p)
{
    for (;;) {
        int code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
        if (code < 0) return code;
        if (code == 0) {
            bool done = !gs_gstate_saved(gs_gstate_saved(igs));

            gs_grestore(igs);
            if (done)
                break;
        } else
            return push_callout(i_ctx_p, "%grestoreallpagedevice");
    }
    return 0;
}

Project: ghostpdl
File: zdevice2.c

zsetpagedevice(i_ctx_t *i_ctx_p)
{
    os_ptr op = osp;
    int code;

/******
    if ( igs->in_cachedevice )
        return_error(gs_error_undefined);
 ******/
    if (r_has_type(op, t_dictionary)) {
        check_dict_read(*op);
#if 0	/****************/
        /*
         * In order to avoid invalidaccess errors on setpagedevice,
         * the dictionary must be allocated in local VM.
         */
        if (!(r_is_local(op)))
            return_error(gs_error_invalidaccess);
#endif	/****************/
        /* Make the dictionary read-only. */
        code = zreadonly(i_ctx_p);
        if (code < 0)
            return code;
    } else {
        check_type(*op, t_null);
    }
    istate->pagedevice = *op;
    pop(1);
    return 0;
}

Project: ghostpdl
File: zdevice2.c

save_page_device(gs_gstate *pgs)
{
    return
        (r_has_type(&gs_int_gstate(pgs)->pagedevice, t_null) &&
         (*dev_proc(gs_currentdevice(pgs), get_page_device))(gs_currentdevice(pgs)) != 0);
}

Project: ghostpdl
File: zdevice2.c

zcallbeginpage(i_ctx_t *i_ctx_p)
{
    os_ptr op = osp;
    gx_device *dev = gs_currentdevice(igs);

    check_type(*op, t_integer);
    if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) {
        int code = (*dev->page_procs.begin_page)(dev, igs);

        if (code < 0)
            return code;
    }
    pop(1);
    return 0;
}

Project: ghostpdl
File: zdevice2.c

z2currentgstate(i_ctx_t *i_ctx_p)
{
    if (!save_page_device(igs))
        return zcurrentgstate(i_ctx_p);
    return push_callout(i_ctx_p, "%currentgstatepagedevice");
}

Project: ghostpdl
File: zdevice2.c

zcurrentpagedevice(i_ctx_t *i_ctx_p)
{
    os_ptr op = osp;
    gx_device *dev = gs_currentdevice(igs);

    push(2);
    if ((*dev_proc(dev, get_page_device))(dev) != 0) {
        op[-1] = istate->pagedevice;
        make_true(op);
    } else {
        make_null(op - 1);
        make_false(op);
    }
    return 0;
}

Project: ghostpdl
File: zdevice2.c

z2restore(i_ctx_t *i_ctx_p)
{
    alloc_save_t *asave;
    bool saveLockSafety = gs_currentdevice_inline(igs)->LockSafetyParams;
    int code = restore_check_save(i_ctx_p, &asave);

    if (code < 0) return code;

    while (gs_gstate_saved(gs_gstate_saved(igs))) {
        code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
        if (code < 0) return code;
        if (code > 0)
            return push_callout(i_ctx_p, "%restore1pagedevice");
        gs_grestore(igs);
    }
    code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
    if (code < 0) return code;
    if (code > 0)
        return push_callout(i_ctx_p, "%restorepagedevice");

    code = dorestore(i_ctx_p, asave);

    if (code < 0) {
        /* An error here is basically fatal, but....
           restore_page_device() has to set LockSafetyParams false so it can
           configure the restored device correctly - in normal operation, that
           gets reset by that configuration. If we hit an error, though, that
           may not happen -  at least ensure we keep the setting through the
           error.
         */
        gs_currentdevice_inline(igs)->LockSafetyParams = saveLockSafety;
    }
    return code;
}

Project: ghostpdl
File: zdevice2.c

z2copy_gstate(i_ctx_t *i_ctx_p)
{
    if (!save_page_device(igs))
        return zcopy_gstate(i_ctx_p);
    return push_callout(i_ctx_p, "%copygstatepagedevice");
}

Project: ghostpdl
File: zdevice2.c

z2gsave(i_ctx_t *i_ctx_p)
{
    if (!save_page_device(igs))
        return gs_gsave(igs);
    return push_callout(i_ctx_p, "%gsavepagedevice");
}

Project: ghostpdl
File: zdevice2.c

z2copy(i_ctx_t *i_ctx_p)
{
    os_ptr op = osp;
    int code = zcopy(i_ctx_p);

    if (code >= 0)
        return code;
    if (!r_has_type(op, t_astruct))
        return code;
    return z2copy_gstate(i_ctx_p);
}

Project: ghostpdl
File: zdevice2.c

zcallendpage(i_ctx_t *i_ctx_p)
{
    os_ptr op = osp;
    gx_device *dev = gs_currentdevice(igs);
    int code;

    check_type(op[-1], t_integer);
    check_type(*op, t_integer);
    if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) {
        code = (*dev->page_procs.end_page)(dev, (int)op->value.intval, igs);
        if (code < 0)
            return code;
        if (code > 1)
            return_error(gs_error_rangecheck);
    } else {
        code = (op->value.intval == 2 ? 0 : 1);
    }
    make_bool(op - 1, code);
    pop(1);
    return 0;
}

Project: ghostpdl
File: zdevice2.c

zcallinstall(i_ctx_t *i_ctx_p)
{
    gx_device *dev = gs_currentdevice(igs);

    if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) {
        int code = (*dev->page_procs.install) (dev, igs);

        if (code < 0)
            return code;
    }
    return 0;
}

Project: ghostpdl
File: zdevice2.c

push_callout(i_ctx_t *i_ctx_p, const char *callout_name)
{
    int code;

    check_estack(1);
    code = name_enter_string(imemory, callout_name, esp + 1);
    if (code < 0)
        return code;
    ++esp;
    r_set_attrs(esp, a_executable);
    return o_push_estack;
}