使用strncpy代替strcpy

4
假设我有一些遗留代码,其中使用了不安全的C STL函数调用,比如 strcpy。我们都知道 strcpy 是不安全的,因为它会使程序易受缓冲区溢出问题的影响。假设我想将所有对 strcpy 的调用替换为对 strncpy 的调用。一种替换所有 strcpy(dest, src) 调用的技术涉及使用参数 (dest, src, dest长度-1) 调用 strncpy,然后用 \0 终止 dest。我知道这样做的问题在于我们并不总是知道 dest 的长度,因为它可能是指向在堆上分配的内存的指针。
让我们假设我可以确定每个调用点的 dest 长度。我可以将所有对 strcpy 的调用替换为调用 strncpy,这将保证我的程序免受缓冲区溢出攻击(至少免于因不当使用 strcpy 而导致的攻击)。然而,这种方法可能会默默截断数据并以不良方式改变程序行为。这种方法是否比检测截断并中止程序更好?或者允许截断但也记录下来?
我是从开发自动修补遗留代码的人的角度提出这个问题的。有没有人对如何最好地处理这个问题有想法呢?

strncpy() 的效果要差得多。查看您的 CRT 实现以获取替代方案。或者自己编写。这不是简单的替换,您需要知道缓冲区大小,这可能需要更改函数签名,以便将缓冲区大小作为参数传递。 - Hans Passant
我无法想象默默地截断数据会带来任何好处。我同意@HansPassant的观点,实现自己的strncpy可能是正确的选择。然后,您可以将至少在该函数内部发生截断时记录日志。 - Jud
如果程序以前由于缓冲区溢出而导致未定义的行为,那么中止程序(并显示适当的消息)对我来说似乎是完全合理的。没有特别的理由期望代码在字符串被截断到缓冲区大小时能正确地运行,因此在没有明确指定截断是需要的情况下(如果您正在替换对strcpy的调用而没有审查它发生的上下文),您不应该进行截断。 - Steve Jessop
@SteveJessop - 这种对程序的更改是否合理地被认为是将安全特性加入现有代码库中?我认为它是符合要求的,因为它通过确保程序根本不执行来防止程序由于缓冲区溢出而执行不安全操作。因此,一些人工干预可以更仔细地检查中止条件并稍后修复问题的根源。只是好奇您的想法 :) - RouteMapper
1
@RouteMapper: 好的,如果是这种情况,也许呈现它的方式是你有一个 strcpy 的替代实现,它 (a) 是符合标准的正确实现,因此除了性能问题外,它不会影响任何正确调用 strcpy 的程序 (尽管为了达到这一点,你不能仅仅调用 strncpy,因为 strncpy 存在零填充行为),(b) 防止缓冲区溢出,(c) 用其他行为替换缓冲区溢出 UB,这有助于诊断问题 (无论是中止还是记录日志)。 - Steve Jessop
显示剩余6条评论
2个回答

6
我们都知道,strcpy 不安全,因为它会导致缓冲区溢出的问题。这并不是 strcpy 的错,程序员需要确保字符串可以适应他们的缓冲区,例如,在复制之前调用 strlen 或确保输入的字符串不能超过他们的缓冲区。
假设我想将所有对 strcpy 的调用替换为对 strncpy 的调用。
除非您正在使用固定大小的字符串,否则您不应该这样做:请记住,strncpy 不仅会复制到终止空字符,还会用空字符填充其余的字符串。如果您正在寻找 strcpy 的“现代替代品”,请考虑使用 strlcpy
这种方法可能会悄悄截断数据并以不良的方式改变程序行为。与检测截断并中止程序相比,这是更好的方法吗?还是允许截断但也记录它呢?
这完全取决于你。这取决于设计中发生这种截断的位置:如果发生在将身份验证信息发送到Web服务的代码中,则最好立即停止该过程;如果发生在将跟踪消息写入日志的代码中,则可能可以忽略此问题,或者记录下来并继续进行。不幸的是,您无法自动决定,因为需要一定程度的程序理解。

我理解strcpy恰如其名,不会因为程序员的操作而“有错”。就像是一个经验不足的操作员手中的危险枪支一样,它被认为是“不安全”的。我之所以问这个问题,是因为我正在研究将安全功能集成到现有代码库中的方法。从研究的角度来看,我可以解释记录与中止的利弊。我并没有选择特定的方法。我只是想确保听取其他人的意见,并对所有方面进行适当的考虑。 - RouteMapper
这个语句“strncpy不仅复制到终止空值,而且还用空字节填充字符串的其余部分”是错误的。根据C标准:“不会复制跟在空字符后面的字符”。 - Vlad from Moscow
1
@VladfromMoscow 当然它们不是复制的!这是因为在它们的位置上写入了空字符([证明链接](http://pubs.opengroup.org/onlinepubs/7990989775/xsh/strncpy.html))。 - Sergey Kalinichenko
1
如果在复制num个字符之前找到源C字符串的末尾(由空字符表示),则会使用零填充目标,直到总共写入num个字符为止。- @VladfromMoscow - RouteMapper
@RouteMapper 你不能自动化“保护”部分而不自动化“理解”部分。这是最大的问题。例如,如果你的自动化代码修复工具正在查看一个接收目标缓冲区指针但没有接收缓冲区大小的孤立函数,修复这个问题需要找到所有相关函数的调用位置,并添加代码传递缓冲区的大小;这可能需要一直沿着调用链走到分配点,这并不容易。 - Sergey Kalinichenko
显示剩余3条评论

1

strncpy不是STL函数。:) C语言没有STL库。我认为用strncpy替换所有的strcpy并不是一个好主意,应该根据情况采取不同的方法。在大多数情况下,使用strcpy就足够了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接