作者:华力 | 来源:互联网 | 2022-12-04 17:19
可以理解的是,将缓冲区错误输出(或创建溢出),但如果12字节缓冲区中使用的字节少于12个,会发生什么?是否有可能或者空尾随时都是0?正交问题可能会有所帮助:缓冲区在实例化但未被应用程序使用时包含哪些内容?
我在Visual Studio中查看了几个宠物程序,似乎它们附加了0(或空字符),但我不确定这是否是一个可能因语言/编译器而异的MS实现.
1> selbie..:
采用以下示例(在代码块内,而不是全局):
char data[12];
memcpy(data, "Selbie", 6);
甚至这个例子:
char* data = new char[12];
memcpy(data, "Selbie", 6);
在上述两种情况下,前6个字节的data
是S
,e
,l
,b
,i
,和e
.剩下的6个字节data
被认为是"未指定的"(可以是任何东西).
是否有可能或者空尾随时都是0?
根本不保证.我所知道的唯一保证零字节填充的分配器是calloc.例:
char* data = calloc(12,1); // will allocate an array of 12 bytes and zero-init each byte
memcpy(data, "Selbie");
缓冲区实例化但应用程序尚未使用时包含哪些内容?
从技术上讲,根据最新的C++标准,分配器提供的字节在技术上被认为是"未指定的".你应该假设它是垃圾数据(任何东西).不要对内容做任何假设.
使用Visual Studio进行调试构建通常会使用with 0xcc
或0xcd
values 初始化缓冲区,但在发布版本中不是这种情况.但是,对于Windows和Visual Studio,有一些编译器标志和内存分配技术可以保证零初始内存分配,但它不可移植.
"你应该假设它可以填充随机字节." - 这个答案很好,但我想反对使用"随机"这个词.分配内存然后读取它不是随机性的良好来源.
"剩余的6个字节的数据是未定义的,但将是一些东西." - 不,这是错的!在未定义的行为中访问未初始化的值.你不能在假设"好吧,那里有东西,我不关心什么,无关紧要"的情况下编写代码.假设未发生对未初始化值的访问,优化器可能会完全重新排列您的代码.
"剩余的6个字节的'数据'是未定义的,但将是一些东西." 但如果它们未定义,那么试图确定"某事"是不是不明确的行为?所以它并不重要,唯一的解决方案是永远不要读未初始化的内存.这不是"随机性"的情况(特别是不作为RNG); 相反,我会说假设未初始化的数据是*有毒*.读取`char`类型可能有一个例外,它不能有陷阱表示或填充,但它仍然没有意义或好的代码进入读取未初始化部分的情况.
@Wilson这是一个任意值(虽然很容易在视觉上识别).[不同的值有不同的含义.](/sf/ask/17360801/)原因是在调试期间给开发人员提供了关于出错的提示(或者简单地说,哪些变量尚未初始化).
2> Matthew Fish..:
C++具有存储类,包括全局,自动和静态.初始化取决于如何声明变量.
char global[12]; // all 0
static char s_global[12]; // all 0
void foo()
{
static char s_local[12]; // all 0
char local[12]; // automatic storage variables are uninitialized, accessing before initialization is undefined behavior
}
一些有趣的细节在这里.
3> Agent_L..:
考虑你的缓冲区,用零填充:
[00][00][00][00][00][00][00][00][00][00][00][00]
现在,让我们写10个字节.值从1开始递增:
[01][02][03][04][05][06][07][08][09][10][00][00]
现在再次,这次,4倍0xFF:
[FF][FF][FF][FF][05][06][07][08][09][10][00][00]
如果在12字节缓冲区中使用少于12个字节会发生什么?是否有可能或者空尾随时都是0?
您可以根据需要进行编写,其余字节保持不变.
正交问题可能会有所帮助:缓冲区在实例化但未被应用程序使用时包含哪些内容?
未指定.预计之前使用此内存的程序(或程序的其他部分)会留下垃圾.
我在Visual Studio中查看了几个宠物程序,似乎它们附加了0(或空字符),但我不确定这是否是一个可能因语言/编译器而异的MS实现.
这正是你的想法.这次有人为你做过这件事,但不能保证它会再次发生.它可能是附加清理代码的编译器标志.某些版本的MSVC用于在调试中运行但不在发布时使用0xCD填充新内存.它也可以是一个系统安全功能,在将内存提供给您的进程之前擦除内存(因此您无法监视其他应用程序).永远记得在重要的地方使用memset
初始化缓冲区.最终,如果您依赖新缓冲区来包含某个值,则在自述文件中使用某些编译器标志.
但清洁并不是必需的.你需要一个12字节长的缓冲区.你用7个字节填充它.然后你把它传递到某个地方 - 你说"这里有7个字节".从中读取缓冲区的大小无关紧要.您希望其他函数尽可能多地阅读,而不是尽可能多地阅读.实际上,在C语言中通常无法判断缓冲区的长度.
并附注:
可以理解的是,将缓冲区错误输出(或创建溢出)
它没有,这就是问题所在.这就是为什么它是一个巨大的安全问题:没有错误,程序试图继续,所以它有时执行它从未意图的恶意内容.因此,我们不得不向操作系统添加一堆机制,例如ASLR,这将增加程序崩溃的可能性并降低其继续损坏内存的可能性.因此,永远不要依赖那些事后的守卫并亲自观察你的缓冲区边界.