Commit message: None
CVE: CVE-2011-3193
CWE: CWE-119
Vulnerability description: Heap-based buffer overflow in the Lookup_MarkMarkPos function in the HarfBuzz module (harfbuzz-gpos.c), as used by Qt before 4.7.4 and Pango, allows remote attackers to cause a denial of service (crash) and possibly execute arbitrary code via a crafted font file.

Project: harfbuzz

static void  Free_Anchor( HB_Anchor*  an)
{
  if ( an->PosFormat == 3 && an->af.af3.DeviceTables )
  {
    _HB_OPEN_Free_Device( an->af.af3.DeviceTables[AF3_X_DEVICE_TABLE] );
    _HB_OPEN_Free_Device( an->af.af3.DeviceTables[AF3_Y_DEVICE_TABLE] );
    FREE( an->af.af3.DeviceTables );
  }
}

Project: harfbuzz

static void  Free_ChainContextPos2( HB_ChainContextPosFormat2*  ccpf2 )
{
  HB_UShort              n, count;

  HB_ChainPosClassSet*  cpcs;


  if ( ccpf2->ChainPosClassSet )
  {
    count = ccpf2->ChainPosClassSetCount;
    cpcs  = ccpf2->ChainPosClassSet;

    for ( n = 0; n < count; n++ )
      Free_ChainPosClassSet( &cpcs[n] );

    FREE( cpcs );
  }

  _HB_OPEN_Free_ClassDefinition( &ccpf2->LookaheadClassDef );
  _HB_OPEN_Free_ClassDefinition( &ccpf2->InputClassDef );
  _HB_OPEN_Free_ClassDefinition( &ccpf2->BacktrackClassDef );

  _HB_OPEN_Free_Coverage( &ccpf2->Coverage );
}

Project: harfbuzz

static void  Free_ChainPosClassSet( HB_ChainPosClassSet*  cpcs )
{
  HB_UShort               n, count;

  HB_ChainPosClassRule*  cpcr;


  if ( cpcs->ChainPosClassRule )
  {
    count = cpcs->ChainPosClassRuleCount;
    cpcr  = cpcs->ChainPosClassRule;

    for ( n = 0; n < count; n++ )
      Free_ChainPosClassRule( &cpcr[n] );

    FREE( cpcr );
  }
}

Project: harfbuzz

static void  Free_ChainPosRule( HB_ChainPosRule*  cpr )
{
  FREE( cpr->PosLookupRecord );
  FREE( cpr->Lookahead );
  FREE( cpr->Input );
  FREE( cpr->Backtrack );
}

Project: harfbuzz

static void  Free_ContextPos3( HB_ContextPosFormat3*  cpf3 )
{
  HB_UShort      n, count;

  HB_Coverage*  c;


  FREE( cpf3->PosLookupRecord );

  if ( cpf3->Coverage )
  {
    count = cpf3->GlyphCount;
    c     = cpf3->Coverage;

    for ( n = 0; n < count; n++ )
      _HB_OPEN_Free_Coverage( &c[n] );

    FREE( c );
  }
}

Project: harfbuzz

static void  Free_LigatureArray( HB_LigatureArray*  la,
				 HB_UShort           num_classes )
{
  HB_UShort            n, count;

  HB_LigatureAttach*  lat;


  if ( la->LigatureAttach )
  {
    count = la->LigatureCount;
    lat   = la->LigatureAttach;

    for ( n = 0; n < count; n++ )
      Free_LigatureAttach( &lat[n], num_classes );

    FREE( lat );
  }
}

Project: harfbuzz

static void  Free_MarkLigPos( HB_GPOS_SubTable* st)
{
  HB_MarkLigPos*  mlp = &st->marklig;

  Free_LigatureArray( &mlp->LigatureArray, mlp->ClassCount );
  Free_MarkArray( &mlp->MarkArray );
  _HB_OPEN_Free_Coverage( &mlp->LigatureCoverage );
  _HB_OPEN_Free_Coverage( &mlp->MarkCoverage );
}

Project: harfbuzz

static void  Free_PosClassRule( HB_PosClassRule*  pcr )
{
  FREE( pcr->PosLookupRecord );
  FREE( pcr->Class );
}

Project: harfbuzz

static void  Free_PosRuleSet( HB_PosRuleSet*  prs )
{
  HB_UShort     n, count;

  HB_PosRule*  pr;


  if ( prs->PosRule )
  {
    count = prs->PosRuleCount;
    pr    = prs->PosRule;

    for ( n = 0; n < count; n++ )
      Free_PosRule( &pr[n] );

    FREE( pr );
  }
}

Project: harfbuzz

static HB_Error  Get_Anchor( GPOS_Instance*   gpi,
			     HB_Anchor*      an,
			     HB_UShort        glyph_index,
			     HB_Fixed*          x_value,
			     HB_Fixed*          y_value )
{
  HB_Error  error = HB_Err_Ok;

#ifdef HB_SUPPORT_MULTIPLE_MASTER
  HB_GPOSHeader*  gpos = gpi->gpos;
#endif
  HB_UShort        ap;

  HB_Short         pixel_value;

  HB_UShort        x_ppem, y_ppem;
  HB_16Dot16         x_scale, y_scale;


  x_ppem  = gpi->font->x_ppem;
  y_ppem  = gpi->font->y_ppem;
  x_scale = gpi->font->x_scale;
  y_scale = gpi->font->y_scale;

  switch ( an->PosFormat )
  {
  case 0:
    /* The special case of an empty AnchorTable */
  default:

    return HB_Err_Not_Covered;

  case 1:
    *x_value = x_scale * an->af.af1.XCoordinate / 0x10000;
    *y_value = y_scale * an->af.af1.YCoordinate / 0x10000;
    break;

  case 2:
    if ( !gpi->dvi )
    {
      hb_uint32 n_points = 0;
      ap = an->af.af2.AnchorPoint;
      if (!gpi->font->klass->getPointInOutline)
          goto no_contour_point;
      error = gpi->font->klass->getPointInOutline(gpi->font, glyph_index, gpi->load_flags, ap, x_value, y_value, &n_points);
      if (error)
          return error;
      /* if n_points is set to zero, we use the design coordinate value pair.
       * This can happen e.g. for sbit glyphs. */
      if (!n_points)
          goto no_contour_point;
    }
    else
    {
    no_contour_point:
      *x_value = x_scale * an->af.af3.XCoordinate / 0x10000;
      *y_value = y_scale * an->af.af3.YCoordinate / 0x10000;
    }
    break;

  case 3:
    if ( !gpi->dvi )
    {
      _HB_OPEN_Get_Device( an->af.af3.DeviceTables[AF3_X_DEVICE_TABLE], x_ppem, &pixel_value );
      *x_value = pixel_value << 6;
      _HB_OPEN_Get_Device( an->af.af3.DeviceTables[AF3_Y_DEVICE_TABLE], y_ppem, &pixel_value );
      *y_value = pixel_value << 6;
    }
    else
      *x_value = *y_value = 0;

    *x_value += x_scale * an->af.af3.XCoordinate / 0x10000;
    *y_value += y_scale * an->af.af3.YCoordinate / 0x10000;
    break;

  case 4:
#ifdef HB_SUPPORT_MULTIPLE_MASTER
    error = (gpos->mmfunc)( gpi->font, an->af.af4.XIdAnchor,
			    x_value, gpos->data );
    if ( error )
      return error;

    error = (gpos->mmfunc)( gpi->font, an->af.af4.YIdAnchor,
			    y_value, gpos->data );
    if ( error )
      return error;
    break;
#else
    return ERR(HB_Err_Not_Covered);
#endif
  }

  return error;
}

Project: harfbuzz

HB_Error  HB_GPOS_Clear_Features( HB_GPOSHeader*  gpos )
{
  HB_UShort i;

  HB_UInt*  properties;


  if ( !gpos )
    return ERR(HB_Err_Invalid_Argument);

  gpos->FeatureList.ApplyCount = 0;

  properties = gpos->LookupList.Properties;

  for ( i = 0; i < gpos->LookupList.LookupCount; i++ )
    properties[i] = 0;

  return HB_Err_Ok;
}

Project: harfbuzz

