[vlc-commits] [Git][videolan/vlc][3.0.x] 8 commits: demux: image: fix jpeg probe size return check

François Cartegnie (@fcartegnie) gitlab at videolan.org
Mon Dec 26 09:14:07 UTC 2022



François Cartegnie pushed to branch 3.0.x at VideoLAN / VLC


Commits:
22ae743f by Francois Cartegnie at 2022-12-26T09:01:27+00:00
demux: image: fix jpeg probe size return check

valid jpeg data offset being greater than 0xff

(cherry picked from commit a0e6c1758586a1b98a0100507a3f0c47839050c1)

- - - - -
9b3b296d by Francois Cartegnie at 2022-12-26T09:01:27+00:00
demux: image: use proper type for peek

(cherry picked from commit 8b310b2fd572dc54356dac1393bd4c14bfef6532)

- - - - -
ab66dd1f by Francois Cartegnie at 2022-12-26T09:01:27+00:00
demux: image: fix probing jpeg starting with XMP

(cherry picked from commit 1c6749b8090a659678ff1cdbd80358405efac3f3)

- - - - -
0118f97c by Francois Cartegnie at 2022-12-26T09:01:27+00:00
codec: jpeg: search every APP1 for XMP projection tag

(cherry picked from commit 7c3130c951840972229b265f8de5679a543d324f)

- - - - -
2acd4474 by Francois Cartegnie at 2022-12-26T09:01:27+00:00
codec: jpeg: check projection values

(cherry picked from commit 4e419fa5266a5574908a4cf6146c12e3c56a0e86)

- - - - -
88d5ca55 by Francois Cartegnie at 2022-12-26T09:01:27+00:00
codec: jpeg: constify

(cherry picked from commit 1a82010c9665ad449fe48545e5c1522bb89e67e1)

- - - - -
9b9cbf2b by Francois Cartegnie at 2022-12-26T09:01:27+00:00
codec: jpeg: use native readers

(cherry picked from commit d80b8049b7ce2c05bf18397a951340668a3a9ced)

- - - - -
062db58d by Francois Cartegnie at 2022-12-26T09:01:27+00:00
codec: jpeg: check every APP1 for orientation

(cherry picked from commit a0d6edda54b329649c50b7de8c97b1509990fd20)

- - - - -


2 changed files:

- modules/codec/jpeg.c
- modules/demux/image.c


Changes:

=====================================
modules/codec/jpeg.c
=====================================
@@ -212,44 +212,14 @@ static int OpenDecoder(vlc_object_t *p_this)
 typedef unsigned int uint;
 typedef unsigned short ushort;
 
-LOCAL( unsigned short )
-de_get16( void * ptr, uint endian ) {
-    unsigned short val;
-
-    memcpy( &val, ptr, sizeof( val ) );
-    if ( endian == G_BIG_ENDIAN )
-    {
-        #ifndef WORDS_BIGENDIAN
-        val = bswap16( val );
-        #endif
-    }
-    else
-    {
-        #ifdef WORDS_BIGENDIAN
-        val = bswap16( val );
-        #endif
-    }
-    return val;
+static uint16_t
+de_get16( const void * ptr, uint endian ) {
+    return (endian == G_BIG_ENDIAN) ? GetWBE(ptr) : GetWLE(ptr);
 }
 
-LOCAL( unsigned int )
-de_get32( void * ptr, uint endian ) {
-    unsigned int val;
-
-    memcpy( &val, ptr, sizeof( val ) );
-    if ( endian == G_BIG_ENDIAN )
-    {
-        #ifndef WORDS_BIGENDIAN
-        val = bswap32( val );
-        #endif
-    }
-    else
-    {
-        #ifdef WORDS_BIGENDIAN
-        val = bswap32( val );
-        #endif
-    }
-    return val;
+static uint32_t
+de_get32( const void * ptr, uint endian ) {
+    return (endian == G_BIG_ENDIAN) ? GetDWBE(ptr) : GetDWLE(ptr);
 }
 
 static bool getRDFFloat(const char *psz_rdf, float *out, const char *psz_var)
@@ -284,42 +254,31 @@ static bool getRDFFloat(const char *psz_rdf, float *out, const char *psz_var)
 
 /* read XMP metadata for projection according to
  * https://developers.google.com/streetview/spherical-metadata */
-static void jpeg_GetProjection(j_decompress_ptr cinfo, video_format_t *fmt)
+static bool jpeg_ParseXMP(const uint8_t *p_buf, size_t i_buf,
+                          video_format_t *fmt)
 {
-    jpeg_saved_marker_ptr xmp_marker = NULL;
-    jpeg_saved_marker_ptr cmarker = cinfo->marker_list;
-
-    while (cmarker)
-    {
-        if (cmarker->marker == EXIF_JPEG_MARKER)
-        {
-            if(cmarker->data_length >= 32 &&
-               !memcmp(cmarker->data, EXIF_XMP_STRING, 29))
-            {
-                xmp_marker = cmarker;
-                break;
-            }
-        }
-        cmarker = cmarker->next;
-    }
+    if(i_buf < 29)
+        return false;
 
-    if (xmp_marker == NULL)
-        return;
-    char *psz_rdf = malloc(xmp_marker->data_length - 29 + 1);
+    /* Allocate temp, zero terminated buffer */
+    /* Fixme: custom memicmp */
+    /* or Fixme: Who's not compliant to XML attributes case ? */
+    char *psz_rdf = malloc(i_buf - 29 + 1);
     if (unlikely(psz_rdf == NULL))
-        return;
-    memcpy(psz_rdf, xmp_marker->data + 29, xmp_marker->data_length - 29);
-    psz_rdf[xmp_marker->data_length - 29] = '\0';
+        return false;
+    memcpy(psz_rdf, p_buf + 29, i_buf - 29);
+    psz_rdf[i_buf - 29] = '\0';
+
+    if (!strcasestr(psz_rdf, "ProjectionType=\"equirectangular\"") &&
+        !strcasestr(psz_rdf, "ProjectionType>equirectangular"))
+    return false;
 
-    /* Try to find the string "GSpherical:Spherical" because the v1
-        spherical video spec says the tag must be there. */
-    if (strcasestr(psz_rdf, "ProjectionType=\"equirectangular\"") ||
-        strcasestr(psz_rdf, "ProjectionType>equirectangular"))
-        fmt->projection_mode = PROJECTION_MODE_EQUIRECTANGULAR;
+    fmt->projection_mode = PROJECTION_MODE_EQUIRECTANGULAR;
 
     /* pose handling */
     float value;
-    if (getRDFFloat(psz_rdf, &value, "PoseHeadingDegrees"))
+    if (getRDFFloat(psz_rdf, &value, "PoseHeadingDegrees") &&
+        value >= 0.f && value <= 360.f)
         fmt->pose.yaw = value;
 
     if (getRDFFloat(psz_rdf, &value, "PosePitchDegrees"))
@@ -338,23 +297,40 @@ static void jpeg_GetProjection(j_decompress_ptr cinfo, video_format_t *fmt)
     if (getRDFFloat(psz_rdf, &value, "InitialViewRollDegrees"))
         fmt->pose.roll = value;
 
-    if (getRDFFloat(psz_rdf, &value, "InitialHorizontalFOVDegrees"))
+    if (getRDFFloat(psz_rdf, &value, "InitialHorizontalFOVDegrees") &&
+        value >= FIELD_OF_VIEW_DEGREES_MIN && value <= FIELD_OF_VIEW_DEGREES_MAX)
         fmt->pose.fov = value;
 
     free(psz_rdf);
+
+    return true;
+}
+
+static void jpeg_FillProjection(j_decompress_ptr cinfo, video_format_t *fmt)
+{
+    for (jpeg_saved_marker_ptr cmarker = cinfo->marker_list;
+                               cmarker != NULL;
+                               cmarker = cmarker->next)
+    {
+        if(cmarker->marker == EXIF_JPEG_MARKER &&
+           cmarker->data_length >= 32 &&
+           !memcmp(cmarker->data, EXIF_XMP_STRING, 29) &&
+           jpeg_ParseXMP(cmarker->data, cmarker->data_length, fmt))
+           break; /* found projection marker */
+    }
 }
 
 /*
  * Look through the meta data in the libjpeg decompress structure to determine
- * if an EXIF Orientation tag is present. If so return its value (1-8),
- * otherwise return 0
+ * if an EXIF Orientation tag is present. If so return true and sets orientation
+ *  value (1-8), otherwise return false
  *
  * This function is based on the function get_orientation in io-jpeg.c, part of
  * the GdkPixbuf library, licensed under LGPLv2+.
  *   Copyright (C) 1999 Michael Zucchi, The Free Software Foundation
 */
-LOCAL( int )
-jpeg_GetOrientation( j_decompress_ptr cinfo )
+#define EXIF_IDENT_STRING   "Exif\0" /* Exif\000\000 */
+static bool jpeg_ParseExifApp1( const uint8_t *p_buf, size_t i_buf, int *orientation )
 {
 
     uint i;                    /* index into working buffer */
@@ -367,37 +343,13 @@ jpeg_GetOrientation( j_decompress_ptr cinfo )
     uint tiff = 0;             /* offset to active tiff header */
     uint endian = 0;           /* detected endian of data */
 
-    jpeg_saved_marker_ptr exif_marker;      /* Location of the Exif APP1 marker */
-    jpeg_saved_marker_ptr cmarker;          /* Location to check for Exif APP1 marker */
-
     const char leth[] = { 0x49, 0x49, 0x2a, 0x00 }; /* Little endian TIFF header */
     const char beth[] = { 0x4d, 0x4d, 0x00, 0x2a }; /* Big endian TIFF header */
 
-    #define EXIF_IDENT_STRING   "Exif\000\000"
     #define EXIF_ORIENT_TAGID   0x112
 
-    /* check for Exif marker (also called the APP1 marker) */
-    exif_marker = NULL;
-    cmarker = cinfo->marker_list;
-
-    while ( cmarker )
-    {
-        if ( cmarker->data_length >= 32 &&
-             cmarker->marker == EXIF_JPEG_MARKER )
-        {
-            /* The Exif APP1 marker should contain a unique
-               identification string ("Exif\0\0"). Check for it. */
-            if ( !memcmp( cmarker->data, EXIF_IDENT_STRING, 6 ) )
-            {
-                exif_marker = cmarker;
-            }
-        }
-        cmarker = cmarker->next;
-    }
-
-    /* Did we find the Exif APP1 marker? */
-    if ( exif_marker == NULL )
-        return 0;
+    if (i_buf < 32)
+        return false;
 
     /* Check for TIFF header and catch endianness */
     i = 0;
@@ -420,13 +372,13 @@ jpeg_GetOrientation( j_decompress_ptr cinfo )
     while ( i < 16 )
     {
         /* Little endian TIFF header */
-        if ( memcmp( &exif_marker->data[i], leth, 4 ) == 0 )
+        if ( memcmp( &p_buf[i], leth, 4 ) == 0 )
         {
             endian = G_LITTLE_ENDIAN;
         }
         /* Big endian TIFF header */
         else
-        if ( memcmp( &exif_marker->data[i], beth, 4 ) == 0 )
+        if ( memcmp( &p_buf[i], beth, 4 ) == 0 )
         {
             endian = G_BIG_ENDIAN;
         }
@@ -443,53 +395,70 @@ jpeg_GetOrientation( j_decompress_ptr cinfo )
 
     /* So did we find a TIFF header or did we just hit end of buffer? */
     if ( tiff == 0 )
-        return 0;
+        return false;
 
     /* Read out the offset pointer to IFD0 */
-    offset = de_get32( &exif_marker->data[i] + 4, endian );
+    offset = de_get32( &p_buf[i] + 4, endian );
     i = i + offset;
 
     /* Check that we still are within the buffer and can read the tag count */
 
-    if ( i > exif_marker->data_length - 2 )
-        return 0;
+    if ( i > i_buf - 2 )
+        return false;
 
     /* Find out how many tags we have in IFD0. As per the TIFF spec, the first
        two bytes of the IFD contain a count of the number of tags. */
-    tags = de_get16( &exif_marker->data[i], endian );
+    tags = de_get16( &p_buf[i], endian );
     i = i + 2;
 
     /* Check that we still have enough data for all tags to check. The tags
        are listed in consecutive 12-byte blocks. The tag ID, type, size, and
        a pointer to the actual value, are packed into these 12 byte entries. */
-    if ( tags * 12U > exif_marker->data_length - i )
-        return 0;
+    if ( tags * 12U > i_buf - i )
+        return false;
 
     /* Check through IFD0 for tags of interest */
     while ( tags-- )
     {
-        tag_type = de_get16( &exif_marker->data[i], endian );
+        tag_type = de_get16( &p_buf[i], endian );
         /* Is this the orientation tag? */
         if ( tag_type == EXIF_ORIENT_TAGID )
         {
-            type = de_get16( &exif_marker->data[i + 2], endian );
-            count = de_get32( &exif_marker->data[i + 4], endian );
+            type = de_get16( &p_buf[i + 2], endian );
+            count = de_get32( &p_buf[i + 4], endian );
 
             /* Check that type and count fields are OK. The orientation field
                will consist of a single (count=1) 2-byte integer (type=3). */
             if ( type != 3 || count != 1 )
-                return 0;
+                return false;
 
             /* Return the orientation value. Within the 12-byte block, the
                pointer to the actual data is at offset 8. */
-            ret = de_get16( &exif_marker->data[i + 8], endian );
-            return ( ret <= 8 ) ? ret : 0;
+            ret = de_get16( &p_buf[i + 8], endian );
+            *orientation = ( ret <= 8 ) ? ret : 0;
+            return true;
         }
         /* move the pointer to the next 12-byte tag field. */
         i = i + 12;
     }
 
-    return 0;     /* No EXIF Orientation tag found */
+    return false; /* No EXIF Orientation tag found */
+}
+
+static int jpeg_GetOrientation( j_decompress_ptr cinfo )
+{
+    int orientation = 0;
+    for (jpeg_saved_marker_ptr cmarker = cinfo->marker_list;
+                               cmarker != NULL;
+                               cmarker = cmarker->next)
+    {
+        if(cmarker->marker == EXIF_JPEG_MARKER &&
+           cmarker->data_length >= 32 &&
+           !memcmp(cmarker->data, EXIF_IDENT_STRING, sizeof(EXIF_IDENT_STRING)) &&
+           jpeg_ParseExifApp1(cmarker->data, cmarker->data_length, &orientation))
+           break; /* found orientation marker */
+    }
+    return orientation;
 }
 
 /*
@@ -539,7 +508,7 @@ static int DecodeBlock(decoder_t *p_dec, block_t *p_block)
         msg_Dbg( p_dec, "Jpeg orientation is %d", i_otag );
         p_dec->fmt_out.video.orientation = ORIENT_FROM_EXIF( i_otag );
     }
-    jpeg_GetProjection(&p_sys->p_jpeg, &p_dec->fmt_out.video);
+    jpeg_FillProjection(&p_sys->p_jpeg, &p_dec->fmt_out.video);
 
     /* Get a new picture */
     if (decoder_UpdateVideoFormat(p_dec))


=====================================
modules/demux/image.c
=====================================
@@ -377,9 +377,9 @@ static bool IsPnm(stream_t *s)
     return true;
 }
 
-static uint8_t FindJpegMarker(int *position, const uint8_t *data, int size)
+static uint8_t FindJpegMarker(size_t *position, const uint8_t *data, size_t size)
 {
-    for (int i = *position; i + 1 < size; i++) {
+    for (size_t i = *position; i + 1 < size; i++) {
         if (data[i + 0] != 0xff || data[i + 1] == 0x00)
             return 0xff;
         if (data[i + 1] != 0xff) {
@@ -392,8 +392,11 @@ static uint8_t FindJpegMarker(int *position, const uint8_t *data, int size)
 static bool IsJfif(stream_t *s)
 {
     const uint8_t *header;
-    int size = vlc_stream_Peek(s, &header, 256);
-    int position = 0;
+    ssize_t peek = vlc_stream_Peek(s, &header, 256);
+    if(peek < 256)
+        return false;
+    size_t size = (size_t) peek;
+    size_t position = 0;
 
     if (FindJpegMarker(&position, header, size) != 0xd8)
         return false;
@@ -435,24 +438,29 @@ static bool IsSpiff(stream_t *s)
     return true;
 }
 
-static bool IsExif(stream_t *s)
+#define EXIF_STRING "Exif" /* includes \0 */
+#define EXIF_XMP_STRING "http://ns.adobe.com/xap/1.0/" /* includes \0 */
+static bool IsExifXMP(stream_t *s)
 {
     const uint8_t *header;
-    ssize_t size = vlc_stream_Peek(s, &header, 256);
-    if (size == -1)
+    ssize_t peek = vlc_stream_Peek(s, &header, 256);
+    if (peek < 256)
         return false;
-    int position = 0;
+    size_t size = (size_t) peek;
+    size_t position = 0;
 
     if (FindJpegMarker(&position, header, size) != 0xd8)
         return false;
     if (FindJpegMarker(&position, header, size) != 0xe1)
         return false;
     position += 2;  /* Skip size */
-    if (position + 5 > size)
-        return false;
-    if (memcmp(&header[position], "Exif\0", 5))
-        return false;
-    return true;
+    if (position + sizeof(EXIF_STRING) <= size &&
+        !memcmp(&header[position], EXIF_STRING, sizeof(EXIF_STRING)))
+        return true;
+    if (position + sizeof(EXIF_XMP_STRING) <= size &&
+        !memcmp(&header[position], EXIF_XMP_STRING, sizeof(EXIF_XMP_STRING)))
+        return true;
+    return false;
 }
 
 static bool FindSVGmarker(int *position, const uint8_t *data, const int size, const char *marker)
@@ -620,7 +628,7 @@ static const image_format_t formats[] = {
       .detect = IsSpiff,
     },
     { .codec = VLC_CODEC_JPEG,
-      .detect = IsExif,
+      .detect = IsExifXMP,
     },
     { .codec = VLC_CODEC_WEBP,
       .detect = IsWebP,



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/da33263b12c9814da922234a3734c393c89345fa...062db58dccb0f5292eb4a3fd94df0128d9403ffc

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/da33263b12c9814da922234a3734c393c89345fa...062db58dccb0f5292eb4a3fd94df0128d9403ffc
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list