API: Avoid overrun when formatting error-message

msgpack_rpc_to_object (called by handle_request .. msgpack_rpc_to_array)
always NUL-terminates API Strings.
But handle_request .. msgpack_rpc_get_handler_for operates on a raw
msgpack_object, before preparation.
This commit is contained in:
Justin M. Keyes 2018-08-29 10:06:35 +02:00
parent 9fe8e3cb2f
commit db17d2c0fa
7 changed files with 30 additions and 40 deletions

View File

@ -40,7 +40,8 @@ MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name,
map_get(String, MsgpackRpcRequestHandler)(methods, m);
if (!rv.fn) {
api_set_error(error, kErrorTypeException, "Invalid method: %s",
api_set_error(error, kErrorTypeException, "Invalid method: %.*s",
m.size > 0 ? m.size : sizeof("<empty>"),
m.size > 0 ? m.data : "<empty>");
}
return rv;

View File

@ -162,7 +162,7 @@ Object dict_get_value(dict_T *dict, String key, Error *err)
dictitem_T *const di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size);
if (di == NULL) {
api_set_error(err, kErrorTypeValidation, "Key '%s' not found", key.data);
api_set_error(err, kErrorTypeValidation, "Key not found: %s", key.data);
return (Object)OBJECT_INIT;
}
@ -191,13 +191,12 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del,
}
if (key.size == 0) {
api_set_error(err, kErrorTypeValidation,
"Empty variable names aren't allowed");
api_set_error(err, kErrorTypeValidation, "Key name is empty");
return rv;
}
if (key.size > INT_MAX) {
api_set_error(err, kErrorTypeValidation, "Key length is too high");
api_set_error(err, kErrorTypeValidation, "Key name is too long");
return rv;
}
@ -220,7 +219,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del,
// Delete the key
if (di == NULL) {
// Doesn't exist, fail
api_set_error(err, kErrorTypeValidation, "Key does not exist: %s",
api_set_error(err, kErrorTypeValidation, "Key not found: %s",
key.data);
} else {
// Return the old value
@ -284,9 +283,7 @@ Object get_option_from(void *from, int type, String name, Error *err)
type, from);
if (!flags) {
api_set_error(err,
kErrorTypeValidation,
"Invalid option name \"%s\"",
api_set_error(err, kErrorTypeValidation, "Invalid option name: '%s'",
name.data);
return rv;
}
@ -303,15 +300,14 @@ Object get_option_from(void *from, int type, String name, Error *err)
rv.data.string.data = stringval;
rv.data.string.size = strlen(stringval);
} else {
api_set_error(err,
kErrorTypeException,
"Unable to get value for option \"%s\"",
api_set_error(err, kErrorTypeException,
"Failed to get value for option '%s'",
name.data);
}
} else {
api_set_error(err,
kErrorTypeException,
"Unknown type for option \"%s\"",
"Unknown type for option '%s'",
name.data);
}
@ -336,24 +332,20 @@ void set_option_to(uint64_t channel_id, void *to, int type,
int flags = get_option_value_strict(name.data, NULL, NULL, type, to);
if (flags == 0) {
api_set_error(err,
kErrorTypeValidation,
"Invalid option name \"%s\"",
api_set_error(err, kErrorTypeValidation, "Invalid option name '%s'",
name.data);
return;
}
if (value.type == kObjectTypeNil) {
if (type == SREQ_GLOBAL) {
api_set_error(err,
kErrorTypeException,
"Unable to unset option \"%s\"",
api_set_error(err, kErrorTypeException, "Cannot unset option '%s'",
name.data);
return;
} else if (!(flags & SOPT_GLOBAL)) {
api_set_error(err,
kErrorTypeException,
"Cannot unset option \"%s\" "
"Cannot unset option '%s' "
"because it doesn't have a global value",
name.data);
return;
@ -370,7 +362,7 @@ void set_option_to(uint64_t channel_id, void *to, int type,
if (value.type != kObjectTypeBoolean) {
api_set_error(err,
kErrorTypeValidation,
"Option \"%s\" requires a boolean value",
"Option '%s' requires a Boolean value",
name.data);
return;
}
@ -378,17 +370,15 @@ void set_option_to(uint64_t channel_id, void *to, int type,
numval = value.data.boolean;
} else if (flags & SOPT_NUM) {
if (value.type != kObjectTypeInteger) {
api_set_error(err,
kErrorTypeValidation,
"Option \"%s\" requires an integer value",
api_set_error(err, kErrorTypeValidation,
"Option '%s' requires an integer value",
name.data);
return;
}
if (value.data.integer > INT_MAX || value.data.integer < INT_MIN) {
api_set_error(err,
kErrorTypeValidation,
"Value for option \"%s\" is outside range",
api_set_error(err, kErrorTypeValidation,
"Value for option '%s' is out of range",
name.data);
return;
}
@ -396,9 +386,8 @@ void set_option_to(uint64_t channel_id, void *to, int type,
numval = (int)value.data.integer;
} else {
if (value.type != kObjectTypeString) {
api_set_error(err,
kErrorTypeValidation,
"Option \"%s\" requires a string value",
api_set_error(err, kErrorTypeValidation,
"Option '%s' requires a string value",
name.data);
return;
}

View File

@ -502,7 +502,7 @@ Integer nvim_strwidth(String text, Error *err)
FUNC_API_SINCE(1)
{
if (text.size > INT_MAX) {
api_set_error(err, kErrorTypeValidation, "String length is too high");
api_set_error(err, kErrorTypeValidation, "String is too long");
return 0;
}
@ -559,7 +559,7 @@ void nvim_set_current_dir(String dir, Error *err)
FUNC_API_SINCE(1)
{
if (dir.size >= MAXPATHL) {
api_set_error(err, kErrorTypeValidation, "Directory string is too long");
api_set_error(err, kErrorTypeValidation, "Directory name is too long");
return;
}
@ -1136,14 +1136,14 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
if (calls.items[i].type != kObjectTypeArray) {
api_set_error(err,
kErrorTypeValidation,
"All items in calls array must be arrays");
"Items in calls array must be arrays");
goto validation_error;
}
Array call = calls.items[i].data.array;
if (call.size != 2) {
api_set_error(err,
kErrorTypeValidation,
"All items in calls array must be arrays of size 2");
"Items in calls array must be arrays of size 2");
goto validation_error;
}

View File

@ -309,7 +309,7 @@ describe('api/buf', function()
eq(1, funcs.exists('b:lua'))
curbufmeths.del_var('lua')
eq(0, funcs.exists('b:lua'))
eq({false, 'Key does not exist: lua'}, meth_pcall(curbufmeths.del_var, 'lua'))
eq({false, 'Key not found: lua'}, meth_pcall(curbufmeths.del_var, 'lua'))
curbufmeths.set_var('lua', 1)
command('lockvar b:lua')
eq({false, 'Key is locked: lua'}, meth_pcall(curbufmeths.del_var, 'lua'))

View File

@ -34,7 +34,7 @@ describe('api/tabpage', function()
eq(1, funcs.exists('t:lua'))
curtabmeths.del_var('lua')
eq(0, funcs.exists('t:lua'))
eq({false, 'Key does not exist: lua'}, meth_pcall(curtabmeths.del_var, 'lua'))
eq({false, 'Key not found: lua'}, meth_pcall(curtabmeths.del_var, 'lua'))
curtabmeths.set_var('lua', 1)
command('lockvar t:lua')
eq({false, 'Key is locked: lua'}, meth_pcall(curtabmeths.del_var, 'lua'))

View File

@ -337,7 +337,7 @@ describe('API', function()
eq(1, funcs.exists('g:lua'))
meths.del_var('lua')
eq(0, funcs.exists('g:lua'))
eq({false, 'Key does not exist: lua'}, meth_pcall(meths.del_var, 'lua'))
eq({false, "Key not found: lua"}, meth_pcall(meths.del_var, 'lua'))
meths.set_var('lua', 1)
command('lockvar lua')
eq({false, 'Key is locked: lua'}, meth_pcall(meths.del_var, 'lua'))
@ -948,7 +948,7 @@ describe('API', function()
}
local status, err = pcall(meths.call_atomic, req)
eq(false, status)
ok(err:match(' All items in calls array must be arrays of size 2') ~= nil)
ok(err:match('Items in calls array must be arrays of size 2') ~= nil)
-- call before was done, but not after
eq(1, meths.get_var('avar'))
@ -958,7 +958,7 @@ describe('API', function()
}
status, err = pcall(meths.call_atomic, req)
eq(false, status)
ok(err:match('All items in calls array must be arrays') ~= nil)
ok(err:match('Items in calls array must be arrays') ~= nil)
eq({2,3}, meths.get_var('bvar'))
req = {

View File

@ -169,7 +169,7 @@ describe('api/win', function()
eq(1, funcs.exists('w:lua'))
curwinmeths.del_var('lua')
eq(0, funcs.exists('w:lua'))
eq({false, 'Key does not exist: lua'}, meth_pcall(curwinmeths.del_var, 'lua'))
eq({false, 'Key not found: lua'}, meth_pcall(curwinmeths.del_var, 'lua'))
curwinmeths.set_var('lua', 1)
command('lockvar w:lua')
eq({false, 'Key is locked: lua'}, meth_pcall(curwinmeths.del_var, 'lua'))