Dans la suite, nous allons considérer que chaque processus représente une zone
de privilège, définissant ainsi ses accès et/ou permissions. Je vais alors
décrire l'architecture que j'ai conçue pour organiser ces processus et garder
une exécution aussi proche que possible que dans l'approche multithread.

\section{La notion d'étage}

L'architecture que nous allons construire se place contextuellement dans celle
de VLC. Elle se base fortement sur la réalisation d'un pipeline de traitement
dynamique, créé au fur et à mesure de la lecture des flux multimédia.

Les différents éléments de ce pipeline seront créés éventuellement dans la même
zone de privilège, éventuellement dans une zone de privilège déjà existante ou
même donnera lieu à la création d'une nouvelle zone de privilège. Pour désigner
ce concept du côté de l'API publique de la sandbox, on crée la notion d'étage,
ou \og{}stages\fg{}, qui permet de ne pas forcément indiquer comment sera créée la
partie du pipeline demandée par le client.

L'étage représente donc une abstraction utilisée par la partie \og{}VLC\fg{} mais
réellement manipulée par la partie \og{}sandbox\fg{} du processus en question.

\section{Le modèle broker}

Dans ce modèle, on considère un processus privilégié qu'on appelle broker, qui
va contrôler tous les échanges entre les différents processus qu'on appelle
workers, mais également leur cycle de vie et leurs permissions et accès.

%TODO: schéma broker-worker

Le broker est donc en charge de faire le routage des différents messages, de
transmettre les ressources et faire la vérification d'accès. Il est également
utile pour vérifier que les processus workers sont encore en vie et signaler les
erreurs.

% TODO: expliquer IPC
Les avantages du modèle broker sont qu'il est généralement le modèle le plus
simple à implémenter, et n'a pas besoin de fonctionnalité particulière du point
de vue du système, à part pour créer les communications inter-processus (IPC).
Nous pouvons même simuler toutes les problématiques d'accès par des jetons
d'accès représenté par une certaine IPC, que le broker associera à un accès réel
et reproduira les actions demandées par le worker.  Il est également bien plus
simple à comprendre, à développer, à isoler et à suivre étant donné que tous les
échanges passent par le broker avant d'être transmis au bon processus. Ce modèle
constitue ainsi un cadre privilégié pour développer la sandbox et l'amener sur
tous les systèmes, à condition de garder en tête les contraintes des autres
modèles.

Le processus broker sera initialisé dans le processus principal et sera donc
père de tous les autres processus de la sandbox. Cela permettra d'utiliser des
techniques comme \texttt{ptrace} sous Linux ou de pouvoir créer des objets
destinés aux processus fils et de leur associer des droits d'accès sous Windows
sans avoir à recourir à des droits de superutilisateur.

\section{Le modèle orchestrateur}

Dans ce modèle, on considère un processus privilégié qu'on appelle
orchestrateur. Celui-ci n'est plus au centre des communications inter-processus
mais permet d'initialiser les connexions entre deux workers qui vont ensuite
discuter directement. On doit donc s'attendre à de meilleures performances étant
donné qu'on diminue les changements de contexte et appels systèmes. On garde cet
\og{}orchestrateur\fg{} pour initialiser chaque nouvel étage mais les modules peuvent
fonctionner indépendamment après, du moment que tous les éléments qui
sont liés à l'étage ont été créés.

Ce modèle nécessite la possibilité d'initialiser une IPC entre deux workers qui
doivent communiquer, mais potentiellement aussi mettre en place une autre IPC
plus efficace pour les transferts de données du pipeline, commme des mémoire
partagées. Il faut également que dans ce modèle, les workers ne puissent pas
récupérer les droits des autres workers.

C'est grâce à ce modèle qu'on peut mettre en valeur la puissance d'expression et
de contrôle des jetons d'accès, car aucun contrôle de l'orchestrateur n'est
nécessaire pour appliquer une réduction des privilèges et définir précisément
les accès disponibles dans chaque partie du pipeline par conception plutôt que
par des règles et contrôles d'accès.

%TODO: schéma orchestrateur-worker

\section{Les processus worker}

%TODO: décrire rôle du processus worker
Dans les deux modèles précédents, on a utilisé des processus workers qui vont
avoir le même rôle, mais éventuellement des implémentations différentes.

Chaque worker est équipé d'un thread jouant le rôle de boucle événementielle et
récupérant les messages soit de tous les autres modules connectés, soit
uniquement depuis le broker selon le modèle utilisé. La boucle événementielle
sera décrite plus tard.

Les workers peuvent démarrer de plusieurs façons. Soit un nouveau processus est
créé en étant configuré comme un worker, avec des IPC correctes déjà attribuées.
Soit le processus est créé depuis un fork et le thread worker peut démarrer tout
de suite en ayant connaissance de son IPC vers l'orchestrateur ou le broker. La
seconde option peut notamment apparaître lorsqu'un processus Zygote est utilisé.

\section{La notion de processus Zygote}

Afin d'optimiser le démarrage de nouveaux processus, beaucoup de systèmes
utilisent un processus spécial, appelé \og{}Zygote\fg{}. Ce processus est responsable de
créer les nouveaux processus et permet un démarrage plus rapide en
préchargeant toutes les bibliothèques et préparant toutes les initialisations
au préalable.

% TODO expliquer et détailler ASLR et android
Les nouveaux processus sont alors créés à partir d'un appel système
\inltype{fork} qui est moins coûteux que la création complète d'un processus
depuis rien. Mais n'est pas officiellement disponible sous Windows,
officieusement disponible sur les versions Windows 10 via les détails internes
liés à l'implémentation des pico-processus, et ne permet pas de pouvoir
efficacement faire de l'ASLR entre les différents processus de l'application. Ce
dernier point existe notamment sur Android, même si des mitigations existent.

Le processus Zygote permet également d'efficacement cloisonner les descripteurs
de fichier à hériter. Mais cela peut être préféré à une méthode plus
systématique de libération de tous les descripteurs associés au processus si
l'on ne souhaite pas utiliser ce processus pour des raisons de performances
également.

Néanmoins, l'autre partie très intéressante du Zygote concerne le débogage de la
sandbox. Sous Linux, \texttt{gdb} n'est capable de s'accrocher qu'à un seul
processus à la fois. Il faut alors choisir avec \texttt{set follow-fork-mode} si
l'on veut déboguer le processus parent ou bien le processus enfant lors d'un
appel à \inltype{fork}. Dans l'architecture que j'ai construite, nous réalisons
les opérations de façon synchrone, même entre deux processus, ce qui permettra
d'attacher des débogueurs au processus Zygote et basculer directement sur le
processus enfant à chaque création de nouveau processus.

\section{Démarrage de la sandbox}

Du côté de l'application cliente qui utilise libvlc et doit être sandboxée, il
faut pouvoir gérer les deux cas de création de processus. Je propose alors une
API inspirée des travaux existants de Guillaume Fournier sur la sandbox seccomp,
mais ne dépendant pas directement des objets libvlc et permettant plus
d'évolution sur les paramètres passables au démarrage de la sandbox.

\begin{code}{c}{API de sandboxing}
vlc_sandbox_t *vlc_sandbox_Init( struct vlc_sandbox_cfg *cfg );
VLC_API int vlc_sandbox_Start( vlc_sandbox_t *sandbox );
VLC_API void vlc_sandbox_Close( vlc_sandbox_t *sandbox );
VLC_API bool vlc_sandbox_ShouldExit( vlc_sandbox_t *sandbox );
/* donne la valeur de retour pour le programme */
VLC_API int vlc_sandbox_Exit( vlc_sandbox_t *sandbox );
\end{code}

Le client peut alors initialiser la sandbox avec le code qui suit. La structure
\inltype{vlc_sandbox_cfg} est prévue à terme pour transmettre l'ensemble des
politiques à appliquer aux différentes zones de privilèges, les paramètres
de création des nouveaux objets dans la sandbox et tout paramètre de sécurité
important.

\begin{code}{c}{Démarrage de la sandbox}
/* count et args contiennent les arguments modifiés pour le démarrage */
vlc_sandbox_t *sandbox = vlc_sandbox_Init( &(struct vlc_sandbox_cfg) {
    .argc       = count,
    .argv       = args,
    /* point d'entrée dans la sandbox */
    .bootstrap  = bootstrap_app,
    .create_vlc = libvlc_new,
    .delete_vlc = libvlc_release,
});

vlc_sandbox_Start( sandbox );
/* À partir d'ici on fonctionne dans la sandbox et ce processus est devenu le
 * broker / orchestrateur. Quand le broker termine, libérer les ressources */
return vlc_sandbox_Close( sandbox );
\end{code}

Dans le cas de la création de worker par fork, la boucle événementielle des
workers est directement appelée. Dans le cas de la création de nouveaux
processus pour chaque worker, \inltype{vlc_sandbox_Init} sera rappelé mais
détectera dans les arguments de la ligne de commande qu'il s'agit d'un worker et
initialisera ses paramètres correctement, puis \inltype{vlc_sandbox_Start}
démarrera la boucle événementielle du worker. On a donc bien une API qui peut
utiliser les deux modes de démarrage des processus.
