首页 文章

`strtol`的实现是什么?

提问于
浏览
3

我只是好奇这个 . strtol 不要求您指定要处理的字节数,因此理论上它可能会被包含一个包含无限数字序列的字符串以供使用,从而导致拒绝服务攻击 . 当然,通过意识到长时间的精度已经耗尽(实际上不能超过65个二进制数的字符)很容易被挫败,没有必要再读一读 .

但是,在遇到第一个非空白字符之前,还需要 strtol 丢弃所需数量的空白字符 . 那么即使它对读数字很聪明,也不会被无尽的空白字符串攻击吗?

4 回答

  • 0

    但是,strtol还需要丢弃尽可能多的空白字符,直到遇到第一个非空白字符 . 那么即使它对读数字很聪明,也不会被无尽的空白字符串攻击吗?

    由于 strtol 对已经在内存中的字符串起作用,因此在将其提供给strtol之前,您必须存储(并从攻击者中读取)一定量的空格(或忘记NUL终止字符串) .

    由于实现可以保持计算有效字符串中可能存在的最大位数,因此您不必像往常一样继续计算 .

    DOS攻击可能会出现错误的实现,但请查看this相关案例(在读取双精度时,这是在java和PHP中,但在C或C实现中可能会发生相同情况) .

  • 0

    strtol 没有单一的实现 . 我怀疑任何实现都容易受到你所描述的那种攻击的影响;显而易见的实现只是遍历数字序列而不是一次性存储它们 . (注意,由于前导 0 s,数字序列可以任意长 . )

    如果要查看实现的代码,可以下载glibc版本here; strtol()stdlib/strtol.c .

  • 3

    好吧,如果你想看到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;
    }
    
  • 1

    我的个人实施 . 我没有使用任何前瞻(访问 p[1] 或类似的东西),所以从理论上讲,你可以将其转换为从流中读取的内容(例如 get_long() ,为字符调用 getc() ) .

    #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 == 0) {
                if (*p == 'X' || *p == 'x') {
                    base = 16, 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')
                digit = ((*p - 'A') & (~('a' ^ 'A'))) + 10;
            else if (*p <= '9')
                digit = *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);
    }
    

相关问题