Torben Mogensen header

Defensiv programmering

Ved DIKUs jubilæum var en af talerne Brian Rakowski fra Google, som snakkede om Chrome browseren. Han nævnte blandt andet, at et af fokusområderne var øget sikkerhed, så jeg spurgte ham bagefter, om de havde overvejet at skrive Chrome i et managed sprog, altså et, der sikrer mod bufferoverløb, pegere til deallokeret lager, osv. Hans svar var, at man havde valgt C på grund af effektivitet, men at man havde sørget for at tilføje checks over det hele.

Det var egentligt et ret forventeligt svar, men argumentet holder ikke rigtigt: Hvis man i koden eksplicit indsætter en masse køretidscheck for at undgå bufferoverløb m.m., så er effektivitetsfordelen ved C tabt. C's effektivitet bunder alene i, at man undlader al form for køretidscheck og tillader adgang til de binære formater af alle værdier (så man f.eks. kan bruge bitmasker på pointere og strings). Begge er ting, der er potentielle sikkerhedshuller, så hvis man vil sikre mod angreb, programmerer man defensivt: Alle steder, hvor der laves potentielt usikre operationer, indsætter man manuelt checks, der forhindrer de mest oplagte angreb.

Disse ekstra checks koster det samme uanset om de er indsat af oversætteren i et managed sprog eller af programmøren i C. Så fordelen ved C er til at overse. Og man mister nogle af fordelene ved *managed *sprog:

  1. Der laves altid check, hvor det er nødvendigt, så man undgår, at programmøren glemmer nogle tilfælde.
  2. Oversætteren kan gennem statiske analyser, typer eller sproginvarianter garantere fravær af visse potentielle problemer og dermed undgå at generere køretidstest, hvor de ikke er nødvendige. Den slags globale egenskaber er svære for en C programmør at overskue, og man risikerer, at de forsvinder, når der ændres andre steder i koden. Oversætteren vil lave analysen igen ved genoversættelse, så der indsættes nye test, hvor det er nødvendigt, men det er svært for en programmør at finde ud af, hvor der er behov for nye test.

Chrome har i stor stil brugt sandboxing for at reducere faren: Når man ikke kan garantere, at koden ikke kan lave grimme ting, så mindsker man konsekvenserne ved at det sker ved ikke at give koden adgang til brugerdata. Men det virker kun i begrænset omfang: Nogle gange skal browseren have adgang til filsystemet.

Så hele ideen om, at man får hurtigere kode ved at bruge C frem for managed sprog, mener jeg er en misforståelse. Den potentielle fordel ved C's lavniveautilgang mistes så snart, man er nødt til at lave defensiv kode. Endvidere er C's effektivitet i forhold til managed sprog overdrevet og skylden i høj grad, at man har brugt OO sprog til sammenligning: Den klassiske OO model med nedarvning, dynamiske metodekald og brug af downcasts giver et overhead, som er uafhængig af, om sproget er managed eller ej.

Så hvis du griber dig selv i at kode defensivt, når du programmerer i C, så burde du overveje, om det ikke var bedre at bruge et andet sprog.

Kommentarer (36)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
#1 Anonym

Det er nogle rigtig gode pointer, som Torben fremlægger. Jeg vil selvfølgelig slå et slag for sprog, der beviseligt ikke har kørselsfejl såsom buffer overløb, aritmetisk overløb osv.

En anden ulempe ved defensiv programmering er, at det kan være svært at opnå tilstrækkelig høj dækning med tests, da det kan være umuligt at få softwaren i en tilstand, der skal detekteres af de defensive konstruktioner.

  • 0
  • 0
#2 Poul-Henning Kamp Blogger

Jeg er enig noget af vejen.

Der er stadig himmelvid performanceforskel på at bruge C med defensiv kodning og på at bruge hvad Torben kalder "et managed sprog" fordi sidstnævnte typisk kommer slæbende med alt for meget ideologisk og runtime bagage man ikke kan slippe for.

Hvis nogen satte sig for at skrive et moderne systemprogrammeringssprog til afløsning af C ville det være alle tiders.

Syretesten er om du kan skrive en POSIX kerne i sproget eller ej: Hvis du ikke kan, er det ikke et systemprorammeringssprog.

