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

System.Drawing.Graphics memory leak when multi-threading on Linux #53663

Closed
tsvx opened this issue Jun 3, 2021 · 4 comments
Closed

System.Drawing.Graphics memory leak when multi-threading on Linux #53663

tsvx opened this issue Jun 3, 2021 · 4 comments

Comments

@tsvx
Copy link

tsvx commented Jun 3, 2021

This is a duplicate of the bug in the libgdiplus since I do not know exactly what leads to the problem.

Description

Consider this following piece of code:

async Task DrawCollage()
{
    var jpegBytes = File.ReadAllBytes("image.jpg"); // any 2000*1500 px, 24 bpp
    using var collage = new Bitmap(1000, 1000);
    using var g = Graphics.FromImage(collage);
    //using var ms = new MemoryStream(jpegBytes);
    //using var image = Image.FromStream(ms);
    //g.DrawImage(image, 0, 0, 500, 500);
}

Note that every graphics object is disposed properly here.

Frequent serialized calls of this method on different threads lead to enormous memory consumption, even with three last lines commented out. Working Set size increases in proportion to the number of threads involved, but GC Heap does not increase.
Uncommenting every line triples the memory consumption.

When I force this method to use one dedicated thread only, no increase in memory consumption is observed.

I expect the memory should not grow as every graphic object is disposed after use.

Configuration

  • Ubuntu 18.04 x86_64
    • libgdiplus 4.4, 6.0.5
  • Ubuntu 18.04 arm64
    • libgdiplus 4.2-2
  • Debian 10 x86_64 (docker, mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim)
  • .NET Core 3.1
  • System.Drawing.Common 5.0.2

Regression?

The bug is not reproducible on Windows 10 x64 with .NET Core 3.1.

Other information

It seems something keeps being cached after dispose in the thread local storage in the libgdiplus library.

Note that the memory does not grow (no visible growth) if the considered task runs infrequently.

A complete code example to reproduce the problem is here: https://github.com/tsvx/SystemDrawingMemoryLeak
It is a work in progress (it reproduces the problem but can be simplified I think).

@dotnet-issue-labeler dotnet-issue-labeler bot added area-System.Drawing untriaged New issue has not been triaged by the area owner labels Jun 3, 2021
@ghost
Copy link

ghost commented Jun 3, 2021

Tagging subscribers to this area: @safern, @tarekgh
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

Consider this following piece of code (drawing a collage):

async Task DrawCollage()
{
    var jpegBytes = File.ReadAllBytes("image.jpg"); // any 2000*1500 px, 24 bpp
    using var collage = new Bitmap(1000, 1000);
    using var g = Graphics.FromImage(collage);
    //using var ms = new MemoryStream(jpegBytes);
    //using var image = Image.FromStream(ms);
    //g.DrawImage(image, 0, 0, 500, 500);
}

Frequent serialized calls of this method on different threads lead to enormous memory consumption, even with three last lines commented out. Working Set size increases in proportion to the number of threads involved, but GC Heap does not increase.

When I force this method to use one dedicated thread only, no increase in memory consumption is observed.

Seems like something is cached in the thread local storage in the libgdiplus library.

Configuration

  • Ubuntu 18.04 x86_64
    • libgdiplus 4.4, 6.0.5
  • Ubuntu 18.04 arm64
    • libgdiplus 4.2-2
  • Debian 10 x86_64 (docker, mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim)
  • .NET Core 3.1
  • System.Drawing.Common 5.0.2

Regression?

The bug is not reproducible on Windows 10 x64 with .NET Core 3.1.

Other information

Author: tsvx
Assignees: -
Labels:

area-System.Drawing, untriaged

Milestone: -

@ghost ghost added this to Untriaged in ML, Extensions, Globalization, etc, POD. Jun 3, 2021
tsvx pushed a commit to tsvx/SystemDrawingMemoryLeak that referenced this issue Jun 3, 2021
@tarekgh tarekgh removed the untriaged New issue has not been triaged by the area owner label Jun 3, 2021
@tarekgh tarekgh added this to the 6.0.0 milestone Jun 3, 2021
@ghost ghost moved this from Untriaged to 6.0.0 in ML, Extensions, Globalization, etc, POD. Jun 3, 2021
@colin-ife-snyk
Copy link

Hi @tsvx
Thanks for raising this issue. I'm interested to know if you think there are significant security implications of this memory leak (i.e. a malicious user could exploit it to crash an application or server), or whether it's just a performance issue?
Thanks!

@botinko
Copy link

botinko commented Jun 8, 2021

We faced the same problem. In our case, it leads to denial of service, since there are limits on memory consumption in k8s.
Any service that uses System.Drawing.Graphics for image processing is subject to such a threat.

@safern
Copy link
Member

safern commented Jul 22, 2021

@tsvx thank you for raising the issue. I did look at this and it seems like it is libgdiplus specific, there is a lot of memory that they are missing to free when disposing a graphics object.

Any service that uses System.Drawing.Graphics for image processing is subject to such a threat.

Agreed, and we have stated in our documentation that we don't recommend System.Drawing.Common on services like web servers, also it is not. thread safe.

I'm going to close this issue as it is libgdiplus specific and also because of a lot of other reasons we have decided to make System.Drawing.Common a windows only library as it is legacy and very tight to winforms. Plus there are better libraries out there for xplat memory manipulation or graphics, like SkiaSharp, ImageSharp or Microsoft.Maui.Graphics.

Please read this document for more information about this decision and let us know if you have any questions: https://github.com/dotnet/designs/blob/main/accepted/2021/system-drawing-win-only/system-drawing-win-only.md

@safern safern closed this as completed Jul 22, 2021
ML, Extensions, Globalization, etc, POD. automation moved this from 6.0.0 to Done Jul 22, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Aug 21, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants