Commit message: Bug 702582, CVE 2020-15900 Memory Corruption in Ghostscript 9.52

Fix the 'rsearch' calculation for the 'post' size to give the correct
size.  Previous calculation would result in a size that was too large,
and could underflow to max uint32_t. Also fix 'rsearch' to return the
correct 'pre' string with empty string match.

A future change may 'undefine' this undocumented, non-standard operator
during initialization as we do with the many other non-standard internal
PostScript operators and procedures.
CVE: CVE-2020-15900
CWE: CWE-787
Vulnerability description: A memory corruption issue was found in Artifex Ghostscript 9.50 and 9.52. Use of a non-standard PostScript operator can allow overriding of file access controls. The 'rsearch' calculation for the 'post' size resulted in a size that was too large, and could underflow to max uint32_t. This was fixed in commit 5d499272b95a6b890a1397e11d20937de000d31b.

Project: ghostpdl
File: zstring.c

search_impl(i_ctx_t *i_ctx_p, bool forward)
{
    os_ptr op = osp;
    os_ptr op1 = op - 1;
    uint size = r_size(op);
    uint count;
    byte *pat;
    byte *ptr;
    byte ch;
    int incr = forward ? 1 : -1;

    check_read_type(*op1, t_string);
    check_read_type(*op, t_string);
    if (size > r_size(op1)) {	/* can't match */
        make_false(op);
        return 0;
    }
    count = r_size(op1) - size;
    ptr = op1->value.bytes;
    if (size == 0)
        goto found;
    if (!forward)
        ptr += count;
    pat = op->value.bytes;
    ch = pat[0];
    do {
        if (*ptr == ch && (size == 1 || !memcmp(ptr, pat, size)))
            goto found;
        ptr += incr;
    }
    while (count--);
    /* No match */
    make_false(op);
    return 0;
found:
    op->tas.type_attrs = op1->tas.type_attrs;
    op->value.bytes = ptr;
    r_set_size(op, size);
    push(2);
    op[-1] = *op1;
    r_set_size(op - 1, ptr - op[-1].value.bytes);
    op1->value.bytes = ptr + size;
    r_set_size(op1, count + (!forward ? (size - 1) : 0));
    make_true(op);
    return 0;
}

Project: ghostpdl
File: zstring.c

search_impl(i_ctx_t *i_ctx_p, bool forward)
{
    os_ptr op = osp;
    os_ptr op1 = op - 1;
    uint size = r_size(op);
    uint count;
    byte *pat;
    byte *ptr;
    byte ch;
    int incr = forward ? 1 : -1;

    check_read_type(*op1, t_string);
    check_read_type(*op, t_string);
    if (size > r_size(op1)) {	/* can't match */
        make_false(op);
        return 0;
    }
    count = r_size(op1) - size;
    ptr = op1->value.bytes;
    if (size == 0)
        goto found;
    if (!forward)
        ptr += count;
    pat = op->value.bytes;
    ch = pat[0];
    do {
        if (*ptr == ch && (size == 1 || !memcmp(ptr, pat, size)))
            goto found;
        ptr += incr;
    }
    while (count--);
    /* No match */
    make_false(op);
    return 0;
found:
    op->tas.type_attrs = op1->tas.type_attrs;
    op->value.bytes = ptr;				/* match */
    op->tas.rsize = size;				/* match */
    push(2);
    op[-1] = *op1;					/* pre */
    op[-3].value.bytes = ptr + size;			/* post */
    if (forward) {
        op[-1].tas.rsize = ptr - op[-1].value.bytes;	/* pre */
        op[-3].tas.rsize = count;			/* post */
    } else {
        op[-1].tas.rsize = count;			/* pre */
        op[-3].tas.rsize -= count + size;		/* post */
    }
    make_true(op);
    return 0;
}