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

Handle EOF in Jpeg bit reader when data is bad to prevent DOS attack. #2516

Merged
merged 9 commits into from
Aug 30, 2023

Conversation

JimBobSquarePants
Copy link
Member

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

This specially crafted file has had the start of frame marker altered to change the dimensions to values (65503x65503) that far exceed the encoded data. Nothing seems to be able to open this but with a very small change we actually can and quickly too.

It's better that we allow decoding than explicitly throwing because there is no way to tell the difference between a maliciously crafted file with complete data from a damaged file with limited data and we already work very hard to enable decoding the latter.

I've also added a fix to allow us to actually parallel process images of this size. I discovered the iterator suffered from an overflow issue during attempts to determine what the maximum dimensions of a file Windows can handle (65500x65500) as it cannot decode a reencoded of the full dimensions with any built in applications.

I've created a branch (release/v3.0.2) based of the v3.0.1 tagged commit and cherry-picked #2501. Once merged I'll publish a release based upon that branch I'll backport this fix into release/v2.x for publishing.

@@ -212,7 +212,7 @@ public bool FindNextMarker()
private int ReadStream()
{
int value = this.badData ? 0 : this.stream.ReadByte();
if (value == -1)
if (value == -1 || this.stream.Position >= this.stream.Length)
Copy link
Member Author

Choose a reason for hiding this comment

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

Seeking to any location beyond the length of the stream is supported.

https://learn.microsoft.com/en-us/dotnet/api/system.io.stream.position?view=net-7.0#remarks

Copy link
Member Author

Choose a reason for hiding this comment

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

Turns out the fix is bad. It's somehow preventing complete data reading.

Copy link
Member Author

Choose a reason for hiding this comment

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

Proper fix implemented now.

@@ -9,6 +9,7 @@ on:
pull_request:
branches:
- main
- release/*
Copy link
Member Author

Choose a reason for hiding this comment

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

Allow the trigger of the build for PRs against release branches.

public void DecodeHang<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(JpegDecoder.Instance);
Copy link
Member Author

Choose a reason for hiding this comment

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

There's no way to test this without actually decoding the image so I've tried to use as little memory as possible.

Copy link
Member

@antonfirsov antonfirsov left a comment

Choose a reason for hiding this comment

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

LGTM, with a few recommendations. I assume we also want to land this change to main?

@@ -50,7 +50,7 @@ public static void IterateRows<T>(Configuration configuration, Rectangle rectang
int width = rectangle.Width;
int height = rectangle.Height;

int maxSteps = DivideCeil(width * height, parallelSettings.MinimumPixelsProcessedPerTask);
int maxSteps = DivideCeil(width * (long)height, parallelSettings.MinimumPixelsProcessedPerTask);
Copy link
Member

Choose a reason for hiding this comment

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

The ParallelRowIterator improvement needs its' own test case, since it's not being used by JpegDecoder. Maybe it's better to do this change in a separate (3.*-only) PR?

Copy link
Member Author

Choose a reason for hiding this comment

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

I've kept it in for simplicities sake but added tests.

@@ -212,7 +212,7 @@ public bool FindNextMarker()
private int ReadStream()
{
int value = this.badData ? 0 : this.stream.ReadByte();
if (value == -1)
if (value == -1 || (this.badData && this.data == 0 && this.stream.Position >= this.stream.Length))
Copy link
Member

@antonfirsov antonfirsov Aug 25, 2023

Choose a reason for hiding this comment

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

Can you describe the stuff behind || in a comment? Maybe you have some good insights after resolving #2516 (comment).

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

@JimBobSquarePants
Copy link
Member Author

I'll squash these commits when merging so I should be able to PR the v3.0.2 branch against main once done but if not, I'll be able to checrry pick the commit and PR that.

Copy link
Member

@antonfirsov antonfirsov left a comment

Choose a reason for hiding this comment

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

:shipit:

@JimBobSquarePants JimBobSquarePants merged commit ccd66b6 into release/v3.0.2 Aug 30, 2023
7 checks passed
@JimBobSquarePants JimBobSquarePants deleted the js/fix-jpeg-dos branch August 30, 2023 02:01
JimBobSquarePants added a commit that referenced this pull request Aug 30, 2023
…#2516)

* Handle EOF in bit reader when data is bad.

* Allow parallel processing of multi-megapixel image

* Stream seek can exceed the length of a stream

* Try triggering on release branches

* Update JpegBitReader.cs

* Skin on Win .NET 6

* All Win OS is an issue

* Address feedback

* add validation to CanIterateWithoutIntOverflow

---------

Co-authored-by: antonfirsov <antonfir@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants