00 Kernel sanitizer'lar nedir
Kernel sanitizer'lar, derleme zamanında kaynak koduna ek kontrol talimatları ekleyen araçlardır. Çalışma zamanında bellek erişimlerini, tip dönüşümlerini ve veri yarışlarını izleyerek hataları hemen bulurlar.
| Sanitizer | Tespit ettiği hatalar | Overhead | Kernel config |
|---|---|---|---|
| KASAN | Out-of-bounds, use-after-free, use-after-return | ~2x bellek, ~2x CPU | CONFIG_KASAN=y |
| UBSAN | Signed overflow, shift exponent, array index OOB, null deref | ~1.1x CPU | CONFIG_UBSAN=y |
| KMSAN | Başlatılmamış bellek kullanımı | ~3x bellek, ~3x CPU | CONFIG_KMSAN=y |
| KCSAN | Veri yarışı (data race), lock-free erişim hataları | ~1.5x CPU | CONFIG_KCSAN=y |
Ortak özellikler
Sanitizer'lar debug kernel'lar için tasarlanmıştır. Üretim kernel'ına eklenmez. Tipik akış: syzkaller fuzzing ortamı veya özel test platformu → sanitizer kernel → hata raporu → kaynak düzeltme → yeniden test. ARM MTE donanım desteğiyle KASAN üretim kullanımına yaklaşmaktadır.
01 KASAN — Kernel Address Sanitizer
KASAN, heap/stack/global bellek erişimlerini shadow memory ile izler. Her byte için 1-bit gölge bellek tutar ve her erişimde bu gölge bayta bakarak erişimin geçerli olup olmadığını kontrol eder.
Shadow memory modeli
Normal bellek alanı: 0x00000000 — 0x7fffffff
KASAN shadow alanı: 0xdffffc00 — 0xffffffff
(1/8 oranında, 8 byte → 1 shadow byte)
Shadow byte anlamı:
0x00 → 8 byte tamamen erişilebilir
0x01–0x07 → ilk N byte erişilebilir, geri kalanlar zehirli
0xFA → heap left redzone
0xFB → heap right redzone
0xFC → use-after-free (freed memory)
0xFD → stack left redzone
0xFE → global redzone
Kernel konfigürasyonu
CONFIG_KASAN=y
CONFIG_KASAN_GENERIC=y # generic mod (x86, ARM64 vb.)
CONFIG_KASAN_OUTLINE=y # inline yerine fonksiyon çağrısı (daha küçük ikili)
# veya
CONFIG_KASAN_INLINE=y # her erişim noktasına instrumentation (daha hızlı)
# Stack ve global değişkenleri de izle
CONFIG_KASAN_STACK=1
# Birden fazla hata bulmak için (ilkinde durmak yerine)
CONFIG_KASAN_MULTI_SHOT=y
Out-of-bounds örneği
/* Bu kod KASAN tarafından yakalanır */
char *buf = kmalloc(16, GFP_KERNEL);
buf[16] = 'X'; /* out-of-bounds: 16 byte tahsis, 17. byte'a yazma */
kfree(buf);
buf[0] = 'Y'; /* use-after-free: kfree sonrası erişim */
02 KASAN bug raporu okumak
KASAN bir hata tespit ettiğinde dmesg'e ayrıntılı bir rapor yazar. Bu raporu okumayı öğrenmek, hatayı hızlıca bulmak için kritiktir.
==================================================================
BUG: KASAN: slab-out-of-bounds in my_driver_write+0x4c/0xa0 [my_driver]
Write of size 1 at addr ffff888012345670 by task kworker/0:1/42
^ ^ ^ ^
erişim tipi boyut adres görev
CPU: 0 PID: 42 Comm: kworker/0:1 Not tainted 6.6.0-rc4 #1
Hardware name: QEMU Standard PC
Call Trace:
dump_stack_lvl+0x64/0x83
print_report+0x165/0x4b0
kasan_report+0xbb/0x1f0
kasan_check_range+0x100/0x1a0
my_driver_write+0x4c/0xa0 [my_driver] ← HATANIN OLDUĞU FONKSİYON
spi_sync+0x2a/0x60
spi_write+0x1e/0x30
...
Allocated by task 42:
__kmalloc+0x2a/0x60
my_driver_probe+0x88/0x120 [my_driver] ← TAHSIS NOKTASI
The buggy address belongs to the object at ffff888012345660
which belongs to the cache kmalloc-16 of size 16
The buggy address is located 16 bytes to the right of
16-byte region [ffff888012345660, ffff888012345670)
^
Tahsis edilen alanın tam sonu
Memory state around the buggy address:
ffff888012345640: fa fb fb fb fc fc fc fc
ffff888012345660: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ffff888012345670: fc fc fc fc fc fc fc fc ← fc = right redzone
^
Erişim bu adrese yapıldı
==================================================================
Rapor bileşenleri
slab-out-of-bounds: heap tampon taşması. use-after-free: serbest bırakılmış belleğe erişim. stack-out-of-bounds: stack buffer taşması.00=temiz, fa=left redzone, fc=right redzone/freed.03 KASAN modları
KASAN, üç farklı modda çalışabilir. Her birinin farklı overhead'i ve platform gereksinimleri vardır.
| Mod | Mekanizma | Platform | Overhead | Kullanım |
|---|---|---|---|---|
| Generic KASAN | Shadow memory + compiler instrumentation | x86, ARM64, MIPS, RISC-V | ~2x RAM | Debug kernel |
| SW Tag-based | ARM64 TBI (Top Byte Ignore) + 8-bit tag | ARM64 | ~1.5x RAM | Debug kernel |
| HW Tag-based (MTE) | ARM Memory Tagging Extension | ARM64 v8.5+ | ~0% CPU ek yük | Üretim adayı |
Hardware Tag-based KASAN (MTE)
CONFIG_KASAN=y
CONFIG_KASAN_HW_TAGS=y # ARM MTE gerektirir
# CONFIG_KASAN_GENERIC is not set
# u-boot veya kernel cmdline parametresi
# kasan.mode=async — asenkron mod, daha düşük overhead
# kasan.mode=sync — senkron mod, daha kesin konum bilgisi
# kasan.mode=asymm — karma mod
Cortex-A55 ve üzeri çekirdekler (Raspberry Pi 5'in Cortex-A76'sı dahil) MTE destekler. Gömülü sistemlerde üretim kalitesinde memory safety için MTE tabanlı KASAN gelecek vaat ediyor — yaklaşık sıfır runtime overhead ile.
04 UBSAN — Undefined Behavior Sanitizer
UBSAN, C standardının "undefined behavior" olarak tanımladığı durumları çalışma zamanında tespit eder. Derleyici varsayımlarına dayanan sessiz hatalar (taşan imzalı tamsayılar, geçersiz kaydırmalar vb.) burada yakalanır.
Konfigürasyon
CONFIG_UBSAN=y
CONFIG_UBSAN_TRAP=y # UB tespit edilince kernel trap atar (önerilen)
# veya
CONFIG_UBSAN_SANITIZE_ALL=y # tüm kaynak dosyalarını instrumentation'a dahil et
# Belirli UB türleri
CONFIG_UBSAN_BOUNDS=y # dizi sınırı aşımı
CONFIG_UBSAN_SHIFT=y # geçersiz bit kaydırma
CONFIG_UBSAN_INTEGER_WRAP=y # işaretli tamsayı taşması
UBSAN'ın yakaladığı hatalar
/* İşaretli tamsayı taşması (UB) */
int a = INT_MAX;
a = a + 1; /* UBSAN: signed integer overflow */
/* Geçersiz bit kaydırma */
uint32_t x = 1;
x = x << 32; /* UBSAN: shift exponent >= width */
x = x << -1; /* UBSAN: shift exponent is negative */
/* Dizi sınırı dışı indeks */
int arr[8];
arr[8] = 0; /* UBSAN: index 8 out of bounds for type 'int[8]' */
/* NULL pointer dereference */
int *p = NULL;
*p = 5; /* UBSAN: null pointer dereference */
UBSAN raporu
================================================================================
UBSAN: signed-integer-overflow in drivers/my_driver/my_driver.c:142:14
s32 overflowed: 2147483647 + 1 cannot be represented in type 's32 (int)'
CPU: 1 PID: 1234 Comm: my_app Not tainted 6.6.0
Call Trace:
dump_stack_lvl
ubsan_epilogue
handle_overflow
my_driver_calc+0x8e/0xc0 [my_driver] ← 142. satır
my_driver_ioctl+0x44/0x90 [my_driver]
================================================================================
05 KMSAN — Kernel Memory Sanitizer
KMSAN, başlatılmamış (uninitialized) bellek kullanımını tespit eder. Kernel stack veya heap belleği başlatmadan kullanan kod, güvenlik açıklarına ve gizli bilgi sızıntısına yol açabilir.
Konfigürasyon
CONFIG_KMSAN=y
# Not: KMSAN, KASAN ile aynı anda etkinleştirilemez
# Not: x86-64 ve ARM64 destekler (6.1+ kernel)
KMSAN'ın yakaladığı hatalar
/* Başlatılmamış stack değişkeni */
int my_func(void)
{
int result; /* başlatılmadı */
if (some_condition())
result = 42;
return result; /* KMSAN: result başlatılmamış olabilir */
}
/* Başlatılmamış yapı alanı */
struct my_msg {
int type;
int value;
char pad[4]; /* doldurma — başlatılmamış */
};
struct my_msg msg;
msg.type = 1;
msg.value = 2;
/* msg.pad başlatılmamış */
copy_to_user(ubuf, &msg, sizeof(msg)); /* KMSAN: pad başlatılmamış */
KMSAN raporu
=====================================================
BUG: KMSAN: uninit-value in copy_to_user+0x7d/0xc0
Uninit was created at:
stack_depot_save
kmsan_save_stack_with_flags
my_ioctl_handler+0x3c/0x80 [my_driver] ← başlatılmamış bölge
Uninit was used at:
copy_to_user+0x7d/0xc0
my_ioctl_handler+0x65/0x80 [my_driver] ← kullanım noktası
=====================================================
Başlatılmamış kernel belleğini userspace'e kopyalamak (copy_to_user), kernel stack veya heap içeriğini ifşa edebilir. Bu tür bilgi sızıntısı (information disclosure) KASLR gibi güvenlik mekanizmalarını atlamamıza olanak tanır. KMSAN bu hataları otomatik olarak bulur.
06 KCSAN — Kernel Concurrency Sanitizer
KCSAN, kilit olmadan yapılan bellek erişimlerini (data race) tespit eder. Iki thread'in aynı bellek konumuna eş zamanlı erişmesi ve en az birinin yazmak olması durumunda uyarı verir.
Konfigürasyon
CONFIG_KCSAN=y
CONFIG_KCSAN_REPORT_ONCE_IN_MS=3000 # en fazla 3 saniyede bir rapor
CONFIG_KCSAN_UDELAY_TASK=80 # task bağlamında rastgele gecikme (µs)
CONFIG_KCSAN_UDELAY_INTERRUPT=20 # interrupt bağlamında gecikme (µs)
KCSAN mekanizması
KCSAN, rastgele seçilen bellek erişimlerinde kısa süreli "watchpoint" yerleştirir. Başka bir CPU eş zamanlı olarak aynı adrese erişirse iki erişim arasında veri yarışı olduğu sonucuna varır.
/* Kilit olmadan iki thread aynı değişkene yazıyor */
int shared_counter = 0; /* global, kilitsiz */
/* Thread 1 */
void increment(void) { shared_counter++; } /* data race */
/* Thread 2 */
void decrement(void) { shared_counter--; } /* data race */
/* Doğru yaklaşım: atomik işlem veya kilit */
atomic_t shared_counter = ATOMIC_INIT(0);
void increment(void) { atomic_inc(&shared_counter); } /* güvenli */
KCSAN raporu
==================================================================
BUG: KCSAN: data-race in increment+0x14/0x20 / decrement+0x14/0x20
write to 0xffffffff82345678 of 4 bytes by task 123 on cpu 1:
increment+0x14/0x20
read to 0xffffffff82345678 of 4 bytes by task 456 on cpu 0:
decrement+0x14/0x20
==================================================================
Kasıtlı olarak kilitsiz erişim yapıyorsanız (lock-free programming) READ_ONCE() ve WRITE_ONCE() makrolarını kullanın. KCSAN bu makroları gördüğünde raporlamayı atlar — erişimin kasıtlı olduğu anlaşılır.
07 Sanitizer performans maliyeti
Sanitizer'ların performans etkisi, hangi sanitizer'ın kullanıldığına ve hedef platforma göre önemli ölçüde değişir. Bu bilgi, hangi sanitizer'ın hangi ortamda kullanılacağını belirlemek için kritiktir.
| Sanitizer | CPU overhead | Bellek overhead | Kod boyutu | Üretim? |
|---|---|---|---|---|
| KASAN Generic | ~1.5–2x | ~2x (shadow) | ~1.5x | Hayır |
| KASAN SW Tag | ~1.2–1.5x | ~1.5x | ~1.3x | Hayır |
| KASAN HW (MTE) | ~1.02x | minimal | ~1.1x | Aday |
| UBSAN | ~1.05–1.15x | minimal | ~1.2x | Hayır |
| KMSAN | ~2.5–3x | ~3x | ~2x | Hayır |
| KCSAN | ~1.3–1.5x | minimal | ~1.15x | Hayır |
Gömülü sistemlerde sanitizer kullanımı
KASAN Generic için shadow memory, kernel'ın kullandığı RAM'ın ~1/8'i kadar ek alan gerektirir. 128 MB RAM'li bir sistem için 16 MB shadow bellek demektir. Bellek kısıtlı gömülü sistemlerde bu kabul edilemez olabilir. KASAN HW (MTE) bu sorunu çözer ama ARM v8.5+ gerektirir.
Sanitizer kernel yapılandırma stratejisi
# Ayrı bir debug kernel yapılandırması oluştur
# (üretim .config dosyasından türetilmiş, sanitizer'lar ekli)
# Üretim .config al
cp /path/to/production.config .config
# Sanitizer'ları ekle
scripts/config --enable CONFIG_KASAN
scripts/config --enable CONFIG_KASAN_GENERIC
scripts/config --enable CONFIG_UBSAN
scripts/config --enable CONFIG_KASAN_MULTI_SHOT
# Debug build yap (üretim kernel'ı etkilemez)
make -j$(nproc) LOCALVERSION="-kasan-debug"
08 Pratik: KASAN ile driver bug bul
Gerçek dünya senaryosu: bir DMA buffer yönetimi sürücüsünde zaman zaman kernel oops'u oluşuyor ama tetikleme koşulları tutarsız. KASAN ile sistematik bulma.
Kasıtlı out-of-bounds bug içeren sürücü
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#define BUF_SIZE 64
static char *dma_buf;
static int __init buggy_init(void)
{
int i;
dma_buf = kmalloc(BUF_SIZE, GFP_KERNEL);
if (!dma_buf)
return -ENOMEM;
pr_info("DMA buffer tahsis edildi: %p, boyut=%d\n", dma_buf, BUF_SIZE);
/* BUG: döngü sınırı 1 fazla — 65. byte'a yazıyor */
for (i = 0; i <= BUF_SIZE; i++) /* <= yerine < olmalıydı */
dma_buf[i] = (char)(i & 0xFF);
pr_info("Buffer başlatıldı\n");
return 0;
}
static void __exit buggy_exit(void)
{
kfree(dma_buf);
pr_info("DMA buffer serbest bırakıldı\n");
}
module_init(buggy_init);
module_exit(buggy_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Kasıtlı OOB bug — KASAN demo");
KASAN raporunu tetikle ve oku
insmod buggy_dma_driver.ko
dmesg | grep -A 40 'BUG: KASAN'
==================================================================
BUG: KASAN: slab-out-of-bounds in buggy_init+0x68/0xa4 [buggy_dma_driver]
Write of size 1 at addr ffff000012ab8040 by task insmod/1234
CPU: 0 PID: 1234 Comm: insmod
Call Trace:
kasan_report
__asan_report_store1_noabort
buggy_init+0x68/0xa4 [buggy_dma_driver] ← 65. byte yazma
do_init_module
Allocated by task 1234:
kmalloc_trace
buggy_init+0x30/0xa4 [buggy_dma_driver] ← BUF_SIZE=64 tahsis
The buggy address is located 0 bytes to the right of
64-byte region [ffff000012ab8000, ffff000012ab8040)
^
ffff...8040 = tam sınır
Memory state:
ffff000012ab8000: 00 00 00 00 00 00 00 00 ← temiz (initialized)
ffff000012ab8020: 00 00 00 00 00 00 00 00
ffff000012ab8040: fc fc fc fc fc fc fc fc ← right redzone
^
KASAN'ın işaret ettiği adres
==================================================================
Hatayı düzelt ve doğrula
/* BUG: for (i = 0; i <= BUF_SIZE; i++) */
/* FIX: */
for (i = 0; i < BUF_SIZE; i++) /* < ile BUF_SIZE dahil değil */
dma_buf[i] = (char)(i & 0xFF);
Hatırlanacaklar
- KASAN Generic: shadow memory ile out-of-bounds ve use-after-free — debug kernel için standart
- KASAN raporu: BUG tipi → Call Trace → "Allocated by" → Memory state — bu sıraya göre oku
- KASAN HW (MTE): ARM v8.5+ için yaklaşık sıfır overhead — üretim adayı
- UBSAN: signed overflow, shift exponent, dizi indeksi — C UB'yi çalışma zamanında yakala
- KMSAN: başlatılmamış bellek → bilgi sızıntısı güvenlik açığı —
copy_to_useröncesi kontrol et - KCSAN: data race — kilitsiz paylaşılan erişimi
READ_ONCE/WRITE_ONCEveya atomik işlemlerle belgele - Sanitizer kernel = ayrı bir debug build — üretim kernel'ını etkilemez
Bir sonraki adım: kdump & crash rehberi — kernel crash sonrası analiz.