Poul-Henning

  • 0
  • 0
#4 Jesper Louis Andersen

Problemet med C og til en hvis grad C++ til gængs applikationsudvikling er at du ender med at skulle skrive en hel runtime selv. Når du så er færdig med dit program, så har du selv udviklet en variant af garbage collection, noget safe-strings, en række konventioner for return af multiple parametre og så videre.

Et sprog som eksempeltvist Java giver dig en runtime som automatiserer meget af det arbejde for dig. Til gengæld er der ikke noget ta-selv-bord: Du bliver tvunget til at æde hele runtimen som udgangspunkt. Hvis dit problem passer nogenlunde ned i stikket, ja så virker det fortrinligt. Hvis ikke, så kan du enten give op og ty til C, eller du kan arbejde dig rundt om runtimen når det er nødvendigt.

Jeg kan ved gud ikke se hvorledes det skulle være absolut umuligt at skrive en operativsystemskernel i Ocaml, I Scheme, I Common Lisp eller i Java. Det største problem er at du skal tilgå hukommelsen eksplicit visse steder, men det kan sagtens coeksistere sammen med en garbage collector og du kan sagtens tilføje et passende stykke library som lader dig ligge hukommelsen ud eksplicit. I sidste ende er der bare tale om de samme assemblerinstruktioner. Mirage-projektet (http://www.openmirage.org/) benytter C til deres stubs, simpelthen fordi det er nemmest, men ellers foregår det meste i ocaml. Anil Madhavapeddy som sidder på det projekt er OpenBSD-udvikler, har skrevet sin egen SSH implementation i Ocaml[1] og må et eller andet sted siges at være i stand til at gøre det.

Hvorfor eksplicit POSIX-compliance? For at være en afart af en UNIX-derivative?

[1] Naturligvis var Ocaml-implementationen en 30% hurtigere end OpenSSH i C, simpelthen fordi han kunne udvikle den hurtigt og så optimere hvor det galdt. Slå Anils phd op hvis du har interesse.

  • 0
  • 0
#8 Anonym

PHK,

Syretesten er om du kan skrive en POSIX kerne i sproget eller ej: Hvis du ikke kan, er det ikke et systemprorammeringssprog.

Det er gjort for mange år siden i MPE/iX.

MPE var et proprietært OS, men de færreste ved, at det også var POSIX compliant, da MPE var rettet til forretningsstrategisk EDB, hvorimod POSIX mere var for 'nørder'.

MPE var oprindeligt skrevet i SPL, og med 'X' æraen, var det MODCAL (Pascal + modifikationer).

Via compiler directives valgte man om man forholdt sig til ren Pascal, eller (MODificeret Pascal), men forskellige niveauer.

En ren Pascal sikrede, at man aldrig røg ud i buffer overflows, eller stack overflows, men gav man et direktiv (modcal), valgte man selv at sætte visse schecks ud af kraft.

Intet var nemmere end at lægge en HP3000 ned - man skulle bare skrive noget 'snot' i et globalt datasegment, helst i det segment, der tilhørte OS'et.

Men for at gøre det, skulle man have privilegier, og aktivt overrule standard privilegier.

"Capabilities" var en stærk feature, som i mine øjne stadig klassificerer MPE som 'lysår' foran nutidige OS'er. http://www.robelle.com/smugbook/capablty.html

R.I.P for MPE, men når vi snakker forretningsstrategisk EDB (og sikkerhed) er/var det stadig langt forud for vor tid.

  • 0
  • 0
#9 Anonym

'Managed' sprog er kun for dem, der ikke evner at styre deres memory((de-)allokering), og min tese er: "Hvis man ikke evner at styre sin memory, hvordan i alveden skulle man forvente man kan styre (kritiske) data!"?

  • 0
  • 0
#10 Torben Mogensen Blogger

'Managed' sprog er kun for dem, der ikke evner at styre deres memory((de-)allokering)

Ligesom sprog af højere niveau end assembler er for folk, der ikke evner at holde styr på, hvilke registre, de har brugt?

Selvfølgelig kan man lave sikre programmer med manuel lagerstyring, men for komplekse systemer kræver det så meget mere arbejde, at det ikke kan betale sig. Og når en anden senere overtager koden og skal lave udvidelser/tilretninger, så ryger sikkerheden nemt ud med badevandet, da den nye programmør ikke kender de invarianter, der sikrer mod (de)allokeringsfejl. Man kan også sagtens lave det hele i assembler.

  • 0
  • 0
#12 Torben Mogensen Blogger

Mit hovedeksempel var browseren Chrome, så argumenter om, at man ikke kan skrive en operativsystemskerne i et [i]managed[/i] sprog er (uanset validitet) egentlig ikke så interessante i den sammenhæng.

En af mine kæpheste er netop, at man bruger C til alt mulig andet end operativsystestemskerner. Det er de færreste C programmer ude i verden, der rent faktisk går ned og roder med [i]interrupt vectors[/i], [i]memory-mapped i/o[/i] osv.

  • 0
  • 0
#13 Poul-Henning Kamp Blogger

Jeg vil gerne give dig ret i, at C bliver brugt steder hvor der findes bedre alternativer.

Men moderne sikkerhedspartitionering er overhovedet ikke slået igennem i nogen sprog endnu, så jeg har svært ved at se hvilket alternativ de havde til C i Chrome.

Der er rigtig mange sprog der ikke fatter at det er brugbart at kalde fork() uden straks at kalde exec() bagefter og derfor er konstruktionen af jails/sandboxes med shared data i subprocesser lig akut diarre for de fleste højniveau-sprog.

De få sprog der kan klare denne finesse, er beregnet til fejl-tolerant videnskabelige udregninger på massive shared-memory cluster computere og egner sig overhovedet ikke til at skrive systemkode med.

C er det eneste portable sprog, der ikke blander sig I hvad du laver, men blot giver dig adgang til at udtrykke hvad CPU'en skal give sig af med og derfor er det default sproget, hvergang man bevæger sig udenfor sprogdesignernes fantasi.

Derfor slipper vi ikke af med C og derfor er ISOs kustode-agtige holdning til sprogets udvikling tåbelig snæversynet.

Poul-Henning

  • 0
  • 0
#14 Donald Axel

Det er ikke kun et spørgsmål om hvilket sprog man bruger, PHK kommer med alle de væsentlige argumenter undtagen ét, som jeg synes er lige så væsentligt:

Det er muligt at skrive rigtig læselig kode med C sproget - det sker ikke automatisk, men den, der vil, kan lære at skrive fejlfri, læselig kode, og i min ringe erfaring langt mere læselig end fx. PERL.

  • 0
  • 0
#15 Lasse Reinholt

Hej Torben,

I C kan du nøjes med at validere en funktions argumenter og/eller inputdata. Hvis valideringen er tilpas grundig (enten løbende eller initielt), så kan resten af koden eksekvere med fuld hastighed uden checks.

Dette kan lade sig gøre op til et vist niveau af kompleksitet. Dekompression med zlib har vist sig at indeholde exploits mens lzjb (i ZFS filsystemet) har vist sig tilstrækkeligt simpelt.

Visse algoritmer kræver endda beviseligt ingen validering overhovedet. Det gælder fx kryptering, kompression af data (ikke dekompression) og de fleste typer af matematiske beregninger.

Managed sprog vil i begge tilfælde indsætte langt, langt flere checks end nødvendigt.

Så når du skriver "den potentielle fordel ved C's lavniveautilgang mistes så snart, man er nødt til at lave defensiv kode.", så er det virkelig en halv sandhed.

  • 0
  • 0
#17 Lasse Reinholt

Hvordan validere man i C at input data rent faktisk er en nul-termineret streng som hverken peger på uallokeret/deallokeret hukommelse eller mangler den afsluttende nulterminering?

Du skal jo blot nøjes med at acceptere pointere fra pålidelige kilder. Giv et helt konkret eksempel på, hvor pointeren skulle komme fra, så vil jeg svare. Det er lidt som et induktionsbevis:

1) Hvis noget data er korrekt og overgives som argument til korrekt kode, så vil det resultere i korrekt outputdata.

2) Det første data er korrekt

  • 0
  • 0
#18 James Avery

Du skal jo blot nøjes med at acceptere pointere fra pålidelige kilder. Giv et helt konkret eksempel på, hvor pointeren skulle komme fra, så vil jeg svare. Det er lidt som et induktionsbevis:

1) Hvis noget data er korrekt og overgives som argument til korrekt kode, så vil det resultere i korrekt outputdata.

2) Det første data er korrekt

Troller du? Jeg tror, du troller. :)

  • 0
  • 0
#19 Lasse Reinholt

Troller du? Jeg tror, du troller. :)

Nej. Der findes ingen måde at gøre det, som Baldur efterlyser i ISO C (der findes dog OS specifikke metoder).

Men der er heller ikke behov for det i de tilfælde, hvor man kan garantere, at sådanne pointere ikke KAN forekomme. Lad os tage et eksempel:

char dst[100]; dst[10] = 0; size_t t = fread(dst, 1, 10, ifile); if(t != 10 || strlen(dst) < 10) abort("Streng for lang eller kort!");

Det kan her garanteres, at dst er 0-termineret og befinder sig i gyldig hukommelse. Baldurs krav er opfyldt.

char *dst = malloc(100); if(!dst) abort("Fejl ved malloc"); dst[20] = 0; int i = 0; do { dst[i] = getchar(); i++; } while (i < 15);

