金牌会员
 
- 积分
- 1369
- 金钱
- 1369
- 注册时间
- 2014-2-13
- 在线时间
- 169 小时
|
本帖最后由 solo 于 2016-1-18 08:10 编辑
缓存溢出问题:
题目挺大,问题很简单,后果挺严重的。
这个问题是我在做项目里偶然遇到的,当时是将一个串口采来的数据(float型)进行格式化显示到屏幕上,正常情况下的数据长度是已知的,但是有一次串口接收来一个异常数据(一个超大的数据,但不超float范围),结果在用sprintf格式化的时候缓存爆掉死机了。。。然后,想着开缓存大点?但是要多大?太大还浪费,得不偿失。或者自己实现个数值显示函数,太麻烦了(不是实现麻烦,甚至已经把UCGUI里的float显示函数抄过来了),自己实现的控制格式繁琐不方便。再或者把sprintf自己实现一遍,汗。。。工程好壮大,不过八度之前自己实现过了。在我即将要拷贝八度的液晶格式格式化函数的时候,偶然发现原来这部分早有人重写过了,也就是现在的标准库函数里的安全版本,但是为毛书上没写过??教科书上真没见过。。。或许是我的错手上明明有好书不看(《PONINTERS ON C》1997-07-25出版 入手晚,还没看完),但是人家书上很负责人的写上了这些。
函数
|
严重性
|
解决方案
|
gets
|
最危险
|
使用 fgets(buf, size, stdin)。这几乎总是一个大问题!
|
strcpy
|
很危险
|
改为使用 strncpy。
|
strcat
|
很危险
|
改为使用 strncat。
|
sprintf
|
很危险
|
改为使用 snprintf,或者使用精度说明符。
|
scanf
|
很危险
|
使用精度说明符,或自己进行解析。
|
sscanf
|
很危险
|
使用精度说明符,或自己进行解析。
|
fscanf
|
很危险
|
使用精度说明符,或自己进行解析。
|
vfscanf
|
很危险
|
使用精度说明符,或自己进行解析。
|
vsprintf
|
很危险
|
改为使用 vsnprintf,或者使用精度说明符。
|
vscanf
|
很危险
|
使用精度说明符,或自己进行解析。
|
vsscanf
|
很危险
|
使用精度说明符,或自己进行解析。
|
streadd
|
很危险
|
确保分配的目的地参数大小是源参数大小的四倍。
|
strecpy
|
很危险
|
确保分配的目的地参数大小是源参数大小的四倍。
|
strtrns
|
危险
|
手工检查来查看目的地大小是否至少与源字符串相等。
|
realpath
|
很危险(或稍小,取决于实现)
|
分配缓冲区大小为 MAXPATHLEN。同样,手工检查参数以确保输入参数不超过 MAXPATHLEN。
|
syslog
|
很危险(或稍小,取决于实现)
|
在将字符串输入传递给该函数之前,将所有字符串输入截成合理的大小。
|
getopt
|
很危险(或稍小,取决于实现)
|
在将字符串输入传递给该函数之前,将所有字符串输入截成合理的大小。
|
getopt_long
|
很危险(或稍小,取决于实现)
|
在将字符串输入传递给该函数之前,将所有字符串输入截成合理的大小。
|
getpass
|
很危险(或稍小,取决于实现)
|
在将字符串输入传递给该函数之前,将所有字符串输入截成合理的大小。
|
getchar
|
中等危险
|
如果在循环中使用该函数,确保检查缓冲区边界。
|
fgetc
|
中等危险
|
如果在循环中使用该函数,确保检查缓冲区边界。
|
getc
|
中等危险
|
如果在循环中使用该函数,确保检查缓冲区边界。
|
read
|
中等危险
|
如果在循环中使用该函数,确保检查缓冲区边界。
|
bcopy
|
低危险
|
确保缓冲区大小与它所说的一样大。
|
fgets
|
低危险
|
确保缓冲区大小与它所说的一样大。
|
memcpy
|
低危险
|
确保缓冲区大小与它所说的一样大。
|
snprintf
|
低危险
|
确保缓冲区大小与它所说的一样大。
|
strccpy
|
低危险
|
确保缓冲区大小与它所说的一样大。
|
strcadd
|
低危险
|
确保缓冲区大小与它所说的一样大。
|
strncpy
|
低危险
|
确保缓冲区大小与它所说的一样大。
|
vsnprintf
|
低危险
|
确保缓冲区大小与它所说的一样大。
|
这里说的比较详细:http://blog.csdn.net/uniquecapo/article/details/38235149
还有一个问题就关于可重入和不可重入函数,有兴趣的接着谈论下。。。
***********************************************************************************************************************************************************************线程安全( Thread-safe )与不安全( not thread-safe)函数的讨论 可重入和线程安全是两个不同的概念:可重入函数一定是线程安全的;线程安全的函数可能是重入的,也可能是不重入的;线程不安全的函数一定是不可重入的。 http://www.cnblogs.com/xiangshancuizhu/archive/2012/10/22/2734497.html http://www.cnblogs.com/xiangshancuizhu/archive/2012/10/22/2734497.html 关于可重入和不可重入的讨论这两篇说的已经很好了,下面就直接来讨论线程安全性的问题。
Thread-safe C library functions
4.59 Thread-safe C library functions
The following table shows the C library functions that are
thread-safe.
Table 4-1 Functions that are
thread-safe
Functions |
Description |
calloc(),
free(), malloc(), realloc() |
The heap functions are thread-safe if the _mutex_ *
functions are implemented.
All threads share a single heap and use mutexes to avoid data
corruption when there is concurrent access. Each heap implementation is
responsible for doing its own locking. If you supply your own allocator, it must
also do its own locking. This enables it to do fine-grained locking if required,
rather than protecting the entire heap with a single mutex (coarse-grained
locking). |
alloca()
|
alloca() is thread-safe because it allocates memory on
the stack.
|
abort(),
raise(), signal(), fenv.h |
The ARM® signal handling functions and
floating-point exception traps are thread-safe.
The settings for signal handlers and floating-point traps are
global across the entire process and are protected by locks. Data corruption
does not occur if multiple threads call signal() or an fenv.h function at the same time. However, be aware that
the effects of the call act on all threads and not only on the calling
thread. |
clearerr(),
fclose(), feof(),ferror(), fflush(), fgetc(),fgetpos(), fgets(),
fopen(),fputc(), fputs(), fread(),freopen(), fseek(), fsetpos(),ftell(),
fwrite(), getc(),getchar(), gets(), perror(),putc(), putchar(), puts(),rewind(),
setbuf(), setvbuf(),tmpfile(), tmpnam(), ungetc() |
The stdio library is thread-safe if the
_mutex_ * functions are implemented.
Each individual stream is protected by a lock, so two threads can
each open their own stdio stream and use it, without interfering
with one another.
If two threads both want to read or write the same stream, locking
at the fgetc() and fputc() level prevents data
corruption, but it is possible that the individual characters output by each
thread might be interleaved in a confusing way.
Notetmpnam() also contains a static buffer but
this is only used if the argument is NULL . To ensure that your use
of tmpnam() is thread-safe, supply your own buffer space.
|
fprintf(),
printf(), vfprintf(), vprintf(), fscanf(), scanf() |
When using these functions:
|
clock() |
clock()
contains static data that is written once at program startup and then only ever
read. Therefore, clock() is thread-safe provided no extra threads
are already running at the time that the library is initialized. |
errno |
errno is thread-safe.
Each thread has its own errno stored in a
__user_perthread_libspace block. This means that each thread can
call errno -setting functions independently and then check
errno afterwards without interference from other
threads. |
atexit() |
The list of exit functions maintained by atexit() is
process-global and protected by a lock.
In the worst case, if more than one thread calls
atexit() , the order that exit functions are called cannot be
guaranteed. |
abs(), acos(),
asin(),atan(), atan2(), atof(),atol(), atoi(), bsearch(),ceil(), cos(),
cosh(),difftime(), div(), exp(),fabs(), floor(), fmod(),frexp(), labs(),
ldexp(),ldiv(), log(), log10(),memchr(), memcmp(), memcpy(),memmove(), memset(),
mktime(),modf(), pow(), qsort(),sin(), sinh(), sqrt(),strcat(), strchr(),
strcmp(),strcpy(), strcspn(), strlcat(),strlcpy(), strlen(),
strncat(),strncmp(), strncpy(), strpbrk(),strrchr(), strspn(),
strstr(),strxfrm(), tan(), tanh() |
These functions are inherently thread-safe. |
longjmp(),
setjmp() |
Although setjmp() and longjmp() keep data
in __user_libspace , they call the __alloca_*
functions, that are thread-safe. |
remove(),
rename(), time() |
These functions use interrupts that communicate with the
ARM debugging environments. Typically, you have to reimplement
these for a real-world application. |
snprintf(),
sprintf(), vsnprintf(),vsprintf(), sscanf(), isalnum(),isalpha(), iscntrl(),
isdigit(),isgraph(), islower(), isprint(),ispunct(), isspace(),
isupper(),isxdigit(), tolower(), toupper(),strcoll(), strtod(),
strtol(),strtoul(), strftime() |
When using these functions, the string-based functions read the
locale settings. Typically, they are thread-safe. However, if you change locale
in mid-session, you must ensure that these functions are not affected.
The string-based functions, such as sprintf() and
sscanf() , do not depend on the stdio
library. |
stdin, stdout,
stderr |
These functions are
thread-safe. |
C library functions that are not thread-safe
4.60 C library functions that are not thread-safe
The following table shows the C library functions that are not
thread-safe.
Table 4-2 Functions that are not
thread-safe
Functions |
Description |
asctime(), localtime(), strtok()
|
These functions are all thread-unsafe. Each contains a static
buffer that might be overwritten by another thread between a call to the
function and the subsequent use of its return value.
ARM® supplies reentrant versions,
_asctime_r() , _localtime_r() , and
_strtok_r() . ARM recommends that you use these functions instead to
ensure safety.
NoteThese reentrant versions take additional parameters.
_asctime_r() takes an additional parameter that is a pointer to a
buffer that the output string is written into. _localtime_r() takes
an additional parameter that is a pointer to a struct tm , that the
result is written into. _strtok_r() takes an additional parameter
that is a pointer to a char pointer to
the next token. |
exit() |
Do not call exit() in a multithreaded program even if
you have provided an implementation of the underlying _sys_exit()
that actually terminates all threads.
In this case, exit() cleans up before calling _sys_exit() so disrupts other
threads. |
gamma(), lgamma(), lgammaf(), lgammal() a |
These extended
mathlib functions use a global variable, _signgam, so
are not thread-safe. |
mbrlen(), mbsrtowcs(), mbrtowc(), wcrtomb(),
wcsrtombs()
|
The C90 multibyte conversion functions (defined in stdlib.h) are not thread-safe, for example
mblen() and mbtowc() , because they contain internal
static state that is shared between all threads without locking.
However, the extended restartable versions (defined in wchar.h) are thread-safe, for example
mbrtowc() and wcrtomb() , provided you pass in a
pointer to your own mbstate_t object. You must
exclusively use these functions with non-NULL mbstate_t
* parameters if you want to ensure thread-safety when handling multibyte
strings. |
rand(),
srand() |
These functions keep internal state that is both global and
unprotected. This means that calls to rand() are never
thread-safe.
ARM recommends that you do one of the following:
-
Use the reentrant versions _rand_r() and
_srand_r() supplied by ARM. These use user-provided buffers instead
of static data within the C library.
-
Use your own locking to ensure that only one thread ever calls
rand() at a time, for example, by defining
$Sub$$rand() if you want to avoid changing your code.
-
Arrange that only one thread ever needs to generate random
numbers.
-
Supply your own random number generator that can have multiple
independent instances.
Note_rand_r() and _srand_r() both
take an additional parameter that is a pointer to a buffer storing the state of
the random number generator. |
setlocale(), localeconv()
|
setlocale() is used for setting and reading locale
settings. The locale settings are global across all threads, and are not
protected by a lock. If two threads call setlocale() to
simultaneously modify the locale settings, or if one thread reads the settings
while another thread is modifying them, data corruption might occur. Also, many
other functions, for example strtod() and sprintf() ,
read the current locale settings. Therefore, if one thread calls
setlocale() concurrently with another thread calling such a
function, there might be unexpected results.
Multiple threads reading the settings
simultaneously is thread-safe in simple cases and if no other thread is
simultaneously modifying those settings, but where internally an intermediate
buffer is required for more complicated returned results, unexpected results can
occur unless you use a reentrant version of setlocale() .
ARM recommends that you either:
-
Choose the locale you want and call setlocale() once
to initialize it. Do this before creating any additional threads in your program
so that any number of threads can read the locale settings concurrently without
interfering with one another.
-
Use the reentrant version _setlocale_r() supplied by
ARM. This returns a string that is either a pointer to a constant
string, or a pointer to a string stored in a user-supplied buffer that can be
used for thread-local storage, rather than using memory within the C library.
The buffer must be at least _SETLOCALE_R_BUFSIZE bytes long,
including space for a trailing NUL .
Be aware that _setlocale_r() is not fully thread-safe
when accessed concurrently to change locale settings. This
access is not lock-protected.
Also, be aware that localeconv() is not thread-safe.
Call the ARM function _get_lconv() with a pointer to a
user-supplied buffer
instead. |
|
|