我真的不知道如何标题,问题是我在下面的代码中看到了一些严肃的魔法。
这里我把所有相关的代码片段及其输出。
void persist_receipt(Receipt * receipt, char * path)
{
int i, fd;
unsigned char block_fname[64];
fd = open( ".receipt", O_RDWR | O_CREAT, 0777);
printf("1.fd=%i\n", fd);
// Write receipt header
write(fd, receipt->hash, 32);
write(fd, receipt->name, 256);
write(fd, &receipt->size, sizeof(int));
printf("2.fd=%i\n", fd);
for(i=0; i<receipt->size; i++)
{
printf("3.fd=%i\n", fd);
sha2hexf(block_fname, receipt->blocks[i].hash);
printf("4.fd=%i\n", fd);
write(fd, block_fname, 64);
}
close(fd);
}
现在让我告诉你这段代码的输出:
1.fd=4
2.fd=4
3.fd=4
4.fd=0
85a2c67fe7f3dc586a0f231d0cc845e7094408769c8391da18d1ed35c7b1cb3c3.fd=0
4.fd=0
cbc6286fedb70a703f403e507e4daca7bb4a493cdd4d0f0c1787e56256afe5fd3.fd=0
4.fd=0
c364417c27bd887142e6c2de1f7a5b777d604c9df8bcd40bb1a20623b38de82b3.fd=0
这个随机文件描述符失去了它的价值是怎么回事?由于他随机将其值转换为0,它会向控制台输出散列值,但它不应该发生!
这是有趣的部分!如果我只声明一个新变量。假设我声明一个新的整数:
void persist_receipt(Receipt * receipt, char * path)
{
int i, fd, p;
unsigned char block_fname[64];
fd = open( ".receipt", O_RDWR | O_CREAT, 0777);
printf("1.fd=%i\n", fd);
// Write receipt header
write(fd, receipt->hash, 32);
write(fd, receipt->name, 256);
write(fd, &receipt->size, sizeof(int));
printf("2.fd=%i\n", fd);
for(i=0; i<receipt->size; i++)
{
printf("3.fd=%i\n", fd);
sha2hexf(block_fname, receipt->blocks[i].hash);
printf("4.fd=%i\n", fd);
write(fd, block_fname, 64);
}
close(fd);
}
如你所见,现在我宣布了一个'p'变量,让我们再次编译并运行它,哦,男孩。
1.fd=4
2.fd=4
3.fd=4
4.fd=4
3.fd=4
4.fd=4
3.fd=4
4.fd=4
3.fd=4
4.fd=4
...
我希望有人能够解释幕后发生的事情。因为这对我来说似乎是一种非常黑暗的魔法,就在那里。我怀疑有一些可疑的'sha2hexf'功能,所以在这里我也放了代码,特别是可疑部分是内部的sprintf调用。
void sha2hexf(unsigned char *outbuf, unsigned char *hash) {
int i;
for (i = 0; i < 32; i++) {
sprintf((char*)outbuf, "%.2x", hash[i]);
outbuf += 2;
}
}
有真正的解释吗?
答案 0 :(得分:1)
你的函数sha2hexf
显然超出了你传入的缓冲区。它是空的 - 终止64字节的字符串(所以你需要缓冲区中至少65个字节)。
编译器经常反向布局堆栈,所以你会得到:
block_fname (64 bytes) | fd (4 bytes) | i (4 bytes)
^ Overflow hits the first byte of 'fd', which zeros it
a little-endian architecture (if fd less than 256).
答案 1 :(得分:1)
sha2hexf正在写outbuf的边界(又名block_fname),因为sprintf在每个循环中附加“\ 0”(你通过递增outbuf来处理,但不是在最后一次循环运行!)。 所以你需要的缓冲区占65个字符。