简洁的想法

 找回密码
 注册
搜索
查看: 680|回复: 1

strcpy This function or variable may be unsafe. Consider using strcpy_s instead

[复制链接]
发表于 2012-6-26 09:41:10 | 显示全部楼层 |阅读模式
使用VS2005以上版本(VS2005、VS2008、VS2010)编译在其他编译器下正常通过的C语言程序,你可能会遇到类似如下的警告提示:
  1. warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
  2. 1>          c:/program files/microsoft visual studio 10.0/vc/include/string.h(105) : 参见“strcpy”的声明
  3. warning C4996: 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
  4. 1>          c:/program files/microsoft visual studio 10.0/vc/include/stdio.h(234) : 参见“fopen”的声明
复制代码
原因解释
这种微软的警告,主要因为那些C库的函数,很多函数内部是不进行参数检测的(包括越界类的),微软担心使用这些会造成内存异常,所以就改写了同样功能的函数,改写了的函数进行了参数的检测,使用这些新的函数会更安全和便捷。关于这些改写的函数你不用专门去记忆,因为编译器对于每个函数在给出警告时,都会告诉你相应的安全函数,查看警告信息就可以获知,在使用时也再查看一下MSDN详细了解。库函数改写例子:
mkdir改写为 _mkdir
fopen”改写为 fopen_s
stricmp改写为 stricmp_s
strcpy改写为strcpy_s
解决方案:

1> 根据下面的warning提示:
参见“fopen”的声明
“This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_DEPRECATE. See online help for details.”

所以可以将函数按warning提示的第二句,改为使用fopen_s函数即可:
例如:
  1. FILE *pFile=fopen("1.txt", "w");
复制代码
改为:
  1. FILE* pFile;
  2. fopen_s(&pFile, "1.txt", "w");
复制代码
可是我的代码只是借VS2005的IDE环境开发而已,以后是要移植到别的ANSI C平台的,所以不想用strcpy_s,舍弃此法。2> 根据warning提示的第三句话:
use _CRT_SECURE_NO_DEPRECATE

项目|属性|配置属性|C/C++|命令行|附加选项,加入【/D "_CRT_SECURE_NO_DEPRECATE" 】(注:加入中括号中完整的内容)

3> 降低警告级别:
项目|属性|配置属性|C/C++|常规,自己根据情况降低警告级别(此法不推荐)
注意:高度重视警告:使用编译器的最高警告级别。应该要求构建是干净利落的(没有警告)。理解所有警告。通过 修改代码而不是降低警告级别来排除警告。
编译器是你的朋友。如果它对某个构造发出警告,这经常是说明你的代码中存在潜在的问题。成功的构建应该是无声无息的(没有警告的)。

转自:http://blog.csdn.net/ycguhang/article/details/7170558


===============================================================

VS2005:declared deprecated warning问题——strcpy_s和strcpy区别


    在VS2005下编译代码,有时会遇到类似如下的警告: warning C4996: 'strcat' was declared deprecated. 通常这类警告都是由于调用了字符串相关函数引起的。虽然这警告无伤大雅,仅仅只是说使用的函数已过时(deprecated)<需要用新的函数来替代>,但看着实在别扭,且看看ms为什么要设置成这样。

    搜索了一下ms的网站,找到了结果。ms认为以前的c/c++库中有一部分函数不够安全,希望程序员可以使用他们的替代安全库(Safe Library)来避免不必要的隐患。 整个原文请点击以下链接访问:Repel Attacks on Your Code with the Visual Studio 2005 Safe C and C++ Libraries

    在网上搜索到的最常用的解决方案,那就是定义 _CRT_SECURE_NO_DEPRECATE 和 _SCL_SECURE_NO_DEPRECATE 来禁止vc2005对此产生警告(依然使用的是非安全库!显然并不是一个好的解决方案)。而且如果使用了ATL,则还需要定义 _ATL_SECURE_NO_DEPRECATE, 使用了MFC则需要定义 _AFX_SECURE_NO_DEPRECATE。
    然而尽管如此,更好的解决方案只需要定义一个宏CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES, 那么VS将会自动替换使用他们的Safe Library来代替C/C++标准库(如strcat将被strcat_f来取代)。

    即使使用了_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES,代码将依旧不够安全, 对此,ms提出了如下10点建议:
      1. 不要认为 strcpy_s 和 strncpy_s( 以及其他的字符串函数)(在空间不够的时候)会自动终止拷贝(truncate截断,不截断则意味着溢出).如果需要自动截断,请使用strncpy_s (同时使用_TRUNCATE作为长度参数)。
      2. 记住fopen_s缺省是独占模式。如需共享使用文件,应该使用_sopen。
      3. 别忘了_dupenv_s, 它将比_getenv_s更容易使用,因为它能自动分配一个正确长度的内存(buffer)。
      4. 在scanf_s中小心参数顺序。
      5. 确定printf_s中格式字符串的正确。
      6. 使用_countof(x)来取代sizeof(x)/sizeof(element). _countof将会正确的计算元素个数,而且如果x是一个指针,编译器将会发出一个警告(来提醒程序员,仅针对C++编译)
      7. 记住所有的sizes(大小,非长度)都是使用characters(字符,unicode下一个字符占2个byte)作为单位,而不是bytes(字节).
      8. 记住所有的sizes(大小,非长度,缘由同上)包含了字符串结束符'/0'(即别忘了很多情况下size需要+1)。
      9. 调试的时候监视数据0xfd。 (在调试版本下)0xfd将会被填充在数据(buffer,通常是字符串)的结尾处。如果运行非你所愿,可能会得到一个长度错误。
      10. 检查所有的错误。 许多新函数相比旧函数,能返回(表示)错误信息(的数值)。


     PS:定义宏_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES好像不能消除警告

