Dobre praktyki w tworzeniu oprogramowania systemów wbudowanych

| Technika

Prawidłowo zaprojektowany system wbudowany gwarantuje, że urządzenie będzie bezpieczne pod wieloma względami. Przede wszystkim podczas normalnej pracy nie będzie ono stanowić zagrożenia dla użytkowników. Ponadto, jeżeli jego awaria albo nieprawidłowości w działaniu mogą być groźne dla ich zdrowia lub życia, jak w przypadku sprzętu medycznego, wiadomo, że zastosowano specjalne rozwiązania, które temu zapobiegną. Poza tym nikt bez uprawnień nie może uzyskać dostępu do urządzenia, na przykład przez Internet.

Dobre praktyki w tworzeniu oprogramowania systemów wbudowanych

To ostatnie jest szczególnie ważne w przypadku sprzętów medycznych, samochodów, systemów monitoringu, automatyki domowej, budynkowej i innych urządzeń, które są podłączone do sieci zakładowej i/lub Internetu.

Ochrona przed ingerencją z zewnątrz w przyszłości jeszcze bardziej zyska na znaczeniu w związku z przewidywanym zwiększeniem liczby urządzeń, które zostaną podłączone do Internetu Rzeczy oraz wprowadzeniem do użytku samochodów autonomicznych.

O czym piszemy?

Za zapewnienie szeroko pojętego bezpieczeństwa odpowiada projektant systemu wbudowanego, który w prostszych urządzeniach opracowuje część sprzętową i programową. W przypadku tych o rozbudowanej funkcjonalności zadanie to jest zazwyczaj podzielone pomiędzy członków zespołu, który obejmuje konstruktorów, programistów, stanowiących zazwyczaj najliczniejszą grupę, i testerów. Cel w zakresie bezpieczeństwa mogą oni łatwiej osiągnąć, stosując sprawdzone rozwiązania.

W artykule przedstawiamy przegląd tych z dziedziny tworzenia oprogramowania. Omawiamy je w oparciu o wyniki ankiety, którą przeprowadzono wśród projektantów systemów wbudowanych na temat przestrzegania przez nich dobrych praktyk w zakresie tworzenia firmware'u.

Oprócz tego na wstępie opisujemy przykłady urządzeń, w przypadku których najprawdopodobniej doszło do zaniedbań na tym etapie. To spowodowało, że były one podatne na ataki hakerów, bądź działały nieprawidłowo, stwarzając realne zagrożenie dla swoich użytkowników.

Problem Sony

Rys. 1. Różne rozwiązania cieszą się wśród programistów systemów wbudowanych zróżnicowaną popularnością

Pod koniec 2016 roku firma SEC Consult, specjalizująca się w dziedzinie cyberbezpieczeństwa, poinformowała o wykryciu kilku luk w zabezpieczeniach sieciowych kamer monitoringu z serii IPELA Engine produkowanych przez firmę Sony. Problem ten dotyczył kilkudziesięciu różnych modeli tych urządzeń.

Analitycy z SEC Consult wykryli luki, testując oprogramowanie kamer w programie IoT Inspector. Za pomocą tego narzędzia szybko udało im się odkryć pliki, w których zostało zapisane, co prawda w postaci zaszyfrowanej, ale zdaniem specjalistów łatwej do odszyfrowania, hasło dla użytkownika root.

Przy okazji wykryto istnienie dwóch innych kont, jednego z nazwą użytkownika primana i takim samym hasłem oraz drugiego, o nazwie użytkownika debug z hasłem popeyConnection. Zdaniem ekspertów z SEC Consult obydwa konta utworzono na etapie projektowania urządzeń, by ułatwić ich kalibrację oraz testowanie.

Według nich tylko kwestią czasu było to, kiedy hakerzy dotrą do tych tak słabo zabezpieczonych i w zasadzie zbędnych poza etapem produkcji danych uwierzytelniających. To z kolei umożliwiłoby im zarówno zdalne, jak i lokalne przejęcie kontroli nad systemem monitoringu zbudowanym z tych kamer.

Potencjał zagrożenia

Gdyby nie odkrycie informatyków z SEC Consult, hakerzy mieliby bardzo szerokie pole do popisu. Przykładowe działania, jakie przestępcy mogliby podjąć, to: szpiegowanie osób i mienia, które jest objęte monitoringiem, zakłócanie jego działania i wyświetlanie zmanipulowanych obrazów. Poza tym mogliby zainfekować kamery złośliwym oprogramowaniem (malware) umożliwiającym ich podłączenie do botnetu.

Przykładem malware'u, który jest nakierowany na urządzenia w sieciach Internetu Rzeczy, jest Mirai. Botnety składające się z urządzeń IoT są później przez hakerów odpłatnie wynajmowane m.in. do przeprowadzania ataków DDoS (Distributed Denial od Service).

Dla uwidocznienia potencjału zagrożenia, jakie niosły ze sobą niedopatrzenia programistów, warto zauważyć, że ze względu na cenę kamery z pechowej serii są używane przede wszystkim w celach profesjonalnych, głównie w przedsiębiorstwach oraz instytucjach rządowych, zaś w czasie, kiedy luki zostały odkryte, według jednego z serwisów, analizujących ruch w Internecie, było do niego podłączonych około 4 tys. takich urządzeń.

Z pewnością jednak na atak narażonych było dużo więcej kamer. Na szczęście nagłośnienie tego problemu sprawiło, że producent szybko opracował niezbędne poprawki oprogramowania oraz zalecił jego jak najszybszą aktualizację.

Historia aparatu Therac-25

Sztandarowym przykładem tego, jak nie powinno się tworzyć oprogramowania systemów wbudowanych, szczególnie dla urządzeń, od których zależy ludzkie zdrowie oraz życie, jest przypadek aparatu Therac-25 używanego w latach 80. ubiegłego stulecia w szpitalach w Kanadzie i w USA w radioterapii pacjentów z chorobą nowotworową. Przez zaniedbania jego programistów kilka osób zmarło, a kilka zostało dotkliwie na trwałe okaleczonych.

Therac-20 był kolejną wersją urządzenia, którego producentem było Atomic Energy of Canada Limited (AECL). Wcześniejsze modele, Therac-6 oraz Therac-20, AECL zbudowało wspólnie z francuską firmą CGR, ale Therac-20 skonstruowało samodzielnie, po rozwiązaniu współpracy z CGR.

Wszystkie trzy wersje były wyposażone w komputer sterujący PDP-11. W aparatach Therac-6 oraz Therac-20 nie był on niezbędny, gdyż możliwe było ich ręczne konfigurowanie. Dlatego na wypadek popełnienia przez operatora błędu, na przykład próby uruchomienia maszyny przy niewłaściwych ustawieniach źródła promieniowania, zastosowano blokady sprzętowe, które automatycznie odłączały aparat od zasilania.

Wypadki

Konfiguracja za pośrednictwem PDP-11 była znacznie szybsza. Krótsze przygotowanie aparatu do pracy pozwalało w takim samym czasie, jak w przypadku konfiguracji ręcznej, obsłużyć większą liczbę pacjentów. Dzięki temu koszt zakupu aparatu, liczony w milionach dolarów, szybciej się zwracał.

Nic więc dziwnego, że w modelu Therac-25 całkowicie zrezygnowano z obsługi ręcznej na rzecz sterowania komputerowego. W jego projekcie nie uwzględniono też zabezpieczeń sprzętowych - poprawność nastaw miał kontrolować tylko komputer. Chociaż przez kilka lat aparat działał bez zarzutu, ta decyzja wkrótce okazała się brzemienna w skutki.

W 1985 roku doszło do pierwszego wypadku z udziałem tej maszyny - kobietę chorą na raka piersi napromieniowano w niej dawką ponad sto razy większą niż dopuszczalna. To spowodowało jej silne poparzenie.

Wkrótce poszkodowana została kolejna pacjentka, a potem następna, dla której jedynym ratunkiem okazał się przeszczep skóry. Jeszcze tragiczniejszy był przypadek mężczyzny, dla którego naświetlania z powodu małego guza zakończyły się chorobą popromienną i śmiercią.

Dochodzenie i odkrycie przyczyny

Producent wielokrotnie testował maszynę, jednak nie udało mu się odtworzyć sytuacji, w której nie działałyby zabezpieczenia automatycznie zmniejszające natężenie promieniowania do bezpiecznego poziomu po wykryciu błędnych nastaw.

Z braku innych pomysłów przyjęto, że niewłaściwie działał system pozycjonowania wiązki. Mimo jego przeprojektowania doszło do kolejnych wypadków. W poszukiwanie przyczyn awarii zaangażowali się wtedy pracownicy szpitala, w którym miały one miejsce.

Jeden z nich zauważył, że za każdym razem wyświetlał się taki sam komunikat o błędzie. Według producenta oznaczał on, że komputer sterujący nie mógł określić, czy dawka promieniowania jest za duża, czy za mała.

Pracownik postanowił odtworzyć warunki, w których został zwrócony ten błąd. W tym celu wraz z operatorem, który obsługiwał aparat w czasie wypadków, wielokrotnie powtarzał całą procedurę. W końcu udało im się odkryć, co było przyczyną awarii.

Okazało się, że powodowało ją szybkie przełączanie się pomiędzy dwoma trybami pracy, które ustawiało układ pozycjonowania wiązki w pozycji nieokreślonej. Te warunki mogła odtworzyć tylko osoba zaznajomiona z urządzeniem, nic więc dziwnego, że producent w laboratoryjnych warunkach, wykonując kolejne kroki wolno i ostrożnie, nie był w stanie określić przyczyny problemu.

Wnioski końcowe

W modelu Therac-20 też występował ten błąd. Aktywował on jednak zabezpieczenia sprzętowe, z których w wersji Therac-25 zrezygnowano, i urządzenie było wyłączane.

Po odkryciu przyczyny wprowadzono poprawki i aparat oddano do użytku. Niestety wkrótce został poparzony kolejny pacjent. Tym razem źródłem awarii było przepełnienie licznika. Jeśli dokładnie w tym samym momencie operator wydał jakąś komendę, nie była ona właściwie interpretowana.

Dalsze dochodzenie wykazało, że twórcą oprogramowania pechowej maszyny była osoba z bardzo małym doświadczeniem w zakresie projektowania systemów działających w czasie rzeczywistym. Dlatego generalnie kod, który stworzyła, choć w dużym stopniu opierał się na kodzie źródłowym z aparatów Therac-6 oraz Therac-20, był bardzo niskiej jakości.

Ponadto nie wykonano analizy czasowej ani testów jednostkowych i nie stworzono drzewa błędów, ani dla części sprzętowej, ani programowej. Niedopatrzeniem ze strony osób, które odpowiadały za projekt całej maszyny, było zatem nie tylko oparcie jej systemu zabezpieczeń wyłącznie na komputerze z tak niedopracowanym oprogramowaniem, lecz w ogóle dopuszczenie tego ostatniego do użytku.

Ankieta

W 2017 roku amerykańska firma Barr Group, projektująca systemy wbudowane i szkoląca z tego zakresu, przeprowadziła internetową ankietę wśród ich projektantów dotyczącą przestrzegania przez nich dobrych praktyk w zakresie tworzenia oprogramowania. Przepytano prawie 2 tys. inżynierów ze średnio kilkunastoletnim stażem zawodowym, z różnych krajów, tworzących systemy wbudowane dla różnych branż.

Wnioski zebrano w dokumencie pt. Embedded systems safety and security survey 2017. Jest on dostępny bezpłatnie na stronie internetowej pod adresem https://barrgroup.com/. Dalej szerzej przedstawiamy wyniki tej ankiety.

Już na wstępie warto jednak zaznaczyć, że głównym wnioskiem wyciągniętym przez analityków Barr Group jest to, że w dość prosty sposób można by poprawić bezpieczeństwo systemów wbudowanych poprzez przestrzeganie powszechnie znanych i łatwych w implementacji sprawdzonych rozwiązań. Niestety osobom, które za to odpowiadają, brakuje motywacji.

Trendy

Pytania, na które odpowiadali ankietowani, na wstępie dotyczyły ogólnej charakterystyki systemów wbudowanych, nad którymi przyszło im obecnie pracować. I tak m.in. okazało się, że tylko jedna trzecia z nich wykorzystuje jeden procesor, a jedna czwarta - cztery procesory. Pozostałe projekty opierają się na dwóch albo trzech procesorach.

W przypadku tych ostatnich w 24% projektów pracę głównego procesora nadzoruje system operacyjny czasu rzeczywistego, komercyjny, jak na przykład VxWorks albo od producenta procesora. W 22% projektów nie ma żadnego OS, udział Linuksa wynosi 17%, zaś systemów open source, jak na przykład FreeROTS, 16%.

Jeżeli z kolei chodzi o łączność, to w 20% projektów zakładano stałe podłączenie do Internetu, a w 40% - czasowe, w większości za pośrednictwem sieci przewodowej, jednak około połowy urządzeń wyposażono także w jeden albo dwa interfejsy bezprzewodowe. Ich użytkownicy będą korzystać z przeglądarki internetowej (38%) albo dedykowanych aplikacji (39%).

Jeśli chodzi o język programowania, to prawie 95% programistów systemów wbudowanych tworzy kod w językach C (ponad 70%) i C++ (ponad 20%). Udział innych języków programowania, jak C#, czy Java, jest marginalny, nie przekracza bowiem 1%.

Zagrożenia i skutki zaniedbań

Spośród wszystkich, którzy wzięli udział w badaniu, około 60% przyznało, że bezpieczeństwo było jednym z wymogów projektowych w przypadku systemu wbudowanego, nad którym aktualnie pracują.

Z kolei zapytani o zdanie na temat największych zagrożeń, jakie niesie ze sobą atak hakerów, w największej liczbie jako główny powód do zmartwień wskazali oni możliwość ingerowania przestępców w działanie urządzenia (ponad 50%).

Według nich obawiać należy się również: kradzieży danych (40%), kradzieży własności intelektualnej (37%), naruszenia prywatności użytkowników (36%), sklonowania produktu (33%), ataku DoS (32%) oraz śmierci lub uszczerbku na zdrowiu użytkowników (27%).

Ankietowanych zapytano również o to, jakie konsekwencje w przypadku systemów wbudowanych, nad którymi pracują, będą miały zaniedbania, przez które urządzenie nie będzie działało właściwie.

15% odpowiedziało, że może dojść do wypadków śmiertelnych, a 13% wie, że zagrożone jest zdrowie ich użytkowników. Ponad 70% z tych urządzeń należało do jednej z czterech grup urządzeń: medycznych, wojskowych / lotniczych, przemysłowych albo samochodowych.

Dobre praktyki

Inne problemy, które wskazali pytani, to: konieczność zwrócenia produktu do producenta (19%), utrata reputacji połączona ze spadkiem sprzedaży (24%) i zwroty produktów przez klientów (10%). 14% pytanych spodziewałoby się poirytowania użytkowników.

Na pytanie o sposoby podniesienia poziomu bezpieczeństwa, jakie preferują w swoich projektach, najwięcej ankietowanych (około 60%) wymieniło kontrolę dostępu i aktualizacje oprogramowania. Inne rozwiązania to: szyfrowanie komunikacji z innymi urządzaniami (47%), kryptografia klucza publicznego (38%), ochrona pamięci (35%), mechanizm secure boot (33%), szyfrowanie komunikacji w obrębie systemu (26%) oraz zaciemnianie kodu źródłowego (obfuskacja), które polega na celowym modyfikowaniu kodu w taki sposób, aby stał się on mniej czytelny dla osób postronnych, a jednocześnie nie zmienił się wynik jego wykonania (13%).

Jeśli natomiast chodzi o metodykę tworzenia oprogramowania, z systemów kontroli wersji korzysta 91% ankietowanych, 36% z nich praktykuje technikę TDD (Test-Driven Development), zaś 80% używa narzędzi do wykrywania błędów (defect tracking).

66% projektantów przestrzega standardów kodowania, a 64% praktykuje przeglądy kodu (code review). Narzędzi do analizy statycznej używa natomiast 51% pytanych.

Dalej szerzej przedstawiamy stosunek, jaki w świetle wyników opisywanej ankiety mają do wybranych z wymienionych dobrych praktyk programiści systemów wbudowanych.

Standardy kodowania

Rys. 2. Metody testów preferowane wśród programistów systemów wbudowanych o znaczeniu krytycznym

Standardy tworzenia kodów źródłowych obejmują wiele aspektów tego procesu. Wytyczne dotyczą z reguły sposobu formatowania kodu, nazewnictwa, sposobu komentowania kodu oraz polecanych i zabranianych konstrukcji programistycznych. Ujednolicenie kodu źródłowego znacząco zmniejsza liczbę błędów i bardzo ułatwia pracę nad nim osobom, które nie są jego autorami.

Jak wynika z ankiety przeprowadzonej przez Barr Group, 17% projektantów systemów wbudowanych, których awaria może mieć wpływ na zdrowie albo życie użytkowników, nie przestrzega standardów kodowania.

Poza tym niestety zaledwie mniej niż połowa standardów kodowania, na których opierają się ich projektanci, to te ułatwiające tworzenie bezpieczniejszych systemów, jak na przykład MISRA, High Integrity C++, czy JSF++, a nie tylko nakierowane przede wszystkim na pisanie kodu łatwego w utrzymaniu, jak standardy, które są tworzone na własny użytek w obrębie danej organizacji.

