• 记一次shm_open返回EINVAL的错误排查


    正文

    看redis的bg数据拷贝的时候想起进程的数据是cow的,想写个程序看看cow的细节,因为要用到信号量操作sem_post和sem_wait,信号量的创建依赖于共享内存对象shm_open,因为很久没使用过的这个系统调用结果导致出了乌龙,把第一个参数name给当成了路径+名字,结果创建的时候返回的对象句柄是-1, errno被设置成了EINVAL(无效参数)

    int shm_open(const char *name, int oflag, mode_t mode);
    

    b shm_open后打了一个断点进入shm_openni单步执行后, 发现shm_open先对name进行了处理,如果开头为/的话会先开头的所有/给干掉,应该是不允许name中有这样的特殊字符,这里的name因为被我当成path+name了,所以会走这个地方, man中的手册做创建的时候有时候也给name前加一个/,但是加了和不加效果是一样的。

    然后程序继续走,走着走着发现retq了,在中间找到了跳到retq的地方, 原来是shm_open+124中对name进行了检查看看是否还存在特殊字符/,如果存在则设置errno后跳转到retq

    shm_open + 356中设置errno为EINVAL,再继续就是retq了

    这里有一个很奇怪的点,上面判断的shm_open+356将errno设置成了EINVAL主要是通过0x16判断得到,当然也可以通过errno的地址,后面标明了地址0x7ffff7dd3fb8,所以我们可以先打印errno的地址,然后再这段汇编里面对比查找一下,但是打印出来的结果却和汇编中给出的地址不同,这个点没有搞明白上stackoverflow提了个问题,这段汇编是没问题的一定是给errno设置EINVAL,

    shm_open+356 到shm_open+365貌似也是不复杂,主要就三句代码,于是耐心下来读一读

    首先是将rip+0x202689这个作为一个8字节的地址,把这个地址对应的变量赋值给eax寄存器,但是打印出来的明明是0x7ffff7dd3fb1,但它的指在gdb中已经表明了是0x7ffff7dd3fb8,这是gdb中让我很困惑的一点,其实rip寄存器永远都是指向下一条要执行的指令,当我们把断点打到这一行的时候,这一行代码还没有运行,运行的时候rip会被更新成下一行指令的地址,然后通过x /xg查看地址0x7ffff7dd3fb8后发现是0xffffffffffffff60,这个值将被放进rax寄存器中

    中间的更改ebx就不看了,看下一句怎么把0x16放入errno中的,这句话的意思是将 0x16放入到 $fs + $rax的地址对应的8字节内存空间中,(rax和)fs都能直接看到,但是$fs是作为段寄存器,gdb中显示的虽然是0但使用fs寄存器的时候还会加上基址偏移是gdb没有告诉我们的于是我们得拿到这个基址偏移, 这里可以参照此处如果获取fs寄存器基址, 简单来说就是有一个函数可以帮我们拿到,这个时候查看被赋完得值确实是0x16,但是地址为什么还是和&errno相等,就不填坑了

  • 相关阅读:
    FastJson的简单使用
    一些没用过的方法的学习
    Windows系统激活
    mysql数据库运行性能检查脚本
    基于windows 的Apache HTTP Server的一次小安装
    MySQL、Oracle批量插入、更新批量inisert、update
    IDEA中右键没有“Subversion”相关目录解决方法
    关于spring boot项目启动报错问题
    我的2016
    用intellij IDEA 编写时,无编程提示问题
  • 原文地址:https://www.cnblogs.com/ishen/p/14303398.html
Copyright © 2020-2023  润新知