1.gets_s()代替gets()
2.strcpy_s()来代替strcpy()
3.strncpy_s()代替strncpy()
4.sprintf_s()代替sprintf()
5.CString::Format(_T("字符转"))代替CString::Format("字符转")
6.strcat_s()代替strcat()
7.fopen_s()代替fopen()
8._vsnprintf_s()代替_vsnprintf()
9._ftime64_s()代替_ftime64()
10._get_tzname ()代替_tzname()
11._snwprintf_s()代替_snwprintf()
12.mbstowcs_s()代替mbstowcs()
13.wcstombs_s()代替wcstombs()
14.wcscat_s()代替wcscat()
15._wcsupr_s()代替_wcsupr()
16._wcslwr_s()代替_wcslwr()
17._wtoi()代替atoi()

转自: http://blog.csdn.net/qingdaoxuelei/article/details/4328169
 楼主| 发表于 2012-6-26 09:52:39 | 显示全部楼层

用Visual C++ 2005 Express Edition构建安全代码

本文内容:
C运行时库的新安全特性
使用标准C++库
标准C++库的边界检查
编译器安全特性
新的C++编程语言

引言
      对编程爱好者来说,想要简单快速地生成安全可靠的程序,如今又多了一个新的选择,那就是微软公司刚刚发布不久的Visual C++ 2005 Express Edition,它是Microsoft Visual C++ 2005系列中最初级的版本,我们可以把它看作是个人版,可以从微软的官方网站免费下载使用。在Visual C++ 2005中,新的语言和新的库特性,可使编写安全可靠的程序变得比以往更加容易,同时,它也提供了标准C++语言强大的功能和高度的可伸缩性,而且,对 .NET Framework编程而言,它可能是最强大的语言了。
  
  
C运行时库的安全特性
      如果正在使用C运行时库构建应用程序,那么应感到欣慰的是,Visual C++现在已经包含了大多数库函数更安全的版本。那些参数中有缓冲区的函数,现在已加入了长度参数,以确保数据不会超出缓冲区,同时也有更多的函数现在可以检查参数的有效性了,以保证在必要时,可调用非法参数处理程序。
      在C运行时库中,最靠不住的函数就是gets了,它用于从标准输入中读取一行,请看下列代码:
  1. char buffer[10] = { 0 };
  2. gets(buffer);
复制代码
第一行声明了缓冲区变量,并把缓冲区内的字符初始化为零;在声明变量时就把变量初始化为一个确定的值,这是一个非常好的习惯。接着,gets函数从标准输入流中读取一行,并存入到缓冲区中。那么错在哪里呢?C风格的数组在传递给函数时不是传值,而是传递了指向第一个数组元素的指针。因此,函数把char[]当成了char*,从而指针中就不再包含用于确认缓冲区大小的信息。那么gets将怎样做呢?它假定缓冲区为无穷大(实际上精确到UINT_MAX),只是简单地从输入流中一直把读取的数值复制到缓冲区中。攻击者很容易利用这一点,现在,这种类型的错误已经是一个“臭名昭著”的缓冲区溢出问题了。

      大多数的最初C运行时库函数都受类似问题的影响,原因是缺乏对参数的验证。而现在是一个“安全第一”的世界,所有此类的函数都应该被提供了更好安全性的相同函数所取代,当然,这要基于现有的代码在多大程度上使用了老式的库函数,可能要花上一点时间来移植到新的安全版本。这些新的函数有一个 _s 后缀,例如,gets函数被gets_s取代,而strcpy被strcpy_s取代,如下例:
  1. char buffer[10] = { 0 };
  2. gets_s(buffer, sizeof (buffer) / sizeof (buffer[0]));
复制代码
gets_s函数新增了一个参数,用于指示最大可写入的字符数(包含了一个空值结束符)。这儿使用的sizeof操作符,可使编译器在编译时确定数组的长度,注意,sizeof返回的是操作数的字节数,必须要除以数组中的第一个元素,从而得到数组中的元素个数。如果将来要移植到Unicode上,可使用以字符计量缓冲区大小的_getws_s函数。

      前面还提到,另一个需要安全检查的常用函数是非常类似的strcpy,像gets函数一样,它不能确定可用的缓冲区大小,只能假定有足够大的空间可容纳源字符串,这将在运行时导致不可预知的行为,下面是使用安全的strcpy_s函数的例子:
  1. char source[] = "Hello world!";
  2. char destination[20] = { 0 };
  3. strcpy_s(destination, sizeof (destination) / sizeof (destination[0]), source);
复制代码
以上是调用新的strcpy_s函数时的大体样子,最明显的不同之处是新增的参数,它以字符计量来确定目的缓冲区的大小。这允许strcpy_s函数进行运行时检查,以保证字符不会超出目的缓冲区。

      另外,还有其他类型的检查可用于保证函数参数的有效性,在以debug构建程序时,有断言(assertion)检查,当条件未满足时,它会显示调试报告。而在debug和release构建时,如果一个特定的条件未满足,将会调用非法参数处理程序,默认情况下,会产生一个访问违例并终止当前程序。这样可保证程序不会有不可预知的行为,当然,只要确保strcpy_s之类的函数在调用时没有非法函数,就可防止此类问题的发生。

      通过使用新的_countof宏,可简化前一个例子,还能防止sizeof操作符的误用。宏_countof返回C风格数组中的元素数,但如果传递给它一个原始指针,将不能通过编译。
  1. strcpy_s(destination, _countof(destination), source);
复制代码
使用标准C++库
      看过C运行时库的新增安全部分之后,让我们来看一下标准C++库是怎样有助于减少代码中错误产生的可能性的。
      从C运行时库转到标准C++库时,其中最有效的一个方法是使用库的vector类。Vector是标准C++库中的一个容器类,模拟了一个一维的T数组,而T可能是任何类型。大多数代码中使用缓冲区的地方,都能由vector类取而代之。让我们再看一下前一节两个例子,在第一个例子中,使用了gets_s函数来从标准输入中读取一行,请看下面的替代方法:
  1. std::vector<char>buffer(10);
  2. gets_s(&buffer[0], buffer.size());
复制代码
最大的不同之处是缓冲区变量现已是一个vector对象,vector对象初始大小为10个字符,并由其构造函数初始化为零;表达式&buffer[0]取vector对象中第一个元素的地址,这是传递vector给一个有缓冲区参数的C函数的正确方法。与sizeof操作符不同的是,所有的容器类计量都采用的是元素,而不是字节。例如,vector的size方法就返回容器类中的元素个数。
      前一节的第二个例子中,使用了strcpy_s函数把字符从源地址复制到目的地址,而vextor类也能被用于取代其中的C风格数组,在演示之前,先来看一下另一个非常有用的标准C++库容器类。
      Basic_string类可在C++中像使用普通类型那样来使用字符串,它也提供了多种重载操作符,以便为C++程序员提供一种更自然的编程模式。相对strcpy_s和其他字符串操作函数而言,更应该使用basic_string类;basic_string是一种类型T的字符容器类,只不过此处的T是字符类型。标准C++库为常用的字符类型都提供了类型定义,string和wstring是分别对应于char和wchar_t元素类型定义,下面的例子演示了basic_string类是多么的简单和安全:
  1. std::string source = "Hello world!";
  2. std::string destination = source;
复制代码
basic_string同时还提供了可用于其他常用字符串操作的方法和操作符,如字符串拼接和在字符串中进行字链查找。
      最后,标准C++库也提供了一个非常强大的I/O库,可用于与标准输入、标准输出和文件流的交互,虽然简单但是安全。尽管gets_s函数使用一个vector比使用C风格数组更好,但通过使用对类basic_istream和basic_ostream的类型定义,甚至能更进一步简化,这样写出的代码,不但简单,而且类型安全;不但能读取字符串,还能从流中读取任何类型的数据。
  1. std::string word;
  2. int number = 0;
  3.   
  4. std::cin >> word >> number;
  5. std::cout << word << std::endl << number << std::endl;
复制代码
cin被定义为basic_istream,能从标准输入中读取键入的字符,而wcin是为wchar_t类型准备的。另一方面,cout被定义为basic_ostream,可用来写入到标准输出中。正如你所想的,这种模式可以无限扩展,而不只限于gets_s和puts函数,但在此的真正价值是,如今程序中想要有一丁点安全缺陷都很难喔。

标准C++库的边界检查
       部分标准C++库的容器类与iterator在默认状态下并不提供边界检查,例如,vector的下标操作符在传统意义上来说,是一种快速、但有潜在不安全因素的访问个体元素的方法,如果想检查访问的安全性,可使用at方法。也许新增的安全性检查可能会导致性能降低,但在大多数时候,性能的损失是可以忽略不计的。请看如下的示例函数:
  1. void PrintAll(const std::vector<int>&numbers)
  2. {
  3.     for (size_t index = 0; index < numbers.size(); ++index)
  4.     {
  5.         std::cout << numbers[index] << std::endl;
  6.     }
  7. }
  8.   
  9. void PrintN(const std::vector<int>& numbers, size_t index)
  10. {
  11.     std::cout << numbers.at(index) << std::endl;
  12. }
复制代码
PrintAll函数使用下标操作符,而且函数控制的index在已知状态下是安全的。

转自:http://blog.csdn.net/lychee007/article/details/4174209
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|小黑屋|Archiver|简洁的想法

GMT+1, 2022-1-28 17:06

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表