memset(addr , 0 , size) 导致Bus error问题分析

news2024/12/28 6:17:06

导致问题出现的demo代码

#define SH_DEV_MEM "/dev/mem"
#define myerror printf
// 获取由mmap映射的设备物理内存
static void *mymmap(u32 offset, u32 size, u8 is_rd_only, u8 is_clear)
{
        void *ptr;
        s32 fd;
        offset = 0x45E00000;
        size = 0x1000;
        is_rd_only = 0;
        is_clear = 1;

        /* open the shared memory object */
        // SH_DEV_MEM is "/dev/mem"
        if (is_rd_only)
                fd = open(SH_DEV_MEM, O_RDONLY | O_SYNC);
        else
                fd = open(SH_DEV_MEM, O_RDWR | O_SYNC);
        if (fd == -1)
        {
                myerror("Open %s: %s\n", SH_DEV_MEM, strerror(errno));
                return NULL;
        }

        // get a pointer to a piece of the shared memory, note that we only map in the amount we need to
        // void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
        if (is_rd_only)
                ptr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, offset);
        else
                ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);

问题现象

在用户态通过mmap映射一段物理地址到用户空间,执行memset(ptr , 0, 0x100)时会导致Bus error:

...
fd = open("/dev/mem", O_RDONLY | O_SYNC);
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x45e00000);
memset(ptr , 0, 0x100);

在这里插入图片描述
但以下情况不会:

  1. memset(ptr , val , size),如果val非0,不会出现Bus error
  2. memset(ptr , val , size),如果val为0,size<0x100,不会出现Bus error
  3. 0x45e00000地址如果添加到memory节点,不会出现Bus error
    在这里插入图片描述

问题分析

通过gdb分析,发现程序停在dc zva指令
在这里插入图片描述
查看arm v8官方文档发现,如果memory的类型是device type,执行dc zva时会有对齐错误产生,
(ps:dc zva cache zero by vir address)
在这里插入图片描述
在内核加打印追踪,发现确实产生了alignment fault
在这里插入图片描述
然后分析/driver/char/mem.c驱动,在mmap的时候,会设置内存的属性,当mmap的物理地址在memblock memory范围内,会设置内存属性为PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN),即normal memory,如果不在memblock memory范围,则设置为PTE_ATTRINDX(MT_DEVICE_nGnRnE) | PTE_PXN | PTE_UXN),即device memory,这解释了,为什么0x45e00000添加到memory节点则不会出现Bus error的原因

static int mmap_mem(struct file *file, struct vm_area_struct *vma)
{
    size_t size = vma->vm_end - vma->vm_start;
    phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT;

    /* It's illegal to wrap around the end of the physical address space. */
    if (offset + (phys_addr_t)size - 1 < offset)
        return -EINVAL;

    if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
        return -EINVAL;

    if (!private_mapping_ok(vma))
        return -ENOSYS;

    if (!range_is_allowed(vma->vm_pgoff, size))
        return -EPERM;

    if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size,
                        &vma->vm_page_prot))
        return -EINVAL;

    vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
                         size,
                         vma->vm_page_prot);

    vma->vm_ops = &mmap_mem_ops;

    /* Remap-pfn-range will mark the range VM_IO */
    if (remap_pfn_range(vma,
                vma->vm_start,
                vma->vm_pgoff,
                size,
                vma->vm_page_prot)) {
        return -EAGAIN;
    }
    return 0;
}

// arch/arm64/mm/mmu.c
pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
                  unsigned long size, pgprot_t vma_prot)
{
    if (!pfn_valid(pfn)){
        return pgprot_noncached(vma_prot);
    }
    else if (file->f_flags & O_SYNC)
    {
        return pgprot_writecombine(vma_prot);
    }
    return vma_prot;
}
EXPORT_SYMBOL(phys_mem_access_prot);

#define pgprot_noncached(prot) \
    __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRnE) | PTE_PXN | PTE_UXN)
#define pgprot_writecombine(prot) \
    __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN)
#define pgprot_device(prot) \
    __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_PXN | PTE_UXN)
#define __HAVE_PHYS_MEM_ACCESS_PROT

int pfn_valid(unsigned long pfn)                                                                                                                               
{                                                                               
    return (pfn & PFN_MASK) == pfn && memblock_is_map_memory(pfn << PAGE_SHIFT);
}                                                                               
EXPORT_SYMBOL(pfn_valid); 

考虑到val为非0,不会出现Bus error,怀疑是glibc的memset,对val为0而且cout > 0x100的操作做了特殊的处理,查看glibc的源码,发现如果set的val,如果等于0,确实会做不同的操作,执行了dc zva指令,这解释了为什么非0的val会没问题,且size<0x100,也不会出现Bus error

// sysdeps/aarch64/memset.S
ENTRY_ALIGN (MEMSET, 6)

    DELOUSE (0)
    DELOUSE (2)

    dup v0.16B, valw
    add dstend, dstin, count

    cmp count, 96
    b.hi    L(set_long)
    cmp count, 16
    b.hs    L(set_medium)
    mov val, v0.D[0]
    
...

L(set_long):
    and valw, valw, 255
    bic dst, dstin, 15
    str q0, [dstin]
    cmp count, 256//
    ccmp valw, 0, 0, cs //cout > 0x100,并且val是0,则会使用zva指令,
    b.eq L(try_zva)
L(no_zva):
    sub count, dstend, dst  /* Count is 16 too large.  */
    sub dst, dst, 16        /* Dst is biased by -32.  */
    sub count, count, 64 + 16   /* Adjust count and bias for loop.  */
1:  stp q0, q0, [dst, 32]
    stp q0, q0, [dst, 64]!
L(tail64):
    subs    count, count, 64
    b.hi    1b
2:  stp q0, q0, [dstend, -64]
    stp q0, q0, [dstend, -32]
    ret

L(try_zva):
#ifdef ZVA_MACRO
    zva_macro
#else
    .p2align 3
    mrs tmp1, dczid_el0
    tbnz    tmp1w, 4, L(no_zva)
    and tmp1w, tmp1w, 15
    cmp tmp1w, 4    /* ZVA size is 64 bytes.  */
    b.ne     L(zva_128)

    /* Write the first and last 64 byte aligned block using stp rather
       than using DC ZVA.  This is faster on some cores.
     */
L(zva_64):
    str q0, [dst, 16]
    stp q0, q0, [dst, 32]
    bic dst, dst, 63
    stp q0, q0, [dst, 64]
    stp q0, q0, [dst, 96]
    sub count, dstend, dst  /* Count is now 128 too large.  */
    sub count, count, 128+64+64 /* Adjust count and bias for loop.  */
    add dst, dst, 128
    nop
1:  dc  zva, dst
    add dst, dst, 64
    subs    count, count, 64
    b.hi    1b
    stp q0, q0, [dst, 0]
    stp q0, q0, [dst, 32]
    stp q0, q0, [dstend, -64]
    stp q0, q0, [dstend, -32]
    ret

    .p2align 3
L(zva_128):
    cmp tmp1w, 5    /* ZVA size is 128 bytes.  */
    b.ne    L(zva_other)

    str q0, [dst, 16]
    stp q0, q0, [dst, 32]
    stp q0, q0, [dst, 64]
    stp q0, q0, [dst, 96]
    bic dst, dst, 127
    sub count, dstend, dst  /* Count is now 128 too large.  */
    sub count, count, 128+128   /* Adjust count and bias for loop.  */
    add dst, dst, 128
1:  dc  zva, dst
    add dst, dst, 128
    subs    count, count, 128
    b.hi    1b
    stp q0, q0, [dstend, -128]
    stp q0, q0, [dstend, -96]
    stp q0, q0, [dstend, -64]
    stp q0, q0, [dstend, -32]
    ret
...

#endif
END (MEMSET)

处理建议

把需要mmap的那段内存添加到memory和reserved-memory节点,以0x45E00000例:

    memory@50000000 {
        device_type = "memory";
        reg = <HIGH32(AP1_REE_MEMBASE) LOW32(AP1_REE_MEMBASE) HIGH32(AP1_REE_MEMSIZE) LOW32(AP1_REE_MEMSIZE) \
               HIGH32(AP1_2ND_MEMBASE) LOW32(AP1_2ND_MEMBASE) HIGH32(AP1_2ND_MEMSIZE) LOW32(AP1_2ND_MEMSIZE) \
               0x0 (VDSP_MEMBASE+0x4000) 0x0 (VDSP_SHARE_MEMSIZE + VDSP_MEMSIZE -0x4000)
               0x0 LOW32(0x45E00000) 0 0x200000>;
    };

    reserved-memory {
        #address-cells = <2>;
        #size-cells = <2>;
        ranges;

        mmap_test {
            /* reserve mem for mmap*/
            compatible = "mmap_test";
            //no-map;//注意不能用no-map标志
            reg = <0x0 LOW32(0x45E00000) 0 0x100000>;
        };
    };

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/736902.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Step By Step】VM安装redhat-server7.9搭建Oracle19C RAC(一)环境配置

