Nel precedente articolo abbiamo descritto cosa a grandi linee il ciclo di vita dei processi. Quest’oggi vediamo cosa si intende per stato “zombie” e di come è strutturata la sicurezza dei processi in macOS.
Lo stato zombie
Tra tutti i possibili stati che un processo può assumere, quello di più difficile comprensione è rappresentato dallo stato zombie. E’ uno stato normalissimo, raggiungibile come qualsiasi altro stato e solitamente, ogni processo trascorre una quantità infinitesima di tempo, poco prima che possa ‘spirare’ in pace (essere killato).
L’obiettivo che ha un processo, cioè il “senso che assume” quando è in esecuzione è completare un’attività e restituire un valore al processo padre che lo ha generato; la cosa divertente è che i processi padre non hanno alcuna responsabilità di allevare e prendersi cura dei propri figli: l’unico obbligo che hanno è di aspettare che il figlio termini l’esecuzione, cioè attendere il famoso valore di ritorno.
Un processo che si trova in stato zombie viene “adottato” dal processo più anziano, pensiamolo come il nonno di tutti i processi, quello che ha PID 1 (launchd) che successivamente lo terminerà. Questo “nonno” è l’unico processo che può sopravvivere rispetto alla morte di tutti gli altri processi, vivendo dalla fase di boot fino a quella di arresto.
Ma cos’è questo stato zombie?
I genitori che sopravvivono, ma abbandonano i processi figli e passano ad eseguire altre operazioni, danneggiano irreparabilmente l’operato dei figli, tanto da bloccarli in una situazione in cui sono nello stato di quasi-morto, cioè di zombie. Gli zombi sono a tutti gli effetti morti. Sono gusci vuoti dei processi, che hanno rilasciato tutte le risorse ma si aggrappano ancora al loro PID e infatti appaiono nell’elenco dei processi come <defunto>. Gli zombie, moriranno nel vero senso del termine solo quando il loro genitore muore, concedendo il riposo e permettendo loro di essere adottati, anche se per poco tempo dal PID 1 prima di essere killati definitivamente.
Le nuove syscall
macOS (e iOS) hanno due nuove chiamate di sistema (syscall) per il controllo dei processi, che gli altri sistemi Unix non possiedono: pid_suspend e pid_resume.
Il primo congela un processo e l’ultimo lo scongela; l’effetto è simile all’invio dei segnali STOP/CONT, ma funzionalmente diverso: innanzitutto, lo stato del processo rimane SSLEEP, ma è un “sonno” molto più profondo perché la sospensione che viene attivata viene eseguita ad un livello inferiore (del task mach) piuttosto che a quella del processo.
In secondo luogo, queste chiamate possono essere utilizzate più volte, incrementando e decrementando opportunamente (non manualmente, lo fa il sistema!) un contatore che tiene conto del conteggio di sospensione del processo. Quindi, per ogni chiamata alla syscall pid_suspend, deve necessariamente essere presente la syscall corrispondente inversa, pid_resume. In sostanza, un processo che ha il contatore di sospensione diverso da zero rimane in sospeso.
Queste due chiamate di sistema sono proprietarie Apple, banalmente non ne viene pubblicato il sorgente; macOS non ne fa un largo uso, al contrario di iOS con la sua SpringBoard dato che alcuni processi vengono sospesi quando l’utente preme il pulsante home o esegue uno swipe verso l’alto con il nuovissimo iPhone X.
Inoltre iOS aggiunge una chiamata di sistema (privata) aggiuntiva, che non è proprio presente in macOS, chiamata pid_shutdown_sockets che consente di arrestare tutti i socket di un processo dall’esterno del processo stesso.
Sicurezza nei processi
Unix è sempre stato un sistema multiutente, nel quale più di un utente può eseguire contemporaneamente vari processi; per garantire sicurezza e isolamento, ogni processo detiene due credenziali principali:
- Identificatore utente (UID): che permette ad un programma di sapere chi lo sta eseguendo e ogni utente deve avere un unico UID del creatore.
- Identificatore del gruppo (GID): che permette di conoscere a quale gruppo fa capo un certo utente.
Gli UID e i GID permettono ad un utente di sapere con quale livello di permesso può accedere ad un file, ad esempio l’utente root ha un UID uguale a 0 e un GID uguale a 0, ciò significa che ha accesso completo a qualsiasi file nel sistema.
Inoltre, gli UID ed i GID assegnati agli utenti sono usati per inizializzare i vari UID e GID dei processi che essi avviano. Questi ultimi sono di volta in volta confrontati dal sistema con gli UID e GID assegnati ai file, unitamente ai loro permessi, per implementare una stretta attività di controllo sui contenuti e operazioni. Questo insieme di informazioni costituisce il profilo di sicurezza dell’utente.
A differenza di linux, macOS non supporta i meccanismi per applicare il principio del privilegio minimo, suddividendo e delegando i privilegi di root a processi non root. Questo riduce ad esempio la necessità per un server Web, di essere eseguito come root solo per poter eseguire una certa operazione.
Per sopperire questa mancanza, macOS e iOS supportano i “diritti”, che sono usati nel meccanismo della sandbox. Insieme al code signing, forniscono un potente meccanismo per “controllare” applicazioni e malware in esecuzione nel sistema.
Anche per questo articolo è tutto. Per eventuali domande, curiosità o feedback potete lasciare un commento qui in basso, a presto!