libheif icon indicating copy to clipboard operation
libheif copied to clipboard

Invalid input: No 'meta' box

Open hackerfactor opened this issue 3 years ago • 3 comments

I have a HEIC file that renders properly on MacOS and with libheif.so.1.11.0 but it stopped rendering when I updated aom, de265, x265, and heif.

It renders with libaom.3.0.0, libde265.so.0.1.1, libx265.so.200, and libheif.so.1.11.0. It fails to render with libaom.3.2.0, libde265.so.0.1.1, libx265.so.201, and libheif.so.1.12.0.

For my use, the error comes from "heif_context_read_from_memory_without_copy" with error.code 2: Could not read HEIF/AVIF file: Invalid input: No 'meta' box

I see the same error when using heif-convert:

$ heif-convert fakeid.heic fakeid.jpg
Could not read HEIF/AVIF file: Invalid input: No 'meta' box

heif-info -d fakeid.heic says:

$ heif-info -d fakeid.heic 
MIME type: image/heic
main brand: heic
compatible brands: heic, mif1

Box: ftyp -----
size: 24   (header size: 8)
major brand: heic
minor version: 0
compatible brands: heic,mif1

(That's all it shows.)

My own heic parser (it just checks structure, not rendering) correctly identifies all of the components and it correctly finds the meta box.

 <summary field='Structure Summary'>
  <summary field='meta' offset='0x1c' size='1090' value=''/>
  <summary field='meta:hdlr' offset='0x2c' size='26' value=''/>
  <summary field='meta:dinf' offset='0x4e' size='28' value=''/>
  <summary field='meta:dinf:dref' offset='0x56' size='20' value=''/>
  <summary field='meta:pitm' offset='0x72' size='6' value='PrimaryID=1'/>
  <summary field='meta:iinf' offset='0x86' size='89' value=''/>
  <summary field='meta:iinf:infe' offset='0x86' size='0' value=''/>
  <summary field='meta:iinf:infe:hvc1' offset='0x86' size='21' value='ID=1, Image=0, 436x748 at 0x0'/>
  <summary field='meta:iinf:infe:Exif' offset='0x9b' size='21' value='ID=2'/>
  <summary field='meta:iinf:infe:mime' offset='0xb0' size='21' value='ID=3'/>
  <summary field='meta:iref' offset='0xe1' size='32' value=''/>
  <summary field='meta:iref:cdsc' offset='0xed' size='6' value=''/>
  <summary field='meta:iref:cdsc' offset='0xfb' size='6' value=''/>
  <summary field='meta:iprp' offset='0x109' size='791' value=''/>
  <summary field='meta:iprp:ipco' offset='0x111' size='758' value=''/>
  <summary field='meta:iprp:ipco:colr' offset='0x119' size='552' value=''/>
  <summary field='meta:iprp:ipco:hvcC' offset='0x349' size='105' value=''/>
  <summary field='meta:iprp:ipco:ispe' offset='0x3ba' size='12' value='436x748 at 0x0'/>
  <summary field='meta:iprp:ipco:clap' offset='0x3ce' size='32' value='435x747 at 0x0'/>
  <summary field='meta:iprp:ipco:irot' offset='0x3f6' size='1' value=''/>
  <summary field='meta:iprp:ipco:pixi' offset='0x3ff' size='8' value=''/>
  <summary field='meta:iprp:ipma' offset='0x40f' size='17' value=''/>
  <summary field='meta:iprp:ipma:data' offset='0x417' size='0' value='ID=1, 436x748 at 0x0, hvcC=2, colr=1, irot=5, clap=4'/>
  <summary field='meta:iloc' offset='0x428' size='50' value=''/>
  <summary field='meta:iloc:data' value='ID=1, Image=0, 436x748 at 0x0, hvcC=2, colr=1, irot=5, clap=4'/>
  <summary field='meta:iloc:data' value='ID=2'/>
  <summary field='meta:iloc:data' value='ID=3'/>
  <summary field='mdat' offset='0x45e' size='49809' value=''/>
 </summary>

I'd rather not post the picture to this public forum since it is a picture of an ID card. (Granted, it's a bad fake id that is likely from a catfish scam, it's still "an id".)

hackerfactor avatar Jan 27 '22 23:01 hackerfactor

I found another example that doesn't contain personal information: https://fotoforensics.com/analysis.php?id=a1900c7f2e297eac075dc3efb893ae1822025eec.17895&fmt=orig

hackerfactor avatar Jan 28 '22 00:01 hackerfactor

Hi, I have the same issue as you and I've manage to narrow it down to this change: https://github.com/strukturag/libheif/commit/2c4cb5712724b5617019dc749b91b0acd0f9ad7c

the issue is because of the type changed from int32_t to uint32_t

  uint32_t clean_aperture_width_num = range.read32();
  uint32_t clean_aperture_width_den = range.read32();
  uint32_t clean_aperture_height_num = range.read32();
  uint32_t clean_aperture_height_den = range.read32();
  uint32_t horizontal_offset_num = range.read32();
  uint32_t horizontal_offset_den = range.read32();
  uint32_t vertical_offset_num = range.read32();
  uint32_t vertical_offset_den = range.read32();
  
  if (clean_aperture_width_num > std::numeric_limits<int32_t>::max() ||
      clean_aperture_width_den > std::numeric_limits<int32_t>::max() ||
      clean_aperture_height_num > std::numeric_limits<int32_t>::max() ||
      clean_aperture_height_den > std::numeric_limits<int32_t>::max() ||
      horizontal_offset_num > std::numeric_limits<int32_t>::max() ||
      horizontal_offset_den > std::numeric_limits<int32_t>::max() ||
      vertical_offset_num > std::numeric_limits<int32_t>::max() ||
      vertical_offset_den > std::numeric_limits<int32_t>::max()) {
    return Error(heif_error_Invalid_input,
                 heif_suberror_Invalid_fractional_number,
                 "Exceeded supported value range.");
  }

The horizontal_offset_num can be negative, like in the image you added the offset is : -32768/65536

| | | Box: clap -----
| | | size: 40   (header size: 8)
| | | clean_aperture: 431/1 x 250/1
| | | offset: -32768/65536 ; 0/1

when this value gets casted to uint32_t it becomes 4290772992 instead and then, when comparing the number with std::numeric_limits<int32_t>::max(), the comparation is done with the max of an int32_t which is 2147483647 and therefore 4290772992 > 2147483647 is true and returns the error.

I think that either the check should check with the max of uint32_t or the type should be int32_t instead.

I've made a PR to my fork with the fix:https://github.com/jaume-pinyol/libheif/pull/6 if you are interested

@farindk do you think it makes sense?, I can do a PR if you want

jaume-pinyol avatar Mar 02 '22 19:03 jaume-pinyol

This fix works (changing uint32_t to int32_t). Can someone add this to the main branch?

hackerfactor avatar Apr 10 '22 16:04 hackerfactor

Hm, the issue with this is that according to the specification, clap should contain unsigned values: Screenshot from 2022-08-30 22-40-37s Now, I'm not sure how to handle this. Strictly speaking, the images appear to be invalid. However, any image using the full 32-bit range also seems highly unlikely, so it wouldn't hurt much if we assumed that the offset can also be negative.

farindk avatar Aug 30 '22 20:08 farindk

This is obviously an error in the standard specification as in the accompanying text, it also says that the offset can be negative. I'll change this accordingly.

farindk avatar Aug 30 '22 20:08 farindk

Changed in 626b620. Thanks for the test case and analysis.

farindk avatar Aug 30 '22 21:08 farindk