文章目录 环境规划网络规划文件系统规划rac用户规划grid与oracle用户规划ASM规划 虚拟机设置搭建虚拟机自定义网卡安装操作系统 操作系统设置关闭services修改/etc/hosts创建用户与组创建文件目录设置环境变量设置内核参数资源限制添加 etc/pam.d/login关闭大页关机挂载本地ISO…

【C++初阶】:vector的基本介绍

vector的基本介绍 一.vector(向量)介绍二.vector原型三.构造四.区别reserve和resize五.二维数组 一.vector(向量)介绍 二.vector原型 vector主要分为两个模板参数&#xff0c;一个是T&#xff0c;T是将数据类型进行实例化&#xff08;本质就是一个数组&#xff09;。第二个参数…

idea 搭建 SpringBoot 集成 mybatis

编译器&#xff1a;IDEA 环境&#xff1a;win10&#xff0c;jdk1.8&#xff0c;maven3.5 数据库&#xff1a;mysql 5.7 一、打开IDEA新建项目 1. 如果你是第一次使用IDEA&#xff0c;那么你需要配置你本地的maven&#xff0c;点击右下角 Configure&#xff0c;如已配置请忽…

第三章:Linux简介及面试常问问题

目录 一、Linux发展史 1.Linux前身-Unix 2.Linux诞生 3.开源文化 4.Linux系统特点 5.Linux操作系统优点 6.Linux操作系统发行版 7.Linux内核命名规则 二、Linux系统的安装 三、linux系统分区简介及如何分区 1.自动分区 2.手动分区 3.linux下查看分区信息和剩余空间…

STM32CubeMX安装

一、配置JAVA环境 1.相关资料链接 链接: CubeMX 提取码&#xff1a;13ec 双击改exe文件。 2.更改默认的安装路径 3.在该exe文件同等目录下&#xff0c;新建一个JAVA-Enviroment文件夹。 选择更改安装路径&#xff0c;放在刚才新建的JAVA-Enviroment文件夹中。 4.等待安装完…

RabbitMQ - 单机部署(超详细)

RabbitMQ部署 1.单机部署 我们在Centos7虚拟机中使用Docker来安装。 1.1.下载镜像 方式一&#xff1a;在线拉取 docker pull rabbitmq:3-management方式二&#xff1a;从本地加载 也可以从网上搜索 RabbitMQ 的 tar 包下载下来 上传到虚拟机中后&#xff0c;使用命令加载…

2023高校夏令营 | 第七年,我们又开营啦!

进入7月&#xff0c;成都的天气就不再像之前那样温和&#xff0c;迎接我们的是热浪滔天&#xff0c;以及一场场猝不及防的高温暴雨。如同当下的毕业季&#xff0c;脱离校园庇护的应届生们涌入就业市场&#xff0c;开始应对来自社会、职场的各种风雨考验。 为了加快推进产教融合…

js深拷贝、js使用递归实现深拷贝

相信看到这篇文章之前你应该多多少少了解过什么是深拷贝了为什么要什么拷贝了 这里就不介绍什么是深拷贝、浅拷贝了 最优解深拷贝方法&#xff1a;递归 这里为了方便直接在vue的页面里面演示了&#xff0c;在其他js文件中使用注意下this mounted() {const obj {str: "字…

实战解决百度旋转验证码

1、效果演示 2、如何识别 2.1准备数据集 首先需要使用爬虫&#xff0c;对验证码图片进行采集&#xff0c;尽量每一种类型都要采集到。 2.2图像矫正 接下来对采集的数据进行人工校正 2.3数据清洗 &#xff08;1&#xff09;对数据进行进行旋转&#xff0c;达到增加数据量的目…

44. 日期交叉问题

