macos-tcc-sigkill-missing-usage-description

Par divinevideo · divine-mobile

Corrige les crashs brutaux des applications macOS (EXC_CRASH / SIGKILL) causés par l'accès à des APIs protégées par TCC sans la clé de description d'utilisation correspondante dans Info.plist. À utiliser quand : (1) L'application crashe immédiatement au contact de Photos, Contacts, Calendar, Reminders, Location, Microphone, Camera, Screen Recording, etc., (2) Le rapport de crash indique `Exception: EXC_CRASH (SIGKILL)` avec `Termination namespace: TCC`, (3) La stack du crash contient `TCC.__TCC_CRASHING_DUE_TO_PRIVACY_VIOLATION__`, (4) Les détails de terminaison mentionnent "This app has crashed because it attempted to access privacy-sensitive data without a usage description", (5) Le flux de publication macOS Flutter / de sauvegarde dans la galerie crashe sur `Gal.putVideo`, `PHPhotoLibrary`, `CNContactStore`, etc. Distinction critique : macOS ne retourne PAS une erreur de refus — il envoie un SIGKILL au processus. Ce skill couvre également la distinction entre `NSPhotoLibraryUsageDescription` et `NSPhotoLibraryAddUsageDescription` qui piège de nombreuses applications.

npx skills add https://github.com/divinevideo/divine-mobile --skill macos-tcc-sigkill-missing-usage-description

macOS TCC SIGKILL : Description d'usage manquante dans Info.plist

Problème

Une app macOS plante avec EXC_CRASH (SIGKILL) dès qu'elle appelle une API protégée par les règles de confidentialité. Il n'y a aucune chance de gérer l'erreur, aucune invite de permission, aucune réponse de refus — juste une terminaison immédiate du processus par le système d'exploitation.

Le plantage est causé par l'application de TCC sur macOS : si votre app se lie à ou appelle une API qui accède à une ressource protégée par les règles de confidentialité et que votre Info.plist ne contient PAS la clé de description d'usage correspondante, le système d'exploitation tue le processus pour violation de confidentialité.

Contexte / Conditions de déclenchement

Le rapport de crash (~/Library/Logs/DiagnosticReports/<App>-*.ips) contient tous ces éléments :

  • "exception": { "type": "EXC_CRASH", "signal": "SIGKILL" }
  • "termination": { "namespace": "TCC", ... }
  • La chaîne des détails de terminaison contient : « This app has crashed because it attempted to access privacy-sensitive data without a usage description »
  • La pile d'appels du thread planté inclut TCC.__TCC_CRASHING_DUE_TO_PRIVACY_VIOLATION__
  • Les frames en dessous sont généralement TCC.__TCCAccessRequest_block_invoke, libxpc.dylib._xpc_connection_reply_callout, les mécanismes de dispatch queue

La chaîne des détails de terminaison vous indique exactement quelle clé manque — lisez-la.

Tableau de correspondance des clés Info.plist

Ressource Clé Info.plist Entitlement (apps en sandbox)
Caméra NSCameraUsageDescription com.apple.security.device.camera
Microphone NSMicrophoneUsageDescription com.apple.security.device.audio-input
Photos (lecture/écriture) NSPhotoLibraryUsageDescription com.apple.security.personal-information.photos-library
Photos (ajout uniquement) NSPhotoLibraryAddUsageDescription com.apple.security.photos.library.add-only
Contacts NSContactsUsageDescription com.apple.security.personal-information.addressbook
Calendrier NSCalendarsUsageDescription com.apple.security.personal-information.calendars
Rappels NSRemindersUsageDescription com.apple.security.personal-information.reminders
Localisation NSLocationUsageDescription (+ variantes WhenInUse / Always) com.apple.security.personal-information.location
Reconnaissance vocale NSSpeechRecognitionUsageDescription
Dossier Bureau NSDesktopFolderUsageDescription com.apple.security.files.user-selected.read-only etc.
Dossier Documents NSDocumentsFolderUsageDescription
Dossier Téléchargements NSDownloadsFolderUsageDescription com.apple.security.files.downloads.read-write

Le piège des Photos (critique)

NSPhotoLibraryAddUsageDescription n'est pas un substitut à NSPhotoLibraryUsageDescription. Ils couvrent des portées de permission différentes :

  • Ajout uniquement (NSPhotoLibraryAddUsageDescription + com.apple.security.photos.library.add-only) : suffit pour les API qui enregistrent uniquement un asset, comme PHAssetCreationRequest depuis un fichier, ou Gal.putVideo(path) sans album.
  • Accès complet (NSPhotoLibraryUsageDescription) : requis pour toute lecture, toute requête, toute recherche/création d'album, et notamment pour Gal.putVideo(path, album: 'SomeAlbum') — car « mettre dans l'album X » doit lire ou créer l'album, ce qui est une opération au niveau de la bibliothèque.

Symptôme en cas d'erreur : l'enregistrement sans album fonctionne, l'enregistrement avec album plante.

Solution

  1. Lisez les détails de terminaison du rapport de crash — il nomme la clé exacte.
  2. Ajoutez la clé à Info.plist de votre app :
    • Flutter macOS : mobile/macos/Runner/Info.plist
    • macOS natif : <target>/Info.plist ou le paramètre de build INFOPLIST_FILE de la cible
  3. Si l'app est en sandbox (com.apple.security.app-sandbox = true en release), ajoutez également l'entitlement correspondant à la fois à Runner/DebugProfile.entitlements et Runner/Release.entitlements.
  4. Reconstruisez. Les changements de Info.plist ne se rechargent pas à chaud dans Flutter — une compilation complète flutter build macos --debug est requise.
  5. Relancez l'app.

Exemple de patch

App Flutter macOS plantant sur gal.putVideo(path, album: 'MyAlbum') :

<!-- mobile/macos/Runner/Info.plist -->
<key>NSPhotoLibraryAddUsageDescription</key>
<string>MyApp needs access to save videos to your Photos library.</string>
<!-- AJOUTEZ CECI : -->
<key>NSPhotoLibraryUsageDescription</key>
<string>MyApp needs access to your Photos library to organize videos into albums.</string>

Puis :

cd mobile
flutter build macos --debug
open build/macos/Build/Products/Debug/MyApp.app

Vérification

  1. Aucun plantage sur l'action qui plantait auparavant.
  2. macOS affiche une invite de permission la première fois (si le statut est .notDetermined).
  3. Vérifiez le répertoire des nouveaux rapports de crash — aucun fichier .ips ne devrait apparaître après l'action :
    ls -t ~/Library/Logs/DiagnosticReports/<AppName>-*.ips 2>/dev/null | head -3
  4. Vérifiez que le bundle .app compilé contient la clé (Info.plist est copié lors de la compilation, mais vérifiez pour être sûr) :
    grep -A1 NSPhotoLibrary build/macos/Build/Products/Debug/MyApp.app/Contents/Info.plist
  5. log show --last 2m --predicate 'subsystem == "com.apple.TCC"' devrait afficher une autorisation pour le bundle ID de l'app au lieu d'un plantage.

Diagnostic à partir du rapport de crash

Une commande rapide pour extraire les informations clés d'un rapport de crash .ips :

python3 -c "
import json, sys
with open(sys.argv[1]) as f:
    f.readline()  # skip header JSON line
    d = json.loads(f.read())
print('Signal:', d.get('exception', {}).get('signal'))
print('Namespace:', d.get('termination', {}).get('namespace'))
print('Details:', d.get('termination', {}).get('details'))
" ~/Library/Logs/DiagnosticReports/MyApp-*.ips

Si namespace == 'TCC' et les détails mentionnent « usage description », cette compétence s'applique.

Notes

  • Pourquoi SIGKILL et pas un refus gracieux ? Apple a décidé que les violations de confidentialité sont une défaillance de politique du développeur, pas une condition d'exécution à gérer. Le système d'exploitation termine le processus pour qu'il n'y ait aucun moyen pour une app de « réessayer quand même » ou de logger des contournements. Il n'y a pas d'API pour capturer ceci.
  • Cela s'applique même aux chemins de code que vous n'appelez pas. Si un framework lié ou un plugin touche une ressource protégée en votre nom (par exemple un plugin média qui sonde Photos au démarrage), vous avez besoin de la description d'usage même si votre propre code ne touche jamais Photos. C'est pourquoi la clé doit être ajoutée chaque fois que vous incluez un plugin qui pourrait accéder à la ressource.
  • Le rechargement à chaud ne vous sauvera pas. Le rechargement à chaud et le redémarrage à chaud de Flutter ne mettent pas à jour Info.plist dans le bundle en cours d'exécution. Vous devez reconstruire complètement.
  • iOS vs macOS : Les mêmes clés existent sur iOS, mais sur iOS une clé manquante entraîne généralement un refus silencieux plutôt qu'un SIGKILL sur les anciennes versions d'iOS. Les versions récentes d'iOS s'alignent sur macOS et vont planter. Dans tous les cas, définissez toujours la clé.
  • La distinction « Ajout » vs Photos complet a été resserrée dans macOS 14/15/26. Les versions antérieures de macOS vous permettaient de faire certaines opérations d'album avec l'ajout uniquement ; les versions récentes ne le permettent pas. Si une app qui fonctionnait sur une macOS antérieure commence à planter après une mise à niveau de macOS, vérifiez ceci en premier.
  • Connexe mais différent : flutter-macos-tcc-responsible-process-camera-denial couvre TCC refusant l'accès à la caméra quand l'attribution du processus responsable est mauvaise (pas de plantage, juste un refus silencieux). C'est un mode de défaillance TCC différent du SIGKILL couvert ici.

Références

Skills similaires