搜索
bottom↓
回复: 18

【C语言】发现 strncpy 函数是个大坑,提醒大家慎用

[复制链接]

出200入2554汤圆

发表于 2022-7-25 00:56:03 | 显示全部楼层 |阅读模式
最近在 C 环境下编写一些字串转换识别代码,偶然间看到 strncpy 这个函数,感觉 string.h 还挺完善。

随手翻看 MSDN,函数的原型也不意外,比起标准的 strcpy 多了一个拷贝数目,想来可以拷贝指定字节:

  char *strncpy( char *dst, const char *src, size_t count );

然而细看 MSDN 才发现这函数并不像表面那样人畜无害,以下分情况讨论:

【count <= strlen(src)】

此时只是拷贝了 src 前 count 个字符,并且不追加 ASCII-NUL(0x00),相当于:

  memcpy(dst, src, count);

拷贝完字串依旧是 dst 的长度,只是复制了 src 前 count 个字符。

【count > strlen(src)】

此时的确拷贝了 src 整串,并且向 dst 追加了 count-strlen(src) 个 ASCII-NUL(0x00),相当于:

  memset(dst, 0x00, count);
  strcpy(dst, src);

拷贝完的确是 src 长度了,但写入一堆没用的 ASCII-NUL 要作何?

-------------------------------------------------------------------

综合上述两种情况,要么是可以被 memcpy 替代,要么是写一堆没用的 NUL,两种情况处理字串都不妥。

搜了搜典型用法: strncpy(dst, src, sizeof(dst)-1)
目标是确保 dst 不发生溢出,但显然依旧有风险:dst非空时,src足够长就会触发“无NUL”的BUG,导致 dst 放飞。

甚至看到坛友用: strncpy(dst, src, strlen(src))
这就不吐槽了,一样有“无NUL”的BUG.

以上,发现 strncpy 是个大坑,发来供大家参考。

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出50入58汤圆

发表于 2022-7-25 09:13:48 | 显示全部楼层
据说大名鼎鼎的缓冲区溢出漏洞大多数都是strcpy造成的

出10入46汤圆

发表于 2022-7-25 09:29:04 | 显示全部楼层
自己调用strncpy , 难道你不考虑 数组的长度范围??? 你的讨论没有意义。 编程界一直存在这两种争议。  

1. 所有程序必须含有完成的输入检测和内容安全检测,优点是安全,缺点是效率低,代码量大。
2. 所有程序仅含关键检测,优点是效率高,代码量少,缺点是不安全。

只要你的代码正确,两种库都是安全的。 针对使用场合,你可以自己追加检测

出200入2554汤圆

 楼主| 发表于 2022-7-25 09:38:46 来自手机 | 显示全部楼层
gonboy 发表于 2022-7-25 09:29
自己调用strncpy , 难道你不考虑 数组的长度范围??? 你的讨论没有意义。 编程界一直存在这两种争议。   ...
(引用自3楼)

并未表达对溢出的担心,只提及无NUL的设计容易歧义。

何以出此言?

出200入2554汤圆

 楼主| 发表于 2022-7-25 09:39:12 来自手机 | 显示全部楼层
鲜衣怒马 发表于 2022-7-25 09:13
据说大名鼎鼎的缓冲区溢出漏洞大多数都是strcpy造成的
(引用自2楼)

所以有了 _s 版本

出10入46汤圆

发表于 2022-7-25 09:56:53 | 显示全部楼层
t3486784401 发表于 2022-7-25 09:38
并未表达对溢出的担心,只提及无NUL的设计容易歧义。

何以出此言?
(引用自4楼)

代码规范即可

出0入0汤圆

发表于 2022-7-25 10:01:26 | 显示全部楼层
snprintf() 也需要注意,虽然最多只写入 (n - 1) 个字符,结尾会写入一个 '\0'
但你依然需要对返回值进行检查,判断是否已经写入完整了

https://cplusplus.com/reference/cstdio/snprintf/

If the resulting string would be longer than n-1 characters, the remaining characters are discarded and not stored, but counted for the value returned by the function.

