Skip to content

[GHSA-j9gm-c75j-xc9q] ImageSharp: TIFF CCITT T4 encoder can write past its compressed output buffer - #10271

Open
JimBobSquarePants wants to merge 1 commit into
JimBobSquarePants/advisory-improvement-10271from
JimBobSquarePants-GHSA-j9gm-c75j-xc9q
Open

JimBobSquarePants wants to merge 1 commit into
JimBobSquarePants/advisory-improvement-10271from
JimBobSquarePants-GHSA-j9gm-c75j-xc9q

Conversation

@JimBobSquarePants

Copy link
Copy Markdown

Updates

  • Affected products
  • Description

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-j9gm-c75j-xc9q

@github

github commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

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

Copilot AI balanced review requested due to automatic review settings October 9, 2026 09:17
@github-actions
github-actions Bot changed the base branch from main to JimBobSquarePants/advisory-improvement-10271 October 9, 2026 09:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The advisory prose still incorrectly states that every release through v4.1.1 is vulnerable.

1 open finding
What changed in this PR

Updates the ImageSharp advisory to reflect the v3 security-fix backport.

Changes:

  • Adds 3.2.0 and 4.1.2 fixed-version boundaries.
  • Splits affected v2/v3 and v4 ranges.
  • Updates remediation guidance.
File Description
GHSA-j9gm-c75j-xc9q.json Updates affected ranges and patched-version details.

