I’m a little disappointed. Even though I have what seems to be the main entry points to malloc and free in IOS, it is still not enough, I must be missing something; or I have the wrong addresses.
I implemented a MemCheck tool, very similar in spirit to Valgrinds MemCheck (hence the same name). I did this by maintaing a map of valid memory regions. Memory regions are created by intercepting the malloc() call in IOS. Regions are destroyed by calling free(). At memory stores, the target of the store is validated against the map of memory.
The memcheck code I wrote is a small amount of code with a straight forward implementation, but it reveals numerous double frees in typical code sequences during IOS bootup. It is highly unlikely that this represents real bugs in IOS, but almost certainly indicates that there are bugs in my own code.
Firstly, dynamically identifying the start address of the current procedure is not entirely simple. The basica approach would be to watch for call instructions, or in MIPS, Jump And Link instructions (JAL and JALR). There are problems with this approach however. It’s common to see tail call optimisations. If the last operation a procedure does is a procedure call, it can optimise the situation by replaing the current procedures epilogue and the call instruction to the target procedure, with a unconditional jump. The target procedure performs its own epilogue in place of the current procedures. When we come across this optimisation, our procedure identifier will fail.
Perhaps a better solution, but one I haven’t implemented, is to identify procedures statically using IDA, and then identify if the program counter arrives at one of these addresses.
Identifying when a procedure exits is also challenging, the typical MIPS convention is JR $ra, or jump to register $return_address. Another approach is to save the return address following a JAL or JALR, and check the program counter against that.
To intercept malloc and free, the only approach to follow is to check the program counter against the addresses of those functions. Too many calls are missed if you are only monitoring the JAL or JALR instructions.
Back to memcheck and the double free false positives..
Having a false positive for a double free, probably indicates that there is a malloc that has been called between the frees that wasn’t correctly identified. Am I missing another entry point for malloc?
The double frees are associated with some kind of timing event as they happen somewhat periodically. I monitored all memory writes looking for the malloc chunk start magic (0xab1234cd). There were no writes to memory using a store word instruction with this magic value. Therefore my malloc finding code does not detecting anything.
Another idea was that the allocated memory was reference counted, and so if this was the case, it could be quite plausible that free could be called multiple times in succession. I tried intercepting memory stores to the semi documented ‘ref’ field in the malloc chunk header. I had limited success with this, as I wasn’t able to see stores that corresponded to the output of ‘show memory’ in IOS. There may have been a logical flaw in my implementation and I will try this approach once more.
Other things to note, is that the malloc chunk headers were being modified outside the functions I identified as malloc and free. This probably indicates that the addresses I have found are internally used malloc/free functions.
[edit: when I say free is recursive, I mean malloc] Also of note is that the free function I identified was being called recursively, or at least the procedure entry was part of a loop. In my implementation, I only accept the first call to free and ignore any recursive calls as identifying buffers to free. I will need to investigate this more.