Aus einem Blogpost zum Thema Sicherheit von Christian Reitwiessner:
Aufgrund der maximalen Stack-Tiefe von 1024 kann der neue Bieter die Stack-Größe jederzeit auf 1023 erhöhen und dann bid() aufrufen, wodurch der send(highestBid)-Aufruf stillschweigend fehlschlägt.
Es scheint also, dass Sie den Stack so vorbereiten können, dass ein send() fehlschlägt, der Vertrag jedoch seine Ausführung fortsetzt und beendet wird.
Was ist die Logik hinter diesem Verhalten und ist es möglich, mehr als ein send() "anzugreifen" oder würde ein zweites send() eine Ausnahme verursachen, wenn das erste bereits "stillschweigend" fehlgeschlagen ist?
Begründung für die Tiefenbegrenzung:
Mit einer Aufruftiefenbegrenzung von 1024 - viele Programmiersprachen brechen bei hohen Stapeltiefen viel schneller ab als bei hoher Speichernutzung oder Rechenlast, sodass die implizite Grenze der Blockgasgrenze möglicherweise nicht ausreicht.
Die EVM muss deterministisch sein. Wenn ein Ethereum-Client in einer Sprache implementiert ist, die bei hohen Stapeltiefen bricht, dann wäre ein solcher Client nicht in der Lage, das Ethereum-Protokoll zu implementieren: Er wäre nicht in der Lage, die Ergebnisse gemäß Konsens zu produzieren.
Ein kürzlich von Vitalik Buterin vorgeschlagener Vorschlag ist, einfach Gas als begrenzenden Faktor zu haben (obwohl die Tiefe 2091 erreichen könnte).
function foo() {
send()
send()
}
Beide send()
können angegriffen werden. Der Aufruf des ersten send()
erhöht die Tiefe um 1, verringert sie aber wieder um 1, wenn sie zurückkehrt. send()
nicht throw
, daher werden keine Ausnahmen generiert.
Um anzugreifen foo()
, ruft sich ein Angreifer rekursiv 1023 Mal selbst auf und ruft dann auf foo()
. (In Solidity sollte die rekursive Funktion so sein external
, dass die Aufruftiefe erhöht wird [im Gegensatz zu internen Funktionen, die Sprünge innerhalb des Codes sind und die Tiefe nicht erhöhen].)
(Ruft sich selbst 1023 Mal auf, da der erste Aufruf vor der Rekursion die Tiefe um 1 erhöht. Zum Zeitpunkt des Aufrufs foo
wird die Tiefe die Grenze von 1024 erreichen.)
BEARBEITEN: EIP 150 macht es unpraktisch, eine Stack-Tiefe von 1024 zu erreichen, wodurch Call-Depth-Angriffe effektiv eliminiert werden. Siehe Wie ändert EIP 150 den Anruftiefenangriff?
Alper
eth
attack()
Funktion, die das tutif (i==1023) foo() else { i++; attack(); }
Garen Vartanian
eth
Garen Vartanian
eth
Garen Vartanian
attack()
Funktion extern aufgerufen werden und der Code sollteif (i==1023) someExternalContract.foo() else { i++; this.attack(); }
korrekt sein? Unter der Annahme eines Gebotsvertrags würde der vorherige Höchstbietende keinen Wert erhalten und der Saldo des Vertrags würde wachsen.eth