这是一个关于如何在一段简单的 C 代码中实现“腰带和吊带”安全的问题。一个老生常谈的问题是如何确保可以将数据移动到某个调用函数内的缓冲区中,而不必担心堆内存在返回后被损坏。这个网站上写了一些关于这个话题的好东西,至少对我来说,仍然不清楚我们在哪里可以获得真正的完全安全。所以我写了以下内容:
/*********************************************************************
* The Open Group Base Specifications Issue 6
* IEEE Std 1003.1, 2004 Edition
*********************************************************************/
#define _XOPEN_SOURCE 600
#include <ctype.h>
#include <errno.h>
#include <locale.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int use_buffer( const char *strin, char **strout, size_t bufsize ) {
size_t len;
int result = -1;
printf ( "in use_buffer() we have address of strout = %p\n", &strout );
printf ( " and that contains an address of %p\n", strout );
printf ( " which points to a buffer address %p\n", *strout );
/* check for null data */
if ( ( strin == NULL ) || ( *strout == NULL ) )
return result;
/* check for zero length data */
if ( strlen(strin) == 0 )
return result;
/* ensure we have a non-zero size buffer to write to ?
* belt and suspenders safety here is not assured. We have
* no way to know if the calling routine actually did
* allocate memory of size bufsize.
*/
len = strlen(strin);
if ( bufsize < len )
return result;
strncpy ( *strout, strin, len );
return len;
}
int main ( int argc, char *argv[] ) {
char *some_buffer;
int retval;
size_t buflen;
if ( argc < 2 ) {
printf ( "usage: %s somestring\n", argv[0] );
return ( EXIT_FAILURE );
}
buflen = (size_t) ( 4 * 4096 );
some_buffer = calloc( buflen, sizeof( unsigned char) );
if ( some_buffer == NULL ) {
perror ( "Could not calloc a 16Kb byte buffer." );
return ( EXIT_FAILURE );
}
printf ( "main() has a 16Kb buffer ready at address = %p\n",
&some_buffer );
retval = use_buffer( argv[1], &some_buffer, buflen );
if ( retval > 0 )
printf ( "Maybe we have %i bytes copied into a buffer.\n", retval );
free ( some_buffer );
some_buffer = NULL; /* belt and suspenders */
return ( EXIT_SUCCESS );
}
编译并运行它,我看到了:
$ ./use_buffer "foo of the bar"
main() has a 16Kb buffer ready at address = ffffffff7ffff710
in use_buffer() we have address of strout = ffffffff7ffff630
and that contains an address of ffffffff7ffff710
which points to a buffer address 100101440
Maybe we have 14 bytes copied into a buffer.
其中一个地址确实看起来不正确。其中之一就是不一样。
真的,很难知道为什么前三个地址在其他一些内存区域中离得很远,而最后一个看起来可能是一些本地堆内存?
以上方法绝对安全带和吊带吗?我的意思是该函数将使用我们知道已预先分配的缓冲区,并且该函数无法将其搞砸。我怀疑该函数是否可以对存储在 strout 中的地址调用 free()。这就像用房间钥匙入住酒店然后放火烧房间一样。站在里面的时候。我想这可以做到..但会很疯狂。
所以这里有两个问题:(1)函数有没有办法验证分配的缓冲区大小?即使方法是触发内存违规。然后(2)将空指针传递给函数然后允许函数根据需要调用/分配缓冲区并传回地址是否安全?
我怀疑(2)被打死了,答案是“安全不能保证”。(旁注:顺便说一句,该死的好电影。)
考虑这个代码位:
/*********************************************************************
* The Open Group Base Specifications Issue 6
* IEEE Std 1003.1, 2004 Edition
*********************************************************************/
#define _XOPEN_SOURCE 600
#include <ctype.h>
#include <errno.h>
#include <locale.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int bad_buffer( const char *strin, char **strout ) {
size_t len;
int result = -1;
char *local_buffer;
/* check for null data */
if ( strin == NULL )
return result;
/* check for zero length data */
len = strlen(strin);
if ( len == 0 )
return result;
/*
* safety not guaranteed ?
*/
local_buffer = calloc( len+1, sizeof( unsigned char) );
printf ( " in bad_buffer() we have local_buffer at = %p\n",
&local_buffer );
strncpy ( local_buffer, strin, len );
*strout = local_buffer;
return len;
}
int main ( int argc, char *argv[] ) {
char *some_buffer;
int retval;
size_t buflen;
if ( argc < 2 ) {
printf ( "usage: %s somestring\n", argv[0] );
return ( EXIT_FAILURE );
}
retval = bad_buffer( argv[1], &some_buffer );
printf ( "in main() we now have some_buffer at addr %p\n", some_buffer );
if ( retval > 0 )
printf ( "Maybe we have %i bytes copied into a buffer.\n", retval );
printf ( "main() says the buffer contains \"%s\"\n", some_buffer );
free ( some_buffer ); /* really ? main() did not allocate this !? */
some_buffer = NULL;
return ( EXIT_SUCCESS );
}
当我编译并运行时,我看到:
$ ./bad_buffer foobar
in bad_buffer() we have local_buffer at = ffffffff7ffff6b0
in main() we now have some_buffer at addr 100101330
Maybe we have 6 bytes copied into a buffer.
main() says the buffer contains "foobar"
这里似乎有些诡异。该函数执行了 calloc,然后将缓冲区的地址填充到strout 中的地址中。所以 strout 是一个指向指针的指针,所以我很好。让我害怕的是,函数分配的内存在完成后没有权利或理由被认为是安全的,我们又回到了 main()。
所以问题号(2)代表“允许函数调用/分配所需的缓冲区有什么安全性吗?”