打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Lesson 10. Pattern 2. Functions with variable numb...
userphoto

2022.05.22 加拿大

关注

Lesson 10. Pattern 2. Functions with variable number of arguments

Jan 23 2012

Typical examples given in most articles on the issues of porting programs to 64-bit systems refer to incorrect use of the functions printf, scanf and their variants.

Example 1:

const char *invalidFormat = '%u'; size_t value = SIZE_MAX; printf(invalidFormat, value);

Example 2:

char buf[9];
sprintf(buf, '%p', pointer);

In the first case, the programmer does not take into account that the type size_t is not equivalent to the type unsigned on a 64-bit platform. It will result in printing an incorrect result if value > UINT_MAX.

In the second case, the author does not take into account that the size of the pointer might be more than 32 bits in future. As a result, this code will cause a buffer overflow on the 64-bit architecture.

Incorrect use of functions with the variable number of arguments is a common error not only for 64-bit architectures but for all architectures. It is explained by the fundamental danger of using these C++ constructs. It is generally accepted to refuse using them and resort to safe programming methods. We highly recommend you to modify your code and employ safe methods. For example, you may replace printf with cout, sprintf with boost::format or std::stringstream.

This recommendation is often criticized by Linux developers who argue that gcc compiler checks if the format string corresponds to the actual arguments passed into the function printf. But they forget that the format string may be called from other program parts or loaded from resources. In other words, the format string is seldom present explicitly in the code of a real program and therefore the compiler cannot check it. If developers use Visual Studio 2005/2008 they will not be able to get the warning on the code like 'void *p = 0; printf('%x', p);' even using the switches /W4 and /Wall.

There exist size specifiers to work with memsize-types in functions like sscanf, printf. If you are developing a Windows application, you may use the 'I' size specifier. For example:

size_t s = 1; printf('%Iu', s);

If you are developing a Linux application, you may try the size specifier 'z'. For example:

size_t s = 1;
printf('%zu', s);

The specifiers are well described in the Wikipedia article 'printf'.

If you have to support a code being ported that uses functions like sscanf, you may employ special macros in the format of control strings that expand into the necessary size specifiers. Here is an example of a macro that helps create a portable code for various systems:

// PR_SIZET on Win64 = 'I' // PR_SIZET on Win32 = '' // PR_SIZET on Linux64 = 'z' // ... size_t u; scanf('%' PR_SIZET 'u', &u);

Here is one more example. Although it looks most strange, the code given here in an abridged form was used in a real application in the UNDO/REDO subsystem:

// Here the pointers were saved in the form of a string
int *p1, *p2;
....
char str[128];
sprintf(str, '%X %X', p1, p2);
// In another function this string was processed
// in this way:
void foo(char *str)
{
  int *p1, *p2;
  sscanf(str, '%X %X', &p1, &p2);
  // The result is incorrect values of pointers p1 and p2.
  ...
}

Manipulation with the pointers using '%X' resulted in an incorrect program behavior on a 64-bit system. This example shows how dangerous may be the depths of large and complex projects written for many years. If your project is rather large and obsolete, you might encounter very interesting fragments like this one.

Diagnosis

Those types that change their sizes on a 64-bit system, i.e. memsize-types, are dangerous for the functions with the variable number of arguments. PVS-Studio static analyzer warns the programmer about such types with the help of the V111 diagnostic warning.

If the types of the arguments have not changed their sizes, the code is considered correct and no warnings are generated. Here is an example of code correct from the analyzer's viewpoint:

printf('%d', 10*5); CString str; size_t n = sizeof(float); str.Format(StrFormat, static_cast<int>(n));

The course authors: Andrey Karpov (karpov@viva64.com), Evgeniy Ryzhkov (evg@viva64.com).

The rightholder of the course 'Lessons on development of 64-bit C/C++ applications' is OOO 'Program Verification Systems'. The company develops software in the sphere of source program code analysis. The company's site: http://www.viva64.com.

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
An expression evaluator
*args and **kwargs in python explained | Python Tips
Dynamic/Variable Amount of Arguments in a function | DaniWeb
Expand Your Programming Vocabulary
C Interview Questions
Let's Write Some x86
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服