Commit message: Faster routines for jpeg decoding

linux 'perf' indicated a bottleneck in getChars
CVE: CVE-2020-35702
Vulnerability description: DCTStream::getChars in DCTStream.cc in Poppler 20.12.1 has a heap-based buffer overflow via a crafted PDF document. NOTE: later reports indicate that this only affects builds from Poppler git clones in late December 2020, not the 20.12.1 release. In this situation, it should NOT be considered a Poppler vulnerability. However, several third-party Open Source projects directly rely on Poppler git clones made at arbitrary times, and therefore the CVE remains useful to users of those projects

Project: poppler
File: GfxState.cc

void GfxState::concatCTM(double a, double b, double c, double d, double e, double f)
{
    double a1 = ctm[0];
    double b1 = ctm[1];
    double c1 = ctm[2];
    double d1 = ctm[3];

    ctm[0] = a * a1 + b * c1;
    ctm[1] = a * b1 + b * d1;
    ctm[2] = c * a1 + d * c1;
    ctm[3] = c * b1 + d * d1;
    ctm[4] = e * a1 + f * c1 + ctm[4];
    ctm[5] = e * b1 + f * d1 + ctm[5];
}

Project: poppler
File: GfxState.cc

void GfxDeviceNColorSpace::getGray(const GfxColor *color, GfxGray *gray) const
{
    double x[gfxColorMaxComps], c[gfxColorMaxComps];
    GfxColor color2;
    int i;

    for (i = 0; i < nComps; ++i) {
        x[i] = colToDbl(color->c[i]);
    }
    func->transform(x, c);
    for (i = 0; i < alt->getNComps(); ++i) {
        color2.c[i] = dblToCol(c[i]);
    }
    alt->getGray(&color2, gray);
}

Project: poppler
File: GfxState.cc

GfxSeparationColorSpace::~GfxSeparationColorSpace()
{
    delete name;
    delete alt;
    delete func;
    if (mapping != nullptr)
        gfree(mapping);
}

Project: poppler
File: GfxState.cc

void GfxDeviceNColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const
{
    clearGfxColor(deviceN);
    if (mapping == nullptr) {
        GfxCMYK cmyk;

        getCMYK(color, &cmyk);
        deviceN->c[0] = cmyk.c;
        deviceN->c[1] = cmyk.m;
        deviceN->c[2] = cmyk.y;
        deviceN->c[3] = cmyk.k;
    } else {
        for (int j = 0; j < nComps; j++)
            if (mapping[j] != -1)
                deviceN->c[mapping[j]] = color->c[j];
    }
}

Project: poppler
File: GfxState.cc

void GfxICCBasedColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length)
{
#ifdef USE_CMS
    if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_RGB) {
        unsigned char *tmp = (unsigned char *)gmallocn(3 * length, sizeof(unsigned char));
        lineTransform->doTransform(in, tmp, length);
        unsigned char *current = tmp;
        for (int i = 0; i < length; ++i) {
            *out++ = *current++;
            *out++ = *current++;
            *out++ = *current++;
        }
        gfree(tmp);
    } else if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_CMYK) {
        unsigned char *tmp = (unsigned char *)gmallocn(4 * length, sizeof(unsigned char));
        lineTransform->doTransform(in, tmp, length);
        unsigned char *current = tmp;
        double c, m, y, k, c1, m1, y1, k1, r, g, b;
        for (int i = 0; i < length; ++i) {
            c = byteToDbl(*current++);
            m = byteToDbl(*current++);
            y = byteToDbl(*current++);
            k = byteToDbl(*current++);
            c1 = 1 - c;
            m1 = 1 - m;
            y1 = 1 - y;
            k1 = 1 - k;
            cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
            *out++ = dblToByte(r);
            *out++ = dblToByte(g);
            *out++ = dblToByte(b);
        }
        gfree(tmp);
    } else {
        alt->getRGBLine(in, out, length);
    }
#else
    alt->getRGBLine(in, out, length);
#endif
}

Project: poppler
File: GfxState.cc

GfxTilingPattern *GfxTilingPattern::parse(Object *patObj, int patternRefNum)
{
    Dict *dict;
    int paintTypeA, tilingTypeA;
    double bboxA[4], matrixA[6];
    double xStepA, yStepA;
    Object resDictA;
    Object obj1;
    int i;

    if (!patObj->isStream()) {
        return nullptr;
    }
    dict = patObj->streamGetDict();

    obj1 = dict->lookup("PaintType");
    if (obj1.isInt()) {
        paintTypeA = obj1.getInt();
    } else {
        paintTypeA = 1;
        error(errSyntaxWarning, -1, "Invalid or missing PaintType in pattern");
    }
    obj1 = dict->lookup("TilingType");
    if (obj1.isInt()) {
        tilingTypeA = obj1.getInt();
    } else {
        tilingTypeA = 1;
        error(errSyntaxWarning, -1, "Invalid or missing TilingType in pattern");
    }
    bboxA[0] = bboxA[1] = 0;
    bboxA[2] = bboxA[3] = 1;
    obj1 = dict->lookup("BBox");
    if (obj1.isArray() && obj1.arrayGetLength() == 4) {
        for (i = 0; i < 4; ++i) {
            Object obj2 = obj1.arrayGet(i);
            if (obj2.isNum()) {
                bboxA[i] = obj2.getNum();
            }
        }
    } else {
        error(errSyntaxWarning, -1, "Invalid or missing BBox in pattern");
    }
    obj1 = dict->lookup("XStep");
    if (obj1.isNum()) {
        xStepA = obj1.getNum();
    } else {
        xStepA = 1;
        error(errSyntaxWarning, -1, "Invalid or missing XStep in pattern");
    }
    obj1 = dict->lookup("YStep");
    if (obj1.isNum()) {
        yStepA = obj1.getNum();
    } else {
        yStepA = 1;
        error(errSyntaxWarning, -1, "Invalid or missing YStep in pattern");
    }
    resDictA = dict->lookup("Resources");
    if (!resDictA.isDict()) {
        error(errSyntaxWarning, -1, "Invalid or missing Resources in pattern");
    }
    matrixA[0] = 1;
    matrixA[1] = 0;
    matrixA[2] = 0;
    matrixA[3] = 1;
    matrixA[4] = 0;
    matrixA[5] = 0;
    obj1 = dict->lookup("Matrix");
    if (obj1.isArray() && obj1.arrayGetLength() == 6) {
        for (i = 0; i < 6; ++i) {
            Object obj2 = obj1.arrayGet(i);
            if (obj2.isNum()) {
                matrixA[i] = obj2.getNum();
            }
        }
    }

    return new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA, &resDictA, matrixA, patObj, patternRefNum);
}

Project: poppler
File: GfxState.cc

static inline void GfxDeviceCMYKColorSpacegetRGBLineHelper(unsigned char *&in, double &r, double &g, double &b)
{
    double c, m, y, k, c1, m1, y1, k1;

    c = byteToDbl(*in++);
    m = byteToDbl(*in++);
    y = byteToDbl(*in++);
    k = byteToDbl(*in++);
    c1 = 1 - c;
    m1 = 1 - m;
    y1 = 1 - y;
    k1 = 1 - k;
    cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
}

Project: poppler
File: GfxState.cc

void GfxFunctionShading::getColor(double x, double y, GfxColor *color) const
{
    double in[2], out[gfxColorMaxComps];

    // NB: there can be one function with n outputs or n functions with
    // one output each (where n = number of color components)
    for (double &i : out) {
        i = 0;
    }
    in[0] = x;
    in[1] = y;
    for (int i = 0; i < getNFuncs(); ++i) {
        if (likely(funcs[i]->getInputSize() <= 2)) {
            funcs[i]->transform(in, &out[i]);
        } else {
            error(errSyntaxWarning, -1, "GfxFunctionShading::getColor: function with input size > 2");
        }
    }
    for (int i = 0; i < gfxColorMaxComps; ++i) {
        color->c[i] = dblToCol(out[i]);
    }
}

Project: poppler
File: GfxState.cc

void GfxLabColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const
{
    double X, Y, Z;

    getXYZ(color, &X, &Y, &Z);
#ifdef USE_CMS
    if (transform != nullptr && transform->getTransformPixelType() == PT_RGB) {
        unsigned char out[gfxColorMaxComps];
        double in[gfxColorMaxComps];

        in[0] = clip01(X);
        in[1] = clip01(Y);
        in[2] = clip01(Z);
        transform->doTransform(in, out, 1);
        rgb->r = byteToCol(out[0]);
        rgb->g = byteToCol(out[1]);
        rgb->b = byteToCol(out[2]);
        return;
    } else if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) {
        unsigned char out[gfxColorMaxComps];
        double in[gfxColorMaxComps];
        double c, m, y, k, c1, m1, y1, k1, r, g, b;

        in[0] = clip01(X);
        in[1] = clip01(Y);
        in[2] = clip01(Z);
        transform->doTransform(in, out, 1);
        c = byteToDbl(out[0]);
        m = byteToDbl(out[1]);
        y = byteToDbl(out[2]);
        k = byteToDbl(out[3]);
        c1 = 1 - c;
        m1 = 1 - m;
        y1 = 1 - y;
        k1 = 1 - k;
        cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
        rgb->r = clip01(dblToCol(r));
        rgb->g = clip01(dblToCol(g));
        rgb->b = clip01(dblToCol(b));
        return;
    }