Igen er Baldurs krav opfyldt.

Resten af koden kan dernæst eksekvere med fuld hatighed uden bounds checking og resten af det, Mogens nævner i blogindlæget.

Den pointer, som Baldur nævner, skal komme et sted fra. Den opstår ikke af ingenting fra en nisse eller en UFO. Den opstår fra kode.

Og så længe denne kode, som overgiver den til dig, er korrekt, og der anvendes korrekt kontrol af returværdier fra fread(), getchar(), malloc(), mv - som i ovenstående eksempler - så er den resulterende pointer også korrekt.

Problemet er så, at over et bestemt kompleksitetsniveau kan man ikke bevise korrektheden for kode eller inputdata (fx at en regex søgestreng opfylder syntaksen før den overgives til en regex funktion). Derfor assert(), check af nul-pointere, array bounds check, osv, osv.

Men disse checks kan man med omhu reducere til langt færre end hvad C# og Java indsætter. Og for mange typer kode, som ikke KAN exploites via input data fordi al data pr definition er gyldig (kryptering, mv), kan det reduceres til 0 checks med noget omhu.

Selvfølgelig bliver udviklingstiden meget længere, og min pointe var kun, at Mogens overdrev temmelig meget med sit budskab.

  • 0
  • 0
#20 Carsten Sonne

At Google har valg C er ikke så underligt. Den primære årsage er vel i alt sin enkelthed at Chrome er målrettet mod flere forskelige platforme. Som PHK skriver:

C er det eneste portable sprog...

Jeg undres dog ofte over C++ ikke er mere udbredt end det er. Sproget er understøttet på næsten enhver tænkelig platform. At C++/CLI stadig er et niche sprog er mere forståeligt. Bjarne Stroustrup udtrykker det meget klart [*]:

A language used to produce CLI modules must be able to express all of the CLI facilities, including the metadata. Only a language that can do that can be considered a systems programming language on .Net. Thus, the Microsoft C++ team concluded that only build-in language facilities are acceptable to their customers. Their design reflects a view that accepts absolutely no restrictions on what part of CLI can be expressed in C++ with the C++/CLI extensions, absolutely no verbosity compared to other languages when using CLI facilities, and absolutely no overheads compared to other languages. They aim at preserving C++ as the dominant systems programming language for Windows.

Måske det samme som PHK beskriver med:

...fordi sidstnævnte typisk kommer slæbende med alt for meget ideologisk og runtime bagage man ikke kan slippe for.

Som det er nu, benyttes managed sprog i vid udstrækning hvor det er muligt. Desværre er der stadig en del begrænsninger i de nuværende implementeringer. Dermed begrænses deres udbredelse helt automatisk.

Trist, trist ...

[*] http://www2.research.att.com/~bs/bs_faq.html#CppCLI

  • 0
  • 0
#21 Klaus Skelbæk Madsen

Bare lige for at få det på plads, så er Chrome altså skrevet i C++.

