Absolutely. Let’s dive deep into one of the most underrated but deadly traps in POSIX threading — something that doesn’t crash loudly, but will haunt you in unpredictable ways:
🔓 14_mutex_unlock_without_lock
💥 “Unlock Without Lock — Undefined Behavior, Undefined Fate”
title: Info
Ce document explique, ligne par ligne, le **scénario asymétrique classique** où un `pthread_mutex_unlock()` est appelé sans qu’un `pthread_mutex_lock()` ait été préalablement acquis par le thread. Résultat ? **Comportement indéfini**, erreurs de runtime *aléatoires*, ou tout simplement... **rien du tout**.
🎯 Objectif Global
Montrer que libérer un mutex sans l’avoir verrouillé est non seulement une erreur logique, mais une faille silencieuse.
Il n’y aura souvent :
-
❌ Aucune erreur de compilation
-
❌ Aucun message d’erreur à l’exécution
-
❌ Aucun crash immédiat
Mais il y aura :
-
🧨 Corruption mémoire
-
🧊 Deadlocks impossibles à traquer
-
☠️ Violations de logique de synchronisation
📂 Code: 14_mutex_unlock_without_lock.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void *rogue_thread(void *arg)
{
(void)arg;
printf("🔓 rogue_thread trying to unlock without locking...\n");
pthread_mutex_unlock(&lock);
printf("🧠 did it crash? did it silently fail?\n");
return (NULL);
}
int main(void)
{
pthread_t rogue;
printf("🚀 Launching rogue thread...\n");
pthread_create(&rogue, NULL, rogue_thread, NULL);
pthread_join(rogue, NULL);
printf("🏁 main() finished\n");
return (0);
}
🔍 Ce que fait ce code
-
Initialise un mutex (non verrouillé)
-
Lance un thread qui appelle
pthread_mutex_unlock()
-
Ne fait aucun lock préalable
-
Aucune garantie de ce qu’il va se passer
💣 Ce que vous pensez que ça va faire
“Bon ça va probablement imprimer un message d’erreur, non ? Ou au pire retourner une erreur ?”
💥 Ce que ça fait vraiment
🧠 Plateforme | 💣 Résultat |
---|---|
Linux glibc | pthread_mutex_unlock retourne EPERM silently |
macOS | ❌ crash libsystem_pthread.dylib |
Old Linux | 🤷 crash, freeze, or successfully returns |
Valgrind | May or may not detect misuse |
Threads waiting | 💀 deadlocks / starvation if logic depends on it |
🧠 Spécification POSIX
“If the current thread does not hold the mutex lock, the behavior is undefined.”
→ POSIX: undefined behavior
→ That means: runtime may do anything, including nothing, including crashing later
🔎 Diagnostic
✅ On glibc, you can do:
int r = pthread_mutex_unlock(&lock);
if (r != 0)
printf("❗ unlock failed: %s\n", strerror(r));
➡ Cela te donne : unlock failed: Operation not permitted
But if you don’t check the return value — you never know.
🔬 Pourquoi c’est grave
Imagine ceci :
pthread_mutex_lock(&lock);
// ... critical section ...
pthread_mutex_unlock(&lock); // Someone ELSE already unlocked it
➡ Vous pensez être synchronisé…
➡ Mais en réalité vous êtes dans une section critique désynchronisée, sans aucune garantie d’exclusivité.
📉 Simulation de Scénario Catastrophe
Thread 1: locks -> critical section -> unlock ✅
Thread 2: accidentally unlocks again ❌
Thread 3: assumes it’s locked, proceeds...
➡ Résultat :
-
🧨 Deux threads dans la section critique en même temps
-
💾 Mémoire partagée corrompue
-
🔥 Race condition en production
✅ Correctif
Toujours :
int r = pthread_mutex_lock(&lock);
// check error
// ...
r = pthread_mutex_unlock(&lock);
if (r != 0)
{
fprintf(stderr, "❌ unlock failed: %s\n", strerror(r));
exit(EXIT_FAILURE);
}
Et surtout : ne jamais unlocker un mutex que votre thread n’a pas locké.
🧠 FAANG Insight
En entreprise, ce genre de bug :
-
❌ Ne sera pas détecté par les tests unitaires
-
❌ Ne provoque pas forcément de segfault
-
✅ Provoque des incidents clients aléatoires
-
⚰️ Tue votre capacité de debug
✅ Checkpoint
Item | ✅ |
---|---|
Mutex initialisé | ✅ |
Thread tente unlock sans lock | ✅ |
Résultat = undefined | ✅ |
Peut crasher ou pas | ✅ |
Pas de protection par défaut | ✅ |
Ne protège rien | ✅ |
🔗 Related Vault Entries
🧠 TL;DR
UNLOCK sans LOCK = Undefined Behavior
Et dans les threads, “undefined” signifie :
-
Peut marcher 999 fois
-
Explose silencieusement la 1000e
-
Et personne ne saura pourquoi
🔥 Want to Go Deeper?
-
❓ Simuler
pthread_mutex_unlock()
sur un mutex verrouillé par un autre thread ? -
❓ Détecter les erreurs avec
valgrind --tool=helgrind
? -
❓ Implémenter une
safe_mutex_unlock()
qui vérifie le propriétaire ?
Say “mutex chaos next”, and I’ll deliver the next asymmetric teardown.