#endif
    X *= whiteX;
    Y *= whiteY;
    Z *= whiteZ;
    // convert XYZ to RGB, including gamut mapping and gamma correction
    const double r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
    const double g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
    const double b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
    rgb->r = dblToCol(sqrt(clip01(r * kr)));
    rgb->g = dblToCol(sqrt(clip01(g * kg)));
    rgb->b = dblToCol(sqrt(clip01(b * kb)));
}

Project: poppler
File: GfxState.cc

void GfxCalGrayColorSpace::getDeviceN(const GfxColor *color, GfxColor *deviceN) const
{
    GfxCMYK cmyk;
    clearGfxColor(deviceN);
    getCMYK(color, &cmyk);
    deviceN->c[0] = cmyk.c;
    deviceN->c[1] = cmyk.m;
    deviceN->c[2] = cmyk.y;
    deviceN->c[3] = cmyk.k;
}

Project: poppler
File: GfxState.cc

void GfxDeviceGrayColorSpace::getCMYKLine(unsigned char *in, unsigned char *out, int length)
{
    for (int i = 0; i < length; i++) {
        *out++ = 0;
        *out++ = 0;
        *out++ = 0;
        *out++ = in[i];
    }
}

Project: poppler
File: GfxState.cc

void GfxIndexedColorSpace::getRGB(const GfxColor *color, GfxRGB *rgb) const
{
    GfxColor color2;

    base->getRGB(mapColorToBase(color, &color2), rgb);
}

Project: poppler
File: GfxState.cc

char *GfxICCBasedColorSpace::getPostScriptCSA()
{
#    if LCMS_VERSION >= 2070
    // The runtime version check of lcms2 is only available from release 2.7 upwards.
    // The generation of the CSA code only works reliably for version 2.10 and upwards.
    // Cf. the explanation in the corresponding lcms2 merge request [1], and the original mail thread [2].
    // [1] https://github.com/mm2/Little-CMS/pull/214
    // [2] https://sourceforge.net/p/lcms/mailman/message/33182987/
    if (cmsGetEncodedCMMversion() < 2100)
        return nullptr;

    int size;

    if (psCSA)
        return psCSA;

    if (!profile) {
        error(errSyntaxWarning, -1, "profile is nullptr");
        return nullptr;
    }

    void *rawprofile = profile.get();
    size = cmsGetPostScriptCSA(cmsGetProfileContextID(rawprofile), rawprofile, getIntent(), 0, nullptr, 0);
    if (size == 0) {
        error(errSyntaxWarning, -1, "PostScript CSA is nullptr");
        return nullptr;
    }

    psCSA = (char *)gmalloc(size + 1);
    cmsGetPostScriptCSA(cmsGetProfileContextID(rawprofile), rawprofile, getIntent(), 0, psCSA, size);
    psCSA[size] = 0;

    // TODO REMOVE-ME-IN-THE-FUTURE
    // until we can depend on https://github.com/mm2/Little-CMS/issues/223 being fixed
    // lcms returns ps code with , instead of . for some locales. The lcms author says
    // that there's no room for any , in the rest of the ps code, so replacing all the , with .
    // is an "acceptable" workaround
    for (int i = 0; i < size; ++i) {
        if (psCSA[i] == ',')
            psCSA[i] = '.';
    }

    return psCSA;
#    else
    return nullptr;
#    endif
}

Project: poppler
File: GfxState.cc

GfxImageColorMap::~GfxImageColorMap()
{
    int i;

    delete colorSpace;
    for (i = 0; i < gfxColorMaxComps; ++i) {
        gfree(lookup[i]);
        gfree(lookup2[i]);
    }
    gfree(byte_lookup);
}

Project: poppler
File: GfxState.cc

