防止全局缓冲区溢出将静态 bool 引用转换为 int 指针

Prevent global buffer overflow casting a static bool reference to int pointer

我有一个静态全局变量 echo 类型是布尔值 和一个函数声明为:

void add_param(char *name,
               int *valp,
               char *documentation,
               setter_function setter);

调用时,echo应该进入第二个参数,意思是

add_param(/*Some string*/ ,(int *)&echo, /*Some string*/, /*Some thing*/)`.

此处,当地址清理器打开时,会出现全局缓冲区溢出错误, 我知道这是因为布尔变量的大小为 1 个字节,而整数为 4 个字节, 并将 echo 的类型更改为整数使程序运行良好。

Here's the error message if its needed:

==4344==ERROR: AddressSanitizer: global-buffer-overflow on address 0x5626b775c400 at pc 0x5626b7745905 bp 0x7ffe6ed6e440 sp
0x7ffe6ed6e430 READ of size 4 at 0x5626b775c400 thread T0
    #0 0x5626b7745904 in do_help_cmd /home/uduru/GitHub-Repos/lab0-c/console.c:307
    #1 0x5626b7745a18 in interpret_cmda /home/uduru/GitHub-Repos/lab0-c/console.c:221
    #2 0x5626b77461fd in interpret_cmd /home/uduru/GitHub-Repos/lab0-c/console.c:244
    #3 0x5626b7747940 in run_console /home/uduru/GitHub-Repos/lab0-c/console.c:660
    #4 0x5626b7744527 in main /home/uduru/GitHub-Repos/lab0-c/qtest.c:788
    #5 0x7fa8cf0100b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #6 0x5626b7741b8d in _start (/home/uduru/GitHub-Repos/lab0-c/qtest+0x8b8d)

0x5626b775c401 is located 0 bytes to the right of global variable
'echo' defined in 'console.c:59:13' (0x5626b775c400) of size 1
SUMMARY: AddressSanitizer: global-buffer-overflow
/home/uduru/GitHub-Repos/lab0-c/console.c:307 in do_help_cmd Shadow
bytes around the buggy address:   
0x0ac556ee3830: f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9   
0x0ac556ee3840: f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9   
0x0ac556ee3850: f9 f9 f9 f9 00 00 00 00 04 f9 f9 f9 f9 f9 f9 f9   
0x0ac556ee3860: 00 00 00 00 00 00 00 00 00 00 f9 f9 f9 f9 f9 f9   
0x0ac556ee3870: 01 f9 f9 f9 f9 f9 f9 f9 01 f9 f9 f9 f9 f9 f9 f9
0x0ac556ee3880:[01]f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9   
0x0ac556ee3890: 04 f9 f9 f9 f9 f9 f9 f9 00 00 00 00 00 00 00 00  
0x0ac556ee38a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  
0x0ac556ee38b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  
0x0ac556ee38c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  
0x0ac556ee38d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
Shadow byte legend (one shadow byte represents 8 application bytes):  
Addressable:             00   
Partially addressable:   01 02 03 04 05 06 07
Heap left redzone:       fa   
Freed heap region:       fd  
Stack left redzone:      f1   
Stack mid redzone:       f2   
Stack right redzone:     f3   
Stack after return:      f5   
Stack use after scope:   f8   
Global redzone:          f9   
Global init order:       f6   
Poisoned by user:        f7   
Container overflow:      fc   
Array cookie:            ac   
Intra object redzone:    bb   
ASan internal:  fe   
Left alloca redzone:     ca   
Right alloca redzone:    cb  
Shadow gap:              cc
==4344==ABORTING ```

但是,我想知道是否有任何方法可以在保持声明原样的同时防止错误?

防止此类错误的正确方法是避免强制转换,在所有地方使用正确的类型,并配置编译器以产生更多警告 (-Wall -Wextra) 并考虑这些警告错误 (-Werror ).

如果 add_param 需要指向 int 的指针,请不要传递指向与类型 int 不兼容的指针。

如果你想让add_param处理不同的类型,你可以将valp参数定义为指向void的指针,并用另一个参数传递期望的类型,例如适当的setter 函数。如果程序有语义错误,您将明确绕过编译器类型检查机制并自行解决。

这是一个例子:

#include <error.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

typedef int (*setter_function)(void *valp, const char *value);
void add_param(const char *name, void *valp, const char *value, setter_function setter) {
    printf("Setting %s parameter to %s: ", name, value);
    if (setter(valp, value))
        printf("failure\n");
    else
        printf("success\n");
}

int set_bool(void *valp, const char *value) {
    bool *bp = valp;
    if (!strcmp(value, "true")) {
        *bp = true;
        return 0;
    }
    if (!strcmp(value, "false")) {
        *bp = false;
        return 0;
    }
    return 1;  // invalid value
}

int set_int(void *valp, const char *value) {
    int *bp = valp;
    char *p;
    long n;

    errno = 0;
    n = strtol(value, &p, 0);

    if (p != value && *p == '[=10=]' && errno == 0 && n >= INT_MIN && n <= INT_MAX) {
        *bp = value;
        return 0;
    }
    return 1;  // invalid value
}

bool echo;
int width;

int main() {
    add_param("echo", &echo, "1", set_bool);
    add_param("echo", &echo, "yes", set_bool);
    add_param("echo", &echo, "true", set_bool);
    add_param("width", &width, "abc", set_int);
    add_param("width", &width, "42", set_int);
    // this will compile, but causes undefined behavior.
    //add_param("echo", &echo, "1", set_int);
    return 0;
}

输出:

Setting echo parameter to 1: failure
Setting echo parameter to yes: failure
Setting echo parameter to true: success
Setting width parameter to abc: failure
Setting width parameter to 42: success