🧠 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: TIFF CCITT T4 encoder can write past its compressed output buffer",
"details": "### Summary\n\nImageSharp's TIFF CCITT Group 3 (T4) encoder can write beyond its allocated compressed-data buffer when encoding narrow 1-bit images. The unchecked writes can corrupt process memory and terminate the process.\n\nThis report concerns only the T4 `CcittGroup3Fax` encoder path. It replaces the prior, unrelated ICC content.\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\nThe T4 encoder and its undersized buffer calculation first shipped in v2.0.0. The narrow-image exploit terminates published v2.0.0 and v4.1.1 while the same-height 64-pixel control succeeds on both. Every release through v4.1.1 retains the vulnerable allocation and unchecked bit-write structure.\n### Preconditions and impact\n\nThe affected path is reached when the application encodes 1-bit image data with `TiffCompression.CcittGroup3Fax`. This can happen when an application explicitly selects `TiffEncoder.BitsPerPixel = Bit1` and `TiffEncoder.Compression = CcittGroup3Fax`. It can also occur when an application decodes a TIFF and re-encodes it using the default `TiffEncoder`, because ImageSharp retains TIFF frame metadata including the compression and bit depth.\n\n`TiffCompressorFactory` creates `T4BitCompressor` for `CcittGroup3Fax`. `TiffCcittCompressor.Initialize` allocates `Width * rowsPerStrip` bytes, but non-modified T4 writes a 12-bit EOL before row data and an additional 12-bit EOL per row. `WriteCode` calls `BitWriterUtils.WriteBit` and `WriteZeroBit`, both of which use `Unsafe.Add` without a capacity check. Thus the encoded bit stream can exceed the allocated span.\n\nA 1-pixel-wide, 2000-row alternating bilevel image caused a fatal `System.AccessViolationException` during T4 compression. This is a memory-corruption and availability issue for applications that expose this encoding flow to attacker-controlled input.\n\n### Tested environment\n\n- Package binary: NuGet `SixLabors.ImageSharp` **4.1.1**\n- Target framework: `net8.0`\n- Runtime: .NET 8.0.30; SDK 8.0.424\n- Operating system: Debian GNU/Linux 12 (bookworm), Linux arm64, Docker\n\nNo active exploitation is known.\n\n### Reproduction\n\nIn a `net8.0` project that references the published `SixLabors.ImageSharp` 4.1.1 binary, save the following as `Program.cs`. Run `dotnet run -- exploit 2000` for the trigger and `dotnet run -- control 2000` for the control.\n\n```csharp\nusing System;\nusing System.IO;\nusing SixLabors.ImageSharp;\nusing SixLabors.ImageSharp.Formats.Tiff;\nusing SixLabors.ImageSharp.Formats.Tiff.Constants;\nusing SixLabors.ImageSharp.PixelFormats;\n\nstring mode = args.Length > 0 ? args[0] : \"exploit\";\nint width = mode == \"control\" ? 64 : 1;\nint height = args.Length > 1 ? int.Parse(args[1]) : 2000;\n\nConsole.WriteLine($\"mode={mode} width={width} height={height}\");\nusing var image = new Image<L8>(width, height);\nfor (int y = 0; y < image.Height; y++)\n for (int x = 0; x < image.Width; x++)\n image[x, y] = new L8((byte)(((x + y) & 1) == 0 ? 255 : 0));\n\nvar metadata = image.Frames.RootFrame.Metadata.GetTiffMetadata();\nmetadata.BitsPerPixel = TiffBitsPerPixel.Bit1;\nmetadata.Compression = TiffCompression.CcittGroup3Fax;\n\nusing var output = new MemoryStream();\nimage.Save(output, new TiffEncoder());\nConsole.WriteLine($\"Encoded OK: {output.Length} bytes\");\n```\n\nAgainst the published 4.1.1 package, this produced:\n\n```text\nmode=exploit width=1 height=2000\nFatal error. System.AccessViolationException: Attempted to read or write protected memory.\n at ...TiffCcittCompressor.GetWhiteTermCode(...)\n at ...T4BitCompressor.CompressStrip(...)\n```\n\nA 64-pixel-wide, 2000-row control using the same Group 3 metadata completed successfully:\n\n```text\nmode=control width=64 height=2000\nEncoded OK: 76230 bytes\n```\n\nThe direct public configuration path also triggers with:\n\n```csharp\nnew TiffEncoder\n{\n BitsPerPixel = TiffBitsPerPixel.Bit1,\n Compression = TiffCompression.CcittGroup3Fax\n};\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\nImageSharp's TIFF CCITT Group 3 (T4) encoder can write beyond its allocated compressed-data buffer when encoding narrow 1-bit images. The unchecked writes can corrupt process memory and terminate the process.\n\nThis report concerns only the T4 `CcittGroup3Fax` encoder path. It replaces the prior, unrelated ICC content.\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\nThe T4 encoder and its undersized buffer calculation first shipped in v2.0.0. The narrow-image exploit terminates published v2.0.0 and v4.1.1 while the same-height 64-pixel control succeeds on both. Every release through v4.1.1 retains the vulnerable allocation and unchecked bit-write structure.\n### Preconditions and impact\n\nThe affected path is reached when the application encodes 1-bit image data with `TiffCompression.CcittGroup3Fax`. This can happen when an application explicitly selects `TiffEncoder.BitsPerPixel = Bit1` and `TiffEncoder.Compression = CcittGroup3Fax`. It can also occur when an application decodes a TIFF and re-encodes it using the default `TiffEncoder`, because ImageSharp retains TIFF frame metadata including the compression and bit depth.\n\n`TiffCompressorFactory` creates `T4BitCompressor` for `CcittGroup3Fax`. `TiffCcittCompressor.Initialize` allocates `Width * rowsPerStrip` bytes, but non-modified T4 writes a 12-bit EOL before row data and an additional 12-bit EOL per row. `WriteCode` calls `BitWriterUtils.WriteBit` and `WriteZeroBit`, both of which use `Unsafe.Add` without a capacity check. Thus the encoded bit stream can exceed the allocated span.\n\nA 1-pixel-wide, 2000-row alternating bilevel image caused a fatal `System.AccessViolationException` during T4 compression. This is a memory-corruption and availability issue for applications that expose this encoding flow to attacker-controlled input.\n\n### Tested environment\n\n- Package binary: NuGet `SixLabors.ImageSharp` **4.1.1**\n- Target framework: `net8.0`\n- Runtime: .NET 8.0.30; SDK 8.0.424\n- Operating system: Debian GNU/Linux 12 (bookworm), Linux arm64, Docker\n\nNo active exploitation is known.\n\n### Reproduction\n\nIn a `net8.0` project that references the published `SixLabors.ImageSharp` 4.1.1 binary, save the following as `Program.cs`. Run `dotnet run -- exploit 2000` for the trigger and `dotnet run -- control 2000` for the control.\n\n```csharp\nusing System;\nusing System.IO;\nusing SixLabors.ImageSharp;\nusing SixLabors.ImageSharp.Formats.Tiff;\nusing SixLabors.ImageSharp.Formats.Tiff.Constants;\nusing SixLabors.ImageSharp.PixelFormats;\n\nstring mode = args.Length > 0 ? args[0] : \"exploit\";\nint width = mode == \"control\" ? 64 : 1;\nint height = args.Length > 1 ? int.Parse(args[1]) : 2000;\n\nConsole.WriteLine($\"mode={mode} width={width} height={height}\");\nusing var image = new Image<L8>(width, height);\nfor (int y = 0; y < image.Height; y++)\n for (int x = 0; x < image.Width; x++)\n image[x, y] = new L8((byte)(((x + y) & 1) == 0 ? 255 : 0));\n\nvar metadata = image.Frames.RootFrame.Metadata.GetTiffMetadata();\nmetadata.BitsPerPixel = TiffBitsPerPixel.Bit1;\nmetadata.Compression = TiffCompression.CcittGroup3Fax;\n\nusing var output = new MemoryStream();\nimage.Save(output, new TiffEncoder());\nConsole.WriteLine($\"Encoded OK: {output.Length} bytes\");\n```\n\nAgainst the published 4.1.1 package, this produced:\n\n```text\nmode=exploit width=1 height=2000\nFatal error. System.AccessViolationException: Attempted to read or write protected memory.\n at ...TiffCcittCompressor.GetWhiteTermCode(...)\n at ...T4BitCompressor.CompressStrip(...)\n```\n\nA 64-pixel-wide, 2000-row control using the same Group 3 metadata completed successfully:\n\n```text\nmode=control width=64 height=2000\nEncoded OK: 76230 bytes\n```\n\nThe direct public configuration path also triggers with:\n\n```csharp\nnew TiffEncoder\n{\n BitsPerPixel = TiffBitsPerPixel.Bit1,\n Compression = TiffCompression.CcittGroup3Fax\n};\n```",
@JimBobSquarePants

