You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux用户态下如何检测C函数对共享内存的重复读取?

解决方案:检测函数对同一内存地址的重复读取

针对你的需求,在Linux用户态下无需修改库代码,可通过以下几种可靠方案实现运行时检测:

方案一:内存保护+信号处理(轻量无依赖)

利用Linux内存权限控制结合SIGSEGV信号处理,拦截重复读取操作:

实现步骤

  1. 初始化监控环境

    • 将目标共享内存区域的权限临时改为PROT_NONE(不可读不可写)。
    • 分配一块与监控区域大小一致的影子位图,用于记录每个内存地址的访问状态(已读/未读)。
  2. 注册信号处理逻辑
    当库函数尝试读取PROT_NONE区域时会触发SIGSEGV信号,在信号处理函数中:

    • 通过siginfo_t获取触发错误的内存地址。
    • 校验地址是否在监控范围内:
      • 若未被访问:临时将地址所在页设为PROT_READ,允许本次读取;读取完成后改回PROT_NONE,并标记该地址为已读。
      • 若已被访问:直接触发运行时错误(如打印日志后调用abort())。

核心代码片段

#include <signal.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define PAGE_SIZE 4096

static void* monitored_start;
static size_t monitored_size;
static unsigned char* access_bitmap;

static void segv_handler(int sig, siginfo_t* info, void* ctx) {
    void* addr = info->si_addr;
    if (addr >= monitored_start && addr < (char*)monitored_start + monitored_size) {
        size_t offset = (size_t)(addr - monitored_start);
        if (access_bitmap[offset]) {
            fprintf(stderr, "ERROR: Repeated read at address %p\n", addr);
            abort();
        }
        access_bitmap[offset] = 1;
        // 临时设置页为可读
        void* page_addr = (void*)((size_t)addr & ~(PAGE_SIZE - 1));
        mprotect(page_addr, PAGE_SIZE, PROT_READ);
        // 注:实际场景需确保读取完成后恢复权限,可结合线程同步或钩子实现
    } else {
        // 非监控区域的段错误,按默认逻辑处理
        signal(sig, SIG_DFL);
        raise(sig);
    }
}

void BEGIN_READ_ONCE(void* input, size_t size) {
    monitored_start = input;
    monitored_size = size;
    access_bitmap = calloc(size, 1);
    mprotect(input, size, PROT_NONE);
    
    struct sigaction sa = {0};
    sa.sa_sigaction = segv_handler;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGSEGV, &sa, NULL);
}

void END_READ_ONCE(void* input, size_t size) {
    mprotect(input, size, PROT_READ | PROT_WRITE); // 恢复原内存权限
    free(access_bitmap);
    signal(SIGSEGV, SIG_DFL);
}

优缺点

  • ✅ 无额外依赖,轻量高效,适合CI环境
  • ❌ 需处理页对齐问题,恢复权限的时机需精准控制

方案二:Intel Pin动态插桩(精准无遗漏)

Intel Pin是用户态动态插桩工具,可拦截所有内存读取指令,精准统计地址访问次数:

实现步骤

  1. 编写Pin工具

    • 拦截所有内存读取指令,注册回调函数。
    • 用哈希表记录监控范围内每个地址的读取计数。
    • 每次读取时校验计数:若≥1则触发错误,否则计数+1。
  2. 运行目标程序
    通过Pin加载工具启动程序:pin -t your_tool.so -- ./your_program

核心逻辑示例(C++)

#include "pin.H"
#include <unordered_map>
#include <iostream>

static void* g_monitor_start;
static size_t g_monitor_size;
static std::unordered_map<void*, int> g_access_count;

VOID OnMemRead(VOID* addr) {
    if (addr >= g_monitor_start && addr < (char*)g_monitor_start + g_monitor_size) {
        if (g_access_count[addr] >= 1) {
            std::cerr << "ERROR: Repeated read at address " << addr << std::endl;
            abort();
        }
        g_access_count[addr]++;
    }
}

VOID Instruction(INS ins, VOID* v) {
    if (INS_IsMemoryRead(ins)) {
        INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)OnMemRead, IARG_MEMORYREAD_EA, IARG_END);
    }
}

INT32 Usage() {
    std::cerr << "Usage: pin -t read_once_check.so -start <addr> -size <size> -- ./program" << std::endl;
    return -1;
}

int main(int argc, char* argv[]) {
    if (PIN_Init(argc, argv)) return Usage();

    // 从命令行参数获取监控区域的起始地址和大小
    g_monitor_start = (void*)strtoul(PIN_GetCommandLineArgument("-start", NULL), NULL, 16);
    g_monitor_size = strtoul(PIN_GetCommandLineArgument("-size", NULL), NULL, 10);

    INS_AddInstrumentFunction(Instruction, NULL);
    PIN_StartProgram();
    return 0;
}

优缺点

  • ✅ 精准拦截所有内存读取,无需处理内存权限问题
  • ✅ 支持预编译库,无需修改目标代码
  • ❌ 需要提前安装Intel Pin,CI环境需配置依赖

方案三:ptrace跟踪(无额外工具但性能低)

利用Linuxptrace系统调用编写跟踪程序,逐指令监控目标进程的内存读取操作:

实现步骤

  1. 编写tracer程序
    • 用ptrace(PTRACE_ATTACH)附加到目标进程。
    • 逐单步执行指令(PTRACE_SINGLESTEP),解析每条指令是否为内存读取操作。
    • 提取目标地址,统计访问次数,重复读取则触发错误。

优缺点

  • ✅ 无额外工具依赖,纯用户态实现
  • ❌ 性能极低,仅适合小范围短时间测试
  • ❌ 需处理不同架构的指令解析,实现复杂度高

方案选择建议

  • 追求轻量高效:优先选方案一,适配大多数测试场景
  • 需要精准无遗漏检测:优先选方案二,适合复杂库函数测试
  • 无法安装额外工具:可尝试方案三,仅适合简单场景

内容的提问来源于stack exchange,提问作者Gilles 'SO- stop being evil'

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 07:28:09