无效引用的虚空缓冲区仍然有效

时间:2018-08-30 15:49:29

标签: linux system-calls void dereference

当我使用一些C代码时,我遇到了这个奇怪的错误。

我在代码中犯了一个错误,并写到buf而不是&buf,但是几乎正常。

...
void* buf;
int ret;

int fd = open("1", O_CREAT | O_RDWR, 0777);
write(fd, "test\n", 5);
lseek(fd, 0, SEEK_SET);
ret = read(fd, buf, 5);                 // Yes, this should be &buf
printf("Ret: %d Str: %s\n", ret, buf);
---- output ----
Ret: 5 Str: test\n

此代码有效,即使我的读取调用中应该有test\n,我的标准输出中也会得到&buf。请注意,将buf更改为&buf是可行的。那不是问题。

这是行不通的:

...
void* buf;
void* blah = "a";     // Using char* still did not work
int ret;

int fd = open("1", O_CREAT | O_RDWR, 0777);
write(fd, "test\n", 5);
lseek(fd, 0, SEEK_SET);
ret = read(fd, buf, 5);
printf("Ret: %d Str: %s\n", ret, buf);
---- output ----
Ret: -1 Str: 1�I��^H��H���PTI��`@

文件1的二进制文件对于两个程序都是相同的。写入1时没有错误。

  • 第一个代码段为何起作用?

  • 如何添加从未使用过的变量,这不再 工作吗?

  • 为什么首先要写buf而不写&buf

以下是每个二进制文件中的字符串部分:

功能代码:

0000770: 0100 0200 0000 0000 0000 0000 0000 0000  ................
0000780: 3100 7465 7374 0a00 4572 723a 2025 640a  1.test..Err: %d.
0000790: 0a00 5374 723a 2025 730a 0000 011b 033b  ..Str: %s......;
00007a0: 3000 0000 0500 0000 34fd ffff 7c00 0000  0.......4...|...

故障代码:

0000770: 0100 0200 0000 0000 0000 0000 0000 0000  ................
0000780: 6100 3100 7465 7374 0a00 4572 723a 2025  a.1.test..Err: %
0000790: 640a 0a00 5374 723a 2025 730a 0000 0000  d...Str: %s.....
00007a0: 011b 033b 3400 0000 0500 0000 30fd ffff  ...;4.......0...

谢谢。

1 个答案:

答案 0 :(得分:0)

(由于几乎不可能在注释中添加多行代码)

使用-Wall -Wextra进行编译的警告:

x.c: In function ‘main’:
x.c:15:25: warning: format ‘%s’ expects argument of type ‘char *’, but argument 3 has type ‘void *’ [-Wformat=]
   printf("Ret: %d Str: %s\n", ret, buf);
                        ~^
                        %p
x.c:14:9: warning: ‘buf’ is used uninitialized in this function [-Wuninitialized]
   ret = read(fd, buf, 5);
         ^~~~~~~~~~~~~~~~

通过valgrind运行程序的结果:

==6978== Memcheck, a memory error detector
==6978== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==6978== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==6978== Command: ./a.out
==6978== 
==6978== Syscall param read(buf) contains uninitialised byte(s)
==6978==    at 0x4F4C081: read (read.c:27)
==6978==    by 0x1087BF: main (x.c:14)
==6978== 
==6978== Syscall param read(buf) points to unaddressable byte(s)
==6978==    at 0x4F4C081: read (read.c:27)
==6978==    by 0x1087BF: main (x.c:14)
==6978==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==6978== 
==6978== Conditional jump or move depends on uninitialised value(s)
==6978==    at 0x4E97A41: vfprintf (vfprintf.c:1643)
==6978==    by 0x4EA0F25: printf (printf.c:33)
==6978==    by 0x1087DC: main (x.c:18)
==6978== 
Ret: -1 Str: (null)
==6978== 
==6978== HEAP SUMMARY:
==6978==     in use at exit: 0 bytes in 0 blocks
==6978==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==6978== 
==6978== All heap blocks were freed -- no leaks are possible
==6978== 
==6978== For counts of detected and suppressed errors, rerun with: -v
==6978== Use --track-origins=yes to see where uninitialised values come from
==6978== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

您必须使buf指向您有权使用的有效内存,否则您将获得未定义的行为,任何事情都可能发生。如果幸运的话,它只会使您的程序崩溃,但您不能指望它。