Inżynier Microsoft proponuje porzucenie języków C i C++
Gavin Thomas, który w Microsofcie sprawuje funkcję Principal Security Engineering Manager, zasugerował, że ze względów bezpieczeństwa czas porzucić języki C i C++. Thomas argumentuje na blogu Microsoftu, że rezygnacja ze starszych języków na rzecz języków bardziej nowoczesnych pozwoli na wyeliminowanie całej klasy błędów bezpieczeństwa.
Od 2004 roku każdy błąd naprawiony w oprogramowaniu Microsoftu jest przez nas przypisywany do jednej z kategorii. Matt Miller podczas konferencji Blue Hat w 2019 roku zauważył, że większość tych dziur powstaje wskutek działań programistów, którzy przypadkowo wprowadzają do kodu C i C++ błędy związane z zarządzeniem pamięcią. Problem narasta w miarę jak Microsoft tworzy coraz więcej kodu i coraz silniej zwraca się w stronę oprogramowania Open Source. A Microsoft nie jest jedyną firmą, która ma problemy z błędami związanymi z zarządzaniem pamięcią, pisze Thomas.
W dalszej części swojego wpisu menedżer wymienia liczne zalety C++, ale zauważa, że język ten ma już swoje lata i pod względem bezpieczeństwa czy metod odstaje od nowszych języków. Zamiast wydawać kolejne zalecenia i tworzyć narzędzia do walki z błędami, powinniśmy skupić się przede wszystkim na tym, by programiści nie wprowadzali błędów do kodu, czytamy.
Dlatego też Thomas proponuje porzucenie C++. Jednym z najbardziej obiecujących nowych języków programistycznych zapewniających bezpieczeństwo jest Rust, opracowany oryginalnie przez Mozillę. Jeśli przemysł programistyczny chce dbać o bezpieczeństwo, powinien skupić się na rozwijaniu narzędzi dla developerów, a nie zajmować się tymi wszystkimi pobocznymi sprawami, ideologią czy przestarzałymi metodami i sposobami rozwiązywania problemów.
Komentarze (7)
darekp, 22 lipca 2019, 10:55
Pytanie dla programistów (głównie), może ktoś się orientuje, na czym polega ta specyfika Rusta jeśli chodzi o zarządzanie pamięcią (w porównaniu np. z Java/C#)? Nie udało mi się znaleźć w necie żadnego krótkiego omówienia, z tego co zrozumiałem, kompilator śledzi przynależność obiektów tworzonych dynamicznie na stercie do zmiennych, na etapie kompilacji jest rozstrzygane, kiedy zwolnić pamięć, możliwe są dwa rodzaje takich jakby referencji do obiektu na stercie, jedna to "na własność" (czyli, gdy zmienna trzymająca "na własność" jakiś kawałek pamięci przestaje być potrzebny, to ta pamięć jest zwalniana), drugi to referencja "pożyczająca od właściciela", która pozwala odwoływać się do obiektu nie zwalniając pamięci (czyli gdy np. jakaś funkcja "pożyczy" sobie obiekt, to może wykonać na nim jakieś manipulacje, np. odczytać wartość pola, ale nie może zwrócić obiektu jako wynik, a poza tym, tak długo jak obiekt jest "pożyczony" funkcji, żaden kod spoza tej funkcji nie może na nim wykonywać żadnych operacji i to też jest sprawdzane przez kompilator)? I nic więcej (np. żadnych innych rodzajów referencji)?
Czy dobrze zrozumiałem?
P.S. Chyba jednak czegoś nie zrozumiałem, bo przychodzi mi do głowy, że przy takiej interpretacji nie ma przeszkód, żeby kilku "pożyczkobiorców" mogło "pożyczyć" naraz ten sam obiekt od "właściciela", jedyne ograniczenie, że "właściciel" nie może zwolnić pamięci obiektu dopóki ktoś go "pożycza".
yaworski, 22 lipca 2019, 11:45
Polecam przeczytać oficjalny manual Rusta. Jest napisany całkiem zwięźle i zrozumiale.
W uproszczeniu jest tak jak napisałeś. Dochodzi jeszcze kwestia tzw lifetime, która jest bardzo ważna przy pożyczaniu. Pożyczona referencja nie może mieć dłuższego lifetime'u niż obiekt, z którego pożycza. Właśnie tego pilnuje kompilator. Czasami nawet trzeba mu w tym pomóc (np jeśli funkcja przyjmuje więcej niż jedną referencję jako argumenty i zwraca jakąś referencję, przez zadeklarowanie lifetime'ów tych referencji). Dodatkowo możesz mieć wiele niemutowalnych referencji jednocześnie, ale tylko jedną mutowalną (przy czym nie możesz mieć jednocześnie niemutowalnych).
Oprócz tego są pewne ułatwienia w bibliotece standardowej, jak np Rc (reference counter), czy jego wersja thread safe czyli Arc (atomic Rc), które przejmują własność obiektu. Można je klonować, co zwiększa licznik referencji trzymanego obiektu i dropowanie kopii powoduje zmniejszenie licznika. Jak licznik dojdzie do zera, to obiekt też jest dropowany.
Są też inne techniki, jak np internal mutability, które trochę obchodzą wewnętrzne zabezpieczenia w sposób w miarę bezpieczny. Obecnie kompilator nie jest jednak w stanie wyłapać błędnego użycia niektórych z tych technik, więc użycie ich jest zalecane tylko jeśli nie da się czegoś zrobić inaczej.
pskosinski, 22 lipca 2019, 12:48
Pozwolę sobie zacytować:
Źródło: https://www.i-programmer.info/news/98-languages/12552-is-rust-really-safe.html
beczek, 22 lipca 2019, 14:08
Trzeba mieć na uwadze, że unsafe jest wymagane do wywołania funkcji zewnętrznych (nie napisanych w języku Rust) przez FFI. Servo korzysta z wielu komponentów napisanych w C++ jak np. silnik JavaScript z Gecko. Stąd nie dziwi mnie, że w tym projekcie często sięgają do bloków `unsafe`.
Dla porównania, w repozytorium Libry ( https://github.com/libra/libra ) na 145 tys. linii kodu blok `unsafe` jest użyty 6 razy.
yaworski, 22 lipca 2019, 14:52
Jeszcze jest używany w kilku miejscach w bibliotece standardowej. Np w std::sync::atomic, czy std::cell, czyli w implementacjach struktur, które używane są do interior mutability.
Pisząc coś, jeżeli potrzebuję odwołać się przez FFI do jakiejś biblioteki, staram się jednak wydzielić obsługę tej biblioteki do oddzielnego crate'a (wszystkie unsafe mam w jednym miejscu). Dzięki temu w kodzie aplikacji czy biblioteki, którą piszę, nigdy nie używam unsafe bezpośrednio. Crate odpowiedzialny za komunikację z zewnętrzną biblioteką łatwiej przetestować niezależnie od pozostałego kodu.
Jeżeli chodzi o Rust, to zacząłem relatywnie niedawno w nim pisać. Wcześniej głównie programowałem w Pythonie, w mniejszym stopniu w C/C++, Lua i Go. Nie wiem, czemu tak długo zwlekałem z nauczeniem się Rusta, bo naprawdę bardzo mi się spodobał. Mam tylko nadzieję, że jego rozwój pójdzie w dobrym kierunku. Trochę rzeczy jeszcze w std brakuje. Sporo jest dostępne tylko w nightly. No ale to w końcu bardzo młody język.
wilk, 23 lipca 2019, 04:46
Tylko, że te błędy to nie wina technologii, tylko czynnika ludzkiego. Od czego dziesiątki lat rozwoju analizatorów kodu, by nie „strzelić sobie w stopę”.
darekp, 23 lipca 2019, 08:10
Te analizatory kodu nie są tak dobre, jak mogłoby się wydawać. Wygląda na to, że zamiast zaawansowanych analizatorów nieraz lepiej działają proste narzędzia, które "przeskakują" większość analizowanego kodu i wyszukują tylko pewne charakterystyczne konstrukcje w kodzie z dużym prawdopodobieństwem będące błędami: http://lambda-the-ultimate.org/node/5348 (np. odwołanie do x.pole występujące przed sprawdzeniem czy x != null).
Pierwsza myśl może być taka, że jeśli zrobić potężny parser, który bardzo dokładnie przeanalizuje kod programu i wygeneruje dla niego wielkie drzewo z bardzo dużą ilością informacji o szczegółach działania (tak robią duże firmy piszące analizatory), to da się przeprowadzić bardziej dokładną analizę i znaleźć więcej błędów. Ale napisać funkcję, która chodzi po takim wielgachnym drzewie z mnóstwem atrybutów i znajduje błędy to jest "mordęga".
Z drugiej strony, ta sytuacja jest w pewnym sensie "fajna", bo wychodzi na to, że - jeśli chodzi o języki programowania, analizatory kodu itp. - pojedynczy programista (albo niewielki zespół) może konkurować z wielkimi koncernami. I tak chyba jest w praktyce - języki programowania to w większości dzieło pojedynczych ludzi (jeden język -> jeden autor, Lua to chyba wyjątek, jakiś większy zespół z jakiegoś katolickiego uniwersytetu w Brazylii, ale nie jestem pewien), firma JetBrains, która tworzy IDE takie jak IntelliJ, PyCharm, ReSharper, jest o wiele mniejsza od Microsoftu czy Google Trzeba "tylko" być zdolnym programistą;)
P.S. Zapomniałem napisać, ale jest jest język programowania, który tak dokładnie sprawdza kod na etapie kompilacji, że w praktyce niemal nie występują błędy runtime'u. To Elm (https://elm-lang.org/). Co prawda tylko dla frontendu na przeglądarce (i chyba trudno sobie wyobrazić pisanie np. systemu operacyjnego w ten sposób), ale z drugiej strony chyba fajna sprawa (dopiero się go uczę, nie maiłem okazji jeszcze wykorzystać w praktyce)