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

Fix a crash in Unreal Engine games when system language is Chinese #182

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

viatearz
Copy link
Contributor

Summary

Recently some Unreal Engine games started using the Extended Virtual Addressing feature. When this feature is enabled, Unreal Engine attempts to extract the Entitlement from the executable and checks whether it contains com.apple.developer.kernel.extended-virtual-addressing.
However, due to an issue in Unreal Engine’s code, it read 8 extra bytes. These extra bytes cause a string decoding failure when the system language is set to Chinese, ultimately leading to a crash.

Explanations

FApplePlatformMemory.cpp
This function calls FIOSPlatformMisc::IsEntitlementEnabled.

bool FApplePlatformMemory::CanOverallocateVirtualMemory()
{
    static bool bHasExtendedVirtualAddressingEntitlement = FIOSPlatformMisc::IsEntitlementEnabled("com.apple.developer.kernel.extended-virtual-addressing");
    return bHasExtendedVirtualAddressingEntitlement;
}

FIOSPlatformMisc.cpp
EntitlementsData() returns an empty string, but the range is nonzero, causing stringByReplacingOccurrencesOfString to crash with a Range out of bounds error.

bool FIOSPlatformMisc::IsEntitlementEnabled(const char * EntitlementToCheck)
{
    NSString* TrueFlag = @"</key><true/>";
    NSString* EntitlementsToFind = [NSString stringWithFormat: @"%s%@" , EntitlementToCheck, TrueFlag];
    
    // Crash here!!!
    NSString* CleanedEntitlementData = [EntitlementsData() stringByReplacingOccurrencesOfString: @"\\s+" withString: @"" options: NSRegularExpressionSearch range: NSMakeRange(0, EntitlementsToFind.length)];

    // ...
}

IOSPlatformMisc.cpp
It reads 8 extra bytes, which causes a decoding failure and returns an empty string.

typedef struct __Blob {
    uint32_t magic;
    uint32_t length;
    char data[];
} CS_GenericBlob;

extern NSString *EntitlementsData(void)
{
    // ...
	
    CS_GenericBlob blob;
    Count = fread(&blob, sizeof(CS_GenericBlob), 1, file);
	
    if (__builtin_bswap32(blob.magic) == 0xfade7171)
    {
        uint32 blobLength = ntohl(blob.length);
        char data[blobLength];
        fread(&data[0], sizeof(char) * blobLength, 1, file);
        NSString *stringFromData = [NSString stringWithFormat: @"%s", data];
        NSLog(@"%@", stringFromData);
        fclose(file);
        return stringFromData;
    }
	
    // ...
}

Why has it read 8 extra bytes?
The blob.length represents the total length of CS_GenericBlob, not the data array.
The actual length of the data array should be blob.length - 8 bytes.

Why does it return an empty string?
Expected data: <plist>...</plist>
Actual data read: <plist>...</plist>\xfa\xde\x71\x72
When we pass the data to stringWithFormat, it uses the encoding returned by CFStringGetSystemEncoding() to decode it. When the system language is set to Chinese, it will use CFStringEncodingMacChineseSimp, but the decoding fails, returning an empty string.

Why doesn’t the crash happen on iOS?
On iOS, CFStringGetSystemEncoding() always returns 0 regardless of the system language, so it works fine. On macOS, CFStringGetSystemEncoding() returns different values depending on the system language.

How to Fix

Hook the string replacement function. If the source string is empty, return immediately to prevent a Range out of bounds error.

@viatearz
Copy link
Contributor Author

also related: PlayCover/PlayCover#1755

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.

1 participant