【问题标题】:Segmentation fault occurs when calling function in the Pin tool在 Pin 工具中调用函数时出现分段错误
【发布时间】:2013-11-22 05:15:13
【问题描述】:

我目前正在构建一个 Pin 工具,用于检测来自 Linux 应用程序的未初始化读取,基于此 blog post
也可以从博客中看到作者的代码。

因为这个是针对 Windows 的,所以我尝试创建一个兼容 Linux 的。 但是当我使用应用程序执行我的 Pin 工具时,会发生分段错误。 奇怪的是调用函数时发生故障(当pin工具调用taint_get函数时发生故障,该函数在taint_define函数中),不是因为访问未初始化的堆指针或此类点一般分段错误。

分段错误的点是这样的:

VOID Instruction(INS ins, VOID *v)
{
   Uninit_Instruction(ins, v);
}

void Uninit_Instruction(INS ins, void* v)
{
   // check if the stack pointer is altered (i.e. memory is allocated on the
   // stack by subtracting an immediate from the stack pointer)
   if(INS_Opcode(ins) == XED_ICLASS_SUB &&
      INS_OperandReg(ins, 0) == REG_STACK_PTR &&
      INS_OperandIsImmediate(ins, 1)) 
   {
      // insert call after, so we can pass the stack pointer directly
      INS_InsertCall(ins, IPOINT_AFTER, (AFUNPTR)taint_undefined,
             IARG_REG_VALUE, 
             REG_STACK_PTR, 
             IARG_ADDRINT, (UINT32) INS_OperandImmediate(ins, 1),
             IARG_END);
   }

   UINT32 memOperands = INS_MemoryOperandCount(ins);

   for (UINT32 memOp = 0; memOp < memOperands; memOp++)
   {
      if (INS_MemoryOperandIsRead(ins, memOp))
      {
     INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)taint_check,
            IARG_INST_PTR,
            IARG_MEMORYOP_EA, memOp,
            IARG_MEMORYREAD_SIZE,
            IARG_END);
      }

      if (INS_MemoryOperandIsWritten(ins, memOp))
      {
     INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)taint_define,
            IARG_MEMORYOP_EA, memOp,
            IARG_MEMORYWRITE_SIZE,
            IARG_END);
      }
   }

}

回调函数如下所示:

// Taint this address as written
void taint_define(ADDRINT addr, UINT32 size)
{
   // Debug purpose
   TraceFile << "taint_define: " << addr << ", " << size << endl;

   // taint the addresses as defined, pretty slow, but easiest to implement
   for (UINT32 i = 0; i < size; i++) 
   {
      //TraceFile << "taint_define_loop size: " << size << endl;
      UINT32 *t = taint_get(addr + i);
      TraceFile << "after taint_get" << endl;
      UINT32 index = (addr + i) % 0x20000;

      // define this bit
      t[index / 32] |= 1 << (index % 32);
   }
}


inline UINT32* taint_get(ADDRINT addr)
{
   // Debug purpose
   TraceFile << "taint_get: " << addr;

   // allocate memory to taint these memory pages
   if(taint[addr / 0x20000] == NULL) {
      // we need an 16kb page to track 128k of memory
      /*
        taint[addr / 0x20000] = (UINT32 *) W::VirtualAlloc(NULL, 0x20000 / 8,
    MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
      */
      taint[addr / 0x20000] = (UINT32*)malloc(0x20000/8);
   }

   return taint[addr / 0x20000];
}

输出如下:

C:Tool (or Pin) caused signal 11 at PC 0x7fcf475e08a4
segmentation fault (core dumped)

日志在这里。

Watched Image count: 0x1
WatchedImage: unread_3vars
Uninit_Image
Uninit_Image
Thread start
taint_define: 0x7fff06930d58, 0x8

我目前正在开发 Fedora 核心 17 x86-64、gcc 4.7.2 和 Pin 2.12-58423。
而且,我的pin工具代码附在here

【问题讨论】:

  • 您应该在-Wall -Wextra - pedantic 上使用所有警告进行编译,然后修复代码,直到编译器不再发出警告。然后使用选项-g 使用调试符号编译并使用调试器(例如gdb)运行程序。然后它会向您显示程序崩溃的确切位置,并允许您检查正在使用的变量。

标签: c++ c segmentation-fault intel-pin


【解决方案1】:

根据这篇博文,我目前正在构建一个 Pin 工具,用于检测来自 Linux 应用程序的未初始化读取。

这并不能真正回答您的问题,您可能还有其他原因学习 Pin 工具,但是...

我们发现基于 Pin 的工具不足以检测非玩具程序。如果您的目标是检测未初始化的内存读取,请考虑使用Memory Sanitizer

【讨论】:

    【解决方案2】:

    readb4write 仅为 32 位。我不知道你是如何编译它的,但即使你添加 -m32 它可能仍然无法工作。这就是我的情况,但我在 Windows 上运行它。
    您只能通过查看注释来判断它是 32 位的:“// 我们使用 0x8000 块 128k 来污染”
    0x8000 x 128kb = 4294967296 这是 32 位进程的虚拟范围限制。
    在 x64 上,您需要在 taint_get 方法中满足 48 bit addresses 的需求。这仍然是一个幼稚的实现,但其他一切也是如此

    typedef UINT64 * TTaint[0x80000];
    TTaint *taintTable[0x10000] = { 0 };
    
    
    inline UINT64 *taint_get(ADDRINT addr)
    {
       UINT64 chunkAddress = addr / 0x20000; //get number address of 128kb chunk. 
    
       UINT64 firstLevAddr = chunkAddress / 0x10000;
       UINT64 secondLevelAddr = chunkAddress % 0x10000;
    
       TTaint *taint = NULL;
       if (taintTable[firstLevAddr] == NULL){
           taintTable[firstLevAddr] = (TTaint*)W::VirtualAlloc(NULL, sizeof(TTaint),
            MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
        }
       taint = taintTable[firstLevAddr];
    
       // allocate memory to taint these memory pages
       if ((*taint)[secondLevelAddr ] == NULL) {
          // we need an 16kb page to track 128k of memory
            (*taint)[secondLevelAddr] = (UINT64 *)W::VirtualAlloc(NULL, 0x20000 / 8,
                MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
       }
       return (*taint)[secondLevelAddr];
    }
    

    此外,大多数(如果不是全部)变量需要是 UINT64 而不是 UINT32。并且需要将 32 改为 64。

    还有一个问题我还没有解决。有一行检测访问未初始化内存的指令是否属于正在检查的程序。它不太可能在 x64 中仍然有效:
    (ip & 0xfff00000) == 0x00400000)
    如果我设法让它工作,我将在 github 上发布代码。

    【讨论】:

      猜你喜欢
      • 2011-01-08
      • 1970-01-01
      • 1970-01-01
      • 2017-09-21
      • 2018-12-15
      • 1970-01-01
      相关资源
      最近更新 更多