void GfxState::setDisplayProfile(const GfxLCMSProfilePtr &localDisplayProfileA)
{
    localDisplayProfile = localDisplayProfileA;
    if (localDisplayProfile) {
        cmsHTRANSFORM transform;
        unsigned int nChannels;
        unsigned int localDisplayPixelType;

        localDisplayPixelType = getCMSColorSpaceType(cmsGetColorSpace(localDisplayProfile.get()));
        nChannels = getCMSNChannels(cmsGetColorSpace(localDisplayProfile.get()));
        // create transform from XYZ
        if ((transform = cmsCreateTransform(XYZProfile.get(), TYPE_XYZ_DBL, localDisplayProfile.get(), COLORSPACE_SH(localDisplayPixelType) | CHANNELS_SH(nChannels) | BYTES_SH(1), INTENT_RELATIVE_COLORIMETRIC, LCMS_FLAGS)) == nullptr) {
            error(errSyntaxWarning, -1, "Can't create Lab transform");
        } else {
            XYZ2DisplayTransformRelCol = std::make_shared<GfxColorTransform>(transform, INTENT_RELATIVE_COLORIMETRIC, PT_XYZ, localDisplayPixelType);
        }

        if ((transform = cmsCreateTransform(XYZProfile.get(), TYPE_XYZ_DBL, localDisplayProfile.get(), COLORSPACE_SH(localDisplayPixelType) | CHANNELS_SH(nChannels) | BYTES_SH(1), INTENT_ABSOLUTE_COLORIMETRIC, LCMS_FLAGS)) == nullptr) {
            error(errSyntaxWarning, -1, "Can't create Lab transform");
        } else {
            XYZ2DisplayTransformAbsCol = std::make_shared<GfxColorTransform>(transform, INTENT_ABSOLUTE_COLORIMETRIC, PT_XYZ, localDisplayPixelType);
        }

        if ((transform = cmsCreateTransform(XYZProfile.get(), TYPE_XYZ_DBL, localDisplayProfile.get(), COLORSPACE_SH(localDisplayPixelType) | CHANNELS_SH(nChannels) | BYTES_SH(1), INTENT_SATURATION, LCMS_FLAGS)) == nullptr) {
            error(errSyntaxWarning, -1, "Can't create Lab transform");
        } else {
            XYZ2DisplayTransformSat = std::make_shared<GfxColorTransform>(transform, INTENT_SATURATION, PT_XYZ, localDisplayPixelType);
        }

        if ((transform = cmsCreateTransform(XYZProfile.get(), TYPE_XYZ_DBL, localDisplayProfile.get(), COLORSPACE_SH(localDisplayPixelType) | CHANNELS_SH(nChannels) | BYTES_SH(1), INTENT_PERCEPTUAL, LCMS_FLAGS)) == nullptr) {
            error(errSyntaxWarning, -1, "Can't create Lab transform");
        } else {
            XYZ2DisplayTransformPerc = std::make_shared<GfxColorTransform>(transform, INTENT_PERCEPTUAL, PT_XYZ, localDisplayPixelType);
        }
    }
}

Project: poppler
File: GfxState.cc

void GfxImageColorMap::getGrayLine(unsigned char *in, unsigned char *out, int length)
{
    int i, j;
    unsigned char *inp, *tmp_line;

    if ((colorSpace2 && !colorSpace2->useGetGrayLine()) || (!colorSpace2 && !colorSpace->useGetGrayLine())) {
        GfxGray gray;

        inp = in;
        for (i = 0; i < length; i++) {
            getGray(inp, &gray);
            out[i] = colToByte(gray);
            inp += nComps;
        }
        return;
    }

    switch (colorSpace->getMode()) {
    case csIndexed:
    case csSeparation:
        tmp_line = (unsigned char *)gmallocn(length, nComps2);
        for (i = 0; i < length; i++) {
            for (j = 0; j < nComps2; j++) {
                unsigned char c = in[i];
                if (byte_lookup)
                    c = byte_lookup[c * nComps2 + j];
                tmp_line[i * nComps2 + j] = c;
            }
        }
        colorSpace2->getGrayLine(tmp_line, out, length);
        gfree(tmp_line);
        break;

    default:
        if (byte_lookup) {
            inp = in;
            for (j = 0; j < length; j++)
                for (i = 0; i < nComps; i++) {
                    *inp = byte_lookup[*inp * nComps + i];
                    inp++;
                }
        }
        colorSpace->getGrayLine(in, out, length);
        break;
    }
}

Project: poppler
File: GfxState.cc

double GfxRadialShading::getDistance(double sMin, double sMax) const
{
    double xMin, yMin, rMin, xMax, yMax, rMax;

    xMin = x0 + sMin * (x1 - x0);
    yMin = y0 + sMin * (y1 - y0);
    rMin = r0 + sMin * (r1 - r0);

    xMax = x0 + sMax * (x1 - x0);
    yMax = y0 + sMax * (y1 - y0);
    rMax = r0 + sMax * (r1 - r0);

    return hypot(xMax - xMin, yMax - yMin) + fabs(rMax - rMin);
}

Project: poppler
File: GfxState.cc

void GfxDeviceRGBColorSpace::getGrayLine(unsigned char *in, unsigned char *out, int length)
{
    int i;

    for (i = 0; i < length; i++) {
        out[i] = (in[i * 3 + 0] * 19595 + in[i * 3 + 1] * 38469 + in[i * 3 + 2] * 7472) / 65536;
    }
}