memcpy 绝对是 C++里的史前巨坑! - V2EX
tool2d
V2EX    C++

memcpy 绝对是 C++里的史前巨坑!

  •  
  •   tool2d Mar 23, 2023 6290 views
    This topic created in 1162 days ago, the information mentioned may be changed or developed.
    都说圣斗士不会在同一套招式下倒下两次,我偏偏就在同一个坑里,掉进去了两次。

    最近写了一个服务器程序,会随机出现一些乱字符,很不好调试,查了半天,发现是 memcpy 复制的时候,有 overlapping buffer 内存,而这个行为在 C++标准里, 是 UB 行为,官方允许复制结果出错!

    windows 上有类似功能的函数,叫 CopyMemory ,就从来没出过这种情况,真是大意了。

    其实以前遇到过这个 bug ,查到有资料说,高版本 linux 会自动判断输入范围,自动改成 memmove ,就轻视了。没想到后来 glibc 为了性能优化,中间又给改回去了。真是晕过去,API 行为都能反复横跳的吗?

    我再也不想用 memcpy 了,别和我说什么优化和性能,以后只用 memmove 走天下。
    28 replies    2023-03-24 15:30:10 +08:00
    greensea
        1
    greensea  
       Mar 23, 2023   18
    我觉得 memcpy 不检查边界这件事情对 C 程序员来说应该是常识来着的
    optional
        2
    optional  
       Mar 23, 2023 via iPhone   2
    有时候 memcpy 的结果才是你想要的呢。
    zagfai
        3
    zagfai  
       Mar 23, 2023
    我觉得 memcpy 不检查边界这件事情对 C 程序员来说应该是常识来着的
    lwh0328
        4
    lwh0328  
       Mar 23, 2023
    我也觉得 memcpy 不检查边界这件事情对 C 程序员来说应该是常识来着的
    dodng12
        5
    dodng12  
       Mar 23, 2023
    我也觉得 memcpy 不检查边界这件事情对 C 程序员来说应该是常识来着的
    tool2d
        6
    tool2d  
    OP
       Mar 23, 2023
    @greensea 习惯了 windows 开发,memcpy 从来就不需要额外检测,自动处理同一块内存里的复制搬运。微软就是一个好保姆,一切都默默帮你处理好了。

    memcpy 行为和 windows 相差甚远,单纯为了性能,我也是能理解的。但不能说 glibc 改了一半后,高版本号又给改了回去啊。这不算偷袭老年人嘛。

    https://man7.org/linux/man-pages/man3/memcpy.3.html 里 note 部分,写明了部分 glibc 版本的影响范围,我就中招了。
    ethusdt
        7
    ethusdt  
       Mar 23, 2023
    确实要注意下, memmove 如果碰到 src 地址小于 dest, 会从尾巴地方往后处理, 这样就避免了 overlapping 数据
    lixile
        9
    lixile  
       Mar 23, 2023
    asan msan tsan lsan ubsan 五管齐下 可以用工具检测
    cy18
        10
    cy18  
       Mar 23, 2023
    说出来是知道的,但是时间久了容易忘,还是得靠工具检查。
    koebehshian
        11
    koebehshian  
       Mar 23, 2023
    有重叠用 memmove ,没有重叠用 memcpy ,memcpy 都让传长度了,有没有溢出肯定程序员负责的。刚学 C 的时候,一看这俩函数功能差不多,就仔细查一下它们的区别。
    sloknyyz
        12
    sloknyyz  
       Mar 23, 2023
    你通过正经手段分配的两段内存怎么可能会重叠。还不是因为自己指针搞来搞去,出事了又来怪 memcpy 。
    tool2d
        13
    tool2d  
    OP
       Mar 23, 2023
    @sloknyyz 所谓重叠,就是 memcpy 复制内存的时候,dst 和 src 是同一块内存的不同区域。

    我就想把一块内存后半段,搬运到前半段,这需求还是挺常见的吧。
    tool2d
        14
    tool2d  
    OP
       Mar 23, 2023   2
    @koebehshian 也就是多一个 if 判断的问题,我已经打算自己写一个封装函数了。

    if (dst <= src || dst >= (src + count))
    {
     // Non-Overlapping Buffers
      memcpy();
    } else {
      memmove();
    }
    nmap
        15
    nmap  
       Mar 23, 2023
    自己菜,这个问题属于常识
    ivvei
        16
    ivvei  
       Mar 23, 2023
    UB 就是这样的了,一个版本一个样也不奇怪。
    icyalala
        17
    icyalala  
       Mar 23, 2023   4
    memcpy 对于 overlap UB 这个是写文档里的。。更早之前 glibc 的 memcpy 刚好对 overlap 还没问题,然后 2010 年马凌提了个性能优化的 patch ,性能有提升,但对 overlap 不支持了。正好 Adobe 程序员和楼主一样乱用,然后导致 flash 出现爆音之类的问题,当时很多人来辩论,甚至 Linus 还过来骂了几句: https://bugzilla.redhat.com/show_bug.cgi?id=638477

    那个作者还在知乎,楼主可以去对线: https://www.zhihu.com/question/35172305/answer/73698602
    ysc3839
        18
    ysc3839  
       Mar 23, 2023 via Android
    https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa366535(v=vs.85)
    If the source and destination blocks overlap, the results are undefined. For overlapped blocks, use the MoveMemory function.
    nightwitch
        19
    nightwitch  
       Mar 23, 2023   1
    #14 楼的代码有什么意义。。memmove 里自带这个判断。
    最好的办法就是当 memcpy 这个函数不存在,memmove 是它的上位替代
    junkun
        20
    junkun  
       Mar 23, 2023   2
    历史遗留问题,名字取得不好。rust 里这两个函数就改成了 std::ptr::copy 和 copy_nonoverlapping ,这就没人会认错了。
    Cormic
        21
    Cormic  
       Mar 23, 2023
    老话说得好:没有金箍棒别揽瓷器活
    xarthur
        22
    xarthur  
       Mar 23, 2023 via iPhone
    UB 害人(
    k9982874
        23
    k9982874  
       Mar 23, 2023 via Android
    菜是原罪
    xuboying
        24
    xuboying  
       Mar 24, 2023
    都说的很好,但是这好像是 C 的问题,C++干嘛要背锅。。。
    yolee599
        25
    yolee599  
       Mar 24, 2023
    memcpy 这个应该是一个程序中用得最多的函数,要求必须是高性能的,如果它内部加了很多判断,这样每调用一次性能都有损失。一个程序中有很多 memcpy 损失的性能就很可观了,因为大部分场景不需要 overlapping 但还是做了判断。
    Yeen
        26
    Yeen  
       Mar 24, 2023
    我记得多年前,很多公司会专门用 memcpy 的传递参数顺序来出题。
    tool2d
        27
    tool2d  
    OP
       Mar 24, 2023
    @yolee599 我已经掉坑里两次了,绝对不想要第三次的体验。已经全局#undef memcpy 了,全部走封装函数。

    UB 不是说能稳定复现的 BUG ,我宁可每次调用函数前,多判断一次的,牺牲不了什么性能。


    @ysc3839
    文档虽让这样说,但其实微软的 memcpy 是 crt 里开源的代码,感觉从来没出现过这类问题。我试了一下安卓,也没问题。仅仅是部分 linux 下的 glibc 有问题。
    smdbh
        28
    smdbh  
       Mar 24, 2023
    感觉是用 memcpy 干 memmove 的活啊
    About     Help     Advertise     Blog     API     FAQ     Solana     2545 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 76ms UTC 16:00 PVG 00:00 LAX 09:00 JFK 12:00
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86