Uh, I seem to be stumbling over these things a lot today... another bug, this time just to take down the network. :p
let's look at this line of code from pushBlock:
block.transactions = new long[block.numberOfTransactions];
Looks pretty innocent, doesn't it? What could possibly go wrong?
Well, except, there's one thing: block.numberOfTransactions is an int, so with the right number, we could try to allocate 2G*8 = 16GB of RAM. This will cause an OutOfMemoryError from the JVM, which we can't catch with any try {} catch(Exception).
The line is getting a bit less innocent, but still, all the block stuff is checked befor we get here, right? RIGHT?...
Let's see, what is checked at that point by slowly going backwards...
At first we see a verifyBlockSignature and verifyGenerationSignature, checking, ehm, well, the blockSignature, generationSignature (that has to match the generatorPublicKey), and the hit.
Next, we see some even more trivial things: lastBlock is checked, and the current blockId has to be unknown.
Now we already arrive at the constructor. Surely, the numberOfTransactions has been checked for sanity before we construct the block... let's see.
Going further, we see checks for payloadLength and blockTimestamp, another previousBlockId check, and a version check.
Suddenly, we're at the beginning of pushBlock... Still there has been no check of the value of numberOfTransactions! It's getting quite not so innocent now... we now have to check all call points of pushBlock and whether buffer is guaranteed to be sane...
Let's start with the occurence in doPost() -> processBlock (It's the shortest, maybe we're lucky
)
The buffer that goes to pushBlock is created by a block.getBytes() call, which basically is just a serialization (btw: y u no serialize???).
The block is created a few lines above using a variable called numberOfTransactions.
And this variable is read... *drum roll* ...from the post request that any person can send to the server.
Uh oh. Not innocent, at all...
So what do we have to do to take down the network?
It has to be our turn to generate a block - check
We have to generate it - check
and right before we calculate the signatures, we set the numberOfTransactions to INT_MAX - check (It's a one-line change in generateBlock, e.g. right before payloadHash is calculated)
So that was pretty easy to get all the clients to crash with an OutOfMemoryError.
But what to do, if we don't want to wait for our next turn to forge a block to take down the network?
There's an even easier solution
Let's take a look at "processBlock" again. It reads a few parameters of the block, generates the block, and ... *queue the drum roll again!* ... allocates a byte buffer of BLOCK_HEADER_LENGTH + payloadLength, without checking anything anywhere. So just post an INT_MAX payloadLength and garbage for the other params, and you've probably taken down a node because it wanted to allocate 2GB of RAM and I guess none of them has stuff like -Xmx=4096M or sth. set.
Phew... that was just too easy.
Obviously: Before typing this post, I tried the latter method on my local 0.5.0 client and it didn't work. So I guess this has been fixed by now.
[edit]
The same can be achieved by modifying what your node responds to a getNextBlocks call. Exactly the same procedure as above.