diff --git a/test/bug-hunting/cve/CVE-2019-10026/Function.cc b/test/bug-hunting/cve/CVE-2019-10026/Function.cc new file mode 100644 index 000000000..6d90b36d7 --- /dev/null +++ b/test/bug-hunting/cve/CVE-2019-10026/Function.cc @@ -0,0 +1,1567 @@ +//======================================================================== +// +// Function.cc +// +// Copyright 2001-2003 Glyph & Cog, LLC +// +//======================================================================== + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include +#include +#include +#include +#include "gmem.h" +#include "gmempp.h" +#include "GList.h" +#include "Object.h" +#include "Dict.h" +#include "Stream.h" +#include "Error.h" +#include "Function.h" + +//------------------------------------------------------------------------ + +// Max depth of nested functions. This is used to catch infinite +// loops in the function object structure. +#define recursionLimit 8 + +//------------------------------------------------------------------------ +// Function +//------------------------------------------------------------------------ + +Function::Function() { +} + +Function::~Function() { +} + +Function *Function::parse(Object *funcObj, int recursion) { + Function *func; + Dict *dict; + int funcType; + Object obj1; + + if (recursion > recursionLimit) { + error(errSyntaxError, -1, "Loop detected in function objects"); + return NULL; + } + + if (funcObj->isStream()) { + dict = funcObj->streamGetDict(); + } else if (funcObj->isDict()) { + dict = funcObj->getDict(); + } else if (funcObj->isName("Identity")) { + return new IdentityFunction(); + } else { + error(errSyntaxError, -1, "Expected function dictionary or stream"); + return NULL; + } + + if (!dict->lookup("FunctionType", &obj1)->isInt()) { + error(errSyntaxError, -1, "Function type is missing or wrong type"); + obj1.free(); + return NULL; + } + funcType = obj1.getInt(); + obj1.free(); + + if (funcType == 0) { + func = new SampledFunction(funcObj, dict); + } else if (funcType == 2) { + func = new ExponentialFunction(funcObj, dict); + } else if (funcType == 3) { + func = new StitchingFunction(funcObj, dict, recursion); + } else if (funcType == 4) { + func = new PostScriptFunction(funcObj, dict); + } else { + error(errSyntaxError, -1, "Unimplemented function type ({0:d})", funcType); + return NULL; + } + if (!func->isOk()) { + delete func; + return NULL; + } + + return func; +} + +GBool Function::init(Dict *dict) { + Object obj1, obj2; + int i; + + //----- Domain + if (!dict->lookup("Domain", &obj1)->isArray()) { + error(errSyntaxError, -1, "Function is missing domain"); + goto err2; + } + m = obj1.arrayGetLength() / 2; + if (m > funcMaxInputs) { + error(errSyntaxError, -1, + "Functions with more than {0:d} inputs are unsupported", + funcMaxInputs); + goto err2; + } + for (i = 0; i < m; ++i) { + obj1.arrayGet(2*i, &obj2); + if (!obj2.isNum()) { + error(errSyntaxError, -1, "Illegal value in function domain array"); + goto err1; + } + domain[i][0] = obj2.getNum(); + obj2.free(); + obj1.arrayGet(2*i+1, &obj2); + if (!obj2.isNum()) { + error(errSyntaxError, -1, "Illegal value in function domain array"); + goto err1; + } + domain[i][1] = obj2.getNum(); + obj2.free(); + } + obj1.free(); + + //----- Range + hasRange = gFalse; + n = 0; + if (dict->lookup("Range", &obj1)->isArray()) { + hasRange = gTrue; + n = obj1.arrayGetLength() / 2; + if (n > funcMaxOutputs) { + error(errSyntaxError, -1, + "Functions with more than {0:d} outputs are unsupported", + funcMaxOutputs); + goto err2; + } + for (i = 0; i < n; ++i) { + obj1.arrayGet(2*i, &obj2); + if (!obj2.isNum()) { + error(errSyntaxError, -1, "Illegal value in function range array"); + goto err1; + } + range[i][0] = obj2.getNum(); + obj2.free(); + obj1.arrayGet(2*i+1, &obj2); + if (!obj2.isNum()) { + error(errSyntaxError, -1, "Illegal value in function range array"); + goto err1; + } + range[i][1] = obj2.getNum(); + obj2.free(); + } + } + obj1.free(); + + return gTrue; + + err1: + obj2.free(); + err2: + obj1.free(); + return gFalse; +} + +//------------------------------------------------------------------------ +// IdentityFunction +//------------------------------------------------------------------------ + +IdentityFunction::IdentityFunction() { + int i; + + // fill these in with arbitrary values just in case they get used + // somewhere + m = funcMaxInputs; + n = funcMaxOutputs; + for (i = 0; i < funcMaxInputs; ++i) { + domain[i][0] = 0; + domain[i][1] = 1; + } + hasRange = gFalse; +} + +IdentityFunction::~IdentityFunction() { +} + +void IdentityFunction::transform(double *in, double *out) { + int i; + + for (i = 0; i < funcMaxOutputs; ++i) { + out[i] = in[i]; + } +} + +//------------------------------------------------------------------------ +// SampledFunction +//------------------------------------------------------------------------ + +SampledFunction::SampledFunction(Object *funcObj, Dict *dict) { + Stream *str; + int sampleBits; + double sampleMul; + Object obj1, obj2; + Guint buf, bitMask; + int bits; + Guint s; + double in[funcMaxInputs]; + int i, j, t, bit, idx; + + idxOffset = NULL; + samples = NULL; + sBuf = NULL; + ok = gFalse; + + //----- initialize the generic stuff + if (!init(dict)) { + goto err1; + } + if (!hasRange) { + error(errSyntaxError, -1, "Type 0 function is missing range"); + goto err1; + } + if (m > sampledFuncMaxInputs) { + error(errSyntaxError, -1, + "Sampled functions with more than {0:d} inputs are unsupported", + sampledFuncMaxInputs); + goto err1; + } + + //----- buffer + sBuf = (double *)gmallocn(1 << m, sizeof(double)); + + //----- get the stream + if (!funcObj->isStream()) { + error(errSyntaxError, -1, "Type 0 function isn't a stream"); + goto err1; + } + str = funcObj->getStream(); + + //----- Size + if (!dict->lookup("Size", &obj1)->isArray() || + obj1.arrayGetLength() != m) { + error(errSyntaxError, -1, "Function has missing or invalid size array"); + goto err2; + } + for (i = 0; i < m; ++i) { + obj1.arrayGet(i, &obj2); + if (!obj2.isInt()) { + error(errSyntaxError, -1, "Illegal value in function size array"); + goto err3; + } + sampleSize[i] = obj2.getInt(); + if (sampleSize[i] <= 0) { + error(errSyntaxError, -1, "Illegal non-positive value in function size array"); + goto err3; + } + obj2.free(); + } + obj1.free(); + idxOffset = (int *)gmallocn(1 << m, sizeof(int)); + for (i = 0; i < (1<= 1; --j, t <<= 1) { + if (sampleSize[j] == 1) { + bit = 0; + } else { + bit = (t >> (m - 1)) & 1; + } + idx = (idx + bit) * sampleSize[j-1]; + } + if (sampleSize[0] == 1) { + bit = 0; + } else { + bit = (t >> (m - 1)) & 1; + } + idxOffset[i] = (idx + bit) * n; + } + + //----- BitsPerSample + if (!dict->lookup("BitsPerSample", &obj1)->isInt()) { + error(errSyntaxError, -1, "Function has missing or invalid BitsPerSample"); + goto err2; + } + sampleBits = obj1.getInt(); + sampleMul = 1.0 / (pow(2.0, (double)sampleBits) - 1); + obj1.free(); + + //----- Encode + if (dict->lookup("Encode", &obj1)->isArray() && + obj1.arrayGetLength() == 2*m) { + for (i = 0; i < m; ++i) { + obj1.arrayGet(2*i, &obj2); + if (!obj2.isNum()) { + error(errSyntaxError, -1, "Illegal value in function encode array"); + goto err3; + } + encode[i][0] = obj2.getNum(); + obj2.free(); + obj1.arrayGet(2*i+1, &obj2); + if (!obj2.isNum()) { + error(errSyntaxError, -1, "Illegal value in function encode array"); + goto err3; + } + encode[i][1] = obj2.getNum(); + obj2.free(); + } + } else { + for (i = 0; i < m; ++i) { + encode[i][0] = 0; + encode[i][1] = sampleSize[i] - 1; + } + } + obj1.free(); + for (i = 0; i < m; ++i) { + inputMul[i] = (encode[i][1] - encode[i][0]) / + (domain[i][1] - domain[i][0]); + } + + //----- Decode + if (dict->lookup("Decode", &obj1)->isArray() && + obj1.arrayGetLength() == 2*n) { + for (i = 0; i < n; ++i) { + obj1.arrayGet(2*i, &obj2); + if (!obj2.isNum()) { + error(errSyntaxError, -1, "Illegal value in function decode array"); + goto err3; + } + decode[i][0] = obj2.getNum(); + obj2.free(); + obj1.arrayGet(2*i+1, &obj2); + if (!obj2.isNum()) { + error(errSyntaxError, -1, "Illegal value in function decode array"); + goto err3; + } + decode[i][1] = obj2.getNum(); + obj2.free(); + } + } else { + for (i = 0; i < n; ++i) { + decode[i][0] = range[i][0]; + decode[i][1] = range[i][1]; + } + } + obj1.free(); + + //----- samples + nSamples = n; + for (i = 0; i < m; ++i) + nSamples *= sampleSize[i]; + samples = (double *)gmallocn(nSamples, sizeof(double)); + buf = 0; + bits = 0; + bitMask = (sampleBits < 32) ? ((1 << sampleBits) - 1) : 0xffffffffU; + str->reset(); + for (i = 0; i < nSamples; ++i) { + if (sampleBits == 8) { + s = str->getChar(); + } else if (sampleBits == 16) { + s = str->getChar(); + s = (s << 8) + str->getChar(); + } else if (sampleBits == 32) { + s = str->getChar(); + s = (s << 8) + str->getChar(); + s = (s << 8) + str->getChar(); + s = (s << 8) + str->getChar(); + } else { + while (bits < sampleBits) { + buf = (buf << 8) | (str->getChar() & 0xff); + bits += 8; + } + s = (buf >> (bits - sampleBits)) & bitMask; + bits -= sampleBits; + } + samples[i] = (double)s * sampleMul; + } + str->close(); + + // set up the cache + for (i = 0; i < m; ++i) { + in[i] = domain[i][0]; + cacheIn[i] = in[i] - 1; + } + transform(in, cacheOut); + + ok = gTrue; + return; + + err3: + obj2.free(); + err2: + obj1.free(); + err1: + return; +} + +SampledFunction::~SampledFunction() { + if (idxOffset) { + gfree(idxOffset); + } + if (samples) { + gfree(samples); + } + if (sBuf) { + gfree(sBuf); + } +} + +SampledFunction::SampledFunction(SampledFunction *func) { + memcpy((void *)this, (void *)func, sizeof(SampledFunction)); + idxOffset = (int *)gmallocn(1 << m, sizeof(int)); + memcpy(idxOffset, func->idxOffset, (1 << m) * (int)sizeof(int)); + samples = (double *)gmallocn(nSamples, sizeof(double)); + memcpy(samples, func->samples, nSamples * sizeof(double)); + sBuf = (double *)gmallocn(1 << m, sizeof(double)); +} + +void SampledFunction::transform(double *in, double *out) { + double x; + int e[funcMaxInputs]; + double efrac0[funcMaxInputs]; + double efrac1[funcMaxInputs]; + int i, j, k, idx0, t; + + // check the cache + for (i = 0; i < m; ++i) { + if (in[i] != cacheIn[i]) { + break; + } + } + if (i == m) { + for (i = 0; i < n; ++i) { + out[i] = cacheOut[i]; + } + return; + } + + // map input values into sample array + for (i = 0; i < m; ++i) { + x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0]; + if (x < 0 || x != x) { // x!=x is a more portable version of isnan(x) + x = 0; + } else if (x > sampleSize[i] - 1) { + x = sampleSize[i] - 1; + } + e[i] = (int)x; + if (e[i] == sampleSize[i] - 1 && sampleSize[i] > 1) { + // this happens if in[i] = domain[i][1] + e[i] = sampleSize[i] - 2; + } + efrac1[i] = x - e[i]; + efrac0[i] = 1 - efrac1[i]; + } + + // compute index for the first sample to be used + idx0 = 0; + for (k = m - 1; k >= 1; --k) { + idx0 = (idx0 + e[k]) * sampleSize[k-1]; + } + idx0 = (idx0 + e[0]) * n; + + // for each output, do m-linear interpolation + for (i = 0; i < n; ++i) { + + // pull 2^m values out of the sample array + for (j = 0; j < (1<>= 1) { + for (k = 0; k < t; k += 2) { + sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k+1]; + } + } + + // map output value to range + out[i] = sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0]; + if (out[i] < range[i][0]) { + out[i] = range[i][0]; + } else if (out[i] > range[i][1]) { + out[i] = range[i][1]; + } + } + + // save current result in the cache + for (i = 0; i < m; ++i) { + cacheIn[i] = in[i]; + } + for (i = 0; i < n; ++i) { + cacheOut[i] = out[i]; + } +} + +//------------------------------------------------------------------------ +// ExponentialFunction +//------------------------------------------------------------------------ + +ExponentialFunction::ExponentialFunction(Object *funcObj, Dict *dict) { + Object obj1, obj2; + int i; + + ok = gFalse; + + //----- initialize the generic stuff + if (!init(dict)) { + goto err1; + } + if (m != 1) { + error(errSyntaxError, -1, "Exponential function with more than one input"); + goto err1; + } + + //----- C0 + if (dict->lookup("C0", &obj1)->isArray()) { + if (hasRange && obj1.arrayGetLength() != n) { + error(errSyntaxError, -1, "Function's C0 array is wrong length"); + goto err2; + } + n = obj1.arrayGetLength(); + if (n > funcMaxOutputs) { + error(errSyntaxError, -1, + "Functions with more than {0:d} outputs are unsupported", + funcMaxOutputs); + goto err2; + } + for (i = 0; i < n; ++i) { + obj1.arrayGet(i, &obj2); + if (!obj2.isNum()) { + error(errSyntaxError, -1, "Illegal value in function C0 array"); + goto err3; + } + c0[i] = obj2.getNum(); + obj2.free(); + } + } else { + if (hasRange && n != 1) { + error(errSyntaxError, -1, "Function's C0 array is wrong length"); + goto err2; + } + n = 1; + c0[0] = 0; + } + obj1.free(); + + //----- C1 + if (dict->lookup("C1", &obj1)->isArray()) { + if (obj1.arrayGetLength() != n) { + error(errSyntaxError, -1, "Function's C1 array is wrong length"); + goto err2; + } + for (i = 0; i < n; ++i) { + obj1.arrayGet(i, &obj2); + if (!obj2.isNum()) { + error(errSyntaxError, -1, "Illegal value in function C1 array"); + goto err3; + } + c1[i] = obj2.getNum(); + obj2.free(); + } + } else { + if (n != 1) { + error(errSyntaxError, -1, "Function's C1 array is wrong length"); + goto err2; + } + c1[0] = 1; + } + obj1.free(); + + //----- N (exponent) + if (!dict->lookup("N", &obj1)->isNum()) { + error(errSyntaxError, -1, "Function has missing or invalid N"); + goto err2; + } + e = obj1.getNum(); + obj1.free(); + + ok = gTrue; + return; + + err3: + obj2.free(); + err2: + obj1.free(); + err1: + return; +} + +ExponentialFunction::~ExponentialFunction() { +} + +ExponentialFunction::ExponentialFunction(ExponentialFunction *func) { + memcpy((void *)this, (void *)func, sizeof(ExponentialFunction)); +} + +void ExponentialFunction::transform(double *in, double *out) { + double x; + int i; + + if (in[0] < domain[0][0]) { + x = domain[0][0]; + } else if (in[0] > domain[0][1]) { + x = domain[0][1]; + } else { + x = in[0]; + } + for (i = 0; i < n; ++i) { + out[i] = c0[i] + pow(x, e) * (c1[i] - c0[i]); + if (hasRange) { + if (out[i] < range[i][0]) { + out[i] = range[i][0]; + } else if (out[i] > range[i][1]) { + out[i] = range[i][1]; + } + } + } + return; +} + +//------------------------------------------------------------------------ +// StitchingFunction +//------------------------------------------------------------------------ + +StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict, + int recursion) { + Object obj1, obj2; + int i; + + ok = gFalse; + funcs = NULL; + bounds = NULL; + encode = NULL; + scale = NULL; + + //----- initialize the generic stuff + if (!init(dict)) { + goto err1; + } + if (m != 1) { + error(errSyntaxError, -1, "Stitching function with more than one input"); + goto err1; + } + + //----- Functions + if (!dict->lookup("Functions", &obj1)->isArray()) { + error(errSyntaxError, -1, + "Missing 'Functions' entry in stitching function"); + goto err1; + } + k = obj1.arrayGetLength(); + funcs = (Function **)gmallocn(k, sizeof(Function *)); + bounds = (double *)gmallocn(k + 1, sizeof(double)); + encode = (double *)gmallocn(2 * k, sizeof(double)); + scale = (double *)gmallocn(k, sizeof(double)); + for (i = 0; i < k; ++i) { + funcs[i] = NULL; + } + for (i = 0; i < k; ++i) { + if (!(funcs[i] = Function::parse(obj1.arrayGet(i, &obj2), + recursion + 1))) { + goto err2; + } + if (funcs[i]->getInputSize() != 1 || + (i > 0 && funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) { + error(errSyntaxError, -1, + "Incompatible subfunctions in stitching function"); + goto err2; + } + obj2.free(); + } + obj1.free(); + + //----- Bounds + if (!dict->lookup("Bounds", &obj1)->isArray() || + obj1.arrayGetLength() != k - 1) { + error(errSyntaxError, -1, + "Missing or invalid 'Bounds' entry in stitching function"); + goto err1; + } + bounds[0] = domain[0][0]; + for (i = 1; i < k; ++i) { + if (!obj1.arrayGet(i - 1, &obj2)->isNum()) { + error(errSyntaxError, -1, + "Invalid type in 'Bounds' array in stitching function"); + goto err2; + } + bounds[i] = obj2.getNum(); + obj2.free(); + } + bounds[k] = domain[0][1]; + obj1.free(); + + //----- Encode + if (!dict->lookup("Encode", &obj1)->isArray() || + obj1.arrayGetLength() != 2 * k) { + error(errSyntaxError, -1, + "Missing or invalid 'Encode' entry in stitching function"); + goto err1; + } + for (i = 0; i < 2 * k; ++i) { + if (!obj1.arrayGet(i, &obj2)->isNum()) { + error(errSyntaxError, -1, + "Invalid type in 'Encode' array in stitching function"); + goto err2; + } + encode[i] = obj2.getNum(); + obj2.free(); + } + obj1.free(); + + //----- pre-compute the scale factors + for (i = 0; i < k; ++i) { + if (bounds[i] == bounds[i+1]) { + // avoid a divide-by-zero -- in this situation, function i will + // never be used anyway + scale[i] = 0; + } else { + scale[i] = (encode[2*i+1] - encode[2*i]) / (bounds[i+1] - bounds[i]); + } + } + + ok = gTrue; + return; + + err2: + obj2.free(); + err1: + obj1.free(); +} + +StitchingFunction::StitchingFunction(StitchingFunction *func) { + int i; + + memcpy((void *)this, (void *)func, sizeof(StitchingFunction)); + funcs = (Function **)gmallocn(k, sizeof(Function *)); + for (i = 0; i < k; ++i) { + funcs[i] = func->funcs[i]->copy(); + } + bounds = (double *)gmallocn(k + 1, sizeof(double)); + memcpy(bounds, func->bounds, (k + 1) * sizeof(double)); + encode = (double *)gmallocn(2 * k, sizeof(double)); + memcpy(encode, func->encode, 2 * k * sizeof(double)); + scale = (double *)gmallocn(k, sizeof(double)); + memcpy(scale, func->scale, k * sizeof(double)); + ok = gTrue; +} + +StitchingFunction::~StitchingFunction() { + int i; + + if (funcs) { + for (i = 0; i < k; ++i) { + if (funcs[i]) { + delete funcs[i]; + } + } + } + gfree(funcs); + gfree(bounds); + gfree(encode); + gfree(scale); +} + +void StitchingFunction::transform(double *in, double *out) { + double x; + int i; + + if (in[0] < domain[0][0]) { + x = domain[0][0]; + } else if (in[0] > domain[0][1]) { + x = domain[0][1]; + } else { + x = in[0]; + } + for (i = 0; i < k - 1; ++i) { + if (x < bounds[i+1]) { + break; + } + } + x = encode[2*i] + (x - bounds[i]) * scale[i]; + funcs[i]->transform(&x, out); +} + +//------------------------------------------------------------------------ +// PostScriptFunction +//------------------------------------------------------------------------ + +// This is not an enum, because we can't foreward-declare the enum +// type in Function.h +// +// NB: This must be kept in sync with psOpNames[] below. +#define psOpAbs 0 +#define psOpAdd 1 +#define psOpAnd 2 +#define psOpAtan 3 +#define psOpBitshift 4 +#define psOpCeiling 5 +#define psOpCopy 6 +#define psOpCos 7 +#define psOpCvi 8 +#define psOpCvr 9 +#define psOpDiv 10 +#define psOpDup 11 +#define psOpEq 12 +#define psOpExch 13 +#define psOpExp 14 +#define psOpFalse 15 +#define psOpFloor 16 +#define psOpGe 17 +#define psOpGt 18 +#define psOpIdiv 19 +#define psOpIndex 20 +#define psOpLe 21 +#define psOpLn 22 +#define psOpLog 23 +#define psOpLt 24 +#define psOpMod 25 +#define psOpMul 26 +#define psOpNe 27 +#define psOpNeg 28 +#define psOpNot 29 +#define psOpOr 30 +#define psOpPop 31 +#define psOpRoll 32 +#define psOpRound 33 +#define psOpSin 34 +#define psOpSqrt 35 +#define psOpSub 36 +#define psOpTrue 37 +#define psOpTruncate 38 +#define psOpXor 39 +// the push/j/jz ops are used internally (and are not listed in psOpNames[]) +#define psOpPush 40 +#define psOpJ 41 +#define psOpJz 42 + +#define nPSOps (sizeof(psOpNames) / sizeof(const char *)) + +// Note: 'if' and 'ifelse' are parsed separately. +// The rest are listed here in alphabetical order. +// +// NB: This must be kept in sync with the psOpXXX defines above. +static const char *psOpNames[] = { + "abs", + "add", + "and", + "atan", + "bitshift", + "ceiling", + "copy", + "cos", + "cvi", + "cvr", + "div", + "dup", + "eq", + "exch", + "exp", + "false", + "floor", + "ge", + "gt", + "idiv", + "index", + "le", + "ln", + "log", + "lt", + "mod", + "mul", + "ne", + "neg", + "not", + "or", + "pop", + "roll", + "round", + "sin", + "sqrt", + "sub", + "true", + "truncate", + "xor" +}; + +struct PSCode { + int op; + union { + double d; + int i; + } val; +}; + +#define psStackSize 100 + +PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) { + Stream *str; + GList *tokens; + GString *tok; + double in[funcMaxInputs]; + int tokPtr, codePtr, i; + + codeString = NULL; + code = NULL; + codeSize = 0; + ok = gFalse; + + //----- initialize the generic stuff + if (!init(dict)) { + goto err1; + } + if (!hasRange) { + error(errSyntaxError, -1, "Type 4 function is missing range"); + goto err1; + } + + //----- get the stream + if (!funcObj->isStream()) { + error(errSyntaxError, -1, "Type 4 function isn't a stream"); + goto err1; + } + str = funcObj->getStream(); + + //----- tokenize the function + codeString = new GString(); + tokens = new GList(); + str->reset(); + while ((tok = getToken(str))) { + tokens->append(tok); + } + str->close(); + + //----- parse the function + if (tokens->getLength() < 1 || + ((GString *)tokens->get(0))->cmp("{")) { + error(errSyntaxError, -1, "Expected '{{' at start of PostScript function"); + goto err2; + } + tokPtr = 1; + codePtr = 0; + if (!parseCode(tokens, &tokPtr, &codePtr)) { + goto err2; + } + codeLen = codePtr; + + //----- set up the cache + for (i = 0; i < m; ++i) { + in[i] = domain[i][0]; + cacheIn[i] = in[i] - 1; + } + transform(in, cacheOut); + + ok = gTrue; + + err2: + deleteGList(tokens, GString); + err1: + return; +} + +PostScriptFunction::PostScriptFunction(PostScriptFunction *func) { + memcpy((void *)this, (void *)func, sizeof(PostScriptFunction)); + codeString = func->codeString->copy(); + code = (PSCode *)gmallocn(codeSize, sizeof(PSCode)); + memcpy(code, func->code, codeSize * sizeof(PSCode)); +} + +PostScriptFunction::~PostScriptFunction() { + gfree(code); + if (codeString) { + delete codeString; + } +} + +void PostScriptFunction::transform(double *in, double *out) { + double stack[psStackSize]; + double x; + int sp, i; + + // check the cache + for (i = 0; i < m; ++i) { + if (in[i] != cacheIn[i]) { + break; + } + } + if (i == m) { + for (i = 0; i < n; ++i) { + out[i] = cacheOut[i]; + } + return; + } + + for (i = 0; i < m; ++i) { + stack[psStackSize - 1 - i] = in[i]; + } + sp = exec(stack, psStackSize - m); + // if (sp < psStackSize - n) { + // error(errSyntaxWarning, -1, + // "Extra values on stack at end of PostScript function"); + // } + if (sp > psStackSize - n) { + error(errSyntaxError, -1, "Stack underflow in PostScript function"); + sp = psStackSize - n; + } + for (i = 0; i < n; ++i) { + x = stack[sp + n - 1 - i]; + if (x < range[i][0]) { + out[i] = range[i][0]; + } else if (x > range[i][1]) { + out[i] = range[i][1]; + } else { + out[i] = x; + } + } + + // save current result in the cache + for (i = 0; i < m; ++i) { + cacheIn[i] = in[i]; + } + for (i = 0; i < n; ++i) { + cacheOut[i] = out[i]; + } +} + +GBool PostScriptFunction::parseCode(GList *tokens, int *tokPtr, int *codePtr) { + GString *tok; + char *p; + int a, b, mid, cmp; + int codePtr0, codePtr1; + + while (1) { + if (*tokPtr >= tokens->getLength()) { + error(errSyntaxError, -1, + "Unexpected end of PostScript function stream"); + return gFalse; + } + tok = (GString *)tokens->get((*tokPtr)++); + p = tok->getCString(); + if (isdigit(*p) || *p == '.' || *p == '-') { + addCodeD(codePtr, psOpPush, atof(tok->getCString())); + } else if (!tok->cmp("{")) { + codePtr0 = *codePtr; + addCodeI(codePtr, psOpJz, 0); + if (!parseCode(tokens, tokPtr, codePtr)) { + return gFalse; + } + if (*tokPtr >= tokens->getLength()) { + error(errSyntaxError, -1, + "Unexpected end of PostScript function stream"); + return gFalse; + } + tok = (GString *)tokens->get((*tokPtr)++); + if (!tok->cmp("if")) { + code[codePtr0].val.i = *codePtr; + } else if (!tok->cmp("{")) { + codePtr1 = *codePtr; + addCodeI(codePtr, psOpJ, 0); + code[codePtr0].val.i = *codePtr; + if (!parseCode(tokens, tokPtr, codePtr)) { + return gFalse; + } + if (*tokPtr >= tokens->getLength()) { + error(errSyntaxError, -1, + "Unexpected end of PostScript function stream"); + return gFalse; + } + tok = (GString *)tokens->get((*tokPtr)++); + if (!tok->cmp("ifelse")) { + code[codePtr1].val.i = *codePtr; + } else { + error(errSyntaxError, -1, + "Expected 'ifelse' in PostScript function stream"); + return gFalse; + } + } else { + error(errSyntaxError, -1, + "Expected 'if' in PostScript function stream"); + return gFalse; + } + } else if (!tok->cmp("}")) { + break; + } else if (!tok->cmp("if")) { + error(errSyntaxError, -1, + "Unexpected 'if' in PostScript function stream"); + return gFalse; + } else if (!tok->cmp("ifelse")) { + error(errSyntaxError, -1, + "Unexpected 'ifelse' in PostScript function stream"); + return gFalse; + } else { + a = -1; + b = nPSOps; + cmp = 0; // make gcc happy + // invariant: psOpNames[a] < tok < psOpNames[b] + while (b - a > 1) { + mid = (a + b) / 2; + cmp = tok->cmp(psOpNames[mid]); + if (cmp > 0) { + a = mid; + } else if (cmp < 0) { + b = mid; + } else { + a = b = mid; + } + } + if (cmp != 0) { + error(errSyntaxError, -1, + "Unknown operator '{0:t}' in PostScript function", + tok); + return gFalse; + } + addCode(codePtr, a); + } + } + return gTrue; +} + +void PostScriptFunction::addCode(int *codePtr, int op) { + if (*codePtr >= codeSize) { + if (codeSize) { + codeSize *= 2; + } else { + codeSize = 16; + } + code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); + } + code[*codePtr].op = op; + ++(*codePtr); +} + +void PostScriptFunction::addCodeI(int *codePtr, int op, int x) { + if (*codePtr >= codeSize) { + if (codeSize) { + codeSize *= 2; + } else { + codeSize = 16; + } + code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); + } + code[*codePtr].op = op; + code[*codePtr].val.i = x; + ++(*codePtr); +} + +void PostScriptFunction::addCodeD(int *codePtr, int op, double x) { + if (*codePtr >= codeSize) { + if (codeSize) { + codeSize *= 2; + } else { + codeSize = 16; + } + code = (PSCode *)greallocn(code, codeSize, sizeof(PSCode)); + } + code[*codePtr].op = op; + code[*codePtr].val.d = x; + ++(*codePtr); +} + +GString *PostScriptFunction::getToken(Stream *str) { + GString *s; + int c; + GBool comment; + + s = new GString(); + comment = gFalse; + while (1) { + if ((c = str->getChar()) == EOF) { + delete s; + return NULL; + } + codeString->append((char)c); + if (comment) { + if (c == '\x0a' || c == '\x0d') { + comment = gFalse; + } + } else if (c == '%') { + comment = gTrue; + } else if (!isspace(c)) { + break; + } + } + if (c == '{' || c == '}') { + s->append((char)c); + } else if (isdigit(c) || c == '.' || c == '-') { + while (1) { + s->append((char)c); + c = str->lookChar(); + if (c == EOF || !(isdigit(c) || c == '.' || c == '-')) { + break; + } + str->getChar(); + codeString->append((char)c); + } + } else { + while (1) { + s->append((char)c); + c = str->lookChar(); + if (c == EOF || !isalnum(c)) { + break; + } + str->getChar(); + codeString->append((char)c); + } + } + return s; +} + +int PostScriptFunction::exec(double *stack, int sp0) { + PSCode *c; + double tmp[psStackSize]; + double t; + int sp, ip, nn, k, i; + + sp = sp0; + ip = 0; + while (ip < codeLen) { + c = &code[ip++]; + switch(c->op) { + case psOpAbs: + if (sp >= psStackSize) { + goto underflow; + } + stack[sp] = fabs(stack[sp]); + break; + case psOpAdd: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = stack[sp + 1] + stack[sp]; + ++sp; + break; + case psOpAnd: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = (int)stack[sp + 1] & (int)stack[sp]; + ++sp; + break; + case psOpAtan: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = atan2(stack[sp + 1], stack[sp]); + ++sp; + break; + case psOpBitshift: + if (sp + 1 >= psStackSize) { + goto underflow; + } + k = (int)stack[sp + 1]; + nn = (int)stack[sp]; + if (nn > 0) { + stack[sp + 1] = k << nn; + } else if (nn < 0) { + stack[sp + 1] = k >> -nn; + } else { + stack[sp + 1] = k; + } + ++sp; + break; + case psOpCeiling: + if (sp >= psStackSize) { + goto underflow; + } + stack[sp] = ceil(stack[sp]); + break; + case psOpCopy: + if (sp >= psStackSize) { + goto underflow; + } + nn = (int)stack[sp++]; + if (nn < 0) { + goto invalidArg; + } + if (sp + nn > psStackSize) { + goto underflow; + } + if (sp - nn < 0) { + goto overflow; + } + for (i = 0; i < nn; ++i) { + stack[sp - nn + i] = stack[sp + i]; + } + sp -= nn; + break; + case psOpCos: + if (sp >= psStackSize) { + goto underflow; + } + stack[sp] = cos(stack[sp]); + break; + case psOpCvi: + if (sp >= psStackSize) { + goto underflow; + } + stack[sp] = (int)stack[sp]; + break; + case psOpCvr: + if (sp >= psStackSize) { + goto underflow; + } + break; + case psOpDiv: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = stack[sp + 1] / stack[sp]; + ++sp; + break; + case psOpDup: + if (sp >= psStackSize) { + goto underflow; + } + if (sp < 1) { + goto overflow; + } + stack[sp - 1] = stack[sp]; + --sp; + break; + case psOpEq: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = stack[sp + 1] == stack[sp] ? 1 : 0; + ++sp; + break; + case psOpExch: + if (sp + 1 >= psStackSize) { + goto underflow; + } + t = stack[sp]; + stack[sp] = stack[sp + 1]; + stack[sp + 1] = t; + break; + case psOpExp: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = pow(stack[sp + 1], stack[sp]); + ++sp; + break; + case psOpFalse: + if (sp < 1) { + goto overflow; + } + stack[sp - 1] = 0; + --sp; + break; + case psOpFloor: + if (sp >= psStackSize) { + goto underflow; + } + stack[sp] = floor(stack[sp]); + break; + case psOpGe: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = stack[sp + 1] >= stack[sp] ? 1 : 0; + ++sp; + break; + case psOpGt: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = stack[sp + 1] > stack[sp] ? 1 : 0; + ++sp; + break; + case psOpIdiv: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = (int)stack[sp + 1] / (int)stack[sp]; + ++sp; + break; + case psOpIndex: + if (sp >= psStackSize) { + goto underflow; + } + k = (int)stack[sp]; + if (k < 0) { + goto invalidArg; + } + if (sp + 1 + k >= psStackSize) { + goto underflow; + } + stack[sp] = stack[sp + 1 + k]; + break; + case psOpLe: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = stack[sp + 1] <= stack[sp] ? 1 : 0; + ++sp; + break; + case psOpLn: + if (sp >= psStackSize) { + goto underflow; + } + stack[sp] = log(stack[sp]); + break; + case psOpLog: + if (sp >= psStackSize) { + goto underflow; + } + stack[sp] = log10(stack[sp]); + break; + case psOpLt: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = stack[sp + 1] < stack[sp] ? 1 : 0; + ++sp; + break; + case psOpMod: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = (int)stack[sp + 1] % (int)stack[sp]; + ++sp; + break; + case psOpMul: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = stack[sp + 1] * stack[sp]; + ++sp; + break; + case psOpNe: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = stack[sp + 1] != stack[sp] ? 1 : 0; + ++sp; + break; + case psOpNeg: + if (sp >= psStackSize) { + goto underflow; + } + stack[sp] = -stack[sp]; + break; + case psOpNot: + if (sp >= psStackSize) { + goto underflow; + } + stack[sp] = stack[sp] == 0 ? 1 : 0; + break; + case psOpOr: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = (int)stack[sp + 1] | (int)stack[sp]; + ++sp; + break; + case psOpPop: + if (sp >= psStackSize) { + goto underflow; + } + ++sp; + break; + case psOpRoll: + if (sp + 1 >= psStackSize) { + goto underflow; + } + k = (int)stack[sp++]; + nn = (int)stack[sp++]; + if (nn < 0) { + goto invalidArg; + } + if (sp + nn > psStackSize) { + goto underflow; + } + if (k >= 0) { + k %= nn; // cppcheck-suppress bughuntingDivByZero + } else { + k = -k % nn; // cppcheck-suppress bughuntingDivByZero + if (k) { + k = nn - k; + } + } + for (i = 0; i < nn; ++i) { + tmp[i] = stack[sp + i]; + } + for (i = 0; i < nn; ++i) { + stack[sp + i] = tmp[(i + k) % nn]; // cppcheck-suppress bughuntingDivByZero + } + break; + case psOpRound: + if (sp >= psStackSize) { + goto underflow; + } + t = stack[sp]; + stack[sp] = (t >= 0) ? floor(t + 0.5) : ceil(t - 0.5); + break; + case psOpSin: + if (sp >= psStackSize) { + goto underflow; + } + stack[sp] = sin(stack[sp]); + break; + case psOpSqrt: + if (sp >= psStackSize) { + goto underflow; + } + stack[sp] = sqrt(stack[sp]); + break; + case psOpSub: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = stack[sp + 1] - stack[sp]; + ++sp; + break; + case psOpTrue: + if (sp < 1) { + goto overflow; + } + stack[sp - 1] = 1; + --sp; + break; + case psOpTruncate: + if (sp >= psStackSize) { + goto underflow; + } + t = stack[sp]; + stack[sp] = (t >= 0) ? floor(t) : ceil(t); + break; + case psOpXor: + if (sp + 1 >= psStackSize) { + goto underflow; + } + stack[sp + 1] = (int)stack[sp + 1] ^ (int)stack[sp]; + ++sp; + break; + case psOpPush: + if (sp < 1) { + goto overflow; + } + stack[--sp] = c->val.d; + break; + case psOpJ: + ip = c->val.i; + break; + case psOpJz: + if (sp >= psStackSize) { + goto underflow; + } + k = (int)stack[sp++]; + if (k == 0) { + ip = c->val.i; + } + break; + } + } + return sp; + + underflow: + error(errSyntaxError, -1, "Stack underflow in PostScript function"); + return sp; + overflow: + error(errSyntaxError, -1, "Stack overflow in PostScript function"); + return sp; + invalidArg: + error(errSyntaxError, -1, "Invalid arg in PostScript function"); + return sp; +} diff --git a/test/bug-hunting/cve/CVE-2019-10026/Function.h b/test/bug-hunting/cve/CVE-2019-10026/Function.h new file mode 100644 index 000000000..5cf45169a --- /dev/null +++ b/test/bug-hunting/cve/CVE-2019-10026/Function.h @@ -0,0 +1,236 @@ +//======================================================================== +// +// Function.h +// +// Copyright 2001-2003 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef FUNCTION_H +#define FUNCTION_H + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma interface +#endif + +#include "gtypes.h" +#include "Object.h" + +class GList; +class Dict; +class Stream; +struct PSCode; + +//------------------------------------------------------------------------ +// Function +//------------------------------------------------------------------------ + +#define funcMaxInputs 32 +#define funcMaxOutputs 32 +#define sampledFuncMaxInputs 16 + +class Function { +public: + + Function(); + + virtual ~Function(); + + // Construct a function. Returns NULL if unsuccessful. + static Function *parse(Object *funcObj, int recursion = 0); + + // Initialize the entries common to all function types. + GBool init(Dict *dict); + + virtual Function *copy() = 0; + + // Return the function type: + // -1 : identity + // 0 : sampled + // 2 : exponential + // 3 : stitching + // 4 : PostScript + virtual int getType() = 0; + + // Return size of input and output tuples. + int getInputSize() { return m; } + int getOutputSize() { return n; } + + double getDomainMin(int i) { return domain[i][0]; } + double getDomainMax(int i) { return domain[i][1]; } + double getRangeMin(int i) { return range[i][0]; } + double getRangeMax(int i) { return range[i][1]; } + GBool getHasRange() { return hasRange; } + + // Transform an input tuple into an output tuple. + virtual void transform(double *in, double *out) = 0; + + virtual GBool isOk() = 0; + +protected: + + int m, n; // size of input and output tuples + double // min and max values for function domain + domain[funcMaxInputs][2]; + double // min and max values for function range + range[funcMaxOutputs][2]; + GBool hasRange; // set if range is defined +}; + +//------------------------------------------------------------------------ +// IdentityFunction +//------------------------------------------------------------------------ + +class IdentityFunction: public Function { +public: + + IdentityFunction(); + virtual ~IdentityFunction(); + virtual Function *copy() { return new IdentityFunction(); } + virtual int getType() { return -1; } + virtual void transform(double *in, double *out); + virtual GBool isOk() { return gTrue; } + +private: +}; + +//------------------------------------------------------------------------ +// SampledFunction +//------------------------------------------------------------------------ + +class SampledFunction: public Function { +public: + + SampledFunction(Object *funcObj, Dict *dict); + virtual ~SampledFunction(); + virtual Function *copy() { return new SampledFunction(this); } + virtual int getType() { return 0; } + virtual void transform(double *in, double *out); + virtual GBool isOk() { return ok; } + + int getSampleSize(int i) { return sampleSize[i]; } + double getEncodeMin(int i) { return encode[i][0]; } + double getEncodeMax(int i) { return encode[i][1]; } + double getDecodeMin(int i) { return decode[i][0]; } + double getDecodeMax(int i) { return decode[i][1]; } + double *getSamples() { return samples; } + +private: + + SampledFunction(SampledFunction *func); + + int // number of samples for each domain element + sampleSize[funcMaxInputs]; + double // min and max values for domain encoder + encode[funcMaxInputs][2]; + double // min and max values for range decoder + decode[funcMaxOutputs][2]; + double // input multipliers + inputMul[funcMaxInputs]; + int *idxOffset; + double *samples; // the samples + int nSamples; // size of the samples array + double *sBuf; // buffer for the transform function + double cacheIn[funcMaxInputs]; + double cacheOut[funcMaxOutputs]; + GBool ok; +}; + +//------------------------------------------------------------------------ +// ExponentialFunction +//------------------------------------------------------------------------ + +class ExponentialFunction: public Function { +public: + + ExponentialFunction(Object *funcObj, Dict *dict); + virtual ~ExponentialFunction(); + virtual Function *copy() { return new ExponentialFunction(this); } + virtual int getType() { return 2; } + virtual void transform(double *in, double *out); + virtual GBool isOk() { return ok; } + + double *getC0() { return c0; } + double *getC1() { return c1; } + double getE() { return e; } + +private: + + ExponentialFunction(ExponentialFunction *func); + + double c0[funcMaxOutputs]; + double c1[funcMaxOutputs]; + double e; + GBool ok; +}; + +//------------------------------------------------------------------------ +// StitchingFunction +//------------------------------------------------------------------------ + +class StitchingFunction: public Function { +public: + + StitchingFunction(Object *funcObj, Dict *dict, int recursion); + virtual ~StitchingFunction(); + virtual Function *copy() { return new StitchingFunction(this); } + virtual int getType() { return 3; } + virtual void transform(double *in, double *out); + virtual GBool isOk() { return ok; } + + int getNumFuncs() { return k; } + Function *getFunc(int i) { return funcs[i]; } + double *getBounds() { return bounds; } + double *getEncode() { return encode; } + double *getScale() { return scale; } + +private: + + StitchingFunction(StitchingFunction *func); + + int k; + Function **funcs; + double *bounds; + double *encode; + double *scale; + GBool ok; +}; + +//------------------------------------------------------------------------ +// PostScriptFunction +//------------------------------------------------------------------------ + +class PostScriptFunction: public Function { +public: + + PostScriptFunction(Object *funcObj, Dict *dict); + virtual ~PostScriptFunction(); + virtual Function *copy() { return new PostScriptFunction(this); } + virtual int getType() { return 4; } + virtual void transform(double *in, double *out); + virtual GBool isOk() { return ok; } + + GString *getCodeString() { return codeString; } + +private: + + PostScriptFunction(PostScriptFunction *func); + GBool parseCode(GList *tokens, int *tokPtr, int *codePtr); + void addCode(int *codePtr, int op); + void addCodeI(int *codePtr, int op, int x); + void addCodeD(int *codePtr, int op, double x); + GString *getToken(Stream *str); + int exec(double *stack, int sp0); + + GString *codeString; + PSCode *code; + int codeLen; + int codeSize; + double cacheIn[funcMaxInputs]; + double cacheOut[funcMaxOutputs]; + GBool ok; +}; + +#endif diff --git a/test/bug-hunting/cve/CVE-2019-10026/README b/test/bug-hunting/cve/CVE-2019-10026/README new file mode 100644 index 000000000..9ce551f5e --- /dev/null +++ b/test/bug-hunting/cve/CVE-2019-10026/README @@ -0,0 +1,3 @@ +Details: +https://nvd.nist.gov/vuln/detail/CVE-2019-10026 +