格式指定符%u与sprintf一起使用会导致崩溃



当我使用%usprintf()应用程序崩溃时,它与%d工作良好

查看代码:

#include <stdio.h>
#include <string.h>
main()
{
    unsigned char dAddr[4];
    unsigned char sMask[4];
    unsigned char nHop[4];
    memset(dAddr,0,sizeof(dAddr));
    memset(sMask,0,sizeof(sMask));
    memset(nHop,0,sizeof(nHop));
    unsigned int u4IpDAddr = 0x01020304;
    unsigned int u4IpSNetMask = 0xffff01ff;
    unsigned int u4NHopGt = 0x01020304;
    char *dip = (char *)&u4IpDAddr;
    char *smk = (char *)&u4IpSNetMask;
    char *nhp = (char *)&u4NHopGt;
    sprintf(dAddr, "%u.%u.%u.%u", dip[3], dip[2], dip[1], dip[0]);  //if I used %d.%d.%d.%d its working fine
    sprintf(sMask, "%u.%u.%u.%u", smk[3], smk[2], smk[1], smk[0]); //if I used %d.%d.%d.%d its working fine
    sprintf(nHop, "%u.%u.%u.%u", nhp[3], nhp[2], nhp[1], nhp[0]); //if I used %d.%d.%d.%d its working fine
    printf("SAM: func %s line %d IpDAddr %s Mask %s NHop %sn",__func__,__LINE__,dAddr,sMask,nHop);
}

当我用以下方式声明一个指针时,它的%u.%u.%u.%u格式工作良好

unsigned char *dip = (unsigned char *)&u4IpDAddr;
unsigned char *smk = (unsigned char *)&u4IpSNetMask;
unsigned char *nhp = (unsigned char *)&u4NHopGt;

有谁能解释当我使用char指针时发生了什么吗?

unsigned char dAddr[4];
unsigned char sMask[4];
unsigned char nHop[4];

不足以容纳字典学输出。

当你在sprintf()中使用这些数组作为目标字符串时,本质上,你会超出分配的内存,创建未定义的行为。

您需要分配更多的内存来使用这些数组作为sprintf()的目的地。

如果在您的平台上对char进行了签名,那么您从u4IpSNetMask中"破解"出来的char值很可能是负的,因为您在u4IpSNetMask中有以0xF...开头的字节。当您将这样的char值发送到sprintf时,它们被转换为负int值,然后由%u说明符重新解释为unsigned值。该行为实际上是未定义的- sprintfint值与%u是非法的。然而,在实践中,您通常会得到一个需要许多字符来表示的巨大正值。这些表示很容易溢出目标缓冲区,破坏程序堆栈并导致程序崩溃。

您可以自己看到sprintf调用在典型平台上生成的内容:http://coliru.stacked-crooked.com/a/6aec03cfdf28f8b2

中间的sprintf产生4294967295.4294967295.1.4294967295。你期望这个怪物能适合只有4个字符长的缓冲区sMask吗?

此外,同样的溢出也发生在%d中,但是%d产生的字符串表示更短(-1代替4294967295)并且损坏更小,这可能就是为什么程序在某种程度上跛行到最后而没有崩溃的原因。但这并没有改变字符串1.2.3.4需要至少8个字符长的字符缓冲区的事实。你只提供了4.

换句话说,您的程序被%d破坏了,就像被%u破坏了一样。如果它没有崩溃与%d,它只是出于随机运气。

最新更新