Hvis man kigger på Chromium sourcen, og ekskluderer alt under third_party biblioteket (som indeholder ting som zlib, nss, mesa, osv.), så er det stort set kun nativeclient delen som er skrevet i C. Selv sandbox delen indeholder kun 3 sølle Linux specifikke .c filer.

Faktisk er der flere .py filer (706) end der er .c filer (566).

Men alternativet Google havde til C, var altså C++ ;-)

  • 0
  • 0
#22 Torben Mogensen Blogger

Bare lige for at få det på plads, så er Chrome altså skrevet i C++.

C++ har præcis de samme problemer med usikkerhed som C, så det ændrer ikke mine argumenter. Dog er C++-programmer typisk langsommere, dels fordi brug af virtuelle metodekald giver overhead og dels fordi STL bibliotekerne er kodet med en anden form for defensiv kodning: De skal være nogenlunde robuste over for exceptions og alt det, som C++ programmører gør med pointere. Det giver en masse overhead i form af ekstra [i]indirections[/i] osv. Se f.eks. Bo Simonsens og Jyrki Katajainens artikler på http://www.cphstl.dk/WWW/reports.html .

C er det eneste portable sprog...

Det er en sandhed med modifikationer på flere måder: Dels er portabiliteten af C ofte ret overvurderet og dels findes mange andre portable sprog.

Hvis du skal skrive et ikke-trivielt portabelt C program, skal du i reglen bruge en masse IFDEFs for at tage højde for maskinspecifikke detaljer. Og dem er der mange af. Den hyppigst forekommende sætning i C standarden er "this is implementation specific" (eller noget ækvivalent hermed).

  • 0
  • 0
#23 Baldur Norddahl

Du skal jo blot nøjes med at acceptere pointere fra pålidelige kilder. Giv et helt konkret eksempel på, hvor pointeren skulle komme fra, så vil jeg svare

Beslut at input til dine funktioner per definition er gode (kommer fra pålidelige kilder) og der er intet behov for at validere input. Hvis du kan kode på den måde får du et lynhurtigt program og alt er godt.

Men nu er titlen på denne blog post "defensiv programmering". Det er der vel næppe det du taler for her.

  • 0
  • 0
#24 Carsten Sonne

Torben,

Hvis overheadet ved OO er acceptabelt i en given situation giver nedenstående argument vel ikke meget mening ?

Dog er C++-programmer typisk langsommere, dels fordi brug af virtuelle metodekald giver overhead og dels fordi STL bibliotekerne er kodet med en anden form for defensiv kodning...

Hvis ikke, hvilket managed alternativ er der til C, dvs. proceduralt, statisk typet og kompileret ?

Dels er portabiliteten af C ofte ret overvurderet og dels findes mange andre portable sprog.

Forskellen er vel i C [b]kan[/b] portabiliteten håndteres. Jeg kan ikke rigtig se hvilket alternativ Google har til implementering af Chrome. En kombination af C++ og Pyton giver i mine øjne god mening.

  • 0
  • 0
#26 Anonym

Managed sprog vil i begge tilfælde indsætte langt, langt flere checks end nødvendigt.

Det er nu en sandhed med modifikationer... Hvis sproget har et tilstrækkeligt stærkt typesystem, kan mange checks undgås. Tag for eksempel nedenstående eksempler:

type Index_Type is range 1 .. 25; type Array_Type is array (Index_Type) of Integer;

My_Index : Index_Type; My_Array : Array_Type; My_Integer : Integer;

...

My_Integer := My_Array (My_Index); -- Ingen check pga. typesikkerhed.

for Index in My_Array'Range loop ... := My_Array (Index) -- Ingen check pga. typesikkerhed. end loop

Der eksisterer en implementation af Skein hash funktionen http://en.wikipedia.org/wiki/Skein_%28hash_function%29 i SPARK (og dermed Ada som er et managed sprog), der er ca. 5% - 10% langsommere end C funktionen.

  • 0
  • 0
#27 Torben Mogensen Blogger

Hvis overheadet ved OO er acceptabelt i en given situation giver nedenstående argument vel ikke meget mening ?

Det mener jeg heller ikke, at det altid er. Managed er ikke ensbetydende med OO. OO giver efter min mening ikke nogen væsentlige fordele og medfører overhead, som det er svært at optimere sig ud af. På den måde minder det om dynamiske typer.

