V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zzzkkk
V2EX  ›  C++

fsantinize 弱智

  •  
  •   zzzkkk · 224 天前 · 5091 次点击
    这是一个创建于 224 天前的主题,其中的信息可能已经有所发展或是发生改变。

    fsantinize 还没智能到那种程度 简直给码农添加负担 给一个静态字符数组前面添加几个空格都会报错

    请看

    图 1

    图 1

    图 2

    图 2

    图 3

    图 3

    编译时报图 3 的错误 运行就报 ==1856626==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff15ef59e9 at pc 0x55d429175340 bp 0x7fff15ef5940 sp 0x7fff15ef5938

    第 1 条附言  ·  224 天前
    是把字符串连双引号拷贝进浏览器 console 用.length 算出来的 是 30 再加 1 等于 31

    刚才把下标 31 去掉了 用 sizeof 打印出来是 31

    但编译加上 fsanitize 还是报错

    这贴讨论的是 fsanitize 的弱智 但它还想做全面的事情 限制了码农的能力 增加了繁琐

    你们着眼的细节是我在增加了很多空格后 无非是忘了改下标

    你们自己去加上 fsanitize 编译看看
    85 条回复    2023-09-17 05:50:50 +08:00
    eagleoflqj
        1
    eagleoflqj  
       224 天前 via iPhone
    你是真不懂还是在钓鱼?
    zzzkkk
        2
    zzzkkk  
    OP
       224 天前
    @eagleoflqj
    不懂 没做过 c c++项目 只会写小代码
    zzzkkk
        3
    zzzkkk  
    OP
       224 天前
    编译报错是合理的

    运行时报错不合理啊
    XiLingHost
        4
    XiLingHost  
       224 天前
    你数数这数组多长了
    zzzkkk
        5
    zzzkkk  
    OP
       224 天前
    @XiLingHost
    这么几个字节对现代计算机来说算啥
    有必要 sanitize 不能运行?
    nightwitch
        6
    nightwitch  
       224 天前
    笑拉了
    XiLingHost
        7
    XiLingHost  
       224 天前
    @zzzkkk 我的意思是,你看看你的初始化字符串的长度和为这个 char[]分配的长度,你的代码里为它分配了 25 个 char ,去掉末尾的\0 ,你的字符串长度应该是 24 个 ascii 字符
    des
        8
    des  
       224 天前 via iPhone
    @eagleoflqj 估计是来炸鱼的
    XiLingHost
        9
    XiLingHost  
       224 天前
    你实在搞不明白,你就直接 char s[INT_MAX]吧
    maplezzz
        10
    maplezzz  
       224 天前   ❤️ 1
    哈哈哈,给我逗笑了
    Hconk
        11
    Hconk  
       224 天前 via iPhone
    @zzzkkk 认真的?😅
    ershierdu
        12
    ershierdu  
       224 天前
    1. 为什么编译时 warning 是合理的,而运行时报错就不合理呢?
    2. 从内存的层面上看,OP 期望运行时它是怎么样的呢?
    yolee599
        13
    yolee599  
       224 天前 via Android
    搁这钓鱼呢?笑死我了,报错那么明显
    zzzkkk
        14
    zzzkkk  
    OP
       224 天前
    char 的长度下标的确写错了

    但下面这样也不行 总共 30 个字符 + 一个末尾'\0'

    ![](
    jonty
        15
    jonty  
       224 天前
    。。。。。。
    zzzkkk
        16
    zzzkkk  
    OP
       224 天前 via Android
    我既然能写出这种 while 循环 数组下标还是知道的 只是被 fsantinize 冲昏了
    zzzkkk
        17
    zzzkkk  
    OP
       224 天前 via Android
    而且 leetcode 的函数设计有问题 一个函数传只一个指针 还开 fsantinize
    weidaizi
        18
    weidaizi  
       224 天前   ❤️ 3
    看完标题:OP 搁着炸鱼?
    看完下面回复:难道这 OP 真的不懂?
    看了一下 OP 的其他帖子:哦,原来是真小白啊
    GeruzoniAnsasu
        19
    GeruzoniAnsasu  
       224 天前   ❤️ 2
    我此时很想看到那个秒杀 nginx 的哥们来点拨一下 OP
    LykorisR
        20
    LykorisR  
       224 天前
    难绷,建议去问 gpt ,这种事浪费大家时间不值得
    Kumo31
        21
    Kumo31  
       224 天前
    建议还是放弃使用 c/cpp 吧
    leaflxh
        22
    leaflxh  
       224 天前
    能留个微信吗,用 sha256 加密一下发出来
    liuguangxuan
        23
    liuguangxuan  
       224 天前 via Android
    @leaflxh 哈哈,这个梗我居然看懂了
    zzzkkk
        24
    zzzkkk  
    OP
       224 天前 via Android
    @leaflxh
    你以为我连这都不知道吗 无法还原
    cnbatch
        25
    cnbatch  
       224 天前
    人眼数很容易数错的,对于比较长的字符串,最好还是写成
    char s[] = "这是字符串"
    中括号内不写任何数字

    如果想知道 s 数组有多长,那就 sizeof(s)

    以 14 楼的图为例,把 s[31]的数字去掉,改成 s[],接着下面一行加一句
    printf("sizeof s: %d\n", (int)sizeof(s));
    就可以准确知道实际大小
    zzzkkk
        26
    zzzkkk  
    OP
       224 天前
    @cnbatch
    是把字符串连双引号拷贝进浏览器 console 用.length 算出来的 是 30 再加 1 等于 31

    刚才把下标 31 去掉了 用 sizeof 打印出来是 31

    但编译加上 fsanitize 还是报错

    这贴讨论的是 fsanitize 的弱智 但它还想做全面的事情 限制了码农的能力 增加了繁琐

    你们着眼的细节是我在增加了很多空格后 无非是忘了改下标

    你们自己去加上 fsanitize 编译看看
    cy18
        27
    cy18  
       224 天前
    @zzzkkk #26 "无非”是忘了改下标...
    fsantinize 帮你找出了这么严重的问题,这时候你就应该默默在心中唱一首《感恩的心》。
    zzzkkk
        28
    zzzkkk  
    OP
       224 天前 via Android
    @cy18
    关键是没有下标 或刚好等于 或远远大于数组元素个数 fsanitize 都报错 这不是假阳性么?所以说它弱智 但还想干全能的事
    geelaw
        29
    geelaw  
       224 天前
    请问楼主期待这段代码发生什么?

    int a[1];
    a[0] = 1;
    a[1] = 2;

    用超长字符串初始化不够长的字符数组的效果和上面差不多。另外楼主并不是忘了改下标,而是忘了改长度,这是两个不同的概念。

    另外 char s[] = "..."; char s2[strlen(s) + 1]; 不是合法的 C++ 代码,没有拒绝编译已经很给面子了。
    zzzkkk
        30
    zzzkkk  
    OP
       224 天前 via Android
    @geelaw
    是 gcc 编译器

    你那段代码 不就是越界么 段错误

    这贴的主题是 fsanitize 检查出假阳性 因为正确的数组长度比如 31 它也报错

    我当然知道越界
    zzzkkk
        31
    zzzkkk  
    OP
       224 天前 via Android
    这个是合法的 char s2[strlen(s)+1]

    不合法的话 你用数组长度+1 的 size 用 malloc 返回指针都不合法
    zzzkkk
        32
    zzzkkk  
    OP
       224 天前 via Android
    我不知道越界的话

    这种高质量 while 循环都写不出来
    zzzkkk
        33
    zzzkkk  
    OP
       224 天前 via Android
    一开始数组下标是正确的 我当时好像去掉了下标 后来又加上正确下标 我发现 fsanitize 报假阳性

    为了截图 才改了代码 改了字符串

    你们没有关注假阳性 反而关注细枝末节
    geelaw
        34
    geelaw  
       224 天前
    @zzzkkk #30 所以你对 char s[25] = "长度为 30 的字符串长度为 30 的字符串"; 出错有什么异议呢?

    另外 s 以空格开头的时候,在

    while (start > s) {
    flag = 0;
    while (*start == ' ') start--;

    这段代码里面会把 start 减到 s 之前,这也是未定义行为。

    https://gist.github.com/GeeLaw/47dea7677eb48e1b9896bfe9d006b5f4
    qbqbqbqb
        35
    qbqbqbqb  
       224 天前
    你的 while 里面的

    while(*start==' ') start--;

    在字符串以空格开头的时候可能会越界,访问到 s[-1]
    cnbatch
        36
    cnbatch  
       224 天前
    简单写了一小段

    #include <stdio.h>

    int main()
    {
    char str[30] = "https://www.v2ex.com/t/974343";
    printf("sizeof str: %d\n", (int)sizeof(str));
    printf("%s\n", str);
    }


    无论 GCC 还是 Clang ,编译和运行都没报错

    编译的命令行:cc -fsanitize=address
    GCC 版本(运行在 Debian 11 ):10.2.1
    Clang 版本(运行在 FreeBSD 13.2 ):14.0.5
    geelaw
        37
    geelaw  
       224 天前
    @zzzkkk #31 C++ 不存在变长数组,所以 int n; scanf("%d", &n); int a[n]; 是完全错误的,即使 n 输入的是 1 也是错误的。
    zzzkkk
        38
    zzzkkk  
    OP
       224 天前 via Android
    减到 s 之前也没问题

    下面 start2++ 刚好指向空格

    下面的代码不会执行
    zzzkkk
        39
    zzzkkk  
    OP
       224 天前 via Android
    @qbqbqbqb
    后面先+1 了 刚好指向空格
    zzzkkk
        40
    zzzkkk  
    OP
       224 天前 via Android
    @geelaw
    这里没有 c 板块 只有 c++

    我本来想发 c 板块
    qbqbqbqb
        41
    qbqbqbqb  
       224 天前
    @zzzkkk 减到 s 之前是有问题的。

    因为你这个循环至少还会再判断一次条件,必然会非法读取越界位置 s[-1]的值,这是不允许的。所以说 sanitizer 会报错。

    而且你也不知道 s 前面有什么二进制数据,“下面 start2++ 刚好指向空格”,指针没有跑飞只是运气好。
    zzzkkk
        42
    zzzkkk  
    OP
       224 天前 via Android
    @geelaw
    char s[] = "add";

    这样也是合法的 编译期就能确定长度 再简单不过的事
    cnbatch
        43
    cnbatch  
       224 天前
    @cnbatch 补充一条,无论是以 C 语言还是 C++来编译,同样都没报错
    qbqbqbqb
        44
    qbqbqbqb  
       224 天前
    假如 s 数组前面二进制数据一堆 0x20 ,指针就跑飞了
    cy18
        45
    cy18  
       224 天前
    @zzzkkk #28 聪明的你倒是把数组长度正确的,会报假阳性的代码贴上来啊...建议聪明的你别用截图,难道指望其他人对着你的图手打一遍代码么...
    zzzkkk
        46
    zzzkkk  
    OP
       224 天前 via Android
    @qbqbqbqb
    减到非空格的位置退出 while 循环
    +1 不是刚好指向空格吗?还需要什么运气?

    我发现 fsanitize 对字符串增加到一定长度后才报错
    zzzkkk
        47
    zzzkkk  
    OP
       224 天前 via Android
    @qbqbqbqb
    数组名前面就是一个 byte 不过不属于数组 吓谁啊
    别小看了这几个 while 循环 是老鸟才能写得出来的
    geelaw
        48
    geelaw  
       224 天前
    @zzzkkk #38

    int a[1];
    a + 0; // OK
    a + 1; // OK
    a - 1; // 未定义行为
    a + 2; // 未定义行为

    和 (a-1)、(a+2) 有没有被解引用没有任何关系。见 C++ 标准 [expr.add]/4:

    When an expression J that has integral type is added to or subtracted from an expression P of pointer type, the result has the type of P.
    (4.1) If P evaluates to a null pointer value and J evaluates to 0, the result is a null pointer value.
    (4.2) Otherwise, if P points to an array element i of an array object x with n elements ([dcl.array]), the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) array element i+j of x if 0≤i+j≤n and the expression P - J points to the (possibly-hypothetical) array element i−j of x if 0≤i−j≤n.
    (4.3) Otherwise, the behavior is undefined.

    注意 4.2 要求表达式的结果指向数组对象或者数组末尾之后,不允许数组开头之前。
    zzzkkk
        49
    zzzkkk  
    OP
       224 天前
    @cy18

    #include "stdio.h"
    #include "stdlib.h"
    #include "string.h"

    int main(int argc, char **argv) {
    char s[] = " a bc ed sdff";
    printf("%d\n%d\n",sizeof(s),strlen(s));
    char *start, *start2;
    start = s;
    while ( *start != '\0') {
    start++;
    }
    --start;
    char s2[strlen(s) + 1];
    char *s2p = s2;
    char flag;
    while (start > s) {
    flag = 0;
    while ( *start == ' ') start--;
    while (start >= s && *start != ' ') start--;
    start2 = start;
    ++start2;
    while ( *start2 != ' ' && *start2 != '\0') {
    *s2p++ = *start2++;
    if (flag == 0) flag = 1;
    }
    if (flag == 1) {
    *s2p++ = ' ';
    }
    }
    *s2p = '\0';
    start = s;
    s2p = s2;
    while ( *s2p != '\0') *start++ = *s2p++;
    *start = '\0';
    printf("%s\n%d\n", s, (int) strlen(s));

    }




    编译:

    cc c.c -fsanitize=address -fsanitize-recover=address -fno-omit-frame-pointer -O0 -Wall
    geelaw
        50
    geelaw  
       224 天前
    @zzzkkk #47 假设编译器不检查内存,并且数组前面全是空格呢?

    另外这个 while 循环写得可以说是乱七八糟了。
    cy18
        51
    cy18  
       224 天前
    @zzzkkk #49
    misdake
        52
    misdake  
       224 天前 via Android
    字符串结尾不是空格的话,while(*start2 != ' ')没有判\0 会超出 s2 范围,一路写出范围
    geelaw
        53
    geelaw  
       224 天前
    @zzzkkk #47 接续 #50 ,假设数组前面一切可读取的虚拟内存都是 ' ',且假设指针加减运算未定义行为的实现就是访问这些虚拟内存,那么这个程序会因为把 start 减到不可读的虚拟内存地址之后继续读 start 而访问违规,从而崩溃。
    zzzkkk
        54
    zzzkkk  
    OP
       224 天前
    @misdake
    这里需要把 while(*start2 != ' ') 改成 while(*start2 != ' ' && *start2 != '\0')
    @geelaw

    嗯 这里需要把 while(*start = '' ) 改成 while(start >=s && *start == '')
    zzzkkk
        55
    zzzkkk  
    OP
       224 天前
    @misdake
    第 14 层 早就加了字符串末尾判断
    zzzkkk
        56
    zzzkkk  
    OP
       224 天前
    @geelaw
    那你有什么更好的 while ? 已经是最高效的了
    zzzkkk
        57
    zzzkkk  
    OP
       224 天前
    重新申明:
    fsanitize 无论字符串多短 都报错了

    早上具体什么情况 一会报错 一会不报错 想不起来了
    flyqie
        58
    flyqie  
       224 天前 via Android
    @zzzkkk #40

    没有 c 板块?

    https://v2ex.com/go/c
    learningman
        59
    learningman  
       224 天前
    @GeruzoniAnsasu 别尬黑,人好歹是会写 epoll 的,只是不会配 nginx 而已
    learningman
        60
    learningman  
       224 天前   ❤️ 4
    “别小看了这几个 while 循环 是老鸟才能写得出来的”
    今日 V2EX 笑话
    learningman
        61
    learningman  
       224 天前
    https://v2ex.com/t/974127#reply10 楼主不会写反转字符串,开了两个帖子一个骂 c 语言一个骂工具链
    learningman
        62
    learningman  
       224 天前
    https://v2ex.com/t/853017#reply244 破案了,写 php 的

    zzzkkk symfony form choicetype 怎么设置选中项
    PHP • zzzkkk • 2021-12-31 18:36:59 PM 忽略
    zzzkkk symfony 每次改动 config/services.yml 都加载很慢
    PHP • zzzkkk • 2021-12-30 14:47:08 PM • 最后回复来自 zzzkkk 忽略 7
    zzzkkk symfony form money type 为什么存到数据库 是价格乘以 100 ?
    PHP • zzzkkk • 2021-12-30 15:12:32 PM • 最后回复来自 zzzkkk 忽略 6
    zzzkkk symfomy form add field 注解模式 property_path 无效
    PHP • zzzkkk • 2021-12-25 20:30:33 PM 忽略
    zzzkkk symfony 项目每次改动都要 clear cache?

    这是把 V2EX 当 chatgpt 使呢?
    zzzkkk
        63
    zzzkkk  
    OP
       224 天前 via Android
    @learningman

    我这代码还真把字符串反转了
    你有本事写个更高效的出来
    zzzkkk
        64
    zzzkkk  
    OP
       224 天前 via Android
    是字符串里的单词顺序反转 不是简单字母反转
    zzzkkk
        65
    zzzkkk  
    OP
       224 天前 via Android
    @learningman
    你有本事写个更高效的 c 代码出来
    zzzkkk
        66
    zzzkkk  
    OP
       224 天前 via Android
    @learningman
    没用过的臃肿框架 不知道不是很正常么
    vsyf
        67
    vsyf  
       224 天前
    @zzzkkk #63
    @zzzkkk #64
    #1
    start = s;
    while ( *start != '\0') {
    start++;
    }
    --start;

    #2
    char s2[strlen(s) + 1];

    看完前面这几行已经可以优化了,后面不看了 XD
    zzzkkk
        68
    zzzkkk  
    OP
       223 天前 via Android
    各位 c c++老鸟 谢谢了 我的确没做过 c c++项目
    但是我这个算法还是最好的吧
    cnbatch
        69
    cnbatch  
       223 天前
    OP 把系统版本、编译器版本一起发出来吧

    我用 GCC 10.2.1 (Debian 11)、GCC 13.2.1 (Fedora 38)和 Clang 14.0.5 (FreeBSD 13.2)都没遇到 OP 主贴图 3 的情况
    zzzkkk
        70
    zzzkkk  
    OP
       223 天前
    @cnbatch

    gcc (Debian 10.2.1-6) 10.2.1 20210110

    aws 的 tiny instance
    Inn0Vat10n
        71
    Inn0Vat10n  
       223 天前
    你可以把会导致 sanitizer 报错的代码原样贴出来,大家来一起帮你数一数到底有多少个字符
    https://godbolt.org/
    ProPh3t
        72
    ProPh3t  
       223 天前   ❤️ 1
    误以为进了民科吧
    zzzkkk
        73
    zzzkkk  
    OP
       223 天前
    @Inn0Vat10n

    哎呀 不知怎么回事 刚才好像是一条 printf 语句到了 sanitize 问题

    现在改成这样 在 linux 上能运行了 但在 leetcode 上还不能运行

    https://godbolt.org/z/M7Wq9hddP
    lixile
        74
    lixile  
       223 天前
    属实是看不下去了 sanitizers 系列工具 已经是误报率非常低的动态扫描方式了,楼主几乎在重复在类似的问题上犯错
    不好好看文档 就上
    所有动态扫描的编译选项不会用在正常的软件上开启 仅仅在需要的时候开启
    另外如果想让扫描结果 尽可能准确 需要添加回溯和调试的编译选项
    -fno-omit-frame-pointer -g
    多看看官方文档吧
    https://github.com/google/sanitizers/wiki/AddressSanitizer
    ajaxgoldfish
        75
    ajaxgoldfish  
       223 天前
    楼主可能的理论基础不够强,以为编译过就是合法的代码,其实这是编译器帮你做的优化,举个你刚才的例子在栈中开辟数组要指定大小,而且还的是字面量或者 const 量,但是在高版本 gcc 或者高版本 msvc 中编译器会在编译阶段计算出并给你填上。建议使用低版本的编译器来学习 C++
    haohaolee
        76
    haohaolee  
       223 天前 via Android
    楼主你可以用 rust 去刷 leetcode ,性能不输 C++,而且不会随便就 runtime 报错
    cnbatch
        77
    cnbatch  
       223 天前
    @haohaolee OP 贴出来的报错明显是编译期警告(连报错都不是),一看就知道不是 runtime 报错
    zzzkkk
        78
    zzzkkk  
    OP
       223 天前 via Android
    @cnbatch
    是运行时报错 没贴上来

    是一个 stack buffer overflow 类似的 santinize 产生的
    zzzkkk
        79
    zzzkkk  
    OP
       223 天前 via Android
    @cnbatch
    你把这里的 reversWords 贴到 leetcode cn 好像第 141 题它还报错 但是在 linux 编译 已经可以运行了

    https://godbolt.org/z/M7Wq9hddP
    qbqbqbqb
        80
    qbqbqbqb  
       223 天前
    @zzzkkk 这不算最好的算法,太繁琐了。

    最方便的写法是:全句字母反转 + 每个单词内部字母反转 = 单词顺序反转

    全句字母反转,只要遍历一遍就可以
    然后再对每个单词内部字母反转,只要正序抠单词,顺便去空格

    比你倒着抠单词方便太多
    qbqbqbqb
        81
    qbqbqbqb  
       223 天前
    这“老鸟”看得我一愣一愣的

    LeetCode 考察算法和数据结构,和编程语言关系不大,“c c++老鸟”也未必不翻车(暂且不讨论这 while 究竟是不是“高质量”)

    而且 LC 上的题目在算法领域也是入门级的那一类,做了几道 LC 上的题目(甚至都没 AC )就能叫“老鸟”真让人摸不着头脑。任何一个正规的大学计算机系开设的数据结构与算法必修课教学的内容深度都比它高得多(当然指的是熟练掌握了课内知识,仅仅及格混过考试不算)。

    做过 LC 就称“老鸟”真不够格(除非 Hard 题能随便秒,这种情况另说)。如果想见识真正的“老鸟”,建议看看洛谷、POJ 、Codeforces 这些网站。
    zzzkkk
        82
    zzzkkk  
    OP
       223 天前 via Android
    @qbqbqbqb
    你说的这个算法没有我这个算法清晰明了 符合直觉
    而且遍历一遍也完不成
    11232as
        83
    11232as  
       223 天前
    “高质量 while”,那确实是大佬了
    Inn0Vat10n
        84
    Inn0Vat10n  
       223 天前
    @zzzkkk 不熟悉 C 的话不建议直接用它刷题了,容易自找困扰,当输入是"A B"时,你上面代码第30行,34行都会写越界
    yanqiyu
        85
    yanqiyu  
       223 天前
    @zzzkkk 写这么多 while 属于看着就血压高了,寻找单词就看寻找边界就行,一个 while/for 反向找单词边界然后输出就够了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2706 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 11:05 · PVG 19:05 · LAX 04:05 · JFK 07:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.