Największa tajemnica CROOK-a: Różnice pomiędzy wersjami

Przejdź do nawigacji Przejdź do wyszukiwania
brak opisu edycji
(Utworzono nową stronę "__NOTOC__ thumb|Błędy w działaniu kompilatora C Na przestrzeni ostatnich niemal trzech lat, które minęły od pierwszego uruchomienia CROOK...")
 
Nie podano opisu zmian
 
(Nie pokazano 5 pośrednich wersji utworzonych przez tego samego użytkownika)
Linia 1: Linia 1:
__NOTOC__
__NOTOC__
[[File:Cc0_bledy.png|thumb|Błędy w działaniu kompilatora C]]
[[File:Cc0_bledy.png|thumb|Błędy w działaniu kompilatora C]]
 
Na przestrzeni niemal trzech lat, które minęły od pierwszego uruchomienia [[CROOK|CROOK-5]] w emulatorze MERY-400, system operacyjny z Politechniki Gdańskiej stawiał wiele zagadek. Większość z nich wynikała z niedostatków w emulacji MERY-400. System czasami odmawiał współpracy, a jego narzędzia nie zawsze spełniały swoje funkcje. Sytuacja poprawiła się znacznie, gdy po kolejnych poprawkach emulator przeszedł wreszcie poprawnie testy procesora i arytmometru dostarczane przez producenta MERY-400. A kiedy udokumentowane zostały i zaimplementowane wszystkie [[Modyfikacje sprzętowe procesora|przeróbki procesora]], CROOK i narzędzia systemowe przestały sprawiać jakiekolwiek problemy.
Na przestrzeni ostatnich niemal trzech lat, które minęły od pierwszego uruchomienia [[CROOK|CROOK-5]] w emulatorze [[EM400]], system operacyjny z Politechniki Gdańskiej stawiał wiele zagadek. Większość z nich wynikała z niedostatków w emulacji MERY-400. System czasami odmawiał współpracy, a jego narzędzia nie zawsze spełniały swoje funkcje. Sytuacja poprawiła się znacznie, gdy po kolejnych poprawkach emulator przeszedł wreszcie poprawnie testy procesora i arytmometru dostarczane przez producenta MERY-400. A kiedy udokumentowane zostały i zaimplementowane wszystkie [[Modyfikacje sprzętowe procesora|przeróbki procesora]], CROOK i narzędzia systemowe przestały sprawiać jakiekolwiek problemy.


Niestety nie można było tego samego powiedzieć o programach użytkowych. Niektóre z nich od czasu do czasu kończyły się błędami, inne niby działały, ale konsekwentnie odmawiały spełniania jakiejś wybranej funkcji. Spektrum problemów było szerokie, a częstotliwość i okoliczności ich występowania wydawały się być przypadkowe. Dotyczyły one jednak zawsze  grupy tych samych binariów.
Niestety nie można było tego samego powiedzieć o programach użytkowych. Niektóre z nich od czasu do czasu kończyły się błędami, inne niby działały, ale konsekwentnie odmawiały spełniania jakiejś wybranej funkcji. Spektrum problemów było szerokie, a częstotliwość i okoliczności ich występowania wydawały się być przypadkowe. Dotyczyły one jednak zawsze  grupy tych samych binariów.


Szczególnie problematyczny okazał się kompilator C. Tylko jedna na kilka- kilkanaście kompilacji przebiegała poprawnie. Pozostałe kończyły się smutnym "error in program", tragicznym "WRONG INSTRUCTION", dziwnymi "ERR BRAK O^PERATORA" lub "ERR BR^AK ZMIENNEJ", czy wreszcie zupełnym zapętleniem się programu.
Szczególnie problematyczny okazał się kompilator C. Tylko jedna na kilkanaście kompilacji przebiegała poprawnie. Pozostałe kończyły się smutnym "error in program", tragicznym "WRONG INSTRUCTION", dziwnymi "ERR BRAK O^PERATORA" lub "ERR BR^AK ZMIENNEJ", czy wreszcie zupełnym zapętleniem się programu.


Powtarzalność problemów była w tym przypadku na tyle wysoka, że kompilator C stał się idealnym kandydatem do podjęcia próby rozwiązania tej ostatniej zagadki CROOK-a.  
Powtarzalność problemów była w tym przypadku na tyle wysoka, że kompilator C stał się idealnym kandydatem do podjęcia próby rozwiązania tej ostatniej zagadki CROOK-a.  
Linia 12: Linia 11:
= Winny CROOK? =
= Winny CROOK? =


Gdzie więc przyczyna?
Gdzie więc przyczyna? Za każdym razem, kiedy pod kontrolą systemu CROOK w emulatorze dzieje się coś dziwnego, jedna hipoteza powraca jak mantra: czy z tą wersją systemu aby na pewno działały poprawnie wszystkie programy?
 
