Skiasharp: Native crash when opening files with special characters in path

Created on 30 Oct 2017  路  9Comments  路  Source: mono/SkiaSharp

Reproduced on Windows 10 using 1.59.2
Example:
c:\albums\涓婄敯闆呯編\folder.jpg

Try to open this using
var skFileStream = new SKFileStream(path);

This causes a native crash which in turn crashes the entire application.

area-SkiaSharp type-bug

Most helpful comment

A couple more observations. This also applies to diacritics, e.g. accent characters.

Additionally, trying to open a file that doesn't exist will also cause a native crash that will bring down the managed process. So I guess the root cause is that if the native layer is unable to open a file, there is currently no way to recover gracefully from that.

All 9 comments

A couple more observations. This also applies to diacritics, e.g. accent characters.

Additionally, trying to open a file that doesn't exist will also cause a native crash that will bring down the managed process. So I guess the root cause is that if the native layer is unable to open a file, there is currently no way to recover gracefully from that.

I am not able to reproduce the crash... Could you attach a stack trace?

However, I can confirm that there is an issue with special characters. The stream is not null (nor crashes), but it has no length. Somewhere the stream is failing to load. I will investigate - it may just be that the skia file stream is not that bright.

In any case, you can use a managed stream:

var stream = new SKManagedStream(File.OpenRead("images/涓婄敯闆呯編/CMYK.jpg"), true);

SkiaSharpDemo.zip

At this point, I am going to close this issue as it cannot be solved from SkiaSharp. This is a limitation in native skia and they even have a TODO:

https://github.com/google/skia/blob/chrome/m59/src/ports/SkOSFile_stdio.cpp#L68-L69

I would recommend wrapping the managed stream as this will further avoid any platform differences. The native streams aren't really meant to be used - they are rather for use by skia.

What the heck was I doing... I am reopening because I am going to fix this.
@LukePulverenti thanks for bringing this up - I have no idea what came over me to close this.

In the meantime, use a managed stream as I mentioned, I will ensure that it is fixed in the next release.

Thanks for the info, and that is what I'm doing, although correct me if I'm wrong but that will incur some overhead. Therefore I have some decision making to try and limit it to only when necessary.

Luke, for reference, attached is the information we collated from our development work on Synology.

All reported failures generated the same stack trace, so it seems to be a reasonably specific scenario.

We're still defaulting to Skia support for x86/x64 based h/w, as the combination of the latest native library with release 1.58 of the managed code has proved stable; in the context of Emby Server at least.

skia_stacktrace.txt

@LukePulverenti I did the work for you. In the next release, the path system will be clever and try to use the fastest method. Because this is only a bug on Windows, iOS, Android, macOS, etc are not affected in the least. On Windows, this is a specific case where the path contains non-ASCII characters, and thus I first check the path. If the path contains non-ASCII character, then open in .NET and wrap it, otherwise continue with the native file stream.

The current logic is pretty simple:


public static bool IsPathSupported (string path)
{
    if (PlatformConfiguration.IsWindows) {
        for (int i = 0; i < path.Length; i++) {
            if (path [i] >= byte.MaxValue)
                return false;
        }
    }

    return true;
}

public static SKStreamAsset OpenStream (string path)
{
    if (!IsPathSupported (path)) {
        return new SKManagedStream (File.OpenRead (path), true);
    } else {
        return new SKFileStream (path);
    }
}

Excellent, thanks. Just for reference, this is the check I have been using
` private static bool HasDiacritics(string text)
{
return !String.Equals(text, text.RemoveDiacritics(), StringComparison.Ordinal);
}

    public bool HasUnicodeCategory(string value, UnicodeCategory category)
    {
        foreach (var chr in value)
        {
            if (char.GetUnicodeCategory(chr) == category)
            {
                return true;
            }
        }

        return false;
    }

    private static bool RequiresSpecialCharacterHack(string path)
    {
        if (HasUnicodeCategory(path, UnicodeCategory.OtherLetter))
        {
            return true;
        }

        if (HasDiacritics(path))
        {
            return true;
        }

        return false;
    }

`

Was this page helpful?
0 / 5 - 0 ratings