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 glibcpthread_mutex_unlock retourne EPERM silently
macOS❌ crash libsystem_pthread.dylib
Old Linux🤷 crash, freeze, or successfully returns
ValgrindMay 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


🧠 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.