OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 8576|回复: 21

关于c语言中不安全的库函数”

[复制链接]

48

主题

537

帖子

2

精华

金牌会员

Rank: 6Rank: 6

积分
1369
金钱
1369
注册时间
2014-2-13
在线时间
169 小时
发表于 2015-12-25 11:06:47 | 显示全部楼层 |阅读模式
本帖最后由 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.

Note

tmpnam() 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:
  • The standard C printf() and scanf() functions use stdio so they are thread-safe.
  • The standard C printf() function is susceptible to changes in the locale settings if called in a multithreaded program.
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.

Note

These 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.

出处: MDK 5.17 说明书

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

48

主题

537

帖子

2

精华

金牌会员

Rank: 6Rank: 6

积分
1369
金钱
1369
注册时间
2014-2-13
在线时间
169 小时
 楼主| 发表于 2015-12-25 11:22:23 | 显示全部楼层
本帖最后由 solo 于 2016-1-16 22:09 编辑


回复 支持 反对

使用道具 举报

15

主题

786

帖子

5

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3223
金钱
3223
注册时间
2015-7-26
在线时间
811 小时
发表于 2015-12-25 11:28:50 | 显示全部楼层
自己写的代码中经常用到你上面提到的库函数,还有些其他的strtok、atoi......等等,而且结构体不对齐用这些确实也很危险,吃过不少亏,你提供的资料很有用,谢谢
我的博客:http://blog.csdn.net/itdo_just
回复 支持 反对

使用道具 举报

22

主题

2254

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
4486
金钱
4486
注册时间
2013-4-22
在线时间
337 小时
发表于 2015-12-25 11:29:16 | 显示全部楼层
keil 中使用sprintf要格外小心,其实是不能使用的,一般需要做对齐处理
回复 支持 反对

使用道具 举报

48

主题

537

帖子

2

精华

金牌会员

Rank: 6Rank: 6

积分
1369
金钱
1369
注册时间
2014-2-13
在线时间
169 小时
 楼主| 发表于 2015-12-25 12:50:22 | 显示全部楼层
回复【4楼】三叶草:
---------------------------------
没遇到过,举个例子?
回复 支持 反对

使用道具 举报

22

主题

180

帖子

1

精华

高级会员

Rank: 4

积分
616
金钱
616
注册时间
2015-6-29
在线时间
101 小时
发表于 2015-12-25 13:01:43 | 显示全部楼层
学习了,虽然不怎么用
我是菜鸟
回复 支持 反对

使用道具 举报

48

主题

537

帖子

2

精华

金牌会员

Rank: 6Rank: 6

积分
1369
金钱
1369
注册时间
2014-2-13
在线时间
169 小时
 楼主| 发表于 2015-12-25 13:03:37 | 显示全部楼层
回复【6楼】负西弱:
---------------------------------
你没处理过字符串吗?
回复 支持 反对

使用道具 举报

88

主题

7377

帖子

5

精华

资深版主

Rank: 8Rank: 8

积分
14980
金钱
14980
注册时间
2013-11-13
在线时间
1823 小时
发表于 2015-12-25 21:07:52 | 显示全部楼层
不错,多谢分享
开往春天的手扶拖拉机
回复 支持 反对

使用道具 举报

18

主题

238

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1823
金钱
1823
注册时间
2014-8-5
在线时间
211 小时
发表于 2015-12-25 22:23:32 | 显示全部楼层
谢谢楼主分享,还没有搞的楼主这么细
回复 支持 反对

使用道具 举报

33

主题

481

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
5075
金钱
5075
注册时间
2013-10-4
在线时间
654 小时
发表于 2015-12-25 22:29:42 | 显示全部楼层
谢谢楼主分享,学习了
回复 支持 反对

使用道具 举报

7

主题

103

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
311
金钱
311
注册时间
2015-8-18
在线时间
63 小时
发表于 2015-12-29 13:53:35 | 显示全部楼层
呵呵,上面问题其实,你用之前严格按照语法,一步步写,问题都不大。
回复 支持 反对

使用道具 举报

34

主题

805

帖子

4

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
1865
金钱
1865
注册时间
2011-3-29
在线时间
140 小时
发表于 2015-12-29 14:19:03 来自手机 | 显示全部楼层
楼上的,不是语法的问题,是内存容易溢出覆盖导致程序乱飞。
回复 支持 反对

