SlideShare ist ein Scribd-Unternehmen logo
1 von 37
Downloaden Sie, um offline zu lesen
Tonący Python 2 pliku się chwyta,
czyli jak debugowaliśmy wyciekające uchwyty pod Windowsem.
Michał Cłapiński, Mateusz Jabłoński
23.05.2018
Czym się zajmujemy?
● Michał Cłapiński
○ michal.clapinski@codilime.com
● Mateusz Jabłoński
○ mateusz.jablonski@codilime.com
● Pracujemy w CodiLime
● Portujemy Tungsten Fabric (dawniej OpenContrail) na Windowsa
● Przygotowujemy system continuous integration w celu testowania naszego kodu
Krótko o Tungsten Fabric
● Multicloud multistack SDN
● SDN = Software-defined networking
● Projekt Linux Foundation
● Budowany za pomocą SConsów
Napotkany problem
● Włączyliśmy budowanie wielowątkowe w naszym CI
○ Znaczne przyśpieszenie budowania (20 minut / 2 godziny)
○ Zysk w CI i na maszynach programistów
Napotkany problem
● Włączyliśmy budowanie wielowątkowe w naszym CI
○ Znaczne przyśpieszenie budowania (20 minut / 2 godziny)
○ Zysk w CI i na maszynach programistów
● Zaczęły pojawiać się nieprzewidywalne błędy
○ ~5% buildów zakończonych niepowodzeniem
○ Błędy na różnym etapie budowania
○ Jeżeli jeden build kompiluje 1000 plików to błąd występował raz na 20000 plików
○ Za każdym razem błąd dotyczył pliku tworzonego przez Pythona
Napotkany problem - przykład
css_DT_bootstrap_css.cpp(4): error C2065: 'DT_bootstrap_css': undeclared identifier
css_DT_bootstrap_css.cpp(4): error C2065: 'DT_bootstrap_css_len': undeclared identifier
scons: *** [sandesh_http.obj] Error 2
scons: building terminated because of errors.
● Plik css_DT_bootstrap_css.cpp jest jest generowany przez SConscript i później
kompilowany
● Plik został źle wygenerowany?
Napotkany problem - przykład
Kod generujący plik css_DT_bootstrap_css.cpp:
1. with open(cname, 'w') as cfile:
2. cfile.write('namespace {n')
3.
4. subprocess.call('xxd -i ' + hname + ' >> ' + os.path.basename(cname), shell=True, cwd=opath)
5.
6. with open(cname, 'a') as cfile:
7. cfile.write('}n')
8. cfile.write(tail_content)
● Brakujący kod jest generowany w linii 4
● Dodanie sprawdzania rezultatu subprocess.call pozwala wychwycić błąd wcześniej
● Nadal nie wiemy jaka jest przyczyna
Napotkany problem - przykład
c1xx: fatal error C1083: Cannot open source file: 'multicast_html.cpp': No such file or directory
scons: *** [multicast_html.obj] Error 2
● multicast_html.cpp jest generowany podobnie do css_DT_bootstrap_css.cpp
● Dlaczego dostajemy błąd ‘No such file or directory’?
● Plik istnieje. Czy został stworzony po próbie jego kompilacji?
Napotkany problem - przykład
Hipoteza: Czy to oznacza błąd w SCons?
Podejście pierwsze: ProcMon
● Process Monitor: narzędzie do monitorowania aktywności systemu plików, rejestru i wątków
● Umożliwia filtrowanie na podstawie różnych kryteriów
○ Nazwa procesu, PID, ścieżka do używanego pliku, wynik i inne
● Chcemy zobaczyć jakie procesy używały jakich plików i kiedy miało to miejsce
● Wielkość logów uniemożliwia budowanie do czasu napotkania błędu z raz włączonym ProcMonem
● Rzadko spotykany błąd, więc ręczny restart ProcMona i budowania trwałby wiele godzin
Podejście pierwsze: ProcMon
Automatyzacja za pomocą skryptu PowerShell:
$ErrorActionPreference = "SilentlyContinue"
Do {
[remove build artifacts]
.internalsprocmon /Quiet /BackingFile proclogslog.pml
.internalsprocmon /WaitForIdle
scons -j 8 contrail-vrouter-agent
$code = $LastExitCode
.internalsprocmon /Terminate
While (Get-Process procmon) {
Start-Sleep 1
}
} While ($code -eq 0)
Podejście pierwsze: ProcMon
DEMO
Podejście pierwsze: ProcMon
● cmd.exe otwiera plik, do którego pisze xxd z powodu użycia przekierowania powłoki (‘>>’)
● Python otworzył plik 3 razy, zamknął 2 razy
● cl.exe próbuje otworzyć plik, do którego uchwyt jest nadal używany przez Pythona
● ‘No such file or directory’, które widzieliśmy w logach to w rzeczywistości SHARING VIOLATION
● Brakujące CloseFile jest wywoływane przez cmd.exe?
Krótko o trybach współdzielenia
HANDLE WINAPI CreateFile(..., _In_ DWORD dwDesiredAccess, _In_ DWORD dwShareMode, ...);
● dwDesiredAccess:
○ GENERIC_READ, GENERIC_WRITE, GENERIC_READ | GENERIC_WRITE (i inne)
● dwShareMode:
○ 0, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE i kombinacje
● ERROR_SHARING_VIOLATION w przypadku niekompatybilnych trybów
● dwShareMode musi brać pod uwagę wcześniejsze wywołanie CreateFile
Krótko o trybach współdzielenia
Pierwsze wywołanie CreateFile Poprawne kolejne wywołania
GENERIC_READ, 0 ---
GENERIC_READ, FILE_SHARE_READ ● GENERIC_READ, FILE_SHARE_READ
● GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE
GENERIC_WRITE, FILE_SHARE_READ ● GENERIC_READ, FILE_SHARE_WRITE
● GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE
GENERIC_WRITE,
FILE_SHARE_READ |
FILE_SHARE_WRITE
● GENERIC_READ, FILE_SHARE_WRITE
● GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE
● GENERIC_WRITE, FILE_SHARE_WRITE
● GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE
● GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE
● GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE
Krótko o trybach współdzielenia
● Python otworzył plik z GENERIC_WRITE | READ_CONTROL
● cl.exe próbuje otworzyć go z FILE_SHARE_READ
● Wywołanie kończy się niepowodzeniem (ERROR_SHARING_VIOLATION)
● Wniosek: Uchwyt na plik nie został zamknięty
Podejście drugie: Frida
● Potrzebowaliśmy potężniejszego, skryptowalnego sposobu monitorowania
● Frida: narzędzie do dynamicznej instrumentacji
● Pozwala wstrzykiwać własny kod do dowolnych procesów
○ Wstrzykuje silnik JavaScriptu V8, pozwala uruchamiać własny kod JS
● Umożliwia przechwytywanie wywołań dowolnych funkcji
○ Logowanie wywołań
○ Modyfikacja argumentów
○ Modyfikacja wartości zwracanej
Podejście drugie: Frida
Minimalny przykład użycia Fridy (monitorowanie otwierania plików):
import frida
import sys
session = frida.attach("myProgram.exe")
script = session.create_script("""
Interceptor.attach(Module.findExportByName(null, "CreateFileA"), {
onLeave: function(handle) {
send("CreateFile " + String(handle.toInt32()))
}
});
""")
def on_message(message, data):
print(message['payload'])
script.on('message', on_message)
script.load()
sys.stdin.read()
Podejście drugie: Frida
var getPathAAddress = Module.findExportByName(null, "GetFinalPathNameByHandleA")
var getPathA = new NativeFunction(getPathAAddress, 'uint32', ['pointer', 'pointer', 'uint32', 'uint32'])
[...]
var handlePath = Memory.alloc(2001)
getPathA(handle, handlePath, 2000, 0)
send("CreateFile " + String(handle.toInt32()) + " " + String(Memory.readUtf8String(handlePath)))
Podejście drugie: Frida
● Wstrzyknęliśmy kod JS do procesu Pythona. Monitorowaliśmy CreateFile i CloseHandle
CreateFile 14468 16:2:46.429 1704 [...]multicast_html.cpp
[...]
CloseHandle 14468 16:2:46.432 1704 [...]multicast_html.cpp
CloseHandle: 1
● Python w każdym przypadku wywoływał poprawnie CloseHandle
● Dlaczego cmd.exe zamyka plik, którego nie otwiera?
● Dlaczego ProcMon nie pokazuje wywołania CloseHandle?
Podejście drugie: Frida
auto f = _open("costam.txt", 0);
char *argv[] = { "cmd.exe", "/c", "pwd && ping 127.0.0.1 -n 5", NULL };
_spawnve(_P_NOWAIT, "C:WindowsSystem32cmd.exe", argv, NULL);
_close(f);
Podejście drugie: Frida
auto f = _open("costam.txt", 0);
char *argv[] = { "cmd.exe", "/c", "pwd && ping 127.0.0.1 -n 5", NULL };
_spawnve(_P_NOWAIT, "C:WindowsSystem32cmd.exe", argv, NULL);
_close(f);
● ProcMon pokazuje jedynie CloseHandle na ostatnim egzemplarzu danego uchwytu (zniszczenie
obiektu w kernelu)
● Wniosek: procesy potomne Pythona niekiedy dziedziczą uchwyty do plików otwieranych przez Pythona
Reprodukcja problemu
Kolejny krok: analiza wywołań CreateFile i CreateProcess:
● Otwarcie pliku i stworzenie procesu cmd.exe nastąpiło praktycznie w tym samym momencie
● Działo się tak za każdym razem, gdy wyciekał uchwyt
● Wyścig w implementacji CPythona? Wyścig w API Windowsa?
Reprodukcja problemu
● SCons do uruchamiania procesu kompilatora używa os.spawnve
● os.spawnve jest zaimplementowane przy użyciu _spawnve z Windows API
● _spawnve jest funkcją Microsoft Windows, inspirowaną funkcjami fork i exec
Reprodukcja problemu
● SCons do uruchamiania procesu kompilatora używa os.spawnve
● os.spawnve jest zaimplementowane przy użyciu _spawnve z Windows API
● _spawnve jest funkcją Microsoft Windows, inspirowaną funkcjami fork i exec
● Problem z implementacją _spawnve?
● Napisaliśmy fragment kodu odtwarzającego problem
○ Nieskończone otwieranie i zamykanie pliku w jednym wątku
○ Nieskończone uruchamianie cmd.exe w drugim wątku
Reprodukcja problemu
import os
from time import sleep
from threading import Thread
def threaded_function1():
while True:
with open("sample.txt", "w"):
pass
def threaded_function2():
while True:
os.spawnve(os.P_WAIT, 'C:Windowssystem32cmd.exe', ['cmd.exe', '/c'], os.environ)
thread1 = Thread(target = threaded_function1)
thread2 = Thread(target = threaded_function2)
thread1.start()
thread2.start()
Reprodukcja problemu
● ProcMon pokazał dziedziczenie wielu uchwytów do ‘sample.txt’ przez cmd.exe
● Nadal nie wiemy, czy jest to problem z implementacją CPythona czy ze _spawnve
Reprodukcja problemu
● ProcMon pokazał dziedziczenie wielu uchwytów do ‘sample.txt’ przez cmd.exe
● Nadal nie wiemy, czy jest to problem z implementacją CPythona czy ze _spawnve
● Szybka weryfikacja: Przepisaliśmy wywołanie os.spawnve na subprocess.call
○ subprocess.call używa funkcji CreateProcess
Reprodukcja problemu
● ProcMon pokazał dziedziczenie wielu uchwytów do ‘sample.txt’ przez cmd.exe
● Nadal nie wiemy, czy jest to problem z implementacją CPythona czy ze _spawnve
● Szybka weryfikacja: Przepisaliśmy wywołanie os.spawnve na subprocess.call
○ subprocess.call używa funkcji CreateProcess
● Błąd nadal występował
● Wniosek: Prawdopodobnie problem z implementacją CPythona
Rozwiązanie
● Uruchomiliśmy przykład weryfikacyjny przy użyciu aktualnej wersji Pythona 3
● Problem zniknął (zarówno dla subprocess.call jak i _spawnve)
● Zdecydowaliśmy się przepisać wszystkie skrypty budujące na Pythona 3
● Od tej pory problem nie występuje - budujemy kod w CI wielowątkowo
● Rozwiązaliśmy problem wyłącznie w naszym CI. Nadal nie znamy jego przyczyny
Rozwiązanie
● Użyliśmy wyszukiwania binarnego
● Instalowaliśmy oficjalne wydania
● Błąd nie występuje w wersji >=3.4
● Kompilowaliśmy pojedyncze commity
● Wymagane były starsze wersje Visual Studio…
Rozwiązanie
● Użyliśmy wyszukiwania binarnego
● Instalowaliśmy oficjalne wydania
● Błąd nie występuje w wersji >=3.4
● Kompilowaliśmy pojedyncze commity
● Wymagane były starsze wersje Visual Studio…
● ...które wymagają starszych wersji Windowsa
Rozwiązanie
Fragment kodu rozwiązujący problem (źródło):
#ifdef MS_WINDOWS
flags |= O_NOINHERIT;
#elif defined(O_CLOEXEC)
flags |= O_CLOEXEC;
#endif
...
self->fd = open(name, flags, 0666);
Rozwiązanie
Issue #18571: Implementation of the PEP 446: file descriptors and file handles
“This PEP proposes to make all file descriptors created by Python non-inheritable by default to reduce
the risk of these issues. This PEP fixes also a race condition in multi-threaded applications on operating
systems supporting atomic flags to create non-inheritable file descriptors.”
Źródło: PEP 446
Rozwiązanie
Wnioski
● Omijajcie Pythona 2 (w szczególności na Windowsie)
● Uważajcie na przypadkowe dziedziczenie (szczególnie w API POSIXowym)
● ProcMon loguje zamknięcie tylko ostatniego egzemplarza danego uchwytu
Pytania

