无法通过strtol()来检测溢出

问题描述 投票:0回答:1

此程序使用词法扫描器将标记分类为符号,字符串,十进制数字,十六进制数字,…。当检测到“数字”时,将其移交给strtol()以将其转换为内部32位二进制值。但是我无法获得strtol()来可靠地返回溢出错误。

转换代码的一部分是:

    errno = 0;      // erase any previous error in errno
    switch (constType) {
…
case lxHex:         // hexadecimal number X'1234567890ABCDEF' (X-string)
fprintf(stderr,"** FindConstantFromString - converting %s\n",constBuffer);
        newDictEntry->dcValue = strtol(constBuffer+2, NULL, 16);
int myerr = errno;
fprintf(stderr,"     value %x errno %d\n",newDictEntry->dcValue, myerr);
        newDictEntry->dcType = syNumber;
        newDictEntry->dcSubType = 4;    // hexadecimal
        if  (   EINVAL == errno
            ||  ERANGE == errno
            ) {
            ErrDict = newDictEntry;
            AnaError (ConstMsg+2);
            newDictEntry->dcType = sySLit;
        };
        result.cstClass = newDictEntry->dcType;
        return result;
…

[当此代码使用错误的输入进行测试时,仅当第一个十六进制数字为> = 8(可能给出负值)时,它才会检测到溢出,如下所示:

    29            | declare v;
    30            | v = x'fedcba9876543210'
** FindConstantFromString - meeting x'fedcba9876543210' as 11
** FindConstantFromString - converting x'fedcba9876543210'
     value ffffffff errno 34
*Error 32: Candidate number x'fedcba9876543210' too large or could not be converted
*Error 20: Unrecognisable lexical unit x'fedcba9876543210' at 30.5
    31            |     + x'123456789abcdef'
** FindConstantFromString - meeting x'123456789abcdef' as 11
** FindConstantFromString - converting x'123456789abcdef'
     value 89abcdef errno 0
    32            |     + 9876543210
** FindConstantFromString - meeting x'fedcba9876543210' as 8
                            symbol already known
** FindConstantFromString - converting x'fedcba9876543210'
     value 0 errno 0
*Error 32: Candidate number x'fedcba9876543210' too large or could not be converted
** FindConstantFromString - meeting 9876543210 as 8
** FindConstantFromString - converting 9876543210
     value 4cb016ea errno 0
    33            |     + '12345a'
** FindConstantFromString - meeting 12345a as 3
    34            |     + '';
** FindConstantFromString - meeting 12345a as 8
                            symbol already known
** FindConstantFromString - converting 12345a
     value 3039 errno 0
*Error 32: Candidate number 12345a too large or could not be converted
** FindConstantFromString - meeting  as 3
** FindConstantFromString - meeting  as 8
                            symbol already known
*Error 33: Empty string cannot be converted to number

在第30行,词法扫描器识别出一个十六进制数,并要求从该十六进制形式进行转换(11 = 1xHex)。 strtol()正确将errno设置为ERANGE,并发出错误消息。然后,溢出的十六进制数将作为字符串保留在字典中。

请注意,返回值为-1,而不是LONG_MAX

在第31行,我们再次有另一个溢出的十六进制数字,但是它不是以8-9a-f开头。再次将其检测为十六进制数字。尝试进行转换,但根本未设置errno。该值对应于数字的低32位。由于成功完成,因此将保留截断后的值。

+应用于“ x'fed…'”和89abcdef时,尝试对string“ x'fed…'”“应该是十进制数字(由8请求表示)进行另一次转换。 ),转换失败,因为“ x”不能以十进制数字开头。

在第32行,我们有一个溢出的十进制987654321。再次,未检测到溢出(未显示代码,但类似于十六进制数字的代码,并在“ endptr”上添加了一个测试,因为字符串可能没有被过滤词法扫描程序,并且包含非法字符)。返回的值包含数字的最少32位。

如果将strtol()更改为strtoul(),则第一个ERANGE错误消失,并且我得到该数字的至少32位。

我在做什么错?

系统:Fedora Linux 29glibc:2.27

c overflow detection strtol
1个回答
1
投票

strtol()从给定的字符串中得出long(即signed long)值。它只关心内部构造的64位long值的上溢或下溢,并且如果没有遇到任何问题,它将最终返回到其调用方。它完全不关心32位值的上溢或下溢。

这就是为什么strtol()仅在您显示的示例中返回错误,其中long值将溢出为负64位数字。 (正如您所指出的,在这种情况下strtoul()不会抱怨,因为在这种情况下unsigned long值不会溢出。您需要输入strtoul()一个17位数字的字符串来使unsigned long溢出。 )

strtol()也不知道或不在乎您的程序将其64位long结果并通过将值分配给32位变量立即丢弃其高4个字节。这种截断是导致您认为“ 返回值是-1,而不是LONG_MAX”的原因。实际上,来自strtol() was LONG_MAX的结果,但是您的程序已舍弃了LONG_MAX的前4个字节,只剩下了低4个字节,其值是0xffffffff-1当被视为32位int时。

如果要使用strtol()生成和审核32位值,则必须自己进行其他范围检查。首先将strtol()结果收集到long变量中,并在执行strtol()期间检查该结果是否指示64位上溢或下溢,然后可以将该long结果与INT_MAX和[ C0],以查看其值是溢出还是下溢32位变量。

[显然,您可以将其包装在一个小函数中,该函数可以(如果您对INT_MIN进行适当的修补)则表现与errno相似,只是它适用于strtol()值而不是int。但是,您应该避免给函数命名long,因为POSIX保留了以strtoi开头的名称,以备将来在标准库中使用。某些系统可能已经有一个str[a-z],而Linux有一天可能会获得一个。

© www.soinside.com 2019 - 2024. All rights reserved.