Ergebnis 1 bis 20 von 255

Thema: while(true) {write();} - Der Programmierer-Spamthread #1

Hybrid-Darstellung

Vorheriger Beitrag Vorheriger Beitrag   Nächster Beitrag Nächster Beitrag
  1. #1
    Zitat Zitat von Mivey Beitrag anzeigen
    Mein C ist nicht so wirklich gut, aber lese ich richtig das "&Buffer" das Problem ist? Weil es ist ja ein Pointer und so
    Ja, richtig. Die interessantere Frage ist aber nicht wo die Ursache ist, sondern was genau dabei passiert. Da es eine lokale Variable auf dem Call Stack ist, wird dieser überschrieben und damit alle im Stack folgenden Rücksprungadressen, Funktionsparameter, etc. Kurz gesagt, Katastrophe. In diesem Fall ist es sogar noch schlimmer, da ohne Debugger das Programm an der Stelle der früher oder später resultierenden Zugriffsverletzung still beendet ("bla bla" wird nicht ausgegeben), ohne auf das Fehlverhalten mit einer entsprechenden Ausnahmebehandlung zu reagieren.

    Zitat Zitat von drunken monkey Beitrag anzeigen
    @ Whiz: Er hat ja geschrieben, "Ähnliches", ist im Original also vielleicht eh sinnvoller, bzw. nicht der Punkt.
    Ursprünglich war es wie erwähnt eine Dateioperation, fread um genau zu sein. memset sollte wirklich nur dem Vorführungszweck dienen.

    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    [...] Auch wenn man den Speicher nicht genullt braucht, würde ich es dennoch machen. Schaden kann es nicht.
    Da würden dir die C-Entwickler vermutlich etwas anderes sagen. Redundanz im Zeichen der Fehlerreduzierung entspricht zum Beispiel nicht der C-Philosophie.

    Die Frage, ob man auf malloc verzichten sollte erinnert mich an die Frage, ob man auf goto verzichten sollte. Hierzu:
    Zitat Zitat
    In computer science:

    1st years don't use goto
    2nd years use goto in assembly language
    3rd years use goto when goto is needed.
    Besonders in C hat goto einen festen Platz und vereinfacht oft den Code, wie zum Beispiel in der Ausnahmebehandlung. Ähnlich verhält es sich mit malloc und calloc. Beide haben Vor- und Nachteile, die je nach Situation andere Gewichtung haben können. Verdammen ist ist in meinen Augen nie gut und schädlicher als fragliche Anwendung.

    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    hab jetzt mal alle Programme geschlossen und mein Testprogramm durchlaufen lassen.
    nun liegt der Unterschied sogar bei nur 47 ms.
    Ohne Code sind diese Resultate nicht wirklich repräsentativ. Man kann auf eine Weise messen und auf eine andere mit jeweils völlig unterschiedlichen Ergebnissen. Entscheidend wären beispielsweise, Granularität, Mittelwertbildung mehrerer Messwerte um Abweichungen durch Interrupts und ähnliches zu minimieren, Reduzierung auf die Messung der tatsächlichen Arbeit (free ist ein Störfaktor), usw.
    Ich habe mal einige Messungen auf meinem Pentium D 2.8 Ghz gemacht und bei mir braucht malloc für 1 MB im Schnitt (10 Messungen hintereinander) etwa 12500 clock cycles (entspricht etwa 4.46 us bei 2.8 Ghz) und calloc etwa 13500 clock cycles (etwa 4.82 us). Damit ist calloc um 8% langsamer als malloc. Nicht viel, aber viel hat man hier auch nicht erwartet, denn Nullsetzung ist sehr billig und in heutigen Zeiten in der Regel SIMD-optimiert. Natürlich erhebe ich keinen Anspruch, dass auch meine Messungen 100%-ig repräsentativ und fehlerfrei sind. Jedenfalls gibt es einen Unterschied, der in manchen Situationen sogar größer sein kann und damit eventuell abgewogen werden könnte, wie zum Beispiel auf Systemen ohne SIMD, wenigen und kleinen Caches etc.

    Geändert von Kyuu (16.02.2010 um 23:04 Uhr)

  2. #2
    Zitat Zitat von Kyuu Beitrag anzeigen
    Verdammen ist ist in meinen Augen nie gut und schädlicher als fragliche Anwendung.
    Stellt sich die Frage, ob du auch noch sprintf() benutzt. Das ist ja bekanntermaßen eine gute Möglichkeit, unkontrolliert Speicher zu überschreiben und viele empfehlen, stattdessen snprintf() (bei C99-fähigen Compilern) oder asprintf() (bei GNU GCC) zu verwenden. Zugegeben, die sind vermutlich beide langsamer, aber solange man nicht per (semi-)formellem Beweis nachweisen will, daß der Puffer nicht überlaufen kann, stellen sie auch Schutz vor einem potentiellen Sicherheitsrisiko dar.

  3. #3
    Also gut, ich verwende eigentlich automatisch schon snprintf() (auch wenn ich praktisch nie C programmiere), aber teilweise würde ich sehr wohl auch sprintf() für gerechtfertigt halten. Z.B. wenn man nur Integers in einen String schreibt – länger als 11/20 Stellen (je nach Größe) können die ja nicht werden. Genauso wenn man bei allen Wildcards auch die Maximallänge angibt (bei Strings halt – floats sind wohl echt problematisch).
    "Verdammen" wäre da vielleicht auch falsch, selbst wenn es allgemein auch kaum schaden wird, einfach immer die sichereren Varianten zu benutzen.

  4. #4
    Interessant wird's dann aber, wenn man in weiser Voraussicht den String auf 11 Stellen dimensioniert hat, weil der Code ja eh nur auf 32bittigen Plattformen zum Einsatz kommt. Und dann kommt plötzlich eine source-kompatible 64bittige Version der Plattform raus oder jemand portiert den Code.

    Das ist (sicherheitstechnisch) das Blöde bei C: Die einzige Sicherheit, die du hast, ist die, die du einbaust. Das ist ein Grund, lieber defensiv zu programmieren als die letzten 0,1% Leistung rauszukitzeln.

  5. #5
    Wie gesagt, am Ende kommt es immer auf die Anforderungen der Software an, die man programmiert. Es ist genauso falsch an den falschen Stellen zu optimieren, wie auch zu optimistisch, oder zu pessimistisch zu programmieren. Hundertprozentige Sicherheit gibt es sowieso nie.

  6. #6
    Doch, gibt es. Sie nennt sich "EAL7-Zertifizierung". Gut, dafür mußt du sämtliche Software und IIRC auch Teile der Hardware formell verifizieren und testen. Aber dann kannst du dir absolut sicher sein, daß jegliche Fehler Hardwaredefekte und nicht Implementierungsfehler sind.

    Okay, als Normalsterblicher wirst du nicht mit EAL7-Kram arbeiten. Aber du solltest schon wissen, wann du wo welche Risiken eingehen kannst. Ich würde mir wohl nicht die Mühe machen, bei jedem Befüllen eines Strings abzuwägen, ob es theoretisch möglich ist, den String mit zu vielen Daten zu befüllen – nicht, wenn es Funktionen gibt, die diese Mühe überflüssig machen.
    Man kann's natürlich übertreiben, aber solange der Performanceverlust durch Verwendung von sprintf()-Alternativen nicht zu groß ist, würde ich sie grundsätzlich verwenden. Und das dürfte außerhalb von synthetischen Benchmarks immer der Fall sein.

  7. #7
    Die CC ist auch keine Silberkugel und unangreifbar. Du kannst dich an absolute Sicherheit annähern, sie aber nie erreichen. Mit jedem erreichten EAL kannst du deine Software als "sicherer", oder als "zur Zeit sicher gegen dies und das" bezeichnen, aber nie als "sicher". Die Hardware spielt IMO auch eine entscheidende Rolle bei der Frage nach der Sicherheit eines Systems. Übrigens habe ich mal irgendwo gelesen, dass EAL7 für Low Level-Implementation nur semi-formelle Spezifikation fordert, so dass es keinen mathematischen Beweis gibt, dass die Low Level-Spezifikation der Implementation entspricht. Dass EAL7 nur für sehr spezielle Systeme mit sehr speziellen Sicherheitsfunktionen überhaupt praktikabel ist, da der Aufwand und die daraus resultierenden Kosten enorm sind, ist bekannt. Naja, ich bleibe dabei, dass absolute Sicherheit - und ich meine absolut - nicht möglich ist. Ich meine mich zudem zu erinnern gelesen zu haben, dass selbst bei der medizinischen Ausrüstung (und anderer Technik mit ähnlichen Sicherheitsstandards) nicht mit absoluter Fehlerfreiheit gerechnet wird und Todesfälle verursacht durch Fehler in diesen Geräten sowohl in Kauf genommen werden (wenn auch minimal), als auch schon vorgekommen sind.

    Was sprintf angeht, überrascht es mich ein wenig, dass du nur die Performance, oder gerade die Performance in Betracht ziehst, da diese kaum bei der sprintf-Familie relevant sein kann. Ich kann mir jedenfalls nicht vorstellen aus welchem sinnvollen Grund sprintf und co. etwas in performancekritischen Bereichen zu suchen hätten. Vielmehr würde ich speziell bei snprintf/asprintf argumentieren, dass ihre Verwendung zusätzlichen Aufwand und damit Komplexität erzeugt in dem Fall, wenn man sich nicht auf einen Compiler beschränken möchte. MSVC ist zum Beispiel nicht C99-konform und hat keine snprintf-Implementation. Nun bietet MSVC zwar sein eigenes snprintf, nämlich _snprintf (was hätte man anderes erwartet), dieses ist aber semantisch von C99-snprintf verschieden -> Aufwand ist notwendig um hier Plattformunabhängigkeit zu erreichen. Man könnte argumentieren, dass dieser Aufwand relativ gering ist, es bleibt trotzdem Aufwand, der betrieben werden muss. IIRC ist das Trimmen von _snprintf auf C99-snprintf aus dem Grund der semantischen Unterschiede sogar nicht so effizient wie man es erwarten würde und würde im Worst Case viele Aufrufe erfordern, was vielleicht nicht wünschenswert ist. Wenn die Software klein genug ist und das Risiko, dass sprintf einen Überlauf verursacht gering ist und nur in Grenzfällen eintreten kann, wie etwa wie in deinem Beispiel mit der Portierung auf 64 Bit, kann man dieses durchaus in Kauf nehmen. Man kann auch mit größerem Buffer arbeiten, was absolut nicht unüblich ist. Ein Wechsel von sprintf zu snprintf/asprintf/_snprintf/etc. ist zu jedem Zeitpunkt immernoch möglich. Ich habe gelernt, dass vorzeitige Paranoia genauso schädlich ist, wie vorzeitige Optimierung und ähnliches Übel und Schritte in diese Richtungen erst gemacht werden sollten, wenn ein Anlass besteht. Es gibt natürlich Ausnahmen.

    Im Übrigen bin ich alles andere als der Meinung, Performance geht über Stabilität und Sicherheit, falls (aus welchem Grund auch immer) bei irgendwem dieser Eindruck entstanden sein sollte. Um ehrlich zu sein, habe ich doch bis jetzt immer wieder auf eine oder andere Art betont, dass immer abgewogen werden sollte, je nach Situation und Anforderungen. Ich bin aber definitiv der Meinung, dass nicht vorschnell Dinge als impraktikabel erklärt werden sollten, allein aufgrund dessen, dass sie unter bestimmten Voraussetzungen unsicher sind. "sicher" erweckt meiner Meinung nach sowieso ein falsches Gefühl der Sicherheit, denn um überhaupt nur entfernt von Sicherheit zu reden ist weit mehr nötig, als nur calloc oder snprintf und co. zu verwenden. Davon abgesehen, dass calloc kaum als "sichere Variante von malloc" gedacht war, sondern vermutlich nur als "die bequeme Variante", im Fall, dass der erhaltene Speicher genullt sein muss. Im Fall von Gleitpunktzahlen und Zeigern ist calloc schonmal keine Silberkugel, denn alle Bits auf Null gesetzt, bedeutet nicht eine Gleitpunktzahl gleich Null, oder einen Nullzeiger, man hat also das gleiche Problem wie in deinem Beispiel mit sprintf und der Portierung auf 64 Bit.

    Geändert von Kyuu (18.02.2010 um 14:28 Uhr)

  8. #8
    C99 ist immer noch nicht flächendeckend implementiert? Oy vey; ich weiß schon, warum ich mich von lowlevel-Programmierung fern halte.

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •