← HUB ✦ Holberton School — C Security ✦

SECURE INPUT
& MEMORY LAB

patch the vuln. survive the audit.

⚠ COEFF 2 — ZERO DROIT À L'ERREUR
LVL 2
MISSION BRIEFING
🎯 CE QUE TU DOIS FAIRE

Tu hérites d'un code C volontairement vulnérable. Ta mission : détecter, analyser et patcher les failles mémoire sans changer le comportement externe du programme.

Task 0 — Familiarisation

Lire, compiler, exécuter. NE PAS modifier le code. Observer seulement.

Analyse dynamique — Valgrind

Lancer Valgrind pour détecter les erreurs mémoire à l'exécution.

Patch

Corriger les vulnérabilités sans casser le comportement externe.

Rapport technique

Expliquer chaque bug, l'outil utilisé, et la correction appliquée.

🕹️ ANALOGIE GAMING

Tu es un pentester dans Cyberpunk 2077. On te donne accès au code source d'un système compromis. Tu dois trouver les failles avant que les hackers adverses les exploitent. Chaque bug non patché = une porte d'entrée pour l'ennemi.

LES 3 BOSS — VULNÉRABILITÉS CLÉS
💣 BOSS 1 — BUFFER OVERFLOW
⭐ LVL 1 — CRITIQUE
Danger level
95%

Un buffer overflow se produit quand tu écris plus de données que la taille allouée d'un buffer. Tu écrases ce qui est en mémoire juste après.

/* ❌ DANGEREUX — gets() ne limite PAS la taille */
char buffer[64];
gets(buffer);          /* Si l'user tape 100 chars → OVERFLOW */

/* ❌ DANGEREUX — scanf sans limite */
char username[32];
scanf("%s", username);  /* lit jusqu'au whitespace, sans limite */

/* ✅ SÉCURISÉ — fgets avec taille explicite */
char buffer[64];
fgets(buffer, sizeof(buffer), stdin); /* Lit MAX 63 chars */

/* ✅ SÉCURISÉ — scanf avec limite */
char username[32];
scanf("%31s", username); /* 31 = taille - 1 pour le \0 */
🕹️ ANALOGIE GAMING

C'est un inventaire de 10 slots dans Minecraft. Si tu forces 20 items dedans, les 10 en trop écrasent l'inventaire du joueur à côté. En mémoire ce joueur c'est peut-être l'adresse de retour de ta fonction — et là c'est le game over.

👻 BOSS 2 — DANGLING POINTER / USE-AFTER-FREE
⭐⭐ LVL 2 — ÉLEVÉ
Danger level
75%

Un dangling pointer pointe vers de la mémoire déjà libérée avec free(). Y accéder = comportement indéfini.

/* ❌ USE-AFTER-FREE */
char *ptr = malloc(64);
free(ptr);
ptr[0] = 'A';  /* DANGER : mémoire déjà libérée */

/* ❌ DOUBLE FREE */
free(ptr);
free(ptr);    /* DANGER : corruption du heap */

/* ✅ BONNE PRATIQUE */
free(ptr);
ptr = NULL;  /* Ne pointe plus vers rien de dangereux */
if (ptr != NULL)
    ptr[0] = 'A'; /* Check avant d'accéder */
🕹️ ANALOGIE GAMING

Tu as une référence vers un ennemi dans un jeu. L'ennemi est tué (free). Si tu continues d'utiliser ta référence pour lui infliger des dégâts, tu touches la zone mémoire d'un autre objet qui a pris sa place.

🕳️ BOSS 3 — MEMORY LEAK
⭐⭐⭐ LVL 3 — HOLBERTON STYLE
Danger level
50%

Un memory leak se produit quand tu alloues de la mémoire avec malloc mais que tu oublies de la libérer avec free.

/* ❌ MEMORY LEAK — malloc sans free */
void create_user(void)
{
    char *name = malloc(64);
    strcpy(name, "Player1");
    /* name n'est jamais free() → LEAK */
}

/* ✅ CORRIGÉ */
void create_user(void)
{
    char *name = malloc(64);
    if (name == NULL)    /* TOUJOURS checker malloc ! */
        return;
    snprintf(name, 64, "Player1");
    /* ... utilisation ... */
    free(name);
    name = NULL;
}
INPUT HANDLING — LE TABLEAU DE LA MORT
📥 FONCTIONS SÉCURISÉES VS DANGEREUSES
FONCTIONLIMITE TAILLE ?STATUTALTERNATIVE
gets() ❌ NON💀 BANNIE C11fgets()
scanf("%s") ❌ NON⚠️ DANGEREUXscanf("%31s")
strcpy() ❌ NON⚠️ RISQUÉ strncpy()
strcat() ❌ NON⚠️ RISQUÉ strncat()
sprintf() ❌ NON⚠️ RISQUÉ snprintf()
fgets() ✅ OUI ✅ SÛR —
strncpy() ✅ OUI ⚠️ Forcer \0Ajouter buf[n-1] = '\0'
snprintf() ✅ OUI ✅ SÛR —
gets() est tellement dangereuse qu'elle a été retirée du standard C11. Si tu la vois dans un code, c'est un red flag immédiat.
VALGRIND — TON SCANNER DE FAILLES
🔍 C'EST QUOI VALGRIND ?

Valgrind est un outil d'analyse dynamique qui exécute ton programme et surveille chaque accès mémoire en temps réel. Il détecte les erreurs que le compilateur ne voit pas.

🕹️ ANALOGIE GAMING

C'est le mode replay avec hitbox visibles dans un jeu de combat. Ton programme tourne normalement, mais Valgrind enregistre chaque coup qui touche une zone interdite. Invisible à l'œil nu, flagrant avec l'outil.

💻 COMMANDES VALGRIND — À MAÎTRISER
$ gcc -g -Wall -Wextra -Werror -pedantic -std=gnu89 main.c -o prog
# Le -g est CRUCIAL : ajoute les numéros de lignes pour Valgrind

$ valgrind ./prog
# Analyse basique

$ valgrind --leak-check=full ./prog
# Détecte toutes les fuites mémoire ← LA COMMANDE PRINCIPALE

$ valgrind --leak-check=full --show-leak-kinds=all ./prog
# Affiche TOUS les types de leaks (definitely/possibly lost...)

$ valgrind --track-origins=yes ./prog
# Trace l'ORIGINE de la mémoire non initialisée

$ gcc -fsanitize=address -g main.c -o prog && ./prog
# Alternative : AddressSanitizer (plus rapide)
Ne jamais utiliser -fsanitize=address ET Valgrind en même temps — ils sont incompatibles.
📊 LIRE LES SORTIES VALGRIND
==1234== Invalid read of size 1
# Lecture hors limites → buffer overflow en lecture

==1234== Invalid write of size 4
# Écriture hors limites → buffer overflow en écriture

==1234== Use of uninitialised value
# Variable utilisée avant d'être initialisée

==1234== Invalid read of size 8 (freed memory)
# Use-after-free → tu lis après free()

==1234== definitely lost: 64 bytes in 1 blocks
# Memory leak confirmé → 64 bytes jamais free()

==1234== All heap blocks were freed -- no leaks are possible
# ✅ C'est ce qu'on veut voir !
Le numéro ==1234== est le PID du process. Il change à chaque exécution, c'est normal.
MALLOC / FREE — LES 4 RÈGLES D'OR
⚙️ LE CONTRAT MALLOC/FREE
#include <stdlib.h>

void *malloc(size_t size);          /* alloue N octets sur le heap */
void  free(void *ptr);              /* libère la mémoire allouée */
void *calloc(size_t n, size_t s);  /* alloue ET initialise à 0 */
void *realloc(void *ptr, size_t s); /* redimensionne une alloc */
1

Toujours vérifier le retour de malloc

Si malloc échoue il retourne NULL. Déréférencer NULL = crash immédiat.

2

1 malloc = 1 free, ni plus ni moins

Oublier = leak. Doubler = corruption du heap (double free).

3

Mettre le pointeur à NULL après free

free(ptr); ptr = NULL; — empêche le use-after-free accidentel.

4

Ne jamais free une adresse non-malloc

Variables locales, milieu de tableau, chaînes littérales → jamais de free.

OWNERSHIP — QUI POSSÈDE QUOI ?
🏠 LE CONCEPT D'OWNERSHIP

L'ownership = savoir qui est responsable de free() un pointeur. C'est la source principale de bugs quand ce n'est pas clair.

/* La fonction alloue ET retourne → l'appelant devient owner */
char *build_message(const char *name)
{
    char *result = malloc(128);
    if (result == NULL)
        return (NULL);
    snprintf(result, 128, "Welcome, %s!", name);
    return (result); /* L'APPELANT devient responsable du free */
}

int main(void)
{
    char *msg = build_message("Link");
    if (msg == NULL)
        return (1);
    printf("%s\n", msg);
    free(msg);    /* main() est l'owner → il free */
    msg = NULL;
    return (0);
}
🕹️ ANALOGIE GAMING

Dans un MMO, si tu craftes une épée et tu la donnes à un autre joueur, c'est lui qui doit la gérer. La fonction qui retourne un malloc transfère la responsabilité du free à l'appelant. Si personne ne s'en souvient → memory leak.

CERT C — LES RÈGLES DU PROJET
📋 EXP33-C

Ne pas lire de mémoire non initialisée

Toute variable doit être initialisée avant lecture.

int x;
printf("%d", x); /* ❌ */
int x = 0;
printf("%d", x); /* ✅ */
📋 EXP34-C

Ne pas déréférencer un pointeur NULL

Toujours vérifier après malloc.

char *p = malloc(64);
p[0] = 'A';     /* ❌ */
if (p != NULL)
    p[0] = 'A';  /* ✅ */
📋 ENV30-C

Pas de comportement non spécifié

Le UB peut "fonctionner" chez toi et crasher sur le serveur Holberton. Ne jamais en dépendre.

📋 MEM30-C

Pas d'accès post-free

Après free(ptr), le pointeur est invalide. Toujours ptr = NULL derrière.

COMPILATION — LES BONS FLAGS
⚙️ FLAGS REQUIS PAR LE PROJET
$ gcc -std=gnu89 -Wall -Wextra -Werror -pedantic -g main.c -o prog

-std=gnu89 → Standard C89 + extensions GNU (Holberton)
-Wall → Active tous les warnings courants
-Wextra → Active des warnings supplémentaires
-Werror → Transforme les warnings en ERREURS (strict)
-pedantic → Respect strict du standard
-g → Infos de debug — INDISPENSABLE pour Valgrind !
LE RAPPORT TECHNIQUE — STRUCTURE GAGNANTE
📝 LES 4 QUESTIONS PAR BUG
1

Quoi — Le bug

Nom de la vulnérabilité + fichier + numéro de ligne exacte

2

Pourquoi — La cause

Quelle hypothèse était violée ? Quelle fonction était dangereuse ?

3

Comment détecté — L'outil

Inspection manuelle ? Valgrind ? Copie du message d'erreur exact.

4

Fix — La correction

Code avant / après. Quelle règle CERT ? Comportement externe inchangé ?

L'énoncé dit : "AI tools may be used only to help explain findings". Utilise Claude pour rédiger le rapport, pas pour trouver les bugs à ta place.
RÉCAP — TON CHEAT CODE FINAL
☠️ LES MOTS QUI DOIVENT TE FAIRE PEUR
gets() scanf("%s") strcpy() sans vérif free() sans NULL après malloc sans NULL check double free() accès après free() variable non initialisée strcat() sans limite sprintf() sans n
✅ LES REMPLACEMENTS SÛRS
gets()→fgets(b, n, stdin)
scanf("%s")→scanf("%31s")
strcpy()→strncpy(d, s, n-1)
strcat()→strncat(d, s, n)
sprintf()→snprintf(b, n, ...)
🔧 VALGRIND EN 3 COMMANDES
$ gcc -g ... -o prog
# toujours avec -g !

$ valgrind --leak-check=full ./prog
# commande principale

$ valgrind --track-origins=yes ./prog
# si mémoire non init