bool SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *catalog, Object *str,
					const double *ptm, int paintType, int /*tilingType*/, Dict *resDict,
					const double *mat, const double *bbox,
					int x0, int y0, int x1, int y1,
					double xStep, double yStep)
{
  PDFRectangle box;
  Gfx *gfx;
  Splash *formerSplash = splash;
  SplashBitmap *formerBitmap = bitmap;
  double width, height;
  int surface_width, surface_height, result_width, result_height, i;
  int repeatX, repeatY;
  SplashCoord matc[6];
  Matrix m1;
  const double *ctm;
  double savedCTM[6];
  double kx, ky, sx, sy;
  bool retValue = false;

  width = bbox[2] - bbox[0];
  height = bbox[3] - bbox[1];

  if (xStep != width || yStep != height)
    return false;

  // calculate offsets
  ctm = state->getCTM();
  for (i = 0; i < 6; ++i) {
    savedCTM[i] = ctm[i];
  }
  state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
  state->concatCTM(1, 0, 0, 1, bbox[0], bbox[1]);
  ctm = state->getCTM();
  for (i = 0; i < 6; ++i) {
    if (!std::isfinite(ctm[i])) {
      state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
      return false;
    }
  }
  matc[4] = x0 * xStep * ctm[0] + y0 * yStep * ctm[2] + ctm[4];
  matc[5] = x0 * xStep * ctm[1] + y0 * yStep * ctm[3] + ctm[5];
  if (splashAbs(ctm[1]) > splashAbs(ctm[0])) {
    kx = -ctm[1];
    ky = ctm[2] - (ctm[0] * ctm[3]) / ctm[1];
  } else {
    kx = ctm[0];
    ky = ctm[3] - (ctm[1] * ctm[2]) / ctm[0];
  }
  result_width = (int) ceil(fabs(kx * width * (x1 - x0)));
  result_height = (int) ceil(fabs(ky * height * (y1 - y0)));
  kx = state->getHDPI() / 72.0;
  ky = state->getVDPI() / 72.0;
  m1.m[0] = (ptm[0] == 0) ? fabs(ptm[2]) * kx : fabs(ptm[0]) * kx;
  m1.m[1] = 0;
  m1.m[2] = 0;
  m1.m[3] = (ptm[3] == 0) ? fabs(ptm[1]) * ky : fabs(ptm[3]) * ky;
  m1.m[4] = 0;
  m1.m[5] = 0;
  m1.transform(width, height, &kx, &ky);
  surface_width = (int) ceil (fabs(kx));
  surface_height = (int) ceil (fabs(ky));

  sx = (double) result_width / (surface_width * (x1 - x0));
  sy = (double) result_height / (surface_height * (y1 - y0));
  m1.m[0] *= sx;
  m1.m[3] *= sy;
  m1.transform(width, height, &kx, &ky);

  if(fabs(kx) < 1 && fabs(ky) < 1) {
    kx = std::min<double>(kx, ky);
    ky = 2 / kx;
    m1.m[0] *= ky;
    m1.m[3] *= ky;
    m1.transform(width, height, &kx, &ky);
    surface_width = (int) ceil (fabs(kx));
    surface_height = (int) ceil (fabs(ky));
    repeatX = x1 - x0;
    repeatY = y1 - y0;
  } else {
    if ((unsigned long) surface_width * surface_height > 0x800000L) {
      state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
      return false;
    }
    while(fabs(kx) > 16384 || fabs(ky) > 16384) {
      // limit pattern bitmap size
      m1.m[0] /= 2;
      m1.m[3] /= 2;
      m1.transform(width, height, &kx, &ky);
    }
    surface_width = (int) ceil (fabs(kx));
    surface_height = (int) ceil (fabs(ky));
    // adjust repeat values to completely fill region
    repeatX = result_width / surface_width;
    repeatY = result_height / surface_height;
    if (surface_width * repeatX < result_width)
      repeatX++;
    if (surface_height * repeatY < result_height)
      repeatY++;
    if (x1 - x0 > repeatX)
      repeatX = x1 - x0;
    if (y1 - y0 > repeatY)
      repeatY = y1 - y0;
  }
  // restore CTM and calculate rotate and scale with rounded matrix
  state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
  state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
  state->concatCTM(width * repeatX, 0, 0, height * repeatY, bbox[0], bbox[1]);
  ctm = state->getCTM();
  matc[0] = ctm[0];
  matc[1] = ctm[1];
  matc[2] = ctm[2];
  matc[3] = ctm[3];

  if (surface_width == 0 || surface_height == 0 || repeatX * repeatY <= 4) {
    state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
    return false;
  }
  m1.transform(bbox[0], bbox[1], &kx, &ky);
  m1.m[4] = -kx;
  m1.m[5] = -ky;

  bitmap = new SplashBitmap(surface_width, surface_height, 1,
                            (paintType == 1) ? colorMode : splashModeMono8, true);
  if (bitmap->getDataPtr() == nullptr) {
    SplashBitmap *tBitmap = bitmap;
    bitmap = formerBitmap;
    delete tBitmap;
    state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
    return false;
  }
  splash = new Splash(bitmap, true);
  if (paintType == 2) {
    SplashColor clearColor;
#ifdef SPLASH_CMYK
    clearColor[0] = (colorMode == splashModeCMYK8 || colorMode == splashModeDeviceN8) ? 0x00 : 0xFF;
#else
    clearColor[0] = 0xFF;
#endif
    splash->clear(clearColor, 0);
  } else {
    splash->clear(paperColor, 0);
  }
  splash->setThinLineMode(formerSplash->getThinLineMode());
  splash->setMinLineWidth(s_minLineWidth);

  box.x1 = bbox[0]; box.y1 = bbox[1];
  box.x2 = bbox[2]; box.y2 = bbox[3];
  gfx = new Gfx(doc, this, resDict, &box, nullptr, nullptr, nullptr, gfxA);
  // set pattern transformation matrix
  gfx->getState()->setCTM(m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]);
  updateCTM(gfx->getState(), m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]);
  gfx->display(str);
  delete splash;
  splash = formerSplash;
  TilingSplashOutBitmap imgData;
  imgData.bitmap = bitmap;
  imgData.paintType = paintType;
  imgData.pattern = splash->getFillPattern();
  imgData.colorMode = colorMode;
  imgData.y = 0;
  imgData.repeatX = repeatX;
  imgData.repeatY = repeatY;
  SplashBitmap *tBitmap = bitmap;
  bitmap = formerBitmap;
  result_width = tBitmap->getWidth() * imgData.repeatX;
  result_height = tBitmap->getHeight() * imgData.repeatY;

  if (splashAbs(matc[1]) > splashAbs(matc[0])) {
    kx = -matc[1];
    ky = matc[2] - (matc[0] * matc[3]) / matc[1];
  } else {
    kx = matc[0];
    ky = matc[3] - (matc[1] * matc[2]) / matc[0];
  }
  kx = result_width / (fabs(kx) + 1);
  ky = result_height / (fabs(ky) + 1);
  state->concatCTM(kx, 0, 0, ky, 0, 0);
  ctm = state->getCTM();
  matc[0] = ctm[0];
  matc[1] = ctm[1];
  matc[2] = ctm[2];
  matc[3] = ctm[3];
  bool minorAxisZero = matc[1] == 0 && matc[2] == 0;
  if (matc[0] > 0 && minorAxisZero && matc[3] > 0) {
    // draw the tiles
    for (int y = 0; y < imgData.repeatY; ++y) {
      for (int x = 0; x < imgData.repeatX; ++x) {
        x0 = splashFloor(matc[4]) + x * tBitmap->getWidth();
        y0 = splashFloor(matc[5]) + y * tBitmap->getHeight();
        splash->blitImage(tBitmap, true, x0, y0);
      }
    }
    retValue = true;
  } else {
    retValue = splash->drawImage(&tilingBitmapSrc, nullptr, &imgData, colorMode, true, result_width, result_height, matc, false, true) == splashOk;
  }
  delete tBitmap;
  delete gfx;
  return retValue;
}

SplashOutputDev::tilingPatternFill: Fix crash on broken file

Issue #802


bool SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *catalog, Object *str,
					const double *ptm, int paintType, int /*tilingType*/, Dict *resDict,
					const double *mat, const double *bbox,
					int x0, int y0, int x1, int y1,
					double xStep, double yStep)
{
  PDFRectangle box;
  Gfx *gfx;
  Splash *formerSplash = splash;
  SplashBitmap *formerBitmap = bitmap;
  double width, height;
  int surface_width, surface_height, result_width, result_height, i;
  int repeatX, repeatY;
  SplashCoord matc[6];
  Matrix m1;
  const double *ctm;
  double savedCTM[6];
  double kx, ky, sx, sy;
  bool retValue = false;

  width = bbox[2] - bbox[0];
  height = bbox[3] - bbox[1];

  if (xStep != width || yStep != height)
    return false;

  // calculate offsets
  ctm = state->getCTM();
  for (i = 0; i < 6; ++i) {
    savedCTM[i] = ctm[i];
  }
  state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
  state->concatCTM(1, 0, 0, 1, bbox[0], bbox[1]);
  ctm = state->getCTM();
  for (i = 0; i < 6; ++i) {
    if (!std::isfinite(ctm[i])) {
      state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
      return false;
    }
  }
  matc[4] = x0 * xStep * ctm[0] + y0 * yStep * ctm[2] + ctm[4];
  matc[5] = x0 * xStep * ctm[1] + y0 * yStep * ctm[3] + ctm[5];
  if (splashAbs(ctm[1]) > splashAbs(ctm[0])) {
    kx = -ctm[1];
    ky = ctm[2] - (ctm[0] * ctm[3]) / ctm[1];
  } else {
    kx = ctm[0];
    ky = ctm[3] - (ctm[1] * ctm[2]) / ctm[0];
  }
  result_width = (int) ceil(fabs(kx * width * (x1 - x0)));
  result_height = (int) ceil(fabs(ky * height * (y1 - y0)));
  kx = state->getHDPI() / 72.0;
  ky = state->getVDPI() / 72.0;
  m1.m[0] = (ptm[0] == 0) ? fabs(ptm[2]) * kx : fabs(ptm[0]) * kx;
  m1.m[1] = 0;
  m1.m[2] = 0;
  m1.m[3] = (ptm[3] == 0) ? fabs(ptm[1]) * ky : fabs(ptm[3]) * ky;
  m1.m[4] = 0;
  m1.m[5] = 0;
  m1.transform(width, height, &kx, &ky);
  surface_width = (int) ceil (fabs(kx));
  surface_height = (int) ceil (fabs(ky));

  sx = (double) result_width / (surface_width * (x1 - x0));
  sy = (double) result_height / (surface_height * (y1 - y0));
  m1.m[0] *= sx;
  m1.m[3] *= sy;
  m1.transform(width, height, &kx, &ky);

  if(fabs(kx) < 1 && fabs(ky) < 1) {
    kx = std::min<double>(kx, ky);
    ky = 2 / kx;
    m1.m[0] *= ky;
    m1.m[3] *= ky;
    m1.transform(width, height, &kx, &ky);
    surface_width = (int) ceil (fabs(kx));
    surface_height = (int) ceil (fabs(ky));
    repeatX = x1 - x0;
    repeatY = y1 - y0;
  } else {
    if ((unsigned long) surface_width * surface_height > 0x800000L) {
      state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
      return false;
    }
    while(fabs(kx) > 16384 || fabs(ky) > 16384) {
      // limit pattern bitmap size
      m1.m[0] /= 2;
      m1.m[3] /= 2;
      m1.transform(width, height, &kx, &ky);
    }
    surface_width = (int) ceil (fabs(kx));
    surface_height = (int) ceil (fabs(ky));
    // adjust repeat values to completely fill region
    if (unlikely(surface_width == 0 || surface_height == 0)) {
        state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
        return false;
    }
    repeatX = result_width / surface_width;
    repeatY = result_height / surface_height;
    if (surface_width * repeatX < result_width)
      repeatX++;
    if (surface_height * repeatY < result_height)
      repeatY++;
    if (x1 - x0 > repeatX)
      repeatX = x1 - x0;
    if (y1 - y0 > repeatY)
      repeatY = y1 - y0;
  }
  // restore CTM and calculate rotate and scale with rounded matrix
  state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
  state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
  state->concatCTM(width * repeatX, 0, 0, height * repeatY, bbox[0], bbox[1]);
  ctm = state->getCTM();
  matc[0] = ctm[0];
  matc[1] = ctm[1];
  matc[2] = ctm[2];
  matc[3] = ctm[3];

  if (surface_width == 0 || surface_height == 0 || repeatX * repeatY <= 4) {
    state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
    return false;
  }
  m1.transform(bbox[0], bbox[1], &kx, &ky);
  m1.m[4] = -kx;
  m1.m[5] = -ky;

  bitmap = new SplashBitmap(surface_width, surface_height, 1,
                            (paintType == 1) ? colorMode : splashModeMono8, true);
  if (bitmap->getDataPtr() == nullptr) {
    SplashBitmap *tBitmap = bitmap;
    bitmap = formerBitmap;
    delete tBitmap;
    state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
    return false;
  }
  splash = new Splash(bitmap, true);
  if (paintType == 2) {
    SplashColor clearColor;
#ifdef SPLASH_CMYK
    clearColor[0] = (colorMode == splashModeCMYK8 || colorMode == splashModeDeviceN8) ? 0x00 : 0xFF;
#else
    clearColor[0] = 0xFF;
#endif
    splash->clear(clearColor, 0);
  } else {
    splash->clear(paperColor, 0);
  }
  splash->setThinLineMode(formerSplash->getThinLineMode());
  splash->setMinLineWidth(s_minLineWidth);

  box.x1 = bbox[0]; box.y1 = bbox[1];
  box.x2 = bbox[2]; box.y2 = bbox[3];
  gfx = new Gfx(doc, this, resDict, &box, nullptr, nullptr, nullptr, gfxA);
  // set pattern transformation matrix
  gfx->getState()->setCTM(m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]);
  updateCTM(gfx->getState(), m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]);
  gfx->display(str);
  delete splash;
  splash = formerSplash;
  TilingSplashOutBitmap imgData;
  imgData.bitmap = bitmap;
  imgData.paintType = paintType;
  imgData.pattern = splash->getFillPattern();
  imgData.colorMode = colorMode;
  imgData.y = 0;
  imgData.repeatX = repeatX;
  imgData.repeatY = repeatY;
  SplashBitmap *tBitmap = bitmap;
  bitmap = formerBitmap;
  result_width = tBitmap->getWidth() * imgData.repeatX;
  result_height = tBitmap->getHeight() * imgData.repeatY;

  if (splashAbs(matc[1]) > splashAbs(matc[0])) {
    kx = -matc[1];
    ky = matc[2] - (matc[0] * matc[3]) / matc[1];
  } else {
    kx = matc[0];
    ky = matc[3] - (matc[1] * matc[2]) / matc[0];
  }
  kx = result_width / (fabs(kx) + 1);
  ky = result_height / (fabs(ky) + 1);
  state->concatCTM(kx, 0, 0, ky, 0, 0);
  ctm = state->getCTM();
  matc[0] = ctm[0];
  matc[1] = ctm[1];
  matc[2] = ctm[2];
  matc[3] = ctm[3];
  bool minorAxisZero = matc[1] == 0 && matc[2] == 0;
  if (matc[0] > 0 && minorAxisZero && matc[3] > 0) {
    // draw the tiles
    for (int y = 0; y < imgData.repeatY; ++y) {
      for (int x = 0; x < imgData.repeatX; ++x) {
        x0 = splashFloor(matc[4]) + x * tBitmap->getWidth();
        y0 = splashFloor(matc[5]) + y * tBitmap->getHeight();
        splash->blitImage(tBitmap, true, x0, y0);
      }
    }
    retValue = true;
  } else {
    retValue = splash->drawImage(&tilingBitmapSrc, nullptr, &imgData, colorMode, true, result_width, result_height, matc, false, true) == splashOk;
  }
  delete tBitmap;
  delete gfx;
  return retValue;
}


static void parse_rtcp_bye(pjmedia_rtcp_session *sess,
			   const void *pkt,
			   pj_size_t size)
{
    pj_str_t reason = {"-", 1};

    /* Check and get BYE reason */
    if (size > 8) {
	reason.slen = PJ_MIN(sizeof(sess->stat.peer_sdes_buf_),
                             *((pj_uint8_t*)pkt+8));
	pj_memcpy(sess->stat.peer_sdes_buf_, ((pj_uint8_t*)pkt+9),
		  reason.slen);
	reason.ptr = sess->stat.peer_sdes_buf_;
    }

    /* Just print RTCP BYE log */
    PJ_LOG(5, (sess->name, "Received RTCP BYE, reason: %.*s",
	       reason.slen, reason.ptr));
}

Merge pull request from GHSA-3qx3-cg72-wrh9


static void parse_rtcp_bye(pjmedia_rtcp_session *sess,
			   const void *pkt,
			   pj_size_t size)
{
    pj_str_t reason = {"-", 1};

    /* Check and get BYE reason */
    if (size > 8) {
    	/* Make sure the BYE reason does not exceed:
    	 * - the size of the available buffer
    	 * - the declared reason's length
    	 * - the actual packet size
    	 */
	reason.slen = PJ_MIN(sizeof(sess->stat.peer_sdes_buf_),
                             *((pj_uint8_t*)pkt+8));
        reason.slen = PJ_MIN(reason.slen, size-9);

	pj_memcpy(sess->stat.peer_sdes_buf_, ((pj_uint8_t*)pkt+9),
		  reason.slen);
	reason.ptr = sess->stat.peer_sdes_buf_;
    }

    /* Just print RTCP BYE log */
    PJ_LOG(5, (sess->name, "Received RTCP BYE, reason: %.*s",
	       reason.slen, reason.ptr));
}


eval7(
    char_u	**arg,
    typval_T	*rettv,
    evalarg_T	*evalarg,
    int		want_string)	// after "." operator
{
    int		evaluate = evalarg != NULL
				      && (evalarg->eval_flags & EVAL_EVALUATE);
    int		len;
    char_u	*s;
    char_u	*name_start = NULL;
    char_u	*start_leader, *end_leader;
    int		ret = OK;
    char_u	*alias;

    /*
     * Initialise variable so that clear_tv() can't mistake this for a
     * string and free a string that isn't there.
     */
    rettv->v_type = VAR_UNKNOWN;

    /*
     * Skip '!', '-' and '+' characters.  They are handled later.
     */
    start_leader = *arg;
    if (eval_leader(arg, in_vim9script()) == FAIL)
	return FAIL;
    end_leader = *arg;

    if (**arg == '.' && (!isdigit(*(*arg + 1))
#ifdef FEAT_FLOAT
	    || in_old_script(2)
#endif
	    ))
    {
	semsg(_(e_invalid_expression_str), *arg);
	++*arg;
	return FAIL;
    }

    switch (**arg)
    {
    /*
     * Number constant.
     */
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
    case '.':	ret = eval_number(arg, rettv, evaluate, want_string);

		// Apply prefixed "-" and "+" now.  Matters especially when
		// "->" follows.
		if (ret == OK && evaluate && end_leader > start_leader
						  && rettv->v_type != VAR_BLOB)
		    ret = eval7_leader(rettv, TRUE, start_leader, &end_leader);
		break;

    /*
     * String constant: "string".
     */
    case '"':	ret = eval_string(arg, rettv, evaluate);
		break;

    /*
     * Literal string constant: 'str''ing'.
     */
    case '\'':	ret = eval_lit_string(arg, rettv, evaluate);
		break;

    /*
     * List: [expr, expr]
     */
    case '[':	ret = eval_list(arg, rettv, evalarg, TRUE);
		break;

    /*
     * Dictionary: #{key: val, key: val}
     */
    case '#':	if (in_vim9script())
		{
		    ret = vim9_bad_comment(*arg) ? FAIL : NOTDONE;
		}
		else if ((*arg)[1] == '{')
		{
		    ++*arg;
		    ret = eval_dict(arg, rettv, evalarg, TRUE);
		}
		else
		    ret = NOTDONE;
		break;

    /*
     * Lambda: {arg, arg -> expr}
     * Dictionary: {'key': val, 'key': val}
     */
    case '{':	if (in_vim9script())
		    ret = NOTDONE;
		else
		    ret = get_lambda_tv(arg, rettv, in_vim9script(), evalarg);
		if (ret == NOTDONE)
		    ret = eval_dict(arg, rettv, evalarg, FALSE);
		break;

    /*
     * Option value: &name
     */
    case '&':	ret = eval_option(arg, rettv, evaluate);
		break;

    /*
     * Environment variable: $VAR.
     */
    case '$':	ret = eval_env_var(arg, rettv, evaluate);
		break;

    /*
     * Register contents: @r.
     */
    case '@':	++*arg;
		if (evaluate)
		{
		    if (in_vim9script() && IS_WHITE_OR_NUL(**arg))
			semsg(_(e_syntax_error_at_str), *arg);
		    else if (in_vim9script() && !valid_yank_reg(**arg, FALSE))
			emsg_invreg(**arg);
		    else
		    {
			rettv->v_type = VAR_STRING;
			rettv->vval.v_string = get_reg_contents(**arg,
								GREG_EXPR_SRC);
		    }
		}
		if (**arg != NUL)
		    ++*arg;
		break;

    /*
     * nested expression: (expression).
     * or lambda: (arg) => expr
     */
    case '(':	ret = NOTDONE;
		if (in_vim9script())
		{
		    ret = get_lambda_tv(arg, rettv, TRUE, evalarg);
		    if (ret == OK && evaluate)
		    {
			ufunc_T *ufunc = rettv->vval.v_partial->pt_func;

			// Compile it here to get the return type.  The return
			// type is optional, when it's missing use t_unknown.
			// This is recognized in compile_return().
			if (ufunc->uf_ret_type->tt_type == VAR_VOID)
			    ufunc->uf_ret_type = &t_unknown;
			if (compile_def_function(ufunc,
				     FALSE, COMPILE_TYPE(ufunc), NULL) == FAIL)
			{
			    clear_tv(rettv);
			    ret = FAIL;
			}
		    }
		}
		if (ret == NOTDONE)
		{
		    *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
		    ret = eval1(arg, rettv, evalarg);	// recursive!

		    *arg = skipwhite_and_linebreak(*arg, evalarg);
		    if (**arg == ')')
			++*arg;
		    else if (ret == OK)
		    {
			emsg(_(e_missing_closing_paren));
			clear_tv(rettv);
			ret = FAIL;
		    }
		}
		break;

    default:	ret = NOTDONE;
		break;
    }

    if (ret == NOTDONE)
    {
	/*
	 * Must be a variable or function name.
	 * Can also be a curly-braces kind of name: {expr}.
	 */
	s = *arg;
	len = get_name_len(arg, &alias, evaluate, TRUE);
	if (alias != NULL)
	    s = alias;

	if (len <= 0)
	    ret = FAIL;
	else
	{
	    int	    flags = evalarg == NULL ? 0 : evalarg->eval_flags;

	    if (evaluate && in_vim9script() && len == 1 && *s == '_')
	    {
		emsg(_(e_cannot_use_underscore_here));
		ret = FAIL;
	    }
	    else if ((in_vim9script() ? **arg : *skipwhite(*arg)) == '(')
	    {
		// "name(..."  recursive!
		*arg = skipwhite(*arg);
		ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL);
	    }
	    else if (flags & EVAL_CONSTANT)
		ret = FAIL;
	    else if (evaluate)
	    {
		// get the value of "true", "false" or a variable
		if (len == 4 && in_vim9script() && STRNCMP(s, "true", 4) == 0)
		{
		    rettv->v_type = VAR_BOOL;
		    rettv->vval.v_number = VVAL_TRUE;
		    ret = OK;
		}
		else if (len == 5 && in_vim9script()
						&& STRNCMP(s, "false", 5) == 0)
		{
		    rettv->v_type = VAR_BOOL;
		    rettv->vval.v_number = VVAL_FALSE;
		    ret = OK;
		}
		else if (len == 4 && in_vim9script()
						&& STRNCMP(s, "null", 4) == 0)
		{
		    rettv->v_type = VAR_SPECIAL;
		    rettv->vval.v_number = VVAL_NULL;
		    ret = OK;
		}
		else
		{
		    name_start = s;
		    ret = eval_variable(s, len, 0, rettv, NULL,
					   EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT);
		}
	    }
	    else
	    {
		// skip the name
		check_vars(s, len);
		ret = OK;
	    }
	}
	vim_free(alias);
    }

    // Handle following '[', '(' and '.' for expr[expr], expr.name,
    // expr(expr), expr->name(expr)
    if (ret == OK)
	ret = handle_subscript(arg, name_start, rettv, evalarg, TRUE);

    /*
     * Apply logical NOT and unary '-', from right to left, ignore '+'.
     */
    if (ret == OK && evaluate && end_leader > start_leader)
	ret = eval7_leader(rettv, FALSE, start_leader, &end_leader);
    return ret;
}

patch 8.2.4206: condition with many "(" causes a crash

Problem:    Condition with many "(" causes a crash.
Solution:   Limit recursion to 1000.


