SSH server vytuhne

SSH server vytuhne
« kdy: 04. 09. 2023, 09:13:50 »
Ahoj,
dlouhodobě provozuju program SSH serveru v linuxu postavený na openSSH, psaný v C.
Od začátku co jsem si s tím začal hrát jsem pozoroval, že funkce:
Kód: [Vybrat]
ssh_bind_accept(sshbind, session)má pouze blokující volání. To se mi nikdy nepovedlo vyřešit, ale s tímto umím žít.

Co mi ale vadí je to, že po nějaké době cca jeden den SSH server vytuhne a už nepříjme žádné spojení.
Blokovaná funkce už nikdy neskončí a zůstává to v ní viset dokud nevypnu/zapnu program serveru.

Ze začátku jsem si myslel, že to je proto že mám málo spojení na server. Pak jsem si ale vedle postavil
program SSH klienta, kterej se každou minutu připojí a opět odpojí. Tím jsem si toto vyloučil a stále to vytuhne.

Nyní bych to chtěl vyřešit lépe a chtěl bych to udělat tak, že nebude docházet k vypnutí/zapnutí programu serveru, ale
že se SSH pokusím reinicializovat za běhu. Doufám, že to povede k pro mě přijatelnějšímu chování.

Chtěl bych se ale zeptat jestli někdo netuší proč to SSH vytuhne? Případně jestli něco nemůžu udělat jinak.

Na tom programu serveru beží ještě další např. TCP server, TLS server a žádný z nich to nedělá, takže toto je pro mě velká záhada.
Dík.
 


Logik

  • *****
  • 1 035
    • Zobrazit profil
    • E-mail
Re:SSH server vytuhne
« Odpověď #1 kdy: 04. 09. 2023, 10:23:54 »
Seš si jistej, že tam nemáš např. nějakou chybu správy paměti, špatnej zápis někam atd...?
Zkusil jsi to spustit s valgrindem?

Re:SSH server vytuhne
« Odpověď #2 kdy: 04. 09. 2023, 10:40:53 »
tak tohle určitě vyloučit nemůžu, ale pokud zavolám v linuxu nějakou funkci zde např.
Kód: [Vybrat]
ssh_bind_accept(sshbind, session), tak bych očekával, že ta funkce někdy skončí. V tomto případě ta funkce skončí jen v případě, že příjde SSH spojení jinak neznám jediný způsob jak jí ukončit. V momentě kdy to vytuhne, tak už žádné spojení nepřijme a nikdy tedy neskončí. Přiznám se, že nechápu kde by v tomto případě mohl být problém s pamětí, protože jakmile tu funkci zavolám, tak se celá další obsluha programu předává OS dokud ten funkci neukončí a neumožní pokračovat v programu.

Jinak se omlouvám, ale špatně jsem napsal, že používá openSSH. Používá libssh. Chybně jsem se domníval, že to znamená to stejné. Je možné, že implementace libssh je nějaká zastaralá, nebo neúplná. Možná je chyba právě v tom.


Re:SSH server vytuhne
« Odpověď #3 kdy: 04. 09. 2023, 10:52:26 »
Obsluhu navázaného spojení děláte ve stejném vlákně? Nemůže být problém v tom, že nikdy neskončí vaše obsluha nového spojení?

Re:SSH server vytuhne
« Odpověď #4 kdy: 04. 09. 2023, 11:10:20 »
Ano celá obsluha SSH je v jednom vlákně. Ono to tak musí být, protože funkce příjmu spojení je blokující.
v libssh sice existuje funkce
Kód: [Vybrat]
sss_bind_set_blocking(sshbind,0), ale ta podle mě nefunguje nebo jsem na to nepřišel.
zde je kód:
Kód: [Vybrat]
static void *handle_init_accept_sshbind(void *arg){
  ssh_session session;
  uint8_t pom;
  ssh_bind sshbind = (ssh_bind)arg;
  while(1){
    session = ssh_new();
    if(session != NULL){
     ssh_set_message_callback(session, nc_sshcb_msg, session);
     if(ssh_bind_accept(sshbind, session) != SSH_OK){
       ssh_disconnect(session);
       }
     else{
       if(ssh_handle_key_exchange(session) != SSH_OK){
         ssh_disconnect(session);
         }
       else{
         //spojeni prijato
         }       
       }
     }
   }
   ssh_bind_free(sshbind);
   ssh_finalize();
   return NULL;
   }

