Witte Huis vraagt programmeurs om alleen memorysafetalen te gebruiken

Het Amerikaanse Office of the National Cyber Director vraagt in samenwerking met het Witte Huis aan programmeurs om in principe alleen nog programmeertalen te gebruiken die memorysafe zijn. Rust is een van de talen die als geschikt geldt, terwijl C en C++ als onveilig worden beschouwd.

Het ONCD zegt in een persbericht dat de technische sector proactief 'volledige categorieën kwetsbaarheden' kan vermijden door te stoppen met het gebruiken van bepaalde programmeertalen. Daarom roept het overheidsorgaan programmeurs in het bijbehorende rapport op om te stoppen met het gebruik van onder meer C en C++. Het Witte Huis redeneert dat het gebruik van talen die memorysafe zijn, in bijna alle gevallen mogelijk en schaalbaar is.

In het rapport worden uiteenlopende prominente voorbeelden van virussen, exploits en kwetsbaarheden genoemd als gevolg van het gebrek aan geheugenbeveiliging, waaronder de Morris- en Slammer-wormen, Heartbleed, Trident en Blastpass. Deze cyberbeveiligingsproblemen zouden veroorzaakt zijn door geheugentoegangkwetsbaarheden. Microsoft bevestigde dit in 2019; ongeveer 70 procent van alle beveiligingspatches van de softwaregigant had betrekking op het verbeteren van geheugenbeveiliging.

Memorysafety beschrijft de beveiliging of eventuele kwetsbaarheid van geheugentoegang van software. Onder meer bufferoverflow- en geheugenlekaanvallen maken misbruik van gaten in de geheugenbeveiliging en kunnen daarmee bijvoorbeeld willekeurige code uitvoeren om een systeem te compromitteren. Veel moderne programmeertalen ondersteunen de mogelijkheid om bounds checks uit te voeren, wat een taal memorysafe kan maken.

Door Yannick Spinner

Redacteur

27-02-2024 • 12:10

193

Submitter: Anonymoussaurus

Reacties (193)

193
190
79
6
1
99
Wijzig sortering
Ik moet hier well toevoegen dat zelfs in memory safe languages, je toch memory bugs kan maken. Dit is bijvoorbeeld met rust(waar ik zelf projecten in maak) ook gezien. Maar ik denk dat het meer is dat de kans lager is dat je zulke fouten maakt dan met talen zoals C++ en C.

Het probleem is ook dat zo veel van de code die all bestaat en in productie wordt gebruikt in C of C++ is geschreven, alleen wordt hier langzaam overgestapt op rust zoals je nu bij microsoft ziet(Die zitten delen van hun Core te heschrijven in Rust wat je ook kan zien met hun advertenties voor banen) of het bedrijf tweedegolf die verschillende projecten zit te herschrijven in Rust, zoals sudo. En je hebt ook het project dat de linux coreutils(oftewel de GNU coreutils) zit te heschrijven in rust. En natuurlijk de Linux kernel die nu in de laatste tijd het mogelijk heeft gemaakt(of zit te maken) om delen van de Linux Kernel in rust te schrijven

Maar je kan zien zelfs met heel veel devs die er aan werken, vaak ook van grote bedrijven is het nog steeds een monumentaal ding is om dat allemaal te doen, en dit zal natuurlijk heel lang duren.

[Reactie gewijzigd door Stetsed op 22 juli 2024 20:01]

Ik moet hier well toevoegen dat zelfs in memory safe languages, je toch memory bugs kan maken. Dit is bijvoorbeeld met rust(waar ik zelf projecten in maak) ook gezien. Maar ik denk dat het meer is dat de kans lager is dat je zulke fouten maakt dan met talen zoals C++ en C.
Weet ik eerlijk gezegd niet. Het punt is dat programmeurs in C en C++ vaak veel bewuster bezig zijn met het managen van memory, en dat er ook structureel tooling wordt ingezet om de ellende van vergissingen alsnog te detecteren, gewoon omdat het een bekend probleem is. Zo heb ik laatst een discussie gehad op een open source project dat JavaScript gebruikte, wat een memory leak had. Ik werd voor gek verklaard, want JavaScript kende dat soort problemen niet volgens de package maintainers. Het punt is dat JavaScript programmeurs vaak te weinig last hebben van dit soort issues dat het bewustzijn en de tooling niet ontwikkeld worden, maar dat het wel problematisch genoeg is om je zorgen te maken.
Maar je kan zien zelfs met heel veel devs die er aan werken, vaak ook van grote bedrijven is het nog steeds een monumentaal ding is om dat allemaal te doen, en dit zal natuurlijk heel lang duren.
Het onderliggende probleem is vaak een legacy-probleem: iets herschrijven waarvan iedereen perfect weet hoe het werkt en waarom het zo werkt is een peuleschilletje. Maar door de tijd heb je tientallen maintainers met andere ideeen gehad en zijn er vaak bugs onder hoge tijdsruk "tijdelijk" gefixt. Dat porteren naar een nieuwe taal is gewoon een ramp, omdat je niet weet of en wat je sloopt. En zo'n soort code porteren vraagt eigenlijk ook om een stuk refactoring van de code en functionaliteit, als je het echt goed zo willen doen. Maar dan wordt het een nog akeliger project dan het al was.

Zelf ben ik er wel groot voorstander van om ook te refactoren, anders ben je je vulnerabilities gewoon naar je nieuwe oplossing mee aan het kopieren. Maar dat betekent soms ook breken met het verleden, en dat is best een pittige.
Klinkt alsof je Rust nog nooit gebruikt hebt! Klopt dat? In Rust was ik juist nog meer met mijn memory bezig dan normaal, omdat je per syntax gedwongen wordt alles a la minute af te handelen.
Kan je toelichten wat je bedoeld met "je toch memory bugs kan maken"? Eén van de primaire karakteristieken van Rust is namelijk dat dit niet kan, ik denk dat je een ander soort bugs verkeert categoriseert als memory bugs.
Dit is een repo die een PoC geeft voor memory bugs: https://github.com/Speykious/cve-rs
Dit project is een demo van hoe een lang bekend probleem van de compiler kan geexploit worden met wat ridicule code. Zelfs projecten met extreme black magic code komen zelden of nooit zulke bugs tegen. Theoretisch kan het, maar praktisch...
Famous last words…
Hoe groot is de kans dat als ik wat in elkaar zet dat dat tevens werkend een mens op de maan kan zetten? Dat moet je dus wel met pure intenties doen zulke black magic code. Iets met kansberekening... Kans dat wat ik beschrijf lukt is nihil.
Klopt, maar dit kan je zeggen voor 99.9% van de code die geschreven wordt en op Github terecht komt. Uiteindelijk met de wet van grote schaal komt het toch een keer voor dat dit gebeurt.
C en C++ zijn net zo veilig als Rust.

Het probleem ligt hem aan de mens, die is niet perfect, en die maakt dus foutjes bij het gebruik, waardoor C en C++ als onveilig worden gezien, maar los van de invloed van de mens, zijn de talen uiteindelijk even veilig.

Rust probeert dit mens-geintroduceerde probleem op te lossen door het probleem anders te interpreteren, en door je met een soort "bowlinghekjes" op de juiste baan te houden. Alhoewel ze het probleem hier weliswaar een deel mee hebben opgelost, hebben ze hiermee een ander probleem gecreerd, namelijk dat de taal totaal tegelijkertijd volledig ontoegankelijk is geworden voor de meeste programmeurs.

Natuurlijk is dit op te lossen met o.a. goede documentatie en een hoop training, maar ook dat mist in de meest absolute zin van het woord: De bestaande documentatie voor Rust is vergelijkbaar met een machine-printed specificatie van welke methodes er zijn en haar signatures (wat een standaard IDE je ook geeft), en het heeft geen enkele beschrijving qua wat de methodes doen of waar iets voor is. Ook zijn online trainingen niet doorgrondend genoeg om een goed begrip te krijgen qua hoe je Rust mentaal moet benaderen, zelfs als ervaren als programmeur.

[Reactie gewijzigd door Arckedo op 22 juli 2024 20:01]

Het probleem ligt hem aan de mens, die is niet perfect, en die maakt dus foutjes bij het gebruik, waardoor C en C++ als onveilig worden gezien, maar los van de invloed van de mens, zijn de talen uiteindelijk even veilig.
Zo is een cirkelzaag zonder beschermingskap net zo veilig als een cirkelzaag met beschermingskap. Je moet gewoon je vingers er niet tussen steken. Doorgaans wordt een tool veilig genoemd juist als die menselijke fouten opvangt.
hebben ze hiermee een ander probleem gecreerd, namelijk dat de taal totaal tegelijkertijd volledig ontoegankelijk is geworden voor de meeste programmeurs
Vroeger waren er programmeurs die het maar heel stom vonden dat ze geen goto mochten gebruiken, want zo werkten ze altijd, dus die moeilijke functies maakten het maar erg ontoegankelijk.

Nou wil ik niet zeggen dat rust op dit moment al de heilige graal is, maar huidige gebrekkige documentatie is geen fundamenteel probleem van die "bowlinghekjes".
Hoewel ik alleen wat Pet Shop projectjes heb gedaan in Rust vond ik de documentatie en tutorials juist prima. De taal zelf is conceptueel zeker wennen en ik kan me voorstellen dat het in grote projecten (wat) remmend) werkt.

Maar het is door die concepten juist wel eens stuk veiliger van C/C++. Je moet in Rust heel goed je best doen om in een stuk geheugen terecht te komen wat niet (meer) van jou is. In C(++) kan dit juist vrij eenvoudig met alle gevolgen van dien.

Dat gezegd hebbende: in alle talen kun je verschrikkelijke bugs creëren. Dus voor elke zichzelf respecterend project, is kwaliteitscontrole/testen uitermate belangrijk.
Qua ontwikkeling zal het zeker meer kosten qua tijd en resources. Maar dat is waarschijnlijk minder als je naar de total cost of ownership kijkt. Denk aan onderhoud, aanpassingen, testing en noem het maar op.
Ik heb hier wel een mening over. Ik heb jarenlang geprogrammeerd in C++ en een beetje in C, en deze talen stellen programmeurs in staat om de maximale prestaties uit de hardware te halen, omdat je volledige controle hebt over het geheugen en hoe je dit geheugen gebruikt. Echter, ik heb gemerkt dat veel programmeurs het verkeerd toepassen, of nog steeds gebruikmaken van verouderde versies van C++. Deze programmeurs maken dus basis fouten, maar is dat dan te verwijten een een taal of de programmeur zelf?

De Linux Kernel is geschreven in C, wat complexer is dan C++. De reden hiervoor is prestatiegerelateerd. FFmpeg, dat door alle video streaming diensten wordt gebruikt, is geschreven in C, puur vanwege prestatieoverwegingen. Betekent deze uitspraak van het witte huis dat deze softwarepakketten plotseling nu onveilig zijn en herschreven moeten worden? Hoe veilig de programmeertaal ook is, er geldt een algemene vuistregel: voor elke 1000 regels code is er minimaal 1 bug.

[Reactie gewijzigd door Xieoxer op 22 juli 2024 13:59]

Hoe veilig de programmeertaal ook is, er geldt een algemene vuistregel: voor elke 1000 regels code is er minimaal 1 bug.
Het is een beetje een open deur, maar je wil dus zo min mogelijk regels code schrijven, dus wil je kijken naar talen met een hoger abstractieniveau.

De wet van behoud van ellende zegt dat de totale hoeveelheid problemen waar je mee te maken hebt niet echt veranderd, het is een/de beperkende factor voor alles wat we programmeren. We zijn veel tijd kwijt aan het oplossen van problemen (vaak meer dan aan het programmeren zelf). Als je minder fouten maakt dan kun je meer programmeren waardoor het totaal aantal fouten weer stijgt. Maar mensen minder fouten laten maken is erg moeilijk. Waar je makkelijker invloed op hebt is met wat voor soort fouten je te maken krijgt.

Memory management is bij low-level talen typisch een bron van veel lastige bugs. Performance (bv van een memory manager) is een heel ander soort probleem, een probleem dat je (soms) eenvoudig kan oplossen door een snellere computer te kopen. Als ik mag kiezen tussen een memory manager debuggen in een low-level taal of een snellere computer kopen om een high-level taal te gebruiken met een kant-en-klare memory manager dan kies ik voor de tweede optie.

