libratbox: Fix undefined behaviour advancing pointer beyond end of array.
The C standard does not allow constructing pointers beyond one past the end of an array. Therefore, if size is an unsigned type (size_t), then buf + size is never less than buf. Clang on 32-bit took advantage of the undefined behaviour, causing segfaults. Lightly tested.
This commit is contained in:
parent
7f2508c135
commit
fab79c5d25
1 changed files with 80 additions and 87 deletions
|
@ -151,8 +151,8 @@ put_dec(char *buf, unsigned long long int num)
|
||||||
#define SPECIAL 32 /* 0x */
|
#define SPECIAL 32 /* 0x */
|
||||||
#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
|
#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
|
||||||
|
|
||||||
static char *
|
static size_t
|
||||||
number(char *buf, char *end, unsigned long long int num, int base, int size, int precision,
|
number(char *const buf, const size_t size, size_t idx, unsigned long long int num, int base, int field_width, int precision,
|
||||||
int type)
|
int type)
|
||||||
{
|
{
|
||||||
char sign, tmp[66];
|
char sign, tmp[66];
|
||||||
|
@ -167,7 +167,7 @@ number(char *buf, char *end, unsigned long long int num, int base, int size, int
|
||||||
if(type & LEFT)
|
if(type & LEFT)
|
||||||
type &= ~ZEROPAD;
|
type &= ~ZEROPAD;
|
||||||
if(base < 2 || base > 36)
|
if(base < 2 || base > 36)
|
||||||
return NULL;
|
return idx;
|
||||||
sign = 0;
|
sign = 0;
|
||||||
if(type & SIGN)
|
if(type & SIGN)
|
||||||
{
|
{
|
||||||
|
@ -175,24 +175,24 @@ number(char *buf, char *end, unsigned long long int num, int base, int size, int
|
||||||
{
|
{
|
||||||
sign = '-';
|
sign = '-';
|
||||||
num = -(signed long long int)num;
|
num = -(signed long long int)num;
|
||||||
size--;
|
field_width--;
|
||||||
}
|
}
|
||||||
else if(type & PLUS)
|
else if(type & PLUS)
|
||||||
{
|
{
|
||||||
sign = '+';
|
sign = '+';
|
||||||
size--;
|
field_width--;
|
||||||
}
|
}
|
||||||
else if(type & SPACE)
|
else if(type & SPACE)
|
||||||
{
|
{
|
||||||
sign = ' ';
|
sign = ' ';
|
||||||
size--;
|
field_width--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(need_pfx)
|
if(need_pfx)
|
||||||
{
|
{
|
||||||
size--;
|
field_width--;
|
||||||
if(base == 16)
|
if(base == 16)
|
||||||
size--;
|
field_width--;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* generate full string in tmp[], in reverse order */
|
/* generate full string in tmp[], in reverse order */
|
||||||
|
@ -226,69 +226,69 @@ number(char *buf, char *end, unsigned long long int num, int base, int size, int
|
||||||
if(i > precision)
|
if(i > precision)
|
||||||
precision = i;
|
precision = i;
|
||||||
/* leading space padding */
|
/* leading space padding */
|
||||||
size -= precision;
|
field_width -= precision;
|
||||||
if(!(type & (ZEROPAD + LEFT)))
|
if(!(type & (ZEROPAD + LEFT)))
|
||||||
{
|
{
|
||||||
while(--size >= 0)
|
while(--field_width >= 0)
|
||||||
{
|
{
|
||||||
if(buf < end)
|
if(idx < size)
|
||||||
*buf = ' ';
|
buf[idx] = ' ';
|
||||||
++buf;
|
++idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* sign */
|
/* sign */
|
||||||
if(sign)
|
if(sign)
|
||||||
{
|
{
|
||||||
if(buf < end)
|
if(idx < size)
|
||||||
*buf = sign;
|
buf[idx] = sign;
|
||||||
++buf;
|
++idx;
|
||||||
}
|
}
|
||||||
/* "0x" / "0" prefix */
|
/* "0x" / "0" prefix */
|
||||||
if(need_pfx)
|
if(need_pfx)
|
||||||
{
|
{
|
||||||
if(buf < end)
|
if(idx < size)
|
||||||
*buf = '0';
|
buf[idx] = '0';
|
||||||
++buf;
|
++idx;
|
||||||
if(base == 16)
|
if(base == 16)
|
||||||
{
|
{
|
||||||
if(buf < end)
|
if(idx < size)
|
||||||
*buf = digits[16]; /* for arbitrary base: digits[33]; */
|
buf[idx] = digits[16]; /* for arbitrary base: digits[33]; */
|
||||||
++buf;
|
++idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* zero or space padding */
|
/* zero or space padding */
|
||||||
if(!(type & LEFT))
|
if(!(type & LEFT))
|
||||||
{
|
{
|
||||||
char c = (type & ZEROPAD) ? '0' : ' ';
|
char c = (type & ZEROPAD) ? '0' : ' ';
|
||||||
while(--size >= 0)
|
while(--field_width >= 0)
|
||||||
{
|
{
|
||||||
if(buf < end)
|
if(idx < size)
|
||||||
*buf = c;
|
buf[idx] = c;
|
||||||
++buf;
|
++idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* hmm even more zero padding? */
|
/* hmm even more zero padding? */
|
||||||
while(i <= --precision)
|
while(i <= --precision)
|
||||||
{
|
{
|
||||||
if(buf < end)
|
if(idx < size)
|
||||||
*buf = '0';
|
buf[idx] = '0';
|
||||||
++buf;
|
++idx;
|
||||||
}
|
}
|
||||||
/* actual digits of result */
|
/* actual digits of result */
|
||||||
while(--i >= 0)
|
while(--i >= 0)
|
||||||
{
|
{
|
||||||
if(buf < end)
|
if(idx < size)
|
||||||
*buf = tmp[i];
|
buf[idx] = tmp[i];
|
||||||
++buf;
|
++idx;
|
||||||
}
|
}
|
||||||
/* trailing space padding */
|
/* trailing space padding */
|
||||||
while(--size >= 0)
|
while(--field_width >= 0)
|
||||||
{
|
{
|
||||||
if(buf < end)
|
if(idx < size)
|
||||||
*buf = ' ';
|
buf[idx] = ' ';
|
||||||
++buf;
|
++idx;
|
||||||
}
|
}
|
||||||
return buf;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -315,7 +315,8 @@ rb_vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
|
||||||
int len;
|
int len;
|
||||||
unsigned long long int num;
|
unsigned long long int num;
|
||||||
int i, base;
|
int i, base;
|
||||||
char *str, *end, c;
|
char c;
|
||||||
|
size_t idx;
|
||||||
const char *s;
|
const char *s;
|
||||||
|
|
||||||
int flags; /* flags to number() */
|
int flags; /* flags to number() */
|
||||||
|
@ -330,28 +331,20 @@ rb_vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
|
||||||
|
|
||||||
/* Reject out-of-range values early. Large positive sizes are
|
/* Reject out-of-range values early. Large positive sizes are
|
||||||
used for unknown buffer sizes. */
|
used for unknown buffer sizes. */
|
||||||
if(rb_unlikely((int)size < 0))
|
if(rb_unlikely(size > INT_MAX))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
str = buf;
|
idx = 0;
|
||||||
end = buf + size;
|
|
||||||
|
|
||||||
/* Make sure end is always >= buf */
|
|
||||||
if(end < buf)
|
|
||||||
{
|
|
||||||
end = ((void *)-1);
|
|
||||||
size = end - buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(; *fmt; ++fmt)
|
for(; *fmt; ++fmt)
|
||||||
{
|
{
|
||||||
if(*fmt != '%')
|
if(*fmt != '%')
|
||||||
{
|
{
|
||||||
if(str < end)
|
if(idx < size)
|
||||||
*str = *fmt;
|
buf[idx] = *fmt;
|
||||||
++str;
|
++idx;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,20 +428,20 @@ rb_vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
|
||||||
{
|
{
|
||||||
while(--field_width > 0)
|
while(--field_width > 0)
|
||||||
{
|
{
|
||||||
if(str < end)
|
if(idx < size)
|
||||||
*str = ' ';
|
buf[idx] = ' ';
|
||||||
++str;
|
++idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c = (unsigned char)va_arg(args, int);
|
c = (unsigned char)va_arg(args, int);
|
||||||
if(str < end)
|
if(idx < size)
|
||||||
*str = c;
|
buf[idx] = c;
|
||||||
++str;
|
++idx;
|
||||||
while(--field_width > 0)
|
while(--field_width > 0)
|
||||||
{
|
{
|
||||||
if(str < end)
|
if(idx < size)
|
||||||
*str = ' ';
|
buf[idx] = ' ';
|
||||||
++str;
|
++idx;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -464,23 +457,23 @@ rb_vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
|
||||||
{
|
{
|
||||||
while(len < field_width--)
|
while(len < field_width--)
|
||||||
{
|
{
|
||||||
if(str < end)
|
if(idx < size)
|
||||||
*str = ' ';
|
buf[idx] = ' ';
|
||||||
++str;
|
++idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(i = 0; i < len; ++i)
|
for(i = 0; i < len; ++i)
|
||||||
{
|
{
|
||||||
if(str < end)
|
if(idx < size)
|
||||||
*str = *s;
|
buf[idx] = *s;
|
||||||
++str;
|
++idx;
|
||||||
++s;
|
++s;
|
||||||
}
|
}
|
||||||
while(len < field_width--)
|
while(len < field_width--)
|
||||||
{
|
{
|
||||||
if(str < end)
|
if(idx < size)
|
||||||
*str = ' ';
|
buf[idx] = ' ';
|
||||||
++str;
|
++idx;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -490,7 +483,7 @@ rb_vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
|
||||||
field_width = 2 * sizeof(void *);
|
field_width = 2 * sizeof(void *);
|
||||||
flags |= ZEROPAD;
|
flags |= ZEROPAD;
|
||||||
}
|
}
|
||||||
str = number(str, end,
|
idx = number(buf, size, idx,
|
||||||
(unsigned long)va_arg(args, void *),
|
(unsigned long)va_arg(args, void *),
|
||||||
16, field_width, precision, flags);
|
16, field_width, precision, flags);
|
||||||
continue;
|
continue;
|
||||||
|
@ -502,24 +495,24 @@ rb_vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
|
||||||
if(qualifier == 'l')
|
if(qualifier == 'l')
|
||||||
{
|
{
|
||||||
long *ip = va_arg(args, long *);
|
long *ip = va_arg(args, long *);
|
||||||
*ip = (str - buf);
|
*ip = idx;
|
||||||
}
|
}
|
||||||
else if(qualifier == 'Z' || qualifier == 'z')
|
else if(qualifier == 'Z' || qualifier == 'z')
|
||||||
{
|
{
|
||||||
size_t *ip = va_arg(args, size_t *);
|
size_t *ip = va_arg(args, size_t *);
|
||||||
*ip = (str - buf);
|
*ip = idx;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int *ip = va_arg(args, int *);
|
int *ip = va_arg(args, int *);
|
||||||
*ip = (str - buf);
|
*ip = idx;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case '%':
|
case '%':
|
||||||
if(str < end)
|
if(idx < size)
|
||||||
*str = '%';
|
buf[idx] = '%';
|
||||||
++str;
|
++idx;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* integer number formats - set up the flags and "break" */
|
/* integer number formats - set up the flags and "break" */
|
||||||
|
@ -540,14 +533,14 @@ rb_vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if(str < end)
|
if(idx < size)
|
||||||
*str = '%';
|
buf[idx] = '%';
|
||||||
++str;
|
++idx;
|
||||||
if(*fmt)
|
if(*fmt)
|
||||||
{
|
{
|
||||||
if(str < end)
|
if(idx < size)
|
||||||
*str = *fmt;
|
buf[idx] = *fmt;
|
||||||
++str;
|
++idx;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -583,17 +576,17 @@ rb_vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
|
||||||
if(flags & SIGN)
|
if(flags & SIGN)
|
||||||
num = (signed int)num;
|
num = (signed int)num;
|
||||||
}
|
}
|
||||||
str = number(str, end, num, base, field_width, precision, flags);
|
idx = number(buf, size, idx, num, base, field_width, precision, flags);
|
||||||
}
|
}
|
||||||
if(size > 0)
|
if(size > 0)
|
||||||
{
|
{
|
||||||
if(str < end)
|
if(idx < size)
|
||||||
*str = '\0';
|
buf[idx] = '\0';
|
||||||
else
|
else
|
||||||
end[-1] = '\0';
|
buf[size - 1] = '\0';
|
||||||
}
|
}
|
||||||
/* the trailing null byte doesn't count towards the total */
|
/* the trailing null byte doesn't count towards the total */
|
||||||
return str - buf;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue