`strtol`的实现是什么?

3
我对此很好奇。函数strtol不要求您指定要处理的字节数,因此理论上它可以处理包含无尽数字序列的字符串,从而导致拒绝服务攻击。当然,只要一旦long数据类型的精度已经耗尽(实际上不能超过二进制数的65个字符),就没有必要再读取任何内容,因此可以轻松地避免这种攻击。
但是,函数strtol也需要丢弃多余的空格字符,直到遇到第一个非空格字符。所以,即使它在读取数字时很聪明,如果输入一个无尽的空格字符串,它是否也会受到攻击呢?

5
你的"无限"空格字符串需要无限的内存。这里是一个示例实现,链接为ftp://ftp.irisa.fr/pub/OpenBSD/src/sys/lib/libsa/strtol.c。它确实聪明地避免读取过多位数。 - user786653
您应该验证传递给strtol的参数——正如您应该对大多数库函数一样进行验证。例如,第一个参数必须是一个字符串(即它必须以空字符结尾)。如果涉及到无限长的字符串,也要确保不会传递进去。 - nos
使用fgets()(和sscanf())来读取用户输入! - pmg
4
如果您的应用程序可以被欺骗以尝试消耗“无穷无尽的数字流”,则不需要使用strtol来进行DOS攻击。 - Blastfurnace
@user78:当基本字符集为EBCDIC时,该示例无法工作! - Kerrek SB
显示剩余2条评论
4个回答

4
然而,strtol也需要丢弃尽可能多的空格字符,直到遇到第一个非空格字符。因此,即使它在读取数字方面很聪明,它也可能受到无限空格字符串的攻击吗?
由于strtol在已经存在于内存中的字符串上工作,因此您必须在将其提供给strtol之前存储(并从攻击者那里读取)“无限”数量的空格(或忘记对字符串进行NUL终止)。
由于实现可以计算出有效字符串中可能存在的最大数字数量,所以它不必继续进行,就像您怀疑的那样。
但是,如果实现有缺陷,DOS攻击可能会发生,请查看与之相关的this案例(这是在java和PHP读取double时发生的情况,但在C或C++实现中也可能发生)。

2

strtol 没有单一的实现。我怀疑任何实现都不容易受到您所描述的攻击;显而易见的实现只需遍历数字序列,而无需一次性存储所有数字。(请注意,由于前导的 0,数字序列可以是任意长的。)

如果您想查看实现的代码,可以在此处下载 glibc 版本 herestrtol()stdlib/strtol.c 中。


0
我的个人实现。我没有使用任何前瞻(访问p[1]或类似的东西),所以理论上你可以将其转换为从流中读取的内容(例如,调用getc()来获取字符的get_long())。
#include <errno.h>
#define LONG_MAX ((long)(~0UL>>1))
#define LONG_MIN (~LONG_MAX)
int isspace(int c); /* <-- Forward declare from <ctype.h> */ 

long strtol(const char *restrict nptr, char **restrict endptr, int base) {
    const char *p = nptr, *endp;
    _Bool is_neg = 0, overflow = 0;
    /* Need unsigned so (-LONG_MIN) can fit in these: */
    unsigned long n = 0UL, cutoff;
    int cutlim;
    if (base < 0 || base == 1 || base > 36) {
#ifdef EINVAL /* errno value defined by POSIX */
        errno = EINVAL;
#endif
        return 0L;
    }
    endp = nptr;
    while (isspace(*p))
        p++;
    if (*p == '+') {
        p++;
    } else if (*p == '-') {
        is_neg = 1, p++;
    }
    if (*p == '0') {
        p++;
        /* For strtol(" 0xZ", &endptr, 16), endptr should point to 'x';
         * pointing to ' ' or '0' is non-compliant.
         * (Many implementations do this wrong.) */
        endp = p;
        if (base == 16 && (*p == 'X' || *p == 'x')) {
            p++;
        } else if (base == 2 && (*p == 'B' || *p == 'b')) {
            /* C23 standard supports "0B" and "0b" prefixes. */
            p++;
        } else if (base == 0) {
            if (*p == 'X' || *p == 'x') {
                base = 16, p++;
            } else if (*p == 'B' || *p == 'b') {
                base = 2, p++;
            } else {
                base = 8;
            }
        }
    } else if (base == 0) {
        base = 10;
    }
    cutoff = (is_neg) ? -(LONG_MIN / base) : LONG_MAX / base;
    cutlim = (is_neg) ? -(LONG_MIN % base) : LONG_MAX % base;
    while (1) {
        int c;
        if (*p >= 'A')
            c = ((*p - 'A') & (~('a' ^ 'A'))) + 10;
        else if (*p <= '9')
            c = *p - '0';
        else
            break;
        if (c < 0 || c >= base) break;
        endp = ++p;
        if (overflow) {
            /* endptr should go forward and point to the non-digit character
             * (of the given base); required by ANSI standard. */
            if (endptr) continue;
            break;
        }
        if (n > cutoff || (n == cutoff && c > cutlim)) {
            overflow = 1; continue;
        }
        n = n * base + c;
    }
    if (endptr) *endptr = (char *)endp;
    if (overflow) {
        errno = ERANGE; return ((is_neg) ? LONG_MIN : LONG_MAX);
    }
    return (long)((is_neg) ? -n : n);
}

这个无法编译。 - Kennet Celeste

-1

如果你想查看strtol,你可以查看加州大学的相关资料。

/* 
 * strtol.c --
 *
 *  Source code for the "strtol" library procedure.
 *
 * Copyright (c) 1988 The Regents of the University of California.
 * All rights reserved.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 * 
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */
static const char rcsid[] = "$Header$ SPRITE (Berkeley)";

#include <ctype.h>

extern unsigned long int strtoul(char *string, char **endPtr, int base);

/*
 *----------------------------------------------------------------------
 *
 * strtol --
 *
 *  Convert an ASCII string into an integer.
 *
 * Results:
 *  The return value is the integer equivalent of string.  If endPtr
 *  is non-NULL, then *endPtr is filled in with the character
 *  after the last one that was part of the integer.  If string
 *  doesn't contain a valid integer value, then zero is returned
 *  and *endPtr is set to string.
 *
 * Side effects:
 *  None.
 *
 *----------------------------------------------------------------------
 */

long int
strtol(
    char *string,       /* String of ASCII digits, possibly
                 * preceded by white space.  For bases
                 * greater than 10, either lower- or
                 * upper-case digits may be used.
                 */
    char **endPtr,      /* Where to store address of terminating
                 * character, or NULL. */
    int base            /* Base for conversion.  Must be less
                 * than 37.  If 0, then the base is chosen
                 * from the leading characters of string:
                 * "0x" means hex, "0" means octal, anything
                 * else means decimal.
                 */
)
{
    register char *p;
    int result;

    /*
     * Skip any leading blanks.
     */
    p = string;
    while (isspace(*p)) {
    p += 1;
    }

    /*
     * Check for a sign.
     */
    if (*p == '-') {
    p += 1;
    result = -1*(strtoul(p, endPtr, base));
    } else {
    if (*p == '+') {
        p += 1;
    }
    result = strtoul(p, endPtr, base);
    }
    if ((result == 0) && (endPtr != 0) && (*endPtr == p)) {
    *endPtr = string;
    }
    return result;
}

2
这个 strtol() 在溢出时不像 C 规范中指定的那样返回 INT_MAXINT_MIN - chux - Reinstate Monica
strtoul 的源代码在哪里? - user3064538

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