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

Process terminated. Infinite recursion during resource lookup within System.Private.CoreLib - Argument_InvalidResourceCultureName #86441

Closed
stvonolf opened this issue May 18, 2023 · 10 comments · Fixed by #87411

Comments

@stvonolf
Copy link

We are getting an infinite recursion in System.Private.CoreLib while looking up a localization string via IStringLocalizer.

Runtime environment: k8s (aspnet:6.0.16-alpine3.17-amd64) - does not reproduce on Windows
.net SDK version: 7.0.302

This seems to be a very specific case - if a "-u-" is included in the locale (e.g., "ar-SA-u-NU-latn"). Other locales do not seem to trigger this issue (e.g., "ar-SA").

Example code to trigger the issue:

var locale = "ar-SA-u-NU-latn"; // can be repro also with "xx-u-xx"
var cultureInfo = new CultureInfo(locale); // throws on Windows (CultureNotFoundException), but passes on alpine
CultureInfo.CurrentCulture = cultureInfo;
CultureInfo.CurrentUICulture = cultureInfo;
// Microsoft.Extensions.Localization.IStringLocalizer
var value = this.stringLocalizer["test"]; // infinite recursion happens here

Stack Trace:

Process terminated. Infinite recursion during resource lookup within System.Private.CoreLib.  This may be a bug in System.Private.CoreLib, or potentially in certain extensibility points such as assembly resolve events or CultureInfo names.  Resource name: Argument_InvalidResourceCultureName
   at System.Environment.FailFast(System.String)
   at System.SR.InternalGetResourceString(System.String)
   at System.SR.GetResourceString(System.String)
   at System.Globalization.CultureInfo.VerifyCultureName(System.String, Boolean)
   at System.Resources.ResourceManager.GetResourceFileName(System.Globalization.CultureInfo)
   at System.Resources.ManifestBasedResourceGroveler.GrovelForResourceSet(System.Globalization.CultureInfo, System.Collections.Generic.Dictionary`2<System.String,System.Resources.ResourceSet>, Boolean, Boolean)
   at System.Resources.ResourceManager.InternalGetResourceSet(System.Globalization.CultureInfo, Boolean, Boolean)
   at System.Resources.ResourceManager.GetString(System.String, System.Globalization.CultureInfo)
   at System.SR.InternalGetResourceString(System.String)
   at System.SR.GetResourceString(System.String)
   at System.Globalization.CultureInfo.VerifyCultureName(System.String, Boolean)
   at System.Resources.ResourceManager.GetResourceFileName(System.Globalization.CultureInfo)
   at System.Resources.ManifestBasedResourceGroveler.GrovelForResourceSet(System.Globalization.CultureInfo, System.Collections.Generic.Dictionary`2<System.String,System.Resources.ResourceSet>, Boolean, Boolean)
   at System.Resources.ResourceManager.InternalGetResourceSet(System.Globalization.CultureInfo, Boolean, Boolean)
   at System.Resources.ResourceManager.GetString(System.String, System.Globalization.CultureInfo)
   at System.SR.InternalGetResourceString(System.String)
   at System.SR.GetResourceString(System.String)
   at System.Globalization.CultureInfo.GetCultureNotSupportedExceptionMessage()
   at System.Globalization.CultureInfo..ctor(System.String, Boolean)
   at System.Globalization.CultureInfo..ctor(System.String)
   at System.Reflection.RuntimeAssembly.InternalLoad(System.Runtime.CompilerServices.ObjectHandleOnStack, System.Runtime.CompilerServices.ObjectHandleOnStack, System.Runtime.CompilerServices.StackCrawlMarkHandle, Boolean, System.Runtime.CompilerServices.ObjectHandleOnStack, System.Runtime.CompilerServices.ObjectHandleOnStack)
   at System.Reflection.RuntimeAssembly.InternalLoad(System.Reflection.AssemblyName, System.Reflection.RuntimeAssembly, System.Threading.StackCrawlMark ByRef, Boolean, System.Runtime.Loader.AssemblyLoadContext)
   at System.Reflection.RuntimeAssembly.InternalGetSatelliteAssembly(System.Globalization.CultureInfo, System.Version, Boolean)
   at System.Resources.ManifestBasedResourceGroveler.InternalGetSatelliteAssembly(System.Reflection.Assembly, System.Globalization.CultureInfo, System.Version)
   at System.Resources.ManifestBasedResourceGroveler.GetSatelliteAssembly(System.Globalization.CultureInfo)
   at System.Resources.ManifestBasedResourceGroveler.GrovelForResourceSet(System.Globalization.CultureInfo, System.Collections.Generic.Dictionary`2<System.String,System.Resources.ResourceSet>, Boolean, Boolean)
   at System.Resources.ResourceManager.InternalGetResourceSet(System.Globalization.CultureInfo, Boolean, Boolean)
   at System.Resources.ResourceManager.GetString(System.String, System.Globalization.CultureInfo)
   at Microsoft.Extensions.Localization.ResourceManagerStringLocalizer.GetStringSafely(System.String, System.Globalization.CultureInfo)
   at Microsoft.Extensions.Localization.ResourceManagerStringLocalizer.get_Item(System.String, System.Object[])
   at Microsoft.Extensions.Localization.StringLocalizer`1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].get_Item(System.String, System.Object[])
@ghost ghost added the untriaged New issue has not been triaged by the area owner label May 18, 2023
@ghost
Copy link

ghost commented May 18, 2023

Tagging subscribers to this area: @dotnet/area-system-globalization
See info in area-owners.md if you want to be subscribed.

Issue Details

We are getting an infinite recursion in System.Private.CoreLib while looking up a localization string via IStringLocalizer.

Runtime environment: k8s (aspnet:6.0.16-alpine3.17-amd64) - does not reproduce on Windows
.net SDK version: 7.0.302

This seems to be a very specific case - if a "-u-" is included in the locale (e.g., "ar-SA-u-NU-latn"). Other locales do not seem to trigger this issue (e.g., "ar-SA").

Example code to trigger the issue:

var locale = "ar-SA-u-NU-latn"; // can be repro also with "xx-u-xx"
var cultureInfo = new CultureInfo(locale); // throws on Windows (CultureNotFoundException), but passes on alpine
CultureInfo.CurrentCulture = cultureInfo;
CultureInfo.CurrentUICulture = cultureInfo;
// Microsoft.Extensions.Localization.IStringLocalizer
var value = this.stringLocalizer["test"]; // infinite recursion happens here

Stack Trace:

Process terminated. Infinite recursion during resource lookup within System.Private.CoreLib.  This may be a bug in System.Private.CoreLib, or potentially in certain extensibility points such as assembly resolve events or CultureInfo names.  Resource name: Argument_InvalidResourceCultureName
   at System.Environment.FailFast(System.String)
   at System.SR.InternalGetResourceString(System.String)
   at System.SR.GetResourceString(System.String)
   at System.Globalization.CultureInfo.VerifyCultureName(System.String, Boolean)
   at System.Resources.ResourceManager.GetResourceFileName(System.Globalization.CultureInfo)
   at System.Resources.ManifestBasedResourceGroveler.GrovelForResourceSet(System.Globalization.CultureInfo, System.Collections.Generic.Dictionary`2<System.String,System.Resources.ResourceSet>, Boolean, Boolean)
   at System.Resources.ResourceManager.InternalGetResourceSet(System.Globalization.CultureInfo, Boolean, Boolean)
   at System.Resources.ResourceManager.GetString(System.String, System.Globalization.CultureInfo)
   at System.SR.InternalGetResourceString(System.String)
   at System.SR.GetResourceString(System.String)
   at System.Globalization.CultureInfo.VerifyCultureName(System.String, Boolean)
   at System.Resources.ResourceManager.GetResourceFileName(System.Globalization.CultureInfo)
   at System.Resources.ManifestBasedResourceGroveler.GrovelForResourceSet(System.Globalization.CultureInfo, System.Collections.Generic.Dictionary`2<System.String,System.Resources.ResourceSet>, Boolean, Boolean)
   at System.Resources.ResourceManager.InternalGetResourceSet(System.Globalization.CultureInfo, Boolean, Boolean)
   at System.Resources.ResourceManager.GetString(System.String, System.Globalization.CultureInfo)
   at System.SR.InternalGetResourceString(System.String)
   at System.SR.GetResourceString(System.String)
   at System.Globalization.CultureInfo.GetCultureNotSupportedExceptionMessage()
   at System.Globalization.CultureInfo..ctor(System.String, Boolean)
   at System.Globalization.CultureInfo..ctor(System.String)
   at System.Reflection.RuntimeAssembly.InternalLoad(System.Runtime.CompilerServices.ObjectHandleOnStack, System.Runtime.CompilerServices.ObjectHandleOnStack, System.Runtime.CompilerServices.StackCrawlMarkHandle, Boolean, System.Runtime.CompilerServices.ObjectHandleOnStack, System.Runtime.CompilerServices.ObjectHandleOnStack)
   at System.Reflection.RuntimeAssembly.InternalLoad(System.Reflection.AssemblyName, System.Reflection.RuntimeAssembly, System.Threading.StackCrawlMark ByRef, Boolean, System.Runtime.Loader.AssemblyLoadContext)
   at System.Reflection.RuntimeAssembly.InternalGetSatelliteAssembly(System.Globalization.CultureInfo, System.Version, Boolean)
   at System.Resources.ManifestBasedResourceGroveler.InternalGetSatelliteAssembly(System.Reflection.Assembly, System.Globalization.CultureInfo, System.Version)
   at System.Resources.ManifestBasedResourceGroveler.GetSatelliteAssembly(System.Globalization.CultureInfo)
   at System.Resources.ManifestBasedResourceGroveler.GrovelForResourceSet(System.Globalization.CultureInfo, System.Collections.Generic.Dictionary`2<System.String,System.Resources.ResourceSet>, Boolean, Boolean)
   at System.Resources.ResourceManager.InternalGetResourceSet(System.Globalization.CultureInfo, Boolean, Boolean)
   at System.Resources.ResourceManager.GetString(System.String, System.Globalization.CultureInfo)
   at Microsoft.Extensions.Localization.ResourceManagerStringLocalizer.GetStringSafely(System.String, System.Globalization.CultureInfo)
   at Microsoft.Extensions.Localization.ResourceManagerStringLocalizer.get_Item(System.String, System.Object[])
   at Microsoft.Extensions.Localization.StringLocalizer`1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].get_Item(System.String, System.Object[])