文章目录 题目需求思路一实现一原理二实现二学习链接题目来源 题目需求 现有各品牌优惠周期表&#xff08;promotion_info&#xff09;如下&#xff0c;其记录了每个品牌的每个优惠活动的周期&#xff0c;其中同一品牌的不同优惠活动的周期可能会有交叉。 现要求统计每个品牌…

聊一聊Java中的Steam流 | 京东物流技术团队

1 引言 在我们的日常编程任务中&#xff0c;对于集合的制造和处理是必不可少的。当我们需要对于集合进行分组或查找的操作时&#xff0c;需要用迭代器对于集合进行操作&#xff0c;而当我们需要处理的数据量很大的时候&#xff0c;为了提高性能&#xff0c;就需要使用到并行处…

Golang内存分配及垃圾回收

为什么需要垃圾回收&#xff1f; 自动释放不需要的对象&#xff0c;让出存储器资源&#xff0c;无需程序员手动执行 Go V1.3之前是标记-清除算法 具体步骤 缺点&#xff1a;程序卡顿、扫描整个heap、数据清除会产生heap碎片 V1.3之后&#xff0c;做了简单的优化 V1.5之后&a…

AIFORE Smart Fuzzing Based on Automatic Input Format Reverse Engineering

AIFORE: Smart Fuzzing Based on Automatic Input Format Reverse Engineering 1 中国科学院信息工程研究所2清华大学网络科学与网络空间研究院;中关村实验室3华为技术有限公司奇异安全实验室4中国科学院大学网络安全学院5中国人民大学6洛桑联邦理工学院 论文链接 &#xff1a…

PHP实战开发26-使用PHP生成图片验证码并进行校验

文章目录 一、前言二、什么是验证码&#xff1f;三、PHP生成验证码图像3.1 生成验证码文字3.2 创建图像并绘制验证码文字3.3 将验证码存储到Session中 四、前端使用jQuery验证输入4.1 步骤一&#xff1a;创建HTML结构和CSS样式4.2 使用jQuery发送AJAX请求进行校验4.3 步骤三&am…

Head Pose Estimation头部姿态估计任务的基本了解:从数据集开始

目录 前言一、HPE是什么&#xff1f;二、常用数据集1.COFW2.WFLW3.AFLW2000-3D4.300W-LP 三、SOTA workCVPR2022:SynergyNetCVPR2021 Workshop:ASMNet 四、我们想做的贡献 前言 为了检测司机的疲劳状态&#xff0c;除了基于人脸关键点的检测去判断是否闭眼&#xff0c;是否打哈…

基础算法-差分

差分其实就是求前缀和的逆运算 差分数组&#xff1a; Step1 首先给定一个原数组a&#xff1a;a[1], a[2], a[3],,,,,, a[n]; 然后我们构造一个数组b &#xff1a; b[1] ,b[2] , b[3],,,,,, b[i]; 使得 a[i] b[1] b[2 ] b[3] ,,,,,, b[i] 也就是说&#xff0c;a数组是b…

nvm升级node版本

1.首先有安装nvm和配置环境的前提 2、命令查看&#xff0c;切换到D盘目录下 3、安装到指定的版本 nvm安装指定版本的node 输入&#xff1a;nvm install 16.18.1 4、查看已有的node版本 nvm list 5、项目终端查看

【uniapp开发小程序】实现点击跳转手机通话 拨打电话功能

效果图&#xff1a; 代码展示&#xff1a; <template><view class"page-map"><view class"btn" click"telFun()" style"text-align: center;">电话咨询</view></view> </template> <script&g…

如何使用idea来查找所有未使用的代码?

一、目的 通过idea快速找到项目中没有被使用的代码 二、操作步骤 2.1 Code -> Analyze Code -> Run Inspection by Name 2.2 输入Unused declaration -> 查询没有被使用的方法和类 2.3 选择要扫描的范围 2.4 根据结果进行删除&#xff08;反复几次就可以清除干净&a…

如何获取局域网内海康摄像头的IP地址

文章目录 问题解决方法图像分类网络AlexNetVGGNetGooLeNet系列ResNetDenseNetSwin TransformerMAECoAtNetConvNeXtV1、V2MobileNet系列MPViTVITSWAEfficientNet系列MOBILEVITEdgeViTsMixConvRepLKNetTransFGConvMAEMicroNetRepVGGMaxViTMAFormerGhostNet系列DEiT系列MetaFormer…