eval7(
    char_u	**arg,
    typval_T	*rettv,
    evalarg_T	*evalarg,
    int		want_string)	// after "." operator
{
    int		evaluate = evalarg != NULL
				      && (evalarg->eval_flags & EVAL_EVALUATE);
    int		len;
    char_u	*s;
    char_u	*name_start = NULL;
    char_u	*start_leader, *end_leader;
    int		ret = OK;
    char_u	*alias;
    static	int recurse = 0;

    /*
     * Initialise variable so that clear_tv() can't mistake this for a
     * string and free a string that isn't there.
     */
    rettv->v_type = VAR_UNKNOWN;

    /*
     * Skip '!', '-' and '+' characters.  They are handled later.
     */
    start_leader = *arg;
    if (eval_leader(arg, in_vim9script()) == FAIL)
	return FAIL;
    end_leader = *arg;

    if (**arg == '.' && (!isdigit(*(*arg + 1))
#ifdef FEAT_FLOAT
	    || in_old_script(2)
#endif
	    ))
    {
	semsg(_(e_invalid_expression_str), *arg);
	++*arg;
	return FAIL;
    }

    // Limit recursion to 1000 levels.  At least at 10000 we run out of stack
    // and crash.
    if (recurse == 1000)
    {
	semsg(_(e_expression_too_recursive_str), *arg);
	return FAIL;
    }
    ++recurse;

    switch (**arg)
    {
    /*
     * Number constant.
     */
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
    case '.':	ret = eval_number(arg, rettv, evaluate, want_string);

		// Apply prefixed "-" and "+" now.  Matters especially when
		// "->" follows.
		if (ret == OK && evaluate && end_leader > start_leader
						  && rettv->v_type != VAR_BLOB)
		    ret = eval7_leader(rettv, TRUE, start_leader, &end_leader);
		break;

    /*
     * String constant: "string".
     */
    case '"':	ret = eval_string(arg, rettv, evaluate);
		break;

    /*
     * Literal string constant: 'str''ing'.
     */
    case '\'':	ret = eval_lit_string(arg, rettv, evaluate);
		break;

    /*
     * List: [expr, expr]
     */
    case '[':	ret = eval_list(arg, rettv, evalarg, TRUE);
		break;

    /*
     * Dictionary: #{key: val, key: val}
     */
    case '#':	if (in_vim9script())
		{
		    ret = vim9_bad_comment(*arg) ? FAIL : NOTDONE;
		}
		else if ((*arg)[1] == '{')
		{
		    ++*arg;
		    ret = eval_dict(arg, rettv, evalarg, TRUE);
		}
		else
		    ret = NOTDONE;
		break;

    /*
     * Lambda: {arg, arg -> expr}
     * Dictionary: {'key': val, 'key': val}
     */
    case '{':	if (in_vim9script())
		    ret = NOTDONE;
		else
		    ret = get_lambda_tv(arg, rettv, in_vim9script(), evalarg);
		if (ret == NOTDONE)
		    ret = eval_dict(arg, rettv, evalarg, FALSE);
		break;

    /*
     * Option value: &name
     */
    case '&':	ret = eval_option(arg, rettv, evaluate);
		break;

    /*
     * Environment variable: $VAR.
     */
    case '$':	ret = eval_env_var(arg, rettv, evaluate);
		break;

    /*
     * Register contents: @r.
     */
    case '@':	++*arg;
		if (evaluate)
		{
		    if (in_vim9script() && IS_WHITE_OR_NUL(**arg))
			semsg(_(e_syntax_error_at_str), *arg);
		    else if (in_vim9script() && !valid_yank_reg(**arg, FALSE))
			emsg_invreg(**arg);
		    else
		    {
			rettv->v_type = VAR_STRING;
			rettv->vval.v_string = get_reg_contents(**arg,
								GREG_EXPR_SRC);
		    }
		}
		if (**arg != NUL)
		    ++*arg;
		break;

    /*
     * nested expression: (expression).
     * or lambda: (arg) => expr
     */
    case '(':	ret = NOTDONE;
		if (in_vim9script())
		{
		    ret = get_lambda_tv(arg, rettv, TRUE, evalarg);
		    if (ret == OK && evaluate)
		    {
			ufunc_T *ufunc = rettv->vval.v_partial->pt_func;

			// Compile it here to get the return type.  The return
			// type is optional, when it's missing use t_unknown.
			// This is recognized in compile_return().
			if (ufunc->uf_ret_type->tt_type == VAR_VOID)
			    ufunc->uf_ret_type = &t_unknown;
			if (compile_def_function(ufunc,
				     FALSE, COMPILE_TYPE(ufunc), NULL) == FAIL)
			{
			    clear_tv(rettv);
			    ret = FAIL;
			}
		    }
		}
		if (ret == NOTDONE)
		{
		    *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
		    ret = eval1(arg, rettv, evalarg);	// recursive!

		    *arg = skipwhite_and_linebreak(*arg, evalarg);
		    if (**arg == ')')
			++*arg;
		    else if (ret == OK)
		    {
			emsg(_(e_missing_closing_paren));
			clear_tv(rettv);
			ret = FAIL;
		    }
		}
		break;

    default:	ret = NOTDONE;
		break;
    }

    if (ret == NOTDONE)
    {
	/*
	 * Must be a variable or function name.
	 * Can also be a curly-braces kind of name: {expr}.
	 */
	s = *arg;
	len = get_name_len(arg, &alias, evaluate, TRUE);
	if (alias != NULL)
	    s = alias;

	if (len <= 0)
	    ret = FAIL;
	else
	{
	    int	    flags = evalarg == NULL ? 0 : evalarg->eval_flags;

	    if (evaluate && in_vim9script() && len == 1 && *s == '_')
	    {
		emsg(_(e_cannot_use_underscore_here));
		ret = FAIL;
	    }
	    else if ((in_vim9script() ? **arg : *skipwhite(*arg)) == '(')
	    {
		// "name(..."  recursive!
		*arg = skipwhite(*arg);
		ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL);
	    }
	    else if (flags & EVAL_CONSTANT)
		ret = FAIL;
	    else if (evaluate)
	    {
		// get the value of "true", "false" or a variable
		if (len == 4 && in_vim9script() && STRNCMP(s, "true", 4) == 0)
		{
		    rettv->v_type = VAR_BOOL;
		    rettv->vval.v_number = VVAL_TRUE;
		    ret = OK;
		}
		else if (len == 5 && in_vim9script()
						&& STRNCMP(s, "false", 5) == 0)
		{
		    rettv->v_type = VAR_BOOL;
		    rettv->vval.v_number = VVAL_FALSE;
		    ret = OK;
		}
		else if (len == 4 && in_vim9script()
						&& STRNCMP(s, "null", 4) == 0)
		{
		    rettv->v_type = VAR_SPECIAL;
		    rettv->vval.v_number = VVAL_NULL;
		    ret = OK;
		}
		else
		{
		    name_start = s;
		    ret = eval_variable(s, len, 0, rettv, NULL,
					   EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT);
		}
	    }
	    else
	    {
		// skip the name
		check_vars(s, len);
		ret = OK;
	    }
	}
	vim_free(alias);
    }

    // Handle following '[', '(' and '.' for expr[expr], expr.name,
    // expr(expr), expr->name(expr)
    if (ret == OK)
	ret = handle_subscript(arg, name_start, rettv, evalarg, TRUE);

    /*
     * Apply logical NOT and unary '-', from right to left, ignore '+'.
     */
    if (ret == OK && evaluate && end_leader > start_leader)
	ret = eval7_leader(rettv, FALSE, start_leader, &end_leader);

    --recurse;
    return ret;
}


static void build_dirs(char *src, char *dst, size_t src_prefix_len, size_t dst_prefix_len) {
	char *p = src + src_prefix_len + 1;
	char *q = dst + dst_prefix_len + 1;
	char *r = dst + dst_prefix_len;
	struct stat s;
	bool last = false;
	*r = '\0';
	for (; !last; p++, q++) {
		if (*p == '\0') {
			last = true;
		}
		if (*p == '\0' || (*p == '/' && *(p - 1) != '/')) {
			// We found a new component of our src path.
			// Null-terminate it temporarily here so that we can work
			// with it.
			*p = '\0';
			if (stat(src, &s) == 0 && S_ISDIR(s.st_mode)) {
				// Null-terminate the dst path and undo its previous
				// termination.
				*q = '\0';
				*r = '/';
				r = q;
				mkdir_attr(dst, s.st_mode, 0, 0);
			}
			if (!last) {
				// If we're not at the final terminating null, restore
				// the slash so that we can continue our traversal.
				*p = '/';
			}
		}
	}
}

CVE-2022-31214: fixing the fix, one more time

the previous commit "CVE-2022-31214: fixing the fix"
made private-etc=fonts,fonts and similar commands
fail with an error

fix that regression by tolerating already existing
directories


static void build_dirs(char *src, char *dst, size_t src_prefix_len, size_t dst_prefix_len) {
	char *p = src + src_prefix_len + 1;
	char *q = dst + dst_prefix_len + 1;
	char *r = dst + dst_prefix_len;
	struct stat s;
	bool last = false;
	*r = '\0';
	for (; !last; p++, q++) {
		if (*p == '\0') {
			last = true;
		}
		if (*p == '\0' || (*p == '/' && *(p - 1) != '/')) {
			// We found a new component of our src path.
			// Null-terminate it temporarily here so that we can work
			// with it.
			*p = '\0';
			if (stat(src, &s) == 0 && S_ISDIR(s.st_mode)) {
				// Null-terminate the dst path and undo its previous
				// termination.
				*q = '\0';
				*r = '/';
				r = q;
				if (mkdir(dst, 0700) != 0 && errno != EEXIST)
					errExit("mkdir");
				if (chmod(dst, s.st_mode) != 0)
					errExit("chmod");
			}
			if (!last) {
				// If we're not at the final terminating null, restore
				// the slash so that we can continue our traversal.
				*p = '/';
			}
		}
	}
}


                   Statement::BindParameter(const Napi::Value source, T pos) {
    if (source.IsString()) {
        std::string val = source.As<Napi::String>().Utf8Value();
        return new Values::Text(pos, val.length(), val.c_str());
    }
    else if (OtherInstanceOf(source.As<Object>(), "RegExp")) {
        std::string val = source.ToString().Utf8Value();
        return new Values::Text(pos, val.length(), val.c_str());
    }
    else if (source.IsNumber()) {
        if (OtherIsInt(source.As<Napi::Number>())) {
            return new Values::Integer(pos, source.As<Napi::Number>().Int32Value());
        } else {
            return new Values::Float(pos, source.As<Napi::Number>().DoubleValue());
        }
    }
    else if (source.IsBoolean()) {
        return new Values::Integer(pos, source.As<Napi::Boolean>().Value() ? 1 : 0);
    }
    else if (source.IsNull()) {
        return new Values::Null(pos);
    }
    else if (source.IsBuffer()) {
        Napi::Buffer<char> buffer = source.As<Napi::Buffer<char>>();
        return new Values::Blob(pos, buffer.Length(), buffer.Data());
    }
    else if (OtherInstanceOf(source.As<Object>(), "Date")) {
        return new Values::Float(pos, source.ToNumber().DoubleValue());
    }
    else if (source.IsObject()) {
        std::string val = source.ToString().Utf8Value();
        return new Values::Text(pos, val.length(), val.c_str());
    }
    else {
        return NULL;
    }
}

bug: fix segfault of invalid toString() object (#1450)

* bug: verify toString() returns valid data
* test: faulty toString test


                   Statement::BindParameter(const Napi::Value source, T pos) {
    if (source.IsString()) {
        std::string val = source.As<Napi::String>().Utf8Value();
        return new Values::Text(pos, val.length(), val.c_str());
    }
    else if (OtherInstanceOf(source.As<Object>(), "RegExp")) {
        std::string val = source.ToString().Utf8Value();
        return new Values::Text(pos, val.length(), val.c_str());
    }
    else if (source.IsNumber()) {
        if (OtherIsInt(source.As<Napi::Number>())) {
            return new Values::Integer(pos, source.As<Napi::Number>().Int32Value());
        } else {
            return new Values::Float(pos, source.As<Napi::Number>().DoubleValue());
        }
    }
    else if (source.IsBoolean()) {
        return new Values::Integer(pos, source.As<Napi::Boolean>().Value() ? 1 : 0);
    }
    else if (source.IsNull()) {
        return new Values::Null(pos);
    }
    else if (source.IsBuffer()) {
        Napi::Buffer<char> buffer = source.As<Napi::Buffer<char>>();
        return new Values::Blob(pos, buffer.Length(), buffer.Data());
    }
    else if (OtherInstanceOf(source.As<Object>(), "Date")) {
        return new Values::Float(pos, source.ToNumber().DoubleValue());
    }
    else if (source.IsObject()) {
        Napi::String napiVal = source.ToString();
        // Check whether toString returned a value that is not undefined.
        if(napiVal.Type() == 0) {
            return NULL;
        }

        std::string val = napiVal.Utf8Value();
        return new Values::Text(pos, val.length(), val.c_str());
    }
    else {
        return NULL;
    }
}


bool SQClass::NewSlot(SQSharedState *ss,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic)
{
    SQObjectPtr temp;
    bool belongs_to_static_table = sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE || bstatic;
    if(_locked && !belongs_to_static_table)
        return false; //the class already has an instance so cannot be modified
    if(_members->Get(key,temp) && _isfield(temp)) //overrides the default value
    {
        _defaultvalues[_member_idx(temp)].val = val;
        return true;
    }
    if(belongs_to_static_table) {
        SQInteger mmidx;
        if((sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE) &&
            (mmidx = ss->GetMetaMethodIdxByName(key)) != -1) {
            _metamethods[mmidx] = val;
        }
        else {
            SQObjectPtr theval = val;
            if(_base && sq_type(val) == OT_CLOSURE) {
                theval = _closure(val)->Clone();
                _closure(theval)->_base = _base;
                __ObjAddRef(_base); //ref for the closure
            }
            if(sq_type(temp) == OT_NULL) {
                bool isconstructor;
                SQVM::IsEqual(ss->_constructoridx, key, isconstructor);
                if(isconstructor) {
                    _constructoridx = (SQInteger)_methods.size();
                }
                SQClassMember m;
                m.val = theval;
                _members->NewSlot(key,SQObjectPtr(_make_method_idx(_methods.size())));
                _methods.push_back(m);
            }
            else {
                _methods[_member_idx(temp)].val = theval;
            }
        }
        return true;
    }
    SQClassMember m;
    m.val = val;
    _members->NewSlot(key,SQObjectPtr(_make_field_idx(_defaultvalues.size())));
    _defaultvalues.push_back(m);
    return true;
}

check max member count in class


bool SQClass::NewSlot(SQSharedState *ss,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic)
{
    SQObjectPtr temp;
    bool belongs_to_static_table = sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE || bstatic;
    if(_locked && !belongs_to_static_table)
        return false; //the class already has an instance so cannot be modified
    if(_members->Get(key,temp) && _isfield(temp)) //overrides the default value
    {
        _defaultvalues[_member_idx(temp)].val = val;
        return true;
    }
	if (_members->CountUsed() >= MEMBER_MAX_COUNT) {
		return false;
	}
    if(belongs_to_static_table) {
        SQInteger mmidx;
        if((sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE) &&
            (mmidx = ss->GetMetaMethodIdxByName(key)) != -1) {
            _metamethods[mmidx] = val;
        }
        else {
            SQObjectPtr theval = val;
            if(_base && sq_type(val) == OT_CLOSURE) {
                theval = _closure(val)->Clone();
                _closure(theval)->_base = _base;
                __ObjAddRef(_base); //ref for the closure
            }
            if(sq_type(temp) == OT_NULL) {
                bool isconstructor;
                SQVM::IsEqual(ss->_constructoridx, key, isconstructor);
                if(isconstructor) {
                    _constructoridx = (SQInteger)_methods.size();
                }
                SQClassMember m;
                m.val = theval;
                _members->NewSlot(key,SQObjectPtr(_make_method_idx(_methods.size())));
                _methods.push_back(m);
            }
            else {
                _methods[_member_idx(temp)].val = theval;
            }
        }
        return true;
    }
    SQClassMember m;
    m.val = val;
    _members->NewSlot(key,SQObjectPtr(_make_field_idx(_defaultvalues.size())));
    _defaultvalues.push_back(m);
    return true;
}

check max member count in class


sonmp_decode(struct lldpd *cfg, char *frame, int s,
    struct lldpd_hardware *hardware,
    struct lldpd_chassis **newchassis, struct lldpd_port **newport)
{
	const u_int8_t mcastaddr[] = SONMP_MULTICAST_ADDR;
	struct lldpd_chassis *chassis;
	struct lldpd_port *port;
	struct lldpd_mgmt *mgmt;
	int length, i;
	u_int8_t *pos;
	u_int8_t seg[3], rchassis;
	struct in_addr address;

	log_debug("sonmp", "decode SONMP PDU from %s",
	    hardware->h_ifname);

	if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
		log_warn("sonmp", "failed to allocate remote chassis");
		return -1;
	}
	TAILQ_INIT(&chassis->c_mgmt);
	if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
		log_warn("sonmp", "failed to allocate remote port");
		free(chassis);
		return -1;
	}
#ifdef ENABLE_DOT1
	TAILQ_INIT(&port->p_vlans);
#endif

	length = s;
	pos = (u_int8_t*)frame;
	if (length < SONMP_SIZE) {
		log_warnx("sonmp", "too short SONMP frame received on %s", hardware->h_ifname);
		goto malformed;
	}
	if (PEEK_CMP(mcastaddr, sizeof(mcastaddr)) != 0)
		/* There is two multicast address. We just handle only one of
		 * them. */
		goto malformed;
	/* We skip to LLC PID */
	PEEK_DISCARD(ETHER_ADDR_LEN); PEEK_DISCARD_UINT16;
	PEEK_DISCARD(6);
	if (PEEK_UINT16 != LLC_PID_SONMP_HELLO) {
		log_debug("sonmp", "incorrect LLC protocol ID received for SONMP on %s",
		    hardware->h_ifname);
		goto malformed;
	}

	chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_ADDR;
	if ((chassis->c_id = calloc(1, sizeof(struct in_addr) + 1)) == NULL) {
		log_warn("sonmp", "unable to allocate memory for chassis id on %s",
			hardware->h_ifname);
		goto malformed;
	}
	chassis->c_id_len = sizeof(struct in_addr) + 1;
	chassis->c_id[0] = 1;
	PEEK_BYTES(&address, sizeof(struct in_addr));
	memcpy(chassis->c_id + 1, &address, sizeof(struct in_addr));
	if (asprintf(&chassis->c_name, "%s", inet_ntoa(address)) == -1) {
		log_warnx("sonmp", "unable to write chassis name for %s",
		    hardware->h_ifname);
		goto malformed;
	}
	PEEK_BYTES(seg, sizeof(seg));
	rchassis = PEEK_UINT8;
	for (i=0; sonmp_chassis_types[i].type != 0; i++) {
		if (sonmp_chassis_types[i].type == rchassis)
			break;
	}
	if (asprintf(&chassis->c_descr, "%s",
		sonmp_chassis_types[i].description) == -1) {
		log_warnx("sonmp", "unable to write chassis description for %s",
		    hardware->h_ifname);
		goto malformed;
	}
	mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, &address, sizeof(struct in_addr), 0);
	if (mgmt == NULL) {
		if (errno == ENOMEM)
			log_warn("sonmp", "unable to allocate memory for management address");
		else
			log_warn("sonmp", "too large management address received on %s",
			    hardware->h_ifname);
		goto malformed;
	}
	TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries);
	port->p_ttl = cfg?(cfg->g_config.c_tx_interval * cfg->g_config.c_tx_hold):
	    LLDPD_TTL;
	port->p_ttl = (port->p_ttl + 999) / 1000;

	port->p_id_subtype = LLDP_PORTID_SUBTYPE_LOCAL;
	if (asprintf(&port->p_id, "%02x-%02x-%02x",
		seg[0], seg[1], seg[2]) == -1) {
		log_warn("sonmp", "unable to allocate memory for port id on %s",
		    hardware->h_ifname);
		goto malformed;
	}
	port->p_id_len = strlen(port->p_id);

	/* Port description depend on the number of segments */
	if ((seg[0] == 0) && (seg[1] == 0)) {
		if (asprintf(&port->p_descr, "port %d",
			seg[2]) == -1) {
			log_warnx("sonmp", "unable to write port description for %s",
			    hardware->h_ifname);
			goto malformed;
		}
	} else if (seg[0] == 0) {
		if (asprintf(&port->p_descr, "port %d/%d",
			seg[1], seg[2]) == -1) {
			log_warnx("sonmp", "unable to write port description for %s",
			    hardware->h_ifname);
			goto malformed;
		}
	} else {
		if (asprintf(&port->p_descr, "port %x:%x:%x",
			seg[0], seg[1], seg[2]) == -1) {
			log_warnx("sonmp", "unable to write port description for %s",
			    hardware->h_ifname);
			goto malformed;
		}
	}
	*newchassis = chassis;
	*newport = port;
	return 1;

malformed:
	lldpd_chassis_cleanup(chassis, 1);
	lldpd_port_cleanup(port, 1);
	free(port);
	return -1;
}

sonmp: fix heap overflow when reading SONMP packets

By sending short SONMP packets, an attacker can make the decoder crash
by reading too much data on the heap. SONMP packets are fixed in size,
just ensure we get the enough bytes to contain a SONMP packet.

CVE-2021-43612


sonmp_decode(struct lldpd *cfg, char *frame, int s,
    struct lldpd_hardware *hardware,
    struct lldpd_chassis **newchassis, struct lldpd_port **newport)
{
	const u_int8_t mcastaddr[] = SONMP_MULTICAST_ADDR;
	struct lldpd_chassis *chassis;
	struct lldpd_port *port;
	struct lldpd_mgmt *mgmt;
	int length, i;
	u_int8_t *pos;
	u_int8_t seg[3], rchassis;
	struct in_addr address;

	log_debug("sonmp", "decode SONMP PDU from %s",
	    hardware->h_ifname);

	if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) {
		log_warn("sonmp", "failed to allocate remote chassis");
		return -1;
	}
	TAILQ_INIT(&chassis->c_mgmt);
	if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) {
		log_warn("sonmp", "failed to allocate remote port");
		free(chassis);
		return -1;
	}
#ifdef ENABLE_DOT1
	TAILQ_INIT(&port->p_vlans);
#endif

	length = s;
	pos = (u_int8_t*)frame;
	if (length < SONMP_SIZE + 2*ETHER_ADDR_LEN + sizeof(u_int16_t)) {
		log_warnx("sonmp", "too short SONMP frame received on %s", hardware->h_ifname);
		goto malformed;
	}
	if (PEEK_CMP(mcastaddr, sizeof(mcastaddr)) != 0)
		/* There is two multicast address. We just handle only one of
		 * them. */
		goto malformed;
	/* We skip to LLC PID */
	PEEK_DISCARD(ETHER_ADDR_LEN); PEEK_DISCARD_UINT16;
	PEEK_DISCARD(6);
	if (PEEK_UINT16 != LLC_PID_SONMP_HELLO) {
		log_debug("sonmp", "incorrect LLC protocol ID received for SONMP on %s",
		    hardware->h_ifname);
		goto malformed;
	}

	chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_ADDR;
	if ((chassis->c_id = calloc(1, sizeof(struct in_addr) + 1)) == NULL) {
		log_warn("sonmp", "unable to allocate memory for chassis id on %s",
			hardware->h_ifname);
		goto malformed;
	}
	chassis->c_id_len = sizeof(struct in_addr) + 1;
	chassis->c_id[0] = 1;
	PEEK_BYTES(&address, sizeof(struct in_addr));
	memcpy(chassis->c_id + 1, &address, sizeof(struct in_addr));
	if (asprintf(&chassis->c_name, "%s", inet_ntoa(address)) == -1) {
		log_warnx("sonmp", "unable to write chassis name for %s",
		    hardware->h_ifname);
		goto malformed;
	}
	PEEK_BYTES(seg, sizeof(seg));
	rchassis = PEEK_UINT8;
	for (i=0; sonmp_chassis_types[i].type != 0; i++) {
		if (sonmp_chassis_types[i].type == rchassis)
			break;
	}
	if (asprintf(&chassis->c_descr, "%s",
		sonmp_chassis_types[i].description) == -1) {
		log_warnx("sonmp", "unable to write chassis description for %s",
		    hardware->h_ifname);
		goto malformed;
	}
	mgmt = lldpd_alloc_mgmt(LLDPD_AF_IPV4, &address, sizeof(struct in_addr), 0);
	if (mgmt == NULL) {
		if (errno == ENOMEM)
			log_warn("sonmp", "unable to allocate memory for management address");
		else
			log_warn("sonmp", "too large management address received on %s",
			    hardware->h_ifname);
		goto malformed;
	}
	TAILQ_INSERT_TAIL(&chassis->c_mgmt, mgmt, m_entries);
	port->p_ttl = cfg?(cfg->g_config.c_tx_interval * cfg->g_config.c_tx_hold):
	    LLDPD_TTL;
	port->p_ttl = (port->p_ttl + 999) / 1000;

	port->p_id_subtype = LLDP_PORTID_SUBTYPE_LOCAL;
	if (asprintf(&port->p_id, "%02x-%02x-%02x",
		seg[0], seg[1], seg[2]) == -1) {
		log_warn("sonmp", "unable to allocate memory for port id on %s",
		    hardware->h_ifname);
		goto malformed;
	}
	port->p_id_len = strlen(port->p_id);

	/* Port description depend on the number of segments */
	if ((seg[0] == 0) && (seg[1] == 0)) {
		if (asprintf(&port->p_descr, "port %d",
			seg[2]) == -1) {
			log_warnx("sonmp", "unable to write port description for %s",
			    hardware->h_ifname);
			goto malformed;
		}
	} else if (seg[0] == 0) {
		if (asprintf(&port->p_descr, "port %d/%d",
			seg[1], seg[2]) == -1) {
			log_warnx("sonmp", "unable to write port description for %s",
			    hardware->h_ifname);
			goto malformed;
		}
	} else {
		if (asprintf(&port->p_descr, "port %x:%x:%x",
			seg[0], seg[1], seg[2]) == -1) {
			log_warnx("sonmp", "unable to write port description for %s",
			    hardware->h_ifname);
			goto malformed;
		}
	}
	*newchassis = chassis;
	*newport = port;
	return 1;

malformed:
	lldpd_chassis_cleanup(chassis, 1);
	lldpd_port_cleanup(port, 1);
	free(port);
	return -1;
}


static void *__bpf_ringbuf_reserve(struct bpf_ringbuf *rb, u64 size)
{
	unsigned long cons_pos, prod_pos, new_prod_pos, flags;
	u32 len, pg_off;
	struct bpf_ringbuf_hdr *hdr;

	if (unlikely(size > RINGBUF_MAX_RECORD_SZ))
		return NULL;

	len = round_up(size + BPF_RINGBUF_HDR_SZ, 8);
	cons_pos = smp_load_acquire(&rb->consumer_pos);

	if (in_nmi()) {
		if (!spin_trylock_irqsave(&rb->spinlock, flags))
			return NULL;
	} else {
		spin_lock_irqsave(&rb->spinlock, flags);
	}

	prod_pos = rb->producer_pos;
	new_prod_pos = prod_pos + len;

	/* check for out of ringbuf space by ensuring producer position
	 * doesn't advance more than (ringbuf_size - 1) ahead
	 */
	if (new_prod_pos - cons_pos > rb->mask) {
		spin_unlock_irqrestore(&rb->spinlock, flags);
		return NULL;
	}

	hdr = (void *)rb->data + (prod_pos & rb->mask);
	pg_off = bpf_ringbuf_rec_pg_off(rb, hdr);
	hdr->len = size | BPF_RINGBUF_BUSY_BIT;
	hdr->pg_off = pg_off;

	/* pairs with consumer's smp_load_acquire() */
	smp_store_release(&rb->producer_pos, new_prod_pos);

	spin_unlock_irqrestore(&rb->spinlock, flags);

	return (void *)hdr + BPF_RINGBUF_HDR_SZ;
}

bpf, ringbuf: Deny reserve of buffers larger than ringbuf

A BPF program might try to reserve a buffer larger than the ringbuf size.
If the consumer pointer is way ahead of the producer, that would be
successfully reserved, allowing the BPF program to read or write out of
the ringbuf allocated area.

Reported-by: Ryota Shiga (Flatt Security)
Fixes: 457f44363a88 ("bpf: Implement BPF ring buffer and verifier support for it")
Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Alexei Starovoitov <ast@kernel.org>


static void *__bpf_ringbuf_reserve(struct bpf_ringbuf *rb, u64 size)
{
	unsigned long cons_pos, prod_pos, new_prod_pos, flags;
	u32 len, pg_off;
	struct bpf_ringbuf_hdr *hdr;

	if (unlikely(size > RINGBUF_MAX_RECORD_SZ))
		return NULL;

	len = round_up(size + BPF_RINGBUF_HDR_SZ, 8);
	if (len > rb->mask + 1)
		return NULL;

	cons_pos = smp_load_acquire(&rb->consumer_pos);

	if (in_nmi()) {
		if (!spin_trylock_irqsave(&rb->spinlock, flags))
			return NULL;
	} else {
		spin_lock_irqsave(&rb->spinlock, flags);
	}

	prod_pos = rb->producer_pos;
	new_prod_pos = prod_pos + len;

	/* check for out of ringbuf space by ensuring producer position
	 * doesn't advance more than (ringbuf_size - 1) ahead
	 */
	if (new_prod_pos - cons_pos > rb->mask) {
		spin_unlock_irqrestore(&rb->spinlock, flags);
		return NULL;
	}

	hdr = (void *)rb->data + (prod_pos & rb->mask);
	pg_off = bpf_ringbuf_rec_pg_off(rb, hdr);
	hdr->len = size | BPF_RINGBUF_BUSY_BIT;
	hdr->pg_off = pg_off;

	/* pairs with consumer's smp_load_acquire() */
	smp_store_release(&rb->producer_pos, new_prod_pos);

	spin_unlock_irqrestore(&rb->spinlock, flags);

	return (void *)hdr + BPF_RINGBUF_HDR_SZ;
}


bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length)
{
  u_char *end;
  struct capability cap;
  u_char action;
  struct bgp *bgp;
  afi_t afi;
  safi_t safi;

  bgp = peer->bgp;
  end = pnt + length;

  while (pnt < end)
    {
      /* We need at least action, capability code and capability length. */
      if (pnt + 3 > end)
        {
          zlog_info ("%s Capability length error", peer->host);
          bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
          return -1;
        }

      action = *pnt;

      /* Fetch structure to the byte stream. */
      memcpy (&cap, pnt + 1, sizeof (struct capability));

      /* Action value check.  */
      if (action != CAPABILITY_ACTION_SET
	  && action != CAPABILITY_ACTION_UNSET)
        {
          zlog_info ("%s Capability Action Value error %d",
		     peer->host, action);
          bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
          return -1;
        }

      if (BGP_DEBUG (normal, NORMAL))
	zlog_debug ("%s CAPABILITY has action: %d, code: %u, length %u",
		   peer->host, action, cap.code, cap.length);

      /* Capability length check. */
      if (pnt + (cap.length + 3) > end)
        {
          zlog_info ("%s Capability length error", peer->host);
          bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
          return -1;
        }

      /* We know MP Capability Code. */
      if (cap.code == CAPABILITY_CODE_MP)
        {
	  afi = ntohs (cap.mpc.afi);
	  safi = cap.mpc.safi;

          /* Ignore capability when override-capability is set. */
          if (CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
	    continue;

	  /* Address family check.  */
	  if ((afi == AFI_IP
	       || afi == AFI_IP6)
	      && (safi == SAFI_UNICAST
		  || safi == SAFI_MULTICAST
		  || safi == BGP_SAFI_VPNV4))
	    {
	      if (BGP_DEBUG (normal, NORMAL))
		zlog_debug ("%s CAPABILITY has %s MP_EXT CAP for afi/safi: %u/%u",
			   peer->host,
			   action == CAPABILITY_ACTION_SET
			   ? "Advertising" : "Removing",
			   ntohs(cap.mpc.afi) , cap.mpc.safi);

	      /* Adjust safi code. */
	      if (safi == BGP_SAFI_VPNV4)
		safi = SAFI_MPLS_VPN;

	      if (action == CAPABILITY_ACTION_SET)
		{
		  peer->afc_recv[afi][safi] = 1;
		  if (peer->afc[afi][safi])
		    {
		      peer->afc_nego[afi][safi] = 1;
		      bgp_announce_route (peer, afi, safi);
		    }
		}
	      else
		{
		  peer->afc_recv[afi][safi] = 0;
		  peer->afc_nego[afi][safi] = 0;

		  if (peer_active_nego (peer))
		    bgp_clear_route (peer, afi, safi);
		  else
		    BGP_EVENT_ADD (peer, BGP_Stop);
		}
	    }
        }
      else
        {
          zlog_warn ("%s unrecognized capability code: %d - ignored",
                     peer->host, cap.code);
        }
      pnt += cap.length + 3;
    }
  return 0;
}

[bgpd] cleanup, compact and consolidate capability parsing code

2007-07-26 Paul Jakma <paul.jakma@sun.com>

	* (general) Clean up and compact capability parsing slightly.
	  Consolidate validation of length and logging of generic TLV, and
	  memcpy of capability data, thus removing such from cap specifc
	  code (not always present or correct).
	* bgp_open.h: Add structures for the generic capability TLV header
	  and for the data formats of the various specific capabilities we
	  support.  Hence remove the badly named, or else misdefined, struct
	  capability.
	* bgp_open.c: (bgp_capability_vty_out) Use struct capability_mp_data.
	  Do the length checks *before* memcpy()'ing based on that length
	  (stored capability - should have been validated anyway on input,
	  but..).
	  (bgp_afi_safi_valid_indices) new function to validate (afi,safi)
	  which is about to be used as index into arrays, consolidates
	  several instances of same, at least one of which appeared to be
	  incomplete..
	  (bgp_capability_mp) Much condensed.
	  (bgp_capability_orf_entry) New, process one ORF entry
	  (bgp_capability_orf) Condensed. Fixed to process all ORF entries.
	  (bgp_capability_restart) Condensed, and fixed to use a
	  cap-specific type, rather than abusing capability_mp.
	  (struct message capcode_str) added to aid generic logging.
	  (size_t cap_minsizes[]) added to aid generic validation of
	  capability length field.
	  (bgp_capability_parse) Generic logging and validation of TLV
	  consolidated here. Code compacted as much as possible.
	* bgp_packet.c: (bgp_open_receive) Capability parsers now use
	  streams, so no more need here to manually fudge the input stream
	  getp.
	  (bgp_capability_msg_parse) use struct capability_mp_data. Validate
	  lengths /before/ memcpy. Use bgp_afi_safi_valid_indices.
	  (bgp_capability_receive) Exported for use by test harness.
	* bgp_vty.c: (bgp_show_summary) fix conversion warning
	  (bgp_show_peer) ditto
	* bgp_debug.h: Fix storage 'extern' after type 'const'.
        * lib/log.c: (mes_lookup) warning about code not being in
          same-number array slot should be debug, not warning. E.g. BGP
          has several discontigious number spaces, allocating from
          different parts of a space is not uncommon (e.g. IANA
          assigned versus vendor-assigned code points in some number
          space).


bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length)
{
  u_char *end;
  struct capability_mp_data mpc;
  struct capability_header *hdr;
  u_char action;
  struct bgp *bgp;
  afi_t afi;
  safi_t safi;

  bgp = peer->bgp;
  end = pnt + length;

  while (pnt < end)
    {
      /* We need at least action, capability code and capability length. */
      if (pnt + 3 > end)
        {
          zlog_info ("%s Capability length error", peer->host);
          bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
          return -1;
        }
      action = *pnt;
      hdr = (struct capability_header *)(pnt + 1);

      /* Action value check.  */
      if (action != CAPABILITY_ACTION_SET
	  && action != CAPABILITY_ACTION_UNSET)
        {
          zlog_info ("%s Capability Action Value error %d",
		     peer->host, action);
          bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
          return -1;
        }

      if (BGP_DEBUG (normal, NORMAL))
	zlog_debug ("%s CAPABILITY has action: %d, code: %u, length %u",
		   peer->host, action, hdr->code, hdr->length);

      /* Capability length check. */
      if ((pnt + hdr->length + 3) > end)
        {
          zlog_info ("%s Capability length error", peer->host);
          bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0);
          return -1;
        }

      /* Fetch structure to the byte stream. */
      memcpy (&mpc, pnt + 3, sizeof (struct capability_mp_data));

      /* We know MP Capability Code. */
      if (hdr->code == CAPABILITY_CODE_MP)
        {
	  afi = ntohs (mpc.afi);
	  safi = mpc.safi;

          /* Ignore capability when override-capability is set. */
          if (CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY))
	    continue;

          if (!bgp_afi_safi_valid_indices (afi, &safi))
            {
              if (BGP_DEBUG (normal, NORMAL))
                zlog_debug ("%s Dynamic Capability MP_EXT afi/safi invalid",
                            peer->host, afi, safi);
              continue;
            }

	  /* Address family check.  */
          if (BGP_DEBUG (normal, NORMAL))
            zlog_debug ("%s CAPABILITY has %s MP_EXT CAP for afi/safi: %u/%u",
                       peer->host,
                       action == CAPABILITY_ACTION_SET
                       ? "Advertising" : "Removing",
                       ntohs(mpc.afi) , mpc.safi);

          if (action == CAPABILITY_ACTION_SET)
            {
              peer->afc_recv[afi][safi] = 1;
              if (peer->afc[afi][safi])
                {
                  peer->afc_nego[afi][safi] = 1;
                  bgp_announce_route (peer, afi, safi);
                }
            }
          else
            {
              peer->afc_recv[afi][safi] = 0;
              peer->afc_nego[afi][safi] = 0;

              if (peer_active_nego (peer))
                bgp_clear_route (peer, afi, safi);
              else
                BGP_EVENT_ADD (peer, BGP_Stop);
            }
        }
      else
        {
          zlog_warn ("%s unrecognized capability code: %d - ignored",
                     peer->host, hdr->code);
        }
      pnt += hdr->length + 3;
    }
  return 0;
}


