|
|
| Bu makalenin farklı dillerde bulunduğu adresler: English Deutsch Francais Russian Turkce |
Guido Socher (homepage) Yazar hakkında: Guido, sorunları araştırma fırsatı tanıdıkları için Linux gibi açık kaynak kodlu sistemlerden hoşlanmaktadır. Eğer, zaman ayırabilirseniz, sorunların ana kaynağını bulmanız olasıdır. Türkçe'ye çeviri: Erdal Mutlu <erdal(at)linuxfocus.org> İçerik: |
Bu neden çalışmıyor? Linux uygulamalarındaki hatalar nasıl belirlenebilir ve düzeltilebilir?
Özet:
Herkes Linux altında çalışan uygulamaların hatalarını kolayca belirlenebileceğinden ve düzeltilebileceğinden
söz ediyor. Ne yazık ki, bunun nasıl yapıldığını anlatan belgeler bulmak çok zor.
Bu yazıda, bir uygulamanın aslında nasıl çalıştığını öğrenmeden, uygulamada oluşan hataları
bulmayı öğreneceksiniz.
|
Kaynak kodu kapalı olan sistemlerde bir sorun ortaya çıktığında, yapabilecekleriniz genelde aşağıdaki iki seçenekle sınırlıdır:
Bu engellere karşın, programın tüm kaynak kodunu okumadan ve hasıl çalıştığını öğrenmeden önce, yapabileceğiniz şeyler vardır.
#include
void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);
facility (olanak) iletiyi gönderenin hangi türden bir uygulama olduğunu belirler.
priority (öncelik) iletinin önemini belirtmektedir.
priority (öncelik) için olası değerler şunlardır:
LOG_EMERG
LOG_ALERT
LOG_CRIT
LOG_ERR
LOG_WARNING
LOG_NOTICE
LOG_INFO
LOG_DEBUG
C programlama dilinde yazılan her program sistem çetele dosyasına yazabilir.
Diğer programlama dillerin de kendi olanakları vardır. Kabuk betikleri
bile çetele tutabilir:
logger -p err "Bu yazı /var/log/messages dosyasına yazılacaktır."syslog'un standart yapılandırma dosyasında (/etc/syslog.conf) başka yapılandırmaların yanısıra, aşağıdakine benzer bir satır olması gerekir:
# Log anything (except mail) of level info or higher. # Don't log private authentication messages. *.info;mail.none;authpriv.none /var/log/messages"*.info" öncelik seviyesi LOG_INFO veya daha yüksek olan herşeyi çetele dosyasına yazacaktır. Daha fazla bilgi görmek isterseniz, bunu "*.debug" yapıp syslog'u yeniden başlatabilirsiniz (/etc/init.d/syslog restart).
1) tail -f /var/log/messages komutunu çalıştırın ve başka bir kabuktan hatalı uygulamayı başlatın. Belkide daha şimdiden yanlış giden şeyler hakkında bilgi elde edebilrsiniz. 2) Eğer, 1. adım yeterli olmazsa, /etc/syslog.conf'daki *.info yu *.debug olarak değiştirin ve "/etc/init.d/syslog restart" komutuyla syslog servisini yeniden başlatıp ve 1. adıma dönün.Bu yöntemin eksiği, yazılımcının kaynak kodunda ne kadar denetim yaptığına ve bunları çetele dosyasına gönderip göndermemesine olmasıdır. Eğer, önemli yerlere syslog fonksiyonunu yereştirmemişse, hiçbir şey göremeyebilirsiniz. Başka bir deyişle, sorunları belirleyebilmenin şartı, yazılımcının bazı yerlerde sorunların çıkabileceğini öngörmüş olmasıdır.
strace your_applicationİşte bir örnek:
# strace /usr/sbin/uucico
execve("/usr/sbin/uucico", ["/usr/sbin/uucico", "-S", "uucpssh", "-X", "11"],
[/* 36 vars */]) = 0
uname({sys="Linux", node="brain", ...}) = 0
brk(0) = 0x8085e34
mmap2(NULL, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40014000
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=70865, ...}) = 0
mmap2(NULL, 70865, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40015000
close(3) = 0
open("/lib/libnsl.so.1", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300;\0"..., 1024)
= 1024
fstat64(3, {st_mode=S_IFREG|0755, st_size=89509, ...}) = 0
mmap2(NULL, 84768, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x40027000
mprotect(0x40039000, 11040, PROT_NONE) = 0
mmap2(0x40039000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x11)
= 0x40039000
mmap2(0x4003a000, 6944, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) =
0x4003a000
close(3) = 0
open("/lib/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0`X\1\000"..., 1024)
= 1024
fstat64(3, {st_mode=S_IFREG|0755, st_size=1465426, ...}) = 0
mmap2(NULL, 1230884, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x4003c000
mprotect(0x40163000, 22564, PROT_NONE) = 0
mmap2(0x40163000, 12288, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED, 3, 0x126) = 0x40163000
mmap2(0x40166000, 10276, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40166000
close(3) = 0
munmap(0x40015000, 70865) = 0
brk(0) = 0x8085e34
brk(0x8086e34) = 0x8086e34
brk(0) = 0x8086e34
brk(0x8087000) = 0x8087000
open("/usr/conf/uucp/config", O_RDONLY) = -1 ENOENT (No such file or directory)
rt_sigaction(SIGINT, NULL, {SIG_DFL}, 8) = 0
rt_sigaction(SIGINT, {0x806a700, [],
SA_RESTORER|SA_INTERRUPT, 0x40064d58}, NULL, 8) = 0
rt_sigaction(SIGHUP, NULL, {SIG_DFL}, 8) = 0
rt_sigaction(SIGHUP, {0x806a700, [],
SA_RESTORER|SA_INTERRUPT, 0x40064d58}, NULL, 8) = 0
rt_sigaction(SIGQUIT, NULL, {SIG_DFL}, 8) = 0
rt_sigaction(SIGQUIT, {0x806a700, [],
SA_RESTORER|SA_INTERRUPT, 0x40064d58}, NULL, 8) = 0
rt_sigaction(SIGTERM, NULL, {SIG_DFL}, 8) = 0
rt_sigaction(SIGTERM, {0x806a700, [],
SA_RESTORER|SA_INTERRUPT, 0x40064d58}, NULL, 8) = 0
rt_sigaction(SIGPIPE, NULL, {SIG_DFL}, 8) = 0
rt_sigaction(SIGPIPE, {0x806a700, [],
SA_RESTORER|SA_INTERRUPT, 0x40064d58}, NULL, 8) = 0
getpid() = 1605
getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=1024}) = 0
close(3) = -1 EBADF (Bad file descriptor)
close(4) = -1 EBADF (Bad file descriptor)
close(5) = -1 EBADF (Bad file descriptor)
close(6) = -1 EBADF (Bad file descriptor)
close(7) = -1 EBADF (Bad file descriptor)
close(8) = -1 EBADF (Bad file descriptor)
close(9) = -1 EBADF (Bad file descriptor)
fcntl64(0, F_GETFD) = 0
fcntl64(1, F_GETFD) = 0
fcntl64(2, F_GETFD) = 0
uname({sys="Linux", node="brain", ...}) = 0
umask(0) = 022
socket(PF_UNIX, SOCK_STREAM, 0) = 3
connect(3, {sa_family=AF_UNIX,
path="/var/run/.nscd_socket"}, 110) = -1 ENOENT (No such file or directory)
close(3) = 0
open("/etc/nsswitch.conf", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=499, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40015000
read(3, "# /etc/nsswitch.conf:\n# $Header:"..., 4096) = 499
read(3, "", 4096) = 0
close(3) = 0
munmap(0x40015000, 4096) = 0
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=70865, ...}) = 0
mmap2(NULL, 70865, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40015000
close(3) = 0
open("/lib/libnss_compat.so.2", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300\25"..., 1024)
= 1024
fstat64(3, {st_mode=S_IFREG|0755, st_size=50250, ...}) = 0
mmap2(NULL, 46120, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x40169000
mprotect(0x40174000, 1064, PROT_NONE) = 0
mmap2(0x40174000, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED, 3, 0xa) = 0x40174000
close(3) = 0
munmap(0x40015000, 70865) = 0
uname({sys="Linux", node="brain", ...}) = 0
brk(0) = 0x8087000
brk(0x8088000) = 0x8088000
open("/etc/passwd", O_RDONLY) = 3
fcntl64(3, F_GETFD) = 0
fcntl64(3, F_SETFD, FD_CLOEXEC) = 0
fstat64(3, {st_mode=S_IFREG|0644, st_size=1864, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40015000
_llseek(3, 0, [0], SEEK_CUR) = 0
read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 1864
close(3) = 0
munmap(0x40015000, 4096) = 0
getuid32() = 10
geteuid32() = 10
chdir("/var/spool/uucp") = 0
open("/usr/conf/uucp/sys", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/var/log/uucp/Debug", O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, 0600) = 3
fcntl64(3, F_GETFD) = 0
fcntl64(3, F_SETFD, FD_CLOEXEC) = 0
fcntl64(3, F_GETFL) = 0x401 (flags O_WRONLY|O_APPEND)
fstat64(3, {st_mode=S_IFREG|0600, st_size=296, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40015000
_llseek(3, 0, [0], SEEK_CUR) = 0
open("/var/log/uucp/Log", O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, 0644) = 4
fcntl64(4, F_GETFD) = 0
fcntl64(4, F_SETFD, FD_CLOEXEC) = 0
fcntl64(4, F_GETFL) = 0x401 (flags O_WRONLY|O_APPEND)
Burada ne görüyoruz? İsterseniz aşağıdaki satırlara bir göz atalım:
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
Elde edilen çıktıya bir bakalım. Program ilk önce /etc/ld.so.preload
dosyasını okumaya çalışıyor ve başarısız oluyor, daha sonra /etc/ld.so.cache
dosyasını okumaktadır. Burada başarılı olmakta ve 3 nolu dosya tanımlayıcısını
elde etmektedir. /etc/ld.so.preload dosyasını okuyamaması tam olarak bir hata
anlamına gelmeyebilir, çünkü belkide program varsa ilk önce bu dosyayı okumak
istemiştir. Başka bir deyişle, bir dosyayı okuyamamak, mutlaka hata
anlamına gelmeyebilir. Herşey programın tasarlanma şekline bağlıdır.
strace çıktısındaki tüm open sistem çağrılarına bir göz atalım:
open("/usr/conf/uucp/config", O_RDONLY)= -1 ENOENT (No such file or directory)
open("/etc/nsswitch.conf", O_RDONLY) = 3
open("/etc/ld.so.cache", O_RDONLY) = 3
open("/lib/libnss_compat.so.2", O_RDONLY) = 3
open("/etc/passwd", O_RDONLY) = 3
open("/usr/conf/uucp/sys", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/var/log/uucp/Debug", O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, 0600) = 3
open("/var/log/uucp/Log", O_WRONLY|O_APPEND|O_CREAT|O_NOCTTY, 0644) = 4
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
Şimdi program /usr/conf/uucp/config dosyasını okumaya çalışıyor. Ooo! Ama bu çok
garip benim dosyam /etc/uucp/config dir! Ve /etc/uucp/config dosyasını açmak isteyen
hiçbir satır çıktıda görünmüyor. Hata bu olmalı işte. Herhalde yapılandırma dosyasının yeri,
programın derlenmesi sırasında yanlış belirtildi.
# ulimit -c unlimited # ./lshref -i index.html,index.htm test.html Segmentation fault (core dumped) Exit 139Oluşan core dosyası, gdb hata ayıklayıcısı kullanılarak sorunun neden kaynaklandığı araştırması yapılabilir. gdb'yi çalıştırmadan önce, doğru core dosyasına baktığınızı denetlemede yarar vardır:
# file core.16897 core.16897: ELF 32-bit LSB core file Intel 80386, version 1 (SYSV), SVR4-style, from 'lshref'Tamam, bozuk olan program lshref dir ve onu şimdi gdb'ye yükleyebiliriz. gdb'yi bir core dosyasını incelemek için çalıştırmak istediğinizde, core dosyasının adıyla birlikte, bu dosyanın oluşmasına neden olan programı da belirtmeniz gerekmektedir.
# gdb ./lshref core.23061 GNU gdb Linux (5.2.1-4) Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. Core was generated by `./lshref -i index.html,index.htm test.html'. Program terminated with signal 11, Segmentation fault. Reading symbols from /lib/libc.so.6...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 #0 0x40095e9d in strcpy () from /lib/libc.so.6 (gdb)Şimdi artık programın strcpy fonksiyonunu kullanırken çöktüğünü biliyoruz. Ancak, program içerisinde strcpy birden fazla yerde kullanılıyor olabilir.
gdb ./lshref core.23061 GNU gdb Linux (5.2.1-4) Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. Core was generated by `./lshref -i index.html,index.htm test.html'. Program terminated with signal 11, Segmentation fault. Reading symbols from /lib/libc.so.6...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 #0 0x40095e9d in strcpy () from /lib/libc.so.6 (gdb) backtrace #0 0x40095e9d in strcpy () from /lib/libc.so.6 Cannot access memory at address 0xbfffeb38 (gdb) run ./lshref -i index.html,index.htm test.html Starting program: /home/guido/lshref ./lshref -i index.html,index.htm test.html Program received signal SIGSEGV, Segmentation fault. 0x40095e9d in strcpy () from /lib/libc.so.6 (gdb) backtrace #0 0x40095e9d in strcpy () from /lib/libc.so.6 #1 0x08048d09 in string_to_list () #2 0x080494c8 in main () #3 0x400374ed in __libc_start_main () from /lib/libc.so.6 (gdb)Şimdi görüyoruz ki, sorun yarat strcpy fonksiyonunu string_to_list() fonksiyonu, onu da main() çağırmıştır. string_to_list() fonksiyonuna bir bakalım:
char **string_to_list(char *string){
char *dat;
char *chptr;
char **array;
int i=0;
dat=(char *)malloc(strlen(string))+5000;
array=(char **)malloc(sizeof(char *)*51);
strcpy(dat,string);
malloc satırında bir yazım hatası var gibi gözüküyor. Aslında şöyle olmalıydı:
dat=(char *)malloc(strlen(string)+5000);
#includeProgramı derliyor:#include int add(int *p,int a,int b) { *p=a+b; return(*p); } int main(void) { int i; int *p = 0; /* a null pointer */ printf("result is %d\n", add(p,2,3)); return(0); }
gcc -Wall -g -o exmp exmp.cve çalıştırıyoruz ...
# ./exmp Segmentation fault (core dumped) Exit 139
gdb exmp core.5302 GNU gdb Linux (5.2.1-4) Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. Core was generated by `./exmp'. Program terminated with signal 11, Segmentation fault. Reading symbols from /lib/libc.so.6...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 #0 0x08048334 in add (p=Cannot access memory at address 0xbfffe020 ) at exmp.c:6 6 *p=a+b;gdb, hatanın 6. satırda ve 'p' işaretçisinin, erişim hakkı olmadığı bir bellek alanına erişmek isterken oluştuğunu söylemektedir.
|
|
Görselyöre sayfalarının bakımı, LinuxFocus Editörleri tarafından yapılmaktadır
© Guido Socher, FDL LinuxFocus.org |
Çeviri bilgisi:
|
2004-07-02, generated by lfparser version 2.43