Skip to content

Commit 5d0c684

Browse files
authored
Merge pull request #2084 from br3aker/dp/jpeg-marker-validation
Added sanity check for every jpeg marker
2 parents 7db4792 + 64d9146 commit 5d0c684

File tree

6 files changed

+41
-16
lines changed

6 files changed

+41
-16
lines changed

src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -315,14 +315,22 @@ internal void ParseStream(BufferedReadStream stream, HuffmanScanDecoder scanDeco
315315
if (!fileMarker.Invalid)
316316
{
317317
// Get the marker length.
318-
int remaining = this.ReadUint16(stream) - 2;
318+
int markerContentByteSize = this.ReadUint16(stream) - 2;
319+
320+
// Check whether stream actually has enought bytes to read
321+
// markerContentByteSize is always positive so we cast
322+
// to uint to avoid sign extension
323+
if (stream.RemainingBytes < (uint)markerContentByteSize)
324+
{
325+
JpegThrowHelper.ThrowNotEnoughBytesForMarker(fileMarker.Marker);
326+
}
319327

320328
switch (fileMarker.Marker)
321329
{
322330
case JpegConstants.Markers.SOF0:
323331
case JpegConstants.Markers.SOF1:
324332
case JpegConstants.Markers.SOF2:
325-
this.ProcessStartOfFrameMarker(stream, remaining, fileMarker, metadataOnly);
333+
this.ProcessStartOfFrameMarker(stream, markerContentByteSize, fileMarker, metadataOnly);
326334
break;
327335

328336
case JpegConstants.Markers.SOF5:
@@ -350,7 +358,7 @@ internal void ParseStream(BufferedReadStream stream, HuffmanScanDecoder scanDeco
350358
case JpegConstants.Markers.SOS:
351359
if (!metadataOnly)
352360
{
353-
this.ProcessStartOfScanMarker(stream, remaining);
361+
this.ProcessStartOfScanMarker(stream, markerContentByteSize);
354362
break;
355363
}
356364
else
@@ -364,41 +372,41 @@ internal void ParseStream(BufferedReadStream stream, HuffmanScanDecoder scanDeco
364372

365373
if (metadataOnly)
366374
{
367-
stream.Skip(remaining);
375+
stream.Skip(markerContentByteSize);
368376
}
369377
else
370378
{
371-
this.ProcessDefineHuffmanTablesMarker(stream, remaining);
379+
this.ProcessDefineHuffmanTablesMarker(stream, markerContentByteSize);
372380
}
373381

374382
break;
375383

376384
case JpegConstants.Markers.DQT:
377-
this.ProcessDefineQuantizationTablesMarker(stream, remaining);
385+
this.ProcessDefineQuantizationTablesMarker(stream, markerContentByteSize);
378386
break;
379387

380388
case JpegConstants.Markers.DRI:
381389
if (metadataOnly)
382390
{
383-
stream.Skip(remaining);
391+
stream.Skip(markerContentByteSize);
384392
}
385393
else
386394
{
387-
this.ProcessDefineRestartIntervalMarker(stream, remaining);
395+
this.ProcessDefineRestartIntervalMarker(stream, markerContentByteSize);
388396
}
389397

390398
break;
391399

392400
case JpegConstants.Markers.APP0:
393-
this.ProcessApplicationHeaderMarker(stream, remaining);
401+
this.ProcessApplicationHeaderMarker(stream, markerContentByteSize);
394402
break;
395403

396404
case JpegConstants.Markers.APP1:
397-
this.ProcessApp1Marker(stream, remaining);
405+
this.ProcessApp1Marker(stream, markerContentByteSize);
398406
break;
399407

400408
case JpegConstants.Markers.APP2:
401-
this.ProcessApp2Marker(stream, remaining);
409+
this.ProcessApp2Marker(stream, markerContentByteSize);
402410
break;
403411

404412
case JpegConstants.Markers.APP3:
@@ -411,20 +419,20 @@ internal void ParseStream(BufferedReadStream stream, HuffmanScanDecoder scanDeco
411419
case JpegConstants.Markers.APP10:
412420
case JpegConstants.Markers.APP11:
413421
case JpegConstants.Markers.APP12:
414-
stream.Skip(remaining);
422+
stream.Skip(markerContentByteSize);
415423
break;
416424

417425
case JpegConstants.Markers.APP13:
418-
this.ProcessApp13Marker(stream, remaining);
426+
this.ProcessApp13Marker(stream, markerContentByteSize);
419427
break;
420428

421429
case JpegConstants.Markers.APP14:
422-
this.ProcessApp14Marker(stream, remaining);
430+
this.ProcessApp14Marker(stream, markerContentByteSize);
423431
break;
424432

425433
case JpegConstants.Markers.APP15:
426434
case JpegConstants.Markers.COM:
427-
stream.Skip(remaining);
435+
stream.Skip(markerContentByteSize);
428436
break;
429437

430438
case JpegConstants.Markers.DAC:
@@ -1260,7 +1268,7 @@ private void ProcessStartOfScanMarker(BufferedReadStream stream, int remaining)
12601268
int selectorsBytes = selectorsCount * 2;
12611269
if (remaining != 4 + selectorsBytes)
12621270
{
1263-
JpegThrowHelper.ThrowBadMarker("SOS", remaining);
1271+
JpegThrowHelper.ThrowBadMarker(nameof(JpegConstants.Markers.SOS), remaining);
12641272
}
12651273

12661274
// selectorsCount*2 bytes: component index + huffman tables indices

src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ internal static class JpegThrowHelper
2525
[MethodImpl(InliningOptions.ColdPath)]
2626
public static void ThrowBadMarker(string marker, int length) => throw new InvalidImageContentException($"Marker {marker} has bad length {length}.");
2727

28+
[MethodImpl(InliningOptions.ColdPath)]
29+
public static void ThrowNotEnoughBytesForMarker(byte marker) => throw new InvalidImageContentException($"Input stream does not have enough bytes to parse declared contents of the {marker:X2} marker.");
30+
2831
[MethodImpl(InliningOptions.ColdPath)]
2932
public static void ThrowBadQuantizationTableIndex(int index) => throw new InvalidImageContentException($"Bad Quantization Table index {index}.");
3033

src/ImageSharp/IO/BufferedReadStream.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,15 @@ public override long Position
114114
/// <inheritdoc/>
115115
public override bool CanWrite { get; } = false;
116116

117+
/// <summary>
118+
/// Gets remaining byte count available to read.
119+
/// </summary>
120+
public long RemainingBytes
121+
{
122+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
123+
get => this.Length - this.Position;
124+
}
125+
117126
/// <summary>
118127
/// Gets the underlying stream.
119128
/// </summary>

tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ public partial class JpegDecoderTests
105105
TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException1693A,
106106
TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException1693B,
107107
TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824C,
108+
TestImages.Jpeg.Issues.Fuzz.NullReferenceException2085,
108109
};
109110

110111
private static readonly Dictionary<string, float> CustomToleranceValues = new()

tests/ImageSharp.Tests/TestImages.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ public static class Fuzz
292292
public const string AccessViolationException922 = "Jpg/issues/fuzz/Issue922-AccessViolationException.jpg";
293293
public const string IndexOutOfRangeException1693A = "Jpg/issues/fuzz/Issue1693-IndexOutOfRangeException-A.jpg";
294294
public const string IndexOutOfRangeException1693B = "Jpg/issues/fuzz/Issue1693-IndexOutOfRangeException-B.jpg";
295+
public const string NullReferenceException2085 = "Jpg/issues/fuzz/Issue2085-NullReferenceException.jpg";
295296
}
296297
}
297298

Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)