Müşterilerimiz İletişim EN

Linux Kernel Modülü Oluşturma


linux kernel

Dün gece bir arkadaşım işletim sistemleriyle ilgili ödevi için benden yardım istedi. Ödev, basit bir Linux kernel (çekirdek) modülü oluşturmak ve linked list (bağlı liste) işlemleri yapmakla ilgiliydi. Daha önce bir çekirdek modülü üzerinde çalışmamıştım, ama bir şekilde hızlıca basit bir modül oluşturabileceğimi biliyordum.


Temel Bilgiler


Çekirdek modüllerinin iki giriş noktası var: insmod ile çağırılan init ve rmmod ile çağırılan exit.


#include  
#include  
#include   
MODULE_LICENSE("GPL"); 
MODULE_AUTHOR("egegunes"); 
MODULE_DESCRIPTION("Hello");  
static int __init hello_init(void) { 
    printk(KERN_INFO "Hello World");  
    // If your init function returns a non-zero value, 
    // kernel won't load your module. 
    return 0; 
}  
static void __exit hello_exit(void) { 
    printk(KERN_INFO "Goodbye, cruel world, I'm leaving you today");
}  module_init(hello_init); 
   module_exit(hello_exit);

Bir çekirdek modülü oluşturmak için ihtiyacımız olan tüm kod aslında bu. Modülü derlemek için de bir Makefile’a ihtiyacımız var:


obj-m := hello.o 
KDIR := /lib/modules/$(shell uname -r)/build  
build:
     $(MAKE) -C $(KDIR) M=$(PWD) 
clean:
     $(MAKE) -C $(KDIR) M=$(PWD) clean

Bu Makefile, Linux çekirdeğinin kullandığı kbuild derleme sistemini kullanmak için standart bir içeriğe sahip. Daha fazla bilgi için kernel dokümanlarına bakabilirsiniz.


Modülü herhangi bir ek paket yüklemeden fedora/31-cloud-base v31.20191023.0 Vagrant Box’da oluşturup yükledim.


$ make 
make -C /lib/modules/5.3.7-301.fc31.x86_64/build M=/home/vagrant/hello 
make[1]: Entering directory '/usr/src/kernels/5.3.7-301.fc31.x86_64'
   Building modules, stage 2.
   MODPOST 1 modules 
make[1]: Leaving directory '/usr/src/kernels/5.3.7-301.fc31.x86_64' $ sudo insmod ./hello.ko 
$ sudo dmesg 
... 
[  519.920518] Hello World 
$ sudo rmmod ./hello.ko 
... 
[  519.920518] Hello World 
[  523.976396] Goodbye, cruel world, I'm leaving you today

Oops


Tabii, üzerinde çalıştığım gerçek modül bu kadar basit değildi. init ve exit fonksiyonlarında bir bağlı listenin öğelerini işliyordu. Modülü kaldırmaya çalıştığımda, çekirdek soğuk bir mesajla yanıt verdi:


$ sudo rmmod module 
Killed

Killed mesajını görmek bende otomatik olarak “Bellek mi yetersiz?” sorusunu tetikledi. Ama hayır, bu farklı bir durumdu. Çekirdek log’larına baktım ve doğrudan kendi yarattığım ilk kernel panic ile karşılaştım:


$ sudo dmesg 
[  100.472826] BUG: kernel NULL pointer dereference, address: 0000000000000000 
[  100.472992] #PF: supervisor write access in kernel mode 
[  100.473118] #PF: error_code(0x0002) - not-present page [  100.473241] PGD 0 P4D 0 
[  100.473306] Oops: 0002 [#1] SMP NOPTI 
[  100.473397] CPU: 0 PID: 2394 Comm: rmmod Tainted: G           OE     5.3.7-301.fc31.x86_64 #1 
[  100.473623] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 
[  100.473818] RIP: 0010:hello_exit+0xc/0x1000 [hello] 
[  100.473938] Code: Bad RIP value. 
[  100.474018] RSP: 0018:ffffbd14406cbed8 EFLAGS: 00010246 
[  100.474143] RAX: 000000000000002b RBX: 00000000000000b0 RCX: 0000000000000000 
[  100.474312] RDX: 0000000000000000 RSI: ffff94b8bda17908 RDI: ffff94b8bda17908 
[  100.474496] RBP: ffffffffc0345000 R08: ffff94b8bda17908 R09: 00000000000001b9 
[  100.474664] R10: 0000000000000001 R11: ffffffff92ee37c0 R12: 0000000000000000 
[  100.474833] R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 
[  100.475002] FS:  00007f37d424a740(0000) GS:ffff94b8bda00000(0000) knlGS:0000000000000000 
[  100.475192] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [  100.475329] CR2: ffffffffc0342fe2 CR3: 000000007b780000 CR4: 00000000000406f0 
[  100.475503] Call Trace: 
[  100.475575]  __x64_sys_delete_module+0x13d/0x280 
[  100.475698]  ? exit_to_usermode_loop+0xa7/0x100 
[  100.475811]  do_syscall_64+0x5f/0x1a0 
[  100.475912]  entry_SYSCALL_64_after_hwframe+0x44/0xa9 
[  100.476037] RIP: 0033:0x7f37d4379fab 
[  100.476127] Code: 73 01 c3 48 8b 0d dd fe 0b 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa b8 b0 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d ad fe 0b 00 f7 d8 64 89 01 48 
[  100.476569] RSP: 002b:00007fff8489a3f8 EFLAGS: 00000206 ORIG_RAX: 00000000000000b0 
[  100.476748] RAX: ffffffffffffffda RBX: 000055e696d84780 RCX: 00007f37d4379fab 
[  100.477316] RDX: 000000000000000a RSI: 0000000000000800 RDI: 000055e696d847e8 
[  100.477860] RBP: 00007fff8489a458 R08: 0000000000000000 R09: 0000000000000000 
[  100.478397] R10: 00007f37d43edac0 R11: 0000000000000206 R12: 00007fff8489a620 
[  100.478948] R13: 00007fff8489c7a8 R14: 000055e696d842a0 R15: 000055e696d84780 
[  100.479486] Modules linked in: hello(OE-) e1000 joydev i2c_piix4 video ip_tables vboxvideo(OE) drm_kms_helper ttm drm crct10dif_pclmul crc32_pclmul crc32c_intel ghash_clmulni_intel serio_raw vboxguest(OE) ata_generic pata_acpi 
[  100.481065] CR2: 0000000000000000 
[  100.481567] ---[ end trace ad23453eb9e8484f ]--- 
[  100.482050] RIP: 0010:hello_exit+0xc/0x1000 [hello] 
[  100.482562] Code: Bad RIP value. 
[  100.483004] RSP: 0018:ffffbd14406cbed8 EFLAGS: 00010246 
[  100.483509] RAX: 000000000000002b RBX: 00000000000000b0 RCX: 0000000000000000 
[  100.484062] RDX: 0000000000000000 RSI: ffff94b8bda17908 RDI: ffff94b8bda17908 
[  100.484602] RBP: ffffffffc0345000 R08: ffff94b8bda17908 R09: 00000000000001b9 
[  100.485122] R10: 0000000000000001 R11: ffffffff92ee37c0 R12: 0000000000000000 
[  100.485661] R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 
[  100.486178] FS:  00007f37d424a740(0000) GS:ffff94b8bda00000(0000) knlGS:0000000000000000 
[  100.486734] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033 
[  100.487224] CR2: ffffffffc0342fe2 CR3: 000000007b780000 CR4: 00000000000406f0

Ne kadar karmakarışık olduğunu görüyorsunuz. Bunu gördüğümde o kadar korktum ki, ilk başta ne anlama geldiğini anlamaya bile çalışmadım. Pes edene kadar, deneme yanılma yoluyla hata ayıklamayı denedim.


Kafanız karışmasın, gerçek kernel panic bu değildi. Size gösterebilmek için yukarıdaki örneğe bir hata ekledim:


static void __exit hello_exit(void) {
     int *p;
     
     printk(KERN_INFO "Goodbye, cruel world, I'm leaving you today");
     p = NULL;
     *p = 0; }

Biraz araştırdıktan sonra, bazı mesajları hatayı bulacak kadar anlayabildim. “Oops”, sorunun ciddiyetinin yüksek olduğunu anlatıyor.


BUG: kernel NULL pointer dereference, address: 0000000000000000 asıl hatayı,
RIP: 0010:hello_exit+0xc/0x1000 [hello] hatayı oluşturan komutun işlemci kaydını
gösteriyor.


Bu bilgileri kullanarak sorunlu koda gdb ile ulaşabildim:


$ gdb hello.ko
(gdb) list *(hello_exit+0xc)
0x70 is in hello_exit (/home/vagrant/hello/hello.c:22).
17      static void __exit hello_exit(void) {
18          int *p;
19          printk(KERN_INFO "Goodbye, cruel world, I'm leaving you today\n");
20
21          p = NULL;
22          *p = 0;
23      }
24
25      module_init(hello_init);
26      module_exit(hello_exit);

Yazan: Ege Güneş

Yayınlanma Tarihi: 29.01.2021


Kaynaklar

  1. https://www.tldp.org/LDP/lkmpg/2.6/html/c38.html
  2. https://jvns.ca/blog/2014/09/18/you-can-be-a-kernel-hacker/
  3. https://opensourceforu.com/2011/01/understanding-a-kernel-oops/