A vytuhne to zde:
Kód: [Vybrat]
if(ssh_bind_accept(sshbind, session) != SSH_OK){


Re:SSH server vytuhne
« Odpověď #5 kdy: 04. 09. 2023, 14:33:06 »
Být to tak určitě nemusí. Ten současný kód máte udělaný tak, že se serverem může komunikovat vždy jen jeden klient. Jste si jistý tím, že „zatuhne“ ssh_bind_accept()? Já bych si tipoval, že se spíš neukončí spojení s klientem, a protože to máte udělané tak, že to blokuje jinou komunikaci, nedostane se ssh_bind_accept() zpět ke slovu. Třeba by se to mohlo stát v případě, kdy klient neukončí správně spojení a z pohledu síťové komunikace zůstane spojení otevřené.
Obecně mi nepřipadá šťastné mít server udělaný tak, že v jednu chvíli se může připojit jen jeden klient – nedovedu se představit, že bych to použil jinde než v nějakém jednoduchém prototypu.

Logik

  • *****
  • 1 035
    • Zobrazit profil
    • E-mail
Re:SSH server vytuhne
« Odpověď #6 kdy: 04. 09. 2023, 15:38:05 »
Pokud program zapisuje kam nemá, pak může porušit interní struktury toho socketu a může to dělat cokoli.
Neříkám, že to je nutně Tvůj případ, ale pokud něco blbne, tak zkontrolovat to valgrindem je první věc, co stojí za to udělat,
a "nic Tě to nestojí".

Re:SSH server vytuhne
« Odpověď #7 kdy: 04. 09. 2023, 15:51:25 »
Citace
Jste si jistý tím, že „zatuhne“ ssh_bind_accept()
Jsem si tím jistej, ale nerad bych něco přehlížel takže to ještě jednou ověřím. Nejsem v tomto vůbec kovanej takže všechno je čistě amatérské a někdy ani nevím jak bych měl správně postupovat. Zdrojových příkladů na SSH server jsem nikde moc nenašel, takže takže je to hodně velkej pokus omyl....doufám, že se za to na mě nikdo nebude zlobit a že právě pro takovéto účely vznikají tato fóra. K ladění používám funkci printf a snažím se prokousat těmi hroznými výpisy, ale v případě více vláken možná tato metoda selhává :-)

Citace
Ten současný kód máte udělaný tak, že se serverem může komunikovat vždy jen jeden klient.
Toto není pravda i když to tak na první pohled vypadá. Pokud Vás to zajímá tak trošku víc popíšu jak to je udělané a proč.
funkce
Citace
ssh_bind_accept(sshbind, session)
je pro mě blokující. Jakmile jí tedy zavolám tak neskončí dokud se žádnej klient nepokusí navázat spojení. To je pokud chcete komunikovat s více zařízeními dost omezující nemyslíte? Nevím jak to dělá někdo kdo to umí ale já to řeším tak, že celý příjem SSH spojení běží ve vlastním vlákně. Jakmile je spojení přijato, předávám přes mutex deskriptor navázaného spojení hlavnímu vláknu. Jakmile je deskriptor předaný tak se vracím opět k
Citace
ssh_bind_accept(sshbind, session)
a čekám na další spojení. Vlákno tedy jen čeká v blokující funkci a jakmile vrátí příjem, předá ukazatel a opakuje se. Veškeré odbavení spojení jako příjem, odeslání a ukončení je už řešeno jako neblokující v jiném vlákně. Tenhle princip mi funguje a zkoušel jsem i takové experimenty, že jsem si na úplně jiném počítači vytvořil tisíce SSH klientů, které se trvale jen připojovali a odpojovali. Veškeré mé pokusy a omyly vždy vedly ke stejnému závěru. Tím je právě ta blokující funkce, takže se furt točím dokola.

Citace
zkontrolovat to valgrindem je první věc
Toto jsem opakovaně zkoušel, ale nikdy jsem na nic nepřišel. Asi se k tomu s odstupem času opět vrátím.

Re:SSH server vytuhne
« Odpověď #8 kdy: 04. 09. 2023, 17:00:21 »
Citace
Ten současný kód máte udělaný tak, že se serverem může komunikovat vždy jen jeden klient.
Toto není pravda i když to tak na první pohled vypadá.
Zmátlo mne to špatné odsazení.