使用道具 举报

48

主题

537

帖子

2

精华

金牌会员

Rank: 6Rank: 6

积分
1369
金钱
1369
注册时间
2014-2-13
在线时间
169 小时
 楼主| 发表于 2015-12-30 08:15:15 | 显示全部楼层
feisheng168 发表于 2015-12-29 13:53
呵呵,上面问题其实,你用之前严格按照语法,一步步写,问题都不大。

如果需要格式化的数据的来自外部的呢,每次格式化前先判断长度?做过,但是麻烦增加程序的复杂度,这个应该sprintf自己去解决。“%10d 在未超出限定长度时进行补零或空格,当超出限定长度时按数据的实际长度来”sprintf的这个规定很危险。。。
回复 支持 反对

使用道具 举报

48

主题

537

帖子

2

精华

金牌会员

Rank: 6Rank: 6

积分
1369
金钱
1369
注册时间
2014-2-13
在线时间
169 小时
 楼主| 发表于 2015-12-30 08:15:51 | 显示全部楼层
ofourme 发表于 2015-12-29 14:19
楼上的,不是语法的问题,是内存容易溢出覆盖导致程序乱飞。

确实很难控制
回复 支持 反对

使用道具 举报

13

主题

296

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2067
金钱
2067
注册时间
2012-5-26
在线时间
292 小时
发表于 2015-12-30 08:35:17 | 显示全部楼层
谢谢分享,以后用到会注意
活着才是王道!健康是一切的前提!
回复 支持 反对

使用道具 举报

18

主题

422

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1139
金钱
1139
注册时间
2014-5-24
在线时间
116 小时
发表于 2015-12-30 08:57:41 | 显示全部楼层
算不得什么危险系数,不过是有些函数副作用明显,需要正确调用。
I See Fire.·.·.·.·.·.·.·.·.·.·.·.·.·.·.·.·.·.·.·.  只给方向,不妨碍思考
回复 支持 反对

使用道具 举报

48

主题

537

帖子

2

精华

金牌会员

Rank: 6Rank: 6

积分
1369
金钱
1369
注册时间
2014-2-13
在线时间
169 小时
 楼主| 发表于 2015-12-30 09:55:35 | 显示全部楼层
emWin 发表于 2015-12-30 08:57
算不得什么危险系数,不过是有些函数副作用明显,需要正确调用。

这个正确调用恐怕不如换成安全版的简单吧,如果正确调用恐怕很麻烦
回复 支持 反对

使用道具 举报

48

主题

537

帖子

2

精华

金牌会员

Rank: 6Rank: 6

积分
1369
金钱
1369
注册时间
2014-2-13
在线时间
169 小时
 楼主| 发表于 2016-1-16 09:47:33 | 显示全部楼层
此贴废了,重发一个
回复 支持 反对

使用道具 举报

357

主题

1478

帖子

12

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
8122
金钱
8122
注册时间
2015-10-15
在线时间
2928 小时
发表于 2016-1-16 11:49:03 | 显示全部楼层
你是从其他网站复制的吗?
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

48

主题

537

帖子

2

精华

金牌会员

Rank: 6Rank: 6

积分
1369
金钱
1369
注册时间
2014-2-13
在线时间
169 小时
 楼主| 发表于 2016-1-16 11:56:09 | 显示全部楼层
openedvadmin 发表于 2016-1-16 11:49
你是从其他网站复制的吗?

发绿的表格式是复制过来的
回复 支持 反对

使用道具 举报

48

主题

537

帖子

2

精华

金牌会员

Rank: 6Rank: 6

积分
1369
金钱
1369
注册时间
2014-2-13
在线时间
169 小时
 楼主| 发表于 2016-1-16 11:56:39 | 显示全部楼层
openedvadmin 发表于 2016-1-16 11:49
你是从其他网站复制的吗?

但是之前这么操作没问题的
回复 支持 反对

使用道具 举报

48

主题

537

帖子

2

精华

金牌会员

Rank: 6Rank: 6

积分
1369
金钱
1369
注册时间
2014-2-13
在线时间
169 小时
 楼主| 发表于 2016-1-18 08:11:33 | 显示全部楼层
帖子复活了
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-7-15 10:41

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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