Za każdym razem, kiedy pod kontrolą systemu CROOK w emulatorze dzieje się coś dziwnego, jedna hipoteza powraca jak mantra: czy z tą wersją systemu aby na pewno działały poprawnie wszystkie programy?


Obraz dysku z systemem uruchamianym w EM400 pochodzi z maszyny, która do końca swoich dni była sprawna i wykorzystywana przez autorów CROOK-a do jego rozwoju. Nowe funkcjonalności były dodawane, inne usprawniane. Czy na pewno zachowana była ciągła kompatybilność? Niepokój jest tym bardziej uzasadniony, że dziwną plagą dotknięte były głównie nowsze binaria. Budowane narzędziami, które nie istniały dla starszych wersji systemu, na przykład konsolidatorem LINK stworzonym w Instytucie Informatyki Uniwersytetu Warszawskiego.
Obraz dysku z systemem uruchamianym w EM400 pochodzi z maszyny, która do końca swoich dni była sprawna i wykorzystywana przez autorów CROOK-a do jego rozwoju. Nowe funkcjonalności były dodawane, inne usprawniane. Czy na pewno zachowana była ciągła kompatybilność? Niepokój jest tym bardziej uzasadniony, że dziwną plagą dotknięte były głównie nowsze binaria. Budowane narzędziami, które nie istniały dla starszych wersji systemu, na przykład konsolidatorem LINK stworzonym w Instytucie Informatyki Uniwersytetu Warszawskiego.
Linia 26: Linia 23:
EM400 przygotowany jest na badanie takich sytuacji i pozwala z dużą dokładnością śledzić wszystko to, co dzieje się w emulowanym systemie. Może zapisywać do pliku log zawierający nie tylko każdy krok procesora, ale również wywołania systemowe, operacje I/O, obsługę przerwań, etc. Choć taki log ma nieraz kilkaset megabajtów i jego analiza bywa żmudna, to jest to niemal pewna droga do sukcesu.  
EM400 przygotowany jest na badanie takich sytuacji i pozwala z dużą dokładnością śledzić wszystko to, co dzieje się w emulowanym systemie. Może zapisywać do pliku log zawierający nie tylko każdy krok procesora, ale również wywołania systemowe, operacje I/O, obsługę przerwań, etc. Choć taki log ma nieraz kilkaset megabajtów i jego analiza bywa żmudna, to jest to niemal pewna droga do sukcesu.  


Zacznijmy więc od takiego właśnie „podglądnięcia” działań kompilatora C. Już kilka minut analizy zapisów w logu pod kątem najbardziej prawdopodobnych anomalii prowadzi do następującego fragmentu:
Zacznijmy więc od takiego właśnie podejrzenia działań kompilatora C. Już kilka minut analizy zapisów w logu pod kątem najbardziej prawdopodobnych anomalii prowadzi do następującego fragmentu, wskazującego bezpośrednią przyczynę problemu:


  CPU 2 | USR  2:0xf1e9 CC0    |    AWT r7, -1          T = -1
  CPU 2 | USR  2:0xf1e9 CC0    |    AWT r7, -1          T = -1
Linia 33: Linia 30:
  CPU 2 | USR  2:0xf1eb CC0    |    (ineffective: illegal instruction) opcode: 000 000 0 000 100 010 (0x0022)
  CPU 2 | USR  2:0xf1eb CC0    |    (ineffective: illegal instruction) opcode: 000 000 0 000 100 010 (0x0022)


Mówi on, że w trakcie wykonywania procesu w przestrzeni użytkownika, procesor trafił na nielegalną instrukcję o kodzie '''0x22'''. Dziwne, bo deasemblacja zbioru z programem pokazuje, że powinna w tym miejscu być zupełnie legalna instrukcja:
Mówi on, że w trakcie wykonywania procesu w przestrzeni użytkownika, pod adresem '''0xf1eb''' w bloku pamięci nr '''2''', procesor trafił na nielegalną instrukcję o kodzie '''0x22'''. Dziwne, bo deasemblacja zbioru z programem pokazuje, że powinna w tym miejscu być zupełnie legalna instrukcja:


  0xf1e9:            AWT  r7, -1
  0xf1e9:            AWT  r7, -1
Linia 41: Linia 38:
Podejrzenie o błąd w emulatorze nabiera sensu. Możliwe wyjaśnienia takiej sytuacji dzielą się zasadniczo na dwie grupy:
Podejrzenie o błąd w emulatorze nabiera sensu. Możliwe wyjaśnienia takiej sytuacji dzielą się zasadniczo na dwie grupy:


# Emulator dokonuje niepoprawnego odczytu pamięci, mimo że w komórce znajduje się poprawna wartość (przyczyną może być na przykład niespójny stan pamięci między wątkami, czy błąd w emulacji adresowania).
# Emulator dokonuje niepoprawnego odczytu pamięci, mimo że w komórce znajduje się poprawna wartość (przyczyną może być na przykład niespójny stan pamięci między wątkami emulacji, czy błąd w emulacji adresowania).
# Wartość '''0x22''' została do komórki faktycznie zapisana (np. z powodu błędu w adresowaniu, czy błędu po stronie urządzenia I/O, piszącego do pamięci), a odczyt jest poprawny.
# Wartość '''0x22''' została do komórki faktycznie zapisana (np. z powodu błędu w adresowaniu, czy błędu po stronie urządzenia I/O, piszącego do pamięci), a odczyt jest poprawny.


Większość potencjalnych błędów z pierwszej grupy została szybko wyeliminowana testami, w które nie ma się co tutaj zagłębiać. Pozostaje w zasadzie tylko druga, znacznie bardziej prawdopodobna możliwość: coś nadpisuje zawartość nieszczęsnej komórki '''0xf1eb''' w segmencie pamięci kompilatora. Po dodaniu śledzenia zapisów pod ten adres okazuje się jednak, że oprócz wstępnego ładowania obrazu procesu, poszukiwany zapis nie jest w ogóle wykonywany! Przy kolejnych uruchomieniach kompilatora komórka '''0xf1eb''' zawiera poprawny rozkaz, a kompilator jak nie działał, tak nie działa.
Większość potencjalnych błędów z pierwszej grupy można szybko wyeliminować testami, w które nie ma się co tutaj zagłębiać. Pozostaje w zasadzie tylko druga, znacznie bardziej prawdopodobna możliwość: coś nadpisuje zawartość nieszczęsnej komórki '''0xf1eb''' w segmencie pamięci kompilatora. Po dodaniu śledzenia zapisów pod ten adres okazuje się jednak, że oprócz wstępnego ładowania obrazu procesu, poszukiwany zapis nie jest w ogóle wykonywany! Przy kolejnych uruchomieniach kompilatora komórka '''0xf1eb''' zawiera poprawny rozkaz, a kompilator jak nie działał, tak nie działa.


Spróbujmy w takim razie zebrać większą próbkę wystąpień pierwotnie zauważonego problemu. Krok wstecz i kolejne logi z różnych wywołań '''CC0''' pokazują, że nielegalne instrukcje owszem, wciąż pojawiają się w kodzie kompilatora, ale pod różnymi, losowo wyglądającymi (sic!) adresami. W dodatku niemal zawsze mają ten sam kod: '''0x22'''.
Spróbujmy w takim razie zebrać większą próbkę wystąpień pierwotnie zauważonego problemu. Krok wstecz i kolejne logi z różnych wywołań '''CC0''' pokazują, że nielegalne instrukcje owszem, wciąż pojawiają się w kodzie kompilatora, ale pod różnymi, losowo wyglądającymi (sic!) adresami. W dodatku niemal zawsze mają ten sam kod: '''0x22'''.
Linia 52: Linia 49:
  CPU 2 |  OS  2:0x137a CC0    |    PW r6, r1            N = 0xf1eb = -3605
  CPU 2 |  OS  2:0x137a CC0    |    PW r6, r1            N = 0xf1eb = -3605


Ta jedna, niewinnie wyglądająca linia, jest niczym obuch celnie wymierzony w czerep autora niniejszego tekstu. Znaczy bowiem, że pamięć procesu niszczona jest instrukcją pochodzącą wprost z jądra systemu CROOK-5.
Ta jedna, niewinnie wyglądająca linia, jest niczym obuch celnie wymierzony w czerep autora niniejszego tekstu. Znaczy bowiem, że pamięć procesu niszczona jest przez jądro systemu CROOK-5.


= Jednak CROOK =
= Jednak CROOK =
Linia 85: Linia 82:
= Błąd? =
= Błąd? =


Dobrze zatem. Skoro mamy jedną linię asemblera, z którą związane są aż trzy wyjątkowo dziwne obserwacje, to załóżmy przez chwilę, że faktycznie mamy do czynienia z błędem. Spróbujmy uruchomić CROOK-a z małą poprawką: Zablokujmy podejrzany zapis przez proste zastąpienie instrukcji pod adresem '''0x137a''' instrukcją '''NOP'''. Rezultat? W tak „naprawionym” CROOK-u zarówno kompilator, jak i inne nie działające wcześniej programy funkcjonują poprawnie.
Dobrze zatem. Skoro mamy jedną linię asemblera, z którą związane są aż trzy wyjątkowo dziwne obserwacje, to załóżmy przez chwilę, że faktycznie mamy do czynienia z błędem. Spróbujmy uruchomić CROOK-a z małą poprawką: Zablokujmy podejrzany zapis przez proste zastąpienie instrukcji pod adresem '''0x137a''' instrukcją pustą ('''NOP'''). Rezultat? W tak „naprawionym” CROOK-u zarówno kompilator, jak i inne nie działające wcześniej programy funkcjonują poprawnie.
 