Kies waar mogelijk voor het soort problemen dat je kan/wil oplossen.
De wet van behoud van ellende zegt dat de totale hoeveelheid problemen waar je mee te maken hebt niet echt veranderd, het is een/de beperkende factor voor alles wat we programmeren. We zijn veel tijd kwijt aan het oplossen van problemen (vaak meer dan aan het programmeren zelf). Als je minder fouten maakt dan kun je meer programmeren waardoor het totaal aantal fouten weer stijgt. Maar mensen minder fouten laten maken is erg moeilijk. Waar je makkelijker invloed op hebt is met wat voor soort fouten je te maken krijgt.
Mee eens.
Memory management is bij low-level talen typisch een bron van veel lastige bugs. Performance (bv van een memory manager) is een heel ander soort probleem, een probleem dat je (soms) eenvoudig kan oplossen door een snellere computer te kopen. Als ik mag kiezen tussen een memory manager debuggen in een low-level taal of een snellere computer kopen om een high-level taal te gebruiken met een kant-en-klare memory manager dan kies ik voor de tweede optie.
Er is voor C en C++ ook wel erg veel extra tooling om dat debuggen te ondersteunen. En omdat je het zelf veroorzaakt is het vaak veel duidelijker waar de ellende vandaan komt, en hoe het op te lossen is.

Die higher level talen hebben ook gewoon last van memory leaks en vergelijkbare ellende, alleen het komt minder vaak voor en als je er last van hebt sta je er veel meer alleen voor. En omdat het voor hoger level talen het geheugen gemanaged wordt door een abstracte "Garbage collector", die dit "automatisch" opgelost, ben je afhankelijk van alle rare eigenschappen van die garbage collector. Voorbeeldje: in JavaScript wordt door sommige garbage collectors het geheugen van een variabele alsnog niet vrijgegeven, zelfs als je hem expliciet op null zet (zie deze link naar Facebook engineering).
EDIT: zoals gebruikelijk zijn er weer mensjes die het niet met me eens zijn (ofschoon wat ik zeg feitelijk waar én on-topic is) en daarvoor het moderatiesysteem totaal misbruiken :+
En omdat het voor hoger level talen het geheugen gemanaged wordt door een abstracte "Garbage collector", die dit "automatisch" opgelost, ben je afhankelijk van alle rare eigenschappen van die garbage collector.
Wat je zegt is helemaal waar, maar het is nog erger dan dat zelfs.

Als professionele compiler engineer kan ik zeggen dat de problemen met garbage collectors op z'n minst de volgende punten omvatten:

1. Er is een runtime overhead die zowel in RAM usage, CPU cycles (vanwege het runtime component van GC) én latency (omdat de wereld vaak stop moet worden gezet terwijl het runtime component draait) gemeten wordt, relatief aan het equivalente programma in C, C++, of een compile time memory-safe taal zoals Rust. En hoewel daar stappen in zijn gezet in de afgelopen 10 jaar zal dit feit fundamenteel nooit veranderen. Er zijn bijvoorbeeld voldoende blogs te vinden, geschreven over de afgelopen paar jaar, van bedrijven die hun Go servers om hebben geschreven naar Rust om van de zéér irritante latency spikes af te komen, iets dat dus zo goed als onmogelijk te doen is gebleken met garbage collected languages. En in zoverre dat het wel kan, levert het code op waar alleen een moeder van kan houden, en hoe dan ook niet idiomatisch is.

2. Garbage collectors zijn enorme ballen complexiteit, met bijbehorende bugs. Hoeveel dit uitmaakt hangt af van wat je erop draait.
Vaak zijn het geen bugs die je direct merkt (de garbage collectors worden daar wel goed genoeg voor getest) maar het is nu wachten op CVEs die misbruik maken van die bugs. Oftewel: Garbage Collectors zijn een vermijdbaar security risico.

3. Garbage Collectors lossen het échte probleem niet eens goed op. Het echte probleem is namelijk niet memory management, maar resource management. Dat is een breder probleem waar RAM slechts 1 onderdeel van is. Andere resources zijn bijvoorbeeld sockets en open files. Garbage Collectors werken alléén op RAM, en 1 van de nare gevolgen is dat je dan constructs krijgt in zulke talen (denk aan `with`-style statements in bv Python, of handmatig files moeten `close()`n in andere talen), wat in feite niks meer dan slecht bedachte fixes zijn voor een systeem dat dus niet fit for purpose is.
Zulke constructs leveren minder flexibiliteit op dan mogelijk was geweest met een holistische oplossing die het resource management probleem op een behoorlijke manier aanpakt. Een goed voorbeeld van een taal die dat wel goed doet is Rust: door slim gebruik van compile-time type checking en borrow checking is resource management niet alleen te doen, maar zelfs heel elegant en performant, en levert het voor 99% van de code exact 0 runtime overhead op langs welke dimensie (runtime, RAM usage, latency) dan ook.

[Reactie gewijzigd door Jeanpaul145 op 22 juli 2024 20:01]

Wat je hier voorstelt als GC is bijna herimplementatie van het OS.

Je kunt alleen met geheugen gerelateerde resources managen door dit in een taal af te dwingen via constructen als 'with' of expliciet levensduur te garanderen als Rust, waar de laatste vziw ook problemen kan opleveren als je het ontwerp aanpast en ownership niet mee wil werken (verander een stack-implementatie eens naar een heap-implementatie).

Ik zie niet hoe je dit gaat oplossen, een subsysteem dat voor jou geheugenmanagement overneemt zal nooit het meest optimaal zijn (kan dat uberhaupt met virtual memory?) en volgens mij was een GC ooit bedoeld om NULL-pointers, overflows, etc. te voorkomen.

Dat is prima gelukt en ja, er zullen security issues in de GC zijn en komen, maar dat lijkt me beter als dat op te lossen is op 1 plek in plaats van losgeslagen over het hele systeem.

Daarnaast gaat de OO-methodiek niet zomaar de prullenbak in, de patterns en ideeen zijn zeker bruikbaar voor grote projecten, de door jouw aangekaarte problematiek rond GC is precies waar de aversie tegen DCOM/CORBA vandaan komt van de Rust developers.

Rust is geen heilige graal en een GC is dat niet.
volgens mij was een GC ooit bedoeld om NULL-pointers ... te voorkomen.
Beetje offtopic, maar specifiek null-pointers hebben weinig met wel of geen garbage collection te maken. Als je veilig met null-pointers om wil gaan moet je gewoon tijdens het dereferencen checken of je pointer 0 is, daar heb je geen GC voor nodig, en een GC gaat daar ook niks aan toevoegen.

Of gewoon een taal zonder null gebruiken natuurlijk. Als je een appel verwacht moet je ook een appel krijgen, niet "stiekem toch geen appel".
Beetje offtopic, maar specifiek null-pointers hebben weinig met wel of geen garbage collection te maken.

Als je veilig met null-pointers om wil gaan moet je gewoon tijdens het dereferencen checken of je pointer 0 is, daar heb je geen GC voor nodig, en een GC gaat daar ook niks aan toevoegen.
'Awel, goed voorbeeld is in C met een toolkit als GTK+ dat intern refcounting (=GC) gebruikt. Een NULL-pointer exceptie is zowat het eerste wat je tegenkomt als een testscenario een vergissing in pointergoochelen of arithmetics hierover valt. Kan me herinneren dat dit één van de marketingleuzen van Java of .NET was.

Daarnaast, je kunt wel pointers checken op NULL (of nonnull attr in functie definitie) maar dat garandeert niet dat je geen geheugenlekken krijgt (hallo Motif) als door programmeerfout de datastructuur niet consistent meer is.

Met een beetje complexe graaf in C wordt het al gauw interessant om GC te overwegen en dat is al bij de meeste compilers en interpreters het geval.
Of gewoon een taal zonder null gebruiken natuurlijk. Als je een appel verwacht moet je ook een appel krijgen, niet "stiekem toch geen appel".
Dat maakt het erg vervelend als je later een andere appelsoort wilt toevoegen maar compatibel wil blijven met iedereen die niet alle soorten appels heeft of als je een appel verwacht maar weet dat geen appel gegarandeerd kan worden.

Kun je gedeeltelijk afvangen met enum, maar in sommige gevallen bij Rust (linked-list etc) wordt dat dusdanig omslachtig en onintuïtief dat een null niet uit de lucht gegrepen is.
Dat maakt het erg vervelend als je later een andere appelsoort wilt toevoegen maar compatibel wil blijven met iedereen die niet alle soorten appels heeft of als je een appel verwacht maar weet dat geen appel gegarandeerd kan worden.
Dat is misschien vervelend in de context in C(++) maar dit kun je prima statisch oplossen (met annotaties of gewoon met een typesysteem waar null expliciet is). Neem bijvoorbeeld Haskell. Als je niet zeker weet dat er een appel is dan geef je een "Maybe Appel" in plaats van een "Appel", en dan weten je caller/callee van elkaar wat ze moeten verwachten. En een "Maybe Appel" kan geinstantieerd worden met een "Just Goudreinet" of met "Nothing", en de gebruiker van zo'n datastructuur moet die Maybe-laag expliciet uitpakken. Anders gaat de type checker mekkeren.

Dat is natuurlijk een taal op een heel ander niveau van abstractie waar je niet direct met memory pointers bezig bent, maar dat is toch de kern van de discussie.

In een taal als java heb je daar Nullable/NonNull-annotaties voor maar omdat die optioneel zijn, niet gestandaardiseerd, en ingevoerd als mosterd na de maaltijd, heb je in de context van java in de praktijk gewoon nooit garanties.

[Reactie gewijzigd door bwerg op 22 juli 2024 20:01]

Garbage Collectors lossen het échte probleem niet eens goed op
Het ligt maar aan wat je "het echte probleem" noemt, maar buffer/stack overflows mag je toch wel een probleem noemen... en die komen niet door verkeerd socket-management.

Verder vraag ik me af wat er dan zo slecht is aan try-with-resource-achtige constructies. Inflexibel, vast, maar voor de meeste toepassingen voldoende, en wel gewoon veilig en zonder kans op resource leaks. Ja, het kan altijd mooier.
Het ligt maar aan wat je "het echte probleem" noemt
Dat heb ik ook genoemd: het echte probleem is resource management.
Alleen memory management tackelen is net zoiets als een auto fabriceren, en dan alleen letten op de performance, waarbij je de veiligheid compleet links laat liggen.
Daar kom je mee weg als je iets bouwt waar jij alleen gebruik van maakt, maar kan problemen opleveren wanneer je op wil schalen.
Verder vraag ik me af wat er dan zo slecht is aan try-with-resource-achtige constructies
Het probleem is meervoudig:
1. Zulke constructies werken in termen van resource destruction op een "best effort" basis. Dat betekent dat achter de schermen zo'n block wordt vertaald naar een expliciete close() call. Vaak werkt dat okay, maar als je ooit met final clauses hebt gewerkt dan weet je dat dat geenszins een garantie is dat de resource behoorlijk wordt opgeruimd. En als dat niet zo is heb je dus een silent resource leak m.a.w. niet alleen heb je een resource leak, je weet niet eens dat dat zo is.

2. De bindings die gegenereerd worden door zulke constructies zitten lexicaal vast in dat block. Als alles wat je wil doen bv een file inlezen is werkt dat prima, maar als je er meer mee wil (bv de file binding opslaan voor gesegmenteerde verwerking) dan kan dat dus niet zonder de volledige inhoud eerst te kopiëren.

3. Bij een behoorlijke oplossing van het resource management probleem zijn zulke constructies dus compleet overbodig.

4. Het is een runtime oplossing voor iets dat prima met compile-time checks kan worden opgelost. Dat betekent niet alleen verloren performance maar ook verloren veiligheid.

En passant, mooie demonstratie van het blub paradox :)

[Reactie gewijzigd door Jeanpaul145 op 22 juli 2024 20:01]

Alleen memory management tackelen is net zoiets als een auto fabriceren, en dan alleen letten op de performance, waarbij je de veiligheid compleet links laat liggen.
Ironische vergelijking, want juist memory unsafety wordt in dit artikel aangehaald als grote bron van security-problemen.
Alleen memory management tackelen
En dat is dan weer een mooie demonstratie van een vals dilemma, want nergens wordt gesteld dat als memory management een probleem is, dat dan andere problemen niet opgelost hoeven te worden. ;)

