From edd44d919cddff368758c34639f50696971ed84e Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Thu, 23 Aug 2018 01:51:30 +0200 Subject: [PATCH] Revisione unlink e analoghe, trattata exit_group. --- filedir.tex | 227 ++++++++++++++++++++++++++++------------------------ procadv.tex | 51 +++++++----- thread.tex | 18 +++++ 3 files changed, 170 insertions(+), 126 deletions(-) diff --git a/filedir.tex b/filedir.tex index 0c4f94d..ec7ad75 100644 --- a/filedir.tex +++ b/filedir.tex @@ -1620,7 +1620,30 @@ ma si limita ad inserire il \textit{pathname} nel collegamento simbolico. Pertanto un collegamento simbolico può anche riferirsi ad un file che non esiste ed in questo caso si ha quello che viene chiamato un \itindex{dangling~link} \textit{dangling link}, letteralmente un -\index{collegamento!ciondolante} ``\textsl{collegamento ciondolante}''. +\index{collegamento!ciondolante} ``\textsl{collegamento ciondolante}''. Ad +esempio possiamo usare il comando \cmd{ln} per creare un collegamento +simbolico nella nostra directory con: +\begin{Console} +piccardi@hain:~/gapil$ \textbf{ln -s /tmp/tmp_file symlink} +\end{Console} +%$ +e questo avrà successo anche se \file{/tmp/tmp\_file} non esiste: +\begin{Console} +piccardi@hain:~/gapil$ \textbf{ls symlink} +symlink +\end{Console} +%$ +ma questo può generare confusione, perché accedendo in lettura a +\file{symlink}, ad esempio con \cmd{cat}, otterremmo: +\begin{Console} +piccardi@hain:~/gapil$ \textbf{cat symlink} +cat: symlink: No such file or directory +\end{Console} +%$ +con un errore che può sembrare sbagliato, dato che \cmd{ls} ci ha mostrato in +prcedenza l'esistenza di \file{symlink}. Se invece andassimo a scrivere su +\file{symlink}, l'effetto sarebbe quello di ottenere la creazione di +\file{/tmp/tmp\_file} (che a quel punto verrebbe creato) senza errori. Come accennato i collegamenti simbolici sono risolti automaticamente dal kernel all'invocazione delle varie \textit{system call}. In @@ -1673,6 +1696,13 @@ funzione che restituisce il file descriptor (normalmente la \func{open}, vedi sez.~\ref{sec:file_open_close}) e tutte le operazioni seguenti fanno riferimento solo a quest'ultimo. +Si tenga anche presente che a partire dal kernel 3.16, se si abilita la +funzionalità dei \textit{protected symlinks} (attiva di default in tutte le +distribuzioni più recenti) la risoluzione dei nomi attraverso un collegamento +simbolico può fallire per una serie di restrizione di sicurezza agguntive +imposte dal meccanismo (si consulti sez.~\ref{sec:procadv_security_misc} per i +dettagli del meccanismo). + Dato che, come indicato in tab.~\ref{tab:file_symb_effect}, funzioni come la \func{open} seguono i collegamenti simbolici, occorrono funzioni apposite per accedere alle informazioni del collegamento invece che a quelle del file a cui @@ -1681,30 +1711,32 @@ simbolico si usa la funzione di sistema \funcd{readlink}, il cui prototipo è: \begin{funcproto}{ \fhead{unistd.h} -\fdecl{int readlink(const char *path, char *buff, size\_t size)} +\fdecl{int readlink(const char *pathname, char *buff, size\_t size)} \fdesc{Legge il contenuto di un collegamento simbolico.} } {La funzione ritorna il numero di caratteri letti dentro \param{buff} in caso di successo e $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EINVAL}] \param{path} non è un collegamento simbolico + \item[\errcode{EACCES}] non si hanno i permessi di attraversamento di una + delle directory del pathname + \item[\errcode{EINVAL}] \param{pathname} non è un collegamento simbolico o \param{size} non è positiva. - \end{errlist} ed inoltre \errval{EACCES}, \errval{EFAULT}, \errval{EIO}, - \errval{ELOOP}, \errval{ENAMETOOLONG}, \errval{ENOENT}, \errval{ENOMEM} e - \errval{ENOTDIR} nel loro significato generico.} + \end{errlist} ed inoltre \errval{EFAULT}, \errval{EIO}, \errval{ELOOP}, + \errval{ENAMETOOLONG}, \errval{ENOENT}, \errval{ENOMEM} e \errval{ENOTDIR} + nel loro significato generico.} \end{funcproto} La funzione legge il \textit{pathname} a cui fa riferimento il collegamento -simbolico indicato dall'argomento \param{path} scrivendolo sul -buffer \param{buff} di dimensione \param{size}. Si tenga presente che la -funzione non termina la stringa con un carattere nullo e che se questa è -troppo lunga la tronca alla dimensione specificata da \param{size} per evitare -di sovrascrivere oltre le dimensioni del buffer. +simbolico indicato dall'argomento \param{pathname} scrivendolo sul buffer +\param{buff} di dimensione \param{size}. Si tenga presente che la funzione non +termina la stringa con un carattere nullo e che se questa è troppo lunga la +tronca alla dimensione specificata da \param{size} per evitare di scrivere +dati oltre le dimensioni del buffer. \begin{figure}[htb] \centering - \includegraphics[width=7.0cm]{img/link_loop} + \includegraphics[width=8cm]{img/link_loop} \caption{Esempio di loop nel filesystem creato con un collegamento simbolico.} \label{fig:file_link_loop} @@ -1715,57 +1747,30 @@ alle directory è che questi possono creare dei \textit{loop} nella risoluzione dei nomi che non possono essere eliminati facilmente. Invece è sempre possibile, ed in genere anche molto utile, creare un collegamento simbolico ad una directory, anche se in questo caso si potranno ottenere anche dei -\textit{loop}. La situazione è illustrata in fig.~\ref{fig:file_link_loop}, -che riporta la struttura della directory \file{/boot}. Come si vede si è -creato al suo interno un collegamento simbolico che punta di nuovo a +\textit{loop}. + +La situazione è illustrata in fig.~\ref{fig:file_link_loop}, che riporta la +struttura della directory \file{/boot}. Come si vede si è creato al suo +interno un collegamento simbolico che punta di nuovo a \file{/boot}.\footnote{il \textit{loop} mostrato in - fig.~\ref{fig:file_link_loop} è stato usato per poter permettere a - \cmd{grub} (un bootloader in grado di leggere direttamente da vari - filesystem il file da lanciare come sistema operativo) di vedere i file - contenuti nella directory \file{/boot} con lo stesso \textit{pathname} con - cui verrebbero visti dal sistema operativo, anche se essi si trovano, come - accade spesso, su una partizione separata (che \cmd{grub} all'avvio vedrebbe - come \file{/}).} - -Questo però può causare problemi per tutti quei programmi che effettuano la -scansione di una directory senza tener conto dei collegamenti simbolici, ad -esempio se lanciassimo un comando del tipo \code{grep -r linux *}, il loop -nella directory porterebbe il comando ad esaminare \file{/boot}, -\file{/boot/boot}, \file{/boot/boot/boot} e così via. + fig.~\ref{fig:file_link_loop} è stato usato per poter permettere a al + \textit{bootloader} \cmd{grub} di vedere i file contenuti nella directory + \file{/boot} con lo stesso \textit{pathname} con cui verrebbero visti dal + sistema operativo, anche quando si trovano, come accade spesso, su una + partizione separata (che \cmd{grub} all'avvio vedrebbe come \file{/}).} Un +\textit{loop} di di questo tipo però può causare problemi per tutti i +programmi che effettuano la scansione di una directory, e ad esempio se +lanciassimo un comando come \code{grep -r linux *}, il \textit{loop} nella +directory porterebbe ad esaminare \file{/boot}, \file{/boot/boot}, +\file{/boot/boot/boot} e così via. Per questo motivo il kernel e le librerie prevedono che nella risoluzione di un \textit{pathname} possano essere seguiti fino ad un certo numero massimo di collegamenti simbolici, il cui valore limite è specificato dalla costante -\constd{MAXSYMLINKS}. Qualora questo limite venga superato viene generato un -errore ed \var{errno} viene impostata al valore \errcode{ELOOP}, che nella -quasi totalità dei casi indica appunto che si è creato un collegamento -simbolico che fa riferimento ad una directory del suo stesso -\textit{pathname}. - -Un altro punto da tenere sempre presente è che, come abbiamo accennato, un -collegamento simbolico può fare riferimento anche ad un file che non esiste; -ad esempio possiamo usare il comando \cmd{ln} per creare un collegamento -simbolico nella nostra directory con: -\begin{Console} -piccardi@hain:~/gapil$ \textbf{ln -s /tmp/tmp_file symlink} -\end{Console} -%$ -e questo avrà successo anche se \file{/tmp/tmp\_file} non esiste: -\begin{Console} -piccardi@hain:~/gapil$ \textbf{ls symlink} -symlink -\end{Console} -%$ -ma questo può generare confusione, perché accedendo in sola lettura a -\file{symlink}, ad esempio con \cmd{cat}, otterremmo un errore: -\begin{Console} -piccardi@hain:~/gapil$ \textbf{cat symlink} -cat: symlink: No such file or directory -\end{Console} -%$ -con un errore che può sembrare sbagliato, dato che \cmd{ls} ci ha mostrato -l'esistenza di \file{symlink}. Se invece scrivessimo su \file{symlink} -otterremmo la creazione di \file{/tmp/tmp\_file} senza errori. +\constd{MAXSYMLINKS}. Se il limite viene superato si ha un errore ed +\var{errno} viene impostata al valore \errcode{ELOOP}, che nella quasi +totalità dei casi indica appunto che si è creato un collegamento simbolico che +fa riferimento ad una directory del suo stesso \textit{pathname}. \itindend{symbolic~link} @@ -1792,13 +1797,16 @@ nome come si può notare ha poco a che fare con il concetto di rimozione, è nel qual caso \var{errno} assumerà uno dei valori:\footnotemark \begin{errlist} \item[\errcode{EACCES}] non si ha il permesso di scrittura sulla directory - che contiene \param{pathname} o di attraversamento di una delle directory - superiori. + che contiene \param{pathname} o quello di attraversamento per una delle + directory superiori. + \item[\errcode{EBUSY}] \param{pathname} non può essere rimosso perché è in + uso da parte del sistema (in particolare per i cosidetti \textit{silly + renames} di NFS). \item[\errcode{EISDIR}] \param{pathname} si riferisce ad una directory. \item[\errcode{EPERM}] il filesystem non consente l'operazione, o la directory che contiene \param{pathname} ha lo \textit{sticky bit} e non si - è il proprietario o non si hanno privilegi amministrativi. + è il proprietario del file o non si hanno privilegi amministrativi. \end{errlist} ed inoltre \errval{EFAULT}, \errval{EIO}, \errval{ELOOP}, \errval{ENOENT}, \errval{ENOMEM}, \errval{ENOTDIR}, \errval{EROFS} nel loro significato generico.} @@ -1815,55 +1823,60 @@ nome come si può notare ha poco a che fare con il concetto di rimozione, è La funzione elimina il nome specificato dall'argomento \param{pathname} nella directory che lo contiene e decrementa il numero di riferimenti nel relativo -\textit{inode}.\footnote{come per \func{link} queste due operazioni sono - effettuate all'interno della \textit{system call} in maniera atomica.} Nel -caso di socket, \textit{fifo} o file di dispositivo rimuove il nome, ma come -per i file normali i processi che hanno aperto uno di questi oggetti possono -continuare ad utilizzarli. Nel caso di cancellazione di un collegamento -simbolico, che consiste solo nel rimando ad un altro file, questo viene -immediatamente eliminato. +\textit{inode}; come per \func{link} queste due operazioni sono effettuate +all'interno della \textit{system call} in maniera atomica rispetto ai +processi. + +Si ricordi che, anche se se ne è rimosso il nome, un file viene realmente +cancellato soltanto quando il numero di collegamenti mantenuto +nell'\textit{inode} diventa nullo; solo allora l'\textit{inode} viene +disallocato e lo spazio che il file occupava sul disco viene liberato. + +Si tenga presente comunque che a questo si aggiunge sempre un'ulteriore +condizione e cioè che non ci siano processi che stiano ancora lavorando sul il +file. Come vedremo in sez.~\ref{sec:file_unix_interface} il kernel una tabella +di tutti file aperti da ciascun processo, che a sua volta contiene i +riferimenti agli \textit{inode} ad essi relativi. Prima di procedere alla +cancellazione dello spazio occupato su disco dal contenuto di un file il +kernel controlla anche questa tabella, per verificare che anche in essa non ci +sia più nessun riferimento all'\textit{inode} in questione, assicurandosi con +questo che nessun processo stia ancora usando il file. + +Nel caso di socket, \textit{fifo} o file di dispositivo la funzione rimuove il +nome, e come per i file normali i processi che hanno aperto uno di questi +oggetti possono continuare ad utilizzarli. Nel caso di cancellazione di un +\textit{link} simbolico, che consiste solo nel rimando ad un altro file, +questo viene immediatamente eliminato e non sarà più utilizzabile. Per cancellare una voce in una directory è necessario avere il permesso di scrittura su di essa, dato che si va a rimuovere una voce dal suo contenuto, e il diritto di esecuzione/attraversamento sulla directory che la contiene (affronteremo in dettaglio l'argomento dei permessi di file e directory in -sez.~\ref{sec:file_access_control}). Se inoltre lo \textit{sticky bit} (vedi -sez.~\ref{sec:file_special_perm}) è impostato occorrerà anche essere -proprietari del file o proprietari della directory o avere i privilegi di -amministratore. - -Si ricordi inoltre che anche se se ne è rimosso il nome da una directory, un -file non viene eliminato dal disco fintanto che tutti i riferimenti ad esso -sono stati cancellati: solo quando il numero di collegamenti mantenuto -nell'\textit{inode} diventa nullo, questo viene disallocato e lo spazio -occupato su disco viene liberato. Si tenga presente comunque che a questo si -aggiunge sempre un'ulteriore condizione e cioè che non ci siano processi che -abbiano il suddetto file aperto.\footnote{come vedremo in - sez.~\ref{sec:file_unix_interface} il kernel mantiene anche una tabella dei - file aperti nei vari processi, che a sua volta contiene i riferimenti agli - \textit{inode} ad essi relativi; prima di procedere alla cancellazione dello - spazio occupato su disco dal contenuto di un file il kernel controlla anche - questa tabella, per verificare che anche in essa non ci sia più nessun - riferimento all'\textit{inode} in questione.} - -Questa caratteristica del sistema può essere usata per essere sicuri di non -lasciare file temporanei su disco in caso di crash di un programma. La tecnica -è quella di aprire un nuovo file e chiamare \func{unlink} su di esso subito +sez.~\ref{sec:file_access_control}). Se inoltre per la directory è impostato +lo \textit{sticky bit} (vedi sez.~\ref{sec:file_special_perm}), occorrerà +anche essere proprietari del file o proprietari della directory o avere i +privilegi di amministratore. + +Questa caratteristica del sistema, che consente di usare un file anche se lo +si è ``cancellato'', può essere usata per essere sicuri di non lasciare file +temporanei su disco in caso di uscita imprevista di un programma. La tecnica è +quella di aprire un nuovo file e chiamare \func{unlink} su di esso subito dopo, in questo modo il contenuto del file sarà sempre disponibile all'interno del processo attraverso il suo file descriptor (vedi sez.~\ref{sec:file_fd}), -ma non ne resta traccia in nessuna directory, e lo spazio occupato su disco -viene immediatamente rilasciato alla conclusione del processo, quando tutti i -file vengono chiusi. +ma non ne resterà traccia in nessuna directory, inoltre lo spazio occupato su +disco verrà immediatamente rilasciato alla conclusione del processo, quando +tutti i file vengono chiusi. Al contrario di quanto avviene con altri Unix, in Linux non è possibile usare -la funzione \func{unlink} sulle directory, nel qual caso si otterrebbe un +la funzione \func{unlink} sulle directory, che in tal caso fallisce con un errore di \errcode{EISDIR}. Per cancellare una directory si deve usare la apposita funzione di sistema \func{rmdir} (che vedremo in sez.~\ref{sec:file_dir_creat_rem}), oppure la funzione \func{remove}. + Quest'ultima è la funzione prevista dallo standard ANSI C per effettuare una -cancellazione generica di un file o di una directory e funziona anche per i -sistemi operativo che non supportano gli \textit{hard link}. Nei sistemi -unix-like \funcd{remove} è equivalente ad usare in maniera trasparente +cancellazione generica di un file o di una directory e viene usata in generale +anche per i sistemi operativi che non supportano gli \textit{hard link}. Nei +sistemi unix-like \funcd{remove} è equivalente ad usare in maniera trasparente \func{unlink} per i file ed \func{rmdir} per le directory; il suo prototipo è: \begin{funcproto}{ @@ -1877,13 +1890,15 @@ unix-like \funcd{remove} è equivalente ad usare in maniera trasparente \func{unlink} e \func{rmdir}.} \end{funcproto} -La funzione utilizza la funzione \func{unlink} per cancellare i file e la -funzione \func{rmdir} (vedi sez.~\ref{sec:file_dir_creat_rem}) per cancellare -le directory.\footnote{questo vale usando la \acr{glibc}; nella \acr{libc4} e - nella \acr{libc5} la funzione \func{remove} era un semplice alias alla - funzione \func{unlink} e quindi non poteva essere usata per le directory.} -Si tenga presente che per alcune implementazioni del protocollo NFS utilizzare -questa funzione può comportare la scomparsa di file ancora in uso. +La funzione utilizza la funzione \func{unlink} per cancellare i file (e si +applica anche a link simbolici, socket, \textit{fifo} e file di dispostivo) e +la funzione \func{rmdir} (vedi sez.~\ref{sec:file_dir_creat_rem}) per +cancellare le directory.\footnote{questo vale usando la \acr{glibc}; nella + \acr{libc4} e nella \acr{libc5} la funzione \func{remove} era un semplice + alias alla funzione \func{unlink} e quindi non poteva essere usata per le + directory.} Si tenga presente che per alcune limitazioni del protocollo +NFS, utilizzare questa funzione su file che usano questo filesystem di rete +può comportare la scomparsa di file ancora in uso. Infine per cambiare nome ad un file o a una directory si usa la funzione di sistema \funcd{rename},\footnote{la funzione è definita dallo standard ANSI C, @@ -1898,9 +1913,9 @@ sistema \funcd{rename},\footnote{la funzione è definita dallo standard ANSI C, {La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EACCESS}] non c'è permesso di scrivere nelle directory + \item[\errcode{EACCESS}] manca il permesso di scrittura sulle directory contenenti \param{oldpath} e \param{newpath} o di attraversare - quelle dei loro \textit{pathname} o di scrivere su \param{newpath} + il loro \textit{pathname} o di scrivere su \param{newpath} se questa è una directory. \item[\errcode{EBUSY}] o \param{oldpath} o \param{newpath} sono in uso da parte di qualche processo (come directory di lavoro o come radice) o del diff --git a/procadv.tex b/procadv.tex index 2859e8b..0b9dd0a 100644 --- a/procadv.tex +++ b/procadv.tex @@ -2013,26 +2013,37 @@ funzione. Il \textit{secure computing mode} è un meccanismo ideato per fornire un supporto per l'esecuzione di codice esterno non fidato e non verificabile a -scopo di calcolo. L'idea era quello di poter vendere la capacità di calcolo -della propria macchina ad un qualche servizio di calcolo distribuito, senza -comprometterne la sicurezza eseguendo codice non sotto il proprio controllo. - -Nella prima versione del meccanismo, introdotto con il kernel 2.6.23 e -disponibile solo avendo è abilitato il supporto nel kernel con -\texttt{CONFIG\_SECCOMP}, questo veniva attivato con \func{prctl} indicando -l'opzione \const{PR\_SET\_SECCOMP}, che all'epoca supportava soltanto l'uso -del valore \const{SECCOMP\_MODE\_STRICT} per \param{arg2}. - -Una volta abilitato il \textit{secure computing mode} in questa modalità (in -seguito denominata \textit{strict mode}) il processo o il \textit{thread} -chiamante potrà utilizzare soltanto un insieme estremamente limitato di -\textit{system call}: \func{read}, \func{write}, \func{\_exit} e -\funcm{sigreturn}. Ogni altra \textit{system call} porterà all'emissione di un -\signal{SIGKILL} (vedi sez.~\ref{sec:sig_termination}). In questa modalità di -utilizzo i dati vengono letti o scritti grazie ad un socket o una -\textit{pipe}, creati prima di lanciare il processo che eseguirà il codice non -fidato, e per evitare problemi di sicurezza non saranno possibili altre -operazioni se non quelle citate. +scopo di calcolo. L'idea era quella di disporre di una modalità di esecuzione +dei programmi che permettesse di vendere la capacità di calcolo della propria +macchina ad un qualche servizio di calcolo distribuito, senza comprometterne +la sicurezza eseguendo codice non sotto il proprio controllo. + +La prima versione del meccanismo è stata introdotta con il kernel +2.6.23,\footnote{e disponibile solo avendo abilitato il supporto nel kernel + con l'opzione di configurazione \texttt{CONFIG\_SECCOMP}.} è molto semplice, +il \textit{secure computing mode} viene attivato con \func{prctl} usando +l'opzione \const{PR\_SET\_SECCOMP}, ed indicando \const{SECCOMP\_MODE\_STRICT} +come valore per \param{arg2} (all'epoca unico valore possibile). Una volta +abilitato in questa modalità (in seguito denominata \textit{strict mode}) il +processo o il \textit{thread} chiamante potrà utilizzare soltanto un insieme +estremamente limitato di \textit{system call}: \func{read}, \func{write}, +\func{\_exit} e \funcm{sigreturn}; l'esecuzione di qualsiasi altra +\textit{system call} comporta l'emissione di un \signal{SIGKILL} e conseguente +terminazione immediata del processo. + +Si tenga presente che in questo caso, con versioni recenti della \acr{glibc} +(il comportamento è stato introdotto con la 2.3), diventa impossibile usare +anche \func{\_exit} in \textit{strict mode}, in quanto questa funzione viene +intercettata ed al suo posto viene chiamata \func{exit\_group} (vedi +sez.~\ref{sec:pthread_management}) che non è consentita e comporta un +\signal{SIGKILL}. + +Si tenga presente che, non essendo \func{execve} fra le funzioni permesse, per +poter eseguire un programma terzo essendo in \textit{strict mode} questo dovrà +essere fornito in una forma di codice interpretabile fornito attraverso un +socket o una \textit{pipe}, creati prima di lanciare il processo che eseguirà +il codice non fidato. + % TODO a partire dal kernel 3.5 è stato introdotto la possibilità di usare un diff --git a/thread.tex b/thread.tex index 6dce4cc..d563477 100644 --- a/thread.tex +++ b/thread.tex @@ -116,6 +116,24 @@ della \acr{glibc}. \label{sec:pthread_management} +Benché la funzione sia utilizzabile anche con i processi, tanto che a partire +dalla versione 2.3 della \acr{glibc} viene a sostituire \func{\_exit} (tramite +un \textit{wrapper} che la utilizza al suo posto) per la terminazione di tutti +i \textit{thread} di un processo si deve usare la funzione di sistema +\func{exit\_group}, il cui prototipo è: + +\begin{funcproto}{ +\fhead{linux/unistd.h} +\fdecl{void exit\_group(int status)} +\fdesc{Termina tutti i \textit{thread} di un processo.} +} +{La funzione non ha errori e pertanto non ritorna.} +\end{funcproto} + +La funzione è sostanzialmente identica alla \textit{system call} \func{\_exit} +ma a differenza di quest'ultima, che termina solo il \textit{thread} +chiamante, termina tutti \textit{thread} del processo. + \section{La sincronizzazione dei \textit{thread}} \label{sec:pthread_sync} -- 2.30.2