Weitere ähnliche Inhalte

Ähnlich wie CodiLime Tech Talk - Michał Cłapiński, Mateusz Jabłoński: Debugging faultily inherited file handles on Microsoft Windows.

xD bug - Jak debugować PHP-owe aplikacje (Xdebug)
xD bug - Jak debugować PHP-owe aplikacje (Xdebug) xD bug - Jak debugować PHP-owe aplikacje (Xdebug)
xD bug - Jak debugować PHP-owe aplikacje (Xdebug) Laravel Poland MeetUp
 
Jak zacząć, aby nie żałować - czyli 50 twarzy PHP
Jak zacząć, aby nie żałować - czyli 50 twarzy PHPJak zacząć, aby nie żałować - czyli 50 twarzy PHP
Jak zacząć, aby nie żałować - czyli 50 twarzy PHPPiotr Horzycki
 
Castle Game Engine presentation at Zlot Programistów Delphi 2023 (Polish)
Castle Game Engine presentation at Zlot Programistów Delphi 2023 (Polish)Castle Game Engine presentation at Zlot Programistów Delphi 2023 (Polish)
Castle Game Engine presentation at Zlot Programistów Delphi 2023 (Polish)Michalis Kamburelis
 
Windows XP PL. Ćwiczenia zaawansowane
Windows XP PL. Ćwiczenia zaawansowaneWindows XP PL. Ćwiczenia zaawansowane
Windows XP PL. Ćwiczenia zaawansowaneWydawnictwo Helion
 
Skazani na firmware. Serwer na ARM64? Tak, to możliwe! S07E03
Skazani na firmware. Serwer na ARM64? Tak, to możliwe! S07E03Skazani na firmware. Serwer na ARM64? Tak, to możliwe! S07E03
Skazani na firmware. Serwer na ARM64? Tak, to możliwe! S07E03Semihalf
 
Exam: 70-511 Enhancing Usability - Windows Application
Exam: 70-511 Enhancing Usability - Windows ApplicationExam: 70-511 Enhancing Usability - Windows Application
Exam: 70-511 Enhancing Usability - Windows ApplicationMaciej Zbrzezny
 
Kopiąc Trufle - Odkrywanie tajemnic najmniej zrozumiałego elementu GraalVM
Kopiąc Trufle - Odkrywanie tajemnic najmniej zrozumiałego elementu GraalVMKopiąc Trufle - Odkrywanie tajemnic najmniej zrozumiałego elementu GraalVM
Kopiąc Trufle - Odkrywanie tajemnic najmniej zrozumiałego elementu GraalVMArtur Skowroński
 
Shall we play a game? PL version
Shall we play a game? PL versionShall we play a game? PL version
Shall we play a game? PL versionMaciej Lasyk
 
Hijackthis – ochrona przed błędami komputerowymi
Hijackthis – ochrona przed błędami komputerowymiHijackthis – ochrona przed błędami komputerowymi
Hijackthis – ochrona przed błędami komputerowymiDorota Ręba
 
Code driven development w Drupalu 7 | DrupalCamp Wrocław 2014
Code driven development w Drupalu 7 | DrupalCamp Wrocław 2014Code driven development w Drupalu 7 | DrupalCamp Wrocław 2014
Code driven development w Drupalu 7 | DrupalCamp Wrocław 2014Grzegorz Bartman
 
Codeception - jak zacząć pisać automatyczne testy do Drupala [PL]
Codeception - jak zacząć pisać automatyczne testy do Drupala [PL]Codeception - jak zacząć pisać automatyczne testy do Drupala [PL]
Codeception - jak zacząć pisać automatyczne testy do Drupala [PL]Droptica
 
Jak stworzyć udany system informatyczny
Jak stworzyć udany system informatycznyJak stworzyć udany system informatyczny
Jak stworzyć udany system informatycznyqbeuek
 
Patterns for organic architecture
Patterns for organic architecturePatterns for organic architecture
Patterns for organic architectureJaroslaw Palka
 
Poznaj GITa - Natalia Stanko
Poznaj GITa - Natalia StankoPoznaj GITa - Natalia Stanko
Poznaj GITa - Natalia StankoNatalia Stanko
 
Programowanie aplikacji dla Windows 8 (WinRT)
Programowanie aplikacji dla Windows 8 (WinRT)Programowanie aplikacji dla Windows 8 (WinRT)
Programowanie aplikacji dla Windows 8 (WinRT)Bartlomiej Zass
 

Ähnlich wie CodiLime Tech Talk - Michał Cłapiński, Mateusz Jabłoński: Debugging faultily inherited file handles on Microsoft Windows. (20)

Behat
BehatBehat
Behat
 
xD bug - Jak debugować PHP-owe aplikacje (Xdebug)
xD bug - Jak debugować PHP-owe aplikacje (Xdebug) xD bug - Jak debugować PHP-owe aplikacje (Xdebug)
xD bug - Jak debugować PHP-owe aplikacje (Xdebug)
 
Jak zacząć, aby nie żałować - czyli 50 twarzy PHP
Jak zacząć, aby nie żałować - czyli 50 twarzy PHPJak zacząć, aby nie żałować - czyli 50 twarzy PHP
Jak zacząć, aby nie żałować - czyli 50 twarzy PHP
 
LinuxKurs_Lekcja05
LinuxKurs_Lekcja05LinuxKurs_Lekcja05
LinuxKurs_Lekcja05
 
Castle Game Engine presentation at Zlot Programistów Delphi 2023 (Polish)
Castle Game Engine presentation at Zlot Programistów Delphi 2023 (Polish)Castle Game Engine presentation at Zlot Programistów Delphi 2023 (Polish)
Castle Game Engine presentation at Zlot Programistów Delphi 2023 (Polish)
 
Windows XP PL. Ćwiczenia zaawansowane
Windows XP PL. Ćwiczenia zaawansowaneWindows XP PL. Ćwiczenia zaawansowane
Windows XP PL. Ćwiczenia zaawansowane
 
Skazani na firmware. Serwer na ARM64? Tak, to możliwe! S07E03
Skazani na firmware. Serwer na ARM64? Tak, to możliwe! S07E03Skazani na firmware. Serwer na ARM64? Tak, to możliwe! S07E03
Skazani na firmware. Serwer na ARM64? Tak, to możliwe! S07E03
 
Exam: 70-511 Enhancing Usability - Windows Application
Exam: 70-511 Enhancing Usability - Windows ApplicationExam: 70-511 Enhancing Usability - Windows Application
Exam: 70-511 Enhancing Usability - Windows Application
 
Kopiąc Trufle - Odkrywanie tajemnic najmniej zrozumiałego elementu GraalVM
Kopiąc Trufle - Odkrywanie tajemnic najmniej zrozumiałego elementu GraalVMKopiąc Trufle - Odkrywanie tajemnic najmniej zrozumiałego elementu GraalVM
Kopiąc Trufle - Odkrywanie tajemnic najmniej zrozumiałego elementu GraalVM
 
Shall we play a game? PL version
Shall we play a game? PL versionShall we play a game? PL version
Shall we play a game? PL version
 
Hijackthis – ochrona przed błędami komputerowymi
Hijackthis – ochrona przed błędami komputerowymiHijackthis – ochrona przed błędami komputerowymi
Hijackthis – ochrona przed błędami komputerowymi
 
Code driven development w Drupalu 7 | DrupalCamp Wrocław 2014
Code driven development w Drupalu 7 | DrupalCamp Wrocław 2014Code driven development w Drupalu 7 | DrupalCamp Wrocław 2014
Code driven development w Drupalu 7 | DrupalCamp Wrocław 2014
 
Codeception - jak zacząć pisać automatyczne testy do Drupala [PL]
Codeception - jak zacząć pisać automatyczne testy do Drupala [PL]Codeception - jak zacząć pisać automatyczne testy do Drupala [PL]
Codeception - jak zacząć pisać automatyczne testy do Drupala [PL]
 
Jak stworzyć udany system informatyczny
Jak stworzyć udany system informatycznyJak stworzyć udany system informatyczny
Jak stworzyć udany system informatyczny
 
Patterns for organic architecture
Patterns for organic architecturePatterns for organic architecture
Patterns for organic architecture
 
Poznaj GITa - Natalia Stanko
Poznaj GITa - Natalia StankoPoznaj GITa - Natalia Stanko
Poznaj GITa - Natalia Stanko
 
Poznaj GITa - Natalia Stanko
Poznaj GITa - Natalia StankoPoznaj GITa - Natalia Stanko
Poznaj GITa - Natalia Stanko
 
Programowanie
ProgramowanieProgramowanie
Programowanie
 
Programowanie
ProgramowanieProgramowanie
Programowanie
 
Programowanie aplikacji dla Windows 8 (WinRT)
Programowanie aplikacji dla Windows 8 (WinRT)Programowanie aplikacji dla Windows 8 (WinRT)
Programowanie aplikacji dla Windows 8 (WinRT)
 

Mehr von CodiLime

CodiLime Tech Talk - Dawid Trzebiatowski i Wojciech Urbański: Opening the Flo...
CodiLime Tech Talk - Dawid Trzebiatowski i Wojciech Urbański: Opening the Flo...CodiLime Tech Talk - Dawid Trzebiatowski i Wojciech Urbański: Opening the Flo...
CodiLime Tech Talk - Dawid Trzebiatowski i Wojciech Urbański: Opening the Flo...CodiLime
 
Rapid help for current networking challenges
Rapid help for current networking challengesRapid help for current networking challenges
Rapid help for current networking challengesCodiLime
 
CodiLime Tech Talk - Grzegorz Rozdzialik: What the java script
CodiLime Tech Talk - Grzegorz Rozdzialik: What the java scriptCodiLime Tech Talk - Grzegorz Rozdzialik: What the java script
CodiLime Tech Talk - Grzegorz Rozdzialik: What the java scriptCodiLime
 
CodiLime Tech Talk - Mateusz Psujek: Keep calm and stay motivated
CodiLime Tech Talk - Mateusz Psujek: Keep calm and stay motivatedCodiLime Tech Talk - Mateusz Psujek: Keep calm and stay motivated
CodiLime Tech Talk - Mateusz Psujek: Keep calm and stay motivatedCodiLime
 
CodiLime Tech Talk - Katarzyna Ziomek-Zdanowicz: RxJS main concepts and real ...
CodiLime Tech Talk - Katarzyna Ziomek-Zdanowicz: RxJS main concepts and real ...CodiLime Tech Talk - Katarzyna Ziomek-Zdanowicz: RxJS main concepts and real ...
CodiLime Tech Talk - Katarzyna Ziomek-Zdanowicz: RxJS main concepts and real ...CodiLime
 
CodiLime Tech Talk - Wojciech Urbański: Cloud Native
CodiLime Tech Talk - Wojciech Urbański: Cloud NativeCodiLime Tech Talk - Wojciech Urbański: Cloud Native
CodiLime Tech Talk - Wojciech Urbański: Cloud NativeCodiLime
 
CodiLime Tech Talk - Łukasz Maksymczuk: Monitoring: Prometheus and Influx
CodiLime Tech Talk - Łukasz Maksymczuk: Monitoring: Prometheus and InfluxCodiLime Tech Talk - Łukasz Maksymczuk: Monitoring: Prometheus and Influx
CodiLime Tech Talk - Łukasz Maksymczuk: Monitoring: Prometheus and InfluxCodiLime
 
CodiLime Tech Talk - Michał Sochoń: Configuration management hell
CodiLime Tech Talk - Michał Sochoń: Configuration management hellCodiLime Tech Talk - Michał Sochoń: Configuration management hell
CodiLime Tech Talk - Michał Sochoń: Configuration management hellCodiLime
 
CodiLime Tech Talk - Adam Kułagowski: IPv6 - introduction
CodiLime Tech Talk - Adam Kułagowski: IPv6 - introductionCodiLime Tech Talk - Adam Kułagowski: IPv6 - introduction
CodiLime Tech Talk - Adam Kułagowski: IPv6 - introductionCodiLime
 
Tech Talk - Konrad Gawda : P4 programming language
Tech Talk - Konrad Gawda : P4 programming languageTech Talk - Konrad Gawda : P4 programming language
Tech Talk - Konrad Gawda : P4 programming languageCodiLime
 