HB_Error  HB_GPOS_Query_Features( HB_GPOSHeader*  gpos,
				  HB_UShort        script_index,
				  HB_UShort        language_index,
				  HB_UInt**       feature_tag_list )
{
  HB_UShort           n;
  HB_Error            error;
  HB_UInt*           ftl;

  HB_ScriptList*     sl;
  HB_ScriptRecord*   sr;
  HB_ScriptTable*    s;
  HB_LangSysRecord*  lsr;
  HB_LangSys*        ls;
  HB_UShort*          fi;

  HB_FeatureList*    fl;
  HB_FeatureRecord*  fr;


  if ( !gpos || !feature_tag_list )
    return ERR(HB_Err_Invalid_Argument);

  sl = &gpos->ScriptList;
  sr = sl->ScriptRecord;

  fl = &gpos->FeatureList;
  fr = fl->FeatureRecord;

  if ( script_index >= sl->ScriptCount )
    return ERR(HB_Err_Invalid_Argument);

  s   = &sr[script_index].Script;
  lsr = s->LangSysRecord;

  if ( language_index == 0xFFFF )
    ls = &s->DefaultLangSys;
  else
  {
    if ( language_index >= s->LangSysCount )
      return ERR(HB_Err_Invalid_Argument);

    ls = &lsr[language_index].LangSys;
  }

  fi = ls->FeatureIndex;

  if ( ALLOC_ARRAY( ftl, ls->FeatureCount + 1, HB_UInt ) )
    return error;

  for ( n = 0; n < ls->FeatureCount; n++ )
  {
    if ( fi[n] >= fl->FeatureCount )
    {
      FREE( ftl );
      return ERR(HB_Err_Invalid_SubTable_Format);
    }
    ftl[n] = fr[fi[n]].FeatureTag;
  }
  ftl[n] = 0;

  *feature_tag_list = ftl;

  return HB_Err_Ok;
}

Project: harfbuzz

HB_Error  HB_GPOS_Select_Language( HB_GPOSHeader*  gpos,
				   HB_UInt         language_tag,
				   HB_UShort        script_index,
				   HB_UShort*       language_index,
				   HB_UShort*       req_feature_index )
{
  HB_UShort           n;

  HB_ScriptList*     sl;
  HB_ScriptRecord*   sr;
  HB_ScriptTable*         s;
  HB_LangSysRecord*  lsr;


  if ( !gpos || !language_index || !req_feature_index )
    return ERR(HB_Err_Invalid_Argument);

  sl = &gpos->ScriptList;
  sr = sl->ScriptRecord;

  if ( script_index >= sl->ScriptCount )
    return ERR(HB_Err_Invalid_Argument);

  s   = &sr[script_index].Script;
  lsr = s->LangSysRecord;

  for ( n = 0; n < s->LangSysCount; n++ )
    if ( language_tag == lsr[n].LangSysTag )
    {
      *language_index = n;
      *req_feature_index = lsr[n].LangSys.ReqFeatureIndex;

      return HB_Err_Ok;
    }

  return HB_Err_Not_Covered;
}

Project: harfbuzz

