Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for issue #1942 #1944

Merged
merged 7 commits into from
Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 43 additions & 20 deletions src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ public sealed class IptcProfile : IDeepCloneable<IptcProfile>
{
private Collection<IptcValue> values;

private const byte IptcTagMarkerByte = 0x1c;

private const uint MaxStandardDataTagSize = 0x7FFF;

/// <summary>
/// Initializes a new instance of the <see cref="IptcProfile"/> class.
/// </summary>
Expand Down Expand Up @@ -78,7 +82,7 @@ public IEnumerable<IptcValue> Values
}

/// <inheritdoc/>
public IptcProfile DeepClone() => new IptcProfile(this);
public IptcProfile DeepClone() => new(this);

/// <summary>
/// Returns all values with the specified tag.
Expand Down Expand Up @@ -207,7 +211,7 @@ public void SetDateTimeValue(IptcTag tag, DateTimeOffset dateTimeOffset)
throw new ArgumentException("iptc tag is not a time or date type");
}

var formattedDate = tag.IsDate()
string formattedDate = tag.IsDate()
? dateTimeOffset.ToString("yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture)
: dateTimeOffset.ToString("HHmmsszzzz", System.Globalization.CultureInfo.InvariantCulture)
.Replace(":", string.Empty);
Expand All @@ -231,7 +235,7 @@ public void SetDateTimeValue(IptcTag tag, DateTimeOffset dateTimeOffset)
/// </summary>
public void UpdateData()
{
var length = 0;
int length = 0;
foreach (IptcValue value in this.Values)
{
length += value.Length + 5;
Expand All @@ -242,7 +246,24 @@ public void UpdateData()
int i = 0;
foreach (IptcValue value in this.Values)
{
this.Data[i++] = 28;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder whether we should add a little ascii table just indicating what the dataset tags look like?

// Standard DataSet Tag
// +-----------+----------------+---------------------------------------------------------------------------------+
// | Octet Pos | Name | Description |
// +==========-+================+=================================================================================+
// | 1 | Tag Marker | Is the tag marker that initiates the start of a DataSet 0x1c. |
// +-----------+----------------+---------------------------------------------------------------------------------+
// | 2 | Record Number | Octet 2 is the binary representation of the record number. Note that the |
// | | | envelope record number is always 1, and that the application records are |
// | | | numbered 2 through 6, the pre-object descriptor record is 7, the object record |
// | | | is 8, and the post - object descriptor record is 9. |
// +-----------+----------------+---------------------------------------------------------------------------------+
// | 3 | DataSet Number | Octet 3 is the binary representation of the DataSet number. |
// +-----------+----------------+---------------------------------------------------------------------------------+
// | 4 and 5 | Data Field | Octets 4 and 5, taken together, are the binary count of the number of octets in |
// | | Octet Count | the following data field(32767 or fewer octets). Note that the value of bit 7 of|
// | | | octet 4(most significant bit) always will be 0. |
// +-----------+----------------+---------------------------------------------------------------------------------+
this.Data[i++] = IptcTagMarkerByte;
this.Data[i++] = 2;
this.Data[i++] = (byte)value.Tag;
this.Data[i++] = (byte)(value.Length >> 8);
Expand All @@ -264,34 +285,36 @@ private void Initialize()

this.values = new Collection<IptcValue>();

if (this.Data == null || this.Data[0] != 0x1c)
if (this.Data == null || this.Data[0] != IptcTagMarkerByte)
{
return;
}

int i = 0;
while (i + 4 < this.Data.Length)
int offset = 0;
while (offset < this.Data.Length - 4)
{
if (this.Data[i++] != 28)
bool isValidTagMarker = this.Data[offset++] == IptcTagMarkerByte;
byte recordNumber = this.Data[offset++];
bool isValidRecordNumber = recordNumber is >= 1 and <= 9;
var tag = (IptcTag)this.Data[offset++];
bool isValidEntry = isValidTagMarker && isValidRecordNumber;

uint byteCount = BinaryPrimitives.ReadUInt16BigEndian(this.Data.AsSpan(offset, 2));
offset += 2;
if (byteCount > MaxStandardDataTagSize)
{
continue;
// Extended data set tag's are not supported.
break;
}

i++;

var tag = (IptcTag)this.Data[i++];

int count = BinaryPrimitives.ReadInt16BigEndian(this.Data.AsSpan(i, 2));
i += 2;

var iptcData = new byte[count];
if ((count > 0) && (i + count <= this.Data.Length))
if (isValidEntry && byteCount > 0 && (offset <= this.Data.Length - byteCount))
{
Buffer.BlockCopy(this.Data, i, iptcData, 0, count);
var iptcData = new byte[byteCount];
Buffer.BlockCopy(this.Data, offset, iptcData, 0, (int)byteCount);
this.values.Add(new IptcValue(tag, iptcData, false));
}

i += count;
offset += (int)byteCount;
}
}
}
Expand Down
103 changes: 50 additions & 53 deletions src/ImageSharp/Metadata/Profiles/IPTC/IptcTagExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,60 +13,57 @@ public static class IptcTagExtensions
/// </summary>
/// <param name="tag">The tag to check the max length for.</param>
/// <returns>The maximum length.</returns>
public static int MaxLength(this IptcTag tag)
public static int MaxLength(this IptcTag tag) => tag switch
{
return tag switch
{
IptcTag.RecordVersion => 2,
IptcTag.ObjectType => 67,
IptcTag.ObjectAttribute => 68,
IptcTag.Name => 64,
IptcTag.EditStatus => 64,
IptcTag.EditorialUpdate => 2,
IptcTag.Urgency => 1,
IptcTag.SubjectReference => 236,
IptcTag.Category => 3,
IptcTag.SupplementalCategories => 32,
IptcTag.FixtureIdentifier => 32,
IptcTag.Keywords => 64,
IptcTag.LocationCode => 3,
IptcTag.LocationName => 64,
IptcTag.ReleaseDate => 8,
IptcTag.ReleaseTime => 11,
IptcTag.ExpirationDate => 8,
IptcTag.ExpirationTime => 11,
IptcTag.SpecialInstructions => 256,
IptcTag.ActionAdvised => 2,
IptcTag.ReferenceService => 10,
IptcTag.ReferenceDate => 8,
IptcTag.ReferenceNumber => 8,
IptcTag.CreatedDate => 8,
IptcTag.CreatedTime => 11,
IptcTag.DigitalCreationDate => 8,
IptcTag.DigitalCreationTime => 11,
IptcTag.OriginatingProgram => 32,
IptcTag.ProgramVersion => 10,
IptcTag.ObjectCycle => 1,
IptcTag.Byline => 32,
IptcTag.BylineTitle => 32,
IptcTag.City => 32,
IptcTag.SubLocation => 32,
IptcTag.ProvinceState => 32,
IptcTag.CountryCode => 3,
IptcTag.Country => 64,
IptcTag.OriginalTransmissionReference => 32,
IptcTag.Headline => 256,
IptcTag.Credit => 32,
IptcTag.Source => 32,
IptcTag.CopyrightNotice => 128,
IptcTag.Contact => 128,
IptcTag.Caption => 2000,
IptcTag.CaptionWriter => 32,
IptcTag.ImageType => 2,
IptcTag.ImageOrientation => 1,
_ => 256
};
}
IptcTag.RecordVersion => 2,
IptcTag.ObjectType => 67,
IptcTag.ObjectAttribute => 68,
IptcTag.Name => 64,
IptcTag.EditStatus => 64,
IptcTag.EditorialUpdate => 2,
IptcTag.Urgency => 1,
IptcTag.SubjectReference => 236,
IptcTag.Category => 3,
IptcTag.SupplementalCategories => 32,
IptcTag.FixtureIdentifier => 32,
IptcTag.Keywords => 64,
IptcTag.LocationCode => 3,
IptcTag.LocationName => 64,
IptcTag.ReleaseDate => 8,
IptcTag.ReleaseTime => 11,
IptcTag.ExpirationDate => 8,
IptcTag.ExpirationTime => 11,
IptcTag.SpecialInstructions => 256,
IptcTag.ActionAdvised => 2,
IptcTag.ReferenceService => 10,
IptcTag.ReferenceDate => 8,
IptcTag.ReferenceNumber => 8,
IptcTag.CreatedDate => 8,
IptcTag.CreatedTime => 11,
IptcTag.DigitalCreationDate => 8,
IptcTag.DigitalCreationTime => 11,
IptcTag.OriginatingProgram => 32,
IptcTag.ProgramVersion => 10,
IptcTag.ObjectCycle => 1,
IptcTag.Byline => 32,
IptcTag.BylineTitle => 32,
IptcTag.City => 32,
IptcTag.SubLocation => 32,
IptcTag.ProvinceState => 32,
IptcTag.CountryCode => 3,
IptcTag.Country => 64,
IptcTag.OriginalTransmissionReference => 32,
IptcTag.Headline => 256,
IptcTag.Credit => 32,
IptcTag.Source => 32,
IptcTag.CopyrightNotice => 128,
IptcTag.Contact => 128,
IptcTag.Caption => 2000,
IptcTag.CaptionWriter => 32,
IptcTag.ImageType => 2,
IptcTag.ImageOrientation => 1,
_ => 256
};

/// <summary>
/// Determines if the given tag can be repeated according to the specification.
Expand Down
2 changes: 1 addition & 1 deletion src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public string Value
byte[] valueBytes;
if (this.Strict && value.Length > maxLength)
{
var cappedValue = value.Substring(0, maxLength);
string cappedValue = value.Substring(0, maxLength);
valueBytes = this.encoding.GetBytes(cappedValue);

// It is still possible that the bytes of the string exceed the limit.
Expand Down
12 changes: 12 additions & 0 deletions tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -288,5 +288,17 @@ public void Decoder_Reads_Correct_Resolution_From_Exif(bool useIdentify) => Test
Assert.Equal(72, imageInfo.Metadata.HorizontalResolution);
Assert.Equal(72, imageInfo.Metadata.VerticalResolution);
});

[Theory]
[WithFile(TestImages.Jpeg.Issues.InvalidIptcTag, PixelTypes.Rgba32)]
public void Decode_WithInvalidIptcTag_DoesNotThrowException<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
Exception ex = Record.Exception(() =>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder);
});
Assert.Null(ex);
}
}
}
1 change: 1 addition & 0 deletions tests/ImageSharp.Tests/TestImages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ public static class Issues
public const string WrongColorSpace = "Jpg/issues/Issue1732-WrongColorSpace.jpg";
public const string MalformedUnsupportedComponentCount = "Jpg/issues/issue-1900-malformed-unsupported-255-components.jpg";
public const string MultipleApp01932 = "Jpg/issues/issue-1932-app0-resolution.jpg";
public const string InvalidIptcTag = "Jpg/issues/Issue1942InvalidIptcTag.jpg";

public static class Fuzz
{
Expand Down
3 changes: 3 additions & 0 deletions tests/Images/Input/Jpg/issues/Issue1942InvalidIptcTag.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.