A terminating null character is automatically appended after the content written.

Notice that only when this returned value is non-negative and less than n, the string has been completely written.

出0入0汤圆

发表于 2022-7-25 10:05:16 | 显示全部楼层
像有 strncpy() 这类问题的函数,可以封装一层,调用完之后都执行一下 arr[sizeof(arr) - 1] = '\0';
这样在不在意字符串超长被截断时,起码结尾有个 '\0' 是安全的

如果在意被截断,那调用之前就要先自己判断长度

出0入85汤圆

发表于 2022-7-26 10:37:50 来自手机 | 显示全部楼层
这是我前公司面试基本题,也是基本代码规范了
memset(dst,0,sizeof(dst));
strncpy(dst,src,sizeof(dst)-1);

出0入85汤圆

发表于 2022-7-26 10:42:33 来自手机 | 显示全部楼层
遇到要做性能调优时,就把memset改 dst[sizeof(dst)-1]=0;

出0入16汤圆

发表于 2022-7-26 12:23:47 来自手机 | 显示全部楼层
我觉得没问题,不然拼接的时候字符串全断了,我都是做完最后赋0,还有字符串长度最好使用strlen

出0入4汤圆

发表于 2022-7-27 09:07:18 | 显示全部楼层
一个是死机问题, 一个是代码逻辑问题.
比如服务器, 一个是让整个服务退出;  一个是某个客户的请求出错.

出0入0汤圆

发表于 2022-8-6 01:48:23 | 显示全部楼层
一般我在操作strncpy的时候都是预先dest尾段强制封0,不过仅限于桌面端,MCU限于处理速度难说,我还没相关开发经验

出0入0汤圆

发表于 2022-8-6 01:50:37 | 显示全部楼层
最不理想的情况就是自己用C写一个结构体,包括载荷和长度两个私有成员,但是这样,再加个gc,感觉就偏向于Python那种虚拟机了,MCU也吃不消

出200入2554汤圆

 楼主| 发表于 2022-8-6 02:10:35 | 显示全部楼层
安替比邻 发表于 2022-8-6 01:50
最不理想的情况就是自己用C写一个结构体,包括载荷和长度两个私有成员,但是这样,再加个gc,感觉就偏向于P ...
(引用自14楼)

VS 是推荐了一系列 _s 版本函数,用于解决内存溢出问题,形如:

strcpy_s( dst, dsize, src )
strcat_s
sprintf_s

可惜单片机环境没有这么细的安全函数,只能自己移植

出0入0汤圆

发表于 2022-8-6 05:30:59 | 显示全部楼层
t3486784401 发表于 2022-8-6 02:10
VS 是推荐了一系列 _s 版本函数,用于解决内存溢出问题,形如:

strcpy_s( dst, dsize, src )
(引用自15楼)

我之前写C的时候全是Linux arm环境(树莓派),所以也就没关注过_s的那群函数,VS那个我回头看看源码怎么实现的吧

出0入0汤圆

发表于 2022-8-6 05:33:10 | 显示全部楼层
安替比邻 发表于 2022-8-6 05:30
我之前写C的时候全是Linux arm环境(树莓派),所以也就没关注过_s的那群函数,VS那个我回头看看源码怎么 ...
(引用自16楼)

淦,前后矛盾了,严格来说我是学C的时候用的树莓派,然后交作业的时候用的VS,但是没提示手敲习惯了所以就延续了在树莓派上使用原版函数的习惯

出0入0汤圆

发表于 2022-8-6 10:13:44 | 显示全部楼层
strncpy经常用的,感觉还可以,使用时确实需要注意楼主提到的问题。
我通常的做法是:
(1)定义一个足够长的dst数组;
(2)memset(dst, 0x00, count);
(3)strncpy(dst, src, sizeof(dst)-1);
当然,前提是,dst的长度足够长,避免src长度过长不能完整复制的问题。

出0入0汤圆

发表于 2022-8-6 12:30:00 | 显示全部楼层
不敢用C库的,都是自己写放心
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-14 08:12

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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