I had been thinking of dynamic ways to catch programming error without making the detection logic complex. My decision was to write a tool that can detect division by zero errors when the division is performed by a function argument. The tool works as follows.
The tool inspects division instructions that are reading stack via
EBP
.
if (INS_Opcode(ins) == XED_ICLASS_IDIV || INS_Opcode(ins) == XED_ICLASS_DIV)
{
[...]
if (INS_IsStackRead(ins) && INS_RegRContain(ins, REG_EBP) && INS_IsMemoryRead(ins))
{
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)TrackDivisionByArg, IARG_INST_PTR, IARG_MEMORYREAD_EA, IARG_REG_VALUE, REG_EBP, IARG_END);
}
Since the function arguments are stored at EBP+XX
it's possible to detect when a division is performed by a function argument.
VOID TrackDivisionByArg(ADDRINT inst_ptr, ADDRINT memoryread_ea, ADDRINT reg_ebp)
{
// Do we read argument of the function (EBP+XX)?
if (memoryread_ea >= reg_ebp)
{
[...]
However, this doesn't mean there is a division by zero error because there might be a test for zero. To filter out obvious false positives the tool inspects if the parameter is accessed before the division.
else if ((INS_Opcode(ins) == XED_ICLASS_CMP || INS_Opcode(ins) == XED_ICLASS_MOV) && INS_IsStackRead(ins) && INS_RegRContain(ins, REG_EBP) && INS_OperandIsImmediate(ins, 1))
{
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)TrackAccessToArgs, IARG_INST_PTR, IARG_MEMORYREAD_EA, IARG_REG_VALUE, REG_EBP, IARG_REG_VALUE, REG_ESP, IARG_END);
}
The tool checks the functions on isolation so if the function parameter is checked for zero by the caller the program may report division by zero error. This is the consequence of the design.The tool uses
std::map
data structure to maintain the state of the analysis. It also contains other research implementation that is not mentioned in this blog post. The source code is available to download here.If you use
std::map
functions you have to use mutex and write lock to protect the data structure from potential corruption unless the target uses no threads.To get better performance it's better to do analysis once the application exists rather than on-the-fly. However, this increases the memory footprint as the data can be accumulating during the execution.
Sometimes it might be a good idea to use Window
Beep
function to make noise if a bug is detected.While it's possible to write efficient Pintools to detect bugs, sometimes if the tool is made to depend on the target application it can perform better.