WandPrivate void CLINoImageOperator(MagickCLI *cli_wand,
  const char *option,const char *arg1n,const char *arg2n)
{
  const char    /* percent escaped versions of the args */
    *arg1,
    *arg2;

#define _image_info     (cli_wand->wand.image_info)
#define _images         (cli_wand->wand.images)
#define _exception      (cli_wand->wand.exception)
#define _process_flags  (cli_wand->process_flags)
#define _option_type    ((CommandOptionFlags) cli_wand->command->flags)
#define IfNormalOp      (*option=='-')
#define IfPlusOp        (*option!='-')

  assert(cli_wand != (MagickCLI *) NULL);
  assert(cli_wand->signature == MagickWandSignature);
  assert(cli_wand->wand.signature == MagickWandSignature);

  if (cli_wand->wand.debug != MagickFalse)
    (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
      "- NoImage Operator: %s \"%s\" \"%s\"", option,
      arg1n != (char *) NULL ? arg1n : "",
      arg2n != (char *) NULL ? arg2n : "");

  arg1 = arg1n;
  arg2 = arg2n;

  /* Interpret Percent Escapes in Arguments - using first image */
  if ( (((_process_flags & ProcessInterpretProperities) != 0 )
        || ((_option_type & AlwaysInterpretArgsFlag) != 0)
       )  && ((_option_type & NeverInterpretArgsFlag) == 0) ) {
    /* Interpret Percent escapes in argument 1 */
    if (arg1n != (char *) NULL) {
      arg1=InterpretImageProperties(_image_info,_images,arg1n,_exception);
      if (arg1 == (char *) NULL) {
        CLIWandException(OptionWarning,"InterpretPropertyFailure",option);
        arg1=arg1n;  /* use the given argument as is */
      }
    }
    if (arg2n != (char *) NULL) {
      arg2=InterpretImageProperties(_image_info,_images,arg2n,_exception);
      if (arg2 == (char *) NULL) {
        CLIWandException(OptionWarning,"InterpretPropertyFailure",option);
        arg2=arg2n;  /* use the given argument as is */
      }
    }
  }
#undef _process_flags
#undef _option_type

  do {  /* break to exit code */
    /*
      No-op options  (ignore these)
    */
    if (LocaleCompare("noop",option+1) == 0)   /* zero argument */
      break;
    if (LocaleCompare("sans",option+1) == 0)   /* one argument */
      break;
    if (LocaleCompare("sans0",option+1) == 0)  /* zero argument */
      break;
    if (LocaleCompare("sans1",option+1) == 0)  /* one argument */
      break;
    if (LocaleCompare("sans2",option+1) == 0)  /* two arguments */
      break;
    /*
      Image Reading
    */
    if ( ( LocaleCompare("read",option+1) == 0 ) ||
      ( LocaleCompare("--",option) == 0 ) ) {
      /* Do Glob filename Expansion for 'arg1' then read all images.
      *
      * Expansion handles '@', '~', '*', and '?' meta-characters while ignoring
      * (but attaching to the filenames in the generated argument list) any
      * [...] read modifiers that may be present.
      *
      * For example: It will expand '*.gif[20x20]' into a list such as
      * 'abc.gif[20x20]',  'foobar.gif[20x20]',  'xyzzy.gif[20x20]'
      *
      * NOTE: In IMv6 this was done globally across all images. This
      * meant you could include IM options in '@filename' lists, but you
      * could not include comments.   Doing it only for image read makes
      * it far more secure.
      *
      * Note: arguments do not have percent escapes expanded for security
      * reasons.
      */
      int      argc;
      char     **argv;
      ssize_t  i;

      argc = 1;
      argv = (char **) &arg1;

      /* Expand 'glob' expressions in the given filename.
        Expansion handles any 'coder:' prefix, or read modifiers attached
        to the filename, including them in the resulting expanded list.
      */
      if (ExpandFilenames(&argc,&argv) == MagickFalse)
        CLIWandExceptArgBreak(ResourceLimitError,"MemoryAllocationFailed",
            option,GetExceptionMessage(errno));

      /* loop over expanded filename list, and read then all in */
      for (i=0; i < (ssize_t) argc; i++) {
        Image *
          new_images;
        if (_image_info->ping != MagickFalse)
          new_images=PingImages(_image_info,argv[i],_exception);
        else
          new_images=ReadImages(_image_info,argv[i],_exception);
        AppendImageToList(&_images, new_images);
        argv[i]=DestroyString(argv[i]);
      }
      argv=(char **) RelinquishMagickMemory(argv);
      break;
    }
    /*
      Image Writing
      Note: Writing a empty image list is valid in specific cases
    */
    if (LocaleCompare("write",option+1) == 0) {
      /* Note: arguments do not have percent escapes expanded */
      char
        key[MagickPathExtent];

      Image
        *write_images;

      ImageInfo
        *write_info;

      /* Need images, unless a "null:" output coder is used */
      if ( _images == (Image *) NULL ) {
        if ( LocaleCompare(arg1,"null:") == 0 )
          break;
        CLIWandExceptArgBreak(OptionError,"NoImagesForWrite",option,arg1);
      }

      (void) FormatLocaleString(key,MagickPathExtent,"cache:%s",arg1);
      (void) DeleteImageRegistry(key);
      write_images=CloneImageList(_images,_exception);
      write_info=CloneImageInfo(_image_info);
      (void) WriteImages(write_info,write_images,arg1,_exception);
      write_info=DestroyImageInfo(write_info);
      write_images=DestroyImageList(write_images);
      break;
    }
    /*
      Parenthesis and Brace operations
    */
    if (LocaleCompare("(",option) == 0) {
      /* stack 'push' images */
      Stack
        *node;

      size_t
        size;

      size=0;
      node=cli_wand->image_list_stack;
      for ( ; node != (Stack *) NULL; node=node->next)
        size++;
      if ( size >= MAX_STACK_DEPTH )
        CLIWandExceptionBreak(OptionError,"ParenthesisNestedTooDeeply",option);
      node=(Stack *) AcquireMagickMemory(sizeof(*node));
      if (node == (Stack *) NULL)
        CLIWandExceptionBreak(ResourceLimitFatalError,
            "MemoryAllocationFailed",option);
      node->data = (void *)cli_wand->wand.images;
      node->next = cli_wand->image_list_stack;
      cli_wand->image_list_stack = node;
      cli_wand->wand.images = NewImageList();

      /* handle respect-parenthesis */
      if (IsStringTrue(GetImageOption(cli_wand->wand.image_info,
                    "respect-parenthesis")) != MagickFalse)
        option="{"; /* fall-thru so as to push image settings too */
      else
        break;
      /* fall thru to operation */
    }
    if (LocaleCompare("{",option) == 0) {
      /* stack 'push' of image_info settings */
      Stack
        *node;

      size_t
        size;

      size=0;
      node=cli_wand->image_info_stack;
      for ( ; node != (Stack *) NULL; node=node->next)
        size++;
      if ( size >= MAX_STACK_DEPTH )
        CLIWandExceptionBreak(OptionError,"CurlyBracesNestedTooDeeply",option);
      node=(Stack *) AcquireMagickMemory(sizeof(*node));
      if (node == (Stack *) NULL)
        CLIWandExceptionBreak(ResourceLimitFatalError,
            "MemoryAllocationFailed",option);

      node->data = (void *)cli_wand->wand.image_info;
      node->next = cli_wand->image_info_stack;

      cli_wand->image_info_stack = node;
      cli_wand->wand.image_info = CloneImageInfo(cli_wand->wand.image_info);
      if (cli_wand->wand.image_info == (ImageInfo *) NULL) {
        CLIWandException(ResourceLimitFatalError,"MemoryAllocationFailed",
            option);
        cli_wand->wand.image_info = (ImageInfo *)node->data;
        node = (Stack *)RelinquishMagickMemory(node);
        break;
      }

      break;
    }
    if (LocaleCompare(")",option) == 0) {
      /* pop images from stack */
      Stack
        *node;

      node = (Stack *)cli_wand->image_list_stack;
      if ( node == (Stack *) NULL)
        CLIWandExceptionBreak(OptionError,"UnbalancedParenthesis",option);
      cli_wand->image_list_stack = node->next;

      AppendImageToList((Image **)&node->data,cli_wand->wand.images);
      cli_wand->wand.images= (Image *)node->data;
      node = (Stack *)RelinquishMagickMemory(node);

      /* handle respect-parenthesis - of the previous 'pushed' settings */
      node = cli_wand->image_info_stack;
      if ( node != (Stack *) NULL)
        {
          if (IsStringTrue(GetImageOption(
                cli_wand->wand.image_info,"respect-parenthesis")) != MagickFalse)
            option="}"; /* fall-thru so as to pop image settings too */
          else
            break;
        }
      else
        break;
      /* fall thru to next if */
    }
    if (LocaleCompare("}",option) == 0) {
      /* pop image_info settings from stack */
      Stack
        *node;

      node = (Stack *)cli_wand->image_info_stack;
      if ( node == (Stack *) NULL)
        CLIWandExceptionBreak(OptionError,"UnbalancedCurlyBraces",option);
      cli_wand->image_info_stack = node->next;

      (void) DestroyImageInfo(cli_wand->wand.image_info);
      cli_wand->wand.image_info = (ImageInfo *)node->data;
      node = (Stack *)RelinquishMagickMemory(node);

      GetDrawInfo(cli_wand->wand.image_info, cli_wand->draw_info);
      cli_wand->quantize_info=DestroyQuantizeInfo(cli_wand->quantize_info);
      cli_wand->quantize_info=AcquireQuantizeInfo(cli_wand->wand.image_info);

      break;
    }
      if (LocaleCompare("print",option+1) == 0)
        {
          (void) FormatLocaleFile(stdout,"%s",arg1);
          break;
        }
    if (LocaleCompare("set",option+1) == 0)
      {
        /* Settings are applied to each image in memory in turn (if any).
           While a option: only need to be applied once globally.

           NOTE: rguments have not been automatically percent expaneded
        */

        /* escape the 'key' once only, using first image. */
        arg1=InterpretImageProperties(_image_info,_images,arg1n,_exception);
        if (arg1 == (char *) NULL)
          CLIWandExceptionBreak(OptionWarning,"InterpretPropertyFailure",
                option);

        if (LocaleNCompare(arg1,"registry:",9) == 0)
          {
            if (IfPlusOp)
              {
                (void) DeleteImageRegistry(arg1+9);
                arg1=DestroyString((char *)arg1);
                break;
              }
            arg2=InterpretImageProperties(_image_info,_images,arg2n,_exception);
            if (arg2 == (char *) NULL) {
              arg1=DestroyString((char *)arg1);
              CLIWandExceptionBreak(OptionWarning,"InterpretPropertyFailure",
                    option);
            }
            (void) SetImageRegistry(StringRegistryType,arg1+9,arg2,_exception);
            arg1=DestroyString((char *)arg1);
            arg2=DestroyString((char *)arg2);
            break;
          }
        if (LocaleNCompare(arg1,"option:",7) == 0)
          {
            /* delete equivelent artifact from all images (if any) */
            if (_images != (Image *) NULL)
              {
                MagickResetIterator(&cli_wand->wand);
                while (MagickNextImage(&cli_wand->wand) != MagickFalse)
                  (void) DeleteImageArtifact(_images,arg1+7);
                MagickResetIterator(&cli_wand->wand);
              }
            /* now set/delete the global option as needed */
            /* FUTURE: make escapes in a global 'option:' delayed */
            arg2=(char *) NULL;
            if (IfNormalOp)
              {
                arg2=InterpretImageProperties(_image_info,_images,arg2n,_exception);
                if (arg2 == (char *) NULL)
                  CLIWandExceptionBreak(OptionWarning,
                       "InterpretPropertyFailure",option);
              }
            (void) SetImageOption(_image_info,arg1+7,arg2);
            arg1=DestroyString((char *)arg1);
            arg2=DestroyString((char *)arg2);
            break;
          }
        /* Set Artifacts/Properties/Attributes all images (required) */
        if ( _images == (Image *) NULL )
          CLIWandExceptArgBreak(OptionWarning,"NoImageForProperty",option,arg1);

        MagickResetIterator(&cli_wand->wand);
        while (MagickNextImage(&cli_wand->wand) != MagickFalse)
          {
            arg2=(char *) NULL;
            if (IfNormalOp)
              {
                arg2=InterpretImageProperties(_image_info,_images,arg2n,_exception);
                if (arg2 == (char *) NULL)
                  CLIWandExceptionBreak(OptionWarning,
                       "InterpretPropertyFailure",option);
              }
            if (LocaleNCompare(arg1,"artifact:",9) == 0)
              (void) SetImageArtifact(_images,arg1+9,arg2);
            else if (LocaleNCompare(arg1,"property:",9) == 0)
              (void) SetImageProperty(_images,arg1+9,arg2,_exception);
            else
              (void) SetImageProperty(_images,arg1,arg2,_exception);
            arg2=DestroyString((char *)arg2);
          }
        MagickResetIterator(&cli_wand->wand);
        arg1=DestroyString((char *)arg1);
        break;
     }
    if (LocaleCompare("clone",option+1) == 0) {
        Image
          *new_images;

        if (*option == '+')
          arg1=AcquireString("-1");
        if (IsSceneGeometry(arg1,MagickFalse) == MagickFalse)
          CLIWandExceptionBreak(OptionError,"InvalidArgument",option);
        if ( cli_wand->image_list_stack == (Stack *) NULL)
          CLIWandExceptionBreak(OptionError,"UnableToCloneImage",option);
        new_images = (Image *)cli_wand->image_list_stack->data;
        if (new_images == (Image *) NULL)
          CLIWandExceptionBreak(OptionError,"UnableToCloneImage",option);
        new_images=CloneImages(new_images,arg1,_exception);
        if (new_images == (Image *) NULL)
          CLIWandExceptionBreak(OptionError,"NoSuchImage",option);
        AppendImageToList(&_images,new_images);
        break;
      }
    /*
       Informational Operations.

       Note that these do not require either a cli-wand or images!
       Though currently a cli-wand much be provided regardless.
    */
    if (LocaleCompare("version",option+1) == 0)
      {
        ListMagickVersion(stdout);
        break;
      }
    if (LocaleCompare("list",option+1) == 0) {
      /*
         FUTURE: This 'switch' should really be part of MagickCore
      */
      ssize_t
        list;

      list=ParseCommandOption(MagickListOptions,MagickFalse,arg1);
      if ( list < 0 ) {
        CLIWandExceptionArg(OptionError,"UnrecognizedListType",option,arg1);
        break;
      }
      switch (list)
      {
        case MagickCoderOptions:
        {
          (void) ListCoderInfo((FILE *) NULL,_exception);
          break;
        }
        case MagickColorOptions:
        {
          (void) ListColorInfo((FILE *) NULL,_exception);
          break;
        }
        case MagickConfigureOptions:
        {
          (void) ListConfigureInfo((FILE *) NULL,_exception);
          break;
        }
        case MagickDelegateOptions:
        {
          (void) ListDelegateInfo((FILE *) NULL,_exception);
          break;
        }
        case MagickFontOptions:
        {
          (void) ListTypeInfo((FILE *) NULL,_exception);
          break;
        }
        case MagickFormatOptions:
          (void) ListMagickInfo((FILE *) NULL,_exception);
          break;
        case MagickLocaleOptions:
          (void) ListLocaleInfo((FILE *) NULL,_exception);
          break;
        case MagickLogOptions:
          (void) ListLogInfo((FILE *) NULL,_exception);
          break;
        case MagickMagicOptions:
          (void) ListMagicInfo((FILE *) NULL,_exception);
          break;
        case MagickMimeOptions:
          (void) ListMimeInfo((FILE *) NULL,_exception);
          break;
        case MagickModuleOptions:
          (void) ListModuleInfo((FILE *) NULL,_exception);
          break;
        case MagickPolicyOptions:
          (void) ListPolicyInfo((FILE *) NULL,_exception);
          break;
        case MagickResourceOptions:
          (void) ListMagickResourceInfo((FILE *) NULL,_exception);
          break;
        case MagickThresholdOptions:
          (void) ListThresholdMaps((FILE *) NULL,_exception);
          break;
        default:
          (void) ListCommandOptions((FILE *) NULL,(CommandOption) list,
            _exception);
          break;
      }
      break;
    }

    CLIWandException(OptionError,"UnrecognizedOption",option);

DisableMSCWarning(4127)
  } while (0);  /* break to exit code. */
RestoreMSCWarning

  /* clean up percent escape interpreted strings */
  if (arg1 != arg1n )
    arg1=DestroyString((char *)arg1);
  if (arg2 != arg2n )
    arg2=DestroyString((char *)arg2);

#undef _image_info
#undef _images
#undef _exception
#undef IfNormalOp
#undef IfPlusOp
}