static HB_Error  Load_MarkLigPos( HB_GPOS_SubTable* st,
				  HB_Stream        stream )
{
  HB_Error  error;
  HB_MarkLigPos*  mlp = &st->marklig;

  HB_UInt  cur_offset, new_offset, base_offset;


  base_offset = FILE_Pos();

  if ( ACCESS_Frame( 4L ) )
    return error;

  mlp->PosFormat = GET_UShort();
  new_offset     = GET_UShort() + base_offset;

  FORGET_Frame();

  cur_offset = FILE_Pos();
  if ( FILE_Seek( new_offset ) ||
       ( error = _HB_OPEN_Load_Coverage( &mlp->MarkCoverage, stream ) ) != HB_Err_Ok )
    return error;
  (void)FILE_Seek( cur_offset );

  if ( ACCESS_Frame( 2L ) )
    goto Fail3;

  new_offset = GET_UShort() + base_offset;

  FORGET_Frame();

  cur_offset = FILE_Pos();
  if ( FILE_Seek( new_offset ) ||
       ( error = _HB_OPEN_Load_Coverage( &mlp->LigatureCoverage,
				stream ) ) != HB_Err_Ok )
    goto Fail3;
  (void)FILE_Seek( cur_offset );

  if ( ACCESS_Frame( 4L ) )
    goto Fail2;

  mlp->ClassCount = GET_UShort();
  new_offset      = GET_UShort() + base_offset;

  FORGET_Frame();

  cur_offset = FILE_Pos();
  if ( FILE_Seek( new_offset ) ||
       ( error = Load_MarkArray( &mlp->MarkArray, stream ) ) != HB_Err_Ok )
    goto Fail2;
  (void)FILE_Seek( cur_offset );

  if ( ACCESS_Frame( 2L ) )
    goto Fail1;

  new_offset = GET_UShort() + base_offset;

  FORGET_Frame();

  cur_offset = FILE_Pos();
  if ( FILE_Seek( new_offset ) ||
       ( error = Load_LigatureArray( &mlp->LigatureArray, mlp->ClassCount,
				     stream ) ) != HB_Err_Ok )
    goto Fail1;

  return HB_Err_Ok;

Fail1:
  Free_MarkArray( &mlp->MarkArray );

Fail2:
  _HB_OPEN_Free_Coverage( &mlp->LigatureCoverage );

Fail3:
  _HB_OPEN_Free_Coverage( &mlp->MarkCoverage );
  return error;
}

Project: harfbuzz

static HB_Error  Load_PosRule( HB_PosRule*  pr,
			       HB_Stream     stream )
{
  HB_Error  error;

  HB_UShort             n, count;
  HB_UShort*            i;

  HB_PosLookupRecord*  plr;


  if ( ACCESS_Frame( 4L ) )
    return error;

  pr->GlyphCount = GET_UShort();
  pr->PosCount   = GET_UShort();

  FORGET_Frame();

  pr->Input = NULL;

  count = pr->GlyphCount - 1;         /* only GlyphCount - 1 elements */

  if ( ALLOC_ARRAY( pr->Input, count, HB_UShort ) )
    return error;

  i = pr->Input;

  if ( ACCESS_Frame( count * 2L ) )
    goto Fail2;

  for ( n = 0; n < count; n++ )
    i[n] = GET_UShort();

  FORGET_Frame();

  pr->PosLookupRecord = NULL;

  count = pr->PosCount;

  if ( ALLOC_ARRAY( pr->PosLookupRecord, count, HB_PosLookupRecord ) )
    goto Fail2;

  plr = pr->PosLookupRecord;

  if ( ACCESS_Frame( count * 4L ) )
    goto Fail1;

  for ( n = 0; n < count; n++ )
  {
    plr[n].SequenceIndex   = GET_UShort();
    plr[n].LookupListIndex = GET_UShort();
  }

  FORGET_Frame();

  return HB_Err_Ok;

Fail1:
  FREE( plr );

Fail2:
  FREE( i );
  return error;
}

Project: harfbuzz

static HB_Error  Lookup_ContextPos1( GPOS_Instance*          gpi,
				     HB_ContextPosFormat1*  cpf1,
				     HB_Buffer              buffer,
				     HB_UShort               flags,
				     HB_UShort               context_length,
				     int                     nesting_level )
{
  HB_UShort        index, property;
  HB_UShort        i, j, k, numpr;
  HB_Error         error;
  HB_GPOSHeader*  gpos = gpi->gpos;

  HB_PosRule*     pr;
  HB_GDEFHeader*  gdef;


  gdef = gpos->gdef;

  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
    return error;

  error = _HB_OPEN_Coverage_Index( &cpf1->Coverage, IN_CURGLYPH(), &index );
  if ( error )
    return error;

  pr    = cpf1->PosRuleSet[index].PosRule;
  numpr = cpf1->PosRuleSet[index].PosRuleCount;

  for ( k = 0; k < numpr; k++ )
  {
    if ( context_length != 0xFFFF && context_length < pr[k].GlyphCount )
      goto next_posrule;

    if ( buffer->in_pos + pr[k].GlyphCount > buffer->in_length )
      goto next_posrule;                       /* context is too long */

    for ( i = 1, j = buffer->in_pos + 1; i < pr[k].GlyphCount; i++, j++ )
    {
      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
      {
	if ( error && error != HB_Err_Not_Covered )
	  return error;

	if ( j + pr[k].GlyphCount - i == (HB_Int)buffer->in_length )
	  goto next_posrule;
	j++;
      }

      if ( IN_GLYPH( j ) != pr[k].Input[i - 1] )
	goto next_posrule;
    }

    return Do_ContextPos( gpi, pr[k].GlyphCount,
			  pr[k].PosCount, pr[k].PosLookupRecord,
			  buffer,
			  nesting_level );

    next_posrule:
      ;
  }

  return HB_Err_Not_Covered;
}

Project: harfbuzz

static HB_Error  Lookup_PairPos( GPOS_Instance*    gpi,
				 HB_GPOS_SubTable* st,
				 HB_Buffer        buffer,
				 HB_UShort         flags,
				 HB_UShort         context_length,
				 int               nesting_level )
{
  HB_Error         error;
  HB_UShort        index, property;
  HB_UInt          first_pos;
  HB_GPOSHeader*  gpos = gpi->gpos;
  HB_PairPos*     pp = &st->pair;

  HB_UNUSED(nesting_level);

  if ( buffer->in_pos >= buffer->in_length - 1 )
    return HB_Err_Not_Covered;           /* Not enough glyphs in stream */

  if ( context_length != 0xFFFF && context_length < 2 )
    return HB_Err_Not_Covered;

  if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
    return error;

  error = _HB_OPEN_Coverage_Index( &pp->Coverage, IN_CURGLYPH(), &index );
  if ( error )
    return error;

  /* second glyph */

  first_pos = buffer->in_pos;
  (buffer->in_pos)++;

  while ( CHECK_Property( gpos->gdef, IN_CURITEM(),
			  flags, &property ) )
  {
    if ( error && error != HB_Err_Not_Covered )
      return error;

    if ( buffer->in_pos == buffer->in_length )
      {
	buffer->in_pos = first_pos;
        return HB_Err_Not_Covered;
      }
    (buffer->in_pos)++;

  }

  switch ( pp->PosFormat )
  {
  case 1:
    error = Lookup_PairPos1( gpi, &pp->ppf.ppf1, buffer,
			     first_pos, index,
			     pp->ValueFormat1, pp->ValueFormat2 );
    break;

  case 2:
    error = Lookup_PairPos2( gpi, &pp->ppf.ppf2, buffer, first_pos,
			     pp->ValueFormat1, pp->ValueFormat2 );
    break;

  default:
    return ERR(HB_Err_Invalid_SubTable_Format);
  }

  /* if we don't have coverage for the second glyph don't skip it for
     further lookups but reset in_pos back to the first_glyph and let
     the caller in Do_String_Lookup increment in_pos */
  if ( error == HB_Err_Not_Covered )
      buffer->in_pos = first_pos;

  /* adjusting the `next' glyph */

  if ( pp->ValueFormat2 )
    (buffer->in_pos)++;

  return error;
}

Project: harfbuzz

_HB_GPOS_Free_SubTable( HB_GPOS_SubTable* st,
			HB_UShort         lookup_type )
{
  switch ( lookup_type ) {
    case HB_GPOS_LOOKUP_SINGLE:		Free_SinglePos		( st ); return;
    case HB_GPOS_LOOKUP_PAIR:		Free_PairPos		( st ); return;
    case HB_GPOS_LOOKUP_CURSIVE:	Free_CursivePos		( st ); return;
    case HB_GPOS_LOOKUP_MARKBASE:	Free_MarkBasePos	( st ); return;
    case HB_GPOS_LOOKUP_MARKLIG:	Free_MarkLigPos		( st ); return;
    case HB_GPOS_LOOKUP_MARKMARK:	Free_MarkMarkPos	( st ); return;
    case HB_GPOS_LOOKUP_CONTEXT:	Free_ContextPos		( st ); return;
    case HB_GPOS_LOOKUP_CHAIN:		Free_ChainContextPos	( st ); return;
  /*case HB_GPOS_LOOKUP_EXTENSION:	Free_ExtensionPos	( st ); return;*/
    default:									return;
  }
}