Rafforzamento della struttura dei media

Per migliorare la sicurezza del dispositivo, Android 7.0 suddivide il processo monolitico mediaserver in più processi con autorizzazioni e funzionalità limitate solo a quelle richieste da ciascun processo. Questi cambiamenti mitigano le vulnerabilità della sicurezza del framework multimediale:

  • Suddivisione dei componenti della pipeline AV in processi sandbox specifici dell'app.
  • Abilitazione di componenti multimediali aggiornabili (estrattori, codec, ecc.).

Queste modifiche migliorano inoltre la sicurezza per gli utenti finali riducendo significativamente la gravità della maggior parte delle vulnerabilità di sicurezza legate ai media, mantenendo al sicuro i dispositivi e i dati degli utenti finali.

Gli OEM e i fornitori di SoC devono aggiornare i propri HAL e le modifiche al framework per renderli compatibili con la nuova architettura. Nello specifico, poiché il codice Android fornito dal fornitore spesso presuppone che tutto venga eseguito nello stesso processo, i fornitori devono aggiornare il proprio codice per passare gli handle nativi ( native_handle ) che hanno significato tra i processi. Per un'implementazione di riferimento delle modifiche relative al rafforzamento dei media, fare riferimento a frameworks/av e frameworks/native .

Cambiamenti architettonici

Le versioni precedenti di Android utilizzavano un unico processo mediaserver monolitico con numerose autorizzazioni (accesso alla fotocamera, accesso all'audio, accesso al driver video, accesso ai file, accesso alla rete, ecc.). Android 7.0 suddivide il processo mediaserver in diversi nuovi processi, ciascuno dei quali richiede un insieme di autorizzazioni molto più ridotto:

rafforzamento del server multimediale

Figura 1. Modifiche all'architettura per il rafforzamento del mediaserver

Questa nuova architettura garantisce che, anche se un processo viene compromesso, il codice dannoso non ha accesso all'intero set di autorizzazioni precedentemente detenuto da mediaserver . I processi sono limitati dalle politiche SElinux e seccomp.

Nota: a causa delle dipendenze dei fornitori, alcuni codec continuano a essere eseguiti nel mediaserver e di conseguenza concedono mediaserver più autorizzazioni del necessario. Nello specifico, Widevine Classic continua a funzionare nel mediaserver per Android 7.0.

Cambiamenti nel MediaServer

In Android 7.0, esiste il processo mediaserver per guidare la riproduzione e la registrazione, ad esempio passando e sincronizzando i buffer tra componenti e processi. I processi comunicano attraverso il meccanismo Binder standard.

In una sessione di riproduzione di file locale standard, l'app passa un descrittore di file (FD) al mediaserver (solitamente tramite l'API Java MediaPlayer) e al mediaserver :

  1. Avvolge l'FD in un oggetto Binder DataSource che viene passato al processo di estrazione, che lo utilizza per leggere dal file utilizzando Binder IPC. (Il mediaextractor non ottiene l'FD ma invece fa richiamare Binder al mediaserver per ottenere i dati.)
  2. Esamina il file, crea l'estrattore appropriato per il tipo di file (ad esempio MP3Extractor o MPEG4Extractor) e restituisce un'interfaccia Binder per l'estrattore al processo mediaserver .
  3. Effettua chiamate IPC di Binder all'estrattore per determinare il tipo di dati nel file (ad esempio dati MP3 o H.264).
  4. Richiama il processo mediacodec per creare codec del tipo richiesto; riceve le interfacce Binder per questi codec.
  5. Effettua ripetute chiamate IPC Binder all'estrattore per leggere campioni codificati, utilizza Binder IPC per inviare dati codificati al processo mediacodec per la decodifica e riceve dati decodificati.

In alcuni casi d'uso, non è coinvolto alcun codec (come una riproduzione scaricata in cui i dati codificati vengono inviati direttamente al dispositivo di output), oppure il codec potrebbe eseguire il rendering dei dati decodificati direttamente invece di restituire un buffer di dati decodificati (riproduzione video).

Modifiche al servizio MediaCodecService

Il servizio codec è il luogo in cui risiedono codificatori e decodificatori. A causa delle dipendenze dei fornitori, non tutti i codec sono ancora presenti nel processo di codec. Nell'Android 7.0:

  • Decoder e codificatori software non sicuri risiedono nel processo di codec.
  • Decoder sicuri e codificatori hardware risiedono nel mediaserver (invariato).

Un'app (o mediaserver ) chiama il processo del codec per creare un codec del tipo richiesto, quindi chiama quel codec per passare i dati codificati e recuperare i dati decodificati (per la decodifica) o per passare i dati decodificati e recuperare i dati codificati (per la codifica) . Il trasferimento dei dati da e verso i codec utilizza già la memoria condivisa, quindi il processo è invariato.

Modifiche a MediaDrmServer

Il server DRM viene utilizzato durante la riproduzione di contenuti protetti da DRM, come i film in Google Play Movies. Gestisce la decrittografia dei dati crittografati in modo sicuro e come tale ha accesso all'archiviazione di certificati e chiavi e ad altri componenti sensibili. A causa delle dipendenze dei fornitori, il processo DRM non viene ancora utilizzato in tutti i casi.

Modifiche dell'AudioServer

Il processo AudioServer ospita componenti relativi all'audio come input e output audio, il servizio policymanager che determina il routing dell'audio e il servizio radio FM. Per dettagli sulle modifiche audio e indicazioni sull'implementazione, vedere Implementare l'audio .

Modifiche a CameraServer

Il CameraServer controlla la telecamera e viene utilizzato durante la registrazione di video per ottenere fotogrammi video dalla telecamera e quindi passarli al mediaserver per un'ulteriore gestione. Per dettagli sulle modifiche e indicazioni sull'implementazione delle modifiche di CameraServer, fare riferimento a Camera Framework Hardening .

Modifiche al servizio Extractor

Il servizio di estrazione ospita gli estrattori , componenti che analizzano i vari formati di file supportati dal framework multimediale. Il servizio di estrazione è il meno privilegiato di tutti i servizi: non può leggere FD, quindi effettua chiamate a un'interfaccia Binder (fornita dal mediaserver for ogni sessione di riproduzione) per accedere ai file.

Un'app (o mediaserver ) effettua una chiamata al processo di estrazione per ottenere un IMediaExtractor , chiama IMediaExtractor per ottenere IMediaSources per la traccia contenuta nel file e quindi chiama IMediaSources per leggere i dati da essi.

Per trasferire i dati tra processi, l'app (o mediaserver ) include i dati nel Answer-Parcel come parte della transazione Binder o utilizza la memoria condivisa:

  • L'utilizzo della memoria condivisa richiede una chiamata Binder aggiuntiva per rilasciare la memoria condivisa ma è più veloce e utilizza meno energia per buffer di grandi dimensioni.
  • L'utilizzo di In-Parcel richiede copie aggiuntive ma è più veloce e consuma meno energia per buffer inferiori a 64 KB.

Implementazione

Per supportare lo spostamento dei componenti MediaDrm e MediaCrypto nel nuovo processo mediadrmserver , i fornitori devono modificare il metodo di allocazione per i buffer sicuri per consentire la condivisione dei buffer tra i processi.

Nelle versioni precedenti di Android, i buffer sicuri venivano allocati nel mediaserver da OMX::allocateBuffer e utilizzati durante la decrittografia nello stesso processo, come mostrato di seguito:

Figura 2. Android 6.0 e allocazione del buffer inferiore nel mediaserver.

In Android 7.0, il processo di allocazione del buffer è cambiato in un nuovo meccanismo che fornisce flessibilità riducendo al minimo l'impatto sulle implementazioni esistenti. Con gli stack MediaDrm e MediaCrypto nel nuovo processo mediadrmserver , i buffer vengono allocati in modo diverso e i fornitori devono aggiornare gli handle del buffer sicuro in modo che possano essere trasportati attraverso il raccoglitore quando MediaCodec invoca un'operazione di decrittografia su MediaCrypto .

Figura 3. Android 7.0 e allocazione del buffer superiore nel mediaserver.

Utilizza handle nativi

OMX::allocateBuffer deve restituire un puntatore a una struttura native_handle , che contiene descrittori di file (FD) e dati interi aggiuntivi. Un native_handle presenta tutti i vantaggi dell'utilizzo degli FD, incluso il supporto del raccoglitore esistente per la serializzazione/deserializzazione, consentendo al contempo una maggiore flessibilità per i fornitori che attualmente non utilizzano gli FD.

Utilizzare native_handle_create() per allocare l'handle nativo. Il codice del framework assume la proprietà della struttura native_handle allocata ed è responsabile del rilascio delle risorse sia nel processo in cui native_handle è originariamente allocato, sia nel processo in cui viene deserializzato. Il framework rilascia handle nativi con native_handle_close() seguito da native_handle_delete() e serializza/deserializza native_handle utilizzando Parcel::writeNativeHandle()/readNativeHandle() .

I fornitori di SoC che utilizzano FD per rappresentare buffer sicuri possono popolare l'FD in native_handle con il proprio FD. I fornitori che non utilizzano FD possono rappresentare buffer sicuri utilizzando campi aggiuntivi in native_buffer .

Imposta la posizione di decrittografia

I fornitori devono aggiornare il metodo di decrittografia OEMCrypto che opera su native_handle per eseguire qualsiasi operazione specifica del fornitore necessaria per rendere native_handle utilizzabile nel nuovo spazio di processo (le modifiche in genere includono aggiornamenti alle librerie OEMCrypto).

Poiché allocateBuffer è un'operazione OMX standard, Android 7.0 include una nuova estensione OMX ( OMX.google.android.index.allocateNativeHandle ) per richiedere questo supporto e una chiamata OMX_SetParameter che notifica all'implementazione OMX che dovrebbe utilizzare handle nativi.