CodiLime Tech Talk - Michał Pawluk: Our production deployment in AWS (HashiCo...
CodiLime Tech Talk - Michał Pawluk: Our production deployment in AWS (HashiCo...CodiLime Tech Talk - Michał Pawluk: Our production deployment in AWS (HashiCo...
CodiLime Tech Talk - Michał Pawluk: Our production deployment in AWS (HashiCo...CodiLime
 
CodiLime Tech Talk - Michał Pawluk: Our deployment of HashiCorp Vault
CodiLime Tech Talk - Michał Pawluk: Our deployment of HashiCorp VaultCodiLime Tech Talk - Michał Pawluk: Our deployment of HashiCorp Vault
CodiLime Tech Talk - Michał Pawluk: Our deployment of HashiCorp VaultCodiLime
 
CodiLime Tech Talk - Jan Kanty Milczek: Basic Recommender Systems – SVDon't
CodiLime Tech Talk - Jan Kanty Milczek: Basic Recommender Systems – SVDon'tCodiLime Tech Talk - Jan Kanty Milczek: Basic Recommender Systems – SVDon't
CodiLime Tech Talk - Jan Kanty Milczek: Basic Recommender Systems – SVDon'tCodiLime
 
CodiLime Tech Talk - Michał Sochoń: Sphinx, reST & Ansible
CodiLime Tech Talk - Michał Sochoń: Sphinx, reST & AnsibleCodiLime Tech Talk - Michał Sochoń: Sphinx, reST & Ansible
CodiLime Tech Talk - Michał Sochoń: Sphinx, reST & AnsibleCodiLime
 
CodiLime Tech Talk - Maciej Sawicki: Streamline application deployments with ...
CodiLime Tech Talk - Maciej Sawicki: Streamline application deployments with ...CodiLime Tech Talk - Maciej Sawicki: Streamline application deployments with ...
CodiLime Tech Talk - Maciej Sawicki: Streamline application deployments with ...CodiLime
 
CodiLime Tech Talk - Jarek Łukow: You need a cloud to test a cloud: using Ope...
CodiLime Tech Talk - Jarek Łukow: You need a cloud to test a cloud: using Ope...CodiLime Tech Talk - Jarek Łukow: You need a cloud to test a cloud: using Ope...
CodiLime Tech Talk - Jarek Łukow: You need a cloud to test a cloud: using Ope...CodiLime
 

Mehr von CodiLime (16)

CodiLime Tech Talk - Dawid Trzebiatowski i Wojciech Urbański: Opening the Flo...
CodiLime Tech Talk - Dawid Trzebiatowski i Wojciech Urbański: Opening the Flo...CodiLime Tech Talk - Dawid Trzebiatowski i Wojciech Urbański: Opening the Flo...
CodiLime Tech Talk - Dawid Trzebiatowski i Wojciech Urbański: Opening the Flo...
 
Rapid help for current networking challenges
Rapid help for current networking challengesRapid help for current networking challenges
Rapid help for current networking challenges
 
CodiLime Tech Talk - Grzegorz Rozdzialik: What the java script
CodiLime Tech Talk - Grzegorz Rozdzialik: What the java scriptCodiLime Tech Talk - Grzegorz Rozdzialik: What the java script
CodiLime Tech Talk - Grzegorz Rozdzialik: What the java script
 
CodiLime Tech Talk - Mateusz Psujek: Keep calm and stay motivated
CodiLime Tech Talk - Mateusz Psujek: Keep calm and stay motivatedCodiLime Tech Talk - Mateusz Psujek: Keep calm and stay motivated
CodiLime Tech Talk - Mateusz Psujek: Keep calm and stay motivated
 
CodiLime Tech Talk - Katarzyna Ziomek-Zdanowicz: RxJS main concepts and real ...
CodiLime Tech Talk - Katarzyna Ziomek-Zdanowicz: RxJS main concepts and real ...CodiLime Tech Talk - Katarzyna Ziomek-Zdanowicz: RxJS main concepts and real ...
CodiLime Tech Talk - Katarzyna Ziomek-Zdanowicz: RxJS main concepts and real ...
 
CodiLime Tech Talk - Wojciech Urbański: Cloud Native
CodiLime Tech Talk - Wojciech Urbański: Cloud NativeCodiLime Tech Talk - Wojciech Urbański: Cloud Native
CodiLime Tech Talk - Wojciech Urbański: Cloud Native
 
CodiLime Tech Talk - Łukasz Maksymczuk: Monitoring: Prometheus and Influx
CodiLime Tech Talk - Łukasz Maksymczuk: Monitoring: Prometheus and InfluxCodiLime Tech Talk - Łukasz Maksymczuk: Monitoring: Prometheus and Influx
CodiLime Tech Talk - Łukasz Maksymczuk: Monitoring: Prometheus and Influx
 
CodiLime Tech Talk - Michał Sochoń: Configuration management hell
CodiLime Tech Talk - Michał Sochoń: Configuration management hellCodiLime Tech Talk - Michał Sochoń: Configuration management hell
CodiLime Tech Talk - Michał Sochoń: Configuration management hell
 
CodiLime Tech Talk - Adam Kułagowski: IPv6 - introduction
CodiLime Tech Talk - Adam Kułagowski: IPv6 - introductionCodiLime Tech Talk - Adam Kułagowski: IPv6 - introduction
CodiLime Tech Talk - Adam Kułagowski: IPv6 - introduction
 
Tech Talk - Konrad Gawda : P4 programming language
Tech Talk - Konrad Gawda : P4 programming languageTech Talk - Konrad Gawda : P4 programming language
Tech Talk - Konrad Gawda : P4 programming language
 
CodiLime Tech Talk - Michał Pawluk: Our production deployment in AWS (HashiCo...
CodiLime Tech Talk - Michał Pawluk: Our production deployment in AWS (HashiCo...CodiLime Tech Talk - Michał Pawluk: Our production deployment in AWS (HashiCo...
CodiLime Tech Talk - Michał Pawluk: Our production deployment in AWS (HashiCo...
 
CodiLime Tech Talk - Michał Pawluk: Our deployment of HashiCorp Vault
CodiLime Tech Talk - Michał Pawluk: Our deployment of HashiCorp VaultCodiLime Tech Talk - Michał Pawluk: Our deployment of HashiCorp Vault
CodiLime Tech Talk - Michał Pawluk: Our deployment of HashiCorp Vault
 
CodiLime Tech Talk - Jan Kanty Milczek: Basic Recommender Systems – SVDon't
CodiLime Tech Talk - Jan Kanty Milczek: Basic Recommender Systems – SVDon'tCodiLime Tech Talk - Jan Kanty Milczek: Basic Recommender Systems – SVDon't
CodiLime Tech Talk - Jan Kanty Milczek: Basic Recommender Systems – SVDon't
 
CodiLime Tech Talk - Michał Sochoń: Sphinx, reST & Ansible
CodiLime Tech Talk - Michał Sochoń: Sphinx, reST & AnsibleCodiLime Tech Talk - Michał Sochoń: Sphinx, reST & Ansible
CodiLime Tech Talk - Michał Sochoń: Sphinx, reST & Ansible
 
CodiLime Tech Talk - Maciej Sawicki: Streamline application deployments with ...
CodiLime Tech Talk - Maciej Sawicki: Streamline application deployments with ...CodiLime Tech Talk - Maciej Sawicki: Streamline application deployments with ...
CodiLime Tech Talk - Maciej Sawicki: Streamline application deployments with ...
 
CodiLime Tech Talk - Jarek Łukow: You need a cloud to test a cloud: using Ope...
CodiLime Tech Talk - Jarek Łukow: You need a cloud to test a cloud: using Ope...CodiLime Tech Talk - Jarek Łukow: You need a cloud to test a cloud: using Ope...
CodiLime Tech Talk - Jarek Łukow: You need a cloud to test a cloud: using Ope...
 

CodiLime Tech Talk - Michał Cłapiński, Mateusz Jabłoński: Debugging faultily inherited file handles on Microsoft Windows.

  • 1. Tonący Python 2 pliku się chwyta, czyli jak debugowaliśmy wyciekające uchwyty pod Windowsem. Michał Cłapiński, Mateusz Jabłoński 23.05.2018
  • 2. Czym się zajmujemy? ● Michał Cłapiński ○ michal.clapinski@codilime.com ● Mateusz Jabłoński ○ mateusz.jablonski@codilime.com ● Pracujemy w CodiLime ● Portujemy Tungsten Fabric (dawniej OpenContrail) na Windowsa ● Przygotowujemy system continuous integration w celu testowania naszego kodu
  • 3. Krótko o Tungsten Fabric ● Multicloud multistack SDN ● SDN = Software-defined networking ● Projekt Linux Foundation ● Budowany za pomocą SConsów
  • 4. Napotkany problem ● Włączyliśmy budowanie wielowątkowe w naszym CI ○ Znaczne przyśpieszenie budowania (20 minut / 2 godziny) ○ Zysk w CI i na maszynach programistów
  • 5. Napotkany problem ● Włączyliśmy budowanie wielowątkowe w naszym CI ○ Znaczne przyśpieszenie budowania (20 minut / 2 godziny) ○ Zysk w CI i na maszynach programistów ● Zaczęły pojawiać się nieprzewidywalne błędy ○ ~5% buildów zakończonych niepowodzeniem ○ Błędy na różnym etapie budowania ○ Jeżeli jeden build kompiluje 1000 plików to błąd występował raz na 20000 plików ○ Za każdym razem błąd dotyczył pliku tworzonego przez Pythona
  • 6. Napotkany problem - przykład css_DT_bootstrap_css.cpp(4): error C2065: 'DT_bootstrap_css': undeclared identifier css_DT_bootstrap_css.cpp(4): error C2065: 'DT_bootstrap_css_len': undeclared identifier scons: *** [sandesh_http.obj] Error 2 scons: building terminated because of errors. ● Plik css_DT_bootstrap_css.cpp jest jest generowany przez SConscript i później kompilowany ● Plik został źle wygenerowany?
  • 7. Napotkany problem - przykład Kod generujący plik css_DT_bootstrap_css.cpp: 1. with open(cname, 'w') as cfile: 2. cfile.write('namespace {n') 3. 4. subprocess.call('xxd -i ' + hname + ' >> ' + os.path.basename(cname), shell=True, cwd=opath) 5. 6. with open(cname, 'a') as cfile: 7. cfile.write('}n') 8. cfile.write(tail_content) ● Brakujący kod jest generowany w linii 4 ● Dodanie sprawdzania rezultatu subprocess.call pozwala wychwycić błąd wcześniej ● Nadal nie wiemy jaka jest przyczyna
  • 8. Napotkany problem - przykład c1xx: fatal error C1083: Cannot open source file: 'multicast_html.cpp': No such file or directory scons: *** [multicast_html.obj] Error 2 ● multicast_html.cpp jest generowany podobnie do css_DT_bootstrap_css.cpp ● Dlaczego dostajemy błąd ‘No such file or directory’? ● Plik istnieje. Czy został stworzony po próbie jego kompilacji?
  • 9. Napotkany problem - przykład Hipoteza: Czy to oznacza błąd w SCons?
  • 10. Podejście pierwsze: ProcMon ● Process Monitor: narzędzie do monitorowania aktywności systemu plików, rejestru i wątków ● Umożliwia filtrowanie na podstawie różnych kryteriów ○ Nazwa procesu, PID, ścieżka do używanego pliku, wynik i inne ● Chcemy zobaczyć jakie procesy używały jakich plików i kiedy miało to miejsce ● Wielkość logów uniemożliwia budowanie do czasu napotkania błędu z raz włączonym ProcMonem ● Rzadko spotykany błąd, więc ręczny restart ProcMona i budowania trwałby wiele godzin
  • 11. Podejście pierwsze: ProcMon Automatyzacja za pomocą skryptu PowerShell: $ErrorActionPreference = "SilentlyContinue" Do { [remove build artifacts] .internalsprocmon /Quiet /BackingFile proclogslog.pml .internalsprocmon /WaitForIdle scons -j 8 contrail-vrouter-agent $code = $LastExitCode .internalsprocmon /Terminate While (Get-Process procmon) { Start-Sleep 1 } } While ($code -eq 0)
  • 13. Podejście pierwsze: ProcMon ● cmd.exe otwiera plik, do którego pisze xxd z powodu użycia przekierowania powłoki (‘>>’) ● Python otworzył plik 3 razy, zamknął 2 razy ● cl.exe próbuje otworzyć plik, do którego uchwyt jest nadal używany przez Pythona ● ‘No such file or directory’, które widzieliśmy w logach to w rzeczywistości SHARING VIOLATION ● Brakujące CloseFile jest wywoływane przez cmd.exe?
  • 14. Krótko o trybach współdzielenia HANDLE WINAPI CreateFile(..., _In_ DWORD dwDesiredAccess, _In_ DWORD dwShareMode, ...); ● dwDesiredAccess: ○ GENERIC_READ, GENERIC_WRITE, GENERIC_READ | GENERIC_WRITE (i inne) ● dwShareMode: ○ 0, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE i kombinacje ● ERROR_SHARING_VIOLATION w przypadku niekompatybilnych trybów ● dwShareMode musi brać pod uwagę wcześniejsze wywołanie CreateFile
  • 15. Krótko o trybach współdzielenia Pierwsze wywołanie CreateFile Poprawne kolejne wywołania GENERIC_READ, 0 --- GENERIC_READ, FILE_SHARE_READ ● GENERIC_READ, FILE_SHARE_READ ● GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE GENERIC_WRITE, FILE_SHARE_READ ● GENERIC_READ, FILE_SHARE_WRITE ● GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE ● GENERIC_READ, FILE_SHARE_WRITE ● GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE ● GENERIC_WRITE, FILE_SHARE_WRITE ● GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE ● GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE ● GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE
  • 16. Krótko o trybach współdzielenia ● Python otworzył plik z GENERIC_WRITE | READ_CONTROL ● cl.exe próbuje otworzyć go z FILE_SHARE_READ ● Wywołanie kończy się niepowodzeniem (ERROR_SHARING_VIOLATION) ● Wniosek: Uchwyt na plik nie został zamknięty
  • 17. Podejście drugie: Frida ● Potrzebowaliśmy potężniejszego, skryptowalnego sposobu monitorowania ● Frida: narzędzie do dynamicznej instrumentacji ● Pozwala wstrzykiwać własny kod do dowolnych procesów ○ Wstrzykuje silnik JavaScriptu V8, pozwala uruchamiać własny kod JS ● Umożliwia przechwytywanie wywołań dowolnych funkcji ○ Logowanie wywołań ○ Modyfikacja argumentów ○ Modyfikacja wartości zwracanej
  • 18. Podejście drugie: Frida Minimalny przykład użycia Fridy (monitorowanie otwierania plików): import frida import sys session = frida.attach("myProgram.exe") script = session.create_script(""" Interceptor.attach(Module.findExportByName(null, "CreateFileA"), { onLeave: function(handle) { send("CreateFile " + String(handle.toInt32())) } }); """) def on_message(message, data): print(message['payload']) script.on('message', on_message) script.load() sys.stdin.read()
  • 19. Podejście drugie: Frida var getPathAAddress = Module.findExportByName(null, "GetFinalPathNameByHandleA") var getPathA = new NativeFunction(getPathAAddress, 'uint32', ['pointer', 'pointer', 'uint32', 'uint32']) [...] var handlePath = Memory.alloc(2001) getPathA(handle, handlePath, 2000, 0) send("CreateFile " + String(handle.toInt32()) + " " + String(Memory.readUtf8String(handlePath)))
  • 20. Podejście drugie: Frida ● Wstrzyknęliśmy kod JS do procesu Pythona. Monitorowaliśmy CreateFile i CloseHandle CreateFile 14468 16:2:46.429 1704 [...]multicast_html.cpp [...] CloseHandle 14468 16:2:46.432 1704 [...]multicast_html.cpp CloseHandle: 1 ● Python w każdym przypadku wywoływał poprawnie CloseHandle ● Dlaczego cmd.exe zamyka plik, którego nie otwiera? ● Dlaczego ProcMon nie pokazuje wywołania CloseHandle?
  • 21. Podejście drugie: Frida auto f = _open("costam.txt", 0); char *argv[] = { "cmd.exe", "/c", "pwd && ping 127.0.0.1 -n 5", NULL }; _spawnve(_P_NOWAIT, "C:WindowsSystem32cmd.exe", argv, NULL); _close(f);
  • 22. Podejście drugie: Frida auto f = _open("costam.txt", 0); char *argv[] = { "cmd.exe", "/c", "pwd && ping 127.0.0.1 -n 5", NULL }; _spawnve(_P_NOWAIT, "C:WindowsSystem32cmd.exe", argv, NULL); _close(f); ● ProcMon pokazuje jedynie CloseHandle na ostatnim egzemplarzu danego uchwytu (zniszczenie obiektu w kernelu) ● Wniosek: procesy potomne Pythona niekiedy dziedziczą uchwyty do plików otwieranych przez Pythona
  • 23. Reprodukcja problemu Kolejny krok: analiza wywołań CreateFile i CreateProcess: ● Otwarcie pliku i stworzenie procesu cmd.exe nastąpiło praktycznie w tym samym momencie ● Działo się tak za każdym razem, gdy wyciekał uchwyt ● Wyścig w implementacji CPythona? Wyścig w API Windowsa?
  • 24. Reprodukcja problemu ● SCons do uruchamiania procesu kompilatora używa os.spawnve ● os.spawnve jest zaimplementowane przy użyciu _spawnve z Windows API ● _spawnve jest funkcją Microsoft Windows, inspirowaną funkcjami fork i exec
  • 25. Reprodukcja problemu ● SCons do uruchamiania procesu kompilatora używa os.spawnve ● os.spawnve jest zaimplementowane przy użyciu _spawnve z Windows API ● _spawnve jest funkcją Microsoft Windows, inspirowaną funkcjami fork i exec ● Problem z implementacją _spawnve? ● Napisaliśmy fragment kodu odtwarzającego problem ○ Nieskończone otwieranie i zamykanie pliku w jednym wątku ○ Nieskończone uruchamianie cmd.exe w drugim wątku
  • 26. Reprodukcja problemu import os from time import sleep from threading import Thread def threaded_function1(): while True: with open("sample.txt", "w"): pass def threaded_function2(): while True: os.spawnve(os.P_WAIT, 'C:Windowssystem32cmd.exe', ['cmd.exe', '/c'], os.environ) thread1 = Thread(target = threaded_function1) thread2 = Thread(target = threaded_function2) thread1.start() thread2.start()
  • 27. Reprodukcja problemu ● ProcMon pokazał dziedziczenie wielu uchwytów do ‘sample.txt’ przez cmd.exe ● Nadal nie wiemy, czy jest to problem z implementacją CPythona czy ze _spawnve
  • 28. Reprodukcja problemu ● ProcMon pokazał dziedziczenie wielu uchwytów do ‘sample.txt’ przez cmd.exe ● Nadal nie wiemy, czy jest to problem z implementacją CPythona czy ze _spawnve ● Szybka weryfikacja: Przepisaliśmy wywołanie os.spawnve na subprocess.call ○ subprocess.call używa funkcji CreateProcess
  • 29. Reprodukcja problemu ● ProcMon pokazał dziedziczenie wielu uchwytów do ‘sample.txt’ przez cmd.exe ● Nadal nie wiemy, czy jest to problem z implementacją CPythona czy ze _spawnve ● Szybka weryfikacja: Przepisaliśmy wywołanie os.spawnve na subprocess.call ○ subprocess.call używa funkcji CreateProcess ● Błąd nadal występował ● Wniosek: Prawdopodobnie problem z implementacją CPythona
  • 30. Rozwiązanie ● Uruchomiliśmy przykład weryfikacyjny przy użyciu aktualnej wersji Pythona 3 ● Problem zniknął (zarówno dla subprocess.call jak i _spawnve) ● Zdecydowaliśmy się przepisać wszystkie skrypty budujące na Pythona 3 ● Od tej pory problem nie występuje - budujemy kod w CI wielowątkowo ● Rozwiązaliśmy problem wyłącznie w naszym CI. Nadal nie znamy jego przyczyny
  • 31. Rozwiązanie ● Użyliśmy wyszukiwania binarnego ● Instalowaliśmy oficjalne wydania ● Błąd nie występuje w wersji >=3.4 ● Kompilowaliśmy pojedyncze commity ● Wymagane były starsze wersje Visual Studio…
  • 32. Rozwiązanie ● Użyliśmy wyszukiwania binarnego ● Instalowaliśmy oficjalne wydania ● Błąd nie występuje w wersji >=3.4 ● Kompilowaliśmy pojedyncze commity ● Wymagane były starsze wersje Visual Studio… ● ...które wymagają starszych wersji Windowsa
  • 33. Rozwiązanie Fragment kodu rozwiązujący problem (źródło): #ifdef MS_WINDOWS flags |= O_NOINHERIT; #elif defined(O_CLOEXEC) flags |= O_CLOEXEC; #endif ... self->fd = open(name, flags, 0666);
  • 34. Rozwiązanie Issue #18571: Implementation of the PEP 446: file descriptors and file handles “This PEP proposes to make all file descriptors created by Python non-inheritable by default to reduce the risk of these issues. This PEP fixes also a race condition in multi-threaded applications on operating systems supporting atomic flags to create non-inheritable file descriptors.” Źródło: PEP 446
  • 36. Wnioski ● Omijajcie Pythona 2 (w szczególności na Windowsie) ● Uważajcie na przypadkowe dziedziczenie (szczególnie w API POSIXowym) ● ProcMon loguje zamknięcie tylko ostatniego egzemplarza danego uchwytu