Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions Crashlytics/Crashlytics/Components/FIRCLSContext.m
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"

#if SWIFT_PACKAGE
@import FirebaseCrashlyticsSwift;
#elif __has_include(<FirebaseCrashlytics/FirebaseCrashlytics-Swift.h>)
#import <FirebaseCrashlytics/FirebaseCrashlytics-Swift.h>
#elif __has_include("FirebaseCrashlytics-Swift.h")
// If frameworks are not available, fall back to importing the header as it
// should be findable from a header search path pointing to the build
// directory. See #12611 for more context.
#import "FirebaseCrashlytics-Swift.h"
#endif

// The writable size is our handler stack plus whatever scratch we need. We have to use this space
// extremely carefully, however, because thread stacks always needs to be page-aligned. Only the
// first allocation is guaranteed to be page-aligned.
Expand Down Expand Up @@ -182,10 +193,12 @@

#if CLS_MACH_EXCEPTION_SUPPORTED
dispatch_group_async(group, queue, ^{
_firclsContext.readonly->machException.path =
FIRCLSContextAppendToRoot(rootPath, FIRCLSReportMachExceptionFile);

FIRCLSMachExceptionInit(&_firclsContext.readonly->machException);
FIRCLSEntitlementsReader* entitlementReader = [[FIRCLSEntitlementsReader alloc] init];
NSNumber* enableEnhancedSecurity =
[[entitlementReader readEntitlements] platformRestrictions];
if (!enableEnhancedSecurity) {
FIRCLSMachExceptionInit(&_firclsContext.readonly->machException);
}
});
#endif

Expand Down
33 changes: 33 additions & 0 deletions Crashlytics/Crashlytics/Entitlements/Entitlements.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Foundation

@objc(FIRCLSEntitlements)
public class Entitlements: NSObject {
@objc public var platformRestrictions: NSNumber?

enum Key: String {
case platformRestrictions = "com.apple.security.hardened-process.platform-restrictions"
}

init(rawValue: [String: Any]) {
for key in [Key.platformRestrictions] {
switch key {
case .platformRestrictions:
platformRestrictions = rawValue[key.rawValue] as? NSNumber
}
}
}
}
137 changes: 137 additions & 0 deletions Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Foundation
import MachO

@objc(FIRCLSEntitlementsReader)
public class EntitlementsReader: NSObject {
private struct CSMultiBlob {
var magic: UInt32
var lentgh: UInt32
var count: UInt32
}

private struct CSBlob {
var type: UInt32
var offset: UInt32
}

private enum CSMagic {
static let embeddedSignature: UInt32 = 0xFADE_0CC0
static let embededEntitlements: UInt32 = 0xFADE_7171
}

@objc public func readEntitlements() -> Entitlements? {
var executableHeader: UnsafePointer<mach_header>? = nil

for i in 0 ..< _dyld_image_count() {
let imageHeader = _dyld_get_image_header(i)

if let filetype = imageHeader?.pointee.filetype, filetype == MH_EXECUTE {
executableHeader = imageHeader
break
}
}

guard let executableHeader = executableHeader else {
print("Application image not found")
return nil
}

let is64bit = executableHeader.pointee.magic == MH_MAGIC_64 || executableHeader.pointee
.magic == MH_CIGAM_64

var curLoadCommandIterator = Int(bitPattern: executableHeader) +
(is64bit ? MemoryLayout<mach_header_64>.size : MemoryLayout<mach_header>.size)

for _ in 0 ..< executableHeader.pointee.ncmds {
guard let segmentCommand = UnsafePointer<segment_command>(
bitPattern: curLoadCommandIterator
)?.pointee else {
return nil
}

if segmentCommand.cmd == LC_CODE_SIGNATURE {
break
}
curLoadCommandIterator = curLoadCommandIterator + Int(segmentCommand.cmdsize)
}

guard let dataCommand =
UnsafePointer<linkedit_data_command>(bitPattern: curLoadCommandIterator)?.pointee else {
return nil
}

let dataStart = Int(bitPattern: executableHeader) + Int(dataCommand.dataoff)

var signatureCursor: Int = dataStart

// This segement is not always has signature start
// When I test on debug build the this segment also contains symbol table
// need to iterate addresses and find the magic first
while signatureCursor < dataStart + Int(dataCommand.datasize) {
guard let multiBlob = UnsafePointer<CSMultiBlob>(bitPattern: signatureCursor)?.pointee else {
return nil
}
print(String(format: "Finding Magic Number: 0x%08X", CFSwapInt32(multiBlob.magic)))
if CFSwapInt32(multiBlob.magic) == CSMagic.embeddedSignature {
return readEntitlementsFromSignature(startingAt: signatureCursor, multiBlob: multiBlob)
}
signatureCursor = signatureCursor + 4
}

print("Cannot find the magic number")
return nil
}

private func readEntitlementsFromSignature(startingAt offset: Int,
multiBlob: CSMultiBlob) -> Entitlements? {
let multiBlobSize = MemoryLayout<CSMultiBlob>.size
let blobSize = MemoryLayout<CSBlob>.size
let itemCount = Int(CFSwapInt32(multiBlob.count))

for index in 0 ..< itemCount {
let blobOffset = offset + multiBlobSize + index * blobSize
let blob = UnsafePointer<CSBlob>(bitPattern: blobOffset)?.pointee

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / client-app-spm (iOS, ClientApp, macos-15)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / client-app-spm (iOS, ClientApp, macos-14)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / iOS-Device (macos-14, Xcode_16.2)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / iOS-Device (macos-15, Xcode_16.4)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.4, watchOS)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / platforms (macOS)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.4, catalyst)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / platforms (tvOS)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / platforms (catalyst)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.4, macOS)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.4, tvOS)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.4, visionOS)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / client-app-spm-source-firestore (iOS, ClientApp, macos-14)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-26, Xcode_26.0, iOS)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / client-app-cocoapods (ClientApp-CocoaPods, macos-15)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / client-app-cocoapods (ClientApp-CocoaPods, macos-14)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / client-app-spm-source-firestore (iOS, ClientApp, macos-15)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-14, Xcode_16.2, iOS)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / spm / spm (macos-15, Xcode_16.4, iOS)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / swift-build-run (macos-14, Xcode_16.2, spm)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it

Check warning on line 107 in Crashlytics/Crashlytics/Entitlements/EntitlementsReader.swift

View workflow job for this annotation

GitHub Actions / swift-build-run (macos-15, Xcode_16.4, spm)

initialization of immutable value 'blob' was never used; consider replacing with assignment to '_' or removing it
// the first 4 bytes are the magic
// the next 4 are the length
// after that is the encoded plist
if
let blob = UnsafePointer<CSBlob>(bitPattern: blobOffset)?.pointee,
let magic = UnsafePointer<UInt32>(bitPattern: offset + Int(CFSwapInt32(blob.offset)))?
.pointee, CFSwapInt32(magic) == CSMagic.embededEntitlements {
if let signatureLength =
UnsafePointer<UInt32>(bitPattern: offset + Int(CFSwapInt32(blob.offset)) + 4)?.pointee,
let entitlementDataPointer =
UnsafeRawPointer(bitPattern: offset + Int(CFSwapInt32(blob.offset)) + 8) {
let data = Data(
bytes: entitlementDataPointer,
count: Int(CFSwapInt32(signatureLength)) - 8
)

guard let rawValues = try? PropertyListSerialization.propertyList(
from: data,
options: [],
format: nil
) as? [String: Any] else {
return nil
}
return Entitlements(rawValue: rawValues)
}
}
}
return nil
}
}
2 changes: 2 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,7 @@ let package = Package(
"CrashlyticsInputFiles.xcfilelist",
"third_party/libunwind/LICENSE",
"Crashlytics/Rollouts/",
"Crashlytics/Entitlements/",
],
sources: [
"Crashlytics/",
Expand Down Expand Up @@ -616,6 +617,7 @@ let package = Package(
path: "Crashlytics",
sources: [
"Crashlytics/Rollouts/",
"Crashlytics/Entitlements/",
]
),
.testTarget(
Expand Down
Loading