Skip to content

Commit 3139a8a

Browse files
Merge pull request #2077 from SixLabors/js/decode-sanitation
Add missing decoder sanitation checks
2 parents 923c5d2 + 911d8d7 commit 3139a8a

File tree

8 files changed

+228
-75
lines changed

8 files changed

+228
-75
lines changed

src/ImageSharp/Formats/Gif/GifDecoderCore.cs

+15-3
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,11 @@ public IImageInfo Identify(BufferedReadStream stream, CancellationToken cancella
221221
/// </summary>
222222
private void ReadGraphicalControlExtension()
223223
{
224-
this.stream.Read(this.buffer, 0, 6);
224+
int bytesRead = this.stream.Read(this.buffer, 0, 6);
225+
if (bytesRead != 6)
226+
{
227+
GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the graphic control extension");
228+
}
225229

226230
this.graphicsControlExtension = GifGraphicControlExtension.Parse(this.buffer);
227231
}
@@ -231,7 +235,11 @@ private void ReadGraphicalControlExtension()
231235
/// </summary>
232236
private void ReadImageDescriptor()
233237
{
234-
this.stream.Read(this.buffer, 0, 9);
238+
int bytesRead = this.stream.Read(this.buffer, 0, 9);
239+
if (bytesRead != 9)
240+
{
241+
GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the image descriptor");
242+
}
235243

236244
this.imageDescriptor = GifImageDescriptor.Parse(this.buffer);
237245
if (this.imageDescriptor.Height == 0 || this.imageDescriptor.Width == 0)
@@ -245,7 +253,11 @@ private void ReadImageDescriptor()
245253
/// </summary>
246254
private void ReadLogicalScreenDescriptor()
247255
{
248-
this.stream.Read(this.buffer, 0, 7);
256+
int bytesRead = this.stream.Read(this.buffer, 0, 7);
257+
if (bytesRead != 7)
258+
{
259+
GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the logical screen descriptor");
260+
}
249261

250262
this.logicalScreenDescriptor = GifLogicalScreenDescriptor.Parse(this.buffer);
251263
}

src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

+47-13
Original file line numberDiff line numberDiff line change
@@ -228,28 +228,41 @@ public void LoadTables(byte[] tableBytes, HuffmanScanDecoder huffmanScanDecoder)
228228
this.Metadata = new ImageMetadata();
229229
this.QuantizationTables = new Block8x8F[4];
230230
this.scanDecoder = huffmanScanDecoder;
231+
232+
if (tableBytes.Length < 4)
233+
{
234+
JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read marker");
235+
}
236+
231237
using var ms = new MemoryStream(tableBytes);
232238
using var stream = new BufferedReadStream(this.Configuration, ms);
233239

234240
// Check for the Start Of Image marker.
235-
stream.Read(this.markerBuffer, 0, 2);
241+
int bytesRead = stream.Read(this.markerBuffer, 0, 2);
236242
var fileMarker = new JpegFileMarker(this.markerBuffer[1], 0);
237243
if (fileMarker.Marker != JpegConstants.Markers.SOI)
238244
{
239245
JpegThrowHelper.ThrowInvalidImageContentException("Missing SOI marker.");
240246
}
241247

242248
// Read next marker.
243-
stream.Read(this.markerBuffer, 0, 2);
244-
byte marker = this.markerBuffer[1];
245-
fileMarker = new JpegFileMarker(marker, (int)stream.Position - 2);
249+
bytesRead = stream.Read(this.markerBuffer, 0, 2);
250+
fileMarker = new JpegFileMarker(this.markerBuffer[1], (int)stream.Position - 2);
246251

247252
while (fileMarker.Marker != JpegConstants.Markers.EOI || (fileMarker.Marker == JpegConstants.Markers.EOI && fileMarker.Invalid))
248253
{
249254
if (!fileMarker.Invalid)
250255
{
251256
// Get the marker length.
252-
int remaining = this.ReadUint16(stream) - 2;
257+
int markerContentByteSize = this.ReadUint16(stream) - 2;
258+
259+
// Check whether stream actually has enought bytes to read
260+
// markerContentByteSize is always positive so we cast
261+
// to uint to avoid sign extension
262+
if (stream.RemainingBytes < (uint)markerContentByteSize)
263+
{
264+
JpegThrowHelper.ThrowNotEnoughBytesForMarker(fileMarker.Marker);
265+
}
253266

254267
switch (fileMarker.Marker)
255268
{
@@ -259,21 +272,26 @@ public void LoadTables(byte[] tableBytes, HuffmanScanDecoder huffmanScanDecoder)
259272
case JpegConstants.Markers.RST7:
260273
break;
261274
case JpegConstants.Markers.DHT:
262-
this.ProcessDefineHuffmanTablesMarker(stream, remaining);
275+
this.ProcessDefineHuffmanTablesMarker(stream, markerContentByteSize);
263276
break;
264277
case JpegConstants.Markers.DQT:
265-
this.ProcessDefineQuantizationTablesMarker(stream, remaining);
278+
this.ProcessDefineQuantizationTablesMarker(stream, markerContentByteSize);
266279
break;
267280
case JpegConstants.Markers.DRI:
268-
this.ProcessDefineRestartIntervalMarker(stream, remaining);
281+
this.ProcessDefineRestartIntervalMarker(stream, markerContentByteSize);
269282
break;
270283
case JpegConstants.Markers.EOI:
271284
return;
272285
}
273286
}
274287

275288
// Read next marker.
276-
stream.Read(this.markerBuffer, 0, 2);
289+
bytesRead = stream.Read(this.markerBuffer, 0, 2);
290+
if (bytesRead != 2)
291+
{
292+
JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read marker");
293+
}
294+
277295
fileMarker = new JpegFileMarker(this.markerBuffer[1], 0);
278296
}
279297
}
@@ -730,7 +748,14 @@ private void ProcessApp1Marker(BufferedReadStream stream, int remaining)
730748

731749
if (ProfileResolver.IsProfile(this.temp, ProfileResolver.XmpMarker.Slice(0, ExifMarkerLength)))
732750
{
733-
int remainingXmpMarkerBytes = XmpMarkerLength - ExifMarkerLength;
751+
const int remainingXmpMarkerBytes = XmpMarkerLength - ExifMarkerLength;
752+
if (remaining < remainingXmpMarkerBytes || this.IgnoreMetadata)
753+
{
754+
// Skip the application header length.
755+
stream.Skip(remaining);
756+
return;
757+
}
758+
734759
stream.Read(this.temp, ExifMarkerLength, remainingXmpMarkerBytes);
735760
remaining -= remainingXmpMarkerBytes;
736761
if (ProfileResolver.IsProfile(this.temp, ProfileResolver.XmpMarker))
@@ -1320,8 +1345,12 @@ private void ProcessStartOfScanMarker(BufferedReadStream stream, int remaining)
13201345
component.ACHuffmanTableId = acTableIndex;
13211346
}
13221347

1323-
// 3 bytes: Progressive scan decoding data
1324-
stream.Read(this.temp, 0, 3);
1348+
// 3 bytes: Progressive scan decoding data.
1349+
int bytesRead = stream.Read(this.temp, 0, 3);
1350+
if (bytesRead != 3)
1351+
{
1352+
JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read progressive scan decoding data");
1353+
}
13251354

13261355
int spectralStart = this.temp[0];
13271356
this.scanDecoder.SpectralStart = spectralStart;
@@ -1344,7 +1373,12 @@ private void ProcessStartOfScanMarker(BufferedReadStream stream, int remaining)
13441373
[MethodImpl(InliningOptions.ShortMethod)]
13451374
private ushort ReadUint16(BufferedReadStream stream)
13461375
{
1347-
stream.Read(this.markerBuffer, 0, 2);
1376+
int bytesRead = stream.Read(this.markerBuffer, 0, 2);
1377+
if (bytesRead != 2)
1378+
{
1379+
JpegThrowHelper.ThrowInvalidImageContentException("jpeg stream does not contain enough data, could not read ushort.");
1380+
}
1381+
13481382
return BinaryPrimitives.ReadUInt16BigEndian(this.markerBuffer);
13491383
}
13501384
}

0 commit comments

Comments
 (0)