2015-11-12 17 views
16

Mam pewne problemy z serwerem dzisiaj i teraz sprowadziłem go do tego, że nie jest w stanie pozbyć się procesów, które dostają segfault.Uszkodzenie segmentacji samo wisi

Po tym, jak proces otrzyma błąd seg-fault, proces się zawiesi, a nie zostanie zabity.

Test, w wyniku którego powinien spowodować błąd Segmentation fault (core dumped).

#include <stdio.h> 
#include <stdlib.h> 
int main(int argc, char **argv) 
{ 
char *buf; 
buf = malloc(1<<31); 
fgets(buf, 1024, stdin); 
printf("%s\n", buf); 
return 1; 
} 

Skompiluj i ustaw uprawnienia za pomocą gcc segfault.c -o segfault && chmod +x segfault.

Uruchomienie tego (i naciśnięcie klawisza Enter 1 raz) na problematycznym serwerze powoduje jego zawieszenie. Uruchomiłem to również na innym serwerze z tą samą wersją jądra (i większością tych samych pakietów), i otrzymuje on błąd seg-fault, a następnie kończy działanie.

Oto kilka ostatnich linii po uruchomieniu strace ./segfault na obu serwerach.

Bad serwer

"\n", 1024)      = 1 
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0} --- 
# It hangs here.... 

Praca serwer

"\n", 1024)      = 1 
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0} --- 
+++ killed by SIGSEGV (core dumped) +++ 
Segmentation fault (core dumped) 
[email protected] { ~ }# echo $? 
139 

Gdy zawiesza procesowe (po to są segfaulted), to jak to wygląda.

nie może^c it

[email protected] { ~ }# ./segfault 

^C^C^C 

Wejście ps aux

root 22944 0.0 0.0 69700 444 pts/18 S+ 15:39 0:00 ./segfault

cat/Proc/22944/stos

[<ffffffff81223ca8>] do_coredump+0x978/0xb10 
[<ffffffff810850c7>] get_signal_to_deliver+0x1c7/0x6d0 
[<ffffffff81013407>] do_signal+0x57/0x6c0 
[<ffffffff81013ad9>] do_notify_resume+0x69/0xb0 
[<ffffffff8160bbfc>] retint_signal+0x48/0x8c 
[<ffffffffffffffff>] 0xffffffffffffffff 

Kolejną zabawną rzeczą jest to, że nie mogę dołączyć do procesu składania w zawieszeniu strace. Czyniąc to faktycznie sprawia, że ​​zostaje zabity.

[email protected] { ~ }# strace -p 1234 
Process 1234 attached 
+++ killed by SIGSEGV (core dumped) +++ 

ulimit -c 0 jest sat i ulimit -c, ulimit -H -c i ulimit -S -c wszystko pokazuje wartość wersję 0

  • jądra: 3.10.0-229.14.1.el7.x86_64
  • Distro wersja: Red Hat Enterprise Linux Server release 7.1 (Maipo)
  • bieganie w VMware

Serwer działa tak, jak powinien na wszystkich pozostałych elementach.

Aktualizacja Wyłączanie ABRT (systemctl stop abrtd.service) rozwiązaniu problemu z procesów już wisiał po podstawowej wysypisko, a nowe procesy core-dumping. Ponowne uruchomienie abrt nie przywróciło problemu.

Aktualizacja 2016-01-26 Mamy problem, który wyglądał podobnie, ale nie do końca taki sam. Początkowy kod używany do testowania:

#include <stdio.h> 
#include <stdlib.h> 
int main(int argc, char **argv) 
{ 
char *buf; 
buf = malloc(1<<31); 
fgets(buf, 1024, stdin); 
printf("%s\n", buf); 
return 1; 
} 

został powieszony. Wyjście cat /proc/<pid>/maps był

00400000-00401000 r-xp 00000000 fd:00 13143328       /root/segfault 
00600000-00601000 r--p 00000000 fd:00 13143328       /root/segfault 
00601000-00602000 rw-p 00001000 fd:00 13143328       /root/segfault 
7f6c08000000-7f6c08021000 rw-p 00000000 00:00 0 
7f6c08021000-7f6c0c000000 ---p 00000000 00:00 0 
7f6c0fd5b000-7f6c0ff11000 r-xp 00000000 fd:00 14284      /usr/lib64/libc-2.17.so 
7f6c0ff11000-7f6c10111000 ---p 001b6000 fd:00 14284      /usr/lib64/libc-2.17.so 
7f6c10111000-7f6c10115000 r--p 001b6000 fd:00 14284      /usr/lib64/libc-2.17.so 
7f6c10115000-7f6c10117000 rw-p 001ba000 fd:00 14284      /usr/lib64/libc-2.17.so 
7f6c10117000-7f6c1011c000 rw-p 00000000 00:00 0 
7f6c1011c000-7f6c1013d000 r-xp 00000000 fd:00 14274      /usr/lib64/ld-2.17.so 
7f6c10330000-7f6c10333000 rw-p 00000000 00:00 0 
7f6c1033b000-7f6c1033d000 rw-p 00000000 00:00 0 
7f6c1033d000-7f6c1033e000 r--p 00021000 fd:00 14274      /usr/lib64/ld-2.17.so 
7f6c1033e000-7f6c1033f000 rw-p 00022000 fd:00 14274      /usr/lib64/ld-2.17.so 
7f6c1033f000-7f6c10340000 rw-p 00000000 00:00 0 
7ffc13b5b000-7ffc13b7c000 rw-p 00000000 00:00 0       [stack] 
7ffc13bad000-7ffc13baf000 r-xp 00000000 00:00 0       [vdso] 
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0     [vsyscall] 