Za każdym razem.


Bez najmniejszych efektów ubocznych.
Za każdym razem. Bez najmniejszych efektów ubocznych.


Ale zanim ktoś zdąży zakrzyknąć: „Czyli ewidentny błąd w CROOK-u!”, to napiszę o kolejnej obserwacji: Procedura '''BLOK''' występuje we ''wszystkich'' zachowanych wersjach jądra systemu, w postaci nie zmienionej ''ani o jotę''. Wygląda na to, że nie jest to jakaś przypadkowa linia, która wkradła się z jedną, nieudaną edycją, a zamierzone, utrzymywane z wersji na wersję zachowanie systemu.
Ale zanim ktoś zdąży zakrzyknąć: „Czyli ewidentny błąd w CROOK-u!”, to napiszę o kolejnej obserwacji: Procedura '''BLOK''' występuje we ''wszystkich'' zachowanych wersjach jądra systemu, w postaci nie zmienionej ''ani o jotę''. Wygląda na to, że nie jest to jakaś przypadkowa linia, która wkradła się z jedną, nieudaną edycją, a zamierzone, utrzymywane z wersji na wersję zachowanie systemu.
Linia 121: Linia 116:
Deep Thought się jednak pomylił. CROOK mówi, że nie 42, tylko 17152.
Deep Thought się jednak pomylił. CROOK mówi, że nie 42, tylko 17152.


Zobrazowany dysk MERY-400 z Politechniki Gdańskiej to około 20MB danych. Zawartość, lub przynajmniej znaczenie większości plików jest zrozumiałe. Ale niektóre z nich pozostawiają w pamięci ślad: „W zasadzie wiadomo co to jest, ale jakoś to dziwnie wygląda”. Takie ślady okazują się czasami być przydatne wiele miesięcy później, dopełniając brakujący fragment innej układanki. Tak też było w tym przypadku.
Zobrazowany dysk MERY-400 z Politechniki Gdańskiej to około 20MB danych. Zawartość większości plików, lub przynajmniej ich rola, jest zrozumiała. Ale niektóre z nich pozostawiają w pamięci ślad: „w zasadzie wiadomo co to jest, ale jakoś dziwnie to wygląda”. Takie ślady okazują się czasami być przydatne wiele miesięcy później, dopełniając brakujący fragment innej układanki. Tak też było w tym przypadku.


Liczba 17152 już gdzieś, kiedyś się pojawiła. W jakimś nie do końca zrozumiałym kontekście, przy okazji prac nad czymś zupełnie innym. Poszukajmy zatem: „grep -r 17152 *” odnajduje makro służące do budowania jakiegoś programu użytkowego. Do tego programu linkowany jest obiekt budowany assemblerem GASS z następującego źródła:
Liczba 17152 już gdzieś, kiedyś się pojawiła. W jakimś nie do końca zrozumiałym kontekście, przy okazji prac nad czymś zupełnie innym. Poszukajmy zatem. Przeczesanie zawartości dysku pod kątem charakterystycznej liczby odnajduje makro służące do budowania jednego z programów użytkowych. Do tego programu linkowany jest obiekt budowany assemblerem GASS z następującego źródła:


           .UNIT        SECRET
           .UNIT        SECRET
Linia 150: Linia 145:


W połowie lat '80, kiedy nie nazwany jeszcze nawet DRM zaczynał dopiero nieśmiało raczkować, stworzony w Instytucie Okrętowym Politechniki Gdańskiej, działający na MERZE-400 CROOK-5 dysponował ''wbudowanymi w system operacyjny'' mechanizmami pozwalającymi twórcom oprogramowania chronić je przed nieautoryzowanym uruchamianiem. Mechanizmami nie wspomnianymi w żadnej dokumentacji.
W połowie lat '80, kiedy nie nazwany jeszcze nawet DRM zaczynał dopiero nieśmiało raczkować, stworzony w Instytucie Okrętowym Politechniki Gdańskiej, działający na MERZE-400 CROOK-5 dysponował ''wbudowanymi w system operacyjny'' mechanizmami pozwalającymi twórcom oprogramowania chronić je przed nieautoryzowanym uruchamianiem. Mechanizmami nie wspomnianymi w żadnej dokumentacji.
Dalsza część historii, opisująca szczegółowo techniczne aspekty zabezpieczenia, dostępna jest tutaj: [[DRM w systemie operacyjnym CROOK-5]]

Menu nawigacyjne