JimBobSquarePants commented Oct 9, 2026 •

Copy link
Copy Markdown
Author

The review is correct. I have corrected the repository advisory description. Its current updated_at is 10/09/2026 11:35:28.

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 modified to the repository advisory's actual update timestamp.

Exact JSON changes
--- a/advisories/github-reviewed/2026/10/GHSA-j9gm-c75j-xc9q/GHSA-j9gm-c75j-xc9q.json
+++ b/advisories/github-reviewed/2026/10/GHSA-j9gm-c75j-xc9q/GHSA-j9gm-c75j-xc9q.json
@@ -4 +4 @@
-  "modified": "2026-10-07T20:24:29Z",
+  "modified": "10/09/2026 11:35:28",
@@ -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\nImageSharp's TIFF CCITT Group 3 (T4) encoder can write beyond its allocated compressed-data buffer when encoding narrow 1-bit images. The unchecked writes can corrupt process memory and terminate the process.\n\nThis report concerns only the T4 `CcittGroup3Fax` encoder path. It replaces the prior, unrelated ICC content.\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\nThe T4 encoder and its undersized buffer calculation first shipped in v2.0.0. The narrow-image exploit terminates published v2.0.0 and v4.1.1 while the same-height 64-pixel control succeeds on both. Every release through v4.1.1 retains the vulnerable allocation and unchecked bit-write structure.\n### Preconditions and impact\n\nThe affected path is reached when the application encodes 1-bit image data with `TiffCompression.CcittGroup3Fax`. This can happen when an application explicitly selects `TiffEncoder.BitsPerPixel = Bit1` and `TiffEncoder.Compression = CcittGroup3Fax`. It can also occur when an application decodes a TIFF and re-encodes it using the default `TiffEncoder`, because ImageSharp retains TIFF frame metadata including the compression and bit depth.\n\n`TiffCompressorFactory` creates `T4BitCompressor` for `CcittGroup3Fax`. `TiffCcittCompressor.Initialize` allocates `Width * rowsPerStrip` bytes, but non-modified T4 writes a 12-bit EOL before row data and an additional 12-bit EOL per row. `WriteCode` calls `BitWriterUtils.WriteBit` and `WriteZeroBit`, both of which use `Unsafe.Add` without a capacity check. Thus the encoded bit stream can exceed the allocated span.\n\nA 1-pixel-wide, 2000-row alternating bilevel image caused a fatal `System.AccessViolationException` during T4 compression. This is a memory-corruption and availability issue for applications that expose this encoding flow to attacker-controlled input.\n\n### Tested environment\n\n- Package binary: NuGet `SixLabors.ImageSharp` **4.1.1**\n- Target framework: `net8.0`\n- Runtime: .NET 8.0.30; SDK 8.0.424\n- Operating system: Debian GNU/Linux 12 (bookworm), Linux arm64, Docker\n\nNo active exploitation is known.\n\n### Reproduction\n\nIn a `net8.0` project that references the published `SixLabors.ImageSharp` 4.1.1 binary, save the following as `Program.cs`. Run `dotnet run -- exploit 2000` for the trigger and `dotnet run -- control 2000` for the control.\n\n```csharp\nusing System;\nusing System.IO;\nusing SixLabors.ImageSharp;\nusing SixLabors.ImageSharp.Formats.Tiff;\nusing SixLabors.ImageSharp.Formats.Tiff.Constants;\nusing SixLabors.ImageSharp.PixelFormats;\n\nstring mode = args.Length > 0 ? args[0] : \"exploit\";\nint width = mode == \"control\" ? 64 : 1;\nint height = args.Length > 1 ? int.Parse(args[1]) : 2000;\n\nConsole.WriteLine($\"mode={mode} width={width} height={height}\");\nusing var image = new Image<L8>(width, height);\nfor (int y = 0; y < image.Height; y++)\n    for (int x = 0; x < image.Width; x++)\n        image[x, y] = new L8((byte)(((x + y) & 1) == 0 ? 255 : 0));\n\nvar metadata = image.Frames.RootFrame.Metadata.GetTiffMetadata();\nmetadata.BitsPerPixel = TiffBitsPerPixel.Bit1;\nmetadata.Compression = TiffCompression.CcittGroup3Fax;\n\nusing var output = new MemoryStream();\nimage.Save(output, new TiffEncoder());\nConsole.WriteLine($\"Encoded OK: {output.Length} bytes\");\n```\n\nAgainst the published 4.1.1 package, this produced:\n\n```text\nmode=exploit width=1 height=2000\nFatal error. System.AccessViolationException: Attempted to read or write protected memory.\n  at ...TiffCcittCompressor.GetWhiteTermCode(...)\n  at ...T4BitCompressor.CompressStrip(...)\n```\n\nA 64-pixel-wide, 2000-row control using the same Group 3 metadata completed successfully:\n\n```text\nmode=control width=64 height=2000\nEncoded OK: 76230 bytes\n```\n\nThe direct public configuration path also triggers with:\n\n```csharp\nnew TiffEncoder\n{\n    BitsPerPixel = TiffBitsPerPixel.Bit1,\n    Compression = TiffCompression.CcittGroup3Fax\n};\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\nImageSharp's TIFF CCITT Group 3 (T4) encoder can write beyond its allocated compressed-data buffer when encoding narrow 1-bit images. The unchecked writes can corrupt process memory and terminate the process.\n\nThis report concerns only the T4 `CcittGroup3Fax` encoder path. It replaces the prior, unrelated ICC content.\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\nThe T4 encoder and its undersized buffer calculation first shipped in v2.0.0. The narrow-image exploit terminates published v2.0.0 and v4.1.1 while the same-height 64-pixel control succeeds on both. Releases from v2.0.0 up to, but not including, v3.2.0, and v4 releases from v4.0.0 up to, but not including, v4.1.2 retain the vulnerable allocation and unchecked bit-write structure.\n### Preconditions and impact\n\nThe affected path is reached when the application encodes 1-bit image data with `TiffCompression.CcittGroup3Fax`. This can happen when an application explicitly selects `TiffEncoder.BitsPerPixel = Bit1` and `TiffEncoder.Compression = CcittGroup3Fax`. It can also occur when an application decodes a TIFF and re-encodes it using the default `TiffEncoder`, because ImageSharp retains TIFF frame metadata including the compression and bit depth.\n\n`TiffCompressorFactory` creates `T4BitCompressor` for `CcittGroup3Fax`. `TiffCcittCompressor.Initialize` allocates `Width * rowsPerStrip` bytes, but non-modified T4 writes a 12-bit EOL before row data and an additional 12-bit EOL per row. `WriteCode` calls `BitWriterUtils.WriteBit` and `WriteZeroBit`, both of which use `Unsafe.Add` without a capacity check. Thus the encoded bit stream can exceed the allocated span.\n\nA 1-pixel-wide, 2000-row alternating bilevel image caused a fatal `System.AccessViolationException` during T4 compression. This is a memory-corruption and availability issue for applications that expose this encoding flow to attacker-controlled input.\n\n### Tested environment\n\n- Package binary: NuGet `SixLabors.ImageSharp` **4.1.1**\n- Target framework: `net8.0`\n- Runtime: .NET 8.0.30; SDK 8.0.424\n- Operating system: Debian GNU/Linux 12 (bookworm), Linux arm64, Docker\n\nNo active exploitation is known.\n\n### Reproduction\n\nIn a `net8.0` project that references the published `SixLabors.ImageSharp` 4.1.1 binary, save the following as `Program.cs`. Run `dotnet run -- exploit 2000` for the trigger and `dotnet run -- control 2000` for the control.\n\n```csharp\nusing System;\nusing System.IO;\nusing SixLabors.ImageSharp;\nusing SixLabors.ImageSharp.Formats.Tiff;\nusing SixLabors.ImageSharp.Formats.Tiff.Constants;\nusing SixLabors.ImageSharp.PixelFormats;\n\nstring mode = args.Length > 0 ? args[0] : \"exploit\";\nint width = mode == \"control\" ? 64 : 1;\nint height = args.Length > 1 ? int.Parse(args[1]) : 2000;\n\nConsole.WriteLine($\"mode={mode} width={width} height={height}\");\nusing var image = new Image<L8>(width, height);\nfor (int y = 0; y < image.Height; y++)\n    for (int x = 0; x < image.Width; x++)\n        image[x, y] = new L8((byte)(((x + y) & 1) == 0 ? 255 : 0));\n\nvar metadata = image.Frames.RootFrame.Metadata.GetTiffMetadata();\nmetadata.BitsPerPixel = TiffBitsPerPixel.Bit1;\nmetadata.Compression = TiffCompression.CcittGroup3Fax;\n\nusing var output = new MemoryStream();\nimage.Save(output, new TiffEncoder());\nConsole.WriteLine($\"Encoded OK: {output.Length} bytes\");\n```\n\nAgainst the published 4.1.1 package, this produced:\n\n```text\nmode=exploit width=1 height=2000\nFatal error. System.AccessViolationException: Attempted to read or write protected memory.\n  at ...TiffCcittCompressor.GetWhiteTermCode(...)\n  at ...T4BitCompressor.CompressStrip(...)\n```\n\nA 64-pixel-wide, 2000-row control using the same Group 3 metadata completed successfully:\n\n```text\nmode=control width=64 height=2000\nEncoded OK: 76230 bytes\n```\n\nThe direct public configuration path also triggers with:\n\n```csharp\nnew TiffEncoder\n{\n    BitsPerPixel = TiffBitsPerPixel.Bit1,\n    Compression = TiffCompression.CcittGroup3Fax\n};\n```\n",

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants