防止全局缓冲区溢出将静态 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
我有一个静态全局变量 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