Hvis ikke, hvilket managed alternativ er der til C, dvs. proceduralt, statisk typet og kompileret ?

Hvis du gerne vil holde dig tæt til C's kodestil, kan du bruge Cyclone, som er designet til at bruges, hvor C ellers bliver brugt.

Cyclone understøtter f.eks. manuel lagerhåndtering, men sikrer statisk, at du ikke deallokerer lager, der stadig har indgående pointere. Og du kan bruge enten simple pointere, hvor det statisk checkes/garanteres, at der ikke er nulreferencer, og at alle referencer er indenfor grænserne, eller "fat pointers", hvor det checkes på køretid.

Et lignende sprog er ATS (http://www.ats-lang.org/).

Hvis du vil have et mindre eksperimentelt sprog, er der f.eks. ML-familien (SML, O'Caml osv.). De er ikke rent funktionelle, så du kan sagtens programmere proceduralt i dem.

  • 0
  • 0
#28 Torben Mogensen Blogger

Managed sprog vil i begge tilfælde indsætte langt, langt flere checks end nødvendigt.

Tværtimod vil defensiv programmering med tiden akkumulere en masse overflødige tests. En compiler for et [i]managed[/i] sprog kan lave statiske analyser og kun indsætte check, hvor den statiske analyse ikke garanterer, at de er overflødige.

Statiske analyser er ikke 100% præcise, så der kan forekomme overflødige checks. Men programmører er heller ikke orakler, så de vil også lave overflødige checks eller -- endnu værre -- undlade checks, hvor de rent faktisk er nødvendige. Problemet forværres efterhånden som koden modificeres -- der kan vere behov for nye checks, og tidligere nødvendige checks kan blive overflødige. Det er de færreste programmører, der gennemanalyserer programmet for at se, hvor der skal tilføjes eller fjernes checks. Men en genoversættelse med statisk analyse kan gøre det.

Der findes også sprog, som ikke indsætter køretidschecks, med mindre programmøren beder om det. Hvis de statiske checks ikke er nok til at garantere mod fejl, får du en fejlmeddelelse, som du kan bruge til at indsætte eksplicitte [i]assertions[/i], som kan guide oversætteren til at finde invarianter, eller programmøren kan ændre typen til en, der dynamisk checker for det, der ikke kan verificeres statisk.

  • 0
  • 0
#29 Carsten Sonne

Torben,

Hvorvidt OO tilfører værdi eller er, er en anden diskussion. Som jeg forstår det, siger du, at fravælgelsen af managed sprog ud fra en tradeoff betragtning ofte er et fejlagtig valg. Det er jeg sådan set enig i. Måske er Chrome ikke lige det bedste eksempel til at illustrere pointen. Med lad det være hvad det er. Og, tak for de gode links.

Der er sådan set ikke noget galt i virksomheder, communities og enkeltperson vælger eller fravælger bestemte sprog af politiske årsager. Så skal man bare melde at det er et politisk valg.

Bortforklaringer med forskellige teknikaliteter som påskud virker utroværdigt. I værste tilfælde må det betragtes som forsøg på politisering eller salg af ”snake-oil”.

  • 0
  • 0
#30 Anonym

Torben,

Ligesom sprog af højere niveau end assembler er for folk, der ikke evner at holde styr på, hvilke registre, de har brugt?

Selvfølgelig kan man lave sikre programmer med manuel lagerstyring, men for komplekse systemer kræver det så meget mere arbejde, at det ikke kan betale sig. Og når en anden senere overtager koden og skal lave udvidelser/tilretninger, så ryger sikkerheden nemt ud med badevandet, da den nye programmør ikke kender de invarianter, der sikrer mod (de)allokeringsfejl. Man kan også sagtens lave det hele i assembler.

Det er jo det rene ævl, du fyrer af her, og afspejler, at du ikke har haft 'fingrene nede i materien'.

Ting er ikke sort og hvidt, men ind imellem også gråt.

Du glemmer en god memorymanager og partiel automatik, som aflaster behovet for lavpraktisk memoryhåndtering.

Tag et kig på Delphi, som netop implementerer disse ting, men UDEN GC, som er noget fanden har skabt (i HPC sammenhæng).

I Delphi kan man erklære: Var A : String.

'Behind the scenes' sørges der for memory allokering/deallokering, og et genialt træk er, at strings er referencecounted, derfor(bla) den ufattelig høje performance, som overgår C og C++.

String handling er et kardinalpunkt i mange applikationer, og netop længde angivelsen (kombineret med zero terminated), gør, at mange strengoperationer kan gennemføres uden at scanne for denne 'null terminering'.

'Null termineringen' er jo netop årsag til alverdens ondt, da mange programmører ikke huskeer at tage højde for denne.

Tag også indbyggede tjeks, og du vil indse hvorfor Delphi er det mest brugte sprog (til desktopapplikationer).

Serverside, og især inden for finans/produktion er det stadig, og vil stadig være COBOL, der styrer.

COBOL programmerne opfylder deres behov, og den indbyggede forretningslogik, og det ville ganske simpelt være tåbeligt at erstatte dem med nye 'lavpraktiske' sprog.

'Rounding errors comes to my mind'....

Fint nok du kommer med lidt impulser, men jeg må konstaterer, at verden ikke har ændret sig de sidste 25+ år, hvor jeg havde nogle datalogistuderende ansat.

Disse folk var mere til besvær end gavn, da de ikke evnede at se EDB fra et forretningsstrategisk synspunkt, men ville 'bruge det de havde lært'.

Det de havde lært kunne ikke bruges til en hujende fis i det virkelige liv, og det ville være en kamp at højne deres niveauer.

Udfaldsrummet blev, at jeg måtte bruge dem som deciderede båndaber, da deres 'holdninger' var en bremse i udviklingen.

Det er hårde ord, men ikke desto mindre sandheden, og jeg forventer da også mange '-' point for mit indlæg.

Det rører mig ikke, men et godt råd er at tage udgangspunkt i det virkelige behov i stedet for at digte hypoteser i en 'højborg'.

Slut herfra...

  • 0
  • 0
#31 Anonym

Ligesom sprog af højere niveau end assembler er for folk, der ikke evner at holde styr på, hvilke registre, de har brugt?

Selvfølgelig kan man lave sikre programmer med manuel lagerstyring, men for komplekse systemer kræver det så meget mere arbejde, at det ikke kan betale sig. Og når en anden senere overtager koden og skal lave udvidelser/tilretninger, så ryger sikkerheden nemt ud med badevandet, da den nye programmør ikke kender de invarianter, der sikrer mod (de)allokeringsfejl. Man kan også sagtens lave det hele i assembler. [quote] Det er jo det rene ævl, du fyrer af her, og afspejler, at du ikke har haft 'fingrene nede i materien'.

[/quote] Her er det vist dig, der vrøvler. Torben har fuldstændig ret i, at så snart et program kommer op i en realistisk størrelse, så kræver det simpelthen for megen manuel indsats for at sikre mod de nævnte fejl vha. defensiv programmering som er temaet i denne tråd. Her er sprogsupport en nødvendighed. Du nævner endda selv Delphi som åbenbart har en sådan sprogsupport for strenge.

Til det jeg laver, er dynamisk lagerallokering forbudt og derfor benytter jeg det selvfølgelig ikke, men jeg benytter stadig et sprog, der giver mig god support til at undgå defensiv programmering.

  • 0
  • 0
#32 Lasse Reinholt

Forskellen er vel i C kan portabiliteten håndteres.

Sure, se hvordan det gøres (ikke for sarte sjæle!): http://pastebin.com/fF40Rb2A - det skal så siges, at biblioteket er et af de mest portable, jeg kender, og opdager kendte og selvopdagne compiler fejl. Ellers kan du se hvor mange fejl i diverse dev tråde, som starter med "Cannot compile on XXXX", "BUS ERROR on Solaris".

Der eksisterer en implementation af Skein hash funktionen http://en.wikipedia.org/wiki/Skein_... i SPARK (og dermed Ada som er et managed sprog), der er ca. 5% - 10% langsommere end C funktionen.

Hashfunktioner er rimelig nemme for compileren at gennemskue, da alle tilgange til memory er på 2D-arrays og lign. med hardkodede konstanter som løkkegrænser. Sjovt, du lige nævner skein - har du noget at gøre med den? Jeg er ved at optimere den (i C) da jeg bruger den i et produkt :)

Der findes også sprog, som ikke indsætter køretidschecks, med mindre programmøren beder om det

Du kan i C# bruge keywordet 'unsafe' og lave noget benchmarking med og uden.

Tværtimod vil defensiv programmering med tiden akkumulere en masse overflødige tests. En compiler for et managed sprog kan lave statiske analyser og kun indsætte check, hvor den statiske analyse ikke garanterer, at de er overflødige.

Det er korrekt, at man manuelt kommer til at fremstille en del overflødige og dobbelte checks. Men C++ vinder stadig i fx http://reverseblade.blogspot.com/2009/02/c-versus-c-versus-java-performa... (Det er i øvrigt svært at finde benchmarks af kode, som ikke bruger standardbiblioteker - da bliver det jo en sammenligning af biblioteker og ikke af kodegenerering)

  • 0
  • 0
#33 Anonym

Sjovt, du lige nævner skein - har du noget at gøre med den? Jeg er ved at optimere den (i C) da jeg bruger den i et produkt :)