funkce
Citace
ssh_bind_accept(sshbind, session)
je pro mě blokující. Jakmile jí tedy zavolám tak neskončí dokud se žádnej klient nepokusí navázat spojení. To je pokud chcete komunikovat s více zařízeními dost omezující nemyslíte?
Ne, není to omezující. Takhle fungují servery na unixech desítky let. Server poslouchá na portu, čeká na nové příchozí spojení (čeká = blokuje). Když přijde spojení od klienta, proces se forkne, fork zpracovává příchozí spojení a původní proces se vrátí k čekání. Takhle vypadá typický unixový server už asi tak půl století. Případně místo forkování se může vytvořit nové vlákno, což se používá třeba na Windows, kde vytvoření nového procesu není tak levná záležitost, jako na linuxu. Neblokující volání je módní výstřelek posledních pár desetiletí, ale to blokující volání není nic, co by vám v něčem bránilo.

Nevím jak to dělá někdo kdo to umí ale já to řeším tak, že celý příjem SSH spojení běží ve vlastním vlákně. Jakmile je spojení přijato, předávám přes mutex deskriptor navázaného spojení hlavnímu vláknu. Jakmile je deskriptor předaný tak se vracím opět k
Citace
ssh_bind_accept(sshbind, session)
a čekám na další spojení. Vlákno tedy jen čeká v blokující funkci a jakmile vrátí příjem, předá ukazatel a opakuje se. Veškeré odbavení spojení jako příjem, odeslání a ukončení je už řešeno jako neblokující v jiném vlákně. Tenhle princip mi funguje a zkoušel jsem i takové experimenty, že jsem si na úplně jiném počítači vytvořil tisíce SSH klientů, které se trvale jen připojovali a odpojovali. Veškeré mé pokusy a omyly vždy vedly ke stejnému závěru. Tím je právě ta blokující funkce, takže se furt točím dokola.
No a jste si jist, že tohle všechno máte správně? Nemůže tam někde dojít k deadlocku? Předáváte si správně data mezi vlákny? Nemůže dojít k blokování v tom pracovním vlákně?

Re:SSH server vytuhne
« Odpověď #9 kdy: 04. 09. 2023, 17:37:30 »
Citace
Nemůže dojít k blokování v tom pracovním vlákně?
Domnívám se, že nikoliv. Vede mě k tomu fakt, že v tom pracovním vlákně běží obsluha dalších filedeskriptorů, které mi normálně komunikují. Současně každou sekundu si na konzoli vypisuji krátký text, že toto pracovní vlákno žije. V podstatě proto jsem se tímto celým začal zabývat, protože v pracovním vlákně se dělo všechno podle očekávání a jen jakoby vlákno na příjem SSH stálo.

Zkusil jsem to pustit přes ten valgrind. Po nějaké době jsem to chcípnul a vypadlo toto:
Kód: [Vybrat]
==48106==
==48106== HEAP SUMMARY:
==48106== in use at exit: 6,740 bytes in 42 blocks
==48106== total heap usage: 383,119 allocs, 383,077 frees, 104,143,130 bytes allocated
==48106==
==48106== 3,406 (32 direct, 3,374 indirect) bytes in 1 blocks are definitely lost in loss record 42 of 42
==48106== at 0x484DA83: calloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==48106== by 0x49FE9AB: ssh_new (in /usr/lib/x86_64-linux-gnu/libssh.so.4.8.7)
==48106== by 0x10CAC4: handle_init_accept_sshbind (server_ssh.c:432)
==48106== by 0x4DAEB42: start_thread (pthread_create.c:442)
==48106== by 0x4E3FBB3: clone (clone.S:100)
==48106==
==48106== LEAK SUMMARY:
==48106== definitely lost: 32 bytes in 1 blocks
==48106== indirectly lost: 3,374 bytes in 26 blocks
==48106== possibly lost: 0 bytes in 0 blocks
==48106== still reachable: 3,334 bytes in 15 blocks
==48106== suppressed: 0 bytes in 0 blocks
==48106== Reachable blocks (those to which a pointer was found) are not shown.
==48106== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==48106==
==48106== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Prijde mi dovny tento radek:
Kód: [Vybrat]
==48106== by 0x49FE9AB: ssh_new (in /usr/lib/x86_64-linux-gnu/libssh.so.4.8.7)
Dela to na me dojem, ze zavolanim funkce ssh_new(), kterou volám každým příjmem se asi vytváří někde nové vlákno. Nevím jestli to něčemu vadí nebo ne, ale dneska už na to nemám sílu tak se tomu budu zas někdy věnovat.
Dík za všechny reakce a hezký zbytek dne :-)

f

Re:SSH server vytuhne
« Odpověď #10 kdy: 06. 09. 2023, 10:51:57 »
A proc se v momente, kdy to je vytuhle, nepodivas pomoci GDB, na cem to visi v tom libssh?

Re:SSH server vytuhne
« Odpověď #11 kdy: 07. 09. 2023, 17:47:28 »
Citace
A proc se v momente, kdy to je vytuhle, nepodivas pomoci GDB, na cem to visi v tom libssh?
Asi proto, že mě to buď nenapadne nebo to neumim :-)

Dneska jsem ale trošku pokročil a zaměřil se na ten valgrind. Možná se mi povedlo najít kde mám chybu, ale ještě si tím nejsem úplně jistej. Musí to běžět docela dlouho, aby to vyplivlo nějaká smysluplná data ve kterých jsem schopen něco najít.

Zdá se, že skutečně špatně pracuji s pamětí což ti chytřejší asi tušili hned. Pokud se z nějakého důvodu rozhodnu navázanou session ukončit tak volám funkci
Kód: [Vybrat]
ssh_disconnect(session); Toto sice session uzavře, ale neuvolňuje to paměť kterou přiřadila funkce
Kód: [Vybrat]
session = ssh_new();. Pravděpodobně je po tomto nutné zavolat ještě funkci
Kód: [Vybrat]
ssh_free(session); Toto mi v celém kódu unikalo a důsledky jsou asi zřejmé. Je zvláštní, že mi ten program sám od sebe vůbec nepadá a jen zatuhne.

Pokud se mi povede ještě ukončit ten accept, bude to ještě lepší, protože pak budu schopen celý příjem SSH korektně ukončit včetně vlákna ve kterém to běží. V tuhle chvíli, když to zastavím natvrdo tak valgrind stále hlásí možný únik což je pořád nějaké varování. Předpokládám, že správný výsledek je když jsou detekované nulové možné i skutečné úniky.





Wasper

  • ***
  • 120
    • Zobrazit profil
    • E-mail
Re:SSH server vytuhne
« Odpověď #12 kdy: 07. 09. 2023, 19:04:09 »
Citace
A proc se v momente, kdy to je vytuhle, nepodivas pomoci GDB, na cem to visi v tom libssh?
Asi proto, že mě to buď nenapadne nebo to neumim :-)
No na podobné problémy je někdy mnohem lepší použít strace, který kompletně ignoruje celý komplexní kód a jen píše syscally, takže často dá velmi dobrý hint, co vlastně hledat.

Z hlavy (zkoukni radši man, hlavně kolem clone/fork a threadů) strace -f -ff -o filename- -s 120 ./program
(případně varianta pro už běžící strace ......  -p PID )

f

Re:SSH server vytuhne
« Odpověď #13 kdy: 07. 09. 2023, 21:13:15 »
strace je skvely nastroj, ale v pripade, ze to je moje aplikace a vim z jake funkce se mi to nevrati, tak je debugger urcite efektivnejsi. Stejne tak bych v tomto pripade doporucil spis ASAN nez valgrind. Valgrind nektere typy chyb odhalit nedokaze, viz napriklad srovnani zde nebo zde

Re:SSH server vytuhne
« Odpověď #14 kdy: 08. 09. 2023, 13:01:02 »
Ať už GDB nebo Valgrind, měla by existovat možnost, v momentě kdy je proces vytuhlý, tak ho "breaknout". Následně si vypsat stack = vnoření funkcí. Až dovnitř do té knihovny. Pokud to na pozadí visí na nějakém syscallu, mělo by to vypsat jako nejhlubší úroveň zanoření ten syscall - čekal bych accept() nebo tak něco. Pokud je to v nekonečné smyčce, tak by to mělo ukázat kde. Podle mého je ale podmínkou, že libssh musí být linknutá včetně debugovacích symbolů, jinak stack trace bude obsahovat jenom nic neříkající numerické adresy... Tzn. možná Vás to donutí, sám si to SSHčko přeložit.

Pokud byste došel k tomu, že visí bloknutý accept(), spotřeba procesoru je nula a příchozí spojení se "nedozvoní", tak je mi to divné... iptables, apparmor/selinux, co já vím... toto jsem nikdy nezažil.

BTW když už si libssh budete překládat ze zdrojáků, tak nemusí být velký problém, povolit debug výpisy / přidat nějaké svoje hlášky na strategická místa, abyste viděl konkrétněji, kam až program před zátuhem doběhl uvniř knihovny.

Rychlým googlem jsem našel zmínku, že se zřejmě v libssh v produkčním buildu dá zvýšit log level...