From b9afa20e4751ccc4fc6ffbd9d3045d3119784c47 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Thu, 13 Aug 2026 12:12:18 -0400 Subject: [PATCH 1/4] Fix settings upgrade on iOS and tvOS Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- ....Configuration.ConfigurationManager.csproj | 1 + .../System/Configuration/AppleApplication.cs | 39 +++ .../System/Configuration/ClientConfigPaths.cs | 40 ++- .../LocalFileSettingsProvider.cs | 227 +++++++++++++++--- .../LocalFileSettingsProviderTests.cs | 218 +++++++++++++++++ src/native/libs/System.Native/entrypoints.c | 1 + .../libs/System.Native/pal_environment.c | 5 + .../libs/System.Native/pal_environment.h | 2 + .../libs/System.Native/pal_environment.m | 7 + 9 files changed, 500 insertions(+), 40 deletions(-) create mode 100644 src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/AppleApplication.cs diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj b/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj index 7e2a56b0409616..d732572033577f 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj @@ -20,6 +20,7 @@ + diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/AppleApplication.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/AppleApplication.cs new file mode 100644 index 00000000000000..e0629207e7c8e6 --- /dev/null +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/AppleApplication.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +namespace System.Configuration +{ + internal static partial class AppleApplication + { + internal static string GetMainBundleIdentifier() + { + IntPtr identifier = Interop.GetMainBundleIdentifier(); + if (identifier == IntPtr.Zero) + { + return null; + } + + try + { + // Apple bundle identifiers are restricted to ASCII letters, digits, periods, and hyphens. + return Marshal.PtrToStringAnsi(identifier); + } + finally + { + Interop.Free(identifier); + } + } + + private static partial class Interop + { + [LibraryImport("libSystem.Native", EntryPoint = "SystemNative_Free")] + internal static partial void Free(IntPtr ptr); + + [LibraryImport("libSystem.Native", EntryPoint = "SystemNative_GetMainBundleIdentifier")] + internal static partial IntPtr GetMainBundleIdentifier(); + } + } +} diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs index f8a034252762c7..9fc88227b89bb3 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs @@ -22,6 +22,7 @@ internal sealed class ClientConfigPaths private const string StrongNameDesc = "StrongName"; private const string UrlDesc = "Url"; private const string PathDesc = "Path"; + private const string BundleIdentifierDesc = "BundleIdentifier"; private static ClientConfigPaths s_current; private static volatile bool s_currentIncludesUserConfig; @@ -142,10 +143,17 @@ private ClientConfigPaths(string exePath, bool includeUserConfig) string applicationUriLower = !string.IsNullOrEmpty(ApplicationUri) ? ApplicationUri.ToLowerInvariant() : null; - string hashSuffix = GetTypeAndHashSuffix(applicationUriLower, isSingleFile); + bool isAppleMobile = IsAlwaysSandboxedAppleMobile(); + string bundleIdentifier = isAppleMobile ? AppleApplication.GetMainBundleIdentifier() : null; + string hashSuffix = GetApplicationIdentitySuffix(applicationUriLower, isSingleFile, isAppleMobile, bundleIdentifier); string part2 = !string.IsNullOrEmpty(namePrefix) && !string.IsNullOrEmpty(hashSuffix) ? namePrefix + hashSuffix : null; + LegacyConfigDirectoryPrefix = isAppleMobile && + !string.IsNullOrEmpty(bundleIdentifier) && + !string.IsNullOrEmpty(namePrefix) + ? namePrefix + "_" + : null; // (3) The product version string part3 = Validate(ProductVersion, limitSize: false); @@ -191,6 +199,8 @@ private ClientConfigPaths(string exePath, bool includeUserConfig) internal string ProductVersion { get; private set; } + internal string LegacyConfigDirectoryPrefix { get; } + internal static ClientConfigPaths GetPaths(string exePath, bool includeUserConfig) { ClientConfigPaths result; @@ -231,6 +241,27 @@ private static string CombineIfValid(string path1, string path2) } } + internal static string GetApplicationIdentitySuffix( + string exePath, + bool isSingleFile, + bool isAppleMobile, + string bundleIdentifier) + { + if (isAppleMobile && !string.IsNullOrEmpty(bundleIdentifier)) + { + try + { + string hash = IdentityHelper.GetStrongHashSuitableForObjectName(bundleIdentifier); + return "_" + BundleIdentifierDesc + "_" + hash; + } + catch (PlatformNotSupportedException) + { + } + } + + return GetTypeAndHashSuffix(exePath, isSingleFile); + } + // Returns a type and hash suffix based on what used to come from app domain evidence. // The evidence we use, in priority order, is Strong Name, Url and Exe Path. If one of // these is found, we compute a SHA1 hash of it and return a suffix based on that. @@ -287,6 +318,13 @@ private static string GetTypeAndHashSuffix(string exePath, bool isSingleFile) return suffix; } + private static bool IsAlwaysSandboxedAppleMobile() + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Create("TVOS")) || + (RuntimeInformation.IsOSPlatform(OSPlatform.Create("IOS")) && + !RuntimeInformation.IsOSPlatform(OSPlatform.Create("MACCATALYST"))); + } + private void SetNamesAndVersion(Assembly exeAssembly, bool isHttp) { Type mainType = null; diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocalFileSettingsProvider.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocalFileSettingsProvider.cs index b2a2ee7085f080..a80120caf79e6e 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocalFileSettingsProvider.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocalFileSettingsProvider.cs @@ -288,63 +288,212 @@ private string GetPreviousConfigFileName(bool isRoaming) string userConfigPath = isRoaming ? ConfigurationManagerInternalFactory.Instance.ExeRoamingConfigDirectory : ConfigurationManagerInternalFactory.Instance.ExeLocalConfigDirectory; + prevConfigFile = FindPreviousConfigFile( + userConfigPath, + ConfigurationManagerInternalFactory.Instance.ExeProductVersion, + ConfigurationManagerInternalFactory.Instance.UserConfigFilename, + ClientConfigPaths.Current.LegacyConfigDirectoryPrefix); - Version currentVersion; - if (!Version.TryParse(ConfigurationManagerInternalFactory.Instance.ExeProductVersion, out currentVersion)) + // Cache for future use. + if (isRoaming) + { + _prevRoamingConfigFileName = prevConfigFile; + } + else { - return null; + _prevLocalConfigFileName = prevConfigFile; } + } + + return prevConfigFile; + } + + internal static string FindPreviousConfigFile( + string currentConfigDirectory, + string currentVersionString, + string userConfigFilename, + string legacyDirectoryPrefix) + { + if (string.IsNullOrEmpty(currentConfigDirectory) || + string.IsNullOrEmpty(userConfigFilename) || + !Version.TryParse(currentVersionString, out Version currentVersion)) + { + return null; + } + + if (string.IsNullOrEmpty(legacyDirectoryPrefix)) + { + return FindPreviousConfigFileUsingExistingBehavior( + currentConfigDirectory, + currentVersion, + userConfigFilename); + } + + DirectoryInfo currentVersionDirectory = new DirectoryInfo(currentConfigDirectory); + if (!string.Equals(currentVersionDirectory.Name, currentVersionString, StringComparison.Ordinal)) + { + return null; + } + + DirectoryInfo currentIdentityDirectory = currentVersionDirectory.Parent; + if (currentIdentityDirectory is null) + { + return null; + } - Version previousVersion = null; - DirectoryInfo previousDirectory = null; - string file = null; + string previousConfigFile = FindPreviousConfigFile( + currentIdentityDirectory, + currentVersion, + userConfigFilename, + out _); + if (previousConfigFile is not null) + { + return previousConfigFile; + } + + string stableIdentityPrefix = legacyDirectoryPrefix + "BundleIdentifier_"; + if (!IsIdentityDirectoryName(currentIdentityDirectory.Name, stableIdentityPrefix)) + { + return null; + } + + DirectoryInfo companyDirectory = currentIdentityDirectory.Parent; + if (companyDirectory is null || !companyDirectory.Exists) + { + return null; + } - DirectoryInfo parentDirectory = Directory.GetParent(userConfigPath); + Version highestVersion = null; + string highestVersionConfigFile = null; + bool highestVersionIsAmbiguous = false; - if (parentDirectory.Exists) + foreach (DirectoryInfo identityDirectory in companyDirectory.GetDirectories()) + { + if (!IsLegacyIdentityDirectoryName(identityDirectory.Name, legacyDirectoryPrefix)) { - foreach (DirectoryInfo directory in parentDirectory.GetDirectories()) - { - Version tempVersion; + continue; + } - if (Version.TryParse(directory.Name, out tempVersion) && tempVersion < currentVersion) - { - if (previousVersion == null) - { - previousVersion = tempVersion; - previousDirectory = directory; - } - else if (tempVersion > previousVersion) - { - previousVersion = tempVersion; - previousDirectory = directory; - } - } - } + string candidateConfigFile = FindPreviousConfigFile( + identityDirectory, + currentVersion, + userConfigFilename, + out Version candidateVersion); + if (candidateConfigFile is null) + { + continue; + } - if (previousDirectory != null) - { - file = Path.Combine(previousDirectory.FullName, ConfigurationManagerInternalFactory.Instance.UserConfigFilename); - } + if (highestVersion is null || candidateVersion > highestVersion) + { + highestVersion = candidateVersion; + highestVersionConfigFile = candidateConfigFile; + highestVersionIsAmbiguous = false; + } + else if (candidateVersion == highestVersion) + { + highestVersionIsAmbiguous = true; + } + } - if (File.Exists(file)) - { - prevConfigFile = file; - } + return highestVersionIsAmbiguous ? null : highestVersionConfigFile; + } + + private static string FindPreviousConfigFileUsingExistingBehavior( + string currentConfigDirectory, + Version currentVersion, + string userConfigFilename) + { + DirectoryInfo identityDirectory = Directory.GetParent(currentConfigDirectory); + if (identityDirectory is null || !identityDirectory.Exists) + { + return null; + } + + Version previousVersion = null; + DirectoryInfo previousVersionDirectory = null; + + foreach (DirectoryInfo versionDirectory in identityDirectory.GetDirectories()) + { + if (Version.TryParse(versionDirectory.Name, out Version version) && + version < currentVersion && + (previousVersion is null || version > previousVersion)) + { + previousVersion = version; + previousVersionDirectory = versionDirectory; } + } - // Cache for future use. - if (isRoaming) + if (previousVersionDirectory is null) + { + return null; + } + + string configFile = Path.Combine(previousVersionDirectory.FullName, userConfigFilename); + return File.Exists(configFile) ? configFile : null; + } + + private static string FindPreviousConfigFile( + DirectoryInfo identityDirectory, + Version currentVersion, + string userConfigFilename, + out Version previousVersion) + { + previousVersion = null; + string previousConfigFile = null; + + if (!identityDirectory.Exists) + { + return null; + } + + foreach (DirectoryInfo versionDirectory in identityDirectory.GetDirectories()) + { + if (!Version.TryParse(versionDirectory.Name, out Version version) || + version >= currentVersion || + (previousVersion is not null && version <= previousVersion)) { - _prevRoamingConfigFileName = prevConfigFile; + continue; } - else + + string configFile = Path.Combine(versionDirectory.FullName, userConfigFilename); + if (File.Exists(configFile)) { - _prevLocalConfigFileName = prevConfigFile; + previousVersion = version; + previousConfigFile = configFile; } } - return prevConfigFile; + return previousConfigFile; + } + + private static bool IsLegacyIdentityDirectoryName(string directoryName, string prefix) + { + return IsIdentityDirectoryName(directoryName, prefix + "StrongName_") || + IsIdentityDirectoryName(directoryName, prefix + "Url_") || + IsIdentityDirectoryName(directoryName, prefix + "Path_"); + } + + private static bool IsIdentityDirectoryName(string directoryName, string prefix) + { + const int Sha1Base32Length = 32; + + if (!directoryName.StartsWith(prefix, StringComparison.Ordinal) || + directoryName.Length != prefix.Length + Sha1Base32Length) + { + return false; + } + + for (int i = prefix.Length; i < directoryName.Length; i++) + { + char c = directoryName[i]; + if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '5'))) + { + return false; + } + } + + return true; } /// diff --git a/src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/LocalFileSettingsProviderTests.cs b/src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/LocalFileSettingsProviderTests.cs index 3e56c3b5340924..de168c67be0153 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/LocalFileSettingsProviderTests.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/LocalFileSettingsProviderTests.cs @@ -3,6 +3,8 @@ using System; using System.Configuration; +using System.IO; +using System.Reflection; using Xunit; namespace System.ConfigurationTests @@ -74,5 +76,221 @@ public void GetPropertyValues_DefaultValueApplied(object defaultValue) Assert.Equal(1, propertyValues.Count); Assert.Equal(defaultValue, propertyValues["Test"].PropertyValue); } + + [Fact] + public void AppleMobileIdentity_UsesStableBoundedBundleIdentifierHash() + { + const string FirstPath = "/var/containers/Bundle/Application/F67E5161-EBAA-4084-B89C-2D17C837D315/Test.app/Test.dll"; + const string SecondPath = "/var/containers/Bundle/Application/9293AD65-BCC7-453C-8D42-8902B64FF19E/Test.app/Test.dll"; + + string first = GetApplicationIdentitySuffix( + FirstPath, + isSingleFile: false, + isAppleMobile: true, + bundleIdentifier: "com.contoso.test"); + string second = GetApplicationIdentitySuffix( + SecondPath, + isSingleFile: false, + isAppleMobile: true, + bundleIdentifier: "com.contoso.test"); + + Assert.Equal(first, second); + Assert.StartsWith("_BundleIdentifier_", first); + Assert.Equal("_BundleIdentifier_".Length + 32, first.Length); + } + + [Fact] + public void NonAppleMobileIdentity_PreservesExistingBehavior() + { + const string ApplicationPath = "/Applications/Test/Test.dll"; + + string expected = GetApplicationIdentitySuffix( + ApplicationPath, + isSingleFile: false, + isAppleMobile: false, + bundleIdentifier: null); + string actual = GetApplicationIdentitySuffix( + ApplicationPath, + isSingleFile: false, + isAppleMobile: false, + bundleIdentifier: "com.contoso.ignored"); + + Assert.Equal(expected, actual); + Assert.DoesNotContain("BundleIdentifier", actual); + } + + [PlatformSpecific(TestPlatforms.iOS | TestPlatforms.tvOS)] + [Fact] + public void AppleMobileBundleIdentifier_IsAvailable() + { + Type appleApplication = typeof(LocalFileSettingsProvider).Assembly.GetType("System.Configuration.AppleApplication"); + MethodInfo getMainBundleIdentifier = appleApplication.GetMethod( + "GetMainBundleIdentifier", + BindingFlags.NonPublic | BindingFlags.Static); + + Assert.False(string.IsNullOrEmpty((string)getMainBundleIdentifier.Invoke(null, null))); + } + + [Fact] + public void FindPreviousConfigFile_StableHierarchyTakesPrecedence() + { + using var temp = new TempDirectory(); + string companyDirectory = temp.Path; + string stableIdentity = Path.Combine(companyDirectory, StableIdentityName); + string currentDirectory = CreateVersion(stableIdentity, "3.0.0.0", createConfig: false); + string expected = Path.Combine(CreateVersion(stableIdentity, "1.0.0.0"), UserConfigFilename); + CreateVersion(Path.Combine(companyDirectory, LegacyIdentityName('a')), "2.0.0.0"); + + string actual = FindPreviousConfigFile( + currentDirectory, + "3.0.0.0", + UserConfigFilename, + LegacyPrefix); + + Assert.Equal(expected, actual); + } + + [Fact] + public void FindPreviousConfigFile_WithoutLegacyPrefixPreservesExistingSelection() + { + using var temp = new TempDirectory(); + string identityDirectory = Path.Combine(temp.Path, "TestApp_Url_" + new string('a', 32)); + string currentDirectory = CreateVersion(identityDirectory, "3.0.0.0", createConfig: false); + CreateVersion(identityDirectory, "1.0.0.0"); + CreateVersion(identityDirectory, "2.0.0.0", createConfig: false); + + string actual = FindPreviousConfigFile( + currentDirectory, + "3.0.0.0", + UserConfigFilename, + legacyDirectoryPrefix: null); + + Assert.Null(actual); + } + + [Fact] + public void FindPreviousConfigFile_LegacyHierarchySelectsHighestValidPriorVersion() + { + using var temp = new TempDirectory(); + string companyDirectory = temp.Path; + string currentDirectory = CreateVersion( + Path.Combine(companyDirectory, StableIdentityName), + "4.0.0.0", + createConfig: false); + CreateVersion(Path.Combine(companyDirectory, LegacyIdentityName('a')), "1.0.0.0"); + string expected = Path.Combine( + CreateVersion(Path.Combine(companyDirectory, LegacyIdentityName('b')), "3.0.0.0"), + UserConfigFilename); + CreateVersion(Path.Combine(companyDirectory, LegacyIdentityName('c')), "4.0.0.0"); + CreateVersion(Path.Combine(companyDirectory, LegacyIdentityName('d')), "5.0.0.0"); + + string actual = FindPreviousConfigFile( + currentDirectory, + "4.0.0.0", + UserConfigFilename, + LegacyPrefix); + + Assert.Equal(expected, actual); + } + + [Fact] + public void FindPreviousConfigFile_ExcludesMalformedAndUnrelatedDirectories() + { + using var temp = new TempDirectory(); + string companyDirectory = temp.Path; + string currentDirectory = CreateVersion( + Path.Combine(companyDirectory, StableIdentityName), + "3.0.0.0", + createConfig: false); + CreateVersion(Path.Combine(companyDirectory, "OtherApp_Url_" + new string('a', 32)), "2.0.0.0"); + CreateVersion(Path.Combine(companyDirectory, LegacyPrefix + "Url_" + new string('6', 32)), "2.0.0.0"); + CreateVersion(Path.Combine(companyDirectory, LegacyPrefix + "Unknown_" + new string('a', 32)), "2.0.0.0"); + CreateVersion(Path.Combine(companyDirectory, LegacyPrefix + "Url_short"), "2.0.0.0"); + CreateVersion(Path.Combine(companyDirectory, LegacyIdentityName('a')), "not-a-version"); + CreateVersion(Path.Combine(companyDirectory, LegacyIdentityName('b')), "2.0.0.0", createConfig: false); + + string actual = FindPreviousConfigFile( + currentDirectory, + "3.0.0.0", + UserConfigFilename, + LegacyPrefix); + + Assert.Null(actual); + } + + [Fact] + public void FindPreviousConfigFile_AmbiguousHighestLegacyVersionIsNotSelected() + { + using var temp = new TempDirectory(); + string companyDirectory = temp.Path; + string currentDirectory = CreateVersion( + Path.Combine(companyDirectory, StableIdentityName), + "3.0.0.0", + createConfig: false); + CreateVersion(Path.Combine(companyDirectory, LegacyIdentityName('a')), "2.0.0.0"); + CreateVersion(Path.Combine(companyDirectory, LegacyIdentityName('b')), "2.0.0.0"); + + string actual = FindPreviousConfigFile( + currentDirectory, + "3.0.0.0", + UserConfigFilename, + LegacyPrefix); + + Assert.Null(actual); + } + + private const string LegacyPrefix = "TestApp_"; + private const string StableIdentityName = LegacyPrefix + "BundleIdentifier_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + private const string UserConfigFilename = "user.config"; + + private static string GetApplicationIdentitySuffix( + string applicationPath, + bool isSingleFile, + bool isAppleMobile, + string bundleIdentifier) + { + Type clientConfigPaths = typeof(LocalFileSettingsProvider).Assembly.GetType("System.Configuration.ClientConfigPaths"); + MethodInfo getApplicationIdentitySuffix = clientConfigPaths.GetMethod( + "GetApplicationIdentitySuffix", + BindingFlags.NonPublic | BindingFlags.Static); + + return (string)getApplicationIdentitySuffix.Invoke( + null, + new object[] { applicationPath, isSingleFile, isAppleMobile, bundleIdentifier }); + } + + private static string FindPreviousConfigFile( + string currentConfigDirectory, + string currentVersion, + string userConfigFilename, + string legacyDirectoryPrefix) + { + MethodInfo findPreviousConfigFile = typeof(LocalFileSettingsProvider).GetMethod( + "FindPreviousConfigFile", + BindingFlags.NonPublic | BindingFlags.Static, + binder: null, + new[] { typeof(string), typeof(string), typeof(string), typeof(string) }, + modifiers: null); + + return (string)findPreviousConfigFile.Invoke( + null, + new object[] { currentConfigDirectory, currentVersion, userConfigFilename, legacyDirectoryPrefix }); + } + + private static string LegacyIdentityName(char hashCharacter) + { + return LegacyPrefix + "Url_" + new string(hashCharacter, 32); + } + + private static string CreateVersion(string identityDirectory, string version, bool createConfig = true) + { + string versionDirectory = Directory.CreateDirectory(Path.Combine(identityDirectory, version)).FullName; + if (createConfig) + { + File.WriteAllText(Path.Combine(versionDirectory, UserConfigFilename), ""); + } + + return versionDirectory; + } } } diff --git a/src/native/libs/System.Native/entrypoints.c b/src/native/libs/System.Native/entrypoints.c index 78e08565015511..a8f682aef7a449 100644 --- a/src/native/libs/System.Native/entrypoints.c +++ b/src/native/libs/System.Native/entrypoints.c @@ -227,6 +227,7 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_GetPriority) DllImportEntry(SystemNative_SetPriority) DllImportEntry(SystemNative_GetCwd) + DllImportEntry(SystemNative_GetMainBundleIdentifier) DllImportEntry(SystemNative_SchedSetAffinity) DllImportEntry(SystemNative_SchedGetAffinity) DllImportEntry(SystemNative_GetProcessPath) diff --git a/src/native/libs/System.Native/pal_environment.c b/src/native/libs/System.Native/pal_environment.c index b61c8fab6c8751..842b083615aeac 100644 --- a/src/native/libs/System.Native/pal_environment.c +++ b/src/native/libs/System.Native/pal_environment.c @@ -30,3 +30,8 @@ void SystemNative_FreeEnviron(char** environment) // no op (void)environment; } + +char* SystemNative_GetMainBundleIdentifier(void) +{ + return NULL; +} diff --git a/src/native/libs/System.Native/pal_environment.h b/src/native/libs/System.Native/pal_environment.h index dee7a10f3aecdc..d6ca15d453a431 100644 --- a/src/native/libs/System.Native/pal_environment.h +++ b/src/native/libs/System.Native/pal_environment.h @@ -11,3 +11,5 @@ PALEXPORT char* SystemNative_GetEnv(const char* variable); PALEXPORT char** SystemNative_GetEnviron(void); PALEXPORT void SystemNative_FreeEnviron(char** environ); + +PALEXPORT char* SystemNative_GetMainBundleIdentifier(void); diff --git a/src/native/libs/System.Native/pal_environment.m b/src/native/libs/System.Native/pal_environment.m index 7705a63da129ac..9739427ad5a9d0 100644 --- a/src/native/libs/System.Native/pal_environment.m +++ b/src/native/libs/System.Native/pal_environment.m @@ -80,3 +80,10 @@ void SystemNative_FreeEnviron(char** environ) free(environ); } } + +char* SystemNative_GetMainBundleIdentifier(void) +{ + NSString* bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; + const char* utf8BundleIdentifier = [bundleIdentifier UTF8String]; + return utf8BundleIdentifier == NULL ? NULL : strdup(utf8BundleIdentifier); +} From f94319c2a9d10f2fdf66e7d39116690f1343a003 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Thu, 13 Aug 2026 13:28:47 -0400 Subject: [PATCH 2/4] Use OS package identities for settings paths Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2a374416-1441-4b46-aeee-27cb227d0d78 --- .../OSX/Interop.CoreFoundation.CFBundle.cs | 32 ++++++++++ .../Interop.GetCurrentPackageFamilyName.cs | 13 ++++ .../src/Interop/Interop.Libraries.cs | 11 ++++ ....Configuration.ConfigurationManager.csproj | 4 ++ .../System/Configuration/AppleApplication.cs | 46 ++++++++----- .../System/Configuration/ClientConfigPaths.cs | 59 ++++++++++++++--- .../LocalFileSettingsProvider.cs | 12 ++-- .../Configuration/WindowsApplication.cs | 43 +++++++++++++ .../LocalFileSettingsProviderTests.cs | 64 ++++++++++++------- src/native/libs/System.Native/entrypoints.c | 1 - .../libs/System.Native/pal_environment.c | 5 -- .../libs/System.Native/pal_environment.h | 2 - .../libs/System.Native/pal_environment.m | 7 -- 13 files changed, 229 insertions(+), 70 deletions(-) create mode 100644 src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFBundle.cs create mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCurrentPackageFamilyName.cs create mode 100644 src/libraries/System.Configuration.ConfigurationManager/src/Interop/Interop.Libraries.cs create mode 100644 src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/WindowsApplication.cs diff --git a/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFBundle.cs b/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFBundle.cs new file mode 100644 index 00000000000000..d2c002d0f0aa8e --- /dev/null +++ b/src/libraries/Common/src/Interop/OSX/Interop.CoreFoundation.CFBundle.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class CoreFoundation + { + internal const uint kCFStringEncodingUTF8 = 0x08000100; + + [LibraryImport(Libraries.CoreFoundationLibrary)] + internal static partial IntPtr CFBundleGetIdentifier(IntPtr bundle); + + [LibraryImport(Libraries.CoreFoundationLibrary)] + internal static partial IntPtr CFBundleGetMainBundle(); + + [LibraryImport(Libraries.CoreFoundationLibrary)] + internal static unsafe partial byte CFStringGetCString( + IntPtr value, + byte* buffer, + IntPtr bufferSize, + uint encoding); + + [LibraryImport(Libraries.CoreFoundationLibrary)] + internal static partial IntPtr CFStringGetLength(IntPtr value); + + [LibraryImport(Libraries.CoreFoundationLibrary)] + internal static partial IntPtr CFStringGetMaximumSizeForEncoding(IntPtr length, uint encoding); + } +} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCurrentPackageFamilyName.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCurrentPackageFamilyName.cs new file mode 100644 index 00000000000000..e9d3678275fbc0 --- /dev/null +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetCurrentPackageFamilyName.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { + [LibraryImport(Libraries.Kernel32)] + internal static unsafe partial int GetCurrentPackageFamilyName(uint* packageFamilyNameLength, char* packageFamilyName); + } +} diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/Interop/Interop.Libraries.cs b/src/libraries/System.Configuration.ConfigurationManager/src/Interop/Interop.Libraries.cs new file mode 100644 index 00000000000000..9704b0eacd13f7 --- /dev/null +++ b/src/libraries/System.Configuration.ConfigurationManager/src/Interop/Interop.Libraries.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +internal static partial class Interop +{ + internal static partial class Libraries + { + internal const string CoreFoundationLibrary = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"; + internal const string Kernel32 = "kernel32.dll"; + } +} diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj b/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj index d732572033577f..500cccd6e26800 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj @@ -20,6 +20,9 @@ + + + @@ -250,6 +253,7 @@ + diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/AppleApplication.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/AppleApplication.cs index e0629207e7c8e6..1a3e3ecdd9cacf 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/AppleApplication.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/AppleApplication.cs @@ -2,38 +2,52 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Runtime.InteropServices; +using System.Text; namespace System.Configuration { - internal static partial class AppleApplication + internal static class AppleApplication { - internal static string GetMainBundleIdentifier() + internal static unsafe string GetMainBundleIdentifier() { - IntPtr identifier = Interop.GetMainBundleIdentifier(); + IntPtr bundle = Interop.CoreFoundation.CFBundleGetMainBundle(); + IntPtr identifier = bundle == IntPtr.Zero + ? IntPtr.Zero + : Interop.CoreFoundation.CFBundleGetIdentifier(bundle); if (identifier == IntPtr.Zero) { return null; } - try + IntPtr length = Interop.CoreFoundation.CFStringGetLength(identifier); + long maximumByteCount = Interop.CoreFoundation.CFStringGetMaximumSizeForEncoding( + length, + Interop.CoreFoundation.kCFStringEncodingUTF8).ToInt64(); + if (maximumByteCount < 0 || maximumByteCount >= int.MaxValue) { - // Apple bundle identifiers are restricted to ASCII letters, digits, periods, and hyphens. - return Marshal.PtrToStringAnsi(identifier); + return null; } - finally + + byte[] buffer = new byte[(int)maximumByteCount + 1]; + fixed (byte* bufferPtr = buffer) { - Interop.Free(identifier); + if (Interop.CoreFoundation.CFStringGetCString( + identifier, + bufferPtr, + new IntPtr(buffer.Length), + Interop.CoreFoundation.kCFStringEncodingUTF8) == 0) + { + return null; + } } - } - private static partial class Interop - { - [LibraryImport("libSystem.Native", EntryPoint = "SystemNative_Free")] - internal static partial void Free(IntPtr ptr); + int terminator = Array.IndexOf(buffer, (byte)0); + if (terminator < 0) + { + return null; + } - [LibraryImport("libSystem.Native", EntryPoint = "SystemNative_GetMainBundleIdentifier")] - internal static partial IntPtr GetMainBundleIdentifier(); + return Encoding.UTF8.GetString(buffer, 0, terminator); } } } diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs index 9fc88227b89bb3..d9c74a47b5b950 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs @@ -23,6 +23,7 @@ internal sealed class ClientConfigPaths private const string UrlDesc = "Url"; private const string PathDesc = "Path"; private const string BundleIdentifierDesc = "BundleIdentifier"; + private const string PackageFamilyNameDesc = "PackageFamilyName"; private static ClientConfigPaths s_current; private static volatile bool s_currentIncludesUserConfig; @@ -143,17 +144,22 @@ private ClientConfigPaths(string exePath, bool includeUserConfig) string applicationUriLower = !string.IsNullOrEmpty(ApplicationUri) ? ApplicationUri.ToLowerInvariant() : null; - bool isAppleMobile = IsAlwaysSandboxedAppleMobile(); - string bundleIdentifier = isAppleMobile ? AppleApplication.GetMainBundleIdentifier() : null; - string hashSuffix = GetApplicationIdentitySuffix(applicationUriLower, isSingleFile, isAppleMobile, bundleIdentifier); + GetStableApplicationIdentity(out string stableIdentityType, out string stableIdentity); + string hashSuffix = GetApplicationIdentitySuffix( + applicationUriLower, + isSingleFile, + stableIdentityType, + stableIdentity); string part2 = !string.IsNullOrEmpty(namePrefix) && !string.IsNullOrEmpty(hashSuffix) ? namePrefix + hashSuffix : null; - LegacyConfigDirectoryPrefix = isAppleMobile && - !string.IsNullOrEmpty(bundleIdentifier) && + LegacyConfigDirectoryPrefix = !string.IsNullOrEmpty(stableIdentity) && !string.IsNullOrEmpty(namePrefix) ? namePrefix + "_" : null; + StableConfigDirectoryName = LegacyConfigDirectoryPrefix is not null + ? part2 + : null; // (3) The product version string part3 = Validate(ProductVersion, limitSize: false); @@ -201,6 +207,8 @@ private ClientConfigPaths(string exePath, bool includeUserConfig) internal string LegacyConfigDirectoryPrefix { get; } + internal string StableConfigDirectoryName { get; } + internal static ClientConfigPaths GetPaths(string exePath, bool includeUserConfig) { ClientConfigPaths result; @@ -244,15 +252,15 @@ private static string CombineIfValid(string path1, string path2) internal static string GetApplicationIdentitySuffix( string exePath, bool isSingleFile, - bool isAppleMobile, - string bundleIdentifier) + string stableIdentityType, + string stableIdentity) { - if (isAppleMobile && !string.IsNullOrEmpty(bundleIdentifier)) + if (!string.IsNullOrEmpty(stableIdentityType) && !string.IsNullOrEmpty(stableIdentity)) { try { - string hash = IdentityHelper.GetStrongHashSuitableForObjectName(bundleIdentifier); - return "_" + BundleIdentifierDesc + "_" + hash; + string hash = IdentityHelper.GetStrongHashSuitableForObjectName(stableIdentity); + return "_" + stableIdentityType + "_" + hash; } catch (PlatformNotSupportedException) { @@ -262,6 +270,37 @@ internal static string GetApplicationIdentitySuffix( return GetTypeAndHashSuffix(exePath, isSingleFile); } + private static void GetStableApplicationIdentity(out string identityType, out string identity) + { + if (IsAlwaysSandboxedAppleMobile()) + { + identityType = BundleIdentifierDesc; + identity = AppleApplication.GetMainBundleIdentifier(); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + identityType = PackageFamilyNameDesc; + try + { + identity = WindowsApplication.GetCurrentPackageFamilyName(); + } + catch (EntryPointNotFoundException) + { + identity = null; + } + } + else + { + identityType = null; + identity = null; + } + + if (string.IsNullOrEmpty(identity)) + { + identityType = null; + } + } + // Returns a type and hash suffix based on what used to come from app domain evidence. // The evidence we use, in priority order, is Strong Name, Url and Exe Path. If one of // these is found, we compute a SHA1 hash of it and return a suffix based on that. diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocalFileSettingsProvider.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocalFileSettingsProvider.cs index a80120caf79e6e..6286c4fdb9d6d4 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocalFileSettingsProvider.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/LocalFileSettingsProvider.cs @@ -292,7 +292,8 @@ private string GetPreviousConfigFileName(bool isRoaming) userConfigPath, ConfigurationManagerInternalFactory.Instance.ExeProductVersion, ConfigurationManagerInternalFactory.Instance.UserConfigFilename, - ClientConfigPaths.Current.LegacyConfigDirectoryPrefix); + ClientConfigPaths.Current.LegacyConfigDirectoryPrefix, + ClientConfigPaths.Current.StableConfigDirectoryName); // Cache for future use. if (isRoaming) @@ -312,7 +313,8 @@ internal static string FindPreviousConfigFile( string currentConfigDirectory, string currentVersionString, string userConfigFilename, - string legacyDirectoryPrefix) + string legacyDirectoryPrefix, + string stableConfigDirectoryName) { if (string.IsNullOrEmpty(currentConfigDirectory) || string.IsNullOrEmpty(userConfigFilename) || @@ -321,7 +323,8 @@ internal static string FindPreviousConfigFile( return null; } - if (string.IsNullOrEmpty(legacyDirectoryPrefix)) + if (string.IsNullOrEmpty(legacyDirectoryPrefix) || + string.IsNullOrEmpty(stableConfigDirectoryName)) { return FindPreviousConfigFileUsingExistingBehavior( currentConfigDirectory, @@ -351,8 +354,7 @@ internal static string FindPreviousConfigFile( return previousConfigFile; } - string stableIdentityPrefix = legacyDirectoryPrefix + "BundleIdentifier_"; - if (!IsIdentityDirectoryName(currentIdentityDirectory.Name, stableIdentityPrefix)) + if (!string.Equals(currentIdentityDirectory.Name, stableConfigDirectoryName, StringComparison.Ordinal)) { return null; } diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/WindowsApplication.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/WindowsApplication.cs new file mode 100644 index 00000000000000..b201896550f5ff --- /dev/null +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/WindowsApplication.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.ComponentModel; + +namespace System.Configuration +{ + internal static class WindowsApplication + { + private const int APPMODEL_ERROR_NO_PACKAGE = 15700; + private const int ERROR_INSUFFICIENT_BUFFER = 122; + private const int ERROR_SUCCESS = 0; + + internal static unsafe string GetCurrentPackageFamilyName() + { + uint length = 0; + int error = Interop.Kernel32.GetCurrentPackageFamilyName(&length, null); + if (error == APPMODEL_ERROR_NO_PACKAGE) + { + return null; + } + + if (error != ERROR_INSUFFICIENT_BUFFER) + { + throw new Win32Exception(error); + } + + char[] buffer = new char[checked((int)length)]; + fixed (char* bufferPtr = buffer) + { + error = Interop.Kernel32.GetCurrentPackageFamilyName(&length, bufferPtr); + } + + if (error != ERROR_SUCCESS) + { + throw new Win32Exception(error); + } + + return new string(buffer, 0, checked((int)length) - 1); + } + } +} diff --git a/src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/LocalFileSettingsProviderTests.cs b/src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/LocalFileSettingsProviderTests.cs index de168c67be0153..c42d1bdd417d91 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/LocalFileSettingsProviderTests.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/LocalFileSettingsProviderTests.cs @@ -77,8 +77,10 @@ public void GetPropertyValues_DefaultValueApplied(object defaultValue) Assert.Equal(defaultValue, propertyValues["Test"].PropertyValue); } - [Fact] - public void AppleMobileIdentity_UsesStableBoundedBundleIdentifierHash() + [Theory] + [InlineData("BundleIdentifier", "com.contoso.test")] + [InlineData("PackageFamilyName", "Microsoft.Windows.Photos_8wekyb3d8bbwe")] + public void StableIdentity_UsesBoundedHashAcrossInstallationPaths(string identityType, string identity) { const string FirstPath = "/var/containers/Bundle/Application/F67E5161-EBAA-4084-B89C-2D17C837D315/Test.app/Test.dll"; const string SecondPath = "/var/containers/Bundle/Application/9293AD65-BCC7-453C-8D42-8902B64FF19E/Test.app/Test.dll"; @@ -86,37 +88,38 @@ public void AppleMobileIdentity_UsesStableBoundedBundleIdentifierHash() string first = GetApplicationIdentitySuffix( FirstPath, isSingleFile: false, - isAppleMobile: true, - bundleIdentifier: "com.contoso.test"); + identityType, + identity); string second = GetApplicationIdentitySuffix( SecondPath, isSingleFile: false, - isAppleMobile: true, - bundleIdentifier: "com.contoso.test"); + identityType, + identity); Assert.Equal(first, second); - Assert.StartsWith("_BundleIdentifier_", first); - Assert.Equal("_BundleIdentifier_".Length + 32, first.Length); + Assert.StartsWith("_" + identityType + "_", first); + Assert.Equal(identityType.Length + 34, first.Length); } [Fact] - public void NonAppleMobileIdentity_PreservesExistingBehavior() + public void NoStableIdentity_PreservesExistingBehavior() { const string ApplicationPath = "/Applications/Test/Test.dll"; string expected = GetApplicationIdentitySuffix( ApplicationPath, isSingleFile: false, - isAppleMobile: false, - bundleIdentifier: null); + stableIdentityType: null, + stableIdentity: null); string actual = GetApplicationIdentitySuffix( ApplicationPath, isSingleFile: false, - isAppleMobile: false, - bundleIdentifier: "com.contoso.ignored"); + stableIdentityType: null, + stableIdentity: "com.contoso.ignored"); Assert.Equal(expected, actual); Assert.DoesNotContain("BundleIdentifier", actual); + Assert.DoesNotContain("PackageFamilyName", actual); } [PlatformSpecific(TestPlatforms.iOS | TestPlatforms.tvOS)] @@ -145,7 +148,8 @@ public void FindPreviousConfigFile_StableHierarchyTakesPrecedence() currentDirectory, "3.0.0.0", UserConfigFilename, - LegacyPrefix); + LegacyPrefix, + StableIdentityName); Assert.Equal(expected, actual); } @@ -163,7 +167,8 @@ public void FindPreviousConfigFile_WithoutLegacyPrefixPreservesExistingSelection currentDirectory, "3.0.0.0", UserConfigFilename, - legacyDirectoryPrefix: null); + legacyDirectoryPrefix: null, + stableConfigDirectoryName: null); Assert.Null(actual); } @@ -188,7 +193,8 @@ public void FindPreviousConfigFile_LegacyHierarchySelectsHighestValidPriorVersio currentDirectory, "4.0.0.0", UserConfigFilename, - LegacyPrefix); + LegacyPrefix, + StableIdentityName); Assert.Equal(expected, actual); } @@ -213,7 +219,8 @@ public void FindPreviousConfigFile_ExcludesMalformedAndUnrelatedDirectories() currentDirectory, "3.0.0.0", UserConfigFilename, - LegacyPrefix); + LegacyPrefix, + StableIdentityName); Assert.Null(actual); } @@ -234,7 +241,8 @@ public void FindPreviousConfigFile_AmbiguousHighestLegacyVersionIsNotSelected() currentDirectory, "3.0.0.0", UserConfigFilename, - LegacyPrefix); + LegacyPrefix, + StableIdentityName); Assert.Null(actual); } @@ -246,8 +254,8 @@ public void FindPreviousConfigFile_AmbiguousHighestLegacyVersionIsNotSelected() private static string GetApplicationIdentitySuffix( string applicationPath, bool isSingleFile, - bool isAppleMobile, - string bundleIdentifier) + string stableIdentityType, + string stableIdentity) { Type clientConfigPaths = typeof(LocalFileSettingsProvider).Assembly.GetType("System.Configuration.ClientConfigPaths"); MethodInfo getApplicationIdentitySuffix = clientConfigPaths.GetMethod( @@ -256,25 +264,33 @@ private static string GetApplicationIdentitySuffix( return (string)getApplicationIdentitySuffix.Invoke( null, - new object[] { applicationPath, isSingleFile, isAppleMobile, bundleIdentifier }); + new object[] { applicationPath, isSingleFile, stableIdentityType, stableIdentity }); } private static string FindPreviousConfigFile( string currentConfigDirectory, string currentVersion, string userConfigFilename, - string legacyDirectoryPrefix) + string legacyDirectoryPrefix, + string stableConfigDirectoryName) { MethodInfo findPreviousConfigFile = typeof(LocalFileSettingsProvider).GetMethod( "FindPreviousConfigFile", BindingFlags.NonPublic | BindingFlags.Static, binder: null, - new[] { typeof(string), typeof(string), typeof(string), typeof(string) }, + new[] { typeof(string), typeof(string), typeof(string), typeof(string), typeof(string) }, modifiers: null); return (string)findPreviousConfigFile.Invoke( null, - new object[] { currentConfigDirectory, currentVersion, userConfigFilename, legacyDirectoryPrefix }); + new object[] + { + currentConfigDirectory, + currentVersion, + userConfigFilename, + legacyDirectoryPrefix, + stableConfigDirectoryName + }); } private static string LegacyIdentityName(char hashCharacter) diff --git a/src/native/libs/System.Native/entrypoints.c b/src/native/libs/System.Native/entrypoints.c index a8f682aef7a449..78e08565015511 100644 --- a/src/native/libs/System.Native/entrypoints.c +++ b/src/native/libs/System.Native/entrypoints.c @@ -227,7 +227,6 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_GetPriority) DllImportEntry(SystemNative_SetPriority) DllImportEntry(SystemNative_GetCwd) - DllImportEntry(SystemNative_GetMainBundleIdentifier) DllImportEntry(SystemNative_SchedSetAffinity) DllImportEntry(SystemNative_SchedGetAffinity) DllImportEntry(SystemNative_GetProcessPath) diff --git a/src/native/libs/System.Native/pal_environment.c b/src/native/libs/System.Native/pal_environment.c index 842b083615aeac..b61c8fab6c8751 100644 --- a/src/native/libs/System.Native/pal_environment.c +++ b/src/native/libs/System.Native/pal_environment.c @@ -30,8 +30,3 @@ void SystemNative_FreeEnviron(char** environment) // no op (void)environment; } - -char* SystemNative_GetMainBundleIdentifier(void) -{ - return NULL; -} diff --git a/src/native/libs/System.Native/pal_environment.h b/src/native/libs/System.Native/pal_environment.h index d6ca15d453a431..dee7a10f3aecdc 100644 --- a/src/native/libs/System.Native/pal_environment.h +++ b/src/native/libs/System.Native/pal_environment.h @@ -11,5 +11,3 @@ PALEXPORT char* SystemNative_GetEnv(const char* variable); PALEXPORT char** SystemNative_GetEnviron(void); PALEXPORT void SystemNative_FreeEnviron(char** environ); - -PALEXPORT char* SystemNative_GetMainBundleIdentifier(void); diff --git a/src/native/libs/System.Native/pal_environment.m b/src/native/libs/System.Native/pal_environment.m index 9739427ad5a9d0..7705a63da129ac 100644 --- a/src/native/libs/System.Native/pal_environment.m +++ b/src/native/libs/System.Native/pal_environment.m @@ -80,10 +80,3 @@ void SystemNative_FreeEnviron(char** environ) free(environ); } } - -char* SystemNative_GetMainBundleIdentifier(void) -{ - NSString* bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; - const char* utf8BundleIdentifier = [bundleIdentifier UTF8String]; - return utf8BundleIdentifier == NULL ? NULL : strdup(utf8BundleIdentifier); -} From 8ab2d55a50459b83316b0c8074bb2f60aba22a53 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Thu, 13 Aug 2026 16:18:45 -0400 Subject: [PATCH 3/4] Update src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/WindowsApplication.cs Co-authored-by: Jan Kotas --- .../src/System/Configuration/WindowsApplication.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/WindowsApplication.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/WindowsApplication.cs index b201896550f5ff..432303afaf4b8f 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/WindowsApplication.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/WindowsApplication.cs @@ -26,7 +26,7 @@ internal static unsafe string GetCurrentPackageFamilyName() throw new Win32Exception(error); } - char[] buffer = new char[checked((int)length)]; + char[] buffer = new char[length]; fixed (char* bufferPtr = buffer) { error = Interop.Kernel32.GetCurrentPackageFamilyName(&length, bufferPtr); From b22ce05aea847837d1b295ffd66f2947b709051d Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Thu, 13 Aug 2026 16:18:57 -0400 Subject: [PATCH 4/4] Update src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/WindowsApplication.cs Co-authored-by: Jan Kotas --- .../src/System/Configuration/WindowsApplication.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/WindowsApplication.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/WindowsApplication.cs index 432303afaf4b8f..9d5739b0f6e003 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/WindowsApplication.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/WindowsApplication.cs @@ -37,7 +37,7 @@ internal static unsafe string GetCurrentPackageFamilyName() throw new Win32Exception(error); } - return new string(buffer, 0, checked((int)length) - 1); + return new string(buffer, 0, (int)length - 1); } } }