Autoblog de

Ce site n'est pas le site officiel de ®om
C'est un blog automatisé qui réplique les articles de

Introducing scrcpy

2018-03-08T12:00:00+01:00 - (source)

I developed an application to display and control Android devices connected on USB. It does not require any root access. It works on GNU/Linux, Windows and Mac OS.


It focuses on:

Like my previous project, gnirehtet, Genymobile accepted to open source it: scrcpy.

You can build, install and run it.

How does scrcpy work?

The application executes a server on the device. The client and the server communicate via a socket over an adb tunnel.

The server streams an H.264 video of the device screen. The client decodes the video frames and displays them.

The client captures input (keyboard and mouse) events, sends them to the server, which injects them to the device.

The documentation gives more details.

Here, I will detail several technical aspects of the application likely to interest developers.

Minimize latency

No buffering

It takes time to encode, transmit and decode the video stream. To minimize latency, we must avoid any additional delay.

For example, let’s stream the screen with screenrecord and play it with VLC:

adb exec-out screenrecord --output-format=h264 - | vlc - --demux h264

Initially, it works, but quickly the latency increases and frames are broken. The reason is that VLC associates a PTS to frames, and buffers the stream to play frames at some target time.

As a consequence, it sometimes prints such errors on stderr:

ES_OUT_SET_(GROUP_)PCR  is called too late (pts_delay increased to 300 ms)

Just before I started the project, Philippe, a colleague who played with WebRTC, advised me to “manually” decode (using FFmpeg) and render frames, to avoid any additional latency. This saved me from wasting time, it was the right solution.

Decoding the video stream to retrieve individual frames with FFmpeg is rather straightforward.

Skip frames

If, for any reason, the rendering is delayed, decoded frames are dropped so that scrcpy always displays the last decoded frame.

Note that this behavior may be changed with a configuration flag:

mesonconf x -Dskip_frames=false

Run a Java main on Android

Capturing the device screen requires some privileges, which are granted to shell.

It is possible to execute Java code as shell on Android, by invoking app_process from adb shell.

Hello, world!

Here is a simple Java application:

public class HelloWorld {
    public static void main(String... args) {
        System.out.println("Hello, world!");

Let’s compile and dex it:

javac -source 1.7 -target 1.7
"$ANDROID_HOME"/build-tools/27.0.2/dx \
    --dex --output classes.dex HelloWorld.class

Then, we push classes.dex to an Android device:

adb push classes.dex /data/local/tmp/

And execute it:

$ adb shell CLASSPATH=/data/local/tmp/classes.dex app_process / HelloWorld
Hello, world!

Access the Android framework

The application can access the Android framework at runtime.

For example, let’s use android.os.SystemClock:

import android.os.SystemClock;

public class HelloWorld {
    public static void main(String... args) {
        System.out.println(" world!");

We link our class against android.jar:

javac -source 1.7 -target 1.7 \
    -cp "$ANDROID_HOME"/platforms/android-27/android.jar

Then run it as before.

Note that scrcpy also needs to access hidden methods from the framework. In that case, linking against android.jar is not sufficient, so it uses reflection.

Like an APK

The execution also works if classes.dex is embedded in a zip/jar:

jar cvf hello.jar classes.dex
adb push hello.jar /data/local/tmp/
adb shell CLASSPATH=/data/local/tmp/hello.jar app_process / HelloWorld

You know an example of a zip containing classes.dex? An APK!

Therefore, it works for any installed APK containing a class with a main method:

$ adb install myapp.apk
$ adb shell pm path
$ adb shell CLASSPATH=/data/app/ \
    app_process / HelloWorld

In scrcpy

To simplify the build system, I decided to build the server as an APK using gradle, even if it’s not a real Android application: gradle provides tasks for running tests, checking style, etc.

Invoked that way, the server is authorized to capture the device screen.

Improve startup time

Quick installation

Nothing is required to be installed on the device by the user: at startup, the client is responsible for executing the server on the device.

We saw that we can execute the main method of the server from an APK either:

Which one to choose?

$ time adb install server.apk
real    0m0,963s

$ time adb push server.apk /data/local/tmp/
real    0m0,022s

So I decided to push.

Note that /data/local/tmp is readable and writable by shell, but not world-writable, so a malicious application may not replace the server just before the client executes it.


If you executed the Hello, world! in the previous section, you may have noticed that running app_process takes some time: Hello, World! is not printed before some delay (between 0.5 and 1 second).

In the client, initializing SDL also takes some time.

Therefore, these initialization steps have been parallelized.

Clean up the device

After usage, we want to remove the server (/data/local/tmp/scrcpy-server.jar) from the device.

We could remove it on exit, but then, it would be left on device disconnection.

Instead, once the server is opened by app_process, scrcpy unlinks (rm) it. Thus, the file is present only for less than 1 second (it is removed even before the screen is displayed).

The file itself (not its name) is actually removed when the last associated open file descriptor is closed (at the latest, when app_process dies).

Handle text input

Handling input received from a keyboard is more complicated than I thought.


There are 2 kinds of “keyboard” events:

Key events provide both the scancode (the physical location of a key on the keyboard) and the keycode (which depends on the keyboard layout). Only keycodes are used by scrcpy (it doesn’t need the location of physical keys).

However, key events are not sufficient to handle text input:

Sometimes it can take multiple key presses to produce a character. Sometimes a single key press can produce multiple characters.

Even simple characters may not be handled easily with key events, since they depend on the layout. For example, on a French keyboard, typing . (dot) generates Shift+;.

Therefore, scrcpy forwards key events to the device only for a limited set of keys. The remaining are handled by text input events.

Inject text

On the Android side, we may not inject text directly (injecting a KeyEvent created by the relevant constructor does not work). Instead, we can retrieve a list of KeyEvents to generate for a char[], using getEvents(char[]).

For example:

char[] chars = {'?'};
KeyEvent[] events = charMap.getEvents(chars);

Here, events is initialized with an array of 4 events:

  2. press KEYCODE_SLASH
  3. release KEYCODE_SLASH

Injecting those events correctly generates the char '?'.

Handle accented characters

Unfortunately, the previous method only works for ASCII characters:

char[] chars = {'é'};
KeyEvent[] events = charMap.getEvents(chars);
// events is null!!!

I first thought there was no way to inject such events from there, until I discussed with Philippe (yes, the same as earlier), who knew the solution: it works when we decompose the characters using combining diacritical dead key characters.

Concretely, instead of injecting "é", we inject "\u0301e":

char[] chars = {'\u0301', 'e'};
KeyEvent[] events = charMap.getEvents(chars);
// now, there are events

Therefore, to support accented characters, scrcpy attempts to decompose the characters using KeyComposition.

EDIT: Accented characters do not work with the virtual keyboard Gboard (the default Google keyboard), but work with the default (AOSP) keyboard and SwiftKey.

Set a window icon

The application window may have an icon, used in the title bar (for some desktop environments) and/or in the desktop taskbar.

The window icon must be set from an SDL_Surface by SDL_SetWindowIcon. Creating the surface with the icon content is up to the developer. For exemple, we could decide to load the icon from a PNG file, or directly from its raw pixels in memory.

Instead, another colleague, Aurélien, suggested I use the XPM image format, which is also a valid C source code: icon.xpm.

Note that the image is not the content of the variable icon_xpm declared in icon.xpm: it’s the whole file! Thus, icon.xpm may be both directly opened in Gimp and included in C source code:

#include "icon.xpm"

As a benefit, we directly “recognize” the icon from the source code, and we can patch it easily: in debug mode, the icon color is changed.


Developing this project was an awesome and motivating experience. I’ve learned a lot (I never used SDL or libav/FFmpeg before).

The resulting application works better than I initially expected, and I’m happy to have been able to open source it.

Discuss on reddit and Hacker News.

Gnirehtet réécrit en Rust

2017-09-21T17:00:00+02:00 - (source)

Il y a quelques mois, j’ai présenté Gnirehtet, un outil de reverse tethering pour Android que j’ai écrit en Java.

Depuis, je l’ai réécrit en Rust.

Et il est également open source ! Téléchargez-le, branchez un téléphone ou une tablette Android, et exécutez :

./gnirehtet run

(adb doit être installé)

Pourquoi Rust?

À Genymobile, nous voulions que Gnirehtet ne nécessite pas d’environnement d’exécution Java (JRE), donc le besoin principal était de compiler l’application vers un binaire exécutable natif.

Par conséquent, j’ai d’abord pensé la réécrire en C ou C++. Mais à ce moment-là (début mai), apprendre Rust m’intéressait, après avoir vaguement entendu parler de ses fonctionnalités:

Cependant, je n’avais jamais écrit une ligne de Rust ni entendu parler de son système de possession, d’emprunt ou de durées de vie.

Mais je suis convaincu que le meilleur moyen d’apprendre un langage de programmation est de travailler à plein temps sur un projet dans ce langage.

J’étais motivé, donc après avoir vérifié que ça pouvait convenir (en gros, j’ai écrit un exemple utilisant la bibliothèque d’I/O asynchrone mio, et je l’ai exécuté à la fois sur Linux et Windows), j’ai décidé de réécrire Gnirehtet en Rust.

Apprendre Rust

Pendant la réécriture, j’ai dévoré successivement le Rust book, Rust by example et le Rustonomicon. J’ai beaucoup appris, et j’aime énormément ce langage. Beaucoup de ses fonctionnalités me manquent maintenant quand je travaille sur un projet C++ :

À propos de l’apprentissage, Paul Graham a écrit:

Reading and experience train your model of the world. And even if you forget the experience or what you read, its effect on your model of the world persists. Your mind is like a compiled program you’ve lost the source of. It works, but you don’t know why.

Pour les non-anglophones, ma propre traduction :

La lecture et l’expérience entraînent votre modèle du monde. Et même si vous oubliez l’expérience ou ce que vous avez lu, son effet sur votre modèle du monde persiste. Votre esprit est comme un programme compilé dont vous auriez perdu le code source. Ça fonctionne, mais vous ne savez pas pourquoi.

Certains des concepts de Rust (comme les durées de vie ou la sémantique de mouvement par défaut) m’ont fourni un jeu de données significativement différent, qui a sans aucun doute affecté mon modèle du monde (de la programmation).

Je ne vais pas présenter toutes ces fonctionnaliés (cliquez sur les liens de la documentation si ça vous intéresse). À la place, je vais essayer d’expliquer où et pourquoi Rust a resisté au design que je voulais implémenter, et comment repenser les problèmes dans le périmètre des contraintes de Rust.

La partie suivant nécessite une certaine connaissance de Rust. Vous pourriez vouloir la passer pour aller directement aux stats.


Je trouvais la conception de l’application Java plutôt réussie, donc je voulais reproduire l’architecture globale dans la version Rust (avec d’éventuelles adaptations pour la rustifier).

Mais j’ai lutté sur les détails, en particulier pour satisfaire le borrow checker. Les règles sont simples:

First, any borrow must last for a scope no greater than that of the owner. Second, you may have one or the other of these two kinds of borrows, but not both at the same time:

  • one or more references (&T) to a resource,
  • exactly one mutable reference (&mut T).

En français :

Premièrement, aucun emprunt ne doit avoir une portée plus grande que celle de son propriétaire. Deuxièmement, vous pouvez avoir l’un ou l’autre de ces types d’emprunts, mais pas les deux à la fois:

  • une ou plusieurs références (&T) vers une ressource,
  • exactement une référence mutable (&mut T).

Cependant, il m’a fallu un peu de temps pour réaliser comment elles entrent en conflit avec certains principes ou modèles de conception.

Voici donc mes retours. J’ai sélectionné 4 sujets qui sont suffisamment généraux pour être indépendants de ce projet particulier :


Les règles d’emprunt contraignent l’encapsulation. C’est la première conséquence que j’ai réalisée.

Voici un exemple canonique :

pub struct Data {
    header: [u8; 4],
    payload: [u8; 20],

impl Data {
    pub fn new() -> Self {
        Self {
            header: [0; 4],
            payload: [0; 20],

    pub fn header(&mut self) -> &mut [u8] {
        &mut self.header

    pub fn payload(&mut self) -> &mut [u8] {
        &mut self.payload

fn main() {
    let mut data = Data::new();
    let header = data.header();
    let payload = data.payload();

Nous créons juste une nouvelle instance de Data, puis associons à des variables locales des références mutables vers les tableaux header et payload, en passant par des accesseurs.

Cependant, cela ne compile pas :

$ rustc
error[E0499]: cannot borrow `data` as mutable more than once at a time
25 |     let header = data.header();
   |                  ---- first mutable borrow occurs here
26 |     let payload = data.payload();
   |                   ^^^^ second mutable borrow occurs here
27 | }
   | - first borrow ends here

Le compilateur ne peut pas faire l’hypothèse que header() et payload() retournent des références vers des données disjointes dans la structure Data. Par conséquent, chacun emprunte la structure data entièrement. Vu que les règles d’emprunt interdisent d’obtenir deux références mutables vers la même ressource, il rejette le second appel.

Parfois, nous faisons face à des limitations temporaires parce que le compilateur n’est pas (encore) assez malin. Ce n’est pas le cas ici : l’implémentation de header() pourrait très bien retourner une référence vers payload, ou écrire dans le tableau payload, enfreignant ainsi les règles d’emprunt. Et la validité d’un appel d’une méthode ne peut pas dépendre de l’implementation de la méthode.

Pour corriger le problème, le compilateur doit être capable de savoir que les variables locales header et payload référencent des données disjointes, par exemple en accédant aux champs directement :

    let header = &mut data.header;
    let payload = &mut data.payload;

ou en exposant une méthode fournissant les deux références simultanément :

struct Data {
    fn header_and_payload(&mut self) -> (&mut [u8], &mut [u8]) {
        (&mut self.header, &mut self.payload)

fn main() {
    let mut data = Data::new();
    let (header, payload) = data.header_and_payload();

De même, dans l’implémentation d’une structure, les règles d’emprunt empêchent de factoriser du code dans une méthode privée facilement. Prenons cet exemple (artificiel) :

pub struct Data {
    buf: [u8; 20],
    prefix_length: usize,
    sum: u32,
    port: u16,

impl Data {
    pub fn update_sum(&mut self) {
        let content = &self.buf[self.prefix_length..];
        self.sum = content.iter().cloned().map(u32::from).sum();

    pub fn update_port(&mut self) {
        let content = &self.buf[self.prefix_length..];
        self.port = (content[2] as u16) << 8 | content[3] as u16;

Ici, le champ buf est un tableau stockant un préfixe et un contenu de manière contiguë.

Nous voulons factoriser la manière dont nous récupérons la slice content, pour que les méthodes update_*() n’aient pas à se préoccuper des détails. Essayons :

 impl Data {
     pub fn update_sum(&mut self) {
-        let content = &self.buf[self.prefix_length..];
+        let content = self.content();
         self.sum = content.iter().cloned().map(u32::from).sum();

     pub fn update_port(&mut self) {
-        let content = &self.buf[self.prefix_length..];
+        let content = self.content();
         self.port = (content[2] as u16) << 8 | content[3] as u16;
+    fn content(&mut self) -> &[u8] {
+        &self.buf[self.prefix_length..]
+    }

Malheureusement, cela ne compile pas :

error[E0506]: cannot assign to `self.sum` because it is borrowed
10 |         let content = self.content();
   |                       ---- borrow of `self.sum` occurs here
11 |         self.sum = content.iter().cloned().map(u32::from).sum();
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.sum` occurs here

error[E0506]: cannot assign to `self.port` because it is borrowed
15 |         let content = self.content();
   |                       ---- borrow of `self.port` occurs here
16 |         self.port = (content[2] as u16) << 8 & content[3] as u16;

Comme dans l’exemple précédent, récupérer une référence à travers une méthode emprunte la structure complète (ici, self).

Pour contourner le problème, nous pouvons expliquer au compilateur que les champs sont disjoints :

impl Data {
    pub fn update_sum(&mut self) {
        let content = Self::content(&self.buf, self.prefix_length);
        self.sum = content.iter().cloned().map(u32::from).sum();

    pub fn update_port(&mut self) {
        let content = Self::content(&self.buf, self.prefix_length);
        self.port = (content[2] as u16) << 8 | content[3] as u16;

    fn content(buf: &[u8], prefix_length: usize) -> &[u8] {

Ça compile, mais cela va totalement à l’encontre de la factorisation : l’appelant doit fournir les champs nécessaires.

Comme alternative, nous pouvons utiliser une macro pour inliner le code :

macro_rules! content {
    ($self:ident) => {

impl Data {
    pub fn update_sum(&mut self) {
        let content = content!(self);
        self.sum = content.iter().cloned().map(u32::from).sum();

    pub fn update_port(&mut self) {
        let content = content!(self);
        self.port = (content[2] as u16) << 8 | content[3] as u16;

Mais c’est loin d’être idéal.

Je pense que nous devons juste l’accepter : l’encapsulation entre parfois en conflit avec les règles d’emprunt. Après tout, ce n’est pas si surprenant : imposer les règles d’emprunt nécessite de suivre chaque accès concret aux ressources, alors que l’encapsulation vise à les abstraire.


Le design pattern observateur est utile pour enregistrer des événements sur un objet.

Dans certains cas, ce pattern pose des difficultés d’implémentation en Rust.

Pour faire simple, considérons que les événements sont des valeurs u32. Voici une implémentation possible :

pub trait EventListener {
    fn on_event(&self, event: u32);

pub struct Notifier {
    listeners: Vec<Box<EventListener>>,

impl Notifier {
    pub fn new() -> Self {
        Self { listeners: Vec::new() }

    pub fn register<T: EventListener + 'static>(&mut self, listener: T) {

    pub fn notify(&self, event: u32) {
        for listener in &self.listeners {

Par commodité, implémentons notre trait EventListener pour les closures :

impl<F: Fn(u32)> EventListener for F {
    fn on_event(&self, event: u32) {

Ainsi, son utilisation est simple :

    let mut notifier = Notifier::new();
    notifier.register(|event| println!("received [{}]", event));

Cela affiche :

received [42]

Jusqu’ici, tout va bien.

Cependant, les choses se compliquent si nous voulons modifier un état sur la réception d’un événement. Par exemple, implémentons une structure pour stocker tous les événements reçus :

pub struct Storage {
    events: Vec<u32>,

impl Storage {
    pub fn new() -> Self {
        Self { events: Vec::new() }

    pub fn store(&mut self, value: u32) {;

    pub fn events(&self) -> &Vec<u32> {

Pour pouvoir remplir ce Storage sur chaque événement reçu, nous devons d’une manière ou d’une autre le passer avec l’event listener, qui sera stocké dans le Notifier. Par conséquent, nous avons besoin qu’une instance de Storage soit partagée entre le code appelant et le Notifier.

Avoir deux références mutables vers le même objet enfreint évidemment les règles d’emprunt, donc nous avons besoin d’un pointeur à compteur de références.

Cependant, un tel pointeur est en lecture seul, donc nous avons également besoin d’un RefCell pour la mutabilité intérieure.

Ainsi, nous allons utiliser une instance de Rc<RefCell<Storage>>. Cela peut sembler trop verbeux, mais utiliser Rc<RefCell<T>> (ou Arc<Mutex<T>> pour la thread-safety) est très courant en Rust. Et il y a pire.

Voici ce que donne le code client :

    use std::cell::RefCell;
    use std::rc::Rc;

    let mut notifier = Notifier::new();

    // first Rc to the Storage
    let rc = Rc::new(RefCell::new(Storage::new()));

    // second Rc to the Storage
    let rc2 = rc.clone();

    // register the listener saving all the received events to the Storage
    notifier.register(move |event| rc2.borrow_mut().store(event));

    assert_eq!(&vec![3, 141, 59], rc.borrow().events());

De cette manière, le Storage est correctement modifié à partir de l’event listener.

Tout n’est pas résolu pour autant. Dans cet exemple, c’était facile, nous avions accès à l’instance Rc<RefCell<Storage>>. Comment faire si nous avons seulement accès au Storage, par exemple si nous voulons que le Storage s’enregistre lui-même à partir de l’une de ses méthodes, sans que l’appelant n’ait à fournir l’instance Rc<RefCell<Storage>> ?

impl Storage {
    pub fn register_to(&self, notifier: &mut Notifier) {
        notifier.register(move |event| {
            /* how to retrieve a &mut Storage from here? */

Nous devons trouver un moyen de récupérer le Rc<RefCell<Storage>> à partir du Storage.

Pour cela, l’idée consiste à rendre Storage conscient de son pointeur à compteur de références. Bien sûr, cela n’a du sens que si Storage est construit dans un Rc<RefCell<Storage>>.

C’est exactement ce que enable_shared_from_this fournit en C++, donc nous pouvons nous inspirer de son fonctionnement : juste stocker un Weak<RefCell<…>>, downgradé à partir du Rc<RefCell<…>>, dans la structure elle-même. De cette manière, nous pouvons l’utiliser pour récupérer une référence &mut Storage à partir de l’event listener :

use std::rc::{Rc, Weak};
use std::cell::RefCell;

pub struct Storage {
    self_weak: Weak<RefCell<Storage>>,
    events: Vec<u32>,

impl Storage {
    pub fn new() -> Rc<RefCell<Self>> {
        let rc = Rc::new(RefCell::new(Self {
            self_weak: Weak::new(), // initialize empty
            events: Vec::new(),
        // set self_weak once we get the Rc instance
        rc.borrow_mut().self_weak = Rc::downgrade(&rc);

    pub fn register_to(&self, notifier: &mut Notifier) {
        let rc = self.self_weak.upgrade().unwrap();
        notifier.register(move |event| rc.borrow_mut().store(event))

Voici comment l’utiliser :

    let mut notifier = Notifier::new();
    let rc = Storage::new();
    rc.borrow().register_to(&mut notifier);
    assert_eq!(&vec![3, 141, 59], rc.borrow().events());

Il est donc possible d’implémenter le design pattern observateur en Rust, mais c’est un peu plus difficile qu’en Java ;-)

Lorsque c’est possible, il est probablement préférable de l’éviter.

Partage de données mutables

Mutable references cannot be aliased.

En français :

Les références mutables ne peuvent pas être aliasées.

Comment partager des données mutables, alors ?

Nous avons vu que nous pouvions utiliser Rc<RefCell<…>> (ou Arc<Mutex<…>>), qui impose les règles d’emprunt à l’exécution. Cependant, ce n’est pas toujours désirable :

Au lieu de cela, nous pourrions utiliser des pointeurs bruts manuellement dans du code non-sûr, mais alors ce serait non-sûr.

Et il y a une autre solution, qui consiste à exposer des vues temporaires d’emprunt d’un objet. Laissez-moi expliquer.

Dans Gnirehtet, un paquet contient une référence vers les données brutes (stockées dans un buffer quelque part) ainsi que les valeur des champs des en-têtes IP et TCP/UDP (parsées à partir du tableau d’octets brut). Nous aurions pu utiliser une structure à plat pour tout stocker :

pub struct Packet<'a> {
    raw: &'a mut [u8],
    ipv4_source: u32,
    ipv4_destination: u32,
    ipv4_protocol: u8,
    // + other ipv4 fields
    transport_source: u16,
    transport_destination: u16,
    // + other transport fields

Le Packet aurait fourni des setters pour tous les champs d’en-têtes (modifiant à la fois les champs du paquet et le tableau d’octets). Par exemple :

impl<'a> Packet<'a> {
    pub fn set_transport_source(&mut self, transport_source: u16) {
        self.transport_source = transport_source;
        let transport = &mut self.raw[20..];
        BigEndian::write_u16(&mut transport[0..2], port);

Mais cette conception ne serait pas terrible (surtout que les champs d’en-têtes TCP et UDP sont différents).

À la place, nous voudrions extraire les en-têtes d’IP et de transport vers des structures séparées, gérant leur propre partie du tableau d’octets :

// violates the borrowing rules

pub struct Packet<'a> {
    raw: &'a mut [u8], // the whole packet (including headers)
    ipv4_header: Ipv4Header<'a>,
    transport_header: TransportHeader<'a>,

pub struct Ipv4Header<'a> {
    raw: &'a mut [u8], // slice related to ipv4 headers
    source: u32,
    destination: u32,
    protocol: u8,
    // + other ipv4 fields

pub struct TransportHeader<'a> {
    raw: &'a mut [u8], // slice related to transport headers
    source: u16,
    destination: u16,
    // + other transport fields

Vous avez immédiatement repéré le problème : il y a plusieurs références vers la même ressource, le tableau d’octets raw, en même temps.

Remarquez que diviser le tableau n’est pas une possibilité ici, vu que les slices de raw se chevauchent : nous avons besoin d’écrire le paquet complet en une seule fois vers la couche réseau, donc le tableau raw dans Packet doit inclure les headers.

Nous avons besoin d’une solution compatible avec les règles d’emprunt.

Voici celle à laquelle je suis parvenu :

Et voici une simplification de l’implémentation réelle :

pub struct Packet<'a> {
    raw: &'a mut [u8],
    ipv4_header: Ipv4HeaderData,
    transport_header: TransportHeaderData,

pub struct Ipv4HeaderData {
    source: u32,
    destination: u32,
    protocol: u8,
    // + other ipv4 fields

pub struct TransportHeaderData {
    source: u16,
    destination: u16,
    // + other transport fields

pub struct Ipv4Header<'a> {
    raw: &'a mut [u8],
    data: &'a mut Ipv4HeaderData,

pub struct TransportHeader<'a> {
    raw: &'a mut [u8],
    data: &'a mut TransportHeaderData,

impl<'a> Packet<'a> {
    pub fn ipv4_header(&mut self) -> Ipv4Header {
        Ipv4Header {
            raw: &mut self.raw[..20],
            data: &mut self.ipv4_header,

    pub fn transport_header(&mut self) -> TransportHeader {
        TransportHeader {
            raw: &mut self.raw[20..40],
            data: &mut self.transport_header,

Les setters sont implémentés sur les vues, où ils détiennent une référence mutable vers le tableau brut :

impl<'a> TransportHeader<'a> {
    pub fn set_source(&mut self, source: u16) { = source;
        BigEndian::write_u16(&mut raw[0..2], source);

    pub fn set_destination(&mut self, destination: u16) { = destination;
        BigEndian::write_u16(&mut raw[2..4], destination);

De cette manière, les règles d’emprunt sont respectées, et l’API est élégante :

    let mut packet = ;
    // "transport_header" borrows "packet" during its scope
    let mut transport_header = packet.transport_header();

Limitations du compilateur

Rust est un langage jeune, et le compilateur a quelques problèmes ennuyeux.

Le pire, d’après moi, est lié aux durées de vie non-lexicales, qui provoque des erreurs inattendues :

struct Container {
    vec: Vec<i32>,

impl Container {
    fn find(&mut self, v: i32) -> Option<&mut i32> {
        None // we don't care the implementation

    fn get(&mut self, v: i32) -> &mut i32 {
        if let Some(x) = self.find(v) {
            return x;
error[E0499]: cannot borrow `self.vec` as mutable more than once at a time
11 |         if let Some(x) = self.find(v) {
   |                          ---- first mutable borrow occurs here
14 |         self.vec.push(v);
   |         ^^^^^^^^ second mutable borrow occurs here
15 |         self.vec.last_mut().unwrap()
16 |     }
   |     - first borrow ends here

Heureusement, cela devrait être corrigé prochainement.

La fonctionnalité d’Impl Trait, permettant aux fonctions de retourner des types abstraits non-boxés, devrait aussi améliorer l’expérience (il y a aussi une proposition étendue).

Le compilateur produit généralement des messages d’erreur très utiles. Mais quand ce n’est pas le cas, ils peuvent être très déroutants.

Sûreté et pièges

Le premier chapitre du Rustonomicon dit :

Safe Rust is For Reals Totally Safe.


Safe Rust is the true Rust programming language. If all you do is write Safe Rust, you will never have to worry about type-safety or memory-safety. You will never endure a null or dangling pointer, or any of that Undefined Behavior nonsense.

En français :

La partie Sûre de Rust est Réellement Totallement Sûre.


Le Rust Sûr est le vrai langage de programmation Rust. Si vous n’écrivez que du Rust Sûr, vous n’aurez jamais à vous inquiétez de la sûreté des types ou de la mémoire. Vous n’aurez jamais à supporter un pointeur null ou dangling, ou l’un de ces comportements indéfinis insensés.

C’est le but. Et c’est presque vrai.


Dans le passé, il a été possible d’écrire du code Rust sûr accédant à de la mémoire libérée.

Cette “leakpocalypse” a conduit à la clarification des guaranties de sûreté : ne pas exécuter un destructeur est maintenant considéré sûr. En d’autres termes, la sûreté mémoire ne peut plus reposer sur RAII (en fait, elle n’a jamais pu, mais cela n’a été remarqué que tardivement).

En conséquence, std::mem::forget est maintenant sûr, et JoinGuard a été déprécié et supprimé de la bibliothèque standard (il a été déplacé vers un crate séparé).

Les autres outils s’appuyant sur RAII (comme Vec::drain()) doivent prendre des précautions particulières pour garantir que la mémoire ne sera pas corrompue.

Ouf, la sûreté mémoire est (maintenant) sauvée.

Infinité indéfinie

En C et C++, les boucles infinies sans effets de bords sont un cas d’undefined behavior. À cause de cela, il est possible d’écrire des programmes qui, de façon inattendue, réfutent le dernier théorème de Fermat.

En pratique, le compilateur Rust s’appuie sur LLVM, qui (actuellement) applique ses optimisations en faisant l’hypothèse que les boucles infinies sans effets de bords ont un comportement indéfini. En conséquence, de tels undefined behaviors se produisent également en Rust.

Voici un exemple minimal pour l’observer :

fn infinite() {
    loop {}

fn main() {

Quand on l’exécute sans optimisations, il se comporte comme “attendu” :

$ rustc && ./ub
^C                    (infinite loop, interrupt it)

Mais activer les optimisations fait paniquer le programme :

$ rustc -O && ./ub
thread 'main' panicked at 'assertion failed: c.borrow().is_none()', /checkout/src/libstd/sys_common/
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Nous pouvons aussi produire des résultats inattendus sans plantage :

fn infinite(mut value: u32) {
    // infinite loop unless value initially equals 0
    while value != 0 {
        if value != 1 {
            value -= 1;

fn main() {
$ rustc && ./ub
^C                    (infinite loop, interrupt it)

Mais avec optimisations :

$ rustc -O && ./ub

C’est un cas particulier, qui sera probablement corrigé dans le futur. En pratique, les garanties de sûreté de Rust sont très fortes (au prix d’être contraignantes).

Erreur de segmentation

Cette section a été ajoutée après la publication.

Il y a d’autres sources d’undefined behaviors (voir les issues taggées I-unsound).

Par exemple, caster une valeur flottante ne pouvant pas être représentée dans le type cible est un undefined behavior, qui peut être propagé pour provoquer une erreur de segmentation :

pub fn f(ary: &[u8; 5]) -> &[u8] {
    let idx = 1e100f64 as usize;

fn main() {
    println!("{}", f(&[1; 5])[0xdeadbeef]);
rustc -O && ./ub
Erreur de segmentation


C’est tout pour mes retours sur le langage lui-même.

En supplément, comparons les versions Java et Rust du serveur relais.

Nombre de lignes

$ cloc relay-{java,rust}/src
Language                     files          blank        comment           code
Rust                            29            687            655           4506
Java                            37            726            701           2931

(tests included)

Le projet Rust est significativement plus gros, pour plusieurs raisons :

La version Java contient plus de fichiers car les tests unitaires sont séparés, alors qu’en Rust ils se trouvent dans le même fichier que les classes qu’ils testent.

Juste pour information, voici les résultats pour le client Android :

$ cloc app/src
Language                     files          blank        comment           code
Java                            15            198            321            875
XML                              6              7              2             76
SUM:                            21            205            323            951

Taille des binaires

Java     gnirehtet.jar                   61K
Rust     gnirehtet                      3.0M
         after "strip -g gnirehtet"     747K
         after "strip gnirehtet"        588K

Le binaire Java lui-même est bien plus petit. La comparaison n’est pas juste cependant, vu qu’il nécessite l’environnement d’exécution Java :

$ du -sh /usr/lib/jvm/java-1.8.0-openjdk-amd64/
156M	/usr/lib/jvm/java-1.8.0-openjdk-amd64/

Utilisation mémoire

Avec une seule connection TCP ouvert, voici la consommation mémoire pour le serveur relais en Java :

$ sudo pmap -x $RELAY_JAVA_PID
                  Kbytes     RSS   Dirty
total kB         4364052   86148   69316

(résultat filtré)

Et pour le serveur relais en Rust :

$ sudo pmap -x $RELAY_RUST_PID
                  Kbytes     RSS   Dirty
total kB           19272    2736     640

Regardez la valeur RSS, qui indique la mémoire réellement utilisée.

Comment on pouvait s’y attendre, la version Java consomme plus de mémoire (86Mo) que la version Rust (moins de 3Mo). De plus, sa valeur est instable à cause de l’allocation de petits objets et leur garbage collection, qui génère aussi davantage de dirty pages. La valeur pour Rust, quant à elle, est très stable : une fois la connection créée, il n’y a plus d’allocations mémoire du tout.

Utilisation CPU

Pour comparer l’utilisation CPU, voici mon scénario : un fichier de 500Mo est hébergé par Apache sur mon ordinateur, je démarre le serveur relais avec perf stat, puis je télécharge le fichier à partir de Firefox sur Android. Dès que le fichier est téléchargé, je stoppe le serveur relais (Ctrl+C).

Voici les résultats pour la version Java :

$ perf stat -B java -jar gnirehtet.jar relay
 Performance counter stats for 'java -jar gnirehtet.jar relay':

      11805,458302      task-clock:u (msec)       #    0,088 CPUs utilized
                 0      context-switches:u        #    0,000 K/sec
                 0      cpu-migrations:u          #    0,000 K/sec
            28 618      page-faults:u             #    0,002 M/sec
    17 908 360 446      cycles:u                  #    1,517 GHz
    13 944 172 792      stalled-cycles-frontend:u #   77,86% frontend cycles idle
    18 437 279 663      instructions:u            #    1,03  insn per cycle
                                                  #    0,76  stalled cycles per insn
     3 088 215 431      branches:u                #  261,592 M/sec
        70 647 760      branch-misses:u           #    2,29% of all branches

     133,975117164 seconds time elapsed

Et pour la version Rust :

$ perf stat -B ./gnirehtet relay
 Performance counter stats for 'target/release/gnirehtet relay':

       2707,479968      task-clock:u (msec)       #    0,020 CPUs utilized
                 0      context-switches:u        #    0,000 K/sec
                 0      cpu-migrations:u          #    0,000 K/sec
             1 001      page-faults:u             #    0,370 K/sec
     1 011 527 340      cycles:u                  #    0,374 GHz
     2 033 810 378      stalled-cycles-frontend:u #  201,06% frontend cycles idle
       981 103 003      instructions:u            #    0,97  insn per cycle
                                                  #    2,07  stalled cycles per insn
        98 929 222      branches:u                #   36,539 M/sec
         3 220 527      branch-misses:u           #    3,26% of all branches

     133,766035253 seconds time elapsed

Je ne suis pas un expert pour analyser les résultats, mais de ce que je comprends de la valeur task-clock:u, la version Rust consomme 4× moins de temps CPU.


Réécrire Gnirehtet en Rust a été une formidable expérience, où j’ai appris un super langage et de nouveaux concepts de programmation. Et maintenant, nous avons une application native avec de meilleures performances.

Bon reverse tethering !

Discussions sur reddit et Hacker News.

Fusionner deux dépôts git

2017-07-12T20:30:00+02:00 - (source)

Ce billet explique comment fusionner un dépôt git (avec son historique) dans un sous-répertoire d’un autre dépôt git.

Cas d’usage

Mon projet principal se trouve dans un dépôt main. J’ai démarré dans un autre dépôt un projet other, que je souhaite finalement intégrer dans un sous-répertoire sub/ du projet principal, en conservant son historique. Après cette fusion, je ne garderai que le dépôt principal.


Les deux projets se trouvent dans le répertoire courant :

$ ls
main  other

Dans le dépôt main, copier la branche master de other dans une nouvelle branche tmp :

cd main
git fetch ../other master:tmp

Le dépôt main contient alors les historiques disjoints des deux projets.

Nous allons maintenant réécrire l’historique complet de la branche tmp pour déplacer tout le contenu dans un sous-répertoire sub/, grâce une commande donnée en exemple de man git filter-branch :

git checkout tmp
git filter-branch --index-filter \
    'git ls-files -s | sed "s-\t\"*-&sub/-" |
        GIT_INDEX_FILE=$ \
            git update-index --index-info &&
     mv "$" "$GIT_INDEX_FILE"'

À ce stade, nous avons toujours deux historiques indépendants, mais le contenu lié à la branche tmp se trouve dans le sous-répertoire sub/.

A---B---C---D master

  X---Y---Z tmp

La dernière étape consiste à relier les deux historiques, soit grâce à un rebase, soit grâce à un merge.

Un rebase réécrit l’historique du sous-projet sur la branche master :

git rebase master

# A---B---C---D---X'--Y'--Z' master

Un merge relie juste les deux historiques grâce à un commit de merge :

git merge tmp --allow-unrelated-histories

# A---B---C---D---M  master
#                /
#       X---Y---Z tmp


J’ai débuté la réécriture du serveur relais de gnirehtet en Rust dans un dépôt séparé. Maintenant qu’il commence à fonctionner, je l’ai fusionné dans un sous-répertoire du dépôt principal tout en conservant l’historique :

git fetch ../rustrelay master:tmp
git checkout tmp
git filter-branch --index-filter \
    'git ls-files -s | sed "s-\t\"*-&rustrelay/-" |
        GIT_INDEX_FILE=$ \
            git update-index --index-info &&
     mv "$" "$GIT_INDEX_FILE"'
git rebase master


2017-03-30T11:50:00+02:00 - (source)

Durant ces dernières semaines chez Genymobile, j’ai développé un outil de reverse tethering pour Android, permettant aux téléphones (et aux tablettes) d’utiliser la connexion internet de l’ordinateur sur lequel ils sont branchés, sans accès root (ni sur le téléphone, ni sur le PC). Il fonctionne sur GNU/Linux, Windows et Mac OS.

Nous avons décidé de le publier en open source, sous le nom de gnirehtet.

Oui, c’est un nom bizarre, jusqu’à ce qu’on réalise qu’il s’agit du résultat de la commande bash :

rev <<< tethering


Il suffit de télécharger la dernière release, de l’extraire, et d’exécuter la commande suivante sur le PC :

./gnirehtet rt

Une fois activé, un logo en forme de clé apparaît dans la barre de statut du téléphone :


Lisez le fichier README pour plus de détails.


Le projet est composé de deux parties :

Depuis, je l’ai réécrit en Rust.

Le client s’enregistre en tant que VPN, de manière à intercepter tout le trafic réseau du téléphone, sous la forme de byte[] de paquets IPv4 bruts, qu’il transmet alors vers le serveur relais sur une connexion TCP (établie par-dessus adb).

Le serveur relais analyse les en-têtes des paquets, ouvre des connexions à partir du PC vers les adresses de destinations demandées, et relaie le contenu dans les deux sens en suivant les protocoles UDP et TCP. Il crée et renvoie des paquets de réponse vers le client Android, qui les écrit sur l’interface VPN.

D’une certaine manière, le serveur relais se comporte comme un NAT, en cela qu’il ouvre des connexions pour le compte d’autres machines qui n’ont pas accès au réseau. Cependant, il diffère des NAT standards dans la manière dont il communique avec les clients, en utilisant un protocole spécifique (très simple) sur une connexion TCP.


Pour plus de détails, lisez la page développeurs.


Une fois que l’application est capable d’intercepter l’intégralité du traffic réseau du téléphone, différentes approches sont possibles. Voici celles que j’ai considérées.

TL;DR: J’ai d’abord étudié l’utilisation d’un “TUN device” sur le PC, mais ça ne répondait pas à nos besoins. J’ai ensuite voulu utiliser SOCKS pour bénéficier des serveurs existants, mais des contraintes nous empêchaient de relayer le trafic UDP. Alors j’ai implémenté gnirehtet.

TUN device

Lors de mes recherches pour savoir comment implémenter le reverse tethering, j’ai d’abord trouvé des projets créant un TUN device sur l’ordinateur (vpn-reverse-tether and SimpleRT).

Cette conception fonctionne très bien, et a plusieurs avantages :

Cependant :

Il se peut néanmoins que ces applications répondent davantage à vos besoins.


Afin d’éviter d’avoir à développer un serveur relais spécifique, ma première idée était d’écrire un client qui parlait le protocole SOCKS (suivant le RFC 1928). Ainsi, il serait possible d’utiliser n’importe quel serveur SOCKS existant, par exemple celui fourni par ssh -D.

Vous l’avez probablement déjà utilisé pour éviter le filtrage des pare-feux en entreprise. Pour cela, démarrez le tunnel :

ssh mon_serveur -ND1080

Puis configurez votre navigateur pour utiliser le proxy SOCKS localhost:1080. N’oubliez pas d’activer la résolution DNS distante pour résoudre les noms de domaine à partir de mon_serveur (dans Firefox, activez network.proxy.socks_remote_dns dans about:config).

Malheureusement, l’implémentation d’OpenSSH ne supporte pas UDP, même si le protocole SOCKS5 lui-même le supporte. Et nous avons besoin d’UDP, au moins pour les requêtes DNS (ainsi que pour NTP).

Si vous avez lu attentivement les deux paragraphes précédents, vous devriez vous demander :

Comment Firefox peut-il résoudre les noms de domaine à distance alors que le proxy SOCKS d’OpenSSH ne supporte même pas UDP ?

La réponse se trouve dans la section 4 du RFC : l’adresse de destination demandée peut être une IPv4, une IPv6 ou un nom de domaine. Par contre, pour utiliser cette fonctionnalité, le client (par exemple Firefox) doit savoir qu’il passe par un proxy (puisqu’il doit explicitement passer le nom de domaine au lieu de le résoudre localement), alors que notre reverse tethering doit être transparent.

Mais tout n’est pas perdu. Certes, OpenSSH ne supporte pas UDP, mais ce n’est qu’une implémentation spécifique, nous pourrions en utiliser une autre. Malheureusement, SOCKS5 relaie UDP sur UDP, et les téléphones et l’ordinateur communiquent sur adb (grâce à adb reverse), qui ne supporte pas non plus la redirection de ports UDP.

Peut-être que nous pourrions au moins relayer les requêtes DNS en les forçant à utiliser TCP, comme le fait tsocks :

tsocks will normally not be able to send DNS queries through a SOCKS server since SOCKS V4 works on TCP and DNS normally uses UDP. Version 1.5 and up do however provide a method to force DNS lookups to use TCP, which then makes them proxyable.

Mais finalement, SOCKS n’est plus une solution aussi attirante pour implémenter le reverse tethering.


Par conséquent, j’ai développé à la fois le client et le serveur relais manuellement.

Ce billet de blog et différents projets open source (SimpleRT, vpn-reverse-tether, LocalVPN et ToyVpn) m’ont beaucoup aidé à comprendre comment implémenter cette solution de reverse tethering.


Gnirehtet permet aux téléphones et tablettes Android d’utiliser facilement la connection internet d’un ordinateur par USB, sans accès root. C’est très utile quand vous ne pouvez pas accéder au réseau par un point d’accès WiFi.

J’espère qu’il pourra être utile à certains d’entre vous.

Discussions sur reddit et Hacker News.


2017-03-12T23:17:12+01:00 - (source)

L’objectif de ce billet est de parvenir à nous connecter à un serveur a priori inaccessible derrière un NAT.


De nos jours, TCP est toujours utilisé en mode client-serveur :

Une fois la connexion établie, cependant, le client et le serveur jouent exactement le même rôle au niveau de la communication. Par contre, très souvent, leur rôle applicatif dépend directement de celui qui a initié la connexion :


Ce fonctionnement paraît tellement naturel que “client” désigne bien souvent à la fois celui qui initie la connexion et celui qui effectue des requêtes (au serveur), alors que “serveur” désigne aussi bien la partie en écoute que celle qui répondra aux requêtes (des clients).

Puis vint le NAT…

Avec la pénurie d’adresses IPv4, le NAT s’est généralisé. Bien souvent, un accès internet ne fournit qu’une seule adresse IPv4. Les différents ordinateurs partageant la même connexion ne sont alors pas accessibles directement depuis l’extérieur (il est nécessaire d’ouvrir des ports).

Ainsi, derrière un NAT sans ports ouverts, un serveur ne sera pas accessible publiquement. Par contre, un client pourra continuer à se connecter à n’importe quel serveur public.


Inversion des rôles

Il existe des situations pour lesquelles nous souhaitons qu’un logiciel joue le rôle de serveur au niveau applicatif, afin de répondre aux requêtes des clients, mais client au niveau de la communication, afin de passer les NATs sans difficultés.

Par exemple, nous pouvons vouloir accéder, grâce à VNC ou SSH, à un ordinateur se trouvant derrière un NAT sur lequel, par hypothèse, nous n’avons pas la main. Dans ce cas, seul le serveur (au sens applicatif) aura la capacité d’ouvrir une connexion vers le client.

Logiciel dédié

Il est possible d’utiliser un logiciel spécialement conçu pour gérer cette inversion des rôles. C’est le cas par exemple de gitso, qui inverse le protocole VNC afin de simplifier l’aide de novices à distance.

Cette solution a cependant l’inconvénient d’être très spécifique, nécessitant un développement supplémentaire pour chaque protocole.

Redirection de port distant via SSH

SSH permet d’ouvrir un tunnel pour rediriger un port d’une machine distance vers une adresse quelconque.

Par exemple, après avoir démarré la redirection :

ssh un_serveur_public -NR2222:localhost:22

toutes les connexions arrivant sur un_serveur_public:2222 seront redirigées de manière transparente vers localhost:22 (sur la machine ayant initié le tunnel, donc).

(Cela nécessite d’activer GatewayPorts yes dans /etc/ssh/sshd_config sur un_serveur_public.)

De cette manière, un serveur SSH inaccessible derrière un NAT est rendu accessible à travers un tunnel en passant par une machine publique (un_serveur_public). Ainsi, il est possible de s’y connecter avec la commande :

ssh un_serveur_public -p2222


Cette stratégie fonctionne bien, mais elle nécessite que la machine qui souhaite exposer un serveur grâce à un tunnel possède un accès SSH sur un_serveur_public.

Si l’on souhaite aider quelqu’un grâce à la prise de contrôle de sa machine à distance, il y a toutes les chances que cette personne n’ait pas d’accès SSH vers une machine publiquement accessible. Il est alors possible de lui créer un compte restreint dédié sur un serveur que l’on contrôle, mais c’est très intrusif, et il faut s’assurer de ne pas réduire la sécurité.

Mais en fait, cette contrainte est superflue.

Redirections SOCAT

La redirection de port distant nécessite des permissions car, outre le fait qu’elle est implémentée sur SSH, il serait déraisonnable d’autoriser n’importe qui à ouvrir une socket en écoute sur un port arbitraire d’une machine distante.

Pour éviter ce problème, nous pouvons décomposer la redirection de port distant fourni par SSH en deux parties :

  1. l’ouverture de la connexion vers un_serveur_public, redirigée vers l’adresse localhost:22 dans l’exemple précédent ;
  2. l’ouverture d’une socket en écoute sur un port (2222) de la machine distante, redirigée vers la première connexion.

L’idée est de mettre en place le premier demi-tunnel sur la machine serveur, et le second demi-tunnel, nécessitant des permissions, sur la machine publique, contrôlée par le client.

Pour cela, nous allons utiliser l’outil socat, qui permet de relayer les données entre deux sockets, quelque soit le rôle qu’elles aient joué lors de l’initialisation.


Pour comprendre son utilisation, nous allons ouvrir grâce à netcat (nc) une socket TCP en écoute sur le port 5000 et nous y connecter :

# terminal 1
nc -l -p 5000
# terminal 2
nc localhost 5000

Toute entrée validée par un retour à la ligne dans le terminal 1 s’affichera dans le terminal 2 (et vice-versa).



Démarrons maintenant dans deux terminaux différents une socket en écoute sur les ports 1111 et 2222 :

# terminal 1
nc -l -p 1111
# terminal 2
nc -l -p 2222

Pour les mettre en communication avec socat, dans un 3e terminal :

socat tcp:localhost:1111 tcp:localhost:2222



Inversement, il est possible de mettre en communication deux sockets actives (sans compter sur leur synchronisation). Pour cela, commençons par ouvrir le serveur relai :

socat tcp-listen:1111 tcp-listen:2222

Puis connectons-y deux sockets :

# terminal 1
nc localhost 1111
# terminal 2
nc localhost 2222



Nous sommes maintenant prêts pour créer l’équivalent d’une redirection de port distant SSH grâce à deux socats, qui vont permettre d’inverser la connexion uniquement sur la portion qui permet de traverser le NAT :

# sur un_serveur_public
socat tcp-listen:1234 tcp-listen:5678
# sur le serveur derrière le NAT
socat tcp:un_serveur_public:1234 tcp:localhost:22
# sur le client
ssh un_serveur_public -p5678



2017-03-01T00:11:35+01:00 - (source)

Le 23 février, une équipe de chercheurs a annoncé avoir cassé SHA-1 en pratique, en générant une collision.

À partir de leur travail, il est possible de produire de nouvelles paires de fichiers PDF arbitrairement différents qui auront la même signature SHA-1. Par exemple :

shadow1-thumb shadow2-thumb

$ sha1sum shadow1.pdf shadow2.pdf
fffe36a1d6f0a76a585af4f3838a4a46b6714f0c  shadow1.pdf
fffe36a1d6f0a76a585af4f3838a4a46b6714f0c  shadow2.pdf
$ sha256sum shadow1.pdf shadow2.pdf
502ccf8ecee10176d891fa4aeab295edec22b95141c2ae16d85f13b39879e37e  shadow1.pdf
2546d272df653c5a99ef0914fa6ed43b336f309758ea873448154ebde90cdfe1  shadow2.pdf

J’explique dans ce billet le principe, et je fournis un outil qui produit, à partir de deux images JPEG, deux fichiers PDF différents de même SHA-1.


En fabriquant leur collision, les auteurs ont pris soin de la rendre réutilisable :

Furthermore, the prefix of the colliding messages was carefully chosen so that they allow an attacker to forge two PDF documents with the same SHA-1 hash yet that display arbitrarily-chosen distinct visual content.

Aujourd’hui, nous allons jouer aux attaquants.

La réutilisation de la collision repose sur le fait qu’avec SHA-1, ajouter un suffixe identique à une collision existante produit encore une collision :

SHA1(A) == SHA1(B) ==> SHA1(A|X) == SHA1(B|X)

(où X|Y est la concaténation de X et de Y)

Autrement dit, vous prenez les fichiers qui produisent une collision, vous ajoutez les mêmes octets aux deux, vous obtenez le même SHA-1 :

$ { cat shattered-1.pdf; echo bonjour; } | sha1sum
4bfd4b804da3aa207b29d6f1300dde507988dc4b  -
$ { cat shattered-2.pdf; echo bonjour; } | sha1sum
4bfd4b804da3aa207b29d6f1300dde507988dc4b  -

Il est donc trivial de créer de nouvelles collisions.

Mais pour qu’elles aient un intérêt, encore faut-il :

  1. que les fichiers produits soient valides ;
  2. qu’une différence entre les fichiers soit visible par l’utilisateur.


Les différences entre shattered-1.pdf et shattered-2.pdf se situent entre les adresses 0xc0 et 0x13f :

diff -U3 <(hd shattered-1.pdf) <(hd shattered-2.pdf)
--- /dev/fd/63  2017-02-28 21:11:11.530135134 +0100
+++ /dev/fd/62  2017-02-28 21:11:11.530135134 +0100
@@ -10,14 +10,14 @@
 00000090  72 65 61 6d 0a ff d8 ff  fe 00 24 53 48 41 2d 31  |ream......$SHA-1|
 000000a0  20 69 73 20 64 65 61 64  21 21 21 21 21 85 2f ec  | is dead!!!!!./.|
 000000b0  09 23 39 75 9c 39 b1 a1  c6 3c 4c 97 e1 ff fe 01  |.#9u.9...<L.....|
-000000c0  73 46 dc 91 66 b6 7e 11  8f 02 9a b6 21 b2 56 0f  |sF..f.~.....!.V.|
-000000d0  f9 ca 67 cc a8 c7 f8 5b  a8 4c 79 03 0c 2b 3d e2  |..g....[.Ly..+=.|
-000000e0  18 f8 6d b3 a9 09 01 d5  df 45 c1 4f 26 fe df b3  |..m......E.O&...|
-000000f0  dc 38 e9 6a c2 2f e7 bd  72 8f 0e 45 bc e0 46 d2  |.8.j./..r..E..F.|
-00000100  3c 57 0f eb 14 13 98 bb  55 2e f5 a0 a8 2b e3 31  |<W......U....+.1|
-00000110  fe a4 80 37 b8 b5 d7 1f  0e 33 2e df 93 ac 35 00  |...7.....3....5.|
-00000120  eb 4d dc 0d ec c1 a8 64  79 0c 78 2c 76 21 56 60  |.M.....dy.x,v!V`|
-00000130  dd 30 97 91 d0 6b d0 af  3f 98 cd a4 bc 46 29 b1  |.0...k..?....F).|
+000000c0  7f 46 dc 93 a6 b6 7e 01  3b 02 9a aa 1d b2 56 0b  |.F....~.;.....V.|
+000000d0  45 ca 67 d6 88 c7 f8 4b  8c 4c 79 1f e0 2b 3d f6  |E.g....K.Ly..+=.|
+000000e0  14 f8 6d b1 69 09 01 c5  6b 45 c1 53 0a fe df b7  |..m.i...kE.S....|
+000000f0  60 38 e9 72 72 2f e7 ad  72 8f 0e 49 04 e0 46 c2  |`8.rr/..r..I..F.|
+00000100  30 57 0f e9 d4 13 98 ab  e1 2e f5 bc 94 2b e3 35  |0W...........+.5|
+00000110  42 a4 80 2d 98 b5 d7 0f  2a 33 2e c3 7f ac 35 14  |B..-....*3....5.|
+00000120  e7 4d dc 0f 2c c1 a8 74  cd 0c 78 30 5a 21 56 64  |.M..,..t..x0Z!Vd|
+00000130  61 30 97 89 60 6b d0 bf  3f 98 cd a8 04 46 29 a1  |a0..`k..?....F).|
 00000140  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
 00000230  00 00 ff fe 00 fc 00 00  00 00 00 00 00 00 ff e0  |................|

Nous devrons donc, quoi qu’il arrive, conserver les 0x140 (320) premiers octets : il s’agira forcément d’un fichier PDF.

Pour analyser la structure sur un exemple minimal, je vous conseille l’exemple fourni à la dernière page du papier (good.pdf et bad.pdf) :

<< -- base64 -d | tar xj


Notre objectif est que les quelques octets différents entre les deux fichiers PDF déterminent l’image à afficher.

Il serait en théorie possible d’appliquer cet aiguillage au niveau de la structure du PDF, mais c’est en fait au niveau du JPEG qu’il sera implémenté :

PDFs with the same MD5 hash have previously been constructed by Gebhardt et al. [12] by exploiting so-called Indexed Color Tables and Color Transformation functions. However, this method is not effective for many common PDF viewers that lack support for these functionalities. Our PDFs rely on distinct parsings of JPEG images, similar to Gebhardt et al.’s TIFF technique [12] and Albertini et al.’s JPEG technique [1].

Le format JPEG

Voici le strict nécessaire à savoir sur le format JPEG pour notre besoin.

Une image est stockée entre les marqueurs 0xffd8 (Start Of Image) et 0xffd9 (End Of Image).

Il est possible d’insérer autant de commentaires que l’on veut, grâce au marqueur 0xfffe suivi de sa taille sur 2 octets (en big-endian). La taille compte le header de taille, mais pas le marqueur initial.

Par exemple, si je veux insérer le commentaire “Hello” au tout début, mon fichier JPEG ressemblera à ceci :

 ff d8 ff fe 00 07 48 65 6c 6c 6f  …  ff d9
 [SOI]                                [EOI]
      [[COM] [LEN]  H  e  l  l  o]

Et c’est à peu près tout ce qu’il y a à savoir.


Mettons en évidence la première différence entre les fichiers en collision.

Dans le fichier 1 :

000000b0  -- -- -- -- -- -- -- --  -- -- -- -- -- ff fe 01
000000c0  73 -- -- -- -- -- -- --  -- -- -- -- -- -- -- --

Dans le fichier 2 :

000000b0  -- -- -- -- -- -- -- --  -- -- -- -- -- ff fe 01
000000c0  7f -- -- -- -- -- -- --  -- -- -- -- -- -- -- --

Chacun définit un bloc de commentaires, mais pas de mêmes tailles. Dans le fichier 1, le début du prochain bloc sera à l’adresse 0x232 (0xbf + 0x173), alors que dans le fichier 2 il sera à l’adresse 0x23e (0xbf + 0x17f).

Nous avons donc trouvé notre aiguillage ; nous allons maintenant utiliser des commentaires JPEG pour cacher soit la première image, soit la seconde.

Pour l’exploiter jusqu’au bout, il suffit de disposer les commentaires astucieusement pour que les deux versions représentent des images parfaitement valides.

Nous allons donc commencer en 0x232 un bloc de commentaires, ayant une taille permettant de recouvrir l’intégralité de l’image que nous allons stocker en 0x23e. Et inversement, nous devons démarrer un commentaire à la fin de l’image stockée en 0x23e pour cacher la deuxième image.

Comparons sur le résultat ce qu’observe un parseur qui parcourt chacun des fichiers.

(GG et HH sont les deux images à stocker. J1 et J2 sont les longueurs des sauts pour enjamber chacune des images.)

Le fichier 1 est parsé ainsi :

00000090  -- -- -- -- -- ff d8 --  -- -- -- -- -- -- -- --
000000b0  -- -- -- -- -- -- -- --  -- -- -- -- -- ff fe 01
000000c0  73 -- -- -- -- -- -- --  -- -- -- -- -- -- -- --
00000230  -- -- ff fe J1 J1 -- --  -- -- -- -- -- -- GG GG
       i  GG GG GG GG GG GG ff fe  J2 J2 HH HH HH HH HH HH
       j  HH HH ff d9 -- -- -- --  -- -- -- -- -- -- -- --

Le fichier 2 est parsé différemment :

00000090  -- -- -- -- -- ff d8 --  -- -- -- -- -- -- -- --
000000b0  -- -- -- -- -- -- -- --  -- -- -- -- -- ff fe 01
000000c0  7f -- -- -- -- -- -- --  -- -- -- -- -- -- -- --
00000230  -- -- ff fe J1 J1 -- --  -- -- -- -- -- -- GG GG
       i  GG GG GG GG GG GG ff fe  J2 J2 HH HH HH HH HH HH
       j  HH HH ff d9 -- -- -- --  -- -- -- -- -- -- -- --

Les structures JPEG sont donc valides dans les deux fichiers. L’image affichée dépendra de l’octet stocké à l’adresse 0xc0, valant soit 0x73, soit 0x7f.

Maintenant, il nous reste à rendre notre PDF valide.


Le header participant à la collision SHA-1 (donc figé) définit des configurations dans des sections séparées (donc non figées) :

00000000  25 50 44 46 2d 31 2e 33  0a 25 e2 e3 cf d3 0a 0a  |%PDF-1.3.%......|
00000010  0a 31 20 30 20 6f 62 6a  0a 3c 3c 2f 57 69 64 74  |.1 0 obj.<</Widt|
00000020  68 20 32 20 30 20 52 2f  48 65 69 67 68 74 20 33  |h 2 0 R/Height 3|
00000030  20 30 20 52 2f 54 79 70  65 20 34 20 30 20 52 2f  | 0 R/Type 4 0 R/|
00000040  53 75 62 74 79 70 65 20  35 20 30 20 52 2f 46 69  |Subtype 5 0 R/Fi|
00000050  6c 74 65 72 20 36 20 30  20 52 2f 43 6f 6c 6f 72  |lter 6 0 R/Color|
00000060  53 70 61 63 65 20 37 20  30 20 52 2f 4c 65 6e 67  |Space 7 0 R/Leng|
00000070  74 68 20 38 20 30 20 52  2f 42 69 74 73 50 65 72  |th 8 0 R/BitsPer|
00000080  43 6f 6d 70 6f 6e 65 6e  74 20 38 3e 3e 0a 73 74  |Component 8>>.st|

Ainsi, la largeur (Width) est définie dans l’objet 2, la hauteur (Height) dans l’objet 3, etc.

Ces objets sont à définir à la suite des fichiers JPEG embarqués. Pour comprendre leur format, le plus simple est de lire le fichier good.pdf que je recommandais plus haut :

tail -c+$((0x746)) good.pdf

On y trouve la définition des objets (entre autres les dimensions de l’image) :

2 0 obj

3 0 obj

4 0 obj

5 0 obj

6 0 obj

7 0 obj

8 0 obj

9 0 obj
  /Type /Catalog
  /Pages 10 0 R

10 0 obj
  /Type /Pages
  /Count 1
  /Kids [11 0 R]

11 0 obj
  /Type /Page
  /Parent 10 0 R
  /MediaBox [0 0 8 8]
  /CropBox [0 0 8 8]
  /Contents 12 0 R
    /XObject <</Im0 1 0 R>>

12 0 obj
<</Length 30>>
  8 0 0 8 0 0 cm
  /Im0 Do

Ensuite vient la table de références croisées ; elle indique l’offset de chacun des objets définis, dans l’ordre :

0 13 
0000000000 65535 f 
0000000017 00000 n 
0000001861 00000 n 
0000001879 00000 n 
0000001897 00000 n 
0000001922 00000 n 
0000001945 00000 n 
0000001972 00000 n 
0000001999 00000 n 
0000002020 00000 n 
0000002076 00000 n 
0000002142 00000 n 
0000002309 00000 n 

À chaque ajout ou suppression de caractères dans la définition des objets, cette table doit être mise à jour.

Le fichier se termine par un trailer, contenant l’offset de la table de références :

trailer << /Root 9 0 R /Size 13>>


Ces offsets sont un peu fastidieux à modifier à la main, mais ça fonctionne.


J’ai donc écrit un petit outil qui applique toutes ces opérations automatiquement.

git clone

(ou sur github)

Il prend en entrée deux images JPEG (de moins de 64K, puisque la taille d’un commentaire est codé sur 2 octets), ainsi que leurs dimensions (afin d’éviter d’utiliser des dépendances pour les extraire) :

./ img1.jpg img2.jpg 200 200

Il génère deux fichiers dans le répertoire courant, shadow1.pdf et shadow2.pdf.

Il ne reste qu’à vérifier qu’ils ont bien le même SHA-1 :

sha1sum shadow1.pdf shadow2.pdf

C++ sans *pointeurs

2017-01-12T19:33:14+01:00 - (source)

Les pointeurs sont utilisés plus souvent que nécessaire en C++.

Je voudrais présenter ici comment caractériser les utilisations abusives et par quoi les remplacer.


La décision d’utiliser des pointeurs dépend en grande partie de l’API des objets utilisés.

API est à comprendre dans un sens très large : je considère que des classes utilisées dans une autre partie d’une même application exposent une API.

L’objectif est donc de concevoir des API de manière à ce que leur utilisation ne nécessite pas de manipuler de pointeurs, ni même si possible de smart pointers.

Cela peut paraître surprenant, mais c’est en fait ainsi que vous utilisez les classes de la STL ou de Qt : vos méthodes ne retournent jamais un raw pointer ni un smart pointer vers une string nouvellement créée.

De manière générale, vous n’écririez pas ceci :

// STL version
string *getName() {
    return new string("my name");

// Qt version
QString *getName() {
    return new QString("my name");

ni ceci :

// STL version
shared_ptr<string> getName() {
    return make_shared<string>("my name");

// Qt version
QSharedPointer<QString> getName() {
    return QSharedPointer<QString>::create("my name");

À la place, vous écririez sûrement :

// STL version
string getName() {
    return "my name";

// Qt version
QString getName() {
    return "my name";

Notre objectif est d’écrire des classes qui s’utiliseront de la même manière.


Il faut distinguer deux types de raw pointers :

  1. ceux qui détiennent l’objet pointé (owning), qui devront être libérés ;
  2. ceux qui ne le détiennent pas (non-owning).

Le plus simple est de les comparer sur un exemple.


Info *getInfo() {
    return new Info(/* … */);

void doSomething() {
    Info *info = getInfo();
    // info must be deleted

Ici, nous avons la responsabilité de supprimer info au bon moment.

C’est ce type de pointeurs dont nous voulons nous débarrasser.


void writeDataTo(QBuffer *buffer) {

void doSomething() {
    QBuffer buffer;

Ici, le pointeur permet juste de passer l’adresse de l’objet, mais la méthode writeDataTo(…) ne doit pas gérer sa durée de vie : elle ne le détient donc pas.

Cet usage est tout-à-fait légitime, nous souhaitons le conserver.

Pour savoir si un pointeur est owning ou non, il suffit de se poser la question suivante : est-ce que lui affecter nullptr provoquerait une fuite mémoire ?

Pourquoi ?

Voici quelques exemples illustrant pourquoi nous voulons éviter les owning raw pointers.

Fuite mémoire

Il est facile d’oublier de supprimer un pointeur dans des cas particuliers.

Par exemple :

bool parse() {
    Parser *parser = createParser();
    QFile file("file.txt");
    if (!
        return false;
    bool result = parser->parse(&file);
    delete parser;
    return result;
    // parser leaked if open failed

Ici, si l’ouverture du fichier a échoué, parser ne sera jamais libéré.

L’exemple suivant est encore plus significatif :

Result *execute() {
    // …
    return new Result(/* … */);

void doWork() {
    // result leaked

Appeler une méthode sans s’occuper du résultat peut provoquer des fuites mémoires.

Double suppression

Il est également possible, par inattention, de supprimer plusieurs fois le même pointeur (ce qui entraîne un undefined behavior).

Par exemple, si device fait partie de la liste devices, ce code le supprime deux fois :

delete device;
// device is deleted twice

Utilisation après suppression

L’utilisation d’un pointeur après sa suppression est également indéfinie.

Je vais prendre un exemple réel en Qt.

Supposons qu’une classe DeviceMonitor surveille le branchement de périphériques, et crée pour chacun un objet Device.

Lorsqu’un périphérique est débranché, un signal Qt provoque l’exécution du slot DeviceMonitor::onDeviceLeft(Device *). Nous voulons alors signaler au reste de l’application que le device est parti (signal DeviceMonitor::deviceLeft(Device *)), puis supprimer l’object device correspondant :

void DeviceMonitor::onDeviceLeft(Device *device) {
    emit deviceLeft(device);
    delete device;
    // slots may use the device after its deletion
    // device->deleteLater() not sufficient

Mais c’est loin d’être trivial.

Si nous le supprimons immédiatement comme ceci, et qu’un slot est branché à DeviceMonitor::deviceLeft(Device *) en Qt::QueuedConnection, alors il est possible que le pointeur soit déjà supprimé quand ce slot sera exécuté.

Un proverbe dit que quand ça crashe avec un delete, “il faut appeller deleteLater() pour corriger le problème” :


Mais malheureusement, ici, c’est faux : si le slot branché au signal DeviceMonitor::deviceLeft(Device *) est associé à un QObject vivant dans un autre thread, rien ne garantit que son exécution aura lieu avant la suppression du pointeur.

L’utilisation des owning raw pointers n’est donc pas seulement vulnérable aux erreurs d’inattention (comme dans les exemples précédents) : dans des cas plus complexes, il devient difficile de déterminer quand supprimer le pointeur.


De manière plus générale, lorsque nous avons un pointeur, nous ne savons pas forcément qui a la responsabilité de le supprimer, ni comment le supprimer :

Data *data = getSomeData();
delete data; // ?
free(data); // ?
custom_deleter(data); // ?

Qt fournit un mécanisme pour supprimer automatiquement les QObject * quand leur parent est détruit. Cependant, cette fonctionnalité ne s’applique qu’aux relations de composition.

Résumons les inconvénients des owning raw pointeurs :


Laissons de côté les pointeurs quelques instants pour observer ce qu’il se passe avec de simples valeurs (des objets plutôt que des pointeurs vers des objets) :

struct Vector {
    int x, y, z;

Vector transform(const Vector &v) {
    return { -v.x, v.z, v.y };

void compute() {
    Vector vector = transform({ 1, 2, 3 });
    emit finished(transform(vector));

C’est plus simple : la gestion mémoire est automatique, et le code est plus sûr. Par exemple, les fuites mémoire et les double suppressions sont impossibles.

Ce sont des avantages dont nous souhaiterions bénéficier également pour les pointeurs.

Privilégier les valeurs

Dans les cas où les pointeurs sont utilisés uniquement pour éviter de retourner des copies (et non pour partager des objets), il est préférable de retourner les objets par valeur à la place.

Par exemple, si vous avez une classe :

struct Result {
    QString message;
    int code;

Évitez :

Result *execute() {
    // …
    return new Result { message, code };

Préférez :

Result execute() {
    // …
    return { message, code };

Certes, dans certains cas, il est moins efficace de passer un objet par valeur qu’à travers un pointeur (car il faut le copier).

Mais cette inefficacité est à relativiser.

D’abord parce que dans certains cas (quand l’objet est copié à partir d’une rvalue reference), la copie sera remplacée par un move. Le move d’un vector par exemple n’entraîne aucune copie (ni move) de ses éléments.

Ensuite parce que les compilateurs optimisent le retour par valeur (RVO), ce qui fait qu’en réalité dans les exemples ci-dessus, aucun Result ni Vector n’est jamais copié ni mové : ils sont directement créés à l’endroit où ils sont affectés (sauf si vous compilez avec le paramètre -fno-elide-constructors).

Mais évidemment, il y a des cas où nous ne pouvons pas simplement remplacer un pointeur par une valeur, par exemple quand un même objet doit être partagé entre différentes parties d’un programme.

Nous voudrions les avantages des valeurs également pour ces cas-là. C’est l’objectif de la suite du billet.

Idiomes C++

Pour y parvenir, nous avons besoin de faire un détour par quelques idiomes couramment utilisés en C++.

Ils ont souvent un nom étrange. Par exemple :

Nous allons étudier les deux premiers.


Prenons un exemple simple :

bool submit() {
    if (!validate())
        return false;
    return something();

Nous souhaitons rendre cette méthode thread-safe grâce à un mutex (std::mutex en STL ou QMutex en Qt).

Supposons que validate() et something() puissent lever une exception.

Le mutex doit être déverrouillé à la fin de l’exécution de la méthode. Le problème, c’est que cela peut se produire à différents endroits, donc nous devons gérer tous les cas :

bool submit() {
    try {
        if (!validate()) {
            return false;
        bool result = something();
        return result;
    } catch (...) {

Le code est beaucoup plus complexe et propice aux erreurs.

Avec des classes utilisant RAII (std::lock_guard en STL ou QMutexLocker en Qt), c’est beaucoup plus simple :

bool submit() {
    QMutexLocker locker(&mutex);
    if (!validate())
        return false;
    return something();

En ajoutant une seule ligne, la méthode est devenue thread-safe.

Cette technique consiste à utiliser le cycle de vie d’un objet pour acquérir une ressource dans le constructeur (ici verrouiller le mutex) et la relâcher dans le destructeur (ici le déverrouiller).

Voici une implémentation simplifiée possible de QMutexLocker :

class QMutexLocker {
    QMutex *mutex;
    explicit QMutexLocker(QMutex *mutex) : mutex(mutex) {
    ~QMutexLocker() {

Comme l’objet est détruit lors de la sortie du scope de la méthode (que ce soit par un return ou par une exception survenue n’importe où), le mutex sera toujours déverrouillé.

Au passage, dans l’exemple ci-dessus, nous remarquons que la variable locker n’est jamais utilisée. RAII complexifie donc la détection des variables inutilisées, car le compilateur doit détecter les effets de bords. Mais il s’en sort bien : ici, il n’émet pas de warning.

Smart pointers

Les smart pointers utilisent RAII pour gérer automatiquement la durée de vie des pointeurs. Il en existe plusieurs.

Dans la STL :

Dans Qt :

Scoped pointers

Le smart pointer le plus simple est le scoped pointer. L’idée est vraiment la même que QMutexLocker, sauf qu’au lieu de vérouiller et déverrouiller un mutex, il stocke un raw pointer et le supprime.

En plus de cela, comme tous les smart pointers, il redéfinit certains opérateurs pour pouvoir être utilisé comme un raw pointer.

Par exemple, voici une implémentation simplifiée possible de QScopedPointer :

template <typename T>
class QScopedPointer {
    T *p;
    explicit QScopedPointer(T *p) : p(p) {}
    ~QScopedPointer() { delete p; }
    T *data() const { return p; }
    operator bool() const { return p; }
    T &operator*() const { return *p; }
    T *operator->() const { return p; }

Et un exemple d’utilisation :

// bad design (owning raw pointer)
DeviceInfo *Device::getDeviceInfo() {
    return new DeviceInfo(/* … */);

void Device::printInfo() {
    QScopedPointer<DeviceInfo> info(getDeviceInfo());
    // used like a raw pointer
    if (info) {
        qDebug() << info->getId();
        DeviceInfo copy = *info;
    // automatically deleted

Shared pointers

Les shared pointers permettent de partager l’ownership (la responsabilité de suppression) d’une ressource.

Ils contiennent un compteur de références, indiquant le nombre d’instances partageant le même pointeur. Lorsque ce compteur tombe à 0, le pointeur est supprimé (il faut donc éviter les cycles).

En pratique, voici ce à quoi ressemblerait une liste de Devices partagés par des QSharedPointers :

class DeviceList {
    QList<QSharedPointer<Device>> devices;
    QSharedPointer<Device> getDevice(int index) const;
    void add(const QSharedPointer<Device> &device);
    void remove(Device *device);
// devices are automatically deleted when necessary

Le partage d’un pointeur découle toujours de la copie d’un shared pointer. C’est la raison pour laquelle getDevice(…) et add(…) manipulent un QSharedPointer.

Le piège à éviter est de créér plusieurs smart pointers indépendants sur le même raw pointer. Dans ce cas, il y aurait deux refcounts à 1 plutôt qu’un refcount à 2, et le pointeur serait supprimé dès la destruction du premier shared pointer, laissant l’autre pendouillant.

Petite parenthèse : la signature des méthodes add et remove sont différentes car une suppression ne nécessite pas de manipuler la durée de vie du Device passé en paramètre.

Refcounted smart pointers are about managing te owned object’s lifetime.

Copy/assign one only when you intend to manipulate the owned object’s lifetime.

Au passage, si en Qt vous passez vos objets de la couche C++ à la couche QML, il faut aussi passer les shared pointers afin de ne pas casser le partage, ce qui implique d’enregistrer le type :

void registerQml() {

Listons donc les avantages des shared pointers :

Cependant, si la gestion mémoire est automatique, elle n’est pas transparente : elle nécessite de manipuler explicitement des QSharedPointer, ce qui est verbeux.

Il est certes possible d’utiliser un alias (typedef) pour atténuer la verbosité :

using DevicePtr = QSharedPointer<Device>;

class DeviceList {
    QList<DevicePtr> devices;
    DevicePtr getDevice(int index) const;
    void add(const DevicePtr &device);
    void remove(Device *device);

Mais quoi qu’il en soit, cela reste plus complexe que des valeurs.

Pour aller plus loin, nous allons devoir faire un détour inattendu, par un idiome qui n’a a priori rien à voir.


PImpl sert à réduire les dépendances de compilation.

Opaque pointers are a way to hide the implementation details of an interface from ordinary clients, so that the implementation may be changed without the need to recompile the modules using it.

Prenons la classe Person suivante (person.h) :

class Person {
    QString name;
    long birth;
    Person(const QString &name, long birth);
    QString getName() const;
    void setName(const QString &name);
    int getAge() const;
    static long countYears(long from, long to);

Elle contient juste un nom et un âge. Elle définit par ailleurs une méthode privée, countYears(…), qu’on imagine appelée dans getAge().

Chaque classe désirant utiliser la classe Person devra l’inclure :

#include "person.h"

Par conséquent, à chaque modification de ces parties privées (qui sont pourtant que des détails d’implémentation), toutes les classes incluant person.h devront être recompilées.

C’est ce que PImpl permet d’éviter, en séparant la classe en deux :

Concrètement, la classe Person précédente est la partie privée. Renommons-la :

class PersonPrivate {
    QString name;
    long birth;
    PersonPrivate(const QString &name, long birth);
    QString getName() const;
    void setName(const QString &name);
    int getAge() const;
    static long countYears(long from, long to);

Créons la partie publique, définissant l’interface souhaitée :

class PersonPrivate; // forward declaration
class Person {
    PersonPrivate *d;
    Person(const QString &name, long birth);
    Person(const Person &other);
    Person &operator=(const Person &other);
    QString getName() const;
    void setName(const QString &name);
    int getAge() const;

Elle contient un pointeur vers PersonPrivate, et lui délègue tous les appels.

Évidemment, Person ne doit pas inclure PersonPrivate, sinon nous aurions les mêmes dépendances de compilation, et nous n’aurions rien résolu. Il faut utiliser à la place une forward declaration.

Voici son implémentation :

Person::Person(const QString &name, long birth) :
        d(new PersonPrivate(name, birth)) {}

Person::Person(const Person &other) :
        d(new PersonPrivate(*other.d)) {}

Person::~Person() { delete d; }

Person &Person::operator=(const Person &other) {
    *d = *other.d;
    return *this;

QString Person::getName() const {
    return d->getName();

void Person::setName(const QString &name) {

int Person::getAge() const {
    return d->getAge();

Le pointeur vers la classe privée est souvent nommé d car il s’agit d’un d-pointer.

Donc comme prévu, tout cela n’a rien à voir avec notre objectif d’éviter d’utiliser des pointeurs.


Mais en fait, si. PImpl permet de séparer les classes manipulées explicitement de l’objet réellement modifié :


Il y a une relation 1-1 entre la classe publique et la classe privée correspondante. Mais nous pouvons imaginer d’autres cardinalités.

Par exemple, Qt partage implicitement les parties privées d’un grand nombre de classes. Il ne les copie que lors d’une écriture (CoW) :


Par exemple, lorsqu’une QString est copiée, la même zone mémoire sera utilisée pour les différentes instances, jusqu’à ce qu’une modification survienne.

Cependant, il ne s’agit que d’un détail d’implémentation utilisé pour améliorer les performances. Du point de vue utilisateur, tout se passe comme si les données étaient réellement copiées :

QString s1 = "ABC";
QString s2 = s1;
Q_ASSERT(s1 == "ABC");

En d’autres termes, les classes publiques ci-dessus ont une sémantique de valeur.

Resource handles

À la place, nous pouvons décider de partager inconditionnellement la partie privée, y compris après une écriture :


Dans ce cas, la classe publique a sémantique d’entité. Elle est qualifiée de resource handle.

C’est bien sûr le cas des smart pointers :

QSharedPointer<Person> p1(new Person("ABC", 42));
QSharedPointer<person> p2 = p1;
Q_ASSERT(p1.getName() == "DEF");
Q_ASSERT(p2.getName() == "DEF");

Mais aussi d’autres classes, comme l’API Dom de Qt :

void addItem(const QDomDocument &document, const QDomElement &element) {
    QDomElement root = document.documentElement();
    root.insertAfter(element, {});
    // the document is modified

PImpl avec des smart pointers

Tout-à-l’heure, j’ai présenté PImpl en utilisant un owning raw pointer :

class PersonPrivate; // forward declaration
class Person {
    // this is a raw pointer!
    PersonPrivate *d;
    // …

Mais en fait, à chaque type de relation correspond un type de smart pointer directement utilisable pour PImpl.

Pour une relation 1-1 classique :

Pour une relation 1-N à sémantique de valeur (CoW) :

Pour une relation 1-N à sémantique d’entité :

Par exemple, donnons à notre classe Person une sémantique d’entité :

class PersonPrivate; // forward declaration
class Person {
    QSharedPointer<PersonPrivate> d;
    Person() = default; // a "null" person
    Person(const QString &name, long birth);
    QString getName() const;
    // shared handles should expose const methods
    void setName(const QString &name) const;
    int getAge() const;
    operator bool() const { return d; }

Person se comporte maintenant comme un pointeur.

Person p1("ABC", 42);
Person p2 = p1;
Q_ASSERT(p1.getName() == "DEF");
Q_ASSERT(p2.getName() == "DEF");

p1 et p2 sont alors des resource handles vers PersonPrivate :


Évidemment, ce n’est pas approprié pour la classe Person, car le comportement est trop inattendu.

Mais je vais présenter un cas réel où ce design est approprié.

En pratique

Pour l’entreprise dans laquelle je suis salarié, j’ai implémenté une fonctionnalité permettant d’utiliser une souris USB branchée sur un PC pour contrôler un téléphone Android connecté en USB.

Concrètement, cela consiste à tranférer (grâce à libusb), à partir du PC, les événements HID reçus de la souris vers le téléphone Android.

J’ai donc (entre autres) créé des resources handles UsbDevice et UsbDeviceHandle qui wrappent les structures C libusb_device et libusb_device_handle, suivant les principes détaillés dans ce billet.

Leur utilisation illustre bien, d’après moi, les bénéfices d’une telle conception.

class UsbDeviceMonitor {
    QList<UsbDevice> devices;
    // …
    UsbDevice getAnyDroid() const;
    UsbDevice getAnyMouse() const;
    void deviceArrived(const UsbDevice &device);
    void deviceLeft(const UsbDevice &device);

UsbDevice peut être retourné par valeur, et passé en paramètre d’un signal par const reference (exactement comme nous le ferions avec un QString).

UsbDevice UsbDeviceMonitor::getAnyMouse() const {
    for (const UsbDevice &device : devices)
        if (device.isMouse())
            return device;
    return {};

Si une souris est trouvée dans la liste, on la retourne simplement ; sinon, on retourne un UsbDevicenull”.

void UsbDeviceMonitor::onHotplugDeviceArrived(const UsbDevice &device) {
    emit deviceArrived(device);

La gestion mémoire est totalement automatique et transparente. Les problèmes présentés sont résolus.

void registerQml() {
// QML
function startForwarding() {
    var mouse = usbDeviceMonitor.getAnyMouse()
    var droid = usbDeviceMonitor.getAnyDroid()
    worker = hid.forward(mouse, droid)

UsbDevice peut naviguer entre la couche C++ et QML.

bool HID::forward(const UsbDevice &mouse, const UsbDevice &droid) {
    UsbDeviceHandle droidHandle =;
    if (!droidHandle)
        return false;
    UsbDeviceHandle mouseHandle =;
    if (!mouseHandle)
        return false;
    // …

Grâce à RAII, les connexions (UsbDeviceHandle) sont fermées automatiquement.

En particulier, si la connexion à la souris échoue, la connexion au téléphone Android est automatiquement fermée.


Dans ces différents exemples, new et delete ne sont jamais utilisés, et par construction, la mémoire sera correctement gérée. Ou plus précisément, si un problème de gestion mémoire existe, il se situera dans l’implémentation de la classe elle-même, et non partout où elle est utilisée.

Ainsi, nous manipulons des handles se comportant comme des pointeurs, ayant les mêmes avantages que les valeurs :

Ils peuvent par contre présenter quelques limitations.

Par exemple, ils sont incompatibles avec QObject. En effet, techniquement, la classe d’un resource handle doit pouvoir être copiée (pour supporter le passage par valeur), alors qu’un QObject n’est pas copiable :

QObjects are identities, not values.

Très concrètement, cela implique que UsbDevice ne pourrait pas supporter de signaux (en tout cas, pas directement). C’est d’ailleurs le cas de beaucoup de classes de Qt : par exemple QString et QList n’héritent pas de QObject.


auto decide = [=] {
    if (semantics == VALUE) {
        if (!mustAvoidCopies)
            return "just use values";
        return "use PImpl + QSharedDataPointer";
    // semantics == ENTITY
    if (entitySemanticsIsObvious)
        return "use PImpl + QSharedPointer";
    return "use smart pointers explicitly";

C’est juste une heuristique…


En suivant ces principes, nous pouvons nous débarrasser des owning raw pointers et des new et delete “nus”. Cela contribue à rendre le code plus simple et plus robuste.

Ce sont d’ailleurs des objectifs qui guident les évolutions du langage C++ :

return 0;

Commentaires statiques avec Jekyll

2017-01-09T18:06:04+01:00 - (source)

Pour ce blog, j’ai abandonné Wordpress pour Jekyll, un moteur de blog statique.

Ainsi, j’écris mes articles en markdown dans mon éditeur favori, je les commite dans un dépôt git, et je génère le blog avec :

jekyll build

Le contenu hébergé étant statique, les pages ainsi générées à partir des sources sont renvoyées telles quelles.

Ce fonctionnement a beaucoup d’avantages :

Sans commentaires

L’inconvénient, c’est qu’un contenu statique est difficilement conciliable avec le support des commentaires (il faut bien d’une manière ou d’une autre exécuter du code lors de la réception d’un commentaire).

Il y a plusieurs manières de contourner le problème.

Il est par exemple possible d’en déporter la gestion (sur un service en ligne comme Disqus ou un équivalent libre – isso – à héberger soi-même). Ainsi, les commentaires peuvent être chargés séparément par le client en Javascript.

Au lieu de cela, j’ai choisi d’intégrer les commentaires aux sources du blog. Voici comment.

L’objectif est d’une part de pouvoir stocker et afficher les commentaires existants, et d’autre part de fournir aux lecteurs la possibilité d’en soumettre de nouveaux, qui me seront envoyés par e-mail.

Je me suis principalement inspiré du contenu de Jekyll::StaticComments, même si, comme nous allons le voir, je n’utilise pas le plug-in lui-même.


L’idée est de stocker les commentaires quelque part dans les sources du site au format YAML.

Le plugin Jekyll::StaticComments nécessite de stocker un fichier par commentaire dans un dossier spécial (_comments) parsé par un script à insérer dans le répertoire _plugins.

Personnellement, je préfère avoir tous les commentaires d’un même post regroupés au sein d’un même fichier. Et pour cela, pas besoin de plug-in : nous pouvons faire correspondre à chaque post dans _posts une liste de commentaires dans _data (un répertoire géré nativement par Jekyll).

Par exemple, ce billet est stocké dans :


Dans l’idéal, je voudrais que les commentaires associés soient stockés dans :


En pratique, pour des raisons techniques (Jekyll ne donne pas accès au nom du fichier), le nom du fichier ne contient pas le numéro du jour :


Il suffit alors de stocker dans ces fichiers les commentaires sous cette forme :

- id: 1
  author: this_is_me
  date: 2017-01-02 10:11:12+01:00
  contents: |

    Ceci est un commentaire écrit en _markdown_.
- id: 2
  author: dev42
  date: 2017-01-02 12:11:10+01:00
  contents: |
    > Ceci est un commentaire écrit en _markdown_.

    Et ça supporte aussi le [Liquid]( :

    {% highlight c %}
    int main() {
        return 0;
    {% endhighlight %}

Pour des exemples réels, voir les sources des commentaires de ce blog.


Maintenant que nous avons les données des commentaires, nous devons les afficher.

Il faut d’abord trouver la liste des commentaires associée à la page courante.

Comme nous ne pouvons pas récupérer directement le nom du fichier d’une page, nous devons reconstruire la chaîne à partir de la variable, qui ici vaut :


Cette ligne de Liquid :

comments{{ | replace: '/', '-' }}

donne la valeur :


Nous avons donc tout ce dont nous avons besoin pour créer le template de commentaires (à stocker dans _include/comments.html) :

{% capture commentid %}comments{{ | replace: '/', '-' }}{% endcapture %}
{% if[commentid] %}
<h2 id="comments">Commentaires</h2>
<div class="comments">
    {% for comment in[commentid] %}
        <div id="comment-{{ }}" class="comment" />
            <div class="comment-author">
                {% if ( %}
                    <a href="{{}}">
                {% endif %}
                {{ }}
                {% if ( %}
                {% endif %}
            <div class="comment-date">
                <a href="#comment-{{ }}">
                    {{ | date: "%-d %B %Y, %H:%M" }}
            <div class="comment-contents">
                {{ comment.contents | liquify | markdownify }}
    {% endfor %}

Il suffit alors d’inclure cette page à l’endroit où vous souhaitez insérer les commentaires (typiquement dans _layout/post.html) :

{% include comments.html %}


Pour proposer aux utilisateurs de poster de nouveaux commentaires, il nous faut un formulaire.

À titre d’exemple, voici le mien (intégré à _include/comments.html) :

<h3 class="comment-title">Poster un commentaire</h3>
<form method="POST" action="/comments/submit.php">
    <input type="hidden" name="post_id" value="{{ }}" />
    <input type="hidden" name="return_url" value="{{ page.url }}" />
    <table class="comment-table">
            <th>Nom :</th>
                <input type="text" size="25" name="name" />
            <th>E-mail :</th>
                <input type="text" size="25" name="email" />
                <em>(requis, non publié)</em>
            <th>Site web :</th>
                <input type="text" size="25" name="url" />
            <td colspan="2">
                <textarea name="comment" rows="10"></textarea>
            <td colspan="2">
                <input class="comment-submit" type="submit" value="Envoyer" />

Ce formulaire est affiché sous les commentaires existants.


L’action du formulaire précédent pointait sur comments/submit.php. Il nous reste donc à écrire dans ce fichier le code à exécuter lorsqu’un utilisateur envoie un commentaire au serveur.

Ce sera la seule partie “dynamique” du site.

Voici les parties importantes de comments/submit.php (basé sur la version de Jekyll::StaticComments) :

$DATE_FORMAT = "Y-m-d H:i:sP";
$EMAIL_ADDRESS = "your@email";
$SUBJECT = "Nouveau commentaire";
$COMMENT_SENT = "sent.html";

$msg = "post_id: " . $_POST["post_id"] . "\n";
$msg .= "email: " . $_POST["email"] . "\n";
$msg .= "---\n";
$msg .= "- id: ?\n";
$msg .= "  author: " . $_POST["name"] . "\n";
if ($_POST["url"] !== '')
    $msg .= "  author-url: " . $_POST["url"] . "\n";
$msg .= "  date: " . date($DATE_FORMAT) . "\n";
$msg .= "  contents: |\n" . $_POST["comment"];

$headers = "From: $EMAIL_ADDRESS\n";
$headers .= "Content-Type: text/plain; charset=utf-8";

if (mail($EMAIL_ADDRESS, $SUBJECT, $msg, $headers))
    include $COMMENT_SENT;
    echo "Le commentaire n'a pas pu être envoyé.";

Quand un commentaire est envoyé avec succès, la page comments/sent.html est affichée à l’utilisateur.

Ainsi, lorsqu’un commentaire est posté, je reçois un mail :

post_id: /2017/01/commentaires-statiques-avec-jekyll
email: my@email
- id: ?
  author: ®om
  date: 2017-01-09 19:27:10+01:00
  contents: |
Ceci est un test.

J’ai d’ailleurs ajouté une règle procmail pour que ces mails arrivent dans un dossier dédié.

Je peux alors copier le contenu dans le .yaml correspondant, formatter le commentaire (entre autres l’indenter de 4 espaces, ce qu’on pourrait automatiser), et le commiter.


Une fois mis en place, vous devriez donc avoir les fichiers suivants :


Je souhaitais depuis longtemps migrer vers un moteur de blog statique, qui correspond davantage à ma façon d’écrire des articles, et offre beaucoup d’avantages (légèreté, sécurité, maintenance…).

Je suis très content d’y être parvenu sans perdre les commentaires ni la possibilité d’en poster de nouveaux.

Certes, la validation est très manuelle, mais c’est le prix à payer pour avoir des commentaires statiques. Pour un blog avec une fréquence de commentaires assez faible, je pense que ce n’est pas très gênant.

Challenge reverse engineering

2015-07-21T16:31:04+02:00 - (source)

Un collègue m’a envoyé récemment le lien vers ce challenge, utilisé pour recruter chez NERD (que je ne connais pas).

Je vous le partage car je l’ai trouvé très intéressant. Le but est de trouver un input qui donne l’output attendu.

Pour info, entre le moment où j’ai eu connaissance du problème et le moment où je l’ai résolu, il s’est écoulé 6 jours. Je n’ai pas travaillé dessus à plein temps, mais ne comptez pas le résoudre en 20 minutes ;-)

Je ne publie ni ma réponse au résultat attendu, ni les explications (ce serait incorrect vis-à-vis des auteurs du challenge). Je fournis par contre à la fin l’input qui donne comme output mon e-mail, ce qui suffit à prouver que j’ai résolu le problème.

Amusez-vous bien !

#include <string.h>

typedef unsigned char u8;
typedef unsigned int u32;

u8 confusion[512]={

u32 diffusion[32]={

u8 input[32]={
//change only this :

void Forward(u8 c[32],u8 d[32],u8 s[512],u32 p[32])
    for(u32 i=0;i<256;i++)
        for(u8 j=0;j<32;j++)

        for(u8 j=0;j<32;j++)
            for(u8 k=0;k<32;k++)
    for(u8 i=0;i<16;i++)

int main(int argc, char* argv[])
    u8 target[]="Hire me!!!!!!!!";
    u8 output[32];


    return memcmp(output,target,16); // => contact jobs(at)

Ma preuve de solution :

diff --git a/HireMe.cpp b/HireMe.cpp
index ca94719..8374683 100644
--- a/HireMe.cpp
+++ b/HireMe.cpp
@@ -45,8 +45,8 @@ u32 diffusion[32]={
 u8 input[32]={
 //change only this :
@@ -70,7 +70,7 @@ void Forward(u8 c[32],u8 d[32],u8 s[512],u32 p[32])
 int main(int argc, char* argv[])
-   u8 target[]="Hire me!!!!!!!!";
+   u8 target[]="[]";
    u8 output[32];

Powered by VroumVroumBlog 0.1.32 - RSS Feed
Download config articles