Jednak kod mniejszy c (int main(void){*(volatile char*)0=0;}), aby wywołać segfault nie spowodować segfault i nie powiesić ...

+6

Sprawdź zwracaną wartość 'malloc'. 100% pewności, że jest NULL. Ale 'malloc (1 << 31);' może faktycznie działać, jeśli> 2 GB wolnej pamięci jest dostępne. –

+1

@Michael Walz: To jest pomysł. Ma to spowodować uszkodzenie. –

+0

Czy możesz dać mi przykład? Nie wiem zbyt wiele o programowaniu w języku c/C++. Podany program przykładowy powinien powodować uszkodzenie. – xeor

Odpowiedz

2

UWAGA - odpowiedź ta zawiera szereg przypuszczenia oparte na niepełnych informacjach. Mam nadzieję, że nadal jest przydatna!

Dlaczego segfault wygląda na zawieszony?

Po wyświetleniu śladu stosu jądro jest zajęte tworzeniem zrzutu pamięci procesu awarii.

Ale dlaczego to trwa tak długo? Prawdopodobnym wytłumaczeniem jest to, że metoda, której używasz do tworzenia segfaultów, powoduje, że proces ma ogromną wirtualną przestrzeń adresową.

Jak wskazano w komentarzach MM, wynik wyrażenia 1 < < 31 jest niezdefiniowany przez normy C, więc trudno jest powiedzieć, jaka wartość rzeczywista jest przekazywana do malloc, ale w oparciu o kolejne zachowanie Zakładam, że jest to duża liczba.

Należy zauważyć, że aby funkcja malloc powiodła się, nie jest konieczne, aby faktycznie mieć w systemie taką ilość pamięci RAM - jądro rozszerzy wirtualny rozmiar procesu, ale faktyczna pamięć RAM zostanie przydzielona tylko wtedy, gdy twój program faktycznie wejdzie do tej pamięci RAM .

Sądzę, że wywołanie malloc powiedzie się, a przynajmniej wróci, ponieważ twierdzisz, że jest to naruszenie po naciśnięciu klawisza Enter, a więc po wywołaniu fgets.

W każdym przypadku segfault prowadzi jądro do wykonania zrzutu pamięci. Jeśli proces ma duży rozmiar wirtualny, może to zająć dużo czasu, zwłaszcza jeśli jądro zdecyduje się zrzucić wszystkie strony, nawet te, które nigdy nie zostały dotknięte przez proces. Nie jestem pewien, czy to zrobi, ale jeśli tak, a jeśli w systemie nie ma wystarczającej ilości pamięci RAM, musiałaby ona zacząć wymieniać strony w pamięci i z niej usuwać, aby zrzucić je na zrzut główny. To spowodowałoby wysokie obciążenie IO, które mogłoby spowodować, że proces przestał reagować (a ogólna wydajność systemu zostałaby obniżona).

Niektóre z nich możesz zweryfikować, przeglądając katalog zrzutu abrtd (prawdopodobnie /var/tmp/abrt lub sprawdź /etc/abrt/abrt.conf), gdzie możesz znaleźć zrzuty pamięci (lub częściowe zrzuty pamięci), które zostały utworzone.

Jeśli jesteś w stanie odtworzyć zachowanie, wtedy można sprawdzić:

  • /proc/[pid]/maps aby zobaczyć mapę przestrzeni adresowej procesu i sprawdzić, czy to naprawdę jest duży
  • użyć narzędzia jak vmstat do Sprawdź, czy system się wymienia, ilość operacji we/wy i stan IO Wait jest doświadczany
  • Jeśli masz działający sar, możesz zobaczyć podobne informacje nawet przez okres przed ponownym uruchomieniem abrtd.

Utworzono zrzut główny, mimo że ulimit -c wynosi 0?

Zgodnie z this bug report, abrtd uruchomi zbieranie zrzutu pamięci bez względu na ustawienia ulimit.

Dlaczego to się nie powtórzyło, gdy arbtd ponownie się uruchomił?

Istnieje kilka możliwych wyjaśnień. Po pierwsze, zależy to od ilości wolnej pamięci RAM w systemie. Może się zdarzyć, że pojedynczy rdzeń zrzutu dużego procesu nie zajmie tak dużo czasu i nie będzie postrzegany jako zawieszony, jeśli będzie wystarczająca ilość wolnej pamięci RAM, a system nie zostanie popchnięty do zamiany.

Jeśli w początkowych eksperymentach miałeś kilka procesów w tym stanie, objawy byłyby znacznie gorsze niż w przypadku, gdy pojedynczy proces źle się zachowuje.

Inną możliwością jest to, że konfiguracja abrtd została zmieniona, ale usługa nie została jeszcze ponownie załadowana, więc po jej ponownym uruchomieniu zaczęto używać nowej konfiguracji, być może zmieniając jej zachowanie.

Możliwe jest również, że aktualizacja yum została zaktualizowana abrtd, ale nie została ponownie uruchomiona, tak że po jej ponownym uruchomieniu uruchomiona była nowa wersja.

+0

Dzięki za dobrą odpowiedź. Program 'lsusb -t' również spowodował błąd segfault, który zachowywał się tak samo (także mały fragment kodu .c w komentarzach). Wisiał przez kilka dni, aż go zabił. W plikach abrt nie dokonano żadnych zmian w konfiguracji, zweryfikowałem to teraz. Sprawdzę twoje sugestie, jeśli to się powtórzy. Ten raport o błędzie wygląda na stary. Całkiem pewne, że to nie jest część problemu tutaj .. – xeor