do not attempt to write a null image list (thanks to Vinay Rohila)


WandPrivate void CLINoImageOperator(MagickCLI *cli_wand,
  const char *option,const char *arg1n,const char *arg2n)
{
  const char    /* percent escaped versions of the args */
    *arg1,
    *arg2;

#define _image_info     (cli_wand->wand.image_info)
#define _images         (cli_wand->wand.images)
#define _exception      (cli_wand->wand.exception)
#define _process_flags  (cli_wand->process_flags)
#define _option_type    ((CommandOptionFlags) cli_wand->command->flags)
#define IfNormalOp      (*option=='-')
#define IfPlusOp        (*option!='-')

  assert(cli_wand != (MagickCLI *) NULL);
  assert(cli_wand->signature == MagickWandSignature);
  assert(cli_wand->wand.signature == MagickWandSignature);

  if (cli_wand->wand.debug != MagickFalse)
    (void) CLILogEvent(cli_wand,CommandEvent,GetMagickModule(),
      "- NoImage Operator: %s \"%s\" \"%s\"", option,
      arg1n != (char *) NULL ? arg1n : "",
      arg2n != (char *) NULL ? arg2n : "");

  arg1 = arg1n;
  arg2 = arg2n;

  /* Interpret Percent Escapes in Arguments - using first image */
  if ( (((_process_flags & ProcessInterpretProperities) != 0 )
        || ((_option_type & AlwaysInterpretArgsFlag) != 0)
       )  && ((_option_type & NeverInterpretArgsFlag) == 0) ) {
    /* Interpret Percent escapes in argument 1 */
    if (arg1n != (char *) NULL) {
      arg1=InterpretImageProperties(_image_info,_images,arg1n,_exception);
      if (arg1 == (char *) NULL) {
        CLIWandException(OptionWarning,"InterpretPropertyFailure",option);
        arg1=arg1n;  /* use the given argument as is */
      }
    }
    if (arg2n != (char *) NULL) {
      arg2=InterpretImageProperties(_image_info,_images,arg2n,_exception);
      if (arg2 == (char *) NULL) {
        CLIWandException(OptionWarning,"InterpretPropertyFailure",option);
        arg2=arg2n;  /* use the given argument as is */
      }
    }
  }
#undef _process_flags
#undef _option_type

  do {  /* break to exit code */
    /*
      No-op options  (ignore these)
    */
    if (LocaleCompare("noop",option+1) == 0)   /* zero argument */
      break;
    if (LocaleCompare("sans",option+1) == 0)   /* one argument */
      break;
    if (LocaleCompare("sans0",option+1) == 0)  /* zero argument */
      break;
    if (LocaleCompare("sans1",option+1) == 0)  /* one argument */
      break;
    if (LocaleCompare("sans2",option+1) == 0)  /* two arguments */
      break;
    /*
      Image Reading
    */
    if ( ( LocaleCompare("read",option+1) == 0 ) ||
      ( LocaleCompare("--",option) == 0 ) ) {
      /* Do Glob filename Expansion for 'arg1' then read all images.
      *
      * Expansion handles '@', '~', '*', and '?' meta-characters while ignoring
      * (but attaching to the filenames in the generated argument list) any
      * [...] read modifiers that may be present.
      *
      * For example: It will expand '*.gif[20x20]' into a list such as
      * 'abc.gif[20x20]',  'foobar.gif[20x20]',  'xyzzy.gif[20x20]'
      *
      * NOTE: In IMv6 this was done globally across all images. This
      * meant you could include IM options in '@filename' lists, but you
      * could not include comments.   Doing it only for image read makes
      * it far more secure.
      *
      * Note: arguments do not have percent escapes expanded for security
      * reasons.
      */
      int      argc;
      char     **argv;
      ssize_t  i;

      argc = 1;
      argv = (char **) &arg1;

      /* Expand 'glob' expressions in the given filename.
        Expansion handles any 'coder:' prefix, or read modifiers attached
        to the filename, including them in the resulting expanded list.
      */
      if (ExpandFilenames(&argc,&argv) == MagickFalse)
        CLIWandExceptArgBreak(ResourceLimitError,"MemoryAllocationFailed",
            option,GetExceptionMessage(errno));

      /* loop over expanded filename list, and read then all in */
      for (i=0; i < (ssize_t) argc; i++) {
        Image *
          new_images;
        if (_image_info->ping != MagickFalse)
          new_images=PingImages(_image_info,argv[i],_exception);
        else
          new_images=ReadImages(_image_info,argv[i],_exception);
        AppendImageToList(&_images, new_images);
        argv[i]=DestroyString(argv[i]);
      }
      argv=(char **) RelinquishMagickMemory(argv);
      break;
    }
    /*
      Image Writing
      Note: Writing a empty image list is valid in specific cases
    */
    if (LocaleCompare("write",option+1) == 0) {
      /* Note: arguments do not have percent escapes expanded */
      char
        key[MagickPathExtent];

      Image
        *write_images;

      ImageInfo
        *write_info;

      /* Need images, unless a "null:" output coder is used */
      if ( _images == (Image *) NULL ) {
        if ( LocaleCompare(arg1,"null:") == 0 )
          break;
        CLIWandExceptArgBreak(OptionError,"NoImagesForWrite",option,arg1);
      }

      (void) FormatLocaleString(key,MagickPathExtent,"cache:%s",arg1);
      (void) DeleteImageRegistry(key);
      write_images=CloneImageList(_images,_exception);
      write_info=CloneImageInfo(_image_info);
      if (write_images != (Image *) NULL)
        (void) WriteImages(write_info,write_images,arg1,_exception);
      write_info=DestroyImageInfo(write_info);
      write_images=DestroyImageList(write_images);
      break;
    }
    /*
      Parenthesis and Brace operations
    */
    if (LocaleCompare("(",option) == 0) {
      /* stack 'push' images */
      Stack
        *node;

      size_t
        size;

      size=0;
      node=cli_wand->image_list_stack;
      for ( ; node != (Stack *) NULL; node=node->next)
        size++;
      if ( size >= MAX_STACK_DEPTH )
        CLIWandExceptionBreak(OptionError,"ParenthesisNestedTooDeeply",option);
      node=(Stack *) AcquireMagickMemory(sizeof(*node));
      if (node == (Stack *) NULL)
        CLIWandExceptionBreak(ResourceLimitFatalError,
            "MemoryAllocationFailed",option);
      node->data = (void *)cli_wand->wand.images;
      node->next = cli_wand->image_list_stack;
      cli_wand->image_list_stack = node;
      cli_wand->wand.images = NewImageList();

      /* handle respect-parenthesis */
      if (IsStringTrue(GetImageOption(cli_wand->wand.image_info,
                    "respect-parenthesis")) != MagickFalse)
        option="{"; /* fall-thru so as to push image settings too */
      else
        break;
      /* fall thru to operation */
    }
    if (LocaleCompare("{",option) == 0) {
      /* stack 'push' of image_info settings */
      Stack
        *node;

      size_t
        size;

      size=0;
      node=cli_wand->image_info_stack;
      for ( ; node != (Stack *) NULL; node=node->next)
        size++;
      if ( size >= MAX_STACK_DEPTH )
        CLIWandExceptionBreak(OptionError,"CurlyBracesNestedTooDeeply",option);
      node=(Stack *) AcquireMagickMemory(sizeof(*node));
      if (node == (Stack *) NULL)
        CLIWandExceptionBreak(ResourceLimitFatalError,
            "MemoryAllocationFailed",option);

      node->data = (void *)cli_wand->wand.image_info;
      node->next = cli_wand->image_info_stack;

      cli_wand->image_info_stack = node;
      cli_wand->wand.image_info = CloneImageInfo(cli_wand->wand.image_info);
      if (cli_wand->wand.image_info == (ImageInfo *) NULL) {
        CLIWandException(ResourceLimitFatalError,"MemoryAllocationFailed",
            option);
        cli_wand->wand.image_info = (ImageInfo *)node->data;
        node = (Stack *)RelinquishMagickMemory(node);
        break;
      }

      break;
    }
    if (LocaleCompare(")",option) == 0) {
      /* pop images from stack */
      Stack
        *node;

      node = (Stack *)cli_wand->image_list_stack;
      if ( node == (Stack *) NULL)
        CLIWandExceptionBreak(OptionError,"UnbalancedParenthesis",option);
      cli_wand->image_list_stack = node->next;

      AppendImageToList((Image **)&node->data,cli_wand->wand.images);
      cli_wand->wand.images= (Image *)node->data;
      node = (Stack *)RelinquishMagickMemory(node);

      /* handle respect-parenthesis - of the previous 'pushed' settings */
      node = cli_wand->image_info_stack;
      if ( node != (Stack *) NULL)
        {
          if (IsStringTrue(GetImageOption(
                cli_wand->wand.image_info,"respect-parenthesis")) != MagickFalse)
            option="}"; /* fall-thru so as to pop image settings too */
          else
            break;
        }
      else
        break;
      /* fall thru to next if */
    }
    if (LocaleCompare("}",option) == 0) {
      /* pop image_info settings from stack */
      Stack
        *node;

      node = (Stack *)cli_wand->image_info_stack;
      if ( node == (Stack *) NULL)
        CLIWandExceptionBreak(OptionError,"UnbalancedCurlyBraces",option);
      cli_wand->image_info_stack = node->next;

      (void) DestroyImageInfo(cli_wand->wand.image_info);
      cli_wand->wand.image_info = (ImageInfo *)node->data;
      node = (Stack *)RelinquishMagickMemory(node);

      GetDrawInfo(cli_wand->wand.image_info, cli_wand->draw_info);
      cli_wand->quantize_info=DestroyQuantizeInfo(cli_wand->quantize_info);
      cli_wand->quantize_info=AcquireQuantizeInfo(cli_wand->wand.image_info);

      break;
    }
      if (LocaleCompare("print",option+1) == 0)
        {
          (void) FormatLocaleFile(stdout,"%s",arg1);
          break;
        }
    if (LocaleCompare("set",option+1) == 0)
      {
        /* Settings are applied to each image in memory in turn (if any).
           While a option: only need to be applied once globally.

           NOTE: rguments have not been automatically percent expaneded
        */

        /* escape the 'key' once only, using first image. */
        arg1=InterpretImageProperties(_image_info,_images,arg1n,_exception);
        if (arg1 == (char *) NULL)
          CLIWandExceptionBreak(OptionWarning,"InterpretPropertyFailure",
                option);

        if (LocaleNCompare(arg1,"registry:",9) == 0)
          {
            if (IfPlusOp)
              {
                (void) DeleteImageRegistry(arg1+9);
                arg1=DestroyString((char *)arg1);
                break;
              }
            arg2=InterpretImageProperties(_image_info,_images,arg2n,_exception);
            if (arg2 == (char *) NULL) {
              arg1=DestroyString((char *)arg1);
              CLIWandExceptionBreak(OptionWarning,"InterpretPropertyFailure",
                    option);
            }
            (void) SetImageRegistry(StringRegistryType,arg1+9,arg2,_exception);
            arg1=DestroyString((char *)arg1);
            arg2=DestroyString((char *)arg2);
            break;
          }
        if (LocaleNCompare(arg1,"option:",7) == 0)
          {
            /* delete equivelent artifact from all images (if any) */
            if (_images != (Image *) NULL)
              {
                MagickResetIterator(&cli_wand->wand);
                while (MagickNextImage(&cli_wand->wand) != MagickFalse)
                  (void) DeleteImageArtifact(_images,arg1+7);
                MagickResetIterator(&cli_wand->wand);
              }
            /* now set/delete the global option as needed */
            /* FUTURE: make escapes in a global 'option:' delayed */
            arg2=(char *) NULL;
            if (IfNormalOp)
              {
                arg2=InterpretImageProperties(_image_info,_images,arg2n,_exception);
                if (arg2 == (char *) NULL)
                  CLIWandExceptionBreak(OptionWarning,
                       "InterpretPropertyFailure",option);
              }
            (void) SetImageOption(_image_info,arg1+7,arg2);
            arg1=DestroyString((char *)arg1);
            arg2=DestroyString((char *)arg2);
            break;
          }
        /* Set Artifacts/Properties/Attributes all images (required) */
        if ( _images == (Image *) NULL )
          CLIWandExceptArgBreak(OptionWarning,"NoImageForProperty",option,arg1);

        MagickResetIterator(&cli_wand->wand);
        while (MagickNextImage(&cli_wand->wand) != MagickFalse)
          {
            arg2=(char *) NULL;
            if (IfNormalOp)
              {
                arg2=InterpretImageProperties(_image_info,_images,arg2n,_exception);
                if (arg2 == (char *) NULL)
                  CLIWandExceptionBreak(OptionWarning,
                       "InterpretPropertyFailure",option);
              }
            if (LocaleNCompare(arg1,"artifact:",9) == 0)
              (void) SetImageArtifact(_images,arg1+9,arg2);
            else if (LocaleNCompare(arg1,"property:",9) == 0)
              (void) SetImageProperty(_images,arg1+9,arg2,_exception);
            else
              (void) SetImageProperty(_images,arg1,arg2,_exception);
            arg2=DestroyString((char *)arg2);
          }
        MagickResetIterator(&cli_wand->wand);
        arg1=DestroyString((char *)arg1);
        break;
     }
    if (LocaleCompare("clone",option+1) == 0) {
        Image
          *new_images;

        if (*option == '+')
          arg1=AcquireString("-1");
        if (IsSceneGeometry(arg1,MagickFalse) == MagickFalse)
          CLIWandExceptionBreak(OptionError,"InvalidArgument",option);
        if ( cli_wand->image_list_stack == (Stack *) NULL)
          CLIWandExceptionBreak(OptionError,"UnableToCloneImage",option);
        new_images = (Image *)cli_wand->image_list_stack->data;
        if (new_images == (Image *) NULL)
          CLIWandExceptionBreak(OptionError,"UnableToCloneImage",option);
        new_images=CloneImages(new_images,arg1,_exception);
        if (new_images == (Image *) NULL)
          CLIWandExceptionBreak(OptionError,"NoSuchImage",option);
        AppendImageToList(&_images,new_images);
        break;
      }
    /*
       Informational Operations.

       Note that these do not require either a cli-wand or images!
       Though currently a cli-wand much be provided regardless.
    */
    if (LocaleCompare("version",option+1) == 0)
      {
        ListMagickVersion(stdout);
        break;
      }
    if (LocaleCompare("list",option+1) == 0) {
      /*
         FUTURE: This 'switch' should really be part of MagickCore
      */
      ssize_t
        list;

      list=ParseCommandOption(MagickListOptions,MagickFalse,arg1);
      if ( list < 0 ) {
        CLIWandExceptionArg(OptionError,"UnrecognizedListType",option,arg1);
        break;
      }
      switch (list)
      {
        case MagickCoderOptions:
        {
          (void) ListCoderInfo((FILE *) NULL,_exception);
          break;
        }
        case MagickColorOptions:
        {
          (void) ListColorInfo((FILE *) NULL,_exception);
          break;
        }
        case MagickConfigureOptions:
        {
          (void) ListConfigureInfo((FILE *) NULL,_exception);
          break;
        }
        case MagickDelegateOptions:
        {
          (void) ListDelegateInfo((FILE *) NULL,_exception);
          break;
        }
        case MagickFontOptions:
        {
          (void) ListTypeInfo((FILE *) NULL,_exception);
          break;
        }
        case MagickFormatOptions:
          (void) ListMagickInfo((FILE *) NULL,_exception);
          break;
        case MagickLocaleOptions:
          (void) ListLocaleInfo((FILE *) NULL,_exception);
          break;
        case MagickLogOptions:
          (void) ListLogInfo((FILE *) NULL,_exception);
          break;
        case MagickMagicOptions:
          (void) ListMagicInfo((FILE *) NULL,_exception);
          break;
        case MagickMimeOptions:
          (void) ListMimeInfo((FILE *) NULL,_exception);
          break;
        case MagickModuleOptions:
          (void) ListModuleInfo((FILE *) NULL,_exception);
          break;
        case MagickPolicyOptions:
          (void) ListPolicyInfo((FILE *) NULL,_exception);
          break;
        case MagickResourceOptions:
          (void) ListMagickResourceInfo((FILE *) NULL,_exception);
          break;
        case MagickThresholdOptions:
          (void) ListThresholdMaps((FILE *) NULL,_exception);
          break;
        default:
          (void) ListCommandOptions((FILE *) NULL,(CommandOption) list,
            _exception);
          break;
      }
      break;
    }

    CLIWandException(OptionError,"UnrecognizedOption",option);

DisableMSCWarning(4127)
  } while (0);  /* break to exit code. */
RestoreMSCWarning

  /* clean up percent escape interpreted strings */
  if (arg1 != arg1n )
    arg1=DestroyString((char *)arg1);
  if (arg2 != arg2n )
    arg2=DestroyString((char *)arg2);

#undef _image_info
#undef _images
#undef _exception
#undef IfNormalOp
#undef IfPlusOp
}