Martwić może również to, że przestrzeganie standardów kodowania nie jest właściwie egzekwowane. Jak bowiem przyznało zaledwie 6% ankietowanych, jest to w ich przypadku w pełni zautomatyzowane, 35% - tylko częściowo, zaś 39% stwierdziło, że uwaga na ten aspekt jest zwracana dopiero przy okazji przeglądu kodu. Aż 19% ma w zakresie zgodności ze standardami kodowania całkowitą swobodę.

Analiza statyczna. Przeglądy kodu

W ramach statycznej analizy kodu błędy w jego strukturze są wykrywane bez jego uruchamiania. Jednym z najpopularniejszych narzędzi używanych w tym celu jest płatna aplikacja PC/Lint. Inny przykład to program Coverity.

Poza ostrzeganiem programistów o potencjalnych problemach w sposób powtarzalny i obiektywny, tego typu narzędzia ułatwiają również zautomatyzowanie zadania egzekwowania przestrzegania reguł standardów pisania kodu.

Jak wspomniano wyżej, nieco ponad połowa ankietowanych przyznała, że sprawdza swój kod w narzędziach do analizy statycznej. Dobrą wiadomością jest to, że do tej grupy zaliczali się także programiści systemów, których awaria może mieć wpływ na zdrowie albo życie użytkowników. Niestety, aż 32% z nich nie wykonuje analizy statycznej.

Blisko 60% programistów potencjalnie niebezpiecznych systemów przyznało, że przeglądy kodu są stałym etapem ich pracy albo praktykują programowanie w parach. Jest to dobra informacja, gdyż powszechnie wiadomo, że są to jedne z najefektywniejszych sposobów na znajdywanie i poprawę błędów w oprogramowaniu.

Niestety, aż 25% programistów systemów wbudowanych o krytycznym znaczeniu stwierdziło, że nie przeprowadza przeglądów kodu, a jeżeli już, to bardzo rzadko. 16% z kolei przyznało, że decydują się na ten krok tylko w przypadku wybranych modułów kodu albo wyłącznie, kiedy napotykają jakieś problemy.

Testowanie

Testowanie systemów wbudowanych może przyjmować różne formy, od przeprowadzania testów jednostkowych poszczególnych elementów kodu, po testowanie metodą HIL (Hardware in the Loop).

Ta druga jest chętnie wykorzystywana w sprawdzaniu złożonych systemów wbudowanych czasu rzeczywistego, na przykład w przemyśle samochodowym i lotniczym. Polega ona na stworzeniu interfejsu składającego się z rzeczywistych elementów. Stanowi on połączenie między platformą, która symuluje sterowane urządzenie, a platformą, która testuje algorytmy zaimplementowane w systemie wbudowanym.

Popularna jest także technika TDD, w której proces tworzenia kodu rozpoczyna się od napisania testów sprawdzających daną funkcjonalność, która dopiero ma zostać zaimplementowana. Ważną korzyścią uzyskiwaną z praktykowania takiego podejścia jest to, że biblioteka testów powiększa się wraz z rozwojem kodu.

To pozwala na wykorzystywanie ich do okresowego sprawdzania, czy sukcesywnie wprowadzane zmiany w sposób niezamierzony nie zmieniają dotychczasowej funkcjonalności systemu.

Jakie testy są najpopularniejsze?

Ważną techniką są również testy regresywne. Dzięki nim jakość kodu w czasie może się tylko poprawiać. W tym przypadku tworzy się dużą bazę testów, które uruchamiania się za każdym razem, gdy kod zostaje zmieniony. Dobrą praktyką jest, żeby za każdym razem, gdy wykryty zostanie błąd, dodawać test, który w przyszłości wykryje ewentualne kolejne błędy tego typu.

Testowanie regresywne można wykorzystywać w połączeniu z techniką TDD, choć nie jest to konieczne. Mimo niezaprzeczalnych zalet wymienionych technik wśród programistów systemów wbudowanych o znaczeniu krytycznym są one mało popularne.

Najczęściej wykonują oni testy na poziomie całego systemu (około 80% pytanych). Na drugim miejscu pod względem popularności są testy jednostkowe typu czarna skrzynka (black box), w przypadku których pomija się wewnętrzną strukturę oprogramowania, uwzględniając tylko wejścia i wyjścia (około 65%).

Zaledwie 59% programistów przeprowadza testy regresywne, a około 55% testy jednostkowe typu biała skrzynka (white box), w których uwzględnia się wewnętrzną strukturę kodu. Około 45% programistów wykonuje symulacje HIL, natomiast tylko jedna trzecia z nich praktykuje technikę TDD.

Monika Jaworowska