sscanf 导致地址越界

发布于:2022-07-25 ⋅ 阅读:(392) ⋅ 点赞:(0)

问题

test.c

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	int a = 100;
	uint8_t b;

	sscanf("0x15", "0x%x", &b);

	printf("a = 0x%x\n", a);
	printf("b = 0x%x\n", b);

	return EXIT_SUCCESS;
}
$ ./test.out 
a = 0x0
b = 0x15

a 的值为什么变成 0 了呢?

解答

因为 sscanf 在给 b 赋值时,由于指定的参数格式是 %x,所以 sscanf 认为 b 是一个 unsigned int 类型,所以把 &b 转换成了 unsigned int * 类型,这样在给 b 赋值时,就是按照 unsigned int 这么大空间进行赋值,结果就操作了别的变量的内存,导致程序出问题。
下图 a) 是我们期望的样子,可是由于 “%x”,导致 &b “管控”的空间从一个字节扩大到了四个字节,“侵占”了变量 a 的空间,如图 b),导致 a 的值被篡改。
在这里插入图片描述

避免

其实在编译时,编译器就已经发出了警告。所以我们在编写代码时,要时刻留意编译器报的警告,能够帮助我们纠正不少失误。
因此,提交代码的一个原则就是:尽量不引入新的警告。

$ gcc test.c -o test.out
test.c: In function ‘main’:
test.c:10:21: warning: format ‘%x’ expects argument of type ‘unsigned int *’, but argument 3 has type ‘uint8_t *’ {aka ‘unsigned char *’} [-Wformat=]
   10 |  sscanf("0x15", "0x%x", &b);
      |                    ~^   ~~
      |                     |   |
      |                     |   uint8_t * {aka unsigned char *}
      |                     unsigned int *
      |                    %hhx

推荐写法

这里推荐一种写法,用一个 int 类型的中间变量 c 进行转储。

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
	int a = 100;
	uint8_t b;
    int c;

	sscanf("0x15", "0x%x", &c);
    b = (uint8_t)c;

	printf("a = 0x%x\n", a);
	printf("b = 0x%x\n", b);

	return EXIT_SUCCESS;
}
a = 0x64
b = 0x15

网站公告

今日签到

点亮在社区的每一天
去签到