Jij stelt dat memory management schijnbaar ondergeschikt is aan resource management, maar het is me nog steeds een raadsel waarom dan. Je legt uit wat er niet goed is aan resource leaks maar je maakt geen vergelijking met memory leaks, en je zegt ook niks over security-impact, waar dit artikel nou net over gaat. Juist in de hoek van security-problemen is memory management behoorlijk prominent. Zie de cijfers van Microsoft in het artikel. Dat terwijl leaks van files en sockets in de security-hoek doorgaans niet meer problemen veroorzaken dan denial-of-service door starvation. Nog steeds een security-probleem, maar vergeleken bij wat je soms met memory leaks kan aanrichten een stuk minder ernstig.

[Reactie gewijzigd door bwerg op 22 juli 2024 20:01]

Het is ook een geval 'kies je probleem'. In veel gevallen heb je genoeg aan de ingebouwde memory manager/ garbage collector. De meeste software is niet groot en complex genoeg om tegen die problemen aan te lopen.

Als je iets geheugenintensiefs bouwt is het misschien beter om geheugen zelf te beheren.

Kies je probleem.

De juiste problemen kiezen voor de situatie is waar het om gaat.
Dat is deels waar. Als je op abstractie of externe libraries vertrouwt echter moet je ook vertrouwen dat de implementatie zelf geen problemen heeft.

Voorbeeld als je bepaalde checks niet uitvoert omdat je denkt dat Rust die dingen afhandelt, dan als er een probleem is met de implementatie in Rust, heb je evengoed een probleem. OpenSSL is hier ook een goed voorbeeld van.

Uiteindelijk is er geen magie in die talen, mensen gaan nog steeds fouten maken. Echter iets herschrijven om te herschrijven zonder te denken of er een probleem is of mogelijk is brengt ook problemen met zich, het is meer aannemelijk dat je een fout maakt in je implementatie die voordien al opgelost was dan dat ergens diep in de code een onveilige memcpy tot exploitatie kan leiden.
Er zit inderdaad geen magie in die talen maar als daar een probleem in zit gaan er meer mensen (mee)kijken dan jij of je team alleen.
Het is meestal ook iets makkelijker een runtime te patchen dan een deploy van eigen code.
Inderdaad. Het is met elke taal mogelijk om onveilig te zijn. En in zo'n beetje elke taal kan je geheugenproblemen introduceren (ja, ook rust).
Het enige grote verschil is de hoeveelheid moeite.
Hoe zie jij het voor je een misbruikbare stack overflow in Rust te introduceren zonder unsafe te gebruiken?
Vanwaar de eis dat je geen unsafe mag gebruiken? Het hoort bij de taal, dus als een developer bad practices toepast kan je in Rust ook software maken die niet memory safe is. Het zal minder snel per ongeluk gebeuren als bij C of C++, maar het is geen garantie dat alles memory safe is als die taal wordt gebruikt.
ishet ook niet zo dat je elke taal die in eerste instantie veilig is opgezet met failsafes om te helpen vaak door onwetendheid en verkeerde informatie op reddit of stackoverflow uiteindelijk een wir war wordt van bad practices en uit eindelijk bijna niemand meer de daadwerkelijk veilige manier kan achterhalen?

Grotendeels door niet diep genoeg zoeken en de eerste google search result geloven
ishet ook niet zo dat je elke taal die in eerste instantie veilig is opgezet met failsafes om te helpen vaak door onwetendheid en verkeerde informatie op reddit of stackoverflow uiteindelijk een wir war wordt van bad practices en uit eindelijk bijna niemand meer de daadwerkelijk veilige manier kan achterhalen?
Nee. Een taal als C en C++ laten je als programmeur doen wat jij wilt, en kennen niet echt safe defaults. Erger nog, voor bepaalde operaties zijn de defaults best akelig in sommige compilers, zeker als je met ongeinitialiseerde pointers in memory gaat lopen wroeten. Maar dat is de filosofie van de taal: de programmeur moet gewoon weten wat hij doet en het is zijn verantwoordelijkheid.

Dat intussen 90% van de applicaties bij elkaar gegoogeld worden, en de meeste programmeurs dus geen flauw idee hebben wat ze nu echt aan het doen zijn, moet je misschien de taal niet verwijten. Je kunt beter die mensen verwijten dat ze zonder enige kennis iets in een riskante taal iets in elkaar knutselen.

Rust is daar een beetje een logische tegenreactie op. Links en rechts zijn er wat veiligheidshekjes geplaatst (alhoewel je daar overheen kunt stappen via unsafe commands) waardoor je de ergste ongelukken vermijd. Of dat voldoende is om iets veilig te noemen, twijfel ik aan. De kern, dat mensen dingen bij elkaar googlelen zonder echt te snappen wat ze doen, verhelp je niet. Dus ze maken andere fouten, wellicht minder erge, maar of het daardoor veiliger wordt weet ik niet.
De kern, dat mensen dingen bij elkaar googlelen zonder echt te snappen wat ze doen, verhelp je niet.
Met die instelling zaten we nu nog met COBOL of PHP 1.0. Die "veiligheidshekjes" zorgen ervoor dat je gewoon een hele klasse problemen voorkomt, en jouw reactie is dat programmeurs maar gewoon veel slimmer moeten zijn. Zo bezien zijn ook autogordels en veiligheidskooien een slecht idee, want het stimuleert alleen maar slecht rijdende bestuurders. Het verkeer zou veel veiliger zijn als we in auto's rijden die bij de minste of geringste fout in elkaar klappen.

Je mag het niet leuk vinden, maar de software-wereld werken juniors, mensen die typo's maken, en mensen die een foutje in een gigantische branch missen omdat ze het gewoon druk hebben. Stellen dat fouten komen puur omdat "intussen" mensen geen flauw idee meer hebben wat ze aan het doen zijn is een extreme oversimplificatie. Bovendien kunnen we nou niet zeggen dat "vroeger" software altijd beter was...
Rust is daar een beetje een logische tegenreactie op. Links en rechts zijn er wat veiligheidshekjes geplaatst (...)
Dat is wat te bondig samengevat.

Onderzoek naar veiligere en formeel verifieerbare talen is reeds sinds de jaren tachtig gaande. Heel veel talen hebben de vruchten van dit onderzoek gebruikt om inderdaad "Veiligheidshekjes" toe te voegen en niet meer dan dat.

Talen zoals Rust, Haskell en PureScript gooien het over een andere boeg: de taal is één groot veiligheidshek, waar er links en rechts wat achterpoortjes aan zijn toegevoegd.

En zelfs met die achterpoortjes zijn deze talen zeer goed statisch verifieerbaar door een type checking systeem, nog vooraleer de compiler er werk van maakt.

Dáár ligt de echte toegevoegde veiligheid: deze type checking systemen gaan veel dieper dan die van een C of C++.
Begrijp me niet verkeerd hoor.
Ik verwijd geen enkele taal dat gebruikers het onveilig in elkaar googlen.
Dat is inherent aan snel resultaat willen en niemand de tijd neemt of kan nemen om te achterhalen wat ze daadwerkelijk aan het doen zijn. Zij het gebrek aan intresse of deadlines
Vanwaar de eis dat je geen unsafe mag gebruiken?
Tja, als dit een serieuze vraag is dan moet je het advies nog eens doorlezen.
Stel je daarbij de graag wat men wil bereiken en stel je vervolgens de vraag waarom deze optie " unsafe" genoemd is.
Je zou inderdaad je hele Rust codebase in een unsafe block kunnen zetten en alleen maar pointers gebruiken. Dan is je codebase net zo veilig als C.

Maar dat is niet realistisch. Dat is niet hoe je Rust leert schrijven, en het unsafe blokje zorgt er in de praktijk voor dat deze code geisoleerd blijft. Je zult niet snel een pointer delen, terwijl je in C geen andere optie hebt.

Een taal die veilige code aanmoedigt, is veiliger dan een taal die dat niet aanmoedigt.

[Reactie gewijzigd door Wolfos op 22 juli 2024 20:01]

Tja, zo kan ik ook wel betogen dat een root user instellen zonder wachtwoord mogelijk is in ssh, dus ssh is onveilig. Zo is alles onveilig.

Is het standaard veilig, is de noodzaak om riskante dingen te doen minimaal, en gebeuren onveilige dingen alleen heel bewust en expliciet? Dat is waar veiligheid van een algemeen en configurabel stuk software om gaat.

[Reactie gewijzigd door bwerg op 22 juli 2024 20:01]

Omdat 1 issue weg is, zijn alle issues nog niet weg. Een developer in rust kan niet zomaar iets onveiligs doen, maar het is prima mogelijk om het bewust te doen. Of om iets te bouwen dat voor de rest van het systeem niet safe is.
Blind vertrouwen op Rust vanwege een aantal extra checks is in zichzelf al niet veilig.
Dit artikel gaat specifiek over memory unsafety, met daarbij een uitleg dat dit een grote en impactvolle subset van de security-problemen is. En de stack overflow waar roelboel het over heeft is daar weer een subset van. Die klasse van problemen is met een memory-safe taal gewoon weg.
Alleen als de taal zelf geen bugs heeft of een "unsafe" keyword in een of andere vorm. Minder is niet hetzelfde als weg.
Grappig dat het helaas wel kan door bugs in de compiler.
Vorige week was er een mooie thread over op readit. https://old.reddit.com/r/...lnerabilities_written_in/
Persoon heeft ook nog een paar YT's gemaakt https://www.youtube.com/watch?v=vfMpIsJwpjU

Maar het is een stuk lastiger en komt in het algemeen niet voor.
Ik ben zelf een groot Rust fan voor de hobby. Gebruik het voor embedded als web.
Hardware integratie. Een GPU kan direct naar memory schrijven. Zelfs als jouw rust game veilig is kan je GPU nog steeds buiten boundaries gaan via instructies die jij geeft. Dat is evengoed waar voor netwerk en andere hardware, inclusief de CPU. Moderne hardware en de kernel heeft hiervoor andere maatregelen, maar als je denkt dat je zomaar even de Linux kernel kan herimplementeren in Rust ga je meer dingen vergeten om die veiligheden in te schakelen.

Daarmee dat zulke herschrijving met zich meebrengen dat oudere hardware niet meer ondersteund wordt. Niet alleen is er geen test platform maar de maatregelen die moderne software (zoals Rust) veilig(er) maken zoals de NX bit en ASLR bestaan gewoon niet. Veel C programma’s kunnen direct veilig gemaakt worden door bepaalde flags aan te zetten in de compiler (zoals stack canaries en SafeStack, ROP en BTI) maar bepaalde dingen zijn gewoon niet beschikbaar op oudere platforms. En als nieuw taal is het gemakkelijk om die dingen dan buiten spel te zetten, C heeft dat voordeel niet en uiteindelijk zal Rust ook in hetzelfde probleem als C vandaag zitten, oude software en oude hardware.
Je geeft zelf al het antwoord: "door unsafe" te gebruiken. Uiteraard moet je daar "moeite" voor doen want als je het gebruikt gaan alarmbellen af, maar dat maakt het niet onmogelijk.
Rust zou in principe net zo snel moeten zijn als C.
Het enige dat rust toevoegt op C is zo ongeveer alle checks die de compiler doet en hoe expliciet je moet zijn daardoor.
De Rust compiler is jonger dan bijvoorbeeld gcc of llvm en mist misschien nog een paar optimalisaties of doet die expres niet omdat ze "unsafe" kunnen zijn.

Rust is een officiele kernel implementatie taal voor linux. Rust en C kan gewoon gemengd worden.

FWIW, ik ben geen rust programmeur. Ik heb er mee gespeeld als alternatief voor C maar vond het verschrikkelijk onprettig in gebruik. Vooral het "module" systeem vond ik verschrikkelijk. Het hebben van een package systeem weer een enorme plus.
Voor nu laat ik het links liggen als een syntax nachtmerrie. Ik zou willen dat er ergens een taal opduikt met een syntax net zo clean als die van .net of java, zonder honderd leestekens in speciale combinaties.
Ik zou willen dat er ergens een taal opduikt met een syntax net zo clean als die van .net of java, zonder honderd leestekens in speciale combinaties.
Waarom niet C# dan? Sinds NativeAOT kun je C# bijna overal inzetten en het is alleen in uitzonderlijke gevallen (intrinsics is C# nog niet goed in) significant langzamer dan C.
Ik programmeer bijna alles in c#. Maar het is een managed language. Zonder targets in embedded platforms. (uitzonderingen als meadow daargelaten). Dus geen .net op de rp2040. .net heeft ook een GC die native of niet de timing in de war kan gooien.
"De Rust compiler is jonger dan bijvoorbeeld gcc of llvm en mist misschien nog een paar optimalisaties of doet die expres niet omdat ze "unsafe" kunnen zijn."

* Rust gebruikt LLVM om te compilen naar machine code.
Rust gebruikt LLVM maar het totale LLVM is veel ouder. De rust frontend voor LLVM kan dus vele malen meer issues bevatten dan andere frontends. Rust was niet magisch ondersteund, daar moest wat voor bijgebouwd worden.
Hier heb je zeker gelijk in, alleen wou ik erop wijzen dat ze niet hun eigen hele compiler hebben gemaakt maar inplaats daarvan LLVM als start punt hebben gebruikt.
Vind jij C complexer? In C++ zit veel meer complexiteit zoals overloading, namespaces, inheritance etc etc.
En references en pointers, exceptions, lambda's, coroutines, templates (waar je hele complexe afgeleide types van kan krijgen).

C zit echt aan de simpele kant van het spectrum, en C++ aan de meest complexe kant.
"Betekent deze uitspraak van het witte huis dat deze softwarepakketten plotseling nu onveilig zijn en herschreven moeten worden?"

Heel kort gezegt ja, dat betekent dit en dat wordt all gedaan. Recent heeft Linux het mogelijk gemaakt om Rust te schrijven voor de kernel. En natuurlijk wordt de hele kernel nu niet overgeschreven naar rust omdat dat oneindig veel werk kan zijn, maar het is nu well mogelijk. Maar het betekent niet meteen dat het onveilig is, alleen is het ding dat we niet weten of het veilig is, we kunnen er van uit gaan dat het redelijk veilig is als er genoeg mensen die het begrijpen over heen zijn gegaan, maar nooit compleet.

Het ding met rust v. c/c++ is het is niet onmogelijk om in rust onveilige code te maken, alleen is het moelijker om het perongeluk te doen. Hierdoor is de kans lager dat dit perongeluk gebeurt en hierdoor zal het overall minder voorkomen, niet dat het helemaal niet voorkomt maar minder is altijd beter.

[Reactie gewijzigd door Stetsed op 22 juli 2024 20:01]

Heel kort: nee.
Windows en Linux zijn de enige keuzes voor een OS. Die zijn geschreven in C/C++ en dat gaat niet veranderen. Dat je wat aanvullend spul kan compilen voor linux in Rust klinkt leuk, maar ik verwacht niet dat het immens populair zal zijn en het echte low level spul zal altijd in C/C++ blijven.

Rust is ook nog steeds een relatief nieuwe taal die zich nog niet op lange termijn heeft bewezen. Pascal was vroeger ook een enorm populaire taal en nu nagenoeg dood. Java was op een gegeven moment extreem populair, maar dat is de laatste tijd toch echt wel afgenomen.

En zelfs Java met z'n garbage memory safe programmeertaal heeft niet voorkomen dat Log4J gebeurde. Je kan claimen dat de taal een hoop kan oplossen, maar je programmeer omgeving kan net zo goed je fouten oppikken door het gebruik van onveilige libraries te flaggen.
De Linux Kernel is geschreven in C, wat complexer is dan C++
In welk alternatief universum is C complexer dan C++ ?

C is juist ontzettend simpel, het is een van de simpelste talen die we hebben. C++ is daarentegen juist een van de meest complexe talen die er bestaat. Zo complex zelfs dat ik er van overtuigd ben dat er niet 1 persoon is die alle features van die taal onder de knie heeft. Zo'n beetje elke feature die een taal kan hebben is in C++ gepropt.
C complexer dan C++?

Hmmm, spuit 11 en 12...

[Reactie gewijzigd door servies op 22 juli 2024 20:01]

Ten eerste: C++ is vele malen complexer dan C (probeer er maar eens een compiler voor te maken. Alleen de templating is al turing compleet).
De hoofdreden dat Linux in C gebakken is, zal deels performance zijn, maar de belangrijkste reden is: het is behoorlijk portable (de hoofdreden dat C uberhaupt bestaat: Unix in assembly was geen doen meer).

Dan terug on-point: wat ik zie is dat C (en C++ in mindere mate) low-level programmeren mogelijk maken, maar hele generaties van studenten die uit een opleiding komen hebben nog nooit een interrupt van een DMA kanaal moeten onderscheiden. Of weten wat de latency van een context-switch is. Aan deze mensen C of C++ geven is niet erg handig, maar eerder gevaarlijk.
Een goede programmeur maakt inderdaad de fouten misschien niet die hier aan bod zijn. Veel programmeurs, helemaal degene die niet zijn getraind hierin, maken nog steeds die fouten. Het is niet zo dat het leren van de taal betekent dat je geen geheugenfouten maakt.

Een paar lessen geheugenveiligheid aan elke programmeur die ooit moet C/C++ in aanraking komt zou kunnen helpen. Het is echter makkelijker, en veiliger om controle in de taal in te bakken wanneer dat kan. Veel van de oudere talen zoals C/C++ zijn op zich niet zo moeilijk te leren, ikzelf heb ook wel eens in die talen geschreven. Maar ik denk dat het heel makkelijk is om het verkeerde aan te leren in die taal, of bepaalde lessen te missen als het om iets als geheugenbeheer gaat.
Gaan ze ook de talen die een C of C++ compiler hebben aanpakken ? Bv Go of Swift.
Vooral Swift zal wel een groot probleem zijn niet ?
Zo lang de compiler-bakker z'n huiswerk gedaan heeft valt het wel mee, zeker als ze ook nog actief bugjes pletten. Vergeet niet dat ongeveer alle compilers iets met C hebben, en heel veel goeie libraries ook. Gewoon omdat het bloedjesnel is mits goed gedaan, en al jaren bestaat.
Alleen is onze hardware zo krachtig en onze codebases zo groot (lees: niet foutloos) dat het meer dan de moeite loont om wat performance in te leveren voor betrouwbaarheid...
Ik vraag me echt af welke veiligheid je dan bedoeld.

aanvallen op de overheid zijn zo specialistisch dat deze altijd blijven bestaan.

Waar zijn ze bang voor?

Een andere oplossing is bv alles open source maken waardoor mensen met veel ogen er naar kunnen kijken (en daarmee de fouten eruit halen).

[Reactie gewijzigd door Kevinp op 22 juli 2024 20:01]

Open Source is niet inherent veiliger of onveiliger dan Closed Source.
Er zit bugs die jarenlang open en bloot in Open Source software zaten(heartbleed).
Het is niet voldoende dat iedereen kan naar kijken(veel ogen).
Ze moeten ook zin hebben en er iets van kennen. En dat laatste gaat verder dan enkel kunnen programmeren. Veel software is dusdanig specialistisch dat je het leek niet zomaar kan reviewen.
Je kan misschien een goeie browser engine geschreven hebben, maar de fijne details van videoSoftware zal je daarom nog niet snappen.
Open Source is niet inherent veiliger of onveiliger dan Closed Source.
Er zit bugs die jarenlang open en bloot in Open Source software zaten(heartbleed).
Het is niet voldoende dat iedereen kan naar kijken(veel ogen).
Ze moeten ook zin hebben en er iets van kennen. En dat laatste gaat verder dan enkel kunnen programmeren. Veel software is dusdanig specialistisch dat je het leek niet zomaar kan reviewen.
Helemaal mee eens. Ik heb wat complexe software open source ontwikkeld die ook nog redelijk gebruikt wordt ook, en krijg ook geregeld vragen van mensen die redelijk weten waar ze het over hebben (vaak omdat ze onze oplossing voor Raspberry Pi naar een Arduino of ESP32 willen porten). En op ons project wordt alles door minimaal een persoon gereviewed. Maar toch worden 99% van de bugs door mijzelf gevonden, of omdat iemand afwijkend gedrag rapporteert of omdat ik zelf met testen tegen iets aanloop. En soms zitten er best nare fouten in waarvan je denkt "dit had toch iemand moeten zien bij een code review"? Probleem is gewoon dat mensen erg moeilijk andermans code kunnen doorgronden, en vaak geen goede vragen kunnen/durven stellen.
Onzin. Denk aan de (beperkte) hardware in een beveiligingscamera die zelfstandig in staat is beelden te analyseren om zo personen of kentekens te herkennen. Dat soort hardware is er enorm bij gebaat om optimaal te presteren bij een laag verbruik.
En 'even' alles herschrijven in een andere taal levert geen problemen op wil je zeggen?

Zo proberen bedrijven al jaaaren van COBOL af te komen. Maar bewijs maar eens dat de nieuwe code in een andere taal net zo werkt als de oude code. Oh ja, en omdat de niet-techneuten geen verschil zien, gaan ze er natuurlijk geen budget/tijd voor uittrekken.
Of, als ze het wel doen, worden ze afgerekend op waarom het zo duur was; je ziet geen verschil.
Er wordt toch niet gesproken van 'even alles herschrijven'? Wel van een oproep om bij voorkeur talen te gebruiken met bepaalde features die de veiligheid kunnen verhogen.
Legacy spul zullen we altijd wel hebben, maar dat betekent niet dat we niet vooruit mogen kijken.
Dus dan ligt het voor de hand talen te nemen die menselijke fouten/slordigheden zoveel mogelijk beperken. In die zin speelt de gekozen programmeertaal dus wel degelijk een rol en dat is wat ze hier bedoelen met memory safe.
Klopt maar hier geldy wel de 80% / 20% regel. (Or eerder de 95% / 5% regel).
Kernels, AI en renderers moeten snel zijn,
maar heel veel code is niet zo performance gevoelig dat er een taal als C/C++ nodig is.
Ook veel onderdelen van OSen zijn niet zo prestatie kritiek, bijvoorbeeld UIs,
zodat er een veilige taal gebruikt kan worden. Windows is een goed voorbeeld :+ (slecht?)
Standaardiseren op een memory-safe alternatief zou fijn zijn.
.
En Rust heeft een veilig soort semi-handmatig memory management dat erg complex is,
dat soms alsnog soms uitgeschaled moet worden met het keyword "unsafe".
.
Iets als Python, Java of C# is vaak prima,
alleen de standaardisatie en licentie voorwaarden zijn dan weer een issue (laatste 2).
.
Momenteel zie ik TypeScript als goede optie, met JavaScript (ECMAscript) er achteraan hobbelend.
Alleen het (npm) package management moet dan veel strakker / meet gestandaardiseerd zijn
om misbruik via die weg te voorkomen.

[Reactie gewijzigd door Geekomatic op 22 juli 2024 20:01]

Licentie voorwaarden voor C#? Heb ik iets gemist? Roslyn is Open Source met een MIT license en Mono is volgens mij niet echt meer een ding. En snap ook niet helemaal waarom standaardisatie een issue zou moeten zijn?

Java daarentegen, ja nee gewoon niet doen.
Je hebt helemaal gelijk
Alleen het commerciele Microsoft zou toekomstig heel misschien
"Oracle-neigingen kunnen gaan krijgen.
Maar daar is momenteel absoluut geen sprake van. En het zou ook niet slim zijn, IMHO.
(ECMAscript heeft een wat neutraler instituut)
.
Als je taal *of* standaard libraries snel veranderen, dan geeft dat veel werk om bij te houden.
Dat is voor oa OSen die 20+ jaar bestaan ongewenst.

[Reactie gewijzigd door Geekomatic op 22 juli 2024 20:01]

Is er een gevaar bij bv Swift? Heb de indruk dat dat wel goed zit. Of….
Zoals Rollercoaster tycoon in de tijd hé , alles in assembler.
In die tijd waren compilers minder geoptimaliseerd dan nu. Tegenwoordig ga jij met handgeschreven assembler niet beter optimaliseren dan een C compiler. Hooguit kleine snippets, maar niet een hele codebase.

Ik heb het hier ooit over gehad met één van de ontwikkelaars van RCT2. Volgens hem was het vooral een nadeel.

[Reactie gewijzigd door Wolfos op 22 juli 2024 20:01]

Tja, je kunt programmeurs wel vertellen om bepaalde features niet te gaan gebruiken, maar met C++ is dat inmiddels wel heel veel. Je hebt ook te maken met oudere libraries en documentatie die die standaarden niet gebruiken, en inderdaad programmeurs die het met een oudere versie van de taal geleerd hebben.
Hoeveel ze er ook aan toevoegen, het blijft altijd een toevoeging. C++ zal nooit een moderne taal worden.

Het is dus altijd meer opletten (en dus gevoeliger voor bugs) dan een taal die met veiligheid in het achterhoofd ontworpen is.

[Reactie gewijzigd door Wolfos op 22 juli 2024 20:01]

Aangezien er gewoon veel slechte programmeurs zijn, kan je dus beter C/C++ vermijden. Die ga je niet verbeteren, maar je kan wel als eis neerleggen om een veilige taal te gebruiken.
In bedrijven hebben de programmeurs meestal maar een beperkte invloed op de keuze van programmeertalen die er worden gebruikt. Het is dus wel goed dat op deze manier het management van bedrijven wordt geholpen om betere keuzes te maken als het gaat om veiligheid van software.
Een taal beschikbaar stellen aan je programmeurs die niet veilig is, is al een fout beleid.

De meeste programmeurs zouden veel baat hebben bij een beperking tot Cobol of VB.Net. Dan schieten ze zich minder hard in de voet. Niet iedereen kan automatisch pre- en postcondities netjes doen, en dan per loop de invariant ook bijna instinctief correct bepalen. Dat vereist training en oefening die de meeste programmeurs niet hebben gekregen en ook nooit krijgen.
Voorheen kon ik leuk allerlei exploits gebruiken en iedereen hacken via MSN messenger. Nu is dat allemaal stukken moeilijker en bijna niet meer te doen. De mensen die dit nog willen moeten dus veel meer uit de kast halen..... En komen dan uit door bijvoorbeeld dit soort easy to make bugs in C en C++ te exploiten. Het blijft een rat race. De "kans" is dan aanzienlijk kleiner met Rust. Echter niet onmogelijk. Je moet het zien als evolutie en een rat race. Beide partijen blijven evolueren of je het wil of niet. Weet niet of jij dat wil dat dingen steeds beter worden? Merk bij oude software engineers dat alles maar stil moet blijven staan. Nieuwe inzichten zijn irritant want lastig aan te leren op oudere leeftijd.
Heel goed zo’n oproep, maar het is een gigantische klus om code te migreren van C-derivaten naar Rust.

Eén bedrijf dat daar actief mee bezig is, is https://tweedegolf.nl/en/blog - toevallig van Nederlandse bodem _/\_0.

Hun (edit1: in samenwerking met Ferrous Systems) rust implementatie van sudo is een concreet voorbeeld van hoe moeilijk en veel werk t kan zijn. Edit2: en dat rust geen garantie is op safe code, omdat er na een audit nog drie security issues waren.

[Reactie gewijzigd door MOmax op 22 juli 2024 20:01]

Hoezo? omdat het woordje "veilig" erin staat? Je moet kijken naar het toepassingsgebied. Voor de meeste applicaties zal een veilig gelabelde taal voldoende zijn (ik vind dit intrinsiek al een gevaarlijke aanname, in SW zitten altijd fouten). Voor performance is (inline) C of zelfs assembly soms echt een betere oplossing.

Het gevaar van dit soort generiek uitlatingen is dat iedereen er als makke schaapjes achteraan loopt en dat het een dogma gaat worden waardoor juist nieuwe problemen worden gecreëerd. Juist bij SW ontwikkeling wordt je geacht altijd kritisch te zijn en te blijven. Risico's manage je niet door ze uit te sluiten en daarna te denken dat ze niet meer optreden, beter treed je ze tegemoet en kijk je per situatie of als uitgangspunt een veilige taal het risico mitigeert.

Dit advies klinkt als iemand die een paar jaar met Rust heeft mogen spelen en gezien heeft dat een aantal lagere programmeertalen problemen kennen die deze persoon niet heeft kunnen tackelen en nu denkt de ideale oplossing in handen te hebben (dit zijn zeer bekende problemen waar meerdere mitigaties voor zijn).
Je hebt gelijk dat je altijd kritisch moet blijven kijken, maar de manier waarop je gelijk er een labeltje op plakt dat iemand het wel denkt te snappen, maar het niet snapt is mij te kort door de bocht.

Ik heb eerder de indruk dat dit advies op statistische analyse van onderzoeken (meta-analyse) is gebaseerd. Wat zijn de kosten van het extra werk van bijvoorbeeld een implementatie in Rust vs het veilig maken van een implementatie in iets als C++ en in welke mate vangt dit problemen af die kunnen worden uitgebuit?

[Reactie gewijzigd door ocf81 op 22 juli 2024 20:01]

maar de manier waarop je gelijk er een labeltje op plakt dat iemand het wel denkt te snappen, maar het niet snapt is mij te kort door de bocht.
Ik begeleid studenten en velen volgen dit soort generieke uitspraken als zoete koek. Misschien moet ik het wijten aan de opleiding die naast generieke uitgangspunten (wat ik op zich helemaal steun, ik werk medisch, veel standaarden dus een default uitgangspunt is vaak de beste weg) niet de moeite nemen om wel te controleren of de gekozen weg de juiste is. Ik werk in de embedded wereld en daar gelden (nog) steeds beperkingen als het gaat om ram/rom/performance. Dit staat bijna haaks op windows/apps programmeren. De kans dat een veilige taal past op een bepaalde micro is bijna nul. De "wat-moet-ik-nu" blik is toch altijd weer verrassend en de domper van C(++) programmeren wordt vaak gevolgd met een "kan-ik-de-opdracht-terug-geven" blik. Design patterns passen niet altijd, of vereisen een andere insteek (extra parameters bij selectie, waarom past quicksort niet en bubblesort wel? - hint:stack grootte-).

Richtlijnen of uitgangspunten sta ik helemaal achter mits daar ook een bewuste onderbouwing aan ten grondslag ligt voor een specifiek project. Een generiek statement als "... kan vermijden door te stoppen met het gebruiken van bepaalde programmeertalen ...". Maakt mij angstig want dan worden ook oplossingsrichtingen dichtgegooid. Ik werk ondertussen te lang in de IT om te beseffen dat er geen golden bullet bestaat en soms de lelijkste bullet, de beste oplossing is.
Dat is interessant. Heb je tips voor mensen die meer in de applicatie-wereld zitten om het perspectief van micro/embedded beter te kunnen begrijpen? Bepaalde projecten die leerrijk kunnen zijn?
Dat is interessant. Heb je tips voor mensen die meer in de applicatie-wereld zitten om het perspectief van micro/embedded beter te kunnen begrijpen? Bepaalde projecten die leerrijk kunnen zijn?
Zelf werk ik aan OpenRowingMonitor, wat gek genoeg als JavaScript applicatie gebouwd is (long story....). We ontwikkelen nu een zusje op ESP32 op basis van C++.

Wat je ziet is dat het lezen van een simpele sensor al een klus op zich is: we zien een puls (opgaande of neergaande flank), met wat trucs kunnen we dit op een RPi op een aantal nano-seconden nauwkeurig doen, zelfs met onderdrukking via een switch-bounce-filter van enkele nano-seconden nauwkeurig. Maar die truc is gebaseerd op een C++ library die dicht tegen de Linux kernel draait en rechtstreeks interfaced met de hardware. Doe je dat niet, dan is de signal bounce niet te onderdrukken en is het signaal te onnauwkeurig om echt bruikbaar te zijn.

Maar we weten ook dat er electronische en mechanische ruis is. We hebben 5 miliseconden om dat signaal van zijn ruis te ontdoen en om te zetten in zinnige data. Redden we dat niet, dan is het volgende signaal er al en dan klapt de hele applicatie in elkaar. We gebruiken hiervoor wiskundig best slimme maar CPU-intensieve algoritmes, dus een belangrijk onderdeel van het project gaat eigenlijk over het tijdig kunnen processen van dat signaal, niet over de onderliggende fysica. En dan eindig je bij pointer-gebaseerde datastructuren die in C++ toch heel veel makkelijker en efficienter te implementeren zijn. En dan kunnen mensen wel problemen hebben met pointers, maar de ESP32 implementatie werkt goed omdat die op standaard C++ pointer-based datastructuren gebaseerd is, en de JavaScript versie heeft een memory-leak....

En dan de data afnemers. BLE is in praktijk een bitsgewijs opgebouwde datastructuur waar je per veld bits aan elkaar plakt. Dit kan in de JavaScript versie in een netjes package, maar zelfs dan ben je nog letterlijk bits in een struct aan het duwen. ANT+ werkt net zo.

En we zien dat de ESP32 is een heerlijk device is, maar hij kan niet en een SSL-connectie vanuit de webserver runnen, en BLE aansturen. Het past gewoon niet omdat we de heapspace niet hebben. En we zien dat sommige zaken versimpeld moeten worden omdat je elke CPU-cycle wil benutten die je hebt, maar het ding niet moet overbelasten.

[Reactie gewijzigd door J_van_Ekris op 22 juli 2024 20:01]

Heb ook een keertje met JavaScript RGB leds aangestuurd. Je zag de jitter en garbage collector terug in de verlichting.... Sindsdien ben ik een beetje klaar met die magic byte talen met garbage collectors.

[Reactie gewijzigd door Immutable op 22 juli 2024 20:01]