Nej desværre. Jeg er involveret i SPARK og Ada brugermiljøet og det var derigennem, jeg blev gjort opmærksom på implementationen.

Prøv at læse denne korte artikel http://www.skein-hash.info/sites/default/files/SPARKSkein.pdf specielt afsnit 4.1.1 og 4.5. Det kan være, du kan bruge det i dit arbejde.

  • 0
  • 0
#34 Carsten Sonne

Det er korrekt, at man manuelt kommer til at fremstille en del overflødige og dobbelte checks. Men C++ vinder stadig...

Sammenligningen er lavet på algoritmer; mindre stykker kode hvor helheden er nem at overskue. I Råstyrke vinder C++ forudsigeligt.

Managed sprog viser sin styrke når kodemængden når et niveau hvor det ikke længere er så nemt at huske, ensidigt gennemskue, hvor der bør laves og hvor der er lavet check.

  • 0
  • 0
#35 Anonym

Problemet forværres efterhånden som koden modificeres -- der kan vere behov for nye checks, og tidligere nødvendige checks kan blive overflødige. Det er de færreste programmører, der gennemanalyserer programmet for at se, hvor der skal tilføjes eller fjernes checks. Men en genoversættelse med statisk analyse kan gøre det.

Et godt eksempel på at nye checks kan blive nødvendige er squashfs filsystemet til Linux, der benytter memcpy() til at kopiere en stang data. En pre-betingelse for memcpy() er at dataområdet der skal kopieres fra, ikke må overlappe med dataområdet der skal kopieres til.

Oprindeligt var der ikke nogen problemer da pre-betingelsen var overholdt, men efterhånden som filsystemet udviklede sig, blev den stang data der skulle kopieres større, og pre-betingelsen var ikke længere opfyldt. Denne fejl forblev uopdaget da implementationen af memcpy() i visse specialtilfælde alligevel fungerede korrekt. Lige pludselig opstod der dog fejl og det viste sig at implementationen af memcpy() var blevet ændret og nu var afhængig af at pre-betingelsen var overholdt også i specialtilfældet.

Altså over tid var nogle datatyper blevet større, men ingen tænkte på at det kunne have ovennævnte konsekvenser og dermed manglede der nogle checks.

  • 0
  • 0
#36 Lasse Reinholt

Altså over tid var nogle datatyper blevet større, men ingen tænkte på at det kunne have ovennævnte konsekvenser og dermed manglede der nogle checks.

Den rette måde at undgå fejlen ville selvfølgelig være at validere blokkens længde i funktionerne, som har med blokken at gøre. Altså at undgå fejlen så tideligt som muligt, meget længe før bounds check af memcpy().

Det er først ved bounds checking, som er "last resort" for denne type fejl, at managed sprog og C(++) adskiller sig fra hinanden.

Ved godt, at det er indlysende for nogle, men nævnte det alligevel for at minde om at skelne mellem de to ting for andre læsere.

  • 0
  • 0
Log ind eller Opret konto for at kommentere