mirror of
https://github.com/neovim/neovim.git
synced 2025-10-06 01:46:29 +00:00
vim-patch:9.1.0181: no overflow check for string formatting (#27863)
Problem: no overflow check for string formatting
Solution: Check message formatting function for overflow.
(Chris van Willegen)
closes: vim/vim#13799
c35fc03dbd
Co-authored-by: Christ van Willegen <cvwillegen@gmail.com>
This commit is contained in:
@@ -986,6 +986,40 @@ static int adjust_types(const char ***ap_types, int arg, int *num_posarg, const
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void format_overflow_error(const char *pstart)
|
||||
{
|
||||
const char *p = pstart;
|
||||
|
||||
while (ascii_isdigit((int)(*p))) {
|
||||
p++;
|
||||
}
|
||||
|
||||
size_t arglen = (size_t)(p - pstart);
|
||||
char *argcopy = xstrnsave(pstart, arglen);
|
||||
semsg(_(e_val_too_large), argcopy);
|
||||
xfree(argcopy);
|
||||
}
|
||||
|
||||
enum { MAX_ALLOWED_STRING_WIDTH = 6400, };
|
||||
|
||||
static int get_unsigned_int(const char *pstart, const char **p, unsigned *uj)
|
||||
{
|
||||
*uj = (unsigned)(**p - '0');
|
||||
(*p)++;
|
||||
|
||||
while (ascii_isdigit((int)(**p)) && *uj < MAX_ALLOWED_STRING_WIDTH) {
|
||||
*uj = 10 * *uj + (unsigned)(**p - '0');
|
||||
(*p)++;
|
||||
}
|
||||
|
||||
if (*uj > MAX_ALLOWED_STRING_WIDTH) {
|
||||
format_overflow_error(pstart);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char *fmt, typval_T *tvs)
|
||||
FUNC_ATTR_NONNULL_ARG(1, 2)
|
||||
{
|
||||
@@ -1019,6 +1053,7 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char *
|
||||
|
||||
// variable for positional arg
|
||||
int pos_arg = -1;
|
||||
const char *pstart = p + 1;
|
||||
|
||||
p++; // skip '%'
|
||||
|
||||
@@ -1038,11 +1073,12 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char *
|
||||
}
|
||||
|
||||
// Positional argument
|
||||
unsigned uj = (unsigned)(*p++ - '0');
|
||||
unsigned uj;
|
||||
|
||||
while (ascii_isdigit((int)(*p))) {
|
||||
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||
if (get_unsigned_int(pstart, &p, &uj) == FAIL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
pos_arg = (int)uj;
|
||||
|
||||
any_pos = 1;
|
||||
@@ -1080,10 +1116,10 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char *
|
||||
|
||||
if (ascii_isdigit((int)(*p))) {
|
||||
// Positional argument field width
|
||||
unsigned uj = (unsigned)(*p++ - '0');
|
||||
unsigned uj;
|
||||
|
||||
while (ascii_isdigit((int)(*p))) {
|
||||
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||
if (get_unsigned_int(arg + 1, &p, &uj) == FAIL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (*p != '$') {
|
||||
@@ -1105,10 +1141,11 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char *
|
||||
} else if (ascii_isdigit((int)(*p))) {
|
||||
// size_t could be wider than unsigned int; make sure we treat
|
||||
// argument like common implementations do
|
||||
unsigned uj = (unsigned)(*p++ - '0');
|
||||
const char *digstart = p;
|
||||
unsigned uj;
|
||||
|
||||
while (ascii_isdigit((int)(*p))) {
|
||||
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||
if (get_unsigned_int(digstart, &p, &uj) == FAIL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (*p == '$') {
|
||||
@@ -1126,10 +1163,10 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char *
|
||||
|
||||
if (ascii_isdigit((int)(*p))) {
|
||||
// Parse precision
|
||||
unsigned uj = (unsigned)(*p++ - '0');
|
||||
unsigned uj;
|
||||
|
||||
while (ascii_isdigit((int)(*p))) {
|
||||
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||
if (get_unsigned_int(arg + 1, &p, &uj) == FAIL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (*p == '$') {
|
||||
@@ -1152,10 +1189,11 @@ static int parse_fmt_types(const char ***ap_types, int *num_posarg, const char *
|
||||
} else if (ascii_isdigit((int)(*p))) {
|
||||
// size_t could be wider than unsigned int; make sure we
|
||||
// treat argument like common implementations do
|
||||
unsigned uj = (unsigned)(*p++ - '0');
|
||||
const char *digstart = p;
|
||||
unsigned uj;
|
||||
|
||||
while (ascii_isdigit((int)(*p))) {
|
||||
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||
if (get_unsigned_int(digstart, &p, &uj) == FAIL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (*p == '$') {
|
||||
@@ -1447,11 +1485,13 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
|
||||
if (*ptype == '$') {
|
||||
// Positional argument
|
||||
unsigned uj = (unsigned)(*p++ - '0');
|
||||
const char *digstart = p;
|
||||
unsigned uj;
|
||||
|
||||
while (ascii_isdigit((int)(*p))) {
|
||||
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||
if (get_unsigned_int(digstart, &p, &uj) == FAIL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
pos_arg = (int)uj;
|
||||
|
||||
p++;
|
||||
@@ -1482,15 +1522,18 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
|
||||
// parse field width
|
||||
if (*p == '*') {
|
||||
const char *digstart = p + 1;
|
||||
|
||||
p++;
|
||||
|
||||
if (ascii_isdigit((int)(*p))) {
|
||||
// Positional argument field width
|
||||
unsigned uj = (unsigned)(*p++ - '0');
|
||||
unsigned uj;
|
||||
|
||||
while (ascii_isdigit((int)(*p))) {
|
||||
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||
if (get_unsigned_int(digstart, &p, &uj) == FAIL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
arg_idx = (int)uj;
|
||||
|
||||
p++;
|
||||
@@ -1502,6 +1545,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, int)));
|
||||
|
||||
if (j > MAX_ALLOWED_STRING_WIDTH) {
|
||||
format_overflow_error(digstart);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (j >= 0) {
|
||||
min_field_width = (size_t)j;
|
||||
} else {
|
||||
@@ -1511,11 +1559,18 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
} else if (ascii_isdigit((int)(*p))) {
|
||||
// size_t could be wider than unsigned int; make sure we treat
|
||||
// argument like common implementations do
|
||||
unsigned uj = (unsigned)(*p++ - '0');
|
||||
const char *digstart = p;
|
||||
unsigned uj;
|
||||
|
||||
while (ascii_isdigit((int)(*p))) {
|
||||
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||
if (get_unsigned_int(digstart, &p, &uj) == FAIL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (uj > MAX_ALLOWED_STRING_WIDTH) {
|
||||
format_overflow_error(digstart);
|
||||
goto error;
|
||||
}
|
||||
|
||||
min_field_width = uj;
|
||||
}
|
||||
|
||||
@@ -1527,22 +1582,32 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
if (ascii_isdigit((int)(*p))) {
|
||||
// size_t could be wider than unsigned int; make sure we
|
||||
// treat argument like common implementations do
|
||||
unsigned uj = (unsigned)(*p++ - '0');
|
||||
const char *digstart = p;
|
||||
unsigned uj;
|
||||
|
||||
while (ascii_isdigit((int)(*p))) {
|
||||
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||
if (get_unsigned_int(digstart, &p, &uj) == FAIL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (uj > MAX_ALLOWED_STRING_WIDTH) {
|
||||
format_overflow_error(digstart);
|
||||
goto error;
|
||||
}
|
||||
|
||||
precision = uj;
|
||||
} else if (*p == '*') {
|
||||
const char *digstart = p;
|
||||
|
||||
p++;
|
||||
|
||||
if (ascii_isdigit((int)(*p))) {
|
||||
// positional argument
|
||||
unsigned uj = (unsigned)(*p++ - '0');
|
||||
unsigned uj;
|
||||
|
||||
while (ascii_isdigit((int)(*p))) {
|
||||
uj = 10 * uj + (unsigned)(*p++ - '0');
|
||||
if (get_unsigned_int(digstart, &p, &uj) == FAIL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
arg_idx = (int)uj;
|
||||
|
||||
p++;
|
||||
@@ -1554,6 +1619,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
&arg_cur, fmt),
|
||||
va_arg(ap, int)));
|
||||
|
||||
if (j > MAX_ALLOWED_STRING_WIDTH) {
|
||||
format_overflow_error(digstart);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (j >= 0) {
|
||||
precision = (size_t)j;
|
||||
} else {
|
||||
@@ -2160,6 +2230,7 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap_st
|
||||
emsg(_("E767: Too many arguments to printf()"));
|
||||
}
|
||||
|
||||
error:
|
||||
xfree(ap_types);
|
||||
va_end(ap);
|
||||
|
||||
|
Reference in New Issue
Block a user