static Image *ReadOneJNGImage(MngInfo *mng_info,
    const ImageInfo *image_info, ExceptionInfo *exception)
{
  Image
    *alpha_image,
    *color_image,
    *image,
    *jng_image;

  ImageInfo
    *alpha_image_info,
    *color_image_info;

  MagickBooleanType
    logging;

  ssize_t
    y;

  MagickBooleanType
    status;

  png_uint_32
    jng_height,
    jng_width;

  png_byte
    jng_color_type,
    jng_image_sample_depth,
    jng_image_compression_method,
    jng_image_interlace_method,
    jng_alpha_sample_depth,
    jng_alpha_compression_method,
    jng_alpha_filter_method,
    jng_alpha_interlace_method;

  register const Quantum
    *s;

  register ssize_t
    i,
    x;

  register Quantum
    *q;

  register unsigned char
    *p;

  unsigned int
    read_JSEP,
    reading_idat;

  size_t
    length;

  jng_alpha_compression_method=0;
  jng_alpha_sample_depth=8;
  jng_color_type=0;
  jng_height=0;
  jng_width=0;
  alpha_image=(Image *) NULL;
  color_image=(Image *) NULL;
  alpha_image_info=(ImageInfo *) NULL;
  color_image_info=(ImageInfo *) NULL;

  logging=LogMagickEvent(CoderEvent,GetMagickModule(),
    "  Enter ReadOneJNGImage()");

  image=mng_info->image;

  if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
    {
      /*
        Allocate next image structure.
      */
      if (logging != MagickFalse)
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
           "  AcquireNextImage()");

      AcquireNextImage(image_info,image,exception);

      if (GetNextImageInList(image) == (Image *) NULL)
        return(DestroyImageList(image));

      image=SyncNextImageInList(image);
    }
  mng_info->image=image;

  /*
    Signature bytes have already been read.
  */

  read_JSEP=MagickFalse;
  reading_idat=MagickFalse;
  for (;;)
  {
    char
      type[MagickPathExtent];

    unsigned char
      *chunk;

    unsigned int
      count;

    /*
      Read a new JNG chunk.
    */
    status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
      2*GetBlobSize(image));

    if (status == MagickFalse)
      break;

    type[0]='\0';
    (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
    length=ReadBlobMSBLong(image);
    count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);

    if (logging != MagickFalse)
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
        "  Reading JNG chunk type %c%c%c%c, length: %.20g",
        type[0],type[1],type[2],type[3],(double) length);

    if (length > PNG_UINT_31_MAX || count == 0)
      {
        if (color_image != (Image *) NULL)
          color_image=DestroyImage(color_image);
        if (color_image_info != (ImageInfo *) NULL)
          color_image_info=DestroyImageInfo(color_image_info);
        ThrowReaderException(CorruptImageError,"CorruptImage");
      }

    p=NULL;
    chunk=(unsigned char *) NULL;

    if (length != 0)
      {
        if (length > GetBlobSize(image))
          ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
        chunk=(unsigned char *) AcquireQuantumMemory(length+MagickPathExtent,
          sizeof(*chunk));

        if (chunk == (unsigned char *) NULL)
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");

        for (i=0; i < (ssize_t) length; i++)
        {
          int
            c;

          c=ReadBlobByte(image);
          if (c == EOF)
            break;
          chunk[i]=(unsigned char) c;
        }

        p=chunk;
      }

    (void) ReadBlobMSBLong(image);  /* read crc word */

    if (memcmp(type,mng_JHDR,4) == 0)
      {
        if (length == 16)
          {
            jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
              (p[2] << 8) | p[3]);
            jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
              (p[6] << 8) | p[7]);
            if ((jng_width == 0) || (jng_height == 0))
              ThrowReaderException(CorruptImageError,
                "NegativeOrZeroImageSize");
            jng_color_type=p[8];
            jng_image_sample_depth=p[9];
            jng_image_compression_method=p[10];
            jng_image_interlace_method=p[11];

            image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
              NoInterlace;

            jng_alpha_sample_depth=p[12];
            jng_alpha_compression_method=p[13];
            jng_alpha_filter_method=p[14];
            jng_alpha_interlace_method=p[15];

            if (logging != MagickFalse)
              {
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                  "    jng_width:      %16lu,    jng_height:     %16lu\n"
                  "    jng_color_type: %16d,     jng_image_sample_depth: %3d\n"
                  "    jng_image_compression_method:%3d",
                  (unsigned long) jng_width, (unsigned long) jng_height,
                  jng_color_type, jng_image_sample_depth,
                  jng_image_compression_method);

                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                  "    jng_image_interlace_method:  %3d"
                  "    jng_alpha_sample_depth:      %3d",
                  jng_image_interlace_method,
                  jng_alpha_sample_depth);

                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                  "    jng_alpha_compression_method:%3d\n"
                  "    jng_alpha_filter_method:     %3d\n"
                  "    jng_alpha_interlace_method:  %3d",
                  jng_alpha_compression_method,
                  jng_alpha_filter_method,
                  jng_alpha_interlace_method);
              }
          }

        if (length != 0)
          chunk=(unsigned char *) RelinquishMagickMemory(chunk);

        continue;
      }

    if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
        ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
         (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
      {
        /*
           o create color_image
           o open color_blob, attached to color_image
           o if (color type has alpha)
               open alpha_blob, attached to alpha_image
        */

        color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));

        if (color_image_info == (ImageInfo *) NULL)
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");

        GetImageInfo(color_image_info);
        color_image=AcquireImage(color_image_info,exception);

        if (color_image == (Image *) NULL)
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");

        if (logging != MagickFalse)
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "    Creating color_blob.");

        (void) AcquireUniqueFilename(color_image->filename);
        status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
          exception);

        if (status == MagickFalse)
          {
            color_image=DestroyImage(color_image);
            return(DestroyImageList(image));
          }

        if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
          {
            alpha_image_info=(ImageInfo *)
              AcquireMagickMemory(sizeof(ImageInfo));

            if (alpha_image_info == (ImageInfo *) NULL)
              {
                color_image=DestroyImage(color_image);
                ThrowReaderException(ResourceLimitError,
                  "MemoryAllocationFailed");
              }

            GetImageInfo(alpha_image_info);
            alpha_image=AcquireImage(alpha_image_info,exception);

            if (alpha_image == (Image *) NULL)
              {
                alpha_image_info=DestroyImageInfo(alpha_image_info);
                color_image=DestroyImage(color_image);
                ThrowReaderException(ResourceLimitError,
                  "MemoryAllocationFailed");
              }

            if (logging != MagickFalse)
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                "    Creating alpha_blob.");

            (void) AcquireUniqueFilename(alpha_image->filename);
            status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
              exception);

            if (status == MagickFalse)
              {
                alpha_image=DestroyImage(alpha_image);
                alpha_image_info=DestroyImageInfo(alpha_image_info);
                color_image=DestroyImage(color_image);
                return(DestroyImageList(image));
              }

            if (jng_alpha_compression_method == 0)
              {
                unsigned char
                  data[18];

                if (logging != MagickFalse)
                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                    "    Writing IHDR chunk to alpha_blob.");

                (void) WriteBlob(alpha_image,8,(const unsigned char *)
                  "\211PNG\r\n\032\n");

                (void) WriteBlobMSBULong(alpha_image,13L);
                PNGType(data,mng_IHDR);
                LogPNGChunk(logging,mng_IHDR,13L);
                PNGLong(data+4,jng_width);
                PNGLong(data+8,jng_height);
                data[12]=jng_alpha_sample_depth;
                data[13]=0; /* color_type gray */
                data[14]=0; /* compression method 0 */
                data[15]=0; /* filter_method 0 */
                data[16]=0; /* interlace_method 0 */
                (void) WriteBlob(alpha_image,17,data);
                (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
              }
          }
        reading_idat=MagickTrue;
      }

    if (memcmp(type,mng_JDAT,4) == 0)
      {
        /* Copy chunk to color_image->blob */

        if (logging != MagickFalse)
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
            "    Copying JDAT chunk data to color_blob.");

        if (length != 0)
          {
            (void) WriteBlob(color_image,length,chunk);
            chunk=(unsigned char *) RelinquishMagickMemory(chunk);
          }

        continue;
      }

    if (memcmp(type,mng_IDAT,4) == 0)
      {
        png_byte
           data[5];

        /* Copy IDAT header and chunk data to alpha_image->blob */

        if (alpha_image != NULL && image_info->ping == MagickFalse)
          {
            if (logging != MagickFalse)
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                "    Copying IDAT chunk data to alpha_blob.");

            (void) WriteBlobMSBULong(alpha_image,(size_t) length);
            PNGType(data,mng_IDAT);
            LogPNGChunk(logging,mng_IDAT,length);
            (void) WriteBlob(alpha_image,4,data);
            (void) WriteBlob(alpha_image,length,chunk);
            (void) WriteBlobMSBULong(alpha_image,
              crc32(crc32(0,data,4),chunk,(uInt) length));
          }

        if (length != 0)
          chunk=(unsigned char *) RelinquishMagickMemory(chunk);

        continue;
      }

    if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
      {
        /* Copy chunk data to alpha_image->blob */

        if (alpha_image != NULL && image_info->ping == MagickFalse)
          {
            if (logging != MagickFalse)
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                "    Copying JDAA chunk data to alpha_blob.");

            (void) WriteBlob(alpha_image,length,chunk);
          }

        if (length != 0)
          chunk=(unsigned char *) RelinquishMagickMemory(chunk);

        continue;
      }

    if (memcmp(type,mng_JSEP,4) == 0)
      {
        read_JSEP=MagickTrue;

        if (length != 0)
          chunk=(unsigned char *) RelinquishMagickMemory(chunk);

        continue;
      }

    if (memcmp(type,mng_bKGD,4) == 0)
      {
        if (length == 2)
          {
            image->background_color.red=ScaleCharToQuantum(p[1]);
            image->background_color.green=image->background_color.red;
            image->background_color.blue=image->background_color.red;
          }

        if (length == 6)
          {
            image->background_color.red=ScaleCharToQuantum(p[1]);
            image->background_color.green=ScaleCharToQuantum(p[3]);
            image->background_color.blue=ScaleCharToQuantum(p[5]);
          }

        chunk=(unsigned char *) RelinquishMagickMemory(chunk);
        continue;
      }

    if (memcmp(type,mng_gAMA,4) == 0)
      {
        if (length == 4)
          image->gamma=((float) mng_get_long(p))*0.00001;

        chunk=(unsigned char *) RelinquishMagickMemory(chunk);
        continue;
      }

    if (memcmp(type,mng_cHRM,4) == 0)
      {
        if (length == 32)
          {
            image->chromaticity.white_point.x=0.00001*mng_get_long(p);
            image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
            image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
            image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
            image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
            image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
            image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
            image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
          }

        chunk=(unsigned char *) RelinquishMagickMemory(chunk);
        continue;
      }

    if (memcmp(type,mng_sRGB,4) == 0)
      {
        if (length == 1)
          {
            image->rendering_intent=
              Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
            image->gamma=1.000f/2.200f;
            image->chromaticity.red_primary.x=0.6400f;
            image->chromaticity.red_primary.y=0.3300f;
            image->chromaticity.green_primary.x=0.3000f;
            image->chromaticity.green_primary.y=0.6000f;
            image->chromaticity.blue_primary.x=0.1500f;
            image->chromaticity.blue_primary.y=0.0600f;
            image->chromaticity.white_point.x=0.3127f;
            image->chromaticity.white_point.y=0.3290f;
          }

        chunk=(unsigned char *) RelinquishMagickMemory(chunk);
        continue;
      }

    if (memcmp(type,mng_oFFs,4) == 0)
      {
        if (length > 8)
          {
            image->page.x=(ssize_t) mng_get_long(p);
            image->page.y=(ssize_t) mng_get_long(&p[4]);

            if ((int) p[8] != 0)
              {
                image->page.x/=10000;
                image->page.y/=10000;
              }
          }

        if (length != 0)
          chunk=(unsigned char *) RelinquishMagickMemory(chunk);

        continue;
      }

    if (memcmp(type,mng_pHYs,4) == 0)
      {
        if (length > 8)
          {
            image->resolution.x=(double) mng_get_long(p);
            image->resolution.y=(double) mng_get_long(&p[4]);
            if ((int) p[8] == PNG_RESOLUTION_METER)
              {
                image->units=PixelsPerCentimeterResolution;
                image->resolution.x=image->resolution.x/100.0f;
                image->resolution.y=image->resolution.y/100.0f;
              }
          }

        chunk=(unsigned char *) RelinquishMagickMemory(chunk);
        continue;
      }

#if 0
    if (memcmp(type,mng_iCCP,4) == 0)
      {
        /* To do: */
        if (length != 0)
          chunk=(unsigned char *) RelinquishMagickMemory(chunk);

        continue;
      }
#endif

    if (length != 0)
      chunk=(unsigned char *) RelinquishMagickMemory(chunk);

    if (memcmp(type,mng_IEND,4))
      continue;

    break;
  }

  /* IEND found */

  /*
    Finish up reading image data:

       o read main image from color_blob.

       o close color_blob.

       o if (color_type has alpha)
            if alpha_encoding is PNG
               read secondary image from alpha_blob via ReadPNG
            if alpha_encoding is JPEG
               read secondary image from alpha_blob via ReadJPEG

       o close alpha_blob.

       o copy intensity of secondary image into
         alpha samples of main image.

       o destroy the secondary image.
  */

  if (color_image_info == (ImageInfo *) NULL)
    {
      assert(color_image == (Image *) NULL);
      assert(alpha_image == (Image *) NULL);
      return(DestroyImageList(image));
    }

  if (color_image == (Image *) NULL)
    {
      assert(alpha_image == (Image *) NULL);
      return(DestroyImageList(image));
    }

  (void) SeekBlob(color_image,0,SEEK_SET);

  if (logging != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
      "    Reading jng_image from color_blob.");

  assert(color_image_info != (ImageInfo *) NULL);
  (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,"%s",
    color_image->filename);

  color_image_info->ping=MagickFalse;   /* To do: avoid this */
  jng_image=ReadImage(color_image_info,exception);

  (void) RelinquishUniqueFileResource(color_image->filename);
  color_image=DestroyImage(color_image);
  color_image_info=DestroyImageInfo(color_image_info);

  if (jng_image == (Image *) NULL)
    return(DestroyImageList(image));

  if (logging != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
      "    Copying jng_image pixels to main image.");

  image->rows=jng_height;
  image->columns=jng_width;

  status=SetImageExtent(image,image->columns,image->rows,exception);
  if (status == MagickFalse)
    return(DestroyImageList(image));

  for (y=0; y < (ssize_t) image->rows; y++)
  {
    s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
    q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
    for (x=(ssize_t) image->columns; x != 0; x--)
    {
      SetPixelRed(image,GetPixelRed(jng_image,s),q);
      SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
      SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
      q+=GetPixelChannels(image);
      s+=GetPixelChannels(jng_image);
    }

    if (SyncAuthenticPixels(image,exception) == MagickFalse)
      break;
  }

  jng_image=DestroyImage(jng_image);

  if (image_info->ping == MagickFalse)
    {
     if (jng_color_type >= 12)
       {
         if (jng_alpha_compression_method == 0)
           {
             png_byte
               data[5];
             (void) WriteBlobMSBULong(alpha_image,0x00000000L);
             PNGType(data,mng_IEND);
             LogPNGChunk(logging,mng_IEND,0L);
             (void) WriteBlob(alpha_image,4,data);
             (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
           }

         (void) CloseBlob(alpha_image);

         if (logging != MagickFalse)
           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
             "    Reading alpha from alpha_blob.");

         (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
           "%s",alpha_image->filename);

         jng_image=ReadImage(alpha_image_info,exception);

         if (jng_image != (Image *) NULL)
           for (y=0; y < (ssize_t) image->rows; y++)
           {
             s=GetVirtualPixels(jng_image,0,y,image->columns,1,
               exception);
             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);

             if (image->alpha_trait != UndefinedPixelTrait)
               for (x=(ssize_t) image->columns; x != 0; x--)
               {
                  SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
                  q+=GetPixelChannels(image);
                  s+=GetPixelChannels(jng_image);
               }

             else
               for (x=(ssize_t) image->columns; x != 0; x--)
               {
                  SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
                  if (GetPixelAlpha(image,q) != OpaqueAlpha)
                    image->alpha_trait=BlendPixelTrait;
                  q+=GetPixelChannels(image);
                  s+=GetPixelChannels(jng_image);
               }

             if (SyncAuthenticPixels(image,exception) == MagickFalse)
               break;
           }
         (void) RelinquishUniqueFileResource(alpha_image->filename);
         alpha_image=DestroyImage(alpha_image);
         alpha_image_info=DestroyImageInfo(alpha_image_info);
         if (jng_image != (Image *) NULL)
           jng_image=DestroyImage(jng_image);
       }
    }

  /* Read the JNG image.  */

  if (mng_info->mng_type == 0)
    {
      mng_info->mng_width=jng_width;
      mng_info->mng_height=jng_height;
    }

  if (image->page.width == 0 && image->page.height == 0)
    {
      image->page.width=jng_width;
      image->page.height=jng_height;
    }

  if (image->page.x == 0 && image->page.y == 0)
    {
      image->page.x=mng_info->x_off[mng_info->object_id];
      image->page.y=mng_info->y_off[mng_info->object_id];
    }

  else
    {
      image->page.y=mng_info->y_off[mng_info->object_id];
    }

  mng_info->image_found++;
  status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
    2*GetBlobSize(image));

  if (status == MagickFalse)
    return(DestroyImageList(image));

  if (logging != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
      "  exit ReadOneJNGImage()");

  return(image);
}


static Image *ReadJNGImage(const ImageInfo *image_info,
                ExceptionInfo *exception)
{
  Image
    *image;

  MagickBooleanType
    logging,
    status;

  MngInfo
    *mng_info;

  char
    magic_number[MagickPathExtent];

  size_t
    count;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
     image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
  image=AcquireImage(image_info,exception);
  mng_info=(MngInfo *) NULL;
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);

  if (status == MagickFalse)
    return((Image *) NULL);

  if (LocaleCompare(image_info->magick,"JNG") != 0)
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");

  /* Verify JNG signature.  */

  count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);

  if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");

  /*
     Verify that file size large enough to contain a JNG datastream.
  */
  if (GetBlobSize(image) < 147)
    ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");

  /* Allocate a MngInfo structure.  */

  mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));

  if (mng_info == (MngInfo *) NULL)
    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");

  /* Initialize members of the MngInfo structure.  */

  (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));

  mng_info->image=image;
  image=ReadOneJNGImage(mng_info,image_info,exception);
  mng_info=MngInfoFreeStruct(mng_info);

  if (image == (Image *) NULL)
    {
      if (logging != MagickFalse)
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
          "exit ReadJNGImage() with error");

      return((Image *) NULL);
    }
  (void) CloseBlob(image);

  if (image->columns == 0 || image->rows == 0)
    {
      if (logging != MagickFalse)
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
          "exit ReadJNGImage() with error");

      ThrowReaderException(CorruptImageError,"CorruptImage");
    }

  if (logging != MagickFalse)
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");

  return(image);
}

Skip MNG CLIP chunk with out-of-range object IDs


static bool test_bad_write(struct torture_context *tctx,
		       struct smbcli_state *cli)
{
	bool ret = false;
	int fnum = -1;
	struct smbcli_request *req = NULL;
	const char *fname = BASEDIR "\\badwrite.txt";
	bool ok = false;

	if (!torture_setup_dir(cli, BASEDIR)) {
		torture_fail(tctx, "failed to setup basedir");
	}

	torture_comment(tctx, "Testing RAW_BAD_WRITE\n");

	fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
	if (fnum == -1) {
		torture_fail_goto(tctx,
			done,
			talloc_asprintf(tctx,
				"Failed to create %s - %s\n",
				fname,
				smbcli_errstr(cli->tree)));
	}

	req = smbcli_request_setup(cli->tree,
				   SMBwrite,
				   5,
				   0);
	if (req == NULL) {
		torture_fail_goto(tctx,
			done,
			talloc_asprintf(tctx, "talloc fail\n"));
	}

	SSVAL(req->out.vwv, VWV(0), fnum);
	SSVAL(req->out.vwv, VWV(1), 65535); /* bad write length. */
	SIVAL(req->out.vwv, VWV(2), 0); /* offset */
	SSVAL(req->out.vwv, VWV(4), 0); /* remaining. */

        if (!smbcli_request_send(req)) {
		torture_fail_goto(tctx,
			done,
			talloc_asprintf(tctx, "Send failed\n"));
        }

        if (!smbcli_request_receive(req)) {
		torture_fail_goto(tctx,
			done,
			talloc_asprintf(tctx, "Reveive failed\n"));
	}

	/*
	 * Check for expected error codes.
	 * ntvfs returns NT_STATUS_UNSUCCESSFUL.
	 */
	ok = (NT_STATUS_EQUAL(req->status, NT_STATUS_INVALID_PARAMETER) ||
	     NT_STATUS_EQUAL(req->status, NT_STATUS_UNSUCCESSFUL));

	if (!ok) {
		torture_fail_goto(tctx,
			done,
			talloc_asprintf(tctx,
				"Should have returned "
				"NT_STATUS_INVALID_PARAMETER or "
				"NT_STATUS_UNSUCCESSFUL "
				"got %s\n",
				nt_errstr(req->status)));
        }

	ret = true;

done:
	if (req != NULL) {
		smbcli_request_destroy(req);
	}
	if (fnum != -1) {
		smbcli_close(cli->tree, fnum);
	}
	smb_raw_exit(cli->session);
	smbcli_deltree(cli->tree, BASEDIR);
	return ret;
}

CVE-2022-32742: s4: torture: Add raw.write.bad-write test.

Reproduces the test code in:

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15085

Add knownfail.

Signed-off-by: Jeremy Allison <jra@samba.org>
Reviewed-by: David Disseldorp <ddiss@samba.org>


static bool test_writex(struct torture_context *tctx,
			struct smbcli_state *cli)
{
	union smb_write io;
	NTSTATUS status;
	bool ret = true;
	int fnum, i;
	uint8_t *buf;
	const int maxsize = 90000;
	const char *fname = BASEDIR "\\test.txt";
	unsigned int seed = time(NULL);
	union smb_fileinfo finfo;
	int max_bits=63;

	if (!torture_setting_bool(tctx, "dangerous", false)) {
		max_bits=33;
		torture_comment(tctx, "dangerous not set - limiting range of test to 2^%d\n", max_bits);
	}

	buf = talloc_zero_array(tctx, uint8_t, maxsize);

	if (!cli->transport->negotiate.lockread_supported) {
		torture_comment(tctx, "Server does not support writeunlock - skipping\n");
		return true;
	}

	if (!torture_setup_dir(cli, BASEDIR)) {
		torture_fail(tctx, "failed to setup basedir");
	}

	torture_comment(tctx, "Testing RAW_WRITE_WRITEX\n");
	io.generic.level = RAW_WRITE_WRITEX;

	fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
	if (fnum == -1) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)));
	}

	torture_comment(tctx, "Trying zero write\n");
	io.writex.in.file.fnum = fnum;
	io.writex.in.offset = 0;
	io.writex.in.wmode = 0;
	io.writex.in.remaining = 0;
	io.writex.in.count = 0;
	io.writex.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.writex.out.nwritten, 0);

	setup_buffer(buf, seed, maxsize);

	torture_comment(tctx, "Trying small write\n");
	io.writex.in.count = 9;
	io.writex.in.offset = 4;
	io.writex.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count);

	memset(buf, 0, maxsize);
	if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__));
	}
	CHECK_BUFFER(buf+4, seed, 9);
	CHECK_VALUE(IVAL(buf,0), 0);

	setup_buffer(buf, seed, maxsize);

	torture_comment(tctx, "Trying large write\n");
	io.writex.in.count = 4000;
	io.writex.in.offset = 0;
	io.writex.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.writex.out.nwritten, 4000);

	memset(buf, 0, maxsize);
	if (smbcli_read(cli->tree, fnum, buf, 0, 4000) != 4000) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__));
	}
	CHECK_BUFFER(buf, seed, 4000);

	torture_comment(tctx, "Trying bad fnum\n");
	io.writex.in.file.fnum = fnum+1;
	io.writex.in.count = 4000;
	io.writex.in.offset = 0;
	io.writex.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);

	torture_comment(tctx, "Testing wmode\n");
	io.writex.in.file.fnum = fnum;
	io.writex.in.count = 1;
	io.writex.in.offset = 0;
	io.writex.in.wmode = 1;
	io.writex.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count);

	io.writex.in.wmode = 2;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.writex.out.nwritten, io.writex.in.count);

	torture_comment(tctx, "Trying locked region\n");
	cli->session->pid++;
	if (NT_STATUS_IS_ERR(smbcli_lock(cli->tree, fnum, 3, 1, 0, WRITE_LOCK))) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "Failed to lock file at %s\n", __location__));
	}
	cli->session->pid--;
	io.writex.in.wmode = 0;
	io.writex.in.count = 4;
	io.writex.in.offset = 0;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);

	torture_comment(tctx, "Setting file as sparse\n");
	status = torture_set_sparse(cli->tree, fnum);
	CHECK_STATUS(status, NT_STATUS_OK);

	if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
		torture_skip(tctx, "skipping large file tests - CAP_LARGE_FILES not set\n");
	}

	torture_comment(tctx, "Trying 2^32 offset\n");
	setup_buffer(buf, seed, maxsize);
	io.writex.in.file.fnum = fnum;
	io.writex.in.count = 4000;
	io.writex.in.offset = 0xFFFFFFFF - 2000;
	io.writex.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.writex.out.nwritten, 4000);
	CHECK_ALL_INFO(io.writex.in.count + (uint64_t)io.writex.in.offset, size);

	memset(buf, 0, maxsize);
	if (smbcli_read(cli->tree, fnum, buf, io.writex.in.offset, 4000) != 4000) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__));
	}
	CHECK_BUFFER(buf, seed, 4000);

	for (i=33;i<max_bits;i++) {
		torture_comment(tctx, "Trying 2^%d offset\n", i);
		setup_buffer(buf, seed+1, maxsize);
		io.writex.in.file.fnum = fnum;
		io.writex.in.count = 4000;
		io.writex.in.offset = ((uint64_t)1) << i;
		io.writex.in.data = buf;
		status = smb_raw_write(cli->tree, &io);
		if (i>33 &&
		    NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
			break;
		}
		CHECK_STATUS(status, NT_STATUS_OK);
		CHECK_VALUE(io.writex.out.nwritten, 4000);
		CHECK_ALL_INFO(io.writex.in.count + (uint64_t)io.writex.in.offset, size);

		memset(buf, 0, maxsize);
		if (smbcli_read(cli->tree, fnum, buf, io.writex.in.offset, 4000) != 4000) {
			ret = false;
			torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__));
		}
		CHECK_BUFFER(buf, seed+1, 4000);
	}
	torture_comment(tctx, "limit is 2^%d\n", i);

	setup_buffer(buf, seed, maxsize);

done:
	smbcli_close(cli->tree, fnum);
	smb_raw_exit(cli->session);
	smbcli_deltree(cli->tree, BASEDIR);
	return ret;
}


static bool check_buffer(struct torture_context *tctx,
			 uint8_t *buf, unsigned int seed, int len, const char *location)
{
	int i;
	srandom(seed);
	for (i=0;i<len;i++) {
		uint8_t v = random();
		if (buf[i] != v) {
			torture_fail(tctx, talloc_asprintf(tctx, "Buffer incorrect at %s! ofs=%d buf=0x%x correct=0x%x\n",
			       location, i, buf[i], v));
			return false;
		}
	}
	return true;
}


static bool test_writeunlock(struct torture_context *tctx,
			     struct smbcli_state *cli)
{
	union smb_write io;
	NTSTATUS status;
	bool ret = true;
	int fnum;
	uint8_t *buf;
	const int maxsize = 90000;
	const char *fname = BASEDIR "\\test.txt";
	unsigned int seed = time(NULL);
	union smb_fileinfo finfo;

	buf = talloc_zero_array(tctx, uint8_t, maxsize);

	if (!cli->transport->negotiate.lockread_supported) {
		torture_skip(tctx, "Server does not support writeunlock - skipping\n");
	}

	if (!torture_setup_dir(cli, BASEDIR)) {
		torture_fail(tctx, "failed to setup basedir");
	}

	torture_comment(tctx, "Testing RAW_WRITE_WRITEUNLOCK\n");
	io.generic.level = RAW_WRITE_WRITEUNLOCK;

	fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
	if (fnum == -1) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)));
	}

	torture_comment(tctx, "Trying zero write\n");
	io.writeunlock.in.file.fnum = fnum;
	io.writeunlock.in.count = 0;
	io.writeunlock.in.offset = 0;
	io.writeunlock.in.remaining = 0;
	io.writeunlock.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.writeunlock.out.nwritten, io.writeunlock.in.count);

	setup_buffer(buf, seed, maxsize);

	torture_comment(tctx, "Trying small write\n");
	io.writeunlock.in.count = 9;
	io.writeunlock.in.offset = 4;
	io.writeunlock.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
	if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__));
	}
	CHECK_BUFFER(buf+4, seed, 9);
	CHECK_VALUE(IVAL(buf,0), 0);

	setup_buffer(buf, seed, maxsize);
	smbcli_lock(cli->tree, fnum, io.writeunlock.in.offset, io.writeunlock.in.count,
		 0, WRITE_LOCK);
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.writeunlock.out.nwritten, io.writeunlock.in.count);

	memset(buf, 0, maxsize);
	if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__));
	}
	CHECK_BUFFER(buf+4, seed, 9);
	CHECK_VALUE(IVAL(buf,0), 0);

	setup_buffer(buf, seed, maxsize);

	torture_comment(tctx, "Trying large write\n");
	io.writeunlock.in.count = 4000;
	io.writeunlock.in.offset = 0;
	io.writeunlock.in.data = buf;
	smbcli_lock(cli->tree, fnum, io.writeunlock.in.offset, io.writeunlock.in.count,
		 0, WRITE_LOCK);
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.writeunlock.out.nwritten, 4000);

	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);

	memset(buf, 0, maxsize);
	if (smbcli_read(cli->tree, fnum, buf, 0, 4000) != 4000) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__));
	}
	CHECK_BUFFER(buf, seed, 4000);

	torture_comment(tctx, "Trying bad fnum\n");
	io.writeunlock.in.file.fnum = fnum+1;
	io.writeunlock.in.count = 4000;
	io.writeunlock.in.offset = 0;
	io.writeunlock.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);

	torture_comment(tctx, "Setting file as sparse\n");
	status = torture_set_sparse(cli->tree, fnum);
	CHECK_STATUS(status, NT_STATUS_OK);

	if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
		torture_skip(tctx, "skipping large file tests - CAP_LARGE_FILES not set\n");
	}

	torture_comment(tctx, "Trying 2^32 offset\n");
	setup_buffer(buf, seed, maxsize);
	io.writeunlock.in.file.fnum = fnum;
	io.writeunlock.in.count = 4000;
	io.writeunlock.in.offset = 0xFFFFFFFF - 2000;
	io.writeunlock.in.data = buf;
	smbcli_lock(cli->tree, fnum, io.writeunlock.in.offset, io.writeunlock.in.count,
		 0, WRITE_LOCK);
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.writeunlock.out.nwritten, 4000);
	CHECK_ALL_INFO(io.writeunlock.in.count + (uint64_t)io.writeunlock.in.offset, size);

	memset(buf, 0, maxsize);
	if (smbcli_read(cli->tree, fnum, buf, io.writeunlock.in.offset, 4000) != 4000) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__));
	}
	CHECK_BUFFER(buf, seed, 4000);

done:
	smbcli_close(cli->tree, fnum);
	smb_raw_exit(cli->session);
	smbcli_deltree(cli->tree, BASEDIR);
	return ret;
}


static bool test_write(struct torture_context *tctx,
		       struct smbcli_state *cli)
{
	union smb_write io;
	NTSTATUS status;
	bool ret = true;
	int fnum;
	uint8_t *buf;
	const int maxsize = 90000;
	const char *fname = BASEDIR "\\test.txt";
	unsigned int seed = time(NULL);
	union smb_fileinfo finfo;

	buf = talloc_zero_array(tctx, uint8_t, maxsize);

	if (!torture_setup_dir(cli, BASEDIR)) {
		torture_fail(tctx, "failed to setup basedir");
	}

	torture_comment(tctx, "Testing RAW_WRITE_WRITE\n");
	io.generic.level = RAW_WRITE_WRITE;

	fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
	if (fnum == -1) {
		ret = false;
		torture_fail_goto(tctx, done,
			talloc_asprintf(tctx, "Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)));
	}

	torture_comment(tctx, "Trying zero write\n");
	io.write.in.file.fnum = fnum;
	io.write.in.count = 0;
	io.write.in.offset = 0;
	io.write.in.remaining = 0;
	io.write.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.write.out.nwritten, 0);

	setup_buffer(buf, seed, maxsize);

	torture_comment(tctx, "Trying small write\n");
	io.write.in.count = 9;
	io.write.in.offset = 4;
	io.write.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.write.out.nwritten, io.write.in.count);

	memset(buf, 0, maxsize);
	if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__));
	}
	CHECK_BUFFER(buf+4, seed, 9);
	CHECK_VALUE(IVAL(buf,0), 0);

	setup_buffer(buf, seed, maxsize);

	torture_comment(tctx, "Trying large write\n");
	io.write.in.count = 4000;
	io.write.in.offset = 0;
	io.write.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.write.out.nwritten, 4000);

	memset(buf, 0, maxsize);
	if (smbcli_read(cli->tree, fnum, buf, 0, 4000) != 4000) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__));
	}
	CHECK_BUFFER(buf, seed, 4000);

	torture_comment(tctx, "Trying bad fnum\n");
	io.write.in.file.fnum = fnum+1;
	io.write.in.count = 4000;
	io.write.in.offset = 0;
	io.write.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);

	torture_comment(tctx, "Setting file as sparse\n");
	status = torture_set_sparse(cli->tree, fnum);
	CHECK_STATUS(status, NT_STATUS_OK);

	if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
		torture_comment(tctx, "skipping large file tests - CAP_LARGE_FILES not set\n");
		goto done;
	}

	torture_comment(tctx, "Trying 2^32 offset\n");
	setup_buffer(buf, seed, maxsize);
	io.write.in.file.fnum = fnum;
	io.write.in.count = 4000;
	io.write.in.offset = 0xFFFFFFFF - 2000;
	io.write.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.write.out.nwritten, 4000);
	CHECK_ALL_INFO(io.write.in.count + (uint64_t)io.write.in.offset, size);

	memset(buf, 0, maxsize);
	if (smbcli_read(cli->tree, fnum, buf, io.write.in.offset, 4000) != 4000) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__));
	}
	CHECK_BUFFER(buf, seed, 4000);

done:
	smbcli_close(cli->tree, fnum);
	smb_raw_exit(cli->session);
	smbcli_deltree(cli->tree, BASEDIR);
	return ret;
}


static bool test_writeclose(struct torture_context *tctx,
			    struct smbcli_state *cli)
{
	union smb_write io;
	NTSTATUS status;
	bool ret = true;
	int fnum;
	uint8_t *buf;
	const int maxsize = 90000;
	const char *fname = BASEDIR "\\test.txt";
	unsigned int seed = time(NULL);
	union smb_fileinfo finfo;

	buf = talloc_zero_array(tctx, uint8_t, maxsize);

	if (!torture_setting_bool(tctx, "writeclose_support", true)) {
		torture_skip(tctx, "Server does not support writeclose - skipping\n");
	}

	if (!torture_setup_dir(cli, BASEDIR)) {
		torture_fail(tctx, "failed to setup basedir");
	}

	torture_comment(tctx, "Testing RAW_WRITE_WRITECLOSE\n");
	io.generic.level = RAW_WRITE_WRITECLOSE;

	fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
	if (fnum == -1) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree)));
	}

	torture_comment(tctx, "Trying zero write\n");
	io.writeclose.in.file.fnum = fnum;
	io.writeclose.in.count = 0;
	io.writeclose.in.offset = 0;
	io.writeclose.in.mtime = 0;
	io.writeclose.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.writeclose.out.nwritten, io.writeclose.in.count);

	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.writeclose.out.nwritten, io.writeclose.in.count);

	setup_buffer(buf, seed, maxsize);

	torture_comment(tctx, "Trying small write\n");
	io.writeclose.in.count = 9;
	io.writeclose.in.offset = 4;
	io.writeclose.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);

	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);

	fnum = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
	io.writeclose.in.file.fnum = fnum;

	if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__));
	}
	CHECK_BUFFER(buf+4, seed, 9);
	CHECK_VALUE(IVAL(buf,0), 0);

	setup_buffer(buf, seed, maxsize);
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.writeclose.out.nwritten, io.writeclose.in.count);

	fnum = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
	io.writeclose.in.file.fnum = fnum;

	memset(buf, 0, maxsize);
	if (smbcli_read(cli->tree, fnum, buf, 0, 13) != 13) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__));
	}
	CHECK_BUFFER(buf+4, seed, 9);
	CHECK_VALUE(IVAL(buf,0), 0);

	setup_buffer(buf, seed, maxsize);

	torture_comment(tctx, "Trying large write\n");
	io.writeclose.in.count = 4000;
	io.writeclose.in.offset = 0;
	io.writeclose.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.writeclose.out.nwritten, 4000);

	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);

	fnum = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
	io.writeclose.in.file.fnum = fnum;

	memset(buf, 0, maxsize);
	if (smbcli_read(cli->tree, fnum, buf, 0, 4000) != 4000) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__));
	}
	CHECK_BUFFER(buf, seed, 4000);

	torture_comment(tctx, "Trying bad fnum\n");
	io.writeclose.in.file.fnum = fnum+1;
	io.writeclose.in.count = 4000;
	io.writeclose.in.offset = 0;
	io.writeclose.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_INVALID_HANDLE);

	torture_comment(tctx, "Setting file as sparse\n");
	status = torture_set_sparse(cli->tree, fnum);
	CHECK_STATUS(status, NT_STATUS_OK);

	if (!(cli->transport->negotiate.capabilities & CAP_LARGE_FILES)) {
		torture_skip(tctx, "skipping large file tests - CAP_LARGE_FILES not set\n");
	}

	torture_comment(tctx, "Trying 2^32 offset\n");
	setup_buffer(buf, seed, maxsize);
	io.writeclose.in.file.fnum = fnum;
	io.writeclose.in.count = 4000;
	io.writeclose.in.offset = 0xFFFFFFFF - 2000;
	io.writeclose.in.data = buf;
	status = smb_raw_write(cli->tree, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	CHECK_VALUE(io.writeclose.out.nwritten, 4000);
	CHECK_ALL_INFO(io.writeclose.in.count + (uint64_t)io.writeclose.in.offset, size);

	fnum = smbcli_open(cli->tree, fname, O_RDWR, DENY_NONE);
	io.writeclose.in.file.fnum = fnum;

	memset(buf, 0, maxsize);
	if (smbcli_read(cli->tree, fnum, buf, io.writeclose.in.offset, 4000) != 4000) {
		ret = false;
		torture_fail_goto(tctx, done, talloc_asprintf(tctx, "read failed at %s\n", __location__));
	}
	CHECK_BUFFER(buf, seed, 4000);

done:
	smbcli_close(cli->tree, fnum);
	smb_raw_exit(cli->session);
	smbcli_deltree(cli->tree, BASEDIR);
	return ret;
}


struct torture_suite *torture_raw_write(TALLOC_CTX *mem_ctx)
{
	struct torture_suite *suite = torture_suite_create(mem_ctx, "write");

	torture_suite_add_1smb_test(suite, "write", test_write);
	torture_suite_add_1smb_test(suite, "write unlock", test_writeunlock);
	torture_suite_add_1smb_test(suite, "write close", test_writeclose);
	torture_suite_add_1smb_test(suite, "writex", test_writex);
	torture_suite_add_1smb_test(suite, "bad-write", test_bad_write);

	return suite;
}


static void setup_buffer(uint8_t *buf, unsigned int seed, int len)
{
	int i;
	srandom(seed);
	for (i=0;i<len;i++) buf[i] = random();
}


PJ_DEF(void) pj_dns_init_a_rr( pj_dns_parsed_rr *rec,
			       const pj_str_t *res_name,
			       unsigned dnsclass,
			       unsigned ttl,
			       const pj_in_addr *ip_addr)
{
    pj_bzero(rec, sizeof(*rec));
    rec->name = *res_name;
    rec->type = PJ_DNS_TYPE_A;
    rec->dnsclass = (pj_uint16_t) dnsclass;
    rec->ttl = ttl;
    rec->rdata.a.ip_addr = *ip_addr;
}

Merge pull request from GHSA-p6g5-v97c-w5q4

* Prevent heap buffer overflow when parsing DNS packets

* Make sure packet parsing doesn't advance beyond max/end

* Update checks

* Remove  check

Co-authored-by: sauwming <ming@teluu.com>


static pj_status_t get_name_len(int rec_counter, const pj_uint8_t *pkt,
				const pj_uint8_t *start, const pj_uint8_t *max,
				int *parsed_len, int *name_len)
{
    const pj_uint8_t *p;
    pj_status_t status;

    /* Limit the number of recursion */
    if (rec_counter > 10) {
	/* Too many name recursion */
	return PJLIB_UTIL_EDNSINNAMEPTR;
    }

    *name_len = *parsed_len = 0;
    p = start;
    while (*p) {
	if ((*p & 0xc0) == 0xc0) {
	    /* Compression is found! */
	    int ptr_len = 0;
	    int dummy;
	    pj_uint16_t offset;

	    /* Get the 14bit offset */
	    pj_memcpy(&offset, p, 2);
	    offset ^= pj_htons((pj_uint16_t)(0xc0 << 8));
	    offset = pj_ntohs(offset);

	    /* Check that offset is valid */
	    if (offset >= max - pkt)
		return PJLIB_UTIL_EDNSINNAMEPTR;

	    /* Get the name length from that offset. */
	    status = get_name_len(rec_counter+1, pkt, pkt + offset, max,
				  &dummy, &ptr_len);
	    if (status != PJ_SUCCESS)
		return status;

	    *parsed_len += 2;
	    *name_len += ptr_len;

	    return PJ_SUCCESS;
	} else {
	    unsigned label_len = *p;

	    /* Check that label length is valid.
	     * Each label consists of an octet length (of size 1) followed
	     * by the octet of the specified length (label_len). Then it
	     * must be followed by either another label's octet length or
	     * a zero length octet (that terminates the sequence).
	     */
	    if (p+1+label_len+1 > max)
		return PJLIB_UTIL_EDNSINNAMEPTR;

	    p += (label_len + 1);
	    *parsed_len += (label_len + 1);

	    if (*p != 0)
		++label_len;

	    *name_len += label_len;
	}
    }
    ++p;
    (*parsed_len)++;

    return PJ_SUCCESS;
}


static pj_status_t parse_query(pj_dns_parsed_query *q, pj_pool_t *pool,
			       const pj_uint8_t *pkt, const pj_uint8_t *start,
			       const pj_uint8_t *max, int *parsed_len)
{
    const pj_uint8_t *p = start;
    int name_len, name_part_len;
    pj_status_t status;

    /* Get the length of the name */
    status = get_name_len(0, pkt, start, max, &name_part_len, &name_len);
    if (status != PJ_SUCCESS)
	return status;

    /* Allocate memory for the name */
    q->name.ptr = (char*) pj_pool_alloc(pool, name_len+4);
    q->name.slen = 0;

    /* Get the name */
    status = get_name(0, pkt, start, max, &q->name);
    if (status != PJ_SUCCESS)
	return status;

    p = (start + name_part_len);

    /* Check the size can accomodate next few fields. */
    if (p + 4 > max)
    	return PJLIB_UTIL_EDNSINSIZE;

    /* Get the type */
    pj_memcpy(&q->type, p, 2);
    q->type = pj_ntohs(q->type);
    p += 2;

    /* Get the class */
    pj_memcpy(&q->dnsclass, p, 2);
    q->dnsclass = pj_ntohs(q->dnsclass);
    p += 2;

    *parsed_len = (int)(p - start);

    return PJ_SUCCESS;
}


PJ_DEF(void) pj_dns_init_aaaa_rr(pj_dns_parsed_rr *rec,
				 const pj_str_t *res_name,
				 unsigned dnsclass,
				 unsigned ttl,
				 const pj_in6_addr *ip_addr)
{
    pj_bzero(rec, sizeof(*rec));
    rec->name = *res_name;
    rec->type = PJ_DNS_TYPE_AAAA;
    rec->dnsclass = (pj_uint16_t) dnsclass;
    rec->ttl = ttl;
    rec->rdata.aaaa.ip_addr = *ip_addr;
}


static pj_status_t get_name(int rec_counter, const pj_uint8_t *pkt,
			    const pj_uint8_t *start, const pj_uint8_t *max,
			    pj_str_t *name)
{
    const pj_uint8_t *p;
    pj_status_t status;

    /* Limit the number of recursion */
    if (rec_counter > 10) {
	/* Too many name recursion */
	return PJLIB_UTIL_EDNSINNAMEPTR;
    }

    p = start;
    while (*p) {
	if ((*p & 0xc0) == 0xc0) {
	    /* Compression is found! */
	    pj_uint16_t offset;

	    /* Get the 14bit offset */
	    pj_memcpy(&offset, p, 2);
	    offset ^= pj_htons((pj_uint16_t)(0xc0 << 8));
	    offset = pj_ntohs(offset);

	    /* Check that offset is valid */
	    if (offset >= max - pkt)
		return PJLIB_UTIL_EDNSINNAMEPTR;

	    /* Retrieve the name from that offset. */
	    status = get_name(rec_counter+1, pkt, pkt + offset, max, name);
	    if (status != PJ_SUCCESS)
		return status;

	    return PJ_SUCCESS;
	} else {
	    unsigned label_len = *p;

	    /* Check that label length is valid.
	     * Each label consists of an octet length (of size 1) followed
	     * by the octet of the specified length (label_len). Then it
	     * must be followed by either another label's octet length or
	     * a zero length octet (that terminates the sequence).
	     */
	    if (p+1+label_len+1 > max)
		return PJLIB_UTIL_EDNSINNAMEPTR;

	    pj_memcpy(name->ptr + name->slen, p+1, label_len);
	    name->slen += label_len;

	    p += label_len + 1;
	    if (*p != 0) {
		*(name->ptr + name->slen) = '.';
		++name->slen;
	    }
	}
    }

    return PJ_SUCCESS;
}


static void copy_query(pj_pool_t *pool, pj_dns_parsed_query *dst,
		       const pj_dns_parsed_query *src,
		       unsigned *nametable_count,
		       pj_str_t nametable[])
{
    pj_memcpy(dst, src, sizeof(*src));
    apply_name_table(nametable_count, nametable, &src->name, pool, &dst->name);
}


PJ_DEF(pj_status_t) pj_dns_make_query( void *packet,
				       unsigned *size,
				       pj_uint16_t id,
				       int qtype,
				       const pj_str_t *name)
{
    pj_uint8_t *p = (pj_uint8_t*)packet;
    const char *startlabel, *endlabel, *endname;
    pj_size_t d;

    /* Sanity check */
    PJ_ASSERT_RETURN(packet && size && qtype && name, PJ_EINVAL);

    /* Calculate total number of bytes required. */
    d = sizeof(pj_dns_hdr) + name->slen + 4;

    /* Check that size is sufficient. */
    PJ_ASSERT_RETURN(*size >= d, PJLIB_UTIL_EDNSQRYTOOSMALL);

    /* Initialize header */
    pj_assert(sizeof(pj_dns_hdr)==12);
    pj_bzero(p, sizeof(struct pj_dns_hdr));
    write16(p+0, id);
    write16(p+2, (pj_uint16_t)PJ_DNS_SET_RD(1));
    write16(p+4, (pj_uint16_t)1);

    /* Initialize query */
    p = ((pj_uint8_t*)packet)+sizeof(pj_dns_hdr);

    /* Tokenize name */
    startlabel = endlabel = name->ptr;
    endname = name->ptr + name->slen;
    while (endlabel != endname) {
	while (endlabel != endname && *endlabel != '.')
	    ++endlabel;
	*p++ = (pj_uint8_t)(endlabel - startlabel);
	pj_memcpy(p, startlabel, endlabel-startlabel);
	p += (endlabel-startlabel);
	if (endlabel != endname && *endlabel == '.')
	    ++endlabel;
	startlabel = endlabel;
    }
    *p++ = '\0';

    /* Set type */
    write16(p, (pj_uint16_t)qtype);
    p += 2;

    /* Set class (IN=1) */
    write16(p, 1);
    p += 2;

    /* Done, calculate length */
    *size = (unsigned)(p - (pj_uint8_t*)packet);

    return 0;
}


PJ_DEF(void) pj_dns_init_srv_rr( pj_dns_parsed_rr *rec,
				 const pj_str_t *res_name,
				 unsigned dnsclass,
				 unsigned ttl,
				 unsigned prio,
				 unsigned weight,
				 unsigned port,
				 const pj_str_t *target)
{
    pj_bzero(rec, sizeof(*rec));
    rec->name = *res_name;
    rec->type = PJ_DNS_TYPE_SRV;
    rec->dnsclass = (pj_uint16_t) dnsclass;
    rec->ttl = ttl;
    rec->rdata.srv.prio = (pj_uint16_t) prio;
    rec->rdata.srv.weight = (pj_uint16_t) weight;
    rec->rdata.srv.port = (pj_uint16_t) port;
    rec->rdata.srv.target = *target;
}


static void apply_name_table( unsigned *count,
			      pj_str_t nametable[],
		    	      const pj_str_t *src,
			      pj_pool_t *pool,
			      pj_str_t *dst)
{
    unsigned i;

    /* Scan strings in nametable */
    for (i=0; i<*count; ++i) {
	if (pj_stricmp(&nametable[i], src) == 0)
	    break;
    }

    /* If name is found in nametable, use the pointer in the nametable */
    if (i != *count) {
	dst->ptr = nametable[i].ptr;
	dst->slen = nametable[i].slen;
	return;
    }

    /* Otherwise duplicate the string, and insert new name in nametable */
    pj_strdup(pool, dst, src);

    if (*count < PJ_DNS_MAX_NAMES_IN_NAMETABLE) {
	nametable[*count].ptr = dst->ptr;
	nametable[*count].slen = dst->slen;

	++(*count);
    }
}


static void write16(pj_uint8_t *p, pj_uint16_t val)
{
    p[0] = (pj_uint8_t)(val >> 8);
    p[1] = (pj_uint8_t)(val & 0xFF);
}


PJ_DEF(void) pj_dns_init_cname_rr( pj_dns_parsed_rr *rec,
				   const pj_str_t *res_name,
				   unsigned dnsclass,
				   unsigned ttl,
				   const pj_str_t *name)
{
    pj_bzero(rec, sizeof(*rec));
    rec->name = *res_name;
    rec->type = PJ_DNS_TYPE_CNAME;
    rec->dnsclass = (pj_uint16_t) dnsclass;
    rec->ttl = ttl;
    rec->rdata.cname.name = *name;
}


PJ_DEF(const char *) pj_dns_get_type_name(int type)
{
    switch (type) {
    case PJ_DNS_TYPE_A:	    return "A";
    case PJ_DNS_TYPE_AAAA:  return "AAAA";
    case PJ_DNS_TYPE_SRV:   return "SRV";
    case PJ_DNS_TYPE_NS:    return "NS";
    case PJ_DNS_TYPE_CNAME: return "CNAME";
    case PJ_DNS_TYPE_PTR:   return "PTR";
    case PJ_DNS_TYPE_MX:    return "MX";
    case PJ_DNS_TYPE_TXT:   return "TXT";
    case PJ_DNS_TYPE_NAPTR: return "NAPTR";
    }
    return "(Unknown)";
}


static pj_status_t parse_rr(pj_dns_parsed_rr *rr, pj_pool_t *pool,
			    const pj_uint8_t *pkt,
			    const pj_uint8_t *start, const pj_uint8_t *max,
			    int *parsed_len)
{
    const pj_uint8_t *p = start;
    int name_len, name_part_len;
    pj_status_t status;

    /* Get the length of the name */
    status = get_name_len(0, pkt, start, max, &name_part_len, &name_len);
    if (status != PJ_SUCCESS)
	return status;

    /* Allocate memory for the name */
    rr->name.ptr = (char*) pj_pool_alloc(pool, name_len+4);
    rr->name.slen = 0;

    /* Get the name */
    status = get_name(0, pkt, start, max, &rr->name);
    if (status != PJ_SUCCESS)
	return status;

    p = (start + name_part_len);

    /* Check the size can accomodate next few fields. */
    if (p+10 > max)
	return PJLIB_UTIL_EDNSINSIZE;

    /* Get the type */
    pj_memcpy(&rr->type, p, 2);
    rr->type = pj_ntohs(rr->type);
    p += 2;

    /* Get the class */
    pj_memcpy(&rr->dnsclass, p, 2);
    rr->dnsclass = pj_ntohs(rr->dnsclass);
    p += 2;

    /* Class MUST be IN */
    if (rr->dnsclass != 1) {
	/* Class is not IN, return error only if type is known (see #1889) */
	if (rr->type == PJ_DNS_TYPE_A     || rr->type == PJ_DNS_TYPE_AAAA  ||
	    rr->type == PJ_DNS_TYPE_CNAME || rr->type == PJ_DNS_TYPE_NS    ||
	    rr->type == PJ_DNS_TYPE_PTR   || rr->type == PJ_DNS_TYPE_SRV)
	{
	    return PJLIB_UTIL_EDNSINCLASS;
	}
    }

    /* Get TTL */
    pj_memcpy(&rr->ttl, p, 4);
    rr->ttl = pj_ntohl(rr->ttl);
    p += 4;

    /* Get rdlength */
    pj_memcpy(&rr->rdlength, p, 2);
    rr->rdlength = pj_ntohs(rr->rdlength);
    p += 2;

    /* Check that length is valid */
    if (p + rr->rdlength > max)
	return PJLIB_UTIL_EDNSINSIZE;

    /* Parse some well known records */
    if (rr->type == PJ_DNS_TYPE_A) {
	pj_memcpy(&rr->rdata.a.ip_addr, p, 4);
	p += 4;

    } else if (rr->type == PJ_DNS_TYPE_AAAA) {
	pj_memcpy(&rr->rdata.aaaa.ip_addr, p, 16);
	p += 16;

    } else if (rr->type == PJ_DNS_TYPE_CNAME ||
	       rr->type == PJ_DNS_TYPE_NS ||
	       rr->type == PJ_DNS_TYPE_PTR)
    {

	/* Get the length of the target name */
	status = get_name_len(0, pkt, p, max, &name_part_len, &name_len);
	if (status != PJ_SUCCESS)
	    return status;

	/* Allocate memory for the name */
	rr->rdata.cname.name.ptr = (char*) pj_pool_alloc(pool, name_len);
	rr->rdata.cname.name.slen = 0;

	/* Get the name */
	status = get_name(0, pkt, p, max, &rr->rdata.cname.name);
	if (status != PJ_SUCCESS)
	    return status;

	p += name_part_len;

    } else if (rr->type == PJ_DNS_TYPE_SRV) {

	/* Priority */
	pj_memcpy(&rr->rdata.srv.prio, p, 2);
	rr->rdata.srv.prio = pj_ntohs(rr->rdata.srv.prio);
	p += 2;

	/* Weight */
	pj_memcpy(&rr->rdata.srv.weight, p, 2);
	rr->rdata.srv.weight = pj_ntohs(rr->rdata.srv.weight);
	p += 2;

	/* Port */
	pj_memcpy(&rr->rdata.srv.port, p, 2);
	rr->rdata.srv.port = pj_ntohs(rr->rdata.srv.port);
	p += 2;

	/* Get the length of the target name */
	status = get_name_len(0, pkt, p, max, &name_part_len, &name_len);
	if (status != PJ_SUCCESS)
	    return status;

	/* Allocate memory for the name */
	rr->rdata.srv.target.ptr = (char*) pj_pool_alloc(pool, name_len);
	rr->rdata.srv.target.slen = 0;

	/* Get the name */
	status = get_name(0, pkt, p, max, &rr->rdata.srv.target);
	if (status != PJ_SUCCESS)
	    return status;
	p += name_part_len;

    } else {
	/* Copy the raw data */
	rr->data = pj_pool_alloc(pool, rr->rdlength);
	pj_memcpy(rr->data, p, rr->rdlength);

	p += rr->rdlength;
    }

    *parsed_len = (int)(p - start);
    return PJ_SUCCESS;
}


PJ_DEF(pj_status_t) pj_dns_parse_packet( pj_pool_t *pool,
				  	 const void *packet,
					 unsigned size,
					 pj_dns_parsed_packet **p_res)
{
    pj_dns_parsed_packet *res;
    const pj_uint8_t *start, *end;
    pj_status_t status;
    unsigned i;

    /* Sanity checks */
    PJ_ASSERT_RETURN(pool && packet && size && p_res, PJ_EINVAL);

    /* Packet size must be at least as big as the header */
    if (size < sizeof(pj_dns_hdr))
	return PJLIB_UTIL_EDNSINSIZE;

    /* Create the structure */
    res = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet);

    /* Copy the DNS header, and convert endianness to host byte order */
    pj_memcpy(&res->hdr, packet, sizeof(pj_dns_hdr));
    res->hdr.id	      = pj_ntohs(res->hdr.id);
    res->hdr.flags    = pj_ntohs(res->hdr.flags);
    res->hdr.qdcount  = pj_ntohs(res->hdr.qdcount);
    res->hdr.anscount = pj_ntohs(res->hdr.anscount);
    res->hdr.nscount  = pj_ntohs(res->hdr.nscount);
    res->hdr.arcount  = pj_ntohs(res->hdr.arcount);

    /* Mark start and end of payload */
    start = ((const pj_uint8_t*)packet) + sizeof(pj_dns_hdr);
    end = ((const pj_uint8_t*)packet) + size;

    /* Parse query records (if any).
     */
    if (res->hdr.qdcount) {
	res->q = (pj_dns_parsed_query*)
		 pj_pool_zalloc(pool, res->hdr.qdcount *
				      sizeof(pj_dns_parsed_query));
	for (i=0; i<res->hdr.qdcount; ++i) {
	    int parsed_len = 0;

	    status = parse_query(&res->q[i], pool, (const pj_uint8_t*)packet,
	    			 start, end, &parsed_len);
	    if (status != PJ_SUCCESS)
		return status;

	    start += parsed_len;
	}
    }

    /* Parse answer, if any */
    if (res->hdr.anscount) {
	res->ans = (pj_dns_parsed_rr*)
		   pj_pool_zalloc(pool, res->hdr.anscount *
					sizeof(pj_dns_parsed_rr));

	for (i=0; i<res->hdr.anscount; ++i) {
	    int parsed_len;

	    status = parse_rr(&res->ans[i], pool, (const pj_uint8_t*)packet,
	    		      start, end, &parsed_len);
	    if (status != PJ_SUCCESS)
		return status;

	    start += parsed_len;
	}
    }

    /* Parse authoritative NS records, if any */
    if (res->hdr.nscount) {
	res->ns = (pj_dns_parsed_rr*)
		  pj_pool_zalloc(pool, res->hdr.nscount *
				       sizeof(pj_dns_parsed_rr));

	for (i=0; i<res->hdr.nscount; ++i) {
	    int parsed_len;

	    status = parse_rr(&res->ns[i], pool, (const pj_uint8_t*)packet,
	    		      start, end, &parsed_len);
	    if (status != PJ_SUCCESS)
		return status;

	    start += parsed_len;
	}
    }

    /* Parse additional RR answer, if any */
    if (res->hdr.arcount) {
	res->arr = (pj_dns_parsed_rr*)
		   pj_pool_zalloc(pool, res->hdr.arcount *
					sizeof(pj_dns_parsed_rr));

	for (i=0; i<res->hdr.arcount; ++i) {
	    int parsed_len;

	    status = parse_rr(&res->arr[i], pool, (const pj_uint8_t*)packet,
	    		      start, end, &parsed_len);
	    if (status != PJ_SUCCESS)
		return status;

	    start += parsed_len;
	}
    }

    /* Looks like everything is okay */
    *p_res = res;

    return PJ_SUCCESS;
}


PJ_DEF(void) pj_dns_packet_dup(pj_pool_t *pool,
			       const pj_dns_parsed_packet*p,
			       unsigned options,
			       pj_dns_parsed_packet **p_dst)
{
    pj_dns_parsed_packet *dst;
    unsigned nametable_count = 0;
#if PJ_DNS_MAX_NAMES_IN_NAMETABLE
    pj_str_t nametable[PJ_DNS_MAX_NAMES_IN_NAMETABLE];
#else
    pj_str_t *nametable = NULL;
#endif
    unsigned i;

    PJ_ASSERT_ON_FAIL(pool && p && p_dst, return);

    /* Create packet and copy header */
    *p_dst = dst = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet);
    pj_memcpy(&dst->hdr, &p->hdr, sizeof(p->hdr));

    /* Initialize section counts in the target packet to zero.
     * If memory allocation fails during copying process, the target packet
     * should have a correct section counts.
     */
    dst->hdr.qdcount = 0;
    dst->hdr.anscount = 0;
    dst->hdr.nscount = 0;
    dst->hdr.arcount = 0;

    /* Copy query section */
    if (p->hdr.qdcount && (options & PJ_DNS_NO_QD)==0) {
	dst->q = (pj_dns_parsed_query*)
		 pj_pool_alloc(pool, p->hdr.qdcount *
				     sizeof(pj_dns_parsed_query));
	for (i=0; i<p->hdr.qdcount; ++i) {
	    copy_query(pool, &dst->q[i], &p->q[i],
		       &nametable_count, nametable);
	    ++dst->hdr.qdcount;
	}
    }

    /* Copy answer section */
    if (p->hdr.anscount && (options & PJ_DNS_NO_ANS)==0) {
	dst->ans = (pj_dns_parsed_rr*)
		   pj_pool_alloc(pool, p->hdr.anscount *
				       sizeof(pj_dns_parsed_rr));
	for (i=0; i<p->hdr.anscount; ++i) {
	    copy_rr(pool, &dst->ans[i], &p->ans[i],
		    &nametable_count, nametable);
	    ++dst->hdr.anscount;
	}
    }

    /* Copy NS section */
    if (p->hdr.nscount && (options & PJ_DNS_NO_NS)==0) {
	dst->ns = (pj_dns_parsed_rr*)
		  pj_pool_alloc(pool, p->hdr.nscount *
				      sizeof(pj_dns_parsed_rr));
	for (i=0; i<p->hdr.nscount; ++i) {
	    copy_rr(pool, &dst->ns[i], &p->ns[i],
		    &nametable_count, nametable);
	    ++dst->hdr.nscount;
	}
    }

    /* Copy additional info section */
    if (p->hdr.arcount && (options & PJ_DNS_NO_AR)==0) {
	dst->arr = (pj_dns_parsed_rr*)
		   pj_pool_alloc(pool, p->hdr.arcount *
				       sizeof(pj_dns_parsed_rr));
	for (i=0; i<p->hdr.arcount; ++i) {
	    copy_rr(pool, &dst->arr[i], &p->arr[i],
		    &nametable_count, nametable);
	    ++dst->hdr.arcount;
	}
    }
}


static void copy_rr(pj_pool_t *pool, pj_dns_parsed_rr *dst,
		    const pj_dns_parsed_rr *src,
		    unsigned *nametable_count,
		    pj_str_t nametable[])
{
    pj_memcpy(dst, src, sizeof(*src));
    apply_name_table(nametable_count, nametable, &src->name, pool, &dst->name);

    if (src->data) {
	dst->data = pj_pool_alloc(pool, src->rdlength);
	pj_memcpy(dst->data, src->data, src->rdlength);
    }

    if (src->type == PJ_DNS_TYPE_SRV) {
	apply_name_table(nametable_count, nametable, &src->rdata.srv.target,
			 pool, &dst->rdata.srv.target);
    } else if (src->type == PJ_DNS_TYPE_A) {
	dst->rdata.a.ip_addr.s_addr =  src->rdata.a.ip_addr.s_addr;
    } else if (src->type == PJ_DNS_TYPE_AAAA) {
	pj_memcpy(&dst->rdata.aaaa.ip_addr, &src->rdata.aaaa.ip_addr,
		  sizeof(pj_in6_addr));
    } else if (src->type == PJ_DNS_TYPE_CNAME) {
	pj_strdup(pool, &dst->rdata.cname.name, &src->rdata.cname.name);
    } else if (src->type == PJ_DNS_TYPE_NS) {
	pj_strdup(pool, &dst->rdata.ns.name, &src->rdata.ns.name);
    } else if (src->type == PJ_DNS_TYPE_PTR) {
	pj_strdup(pool, &dst->rdata.ptr.name, &src->rdata.ptr.name);
    }
}


Status ValidateShapes(OpKernelContext* ctx, const Tensor& hypothesis_indices,
                      const Tensor& hypothesis_values,
                      const Tensor& hypothesis_shape,
                      const Tensor& truth_indices, const Tensor& truth_values,
                      const Tensor& truth_shape) {
  if (!TensorShapeUtils::IsMatrix(hypothesis_indices.shape()))
    return errors::InvalidArgument(
        "hypothesis_indices should be a matrix, but got shape: ",
        hypothesis_indices.shape().DebugString());
  if (!TensorShapeUtils::IsMatrix(truth_indices.shape()))
    return errors::InvalidArgument(
        "truth_indices should be a matrix, but got shape: ",
        truth_indices.shape().DebugString());
  if (!TensorShapeUtils::IsVector(hypothesis_values.shape()))
    return errors::InvalidArgument(
        "hypothesis_values should be a vector, but got shape: ",
        hypothesis_values.shape().DebugString());
  if (!TensorShapeUtils::IsVector(truth_values.shape()))
    return errors::InvalidArgument(
        "truth_values should be a vector, but got shape: ",
        truth_values.shape().DebugString());
  if (!TensorShapeUtils::IsVector(hypothesis_shape.shape()))
    return errors::InvalidArgument(
        "hypothesis_shape should be a vector, but got shape: ",
        hypothesis_shape.shape().DebugString());
  if (!TensorShapeUtils::IsVector(truth_shape.shape()))
    return errors::InvalidArgument(
        "truth_shape should be a vector, but got shape: ",
        truth_shape.shape().DebugString());
  if (hypothesis_values.NumElements() != hypothesis_indices.dim_size(0))
    return errors::InvalidArgument(
        "Expected hypothesis_values.NumElements == "
        "#rows(hypothesis_indices), their shapes are: ",
        hypothesis_values.shape().DebugString(), " and ",
        hypothesis_indices.shape().DebugString());
  if (hypothesis_shape.NumElements() != hypothesis_indices.dim_size(1))
    return errors::InvalidArgument(
        "Expected hypothesis_shape.NumElements == "
        "#cols(hypothesis_indices), their shapes are: ",
        hypothesis_shape.shape().DebugString(), " and ",
        hypothesis_indices.shape().DebugString());
  if (truth_shape.NumElements() < 2)
    return errors::InvalidArgument(
        "Input SparseTensors must have rank at least 2, but truth_shape "
        "rank is: ",
        truth_shape.NumElements());
  if (truth_values.NumElements() != truth_indices.dim_size(0))
    return errors::InvalidArgument(
        "Expected truth_values.NumElements == "
        "#rows(truth_indices), their shapes are: ",
        truth_values.shape().DebugString(), " and ",
        truth_indices.shape().DebugString());
  if (truth_shape.NumElements() != truth_indices.dim_size(1))
    return errors::InvalidArgument(
        "Expected truth_shape.NumElements == "
        "#cols(truth_indices), their shapes are: ",
        truth_shape.shape().DebugString(), " and ",
        truth_indices.shape().DebugString());
  if (truth_shape.NumElements() != hypothesis_shape.NumElements())
    return errors::InvalidArgument(
        "Expected truth and hypothesis to have matching ranks, but "
        "their shapes are: ",
        truth_shape.shape().DebugString(), " and ",
        hypothesis_shape.shape().DebugString());

  return Status::OK();
}

Fix tf.raw_ops.EditDistance vulnerability with negative indices.

Check that indices are non-negative. Fix several identical code sites.
Clean up grammar in error message.

PiperOrigin-RevId: 445442017


  explicit EditDistanceOp(OpKernelConstruction* ctx) : OpKernel(ctx) {
    OP_REQUIRES_OK(ctx, ctx->GetAttr("normalize", &normalize_));
  }


  void Compute(OpKernelContext* ctx) override {
    const Tensor* hypothesis_indices;
    const Tensor* hypothesis_values;
    const Tensor* hypothesis_shape;
    const Tensor* truth_indices;
    const Tensor* truth_values;
    const Tensor* truth_shape;
    OP_REQUIRES_OK(ctx, ctx->input("hypothesis_indices", &hypothesis_indices));
    OP_REQUIRES_OK(ctx, ctx->input("hypothesis_values", &hypothesis_values));
    OP_REQUIRES_OK(ctx, ctx->input("hypothesis_shape", &hypothesis_shape));
    OP_REQUIRES_OK(ctx, ctx->input("truth_indices", &truth_indices));
    OP_REQUIRES_OK(ctx, ctx->input("truth_values", &truth_values));
    OP_REQUIRES_OK(ctx, ctx->input("truth_shape", &truth_shape));

    OP_REQUIRES_OK(
        ctx, ValidateShapes(ctx, *hypothesis_indices, *hypothesis_values,
                            *hypothesis_shape, *truth_indices, *truth_values,
                            *truth_shape));

    TensorShape hypothesis_st_shape;
    OP_REQUIRES_OK(ctx,
                   TensorShapeUtils::MakeShape(
                       hypothesis_shape->vec<int64_t>().data(),
                       hypothesis_shape->NumElements(), &hypothesis_st_shape));
    TensorShape truth_st_shape;
    OP_REQUIRES_OK(ctx, TensorShapeUtils::MakeShape(
                            truth_shape->vec<int64_t>().data(),
                            truth_shape->NumElements(), &truth_st_shape));

    // Assume indices are sorted in row-major order.
    std::vector<int64_t> sorted_order(truth_st_shape.dims());
    std::iota(sorted_order.begin(), sorted_order.end(), 0);

    sparse::SparseTensor hypothesis;
    OP_REQUIRES_OK(ctx, sparse::SparseTensor::Create(
                            *hypothesis_indices, *hypothesis_values,
                            hypothesis_st_shape, sorted_order, &hypothesis));

    sparse::SparseTensor truth;
    OP_REQUIRES_OK(ctx, sparse::SparseTensor::Create(
                            *truth_indices, *truth_values, truth_st_shape,
                            sorted_order, &truth));

    // Group dims 0, 1, ..., RANK - 1.  The very last dim is assumed
    // to store the variable length sequences.
    std::vector<int64_t> group_dims(truth_st_shape.dims() - 1);
    std::iota(group_dims.begin(), group_dims.end(), 0);

    TensorShape output_shape;
    for (int d = 0; d < static_cast<int>(group_dims.size()); ++d) {
      output_shape.AddDim(std::max(hypothesis_st_shape.dim_size(d),
                                   truth_st_shape.dim_size(d)));
    }
    const auto output_elements = output_shape.num_elements();
    OP_REQUIRES(
        ctx, output_elements > 0,
        errors::InvalidArgument("Got output shape ", output_shape.DebugString(),
                                " which has 0 elements"));

    Tensor* output = nullptr;
    OP_REQUIRES_OK(ctx, ctx->allocate_output("output", output_shape, &output));
    auto output_t = output->flat<float>();
    output_t.setZero();

    std::vector<int64_t> output_strides(output_shape.dims());
    output_strides[output_shape.dims() - 1] = 1;
    for (int d = output_shape.dims() - 2; d >= 0; --d) {
      output_strides[d] = output_strides[d + 1] * output_shape.dim_size(d + 1);
    }

    auto hypothesis_grouper = hypothesis.group(group_dims);
    auto truth_grouper = truth.group(group_dims);

    auto hypothesis_iter = hypothesis_grouper.begin();
    auto truth_iter = truth_grouper.begin();

    auto cmp = std::equal_to<T>();

    while (hypothesis_iter != hypothesis_grouper.end() &&
           truth_iter != truth_grouper.end()) {
      sparse::Group truth_i = *truth_iter;
      sparse::Group hypothesis_j = *hypothesis_iter;
      std::vector<int64_t> g_truth = truth_i.group();
      std::vector<int64_t> g_hypothesis = hypothesis_j.group();
      auto truth_seq = truth_i.values<T>();
      auto hypothesis_seq = hypothesis_j.values<T>();

      if (g_truth == g_hypothesis) {
        auto loc = std::inner_product(g_truth.begin(), g_truth.end(),
                                      output_strides.begin(), int64_t{0});
        OP_REQUIRES(
            ctx, 0 <= loc && loc < output_elements,
            errors::Internal("Got an inner product ", loc,
                             " which would require writing to outside of "
                             "the buffer for the output tensor (max elements ",
                             output_elements, ")"));
        output_t(loc) =
            gtl::LevenshteinDistance<T>(truth_seq, hypothesis_seq, cmp);
        if (normalize_) output_t(loc) /= truth_seq.size();

        ++hypothesis_iter;
        ++truth_iter;
      } else if (g_truth > g_hypothesis) {  // zero-length truth
        auto loc = std::inner_product(g_hypothesis.begin(), g_hypothesis.end(),
                                      output_strides.begin(), int64_t{0});
        OP_REQUIRES(
            ctx, 0 <= loc && loc < output_elements,
            errors::Internal("Got an inner product ", loc,
                             " which would require writing to outside of "
                             "the buffer for the output tensor (max elements ",
                             output_elements, ")"));
        output_t(loc) = hypothesis_seq.size();
        if (normalize_ && output_t(loc) != 0.0f) {
          output_t(loc) = std::numeric_limits<float>::infinity();
        }
        ++hypothesis_iter;
      } else {  // zero-length hypothesis
        auto loc = std::inner_product(g_truth.begin(), g_truth.end(),
                                      output_strides.begin(), int64_t{0});
        OP_REQUIRES(
            ctx, 0 <= loc && loc < output_elements,
            errors::Internal("Got an inner product ", loc,
                             " which would require writing to outside of "
                             "the buffer for the output tensor (max elements ",
                             output_elements, ")"));
        output_t(loc) = (normalize_) ? 1.0 : truth_seq.size();
        ++truth_iter;
      }
    }
    while (hypothesis_iter != hypothesis_grouper.end()) {  // zero-length truths
      sparse::Group hypothesis_j = *hypothesis_iter;
      std::vector<int64_t> g_hypothesis = hypothesis_j.group();
      auto hypothesis_seq = hypothesis_j.values<T>();
      auto loc = std::inner_product(g_hypothesis.begin(), g_hypothesis.end(),
                                    output_strides.begin(), int64_t{0});
      OP_REQUIRES(
          ctx, 0 <= loc && loc < output_elements,
          errors::Internal("Got an inner product ", loc,
                           " which would require writing to outside of the "
                           "buffer for the output tensor (max elements ",
                           output_elements, ")"));
      output_t(loc) = hypothesis_seq.size();
      if (normalize_ && output_t(loc) != 0.0f) {
        output_t(loc) = std::numeric_limits<float>::infinity();
      }
      ++hypothesis_iter;
    }
    while (truth_iter != truth_grouper.end()) {  // missing hypotheses
      sparse::Group truth_i = *truth_iter;
      std::vector<int64_t> g_truth = truth_i.group();
      auto truth_seq = truth_i.values<T>();
      auto loc = std::inner_product(g_truth.begin(), g_truth.end(),
                                    output_strides.begin(), int64_t{0});
      OP_REQUIRES(
          ctx, 0 <= loc && loc < output_elements,
          errors::Internal("Got an inner product ", loc,
                           " which would require writing to outside of the "
                           "buffer for the output tensor (max elements ",
                           output_elements, ")"));
      output_t(loc) = (normalize_) ? 1.0 : truth_seq.size();
      ++truth_iter;
    }
  }


GF_Err ftab_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	u32 i;
	GF_FontTableBox *ptr = (GF_FontTableBox *)s;
	e = gf_isom_box_write_header(s, bs);
	if (e) return e;
	gf_bs_write_u16(bs, ptr->entry_count);
	for (i=0; i<ptr->entry_count; i++) {
		gf_bs_write_u16(bs, ptr->fonts[i].fontID);
		if (ptr->fonts[i].fontName) {
			u32 len = (u32) strlen(ptr->fonts[i].fontName);
			gf_bs_write_u8(bs, len);
			gf_bs_write_data(bs, ptr->fonts[i].fontName, len);
		} else {
			gf_bs_write_u8(bs, 0);
		}
	}
	return GF_OK;
}

fixed #2175


GF_Err dlay_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	GF_TextScrollDelayBox*ptr = (GF_TextScrollDelayBox*)s;
	e = gf_isom_box_write_header(s, bs);
	if (e) return e;
	gf_bs_write_u32(bs, ptr->scroll_delay);
	return GF_OK;
}

fixed #2175


void tx3g_box_del(GF_Box *s)
{
	gf_isom_sample_entry_predestroy((GF_SampleEntryBox *)s);
	gf_free(s);
}

fixed #2175


GF_Box *tsel_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_TrackSelectionBox, GF_ISOM_BOX_TYPE_TSEL);
	return (GF_Box *)tmp;
}

fixed #2175


GF_Err dimC_box_read(GF_Box *s, GF_BitStream *bs)
{
	char str[1024];
	u32 i;
	GF_DIMSSceneConfigBox *p = (GF_DIMSSceneConfigBox *)s;

	ISOM_DECREASE_SIZE(p, 3);
	p->profile = gf_bs_read_u8(bs);
	p->level = gf_bs_read_u8(bs);
	p->pathComponents = gf_bs_read_int(bs, 4);
	p->fullRequestHost = gf_bs_read_int(bs, 1);
	p->streamType = gf_bs_read_int(bs, 1);
	p->containsRedundant = gf_bs_read_int(bs, 2);

	i=0;
	str[0]=0;
	while (i < GF_ARRAY_LENGTH(str)) {
		str[i] = gf_bs_read_u8(bs);
		if (!str[i]) break;
		i++;
	}
	ISOM_DECREASE_SIZE(p, i);

	p->textEncoding = gf_strdup(str);

	i=0;
	str[0]=0;
	while (i < GF_ARRAY_LENGTH(str)) {
		str[i] = gf_bs_read_u8(bs);
		if (!str[i]) break;
		i++;
	}
	ISOM_DECREASE_SIZE(p, i);

	p->contentEncoding = gf_strdup(str);
	return GF_OK;
}

fixed #2175


GF_Err dims_on_child_box(GF_Box *s, GF_Box *a, Bool is_rem)
{
	GF_DIMSSampleEntryBox *ptr = (GF_DIMSSampleEntryBox  *)s;
	switch (a->type) {
	case GF_ISOM_BOX_TYPE_DIMC:
		BOX_FIELD_ASSIGN(config, GF_DIMSSceneConfigBox)
		break;
	case GF_ISOM_BOX_TYPE_DIST:
		BOX_FIELD_ASSIGN(scripts, GF_DIMSScriptTypesBox)
		break;
	}
	return GF_OK;
}

fixed #2175


GF_Err twrp_box_read(GF_Box *s, GF_BitStream *bs)
{
	GF_TextWrapBox*ptr = (GF_TextWrapBox*)s;
	ISOM_DECREASE_SIZE(ptr, 1)
	ptr->wrap_flag = gf_bs_read_u8(bs);
	return GF_OK;
}

fixed #2175


GF_Box *tx3g_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_Tx3gSampleEntryBox, GF_ISOM_BOX_TYPE_TX3G);
	gf_isom_sample_entry_init((GF_SampleEntryBox *)tmp);
	return (GF_Box *) tmp;
}

fixed #2175


GF_Err diST_box_read(GF_Box *s, GF_BitStream *bs)
{
	GF_DIMSScriptTypesBox *p = (GF_DIMSScriptTypesBox *)s;

	p->content_script_types = gf_malloc(sizeof(char) * (s->size+1));
	if (!p->content_script_types) return GF_OUT_OF_MEM;
	gf_bs_read_data(bs, p->content_script_types, s->size);
	p->content_script_types[s->size] = 0;
	return GF_OK;
}

fixed #2175


GF_Err text_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	u16 pSize;
	GF_TextSampleEntryBox *ptr = (GF_TextSampleEntryBox*)s;

	e = gf_isom_box_write_header(s, bs);
	if (e) return e;
	gf_bs_write_data(bs, ptr->reserved, 6);
	gf_bs_write_u16(bs, ptr->dataReferenceIndex);

	gf_bs_write_u32(bs, ptr->displayFlags);			/*Display flags*/
	gf_bs_write_u32(bs, ptr->textJustification);	/*Text justification*/
	gf_bs_write_data(bs, ptr->background_color, 6);	/*Background color*/
	gpp_write_box(bs, &ptr->default_box);			/*Default text box*/
	gf_bs_write_data(bs, ptr->reserved1, 8);		/*Reserved*/
	gf_bs_write_u16(bs, ptr->fontNumber);			/*Font number*/
	gf_bs_write_u16(bs, ptr->fontFace);				/*Font face*/
	gf_bs_write_u8(bs, ptr->reserved2);				/*Reserved*/
	gf_bs_write_u16(bs, ptr->reserved3);			/*Reserved*/
	gf_bs_write_data(bs, ptr->foreground_color, 6);	/*Foreground color*/
	//pSize assignment below is not a mistake
	if (ptr->textName && (pSize = (u16) strlen(ptr->textName))) {
		gf_bs_write_u8(bs, pSize);					/*a Pascal string begins with its size*/
		gf_bs_write_data(bs, ptr->textName, pSize);	/*Font name*/
	} else {
		gf_bs_write_u8(bs, 0);
	}
	return GF_OK;
}

fixed #2175


GF_Box *dlay_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_TextScrollDelayBox, GF_ISOM_BOX_TYPE_DLAY);
	return (GF_Box *) tmp;
}

fixed #2175


GF_Err styl_box_read(GF_Box *s, GF_BitStream *bs)
{
	u32 i;
	GF_TextStyleBox*ptr = (GF_TextStyleBox*)s;
	ISOM_DECREASE_SIZE(ptr, 2);
	ptr->entry_count = gf_bs_read_u16(bs);

	if (ptr->size / GPP_STYLE_SIZE < ptr->entry_count)
		return GF_ISOM_INVALID_FILE;

	if (ptr->entry_count) {
		ptr->styles = (GF_StyleRecord*)gf_malloc(sizeof(GF_StyleRecord)*ptr->entry_count);
		if (!ptr->styles) return GF_OUT_OF_MEM;
		for (i=0; i<ptr->entry_count; i++) {
			ISOM_DECREASE_SIZE(ptr, GPP_STYLE_SIZE);
			gpp_read_style(bs, &ptr->styles[i]);
		}
	}
	return GF_OK;
}

fixed #2175


GF_Box *tbox_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_TextBoxBox, GF_ISOM_BOX_TYPE_TBOX);
	return (GF_Box *) tmp;
}

fixed #2175


GF_Box *ftab_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_FontTableBox, GF_ISOM_BOX_TYPE_FTAB);
	return (GF_Box *) tmp;
}

fixed #2175


void tbox_box_del(GF_Box *s)
{
	gf_free(s);
}

fixed #2175


GF_Box *href_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_TextHyperTextBox, GF_ISOM_BOX_TYPE_HREF);
	return (GF_Box *) tmp;
}

fixed #2175


GF_Err gppc_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	GF_3GPPConfigBox *ptr = (GF_3GPPConfigBox *)s;
	e = gf_isom_box_write_header(s, bs);
	if (e) return e;

	gf_bs_write_u32(bs, ptr->cfg.vendor);
	gf_bs_write_u8(bs, ptr->cfg.decoder_version);
	switch (ptr->cfg.type) {
	case GF_ISOM_SUBTYPE_3GP_H263:
		gf_bs_write_u8(bs, ptr->cfg.H263_level);
		gf_bs_write_u8(bs, ptr->cfg.H263_profile);
		break;
	case GF_ISOM_SUBTYPE_3GP_AMR:
	case GF_ISOM_SUBTYPE_3GP_AMR_WB:
		gf_bs_write_u16(bs, ptr->cfg.AMR_mode_set);
		gf_bs_write_u8(bs, ptr->cfg.AMR_mode_change_period);
		gf_bs_write_u8(bs, ptr->cfg.frames_per_sample);
		break;
	case GF_ISOM_SUBTYPE_3GP_EVRC:
	case GF_ISOM_SUBTYPE_3GP_QCELP:
	case GF_ISOM_SUBTYPE_3GP_SMV:
		gf_bs_write_u8(bs, ptr->cfg.frames_per_sample);
		break;
	}
	return GF_OK;
}

fixed #2175


GF_Err tsel_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	u32 i;
	GF_TrackSelectionBox *ptr = (GF_TrackSelectionBox *) s;

	e = gf_isom_full_box_write(s, bs);
	if (e) return e;
	gf_bs_write_u32(bs,ptr->switchGroup);

	for (i = 0; i < ptr->attributeListCount; i++ ) {
		gf_bs_write_u32(bs, ptr->attributeList[i]);
	}

	return GF_OK;
}

fixed #2175


GF_Err hclr_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	GF_TextHighlightColorBox*ptr = (GF_TextHighlightColorBox*)s;
	e = gf_isom_box_write_header(s, bs);
	if (e) return e;
	gpp_write_rgba(bs, ptr->hil_color);
	return GF_OK;
}

fixed #2175


GF_Box *hlit_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_TextHighlightBox, GF_ISOM_BOX_TYPE_HLIT);
	return (GF_Box *) tmp;
}

fixed #2175


GF_Err styl_box_size(GF_Box *s)
{
	GF_TextStyleBox*ptr = (GF_TextStyleBox*)s;

	s->size += 2 + ptr->entry_count * GPP_STYLE_SIZE;
	return GF_OK;
}

fixed #2175


GF_Err text_box_size(GF_Box *s)
{
	GF_TextSampleEntryBox *ptr = (GF_TextSampleEntryBox*)s;

	s->size += 8;
	/*base + this + string length*/
	s->size += 43 + 1;
	if (ptr->textName)
		s->size += strlen(ptr->textName);
	return GF_OK;
}

fixed #2175


GF_Box *gppc_box_new()
{
	//default type is amr but overwritten by box constructor
	ISOM_DECL_BOX_ALLOC(GF_3GPPConfigBox, GF_ISOM_BOX_TYPE_DAMR);
	return (GF_Box *)tmp;
}

fixed #2175


void krok_box_del(GF_Box *s)
{
	GF_TextKaraokeBox*ptr = (GF_TextKaraokeBox*)s;
	if (ptr->records) gf_free(ptr->records);
	gf_free(ptr);
}

fixed #2175


GF_Err hlit_box_size(GF_Box *s)
{
	s->size += 4;
	return GF_OK;
}

fixed #2175


GF_Err krok_box_size(GF_Box *s)
{
	GF_TextKaraokeBox*ptr = (GF_TextKaraokeBox*)s;
	s->size += 6 + 8*ptr->nb_entries;
	return GF_OK;
}

fixed #2175


GF_Box *blnk_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_TextBlinkBox, GF_ISOM_BOX_TYPE_BLNK);
	return (GF_Box *) tmp;
}

fixed #2175


GF_Err dlay_box_read(GF_Box *s, GF_BitStream *bs)
{
	GF_TextScrollDelayBox*ptr = (GF_TextScrollDelayBox*)s;
	ISOM_DECREASE_SIZE(ptr, 4)
	ptr->scroll_delay = gf_bs_read_u32(bs);
	return GF_OK;
}

fixed #2175


GF_Err gppc_box_size(GF_Box *s)
{
	GF_3GPPConfigBox *ptr = (GF_3GPPConfigBox *)s;

	s->size += 5;
	if (!ptr->cfg.type) {
		switch (ptr->type) {
		case GF_ISOM_BOX_TYPE_D263:
			ptr->cfg.type = GF_ISOM_SUBTYPE_3GP_H263;
			break;
		case GF_ISOM_BOX_TYPE_DAMR:
			ptr->cfg.type = GF_ISOM_SUBTYPE_3GP_AMR;
			break;
		case GF_ISOM_BOX_TYPE_DEVC:
			ptr->cfg.type = GF_ISOM_SUBTYPE_3GP_EVRC;
			break;
		case GF_ISOM_BOX_TYPE_DQCP:
			ptr->cfg.type = GF_ISOM_SUBTYPE_3GP_QCELP;
			break;
		case GF_ISOM_BOX_TYPE_DSMV:
			ptr->cfg.type = GF_ISOM_SUBTYPE_3GP_SMV;
			break;
		}
	}
	switch (ptr->cfg.type) {
	case GF_ISOM_SUBTYPE_3GP_H263:
		s->size += 2;
		break;
	case GF_ISOM_SUBTYPE_3GP_AMR:
	case GF_ISOM_SUBTYPE_3GP_AMR_WB:
		s->size += 4;
		break;
	case GF_ISOM_SUBTYPE_3GP_EVRC:
	case GF_ISOM_SUBTYPE_3GP_QCELP:
	case GF_ISOM_SUBTYPE_3GP_SMV:
		s->size += 1;
		break;
	}
	return GF_OK;
}

fixed #2175


GF_Err dims_box_size(GF_Box *s)
{
	u32 pos = 0;
	GF_DIMSSampleEntryBox *p = (GF_DIMSSampleEntryBox *)s;
	s->size += 8;
	gf_isom_check_position(s, (GF_Box *) p->config, &pos);
	gf_isom_check_position(s, (GF_Box *) p->scripts, &pos);
	return GF_OK;
}

fixed #2175


GF_Err twrp_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	GF_TextWrapBox*ptr = (GF_TextWrapBox*)s;
	e = gf_isom_box_write_header(s, bs);
	if (e) return e;
	gf_bs_write_u8(bs, ptr->wrap_flag);
	return GF_OK;
}

fixed #2175


GF_Box *dimC_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_DIMSSceneConfigBox, GF_ISOM_BOX_TYPE_DIMC);
	return (GF_Box *)tmp;
}

fixed #2175


GF_Err tbox_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	GF_TextBoxBox*ptr = (GF_TextBoxBox*)s;
	e = gf_isom_box_write_header(s, bs);
	if (e) return e;
	gpp_write_box(bs, &ptr->box);
	return GF_OK;
}

fixed #2175


GF_Err dims_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_DIMSSampleEntryBox *p = (GF_DIMSSampleEntryBox *)s;
	GF_Err e = gf_isom_box_write_header(s, bs);
	if (e) return e;
	gf_bs_write_data(bs, p->reserved, 6);
	gf_bs_write_u16(bs, p->dataReferenceIndex);
	return GF_OK;
}

fixed #2175


GF_Box *hclr_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_TextHighlightColorBox, GF_ISOM_BOX_TYPE_HCLR);
	return (GF_Box *) tmp;
}

fixed #2175


GF_Err blnk_box_read(GF_Box *s, GF_BitStream *bs)
{
	GF_TextBlinkBox*ptr = (GF_TextBlinkBox*)s;
	ISOM_DECREASE_SIZE(ptr, 4)
	ptr->startcharoffset = gf_bs_read_u16(bs);
	ptr->endcharoffset = gf_bs_read_u16(bs);
	return GF_OK;
}

fixed #2175


GF_Err diST_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_DIMSScriptTypesBox *p = (GF_DIMSScriptTypesBox *)s;
	GF_Err e = gf_isom_box_write_header(s, bs);
	if (e) return e;
	if (p->content_script_types)
		gf_bs_write_data(bs, p->content_script_types, (u32) strlen(p->content_script_types)+1);
	else
		gf_bs_write_u8(bs, 0);
	return GF_OK;
}

fixed #2175


GF_Err tsel_box_size(GF_Box *s)
{
	GF_TrackSelectionBox *ptr = (GF_TrackSelectionBox *) s;
	ptr->size += 4 + (4*ptr->attributeListCount);
	return GF_OK;
}

fixed #2175


void gpp_write_style(GF_BitStream *bs, GF_StyleRecord *rec)
{
	gf_bs_write_u16(bs, rec->startCharOffset);
	gf_bs_write_u16(bs, rec->endCharOffset);
	gf_bs_write_u16(bs, rec->fontID);
	gf_bs_write_u8(bs, rec->style_flags);
	gf_bs_write_u8(bs, rec->font_size);
	gpp_write_rgba(bs, rec->text_color);
}

fixed #2175


GF_Err href_box_write(GF_Box *s, GF_BitStream *bs)
{
	u32 len;
	GF_Err e;
	GF_TextHyperTextBox*ptr = (GF_TextHyperTextBox*)s;
	e = gf_isom_box_write_header(s, bs);
	if (e) return e;

	gf_bs_write_u16(bs, ptr->startcharoffset);
	gf_bs_write_u16(bs, ptr->endcharoffset);
	if (ptr->URL) {
		len = (u32) strlen(ptr->URL);
		gf_bs_write_u8(bs, len);
		gf_bs_write_data(bs, ptr->URL, len);
	} else {
		gf_bs_write_u8(bs, 0);
	}
	if (ptr->URL_hint) {
		len = (u32) strlen(ptr->URL_hint);
		gf_bs_write_u8(bs, len);
		gf_bs_write_data(bs, ptr->URL_hint, len);
	} else {
		gf_bs_write_u8(bs, 0);
	}
	return GF_OK;
}

fixed #2175


GF_Err hlit_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	GF_TextHighlightBox *ptr = (GF_TextHighlightBox *)s;
	e = gf_isom_box_write_header(s, bs);
	if (e) return e;
	gf_bs_write_u16(bs, ptr->startcharoffset);
	gf_bs_write_u16(bs, ptr->endcharoffset);
	return GF_OK;
}

fixed #2175


u32 gpp_read_rgba(GF_BitStream *bs)
{
	u8 r, g, b, a;
	u32 col;
	r = gf_bs_read_u8(bs);
	g = gf_bs_read_u8(bs);
	b = gf_bs_read_u8(bs);
	a = gf_bs_read_u8(bs);
	col = a;
	col<<=8;
	col |= r;
	col<<=8;
	col |= g;
	col<<=8;
	col |= b;
	return col;
}

fixed #2175


void ftab_box_del(GF_Box *s)
{
	GF_FontTableBox *ptr = (GF_FontTableBox *)s;
	if (ptr->fonts) {
		u32 i;
		for (i=0; i<ptr->entry_count; i++)
			if (ptr->fonts[i].fontName) gf_free(ptr->fonts[i].fontName);
		gf_free(ptr->fonts);
	}
	gf_free(ptr);
}

fixed #2175


GF_Err ftab_box_read(GF_Box *s, GF_BitStream *bs)
{
	u32 i;
	GF_FontTableBox *ptr = (GF_FontTableBox *)s;
	ptr->entry_count = gf_bs_read_u16(bs);
	ISOM_DECREASE_SIZE(ptr, 2);

	if (ptr->size<ptr->entry_count*3) {
		GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Corrupted ftap box, skipping\n"));
		ptr->entry_count = 0;
		return GF_OK;
	}
	ptr->fonts = (GF_FontRecord *) gf_malloc(sizeof(GF_FontRecord)*ptr->entry_count);
	if (!ptr->fonts) return GF_OUT_OF_MEM;

	memset(ptr->fonts, 0, sizeof(GF_FontRecord)*ptr->entry_count);
	for (i=0; i<ptr->entry_count; i++) {
		u32 len;
		ISOM_DECREASE_SIZE(ptr, 3);
		ptr->fonts[i].fontID = gf_bs_read_u16(bs);
		len = gf_bs_read_u8(bs);
		if (len) {
			ISOM_DECREASE_SIZE(ptr, len);
			ptr->fonts[i].fontName = (char *)gf_malloc(sizeof(char)*(len+1));
			if (!ptr->fonts[i].fontName) return GF_OUT_OF_MEM;
			gf_bs_read_data(bs, ptr->fonts[i].fontName, len);
			ptr->fonts[i].fontName[len] = 0;
		}
	}
	return GF_OK;
}

fixed #2175


void styl_box_del(GF_Box *s)
{
	GF_TextStyleBox*ptr = (GF_TextStyleBox*)s;
	if (ptr->styles) gf_free(ptr->styles);
	gf_free(ptr);
}

fixed #2175


void dlay_box_del(GF_Box *s)
{
	gf_free(s);
}

fixed #2175


void blnk_box_del(GF_Box *s)
{
	gf_free(s);
}

fixed #2175


GF_Err gppc_box_read(GF_Box *s, GF_BitStream *bs)
{
	GF_3GPPConfigBox *ptr = (GF_3GPPConfigBox *)s;
	if (ptr == NULL) return GF_BAD_PARAM;
	memset(&ptr->cfg, 0, sizeof(GF_3GPConfig));

	ISOM_DECREASE_SIZE(s, 5)
	ptr->cfg.vendor = gf_bs_read_u32(bs);
	ptr->cfg.decoder_version = gf_bs_read_u8(bs);

	switch (ptr->type) {
	case GF_ISOM_BOX_TYPE_D263:
		ISOM_DECREASE_SIZE(s, 2)
		ptr->cfg.H263_level = gf_bs_read_u8(bs);
		ptr->cfg.H263_profile = gf_bs_read_u8(bs);
		break;
	case GF_ISOM_BOX_TYPE_DAMR:
		ISOM_DECREASE_SIZE(s, 4)
		ptr->cfg.AMR_mode_set = gf_bs_read_u16(bs);
		ptr->cfg.AMR_mode_change_period = gf_bs_read_u8(bs);
		ptr->cfg.frames_per_sample = gf_bs_read_u8(bs);
		break;
	case GF_ISOM_BOX_TYPE_DEVC:
	case GF_ISOM_BOX_TYPE_DQCP:
	case GF_ISOM_BOX_TYPE_DSMV:
		ISOM_DECREASE_SIZE(s, 1)
		ptr->cfg.frames_per_sample = gf_bs_read_u8(bs);
		break;
	}
	return GF_OK;
}

fixed #2175


void gpp_read_style(GF_BitStream *bs, GF_StyleRecord *rec)
{
	rec->startCharOffset = gf_bs_read_u16(bs);
	rec->endCharOffset = gf_bs_read_u16(bs);
	rec->fontID = gf_bs_read_u16(bs);
	rec->style_flags = gf_bs_read_u8(bs);
	rec->font_size = gf_bs_read_u8(bs);
	rec->text_color = gpp_read_rgba(bs);
}

fixed #2175


GF_Err dims_box_read(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	GF_DIMSSampleEntryBox *p = (GF_DIMSSampleEntryBox *)s;

	e = gf_isom_base_sample_entry_read((GF_SampleEntryBox *)p, bs);
	if (e) return e;

	ISOM_DECREASE_SIZE(p, 8);
	return gf_isom_box_array_read(s, bs);
}

fixed #2175


GF_Err styl_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	u32 i;
	GF_TextStyleBox*ptr = (GF_TextStyleBox*)s;
	e = gf_isom_box_write_header(s, bs);
	if (e) return e;

	gf_bs_write_u16(bs, ptr->entry_count);
	for (i=0; i<ptr->entry_count; i++) gpp_write_style(bs, &ptr->styles[i]);
	return GF_OK;
}

fixed #2175


GF_Err dlay_box_size(GF_Box *s)
{
	s->size += 4;
	return GF_OK;
}

fixed #2175


GF_Err hlit_box_read(GF_Box *s, GF_BitStream *bs)
{
	GF_TextHighlightBox *ptr = (GF_TextHighlightBox *)s;
	ISOM_DECREASE_SIZE(ptr, 4)
	ptr->startcharoffset = gf_bs_read_u16(bs);
	ptr->endcharoffset = gf_bs_read_u16(bs);
	return GF_OK;
}

fixed #2175


GF_Err tx3g_box_read(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	GF_Tx3gSampleEntryBox *ptr = (GF_Tx3gSampleEntryBox*)s;

	ISOM_DECREASE_SIZE(ptr, (18 + GPP_BOX_SIZE + GPP_STYLE_SIZE) );

	e = gf_isom_base_sample_entry_read((GF_SampleEntryBox *)ptr, bs);
	if (e) return e;

	ptr->displayFlags = gf_bs_read_u32(bs);
	ptr->horizontal_justification = gf_bs_read_u8(bs);
	ptr->vertical_justification = gf_bs_read_u8(bs);
	ptr->back_color = gpp_read_rgba(bs);
	gpp_read_box(bs, &ptr->default_box);
	gpp_read_style(bs, &ptr->default_style);

	return gf_isom_box_array_read(s, bs);
}

fixed #2175


void href_box_del(GF_Box *s)
{
	GF_TextHyperTextBox*ptr = (GF_TextHyperTextBox*)s;
	if (ptr->URL) gf_free(ptr->URL);
	if (ptr->URL_hint) gf_free(ptr->URL_hint);
	gf_free(ptr);
}

fixed #2175


void twrp_box_del(GF_Box *s)
{
	gf_free(s);
}

fixed #2175


void tsel_box_del(GF_Box *s)
{
	GF_TrackSelectionBox *ptr;
	ptr = (GF_TrackSelectionBox *) s;
	if (ptr == NULL) return;
	if (ptr->attributeList) gf_free(ptr->attributeList);
	gf_free(ptr);
}

fixed #2175


GF_Err blnk_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	GF_TextBlinkBox*ptr = (GF_TextBlinkBox*)s;
	e = gf_isom_box_write_header(s, bs);
	if (e) return e;
	gf_bs_write_u16(bs, ptr->startcharoffset);
	gf_bs_write_u16(bs, ptr->endcharoffset);
	return GF_OK;
}

fixed #2175


GF_Box *styl_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_TextStyleBox, GF_ISOM_BOX_TYPE_STYL);
	return (GF_Box *) tmp;
}

fixed #2175


GF_Err ftab_box_size(GF_Box *s)
{
	u32 i;
	GF_FontTableBox *ptr = (GF_FontTableBox *)s;

	s->size += 2;
	for (i=0; i<ptr->entry_count; i++) {
		s->size += 3;
		if (ptr->fonts[i].fontName) s->size += strlen(ptr->fonts[i].fontName);
	}
	return GF_OK;
}

fixed #2175


GF_Err tx3g_on_child_box(GF_Box *s, GF_Box *a, Bool is_rem)
{
	GF_Tx3gSampleEntryBox *ptr = (GF_Tx3gSampleEntryBox*)s;
	switch (a->type) {
	case GF_ISOM_BOX_TYPE_FTAB:
		BOX_FIELD_ASSIGN(font_table, GF_FontTableBox)
		break;
	default:
		return GF_OK;
	}
	return GF_OK;
}

fixed #2175


GF_Err dimC_box_size(GF_Box *s)
{
	GF_DIMSSceneConfigBox *p = (GF_DIMSSceneConfigBox *)s;
    s->size += 3 + 2;
    if (p->textEncoding) s->size += strlen(p->textEncoding);
    if (p->contentEncoding) s->size += strlen(p->contentEncoding);
	return GF_OK;
}

fixed #2175


GF_Box *text_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_TextSampleEntryBox, GF_ISOM_BOX_TYPE_TEXT);
	gf_isom_sample_entry_init((GF_SampleEntryBox *)tmp);
	return (GF_Box *) tmp;
}

fixed #2175


GF_Err tsel_box_read(GF_Box *s,GF_BitStream *bs)
{
	u32 i;
	GF_TrackSelectionBox *ptr = (GF_TrackSelectionBox *) s;

	ISOM_DECREASE_SIZE(ptr, 4);
	ptr->switchGroup = gf_bs_read_u32(bs);

	if (ptr->size % 4) return GF_ISOM_INVALID_FILE;
	ptr->attributeListCount = (u32)ptr->size/4;
	ptr->attributeList = gf_malloc(ptr->attributeListCount*sizeof(u32));
	if (ptr->attributeList == NULL) return GF_OUT_OF_MEM;

	for (i=0; i< ptr->attributeListCount; i++) {
		ptr->attributeList[i] = gf_bs_read_u32(bs);
	}
	return GF_OK;
}

fixed #2175


GF_Err dimC_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_DIMSSceneConfigBox *p = (GF_DIMSSceneConfigBox *)s;
	GF_Err e = gf_isom_box_write_header(s, bs);
	if (e) return e;
	gf_bs_write_u8(bs, p->profile);
	gf_bs_write_u8(bs, p->level);
	gf_bs_write_int(bs, p->pathComponents, 4);
	gf_bs_write_int(bs, p->fullRequestHost, 1);
	gf_bs_write_int(bs, p->streamType, 1);
	gf_bs_write_int(bs, p->containsRedundant, 2);
    if (p->textEncoding)
        gf_bs_write_data(bs, p->textEncoding, (u32) strlen(p->textEncoding));
    gf_bs_write_u8(bs, 0);
    if (p->contentEncoding)
        gf_bs_write_data(bs, p->contentEncoding, (u32) strlen(p->contentEncoding));
    gf_bs_write_u8(bs, 0);
	return GF_OK;
}

fixed #2175


GF_Err krok_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	u32 i;
	GF_TextKaraokeBox*ptr = (GF_TextKaraokeBox*)s;
	e = gf_isom_box_write_header(s, bs);
	if (e) return e;

	gf_bs_write_u32(bs, ptr->highlight_starttime);
	gf_bs_write_u16(bs, ptr->nb_entries);
	for (i=0; i<ptr->nb_entries; i++) {
		gf_bs_write_u32(bs, ptr->records[i].highlight_endtime);
		gf_bs_write_u16(bs, ptr->records[i].start_charoffset);
		gf_bs_write_u16(bs, ptr->records[i].end_charoffset);
	}
	return GF_OK;
}

fixed #2175


GF_Err hclr_box_read(GF_Box *s, GF_BitStream *bs)
{
	GF_TextHighlightColorBox*ptr = (GF_TextHighlightColorBox*)s;
	ISOM_DECREASE_SIZE(ptr, 4)
	ptr->hil_color = gpp_read_rgba(bs);
	return GF_OK;
}

fixed #2175


GF_Err hclr_box_size(GF_Box *s)
{
	s->size += 4;
	return GF_OK;
}

fixed #2175


void dimC_box_del(GF_Box *s)
{
	GF_DIMSSceneConfigBox *p = (GF_DIMSSceneConfigBox *)s;
	if (p->contentEncoding) gf_free(p->contentEncoding);
	if (p->textEncoding) gf_free(p->textEncoding);
	gf_free(p);
}

fixed #2175


void text_box_del(GF_Box *s)
{
	GF_TextSampleEntryBox *ptr = (GF_TextSampleEntryBox*)s;
	gf_isom_sample_entry_predestroy((GF_SampleEntryBox *)s);

	if (ptr->textName)
		gf_free(ptr->textName);
	gf_free(ptr);
}

fixed #2175


void diST_box_del(GF_Box *s)
{
	GF_DIMSScriptTypesBox *p = (GF_DIMSScriptTypesBox *)s;
	if (p->content_script_types) gf_free(p->content_script_types);
	gf_free(p);
}

fixed #2175


GF_Err blnk_box_size(GF_Box *s)
{
	s->size += 4;
	return GF_OK;
}

fixed #2175


GF_Err twrp_box_size(GF_Box *s)
{
	s->size += 1;
	return GF_OK;
}

fixed #2175


GF_Err tbox_box_size(GF_Box *s)
{
	s->size += 8;
	return GF_OK;
}

fixed #2175


GF_Err href_box_size(GF_Box *s)
{
	GF_TextHyperTextBox*ptr = (GF_TextHyperTextBox*)s;
	s->size += 6;
	if (ptr->URL) s->size += strlen(ptr->URL);
	if (ptr->URL_hint) s->size += strlen(ptr->URL_hint);
	return GF_OK;
}

fixed #2175


GF_Err krok_box_read(GF_Box *s, GF_BitStream *bs)
{
	GF_TextKaraokeBox*ptr = (GF_TextKaraokeBox*)s;

	ISOM_DECREASE_SIZE(ptr, 6)
	ptr->highlight_starttime = gf_bs_read_u32(bs);
	ptr->nb_entries = gf_bs_read_u16(bs);
	if (ptr->size / 8 < ptr->nb_entries)
		return GF_ISOM_INVALID_FILE;

	if (ptr->nb_entries) {
		u32 i;
		ptr->records = (KaraokeRecord*)gf_malloc(sizeof(KaraokeRecord)*ptr->nb_entries);
		if (!ptr->records) return GF_OUT_OF_MEM;
		for (i=0; i<ptr->nb_entries; i++) {
			ISOM_DECREASE_SIZE(ptr, 8)
			ptr->records[i].highlight_endtime = gf_bs_read_u32(bs);
			ptr->records[i].start_charoffset = gf_bs_read_u16(bs);
			ptr->records[i].end_charoffset = gf_bs_read_u16(bs);
		}
	}
	return GF_OK;
}

fixed #2175


GF_Err tbox_box_read(GF_Box *s, GF_BitStream *bs)
{
	GF_TextBoxBox*ptr = (GF_TextBoxBox*)s;
	ISOM_DECREASE_SIZE(ptr, GPP_BOX_SIZE)
	gpp_read_box(bs, &ptr->box);
	return GF_OK;
}

fixed #2175


void gpp_write_rgba(GF_BitStream *bs, u32 col)
{
	gf_bs_write_u8(bs, (col>>16) & 0xFF);
	gf_bs_write_u8(bs, (col>>8) & 0xFF);
	gf_bs_write_u8(bs, (col) & 0xFF);
	gf_bs_write_u8(bs, (col>>24) & 0xFF);
}

fixed #2175


GF_Err tx3g_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	GF_Tx3gSampleEntryBox *ptr = (GF_Tx3gSampleEntryBox*)s;

	e = gf_isom_box_write_header(s, bs);
	if (e) return e;
	gf_bs_write_data(bs, ptr->reserved, 6);
	gf_bs_write_u16(bs, ptr->dataReferenceIndex);
	gf_bs_write_u32(bs, ptr->displayFlags);
	gf_bs_write_u8(bs, ptr->horizontal_justification);
	gf_bs_write_u8(bs, ptr->vertical_justification);
	gpp_write_rgba(bs, ptr->back_color);
	gpp_write_box(bs, &ptr->default_box);
	gpp_write_style(bs, &ptr->default_style);
	return GF_OK;
}

fixed #2175


void gppc_box_del(GF_Box *s)
{
	GF_3GPPConfigBox *ptr = (GF_3GPPConfigBox *)s;
	if (ptr == NULL) return;
	gf_free(ptr);
}

fixed #2175


GF_Box *diST_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_DIMSScriptTypesBox, GF_ISOM_BOX_TYPE_DIST);
	return (GF_Box *)tmp;
}

fixed #2175


void hlit_box_del(GF_Box *s)
{
	gf_free(s);
}

fixed #2175


GF_Box *krok_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_TextKaraokeBox, GF_ISOM_BOX_TYPE_KROK);
	return (GF_Box *) tmp;
}

fixed #2175