static int __io_sync_cancel(struct io_uring_task *tctx,
			    struct io_cancel_data *cd, int fd)
{
	struct io_ring_ctx *ctx = cd->ctx;

	/* fixed must be grabbed every time since we drop the uring_lock */
	if ((cd->flags & IORING_ASYNC_CANCEL_FD) &&
	    (cd->flags & IORING_ASYNC_CANCEL_FD_FIXED)) {
		unsigned long file_ptr;

		if (unlikely(fd > ctx->nr_user_files))
			return -EBADF;
		fd = array_index_nospec(fd, ctx->nr_user_files);
		file_ptr = io_fixed_file_slot(&ctx->file_table, fd)->file_ptr;
		cd->file = (struct file *) (file_ptr & FFS_MASK);
		if (!cd->file)
			return -EBADF;
	}

	return __io_async_cancel(cd, tctx, 0);
}

io_uring: fix off-by-one in sync cancelation file check

The passed in index should be validated against the number of registered
files we have, it needs to be smaller than the index value to avoid going
one beyond the end.

Fixes: 78a861b94959 ("io_uring: add sync cancelation API through io_uring_register()")
Reported-by: Luo Likang <luolikang@nsfocus.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>


static int __io_sync_cancel(struct io_uring_task *tctx,
			    struct io_cancel_data *cd, int fd)
{
	struct io_ring_ctx *ctx = cd->ctx;

	/* fixed must be grabbed every time since we drop the uring_lock */
	if ((cd->flags & IORING_ASYNC_CANCEL_FD) &&
	    (cd->flags & IORING_ASYNC_CANCEL_FD_FIXED)) {
		unsigned long file_ptr;

		if (unlikely(fd >= ctx->nr_user_files))
			return -EBADF;
		fd = array_index_nospec(fd, ctx->nr_user_files);
		file_ptr = io_fixed_file_slot(&ctx->file_table, fd)->file_ptr;
		cd->file = (struct file *) (file_ptr & FFS_MASK);
		if (!cd->file)
			return -EBADF;
	}

	return __io_async_cancel(cd, tctx, 0);
}


stl_remove_degenerate(stl_file *stl, int facet) {
  int edge1;
  int edge2;
  int edge3;
  int neighbor1;
  int neighbor2;
  int neighbor3;
  int vnot1;
  int vnot2;
  int vnot3;

  if (stl->error) return;

  if(   !memcmp(&stl->facet_start[facet].vertex[0],
                &stl->facet_start[facet].vertex[1], sizeof(stl_vertex))
        && !memcmp(&stl->facet_start[facet].vertex[1],
                   &stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) {
    /* all 3 vertices are equal.  Just remove the facet.  I don't think*/
    /* this is really possible, but just in case... */
    printf("removing a facet in stl_remove_degenerate\n");

    stl_remove_facet(stl, facet);
    return;
  }

  if(!memcmp(&stl->facet_start[facet].vertex[0],
             &stl->facet_start[facet].vertex[1], sizeof(stl_vertex))) {
    edge1 = 1;
    edge2 = 2;
    edge3 = 0;
  } else if(!memcmp(&stl->facet_start[facet].vertex[1],
                    &stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) {
    edge1 = 0;
    edge2 = 2;
    edge3 = 1;
  } else if(!memcmp(&stl->facet_start[facet].vertex[2],
                    &stl->facet_start[facet].vertex[0], sizeof(stl_vertex))) {
    edge1 = 0;
    edge2 = 1;
    edge3 = 2;
  } else {
    /* No degenerate. Function shouldn't have been called. */
    return;
  }
  neighbor1 = stl->neighbors_start[facet].neighbor[edge1];
  neighbor2 = stl->neighbors_start[facet].neighbor[edge2];

  if(neighbor1 == -1) {
    stl_update_connects_remove_1(stl, neighbor2);
  }
  if(neighbor2 == -1) {
    stl_update_connects_remove_1(stl, neighbor1);
  }

  neighbor3 = stl->neighbors_start[facet].neighbor[edge3];
  vnot1 = stl->neighbors_start[facet].which_vertex_not[edge1];
  vnot2 = stl->neighbors_start[facet].which_vertex_not[edge2];
  vnot3 = stl->neighbors_start[facet].which_vertex_not[edge3];

  if(neighbor1 != -1){
    stl->neighbors_start[neighbor1].neighbor[(vnot1 + 1) % 3] = neighbor2;
    stl->neighbors_start[neighbor1].which_vertex_not[(vnot1 + 1) % 3] = vnot2;
  }
  if(neighbor2 != -1){
    stl->neighbors_start[neighbor2].neighbor[(vnot2 + 1) % 3] = neighbor1;
    stl->neighbors_start[neighbor2].which_vertex_not[(vnot2 + 1) % 3] = vnot1;
  }

  stl_remove_facet(stl, facet);

  if(neighbor3 != -1) {
    stl_update_connects_remove_1(stl, neighbor3);
    stl->neighbors_start[neighbor3].neighbor[(vnot3 + 1) % 3] = -1;
  }
}

Fix heap buffer overflow in stl_update_connects_remove_1

- Add argument value check to the stl_update_connects_remove_1
- Add neighbor value check in stl_remove_degenerate

Fixes https://github.com/admesh/admesh/issues/28
Merges https://github.com/admesh/admesh/pull/55


stl_remove_degenerate(stl_file *stl, int facet) {
  int edge1;
  int edge2;
  int edge3;
  int neighbor1;
  int neighbor2;
  int neighbor3;
  int vnot1;
  int vnot2;
  int vnot3;

  if (stl->error) return;

  if(   !memcmp(&stl->facet_start[facet].vertex[0],
                &stl->facet_start[facet].vertex[1], sizeof(stl_vertex))
        && !memcmp(&stl->facet_start[facet].vertex[1],
                   &stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) {
    /* all 3 vertices are equal.  Just remove the facet.  I don't think*/
    /* this is really possible, but just in case... */
    printf("removing a facet in stl_remove_degenerate\n");

    stl_remove_facet(stl, facet);
    return;
  }

  if(!memcmp(&stl->facet_start[facet].vertex[0],
             &stl->facet_start[facet].vertex[1], sizeof(stl_vertex))) {
    edge1 = 1;
    edge2 = 2;
    edge3 = 0;
  } else if(!memcmp(&stl->facet_start[facet].vertex[1],
                    &stl->facet_start[facet].vertex[2], sizeof(stl_vertex))) {
    edge1 = 0;
    edge2 = 2;
    edge3 = 1;
  } else if(!memcmp(&stl->facet_start[facet].vertex[2],
                    &stl->facet_start[facet].vertex[0], sizeof(stl_vertex))) {
    edge1 = 0;
    edge2 = 1;
    edge3 = 2;
  } else {
    /* No degenerate. Function shouldn't have been called. */
    return;
  }
  neighbor1 = stl->neighbors_start[facet].neighbor[edge1];
  neighbor2 = stl->neighbors_start[facet].neighbor[edge2];

  if(neighbor1 == -1 && neighbor2 != -1) {
    stl_update_connects_remove_1(stl, neighbor2);
  }
  else if (neighbor2 == -1 && neighbor1 != -1) {
    stl_update_connects_remove_1(stl, neighbor1);
  }

  neighbor3 = stl->neighbors_start[facet].neighbor[edge3];
  vnot1 = stl->neighbors_start[facet].which_vertex_not[edge1];
  vnot2 = stl->neighbors_start[facet].which_vertex_not[edge2];
  vnot3 = stl->neighbors_start[facet].which_vertex_not[edge3];

  if(neighbor1 != -1){
    stl->neighbors_start[neighbor1].neighbor[(vnot1 + 1) % 3] = neighbor2;
    stl->neighbors_start[neighbor1].which_vertex_not[(vnot1 + 1) % 3] = vnot2;
  }
  if(neighbor2 != -1){
    stl->neighbors_start[neighbor2].neighbor[(vnot2 + 1) % 3] = neighbor1;
    stl->neighbors_start[neighbor2].which_vertex_not[(vnot2 + 1) % 3] = vnot1;
  }

  stl_remove_facet(stl, facet);

  if(neighbor3 != -1) {
    stl_update_connects_remove_1(stl, neighbor3);
    stl->neighbors_start[neighbor3].neighbor[(vnot3 + 1) % 3] = -1;
  }
}


stl_update_connects_remove_1(stl_file *stl, int facet_num) {
  int j;

  if (stl->error) return;
  /* Update list of connected edges */
  j = ((stl->neighbors_start[facet_num].neighbor[0] == -1) +
       (stl->neighbors_start[facet_num].neighbor[1] == -1) +
       (stl->neighbors_start[facet_num].neighbor[2] == -1));
  if(j == 0) {		       /* Facet has 3 neighbors */
    stl->stats.connected_facets_3_edge -= 1;
  } else if(j == 1) {	     /* Facet has 2 neighbors */
    stl->stats.connected_facets_2_edge -= 1;
  } else if(j == 2) {	     /* Facet has 1 neighbor  */
    stl->stats.connected_facets_1_edge -= 1;
  }
}


stl_update_connects_remove_1(stl_file *stl, int facet_num) {
  int j;

  if (
    stl->error ||
    facet_num < 0
  ) return;

  /* Update list of connected edges */
  j = ((stl->neighbors_start[facet_num].neighbor[0] == -1) +
       (stl->neighbors_start[facet_num].neighbor[1] == -1) +
       (stl->neighbors_start[facet_num].neighbor[2] == -1));
  if(j == 0) {		       /* Facet has 3 neighbors */
    stl->stats.connected_facets_3_edge -= 1;
  } else if(j == 1) {	     /* Facet has 2 neighbors */
    stl->stats.connected_facets_2_edge -= 1;
  } else if(j == 2) {	     /* Facet has 1 neighbor  */
    stl->stats.connected_facets_1_edge -= 1;
  }
}