Dat is moeilijk, er is nog zo'n grote wereld ....
  • koop voor een paar tientjes een STC/Atgema 16bit development board en ga daar eens mee spelen. Voor een klant hebben wij op een 8bitter ooit eens een elktriciteitsmeter gemaakt. Fotosensor en je bent nauwkeuriger dan de P1 meters. Lees die dat maar uit via een app 1x per dag of week (dit voorbeeld kent genoeg projectjes op het internet).
  • DSP development board, als je geluid leuker vind? Soms ook digitale beeldbewerking bij zwaardere modellen
  • stuk pittiger is werken met FPGA's, vak appart en compleet anders denken op de 10 nanoseconden nauwkeurig. In safety systems vaak gebruikt door zij snelheid, maar die zelfde snelheid wordt ook veel gebruikt in video bewerking. Lees als voorbeeld een live stream in en signaleer veranderingen en geef dit terug als bericht via je Linux of Windows omgeving.
  • Matlab/Simulink integratie in een Linux of Windows omgeving. Dit ken ik alleen in zakelijk professionele omgevingen. Bijvoorbeeld een stappenmotor aansturen (kan ook met micro's en DSP), maar als ze gecombineerd moeten werken ...
Bovenstaande kun je weer integreren met apps, veel van die interfacing is C(++) gebaseerd, geen mooie json api's maar dedicated byte based protocollen. Deze dedicated protocollen kun je vast met veilige talen aan de Windows/Linux kant implementeren maar de data komt van implementaties die niet dat label hebben. Het testen van deze interfaces is vaak ook nog weer met Phyton scripting ...

[Reactie gewijzigd door hamsteg op 22 juli 2024 20:01]

Gebruik je in deze tijd wel de juiste hardware?
Massaproductie / prijs gaat niet met ARM processoren. Daarnaast bewust 16bit STC/Atmega genoemd omdat hier een enorme userbase is met leuke kleine projectjes.
Dit advies klinkt als iemand die een paar jaar met Rust heeft mogen spelen
Daarmee wuif je nogal makkelijk weg dat enorm veel serieuze exploits veroorzaakt worden door categorieën van fouten die in veel moderne talen gewoon niet gemaakt kunnen worden. In de security-wereld is het echt niet heel gek om te horen dat je memory-unsafe talen moet vermijden als dat mogelijk is. Ook in memory-safe talen is het vaak wel mogelijk om calls te doen naar geoptimalizeerde low-level code zodat je het probleem in ieder geval lokaliseert, wanneer je er niet helemaal omheen kan.
dit zijn zeer bekende problemen waar meerdere mitigaties voor zijn
Mitigaties die niet waterdicht zijn en mitigaties waar de programmeur elke keer opnieuw aan moet denken. Ja, natuurlijk kan je veilige programma's schrijven in assembly maar je kan er ook onveilige programma's in schrijven, en voor een grote en belangrijke klasse van security-problemen is dat in een memory-safe taal gewoon niet mogelijk.
Sorry, vind ik een beetje onzin wat schrijft.

In alle talen zitten exploits. De oudere C(++) programma's/libraries hebben meer kans omdat vroeger domweg geen rekening werd gehouden met "hackers" en toen een incidentele overflow geaccepteerd werd. Tegenwoordig kun je, met de kennis van nu, zelfde kwaliteit van software leveren. De legacy is inderdaad groot, en daar waar mogelijk heel graag herschrijven in een veilige taal die gericht is op de kennis en ervaring van "open" internet gebruik want het gaat verder dan een beetje memory beheer - maar dit is niet voor alles de oplossing.

Er zijn tools beschikbaar die je bron-code inspecteren en net zo veilig code kunnen opleveren als "veilig talen". Deze tools integreren zeer eenvoudig in je build-pipeline en bewaken je kwaliteit per build-cycle. Ze zijn kostbaar die tools, dat klopt en dit is dus de afweging die je bij het begin van een project moet maken.
Er zijn tools beschikbaar die je bron-code inspecteren en net zo veilig code kunnen opleveren als "veilig talen".
Behalve dat die inspecties per definitie incompleet zijn omdat runtime-analyse inherent onberekenbaar is. Zo heb ik een python-developer mij ooit laten vertellen dat er ook prima IDE's zijn die statische type-analyse op python-code kon doen. Leuk, maar die analyse gaf zo nu en dan wel gewoon foute informatie omdat die analyse gewoon een fundamenteel foute aanpak is. Het is pleisters plakken ten opzichte van een fundamenteel veilige aanpak waarin deze klasse van problemen gewoon niet voorkomt.

Je zegt heel treffend dat het net zo veilige code kan opleveren. Je wil niet dat het "kan", je wil dat het gewoon gegarandeerd is, waar mogelijk.

Ik heb niet het hele rapport gelezen maar als ik zo even snel de relevante stukjes tekst doorscan is het ook gewoon een heel genuanceerde tekst waarin echt niet wordt gesteld "het kan altijd memory-safe". Nee, als je een bootloader schrijft kan dat niet in java, dat snappen de auteurs van dit rapport ook wel.

En zelf stel ik dat ook niet. Dus welk deel van wat ik zeg is nou precies onzin?
In alle talen zitten exploits.
Dit is natuurlijk een dooddoener, en het is gewoon niet relevant. Je wil de kans op fouten minimalizeren en de afhankelijkeheden op de individuele programmeur vervangen door tooling die zo ontworpen is dat het automatisch correct en veilig gaat, als dat mogelijk is, en voor zover dat mogelijk is. Dat er dan nog steeds mogelijkheden tot het maken van fouten overblijven is niet relevant in de discussie over wat je er wel mogelijk is.

Je stapt er ook nogal makkelijk overheen dat er ook gewoon andere redenen zijn dat mensen in C(++) programmeren, zoals dat ze gewoon die taal al kennen en dat dan maar gewoon als standaardkeuze nemen. Die groep zal langzaamaan steeds kleiner worden, maar voor die groep is wel de boodschap: doe dat niet.

[Reactie gewijzigd door bwerg op 22 juli 2024 20:01]

Je wil niet dat het "kan", je wil dat het gewoon gegarandeerd is, waar mogelijk.
Waar (niet) mogelijk is een grotere doorsnijding dan jij denkt en daar moet ook goede software voor geschreven worden waarbij overwegingen kunnen lijden tot de keuze van een taal die niet gelabeld is als veilig maar daarmee niet onveilig hoeft te zijn.
Waar (niet) mogelijk is een grotere doorsnijding dan jij denkt
Hoe weet jij wat ik denk over hoe groot die doorsnijding is? :P

Zit jij in een domein waar dit niet mogelijk is? Top, dan heb je die afweging gemaakt en kan je door met je leven. Ik zeg helemaal niks over hoe groot de sectoren zijn waarin dit het geval is.

Maar ben je iemand die gewoon C++ geleerd heeft en daardoor schrijf je nu gewoon applicaties in C++? Misschien wil je dat dan nog een keer heroverwegen.
Ah, we maken het persoonlijk. Zwakte bod. Inderdaad heb ik in een heel grijs verleden C++ geprogrammeerd maar ben afgestapt omdat dit toen niet voldoen object georiënteerd was en teveel coderen was. Ik werk op dit moment aan een systeem met 7-10 programmeer talen. Dit gaat van heel low embedded programmeren, naar heel high level modelleren en code genereren. Het gaat om de bewuste keuze, niet om het op een statief zetten van een taal.
Ah, we maken het persoonlijk.
Nee hoor. Ik wil niet zeggen dat dat specifiek op jou slaat. Daar slaat toch die alinea "zit je in een domein waarin dat niet mogelijk is" op.

De afweging die ik hier noem is gewoon het advies wat in het rapport genoemd wordt en wat best logisch en onderbouwd is. Geen memory-unsafe applicaties schrijven tenzij je daar een goede reden voor hebt, en "ik ken deze taal nou eenmaal" is niet die goede reden.

[Reactie gewijzigd door bwerg op 22 juli 2024 20:01]

Rust kent gewoon 'unsafe' waarmee je al die mooie checks weer overboord gooit. Opeens kun je weer hele ranzige dingen doen, die je niet moet willen, maar soms toch doet. Zeker als je met de buitenwereld kletst.
En dan maak je dus expliciet de keuze dat je dat doet, op plekken waar het echt nodig is. Je doet het niet gewoon standaard, of omdat je het niet doorhebt en een foutje maakt. En als je gewoon letterlijk je hele code base vol zet met "unsafe" dan zegt dat woord wel genoeg, die code is unsafe. :+ Dan weet elke andere developer die naar je code kijkt waar hij aan toe is.

Ja, natuurlijk kun je fouten maken. Je kan vanuit een programma typisch ook een shell openen en rm -rf /home/username doen. Dat wil niet zeggen dat je keuzes kan maken om die kans te minimaliseren, bijvoorbeeld door standaard en impliciet zo veilig mogelijke keuzes te maken.

[Reactie gewijzigd door bwerg op 22 juli 2024 20:01]

Risico's manage je niet door ze uit te sluiten en daarna te denken dat ze niet meer optreden, beter treed je ze tegemoet en kijk je per situatie of als uitgangspunt een veilige taal het risico mitigeert.
Je kan risico's behoorlijk verminderen als je begint met een memory safe taal en dan per situatie kijkt of performance het extra risico waard is. Secure by default.
Dit advies klinkt als iemand die
Ik geloof dat ze daar nog een vacature open hebben
Terzijde, sudo werd veel eenvoudiger geherimplementeerd in OpenBSD als "doas". Misschien was dat een beter vertrekpunt.
Doas is eenvoudig omdat het minder kan. De reden dat sudo zo “moeilijk” is om te herimplementeren is omdat het voor alle situaties sinds de 1970s kan ingezet worden, en het werkt. Als je single-user gebruiker op de computer bent en je wilt root, gebruik doas en veel mensen hebben niet meer nodig, maar de knul die denkt dat ie in een paar uur sudo kan herschrijven weet niets van de geschiedenis, systeem management en alle mogelijke gebruik van sudo en eenmaal je begint met bepaalde features zoals resetten van omgevingsvariabelen etc herimplementeer je dezelfde of andere bugs die je denkt op te lossen.

Uiteindelijk zijn deze talen goed voor 1 ding en dat is boundary checks op de variabelen dat je gebruikt. De andere 99% van bugs en exploits (denkfouten, aannames en shortcuts) worden hier niet magisch mee opgelost en je kunt die checks evengoed in C inbouwen.

[Reactie gewijzigd door Guru Evi op 22 juli 2024 20:01]

Misschien is minder juist beter? Dat is ook het principe achter Unix, simpele programma's aan elkaar rijgen voor complexere uitkomsten. En als er dan nog use cases zijn die echt met sudo moeten, dan heb je in elk geval je aanvalsoppervlak beperkt.
Dat is natuurlijk waar, sudo is ook met dit principe gebouwd. De sudo binary op zichzelf is maar een paar duizend lijntjes code, het is het volledige sudo ecosysteem (exec, edit, pair, plugins, configuratie) dat moet werken als je sudo wilt vervangen.
De andere 99% van bugs en exploits
In het artikel staat anders genoemd dat, specifiek voor microsoft, die andere bugs en exploits maar 30% zijn. ;)

"Je kunt die checks inbouwen", ja, nogal wiedes, je kunt elk probleem in elke taal omzeilen. Het punt van geautomatiseerde validatie is natuurlijk dat het gegarandeerd gebeurt. Met zo'n redenering kun je prima strings handmatig in SQL-statements plakken met een halfbakken zelfgeschreven escaping, in plaats van met prepared statements, want je kunt het zo goed doen. Als je developer echter een klein dom foutje maakt ben je echter wel de zak en heb je een exploit aan je broek hangen die je gegarandeerd had vermeden als je gewoon moderne standaarden had gevolgd.

Los van welke fouten je wel of niet kan maken speelt specifiek bij de klasse van memory-safety-bugs ook de impact mee. Met een beetje geluk kan een gebruiker door het complete geheugen van je applicatie graven. Als je de ergste exploits op een rijtje zet zitten daar ook relatief veel van dit soort problemen bij. Een duffe library om een plaatje te openen kan zo ineens compleet ongerelateerde wachtwoorden gaan uitspugen.

[Reactie gewijzigd door bwerg op 22 juli 2024 20:01]

Anoniem: 80910 @bwerg28 februari 2024 11:28
Moet memory niet gewoon encrypted worden? Dan ben je er toch ook? Zonder sleutel geen bruikbare data
En hoe ga je dat geheugen dan gebruiken als je programma het niet meer kan uitlezen?
Waar, maar elke grote verandering begint met een oproep. Dus in die zin is het begrijpelijk dat het Witte Huis zo'n oproep doet. Als ze het niet doen, blijven we tot in lengte der dagen in C en C++ ontwikkelen voor een groot deel van onze kritieke systemen.
Geen enkele taal is 100% memory safe, ook rust niet. Maaaar als je juist c++ programmers, en dus een moderne variant dan ben je net zo memory safe als rust.
In beide talen is het wel moeilijk dat zo te doen…

Maar waarom rust genoemd wordt is mij niet duidelijk, er zijn vele andere talen memory safe, ook Java, scale, kotlin en zou dan eerder voor Java of kotlin gaan dan rust. Kwa snelheid zal het voor het meerendeel niet uitmaken..
Rust is wel even andere koek dan de talen die je noemt. Rust heeft geen garbage collector en werkt met ownership die volledig door de compiler afgedwongen wordt. Je kunt alleen maar unsafe programmeren als je dat expliciet tegen de compiler vertelt. Als je er meer over wilt weten zou ik zoeken op Rust borrow checker.
Het rapport gaat alleen voor de ruimtevaart in op specifieke programmeertalen. Rust werd daar als potentiele vervanger voor C gezien.
Geen enkele taal is 100% memory safe, ook rust niet.
Doel je hiermee op het "unsafe"-keyword? Ja, die is natuurlijk niet memory-safe, maar als je die niet nodig hebt en dus niet gebruikt dan is het toch echt wel gewoon 100% memory safe. En hetzelfde geldt voor genoeg andere talen. Er is geen fundamentele reden waarom een taal niet memory safe zou kunnen zijn.

In veel high-level talen gaat het je gewoon niet lukken om een random stuk geheugen te interpreteren op een manier die totaal niet de bedoeling was. Ik kan zo snel geen memory unsafety in java, python of haskell of andere high-level talen bedenken anders dan het expliciet aanroepen van algemeen bekende unsafe hoekjes zoals JNI.
Maaaar als je juist c++ programmers, en dus een moderne variant dan ben je net zo memory safe als rust.
"Als je geen fouten maakt is het gegarandeerd veilig!"

[Reactie gewijzigd door bwerg op 22 juli 2024 20:01]

De geschiedenis heeft laten zien dat er zeker wel memory-safe talen zijn. De grote uitdaging zit in de implementatie en vooral de uitbreidingen die daar vaak bij komen.

De taal 'Pascal' heeft heel veel zaken die er voor moet zorgen dat ze netjes werkt in het geheugen. Helaas zijn de implementaties zoals TurboPascal zodanig dat er veel snellere code mee geschreven kan worden. Helaas wel door de taal uit te breiden met zaken die juist het netjes werken in het geheugen minder strak te controleren.

Daarna is Modula-2 gekomen. Die heeft best veel zaken opgepakt die in Pascal niet mogelijk waren maar wel nodig bleken. Het zou zo moeten zijn dat je in Modula-2 zelfs firmware en device-drivers kan schrijven zodat een kernel helemaal netjes in deze taal is geschreven.

En zo zijn er nog meer talen die op hun eigen manier beloven netjes met het geheugen om te springen maar daar met een ander zicht op de zaak toch weer minder netjes uit springen.