Author: stvonolf
Assignees: -
Labels:

area-System.Globalization

Milestone: -

@tarekgh
Copy link
Member

tarekgh commented May 19, 2023

Currently the extensions locale names are not handled correctly. We call ICU uloc_getName method to normalize the name which returns the form of the name that fails as it is not BCP-47 format. For example, xx-u-xx will be normalized to xx@xx=yes. I suggest for now refrain from using locales with extensions. Or manually normalize the locale name before using it.

@tarekgh tarekgh added this to the Future milestone May 19, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label May 19, 2023
@IhnatKlimchuk
Copy link

@tarekgh is there any plans to make it safe and handle this internally? It looks weird to have a .net functionality that under certain arguments get not an ArgumentException but termination of the whole process and everyone is well aware of it. For example, it can be used in vulnerabilities for "cleanup".

@stvonolf
Copy link
Author

stvonolf commented May 19, 2023

I totally agree. If the data comes from a client request, then it can be used to crash a service. @tarekgh could this be a potential vulnerability?

@tarekgh
Copy link
Member

tarekgh commented May 19, 2023

@stvonolf @IhnatKlimchuk I am keeping the issue open to track fixing it. Yes, we'll provide a fix for that. Thanks!

@IhnatKlimchuk
Copy link

IhnatKlimchuk commented May 22, 2023

FYI, looks like affected cultures can be detected by 2 tests:

  1. Create new culture from existing by name:
var initialCulture = new Culture("ar-SA-u-NU-latn");
var finalCulture = new Culture(initialCulture.Name);
// throws handable exception
  1. Affected culture will throw if CompareInfo property is initialized:
var initialCulture = new Culture("ar-SA-u-NU-latn");
var isValid = initialCulture.CompareInfo != null;
// throws handable exception

@tarekgh
Copy link
Member

tarekgh commented May 22, 2023

When creating a culture with extensions part -u-, ICU convert the name on the form something@another=value. Like xx-u-XX will be converted to xx@XX=yes. so, users can detect such cultures by looking for @.

@IhnatKlimchuk
Copy link

@tarekgh Well... that's not the only case. I was playing around and here my findings:

ar-SA-u-NU-latn -> ar-SA@numbers=latn (fails)
de-DE-u-co-phonebk -> de-DE_  (not fails, but CompareInfo throws exception, name has underscore and can't be re-created)

but here is a interesting one:

ja-Kana-t-it -> ja-Kana@t=it (fails)

Btw, tested on mcr.microsoft.com/dotnet/aspnet:6.0.16-alpine3.17-amd64 with ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false

@tarekgh
Copy link
Member

tarekgh commented May 23, 2023

@IhnatKlimchuk

ja-Kana-t-it -> ja-Kana@t=it (fails)

Why do you think this is more interesting than the regular -u- one? both -u- and -t- are extensions and have similar behavior.

I am aware of all these cases, and I'll do the fix for all of them. Thanks for the feedback.

@tarekgh
Copy link
Member

tarekgh commented Jun 10, 2023

Fixed by the attached PRs and also ported the fix to .NET 6.0 and .NET 7.0.

@tarekgh tarekgh closed this as completed Jun 10, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Jul 11, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants