Skip to content

Commit

Permalink
Support for 64-bit box lengths looked broken.
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinbackhouse committed Jul 23, 2021
1 parent 8c64e9a commit 02d4ef2
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 59 deletions.
6 changes: 3 additions & 3 deletions include/exiv2/bmffimage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ namespace Exiv2
@param start offset in file (default, io_->tell())
@
*/
void parseTiff(uint32_t root_tag, uint32_t length);
void parseTiff(uint32_t root_tag, uint32_t length,uint32_t start);
void parseTiff(uint32_t root_tag, uint64_t length);
void parseTiff(uint32_t root_tag, uint64_t length,uint64_t start);
//@}

//@{
Expand All @@ -103,7 +103,7 @@ namespace Exiv2
@param start offset in file
@
*/
void parseXmp(uint32_t length,uint32_t start);
void parseXmp(uint64_t length,uint64_t start);
//@}

//! @name Manipulators
Expand Down
103 changes: 47 additions & 56 deletions src/bmffimage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,6 @@
#include <iostream>
#include <string>

struct BmffBoxHeader
{
uint32_t length;
uint32_t type;
};

#define TAG_ftyp 0x66747970 /**< "ftyp" File type box */
#define TAG_avif 0x61766966 /**< "avif" AVIF */
#define TAG_avio 0x6176696f /**< "avio" AVIF */
Expand Down Expand Up @@ -202,47 +196,40 @@ namespace Exiv2
bTrace = true ;
#endif

BmffBoxHeader box = {0, 0};
size_t hdrsize = sizeof(box);
// 8-byte buffer for parsing the box length and type.
byte hdrbuf[2 * sizeof(uint32_t)];

size_t hdrsize = sizeof(hdrbuf);
enforce(hdrsize <= static_cast<size_t>(pbox_end - address), Exiv2::kerCorruptedMetadata);
if (io_->read(reinterpret_cast<byte*>(&box), sizeof(box)) != sizeof(box))
if (io_->read(reinterpret_cast<byte*>(&hdrbuf), sizeof(hdrbuf)) != sizeof(hdrbuf))
return result;

box.length = getLong(reinterpret_cast<byte*>(&box.length), endian_);
box.type = getLong(reinterpret_cast<byte*>(&box.type), endian_);
// The box length is encoded as a uint32_t by default, but the special value 1 means
// that it's a uint64_t.
uint64_t box_length = getLong(reinterpret_cast<byte*>(&hdrbuf[0]), endian_);
uint32_t box_type = getLong(reinterpret_cast<byte*>(&hdrbuf[sizeof(uint32_t)]), endian_);
bool bLF = true;

if ( bTrace ) {
bLF = true;
out << indent(depth) << "Exiv2::BmffImage::boxHandler: " << toAscii(box.type)
<< Internal::stringFormat(" %8ld->%u ", address, box.length);
out << indent(depth) << "Exiv2::BmffImage::boxHandler: " << toAscii(box_type)
<< Internal::stringFormat(" %8ld->%u ", address, box_length);
}

if (box.length == 1) {
if (box_length == 1) {
// The box size is encoded as a uint64_t, so we need to read another 8 bytes.
hdrsize += 8;
enforce(hdrsize <= static_cast<size_t>(pbox_end - address), Exiv2::kerCorruptedMetadata);
DataBuf data(8);
io_->read(data.pData_, data.size_);
const uint64_t sz = getULongLong(data.pData_, endian_);
// Check that `sz` is safe to cast to `long`.
enforce(sz <= static_cast<uint64_t>(std::numeric_limits<long>::max()),
Exiv2::kerCorruptedMetadata);
result = static_cast<long>(sz);
// sanity check
if (result < 8 || result > static_cast<long>(io_->size()) - address) {
result = static_cast<long>(io_->size());
box.length = result;
} else {
box.length = static_cast<long>(io_->size() - address);
}
box_length = getULongLong(data.pData_, endian_);
}

// read data in box and restore file position
long restore = io_->tell();
enforce(box.length >= hdrsize, Exiv2::kerCorruptedMetadata);
enforce(box.length - hdrsize <= static_cast<size_t>(pbox_end - restore), Exiv2::kerCorruptedMetadata);
DataBuf data(box.length - hdrsize);
enforce(box_length >= hdrsize, Exiv2::kerCorruptedMetadata);
enforce(box_length - hdrsize <= static_cast<size_t>(pbox_end - restore), Exiv2::kerCorruptedMetadata);
DataBuf data(box_length - hdrsize);
const long box_end = restore + data.size_;
io_->read(data.pData_, data.size_);
io_->seek(restore, BasicIo::beg);
Expand All @@ -251,15 +238,15 @@ namespace Exiv2
uint8_t version = 0;
uint32_t flags = 0;

if (fullBox(box.type)) {
if (fullBox(box_type)) {
enforce(data.size_ - skip >= 4, Exiv2::kerCorruptedMetadata);
flags = getLong(data.pData_ + skip, endian_); // version/flags
version = static_cast<int8_t>(flags) >> 24;
version &= 0x00ffffff;
skip += 4;
}

switch (box.type) {
switch (box_type) {
case TAG_ftyp: {
enforce(data.size_ >= 4, Exiv2::kerCorruptedMetadata);
fileType_ = getLong(data.pData_, endian_);
Expand Down Expand Up @@ -320,11 +307,11 @@ namespace Exiv2
bLF = false;
}
io_->seek(skip, BasicIo::cur);
while (io_->tell() < static_cast<long>((address + box.length))) {
while (io_->tell() < static_cast<long>((address + box_length))) {
io_->seek(boxHandler(out,option,box_end,depth + 1), BasicIo::beg);
}
// post-process meta box to recover Exif and XMP
if (box.type == TAG_meta) {
if (box_type == TAG_meta) {
if ( ilocs_.find(exifID_) != ilocs_.end()) {
const Iloc& iloc = ilocs_.find(exifID_)->second;
if ( bTrace ) {
Expand Down Expand Up @@ -362,13 +349,13 @@ namespace Exiv2
uint32_t itemCount = version < 2 ? getShort(data.pData_ + skip, endian_)
: getLong(data.pData_ + skip, endian_);
skip += version < 2 ? 2 : 4;
if (itemCount && itemCount < box.length / 14 && offsetSize == 4 && lengthSize == 4 &&
((box.length - 16) % itemCount) == 0) {
if (itemCount && itemCount < box_length / 14 && offsetSize == 4 && lengthSize == 4 &&
((box_length - 16) % itemCount) == 0) {
if ( bTrace ) {
out << std::endl;
bLF = false;
}
long step = (box.length - 16) / itemCount; // length of data per item.
long step = (box_length - 16) / itemCount; // length of data per item.
long base = skip;
for (uint32_t i = 0; i < itemCount; i++) {
skip = base + i * step; // move in 14, 16 or 18 byte steps
Expand Down Expand Up @@ -416,8 +403,7 @@ namespace Exiv2
// 12.1.5.2
case TAG_colr: {
if (data.size_ >=
static_cast<long>(skip + 4 +
sizeof(box))) { // .____.HLino..__mntrR 2 0 0 0 0 12 72 76 105 110 111 2 16 ...
static_cast<long>(skip + 4 + 8)) { // .____.HLino..__mntrR 2 0 0 0 0 12 72 76 105 110 111 2 16 ...
// https://www.ics.uci.edu/~dan/class/267/papers/jpeg2000.pdf
uint8_t meth = data.pData_[skip+0];
uint8_t prec = data.pData_[skip+1];
Expand Down Expand Up @@ -445,49 +431,51 @@ namespace Exiv2
bLF = false;
}
if (name == "cano") {
while (io_->tell() < static_cast<long>(address + box.length)) {
while (io_->tell() < static_cast<long>(address + box_length)) {
io_->seek(boxHandler(out,option,box_end,depth + 1), BasicIo::beg);
}
} else if ( name == "xmp" ) {
parseXmp(box.length,io_->tell());
parseXmp(box_length,io_->tell());
}
} break;

case TAG_cmt1:
parseTiff(Internal::Tag::root, box.length);
parseTiff(Internal::Tag::root, box_length);
break;
case TAG_cmt2:
parseTiff(Internal::Tag::cmt2, box.length);
parseTiff(Internal::Tag::cmt2, box_length);
break;
case TAG_cmt3:
parseTiff(Internal::Tag::cmt3, box.length);
parseTiff(Internal::Tag::cmt3, box_length);
break;
case TAG_cmt4:
parseTiff(Internal::Tag::cmt4, box.length);
parseTiff(Internal::Tag::cmt4, box_length);
break;
case TAG_exif:
parseTiff(Internal::Tag::root, box.length,address+8);
parseTiff(Internal::Tag::root, box_length,address+8);
break;
case TAG_xml:
parseXmp(box.length,io_->tell());
parseXmp(box_length,io_->tell());
break;

default: break ; /* do nothing */
}
if ( bLF&& bTrace) out << std::endl;

// return address of next box
result = (address + box.length);
result = (address + box_length);

return result;
}

void BmffImage::parseTiff(uint32_t root_tag, uint32_t length,uint32_t start)
void BmffImage::parseTiff(uint32_t root_tag, uint64_t length,uint64_t start)
{
// read and parse exif data
long restore = io_->tell();
DataBuf exif(length);
io_->seek(start,BasicIo::beg);
enforce(length <= std::numeric_limits<long>::max(), kerCorruptedMetadata);
DataBuf exif(static_cast<long>(length));
enforce(start <= std::numeric_limits<long>::max(), kerCorruptedMetadata);
io_->seek(static_cast<long>(start),BasicIo::beg);
if ( exif.size_ > 8 && io_->read(exif.pData_,exif.size_) == exif.size_ ) {
// hunt for "II" or "MM"
long eof = 0xffffffff; // impossible value for punt
Expand All @@ -506,10 +494,11 @@ namespace Exiv2
io_->seek(restore,BasicIo::beg);
}

void BmffImage::parseTiff(uint32_t root_tag, uint32_t length)
void BmffImage::parseTiff(uint32_t root_tag, uint64_t length)
{
if (length > 8) {
DataBuf data(length - 8);
enforce(length - 8 <= std::numeric_limits<long>::max(), kerCorruptedMetadata);
DataBuf data(static_cast<long>(length - 8));
long bufRead = io_->read(data.pData_, data.size_);

if (io_->error())
Expand All @@ -523,15 +512,17 @@ namespace Exiv2
}
}

void BmffImage::parseXmp(uint32_t length,uint32_t start)
void BmffImage::parseXmp(uint64_t length,uint64_t start)
{
if (length > 8) {
long restore = io_->tell() ;
io_->seek(start,BasicIo::beg);
enforce(start <= std::numeric_limits<long>::max(), kerCorruptedMetadata);
io_->seek(static_cast<long>(start),BasicIo::beg);

DataBuf xmp(length+1);
enforce(length < std::numeric_limits<long>::max(), kerCorruptedMetadata);
DataBuf xmp(static_cast<long>(length+1));
xmp.pData_[length]=0 ; // ensure xmp is null terminated!
if ( io_->read(xmp.pData_, length) != length )
if ( io_->read(xmp.pData_, length) != static_cast<long>(length) )
throw Error(kerInputDataReadFailed);
if ( io_->error() )
throw Error(kerFailedToReadImageData);
Expand Down

0 comments on commit 02d4ef2

Please sign in to comment.