Naar mijn idee is het belangrijkste dat de software ontwikkelaars besef hebben van de mogelijkheden en de onmogelijkheden van de taal waarin ze werken en dat past op de opdracht die er ligt. En dat de code ook netjes wordt geschreven volgens de concepten van de taal. In C++ en C# kan je gewoon met c code hele programma's schrijven maar dat is niet de bedoeling. En als je een beetje je best doet, kan je in C ook object-oriented code schrijven, als je daar de juiste concepten maar blijft hanteren en consequent werkt.
En sudo is nou net zo'n tool waarvan ik denk: boundary overflows zijn waarschijnlijk niet de grootste bron van ellende. En inderdaad: er staan wel specifieke C achtige bugs in, maar een deel is gewoon denkfouten. De Rust versie (sudo-rs) staat er trouwens ook gewoon tussen. Herschrijven lost het nooit op: alle bugs uit verleden komen dan eerst gewoon weer terug. Lees anders deze even.
Anoniem: 80910 @latka28 februari 2024 11:46
Wel er is een cve geweest op het sudo commando. Herschrijven van scratch heb ik 4 jaar geleden gedaan met mijn framework en parser. Op een gegeven moment sta je voor de keuze, bug oplossen of herschrijven. Nu is herschrijven in het begin leuker, totdat je ongeveer dezelfde fouten weer tegen komt. Bij een volgende keer zou ik mijn oude code in een staat zetten waarbij alles nog functioneerd. Dat had ik nu niet. Achteraf kost het herschrijven veel tijd, een goede basis ook, is maar net wat je belangrijk vindt.
Rust is ver van memory safe, aangezien het toelaat om unsafe code toe te passen. Daarnaast is de borrow checker te omzeilen.

Rust is meer de template voor de veilige programmeertaal die nog niet bestaat; compile time checks voor veilig geheugen gebruik zijn zeker weten een stuk van de puzzel, maar zeker niet de hele puzzel.

Ik stel dat Go twee andere stukjes van de puzzel heeft. 1. Simpele eenvoudig te leren syntax en 2. boven alles - zeer anaal als het gaat om fouten afhandelen.
Rust is by default safe, en staat je toe om (op jouw verzoek!) unsafe te werken. Uiteraard kun je overal omheen, maar je moet er moeite voor doen, in plaats van dat je gewoon uit een nieuwe char *text even text[1337] opvraagt.

Uiteraard kan er nog genoeg aan verbeterd worden, maar het gaat de goede kant op, zonder een eigenwijze Go-mpiler die alles op zijn manier wil hebben, omdat het nou eenmaal zo bedacht is. We gaan wel zien welke taal we over 10 jaar hebben, maar ik zie het langzaam evolueren, en da's goed.
Het document roept niet op tot het gebruik van een bepaalde klasse programmeertalen en draagt zelfs Java als tegenvoorbeeld aan.

Het document roept wel op tot het gebruik van formele methoden.

Dat is de echte oplossing, maar voordat je dat toe kan passen moet je wel eerst een MSc in de informatica hebben behaald.

Uiteraard wordt het gebruik van formele methoden wel eenvoudiger als een programmeertaal "memory safe" is.
maar voordat je dat toe kan passen moet je wel eerst een MSc in de informatica hebben behaald.
Elke vorm van statische analyse die in moderne compilers ingebakken zit is toch echt een praktische en toegankelijke vorm van formele methoden. Idem voor runtime-analyse (zoals een JRE met garbage collection).
Elke vorm van statische analyse die in moderne compilers ingebakken zit is toch echt een praktische en toegankelijke vorm van formele methoden. Idem voor runtime-analyse (zoals een JRE met garbage collection).
Dat klopt, maar dat is niet waar het document het over heeft.
Waar het document het wel over heeft is de procedure waarin programmeurs weer ouderwets invarianten gaan opschrijven en bewijzen gaan leveren d.m.v. inductie en Hoarelogica.
Ik denk toch echt dat je een verkeerd beeld hebt van formele methoden. We hebben het over wiskundig bewezen en gesloten formalismen, zoals Tony Hoare en Edsger Dijkstra hebben ontwikkeld, en waar Alan Turing al in 1938 van heeft aangetoond dat dat niet automatisch gereconstrueerd kan worden.
Typeringssystemen zijn toch echt ook gewoon een onderdeel van formele methoden. Niet elke formele methode is onberekenbaar, en andersom is het berekenbaar zijn zeker geen criterium om een methode "niet formeel" te noemen. En dan heb je nog methoden die berekenbaar genoeg zijn om in de praktijk massaal op grote schaal toepasbaar te zijn, want sommige moderne talen hebben typeringssystemen die in het algemeen ook onberekenbaar zijn, ook al zijn ze in de praktijk wel bruikbaar.

Een compiler die klaagt dat je variabele een verkeerd type heeft of dat een statement onbereikbaar is net zo goed een toepassing van een formele methoden als een correctheidsbewijs. Dat zijn ook gewoon wiskundig bewezen en gesloten formalismen, anders doe je typetheorie en control flow analysis wel erg tekort. Idem voor memory safety, in deze commentaartjes wordt rust veel bediscussieërd en dat typeringssysteem komt toch echt ook gewoon uit de formele hoek (in dezelfde hoek als bijvoorbeeld separation logic).

[Reactie gewijzigd door bwerg op 22 juli 2024 20:01]

Typeringssystemen zijn toch echt ook gewoon een onderdeel van formele methoden. Niet elke formele methode is onberekenbaar, en andersom is het berekenbaar zijn zeker geen criterium om een methode "niet formeel" te noemen.
Een stukje van een formele methode toepassen, is zeker niet hetzelfde als werken op basis van formele methoden. Ik volg gewoon de definitie van de wereldwijd erkende veiligheidsstandaarden, zoals de CENELEC en IEC61508 familie, en die beschrijven zeer expliciet wat wel en niet als formele methoden beschouwd mogen worden. Range checkers en vergelijkbaar spul is leuk, maar dat is geen formele methode. Overigens zit de echte waarde van formele methoden in de wiskundige geslotenheid van het formalisme, en dat pas je geheel toe, of geheel niet.
En ik volg de wetenschappelijke definitie als verzamelnaam van verschillende onderzoeksgebieden, die helemaal niet zo strikt zijn als de specifieke standaard die je nu aanhaalt. Je pikt nu range checkers eruit maar dat is iets totaal anders dan een typeringssysteem of control flow analysis, waar ik het nu over heb.

Overigens is het concept van een onder- of overschatting ook gewoon heel gangbaar in de wiskunde en ook in veel takken van formele methoden die jij nu aanhaalt. Model-checkers die over control flow en data redeneren doen vaak genoeg een onderschatting om toch uitspraken te kunnen over problemen die te complex zijn om geheel door te rekenen, en dat kun je moeilijk geen formele methode noemen. De concepten van correctheid, volledigheid en berekenbaarheid zijn niet hetzelfde, en dat lijk je een beetje op één hoop te gooien. Turing heeft aangetoond dat veel problemen onberekenbaar zijn maar de klasse van die problemen zijn zeker niet hetzelfde als de klasse van problemen in formele methoden, of de klasse van problemen die gesloten zijn. De voorbeelden die ik aanhaal hebben ook gewoon gesloten varianten, typeringstheorieen zijn dat doorgaans en iets als reachability analysis ook. Die varianten bewijzen natuurlijk niet dat je programma in zijn in alle opzichten correct is (hoe simpeler de theorie, hoe minder die bewijst) maar dat doet geen enkele formele methode.

[Reactie gewijzigd door bwerg op 22 juli 2024 20:01]

En ik volg de wetenschappelijke definitie als verzamelnaam van verschillende onderzoeksgebieden, die helemaal niet zo strikt zijn als de specifieke standaard die je nu aanhaalt.
Dit is niet een enkele standaard. Het is een wereldwijd erkende familie van veiligheidsstandaarden, die elkaar wederzijds erkennen en per toepassingsgebied (spoorwegen, proces industrie, nuclaire toepassingen, automotive, medical, productiemachines) verdere invulling geven aan de toepassing van veiligheidsfuncties in software. Dit is de industrie zelf die samen met universiteiten, per sector, wereldwijd, hele harde afspraken gemaakt heeft wat men waaronder verstaat en wanneer men wat heeft toe te passen. En in veel gevallen zijn deze normen onderdeel van de wetgeving voor de industrie, en is de definitie dus zelfs juridisch bindend.
Je pikt nu range checkers eruit maar dat is iets totaal anders dan een typeringssysteem of control flow analysis, waar ik het nu over heb.
Maar analyse is achteraf. Het fundamentele punt is dat formele methoden ze juist in het ontwerpproces het ontwerp vormgeven, en daar de keten gesloten blijft. Achteraf analyseren zijn best leuke dingen gedaan, ook in Nederland (waar ik zelf ook zijdelings bij betrokken ben), maar het levert gewoon niet de bewijskracht op die je nodig hebt.
Overigens is het concept van een onder- of overschatting ook gewoon heel gangbaar in de wiskunde en ook in veel takken van formele methoden die jij nu aanhaalt. Model-checkers die over control flow en data redeneren doen vaak genoeg een onderschatting om toch uitspraken te kunnen over problemen die te complex zijn om geheel door te rekenen, en dat kun je moeilijk geen formele methode noemen.
Het is geen formele methode. Punt. Het staat niet op het lijstje, en is daarmee heeft de industrie besloten dat het niet bewijskrachtig genoeg is. Het is een wiskundige benadering die wat inzicht verschaft, maar het is geen formele methode. Redeneren dat het wel goed zal zitten is echt wat anders dan een sluitend wiskundig bewijs dat wat je roept klopt onder alle onstandigheden.
De concepten van correctheid, volledigheid en berekenbaarheid zijn niet hetzelfde, en dat lijk je een beetje op één hoop te gooien. Turing heeft aangetoond dat veel problemen onberekenbaar zijn maar de klasse van die problemen zijn zeker niet hetzelfde als de klasse van problemen in formele methoden, of de klasse van problemen die gesloten zijn. De voorbeelden die ik aanhaal hebben ook gewoon gesloten varianten, typeringstheorieen zijn dat doorgaans en iets als reachability analysis ook. Die varianten bewijzen natuurlijk niet dat je programma in zijn in alle opzichten correct is (hoe simpeler de theorie, hoe minder die bewijst) maar dat doet geen enkele formele methode.
Maar dat is juist wel wat de industrie en de wetgeving heel expliciet verwacht als je formele methoden roept: een wiskundig gesloten bewijs dat het programma in al zijn opzichten correct is. En er is maar een manier om dat in alle situaties te kunnen maken: door mensen tijdens het ontwerp.
Het is geen formele methode. Punt.
Wel. Punt.

Dit is gewoon een definitiekwestie. Jij hebt er één in de context van jouw vakgebied die volledige correctheid vereist - waarbij ik mij dan afvraag hoe je gaat bewijzen dat je specificatie volledig en correct is - maar dat is gewoon niet de enige definitie, en in tegenstelling tot wat je aanhaalt, op de universitaire afdelingen voor formele methoden ook gewoon niet de gangbare definitie. Die is veel breder. Een ISO-standaard voor een specifieke sector mag juridisch bindend zijn maar als je de term op zich juridisch bindend gaat noemen mag je wel even heel wat journals en conferenties letterlijk genaamd "Formal Methods" gaan aanklagen waar zelfs testen langskomt, maar ook zeker het verifiëren van specificaties in plaats van implementaties, het afleiden van specificaties, het bewijzen van deelspecificaties, het bewijzen van zeer beperkte eigenschappen, het benaderen van een bewijs, een onderschatting of overschatting geven van een bewijs, en ga zo maar door. Die term wordt gebruik als verzamelnaam van alle methoden, die formeel zijn, die als bouwsteentjes gebruikt kunnen worden in de zeer strikte definitie die jij "formele methoden" noemt. "Gebruik van formele methoden" wordt daar als term gebruik voor het toepassen van al deze zaken, of dat nou volledige correctheidsgaranties geeft of niet (meestal niet).

Je kunt een model-checker ook gebruiken om niet-veiligheidskritische systemen te verifiëren waar geen veiligheidsstandaarden van toepassing zijn, en dat wordt in die hoek gewoon een formele methode genoemd. Door de industrie en door academici (onder andere van afdelingen genaamd, je raad het al, "formele methoden"). Of jij nou "Punt" gaat zeggen of niet. :P
Maar analyse is achteraf.
Een correctheidsbewijs kan toch ook achteraf? Een typechecker is net zo achteraf als een correctheidsbewijs, het is alleen binnen een fractie van een seconde klaar omdat het een veel makkelijkere en beperktere correctheidseigenschap is dan een domeinspecifieke correctheidseigenschap.

