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
Since the function arguments are stored at
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);
EBP+XXit's possible to detect when a division is performed by a function argument.
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.
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)
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.
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 uses
std::mapdata 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::mapfunctions 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
Beepfunction 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.