[Reactie gewijzigd door bwerg op 22 juli 2024 20:01]

Het document roept wel op tot het gebruik van formele methoden.
Voor veiligheidskritische toepaasing is dit al decennia de standaard (zoals ook blijkt uit de CENELEC standaarden en de IEC61508 familie). Maar daar mag C/C++ gewoon met een vrij uitgebreide codeerstandaard die alles wat leuk is verbied.
Dat is de echte oplossing, maar voordat je dat toe kan passen moet je wel eerst een MSc in de informatica hebben behaald.
Valt tegen. Formele methoden worden lang niet zo intensief meer onderwezen als 30 jaar terug, waar het in Eindhoven de kern van het onderwijs vormde. Er komt tegenwoordig een handjevol afstudeerders van specifieke richtingen in Twente, Eindhoven etc.. En die hebben er dan aan gesnuffeld. De mensen die er echt mee uit de voeten kunnen zijn helaas nog steeds PhD's die in dat vakgebied zijn gepromoveerd. Dan heb je het over enkelingen in het afgelopen decenium.

De toegankelijkheid van dit soort methoden is helaas nog steeds erg beperkt, ondanks hele serieuze pogingen om dat te verbeteren.
Uiteraard wordt het gebruik van formele methoden wel eenvoudiger als een programmeertaal "memory safe" is.
Dat is een mythe. Totale kolder (en ik werk al 25+ jaar met formele methoden in veiligheidskritische toepassingen).

In praktijk verschuif je het probleem. Formele methoden zijn ontworpen om wiskundig gesloten te zijn. Als ontwerper kun je dan wiskundig bewijzen dat het ontwerp de eisen invult (mits deze wiskundig goed dekkend beschreven zijn).

Maar, je kunt formele methoden niet rechtstreeks uitvoeren in een PC of PLC. Dus er is een tussenstap voor nodig. In theorie zou dat rechtstreeks via een interne compiler kunnen gaan, maar vaak gaat dit via C/C++ of een andere taal. Dus het wiskundige model genereert de C/C++ code (of het PLC ladderdiagram, of wat er ook voor nodig is om het executeerbaar te krijgen), soms met nog externe code-injectie om specifieke equipment ook daadwerkelijk aan te sturen. En dat is waar het misgaat: die cross-compiler/injectie maakt fouten. Zelfs als deze SIL-4 gecertificeerd is. En zoals een collega in mijn werkveld structureel roept "Je hebt pas goed getest als je minstens een compilerfout vindt...:
Dat is een mythe. Totale kolder (en ik werk al 25+ jaar met formele methoden in veiligheidskritische toepassingen).

In praktijk verschuif je het probleem. Formele methoden zijn ontworpen om wiskundig gesloten te zijn.
Ik ben hiervan op de hoogte en ik ben tevens ook één van de (volgens u) weinigen die nog wel goed onderwezen is in het gebruik van formele methoden. Mijn universiteit, staat dan ook niet op de door u genoemde lijst.

Echter, het is wel degelijk zo dat het gebruik van memory-safe programmeertalen het gebruik van bepaalde formele methoden fors eenvoudiger maakt. Stappen die men bijvoorbeeld over kan slaan is het leveren van bewijzen voor de onmogelijkheid van het schrijven buiten bepaalde arrays of in bepaalde registers, geheugens die vollopen en bepaalde foutcondities omdat die door de taal zelf al worden afgevangen. Dat scheelt erg veel stappen in de bewijsvoering.

[Reactie gewijzigd door vliegendekat op 22 juli 2024 20:01]

Vertel, wat is het verschil tussen een formele methode en informeel?
Vertel, wat is het verschil tussen een formele methode en informeel?
Zoals @J_van_Ekris hierboven aangeeft: Formele methoden zijn wiskundig gesloten manieren om bewijzen te leveren over de correctheid van een programma.

Daardoor is het zo dat als je een programma via een formele methode als correct kan bewijzen, in ieder geval op software-niveau vrij van bugs en fouten.

Dat neemt niet weg dat er nog steeds fouten in de specificaties en/of de hardware veroorzaakt kunnen worden, maar het komt dan in ieder geval niet meer voort uit domme programmeerfouten of aannames in de code die geschreven is.

Het is vaak echter grotendeels handwerk.
Ik vind het nogal irritant dat C en C++ over dezelfde kam geschoren worden. Een redelijk moderne stijl van C++ schrijven komt een stuk dichter in de buurt van iets wat redelijk veilig is. Hopelijk wordt dit gebruikt als argument om wijzigingen door te voeren in de C++ spec in plaats van een puur negatief verhaal.
Volledig met u eens. In C komen de volgende problemen voor:
  • dangling pointers
  • memory leaks
  • buffer overruns
Met modern C++ zijn deze zaken opgelost en kom je ze niet meer tegen. Het grote probleem blijft gebrek aan kennis bij sommige programmeurs. Als je niet goed weet wat je moet doen blijf dan weg van C / C++.

Het performance verlies van alles checken mag overigens niet onderschat worden. Compilers kunnen erdoor slechter optimaliseren. Dure zaken als alle pixels uitlezen uit een image worden hopeloos als elke benadering gecontroleerd wordt. Met grote applicaties wil je niet dat over alles een performance multiplier overheen wordt gehaald.

Ruw statement van de Amerikaanse overheid.
Eens dat het met modern C++ makkelijker gemaakt wordt om veilige code te schrijven, maar het vereist nog steeds kennis en discipline. En in mijn ervaring een niveau van kennis en discipline die je slechts van een heel beperkt aantal ontwikkelaars kunt verwachten.

In talen als Rust wordt het je gewoon onmogelijk gemaakt om dangling pointers en memory leaks te hebben, zelfs als je maar beperkte kennis en discipline hebt.
[...]memory leaks[...]

Met modern C++ zijn deze zaken opgelost en kom je ze niet meer tegen.
Nog steeds mogelijk hoor... :p
#include <vector>
struct V : std::vector<V> {};

int main() {
  V v;
  v.emplace_back();
  v.swap(v.front());
}

[Reactie gewijzigd door RayNbow op 22 juli 2024 20:01]

Heel vaak kom je wel uit op e.g. C gebaseerde bibliotheken en API's. Dan is het erg leuk dat C++ zelf veilig is, maar dan moet je toch data omzetten naar char* en weer terug. Als je pech hebt zit je nog met veel string representaties te stoeien.
Het kan wel, maar het gaat niet vanzelf goed en vereist discipline van de programmeur. De oproep is hier dat je het makkelijk moet maken om by default het juiste te doen.
zijn memory safe programming languages beperkter dan huidige talen zoals C en C++?
Ja en Nee. Ze kunnen alles wat c of c++ kan. Er zitten gewoon extra checks in een runtime of compiler of allebei.
Dus ze staan minder makkelijk fouten toe of constructies die makkelijk tot fouten leiden. Is dat een beperking? Ik vind van niet, want het zijn dingen die je in talen als c of c++ ook niet moet doen.
En veel van die dingen kan je soms in die safe talen alsnog als je expliciet aangeeft dit je het echt wil.
Vooropgesteld dat ik Rust niet ken, maar ik heb wel al meer dan 15 jaar ervaring met C++.
Wat er met name fout kan gaan in C/C++ is dat je gealloceerd geheugen niet vrijgeeft of dat het niet meer "bestaat" als je het gebruikt.
Echter dat is met name iets wat resulteert in een crash of het triggert een exceptie die afgehandeld moet worden.

Wat er ook nogal makkelijk fout kan gaan is dat heel veel code alleen maar kijkt naar of ze een '0' character tegenkomen bij het parsen van een string en niet zozeer naar de verwachte text-lengte.
Hiermee kun je ook vrij specifieke exploits toepassen, al is dit wel een stuk lastiger te exploiteren.
Dit zou je echter redelijk goed kunnen afschermen door dergelijke checks in te bouwen in classes die je dan gebruikt ipv. elke keer low-level memory access te doen.
Dit vergt echter wel wat discipline en tijd/moeite wat zich niet meteen terugbetaalt (op de langere termijn wel, maar helaas werkt de praktijk niet zo)

In principe heb je veel directere toegang tot geheugen, maar je OS zou dat als het goed is redelijk af moeten kunnen schermen dat je niet zomaar bij geheugen van andere processen kunt.

Maar volgens mij is dat niet wat C/C++ nu echt "onveilig" maakt.
Volgens mij is het meer dat je vrijwel nooit geheugen "leeg maakt" voor en na gebruik en dat er zodoende dus geprepareerde data ergens kan staan, of vertrouwelijke data langer in het geheugen blijft staan dan nodig is.
Dat wissen van geheugen voor het vrijgeven kost wel CPU-cycles en maakt het dus trager.
Neemt niet weg dat voor heel veel toepassingen die extra vertraging niet echt een issue is met de hardware van tegenwoordig.
Niet vrijgeven is niet zo'n probleem (crasht je app na een tijdje, meer niet). Double free (ofwel: geheugen is vrijgegeven terwijl je het niet verwacht als opmaat naar een: nog maar een keer free-en dan) is wel degelijk exploitable. GC talen zijn echt beter dan programmeurs hierin.

En C++ strings doen toch geen archaïsche C-strings meer mag ik hopen. Dat was de hele belofte van C++.

De meeste C exploits zijn vooral gericht op meer privileges krijgen. De meeste low-level tooling (op Windows en Unix) is in C-achtige talen gemaakt. Als je die tools zo gek krijgt om ergens in de EIGEN adresruimte iets om te klapperen zodat je een eigen shell kunt starten met de privileges van de tool in kwestie ben je weg.
Laten heel veel netwerk diensten nou ook in C/C++ geschreven zijn en het feest is compleet.
[...]
En C++ strings doen toch geen archaïsche C-strings meer mag ik hopen. Dat was de hele belofte van C++.
[...]
std::string doet wel aan checks enzo, maar eigenlijk alles wat met een char pointer werkt zou potentieel de fout in kunnen gaan. Best wel veel code geeft niet eens een lengte mee, dus moet je er maar vanuit gaan dat de string eindigt met een '0' binnen het gealloceerde blok.
std::string is geen onderdeel van C++, het is wel onderdeel van STL, dus best wel veel gebruikt.
Het is in elk geval wel een goed voorbeeld van waar ik op doelde met classes waarin je de memory-allocatie geregeld hebt en daar dus minder over hoeft na te denken.
Zitten nog steeds dingen in waar je je goed bewust van moet zijn, zoals dat een iterator (strict genomen niet veel anders dan een pointer naar een element van je array) invalid kan worden, zonder dat dat makkelijk te checken is.

Persoonlijk ben ik meer voorstander van het gebruik van (const) references naar objecten en classes bij de aanroep van een functie. Daarmee leg je de verantwoording van het alloceren en opruimen van een object daar waar de functie aangeroepen wordt en voorkom je ook dat dingen onnodig omgezet moeten worden.
Bijv. iets is al een std::string, maar de functie vraagt om een const char* en in die functie maken ze er weer een std::string van.
Anoniem: 80910 @TD-er28 februari 2024 11:55
In php heb je array_key_exists, dan heb je geen pointer probleem meer
je OS zou dat als het goed is redelijk af moeten kunnen schermen dat je niet zomaar bij geheugen van andere processen kunt
Maar wel bij alles in je eigen proces. Het is wel jammer als je webserver een foutje maakt in de input-validatie en dat hij daarna ongerelateerde data naar buiten begint te spugen of het de control flow beïnvloedt. Ja, meestal krijg je gewoon een segfault, maar dat is gewoon prijsschieten. En het uitbuiten van dit soort hoekjes is tot de bodem uitgezocht.
De NSA heeft dezelfde aanbeveling al eerder gegeven. Ik denk dat het Witte Huis die aanbeveling eigenlijk overneemt.

Hier de link met daarin een link naar het volledig rapport waarin oa. wordt beschreven welke talen memory safe zijn:

https://www.nsa.gov/Press...secure-software-products/
Er wordt natuurlijk snel vergeten dat ook Rust niet altijd 'safe' is. Uitendelijk is een computer tegenwoordig helemaal memory mapped en dus moet je naar een specifiek memory adres lezen/schrijven om met de hardware te interageren. Dat kan niet 'safe' gemaakt worden en daarom biedt Rust ook het unsafe block aan.
Interessant, dit zal wel een opsteker worden voor Mozilla aangezien die Rust hebben uitgevonden en beheren.

Op dit item kan niet meer gereageerd worden.