[rcl] Guard against bad allocation calling rcl_arguments_copy() (#367)

* [rcl] Add test for copying arguments struct with no arguments

* Override allocate function in test to reveal bug

* [rcl] Only allocate arrays if there are things to copy in rcl_argument_copy()

Also guard against freeing invalid pointers if rcl_argument_copy() fails.

* Remove uncessary guard against NULL pointer
This commit is contained in:
Jacob Perron 2019-01-03 16:31:12 -08:00 committed by GitHub
parent 22e61b4107
commit 65bf34b3b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 32 deletions

View file

@ -499,9 +499,13 @@ rcl_arguments_copy(
// Zero so it's safe to call rcl_arguments_fini() if an error occurrs while copying.
args_out->impl->num_remap_rules = 0;
args_out->impl->remap_rules = NULL;
args_out->impl->unparsed_args = NULL;
args_out->impl->num_unparsed_args = 0;
args_out->impl->parameter_files = NULL;
args_out->impl->num_param_files_args = 0;
if (args->impl->num_unparsed_args) {
// Copy unparsed args
args_out->impl->unparsed_args = allocator.allocate(
sizeof(int) * args->impl->num_unparsed_args, allocator.state);
@ -515,7 +519,9 @@ rcl_arguments_copy(
args_out->impl->unparsed_args[i] = args->impl->unparsed_args[i];
}
args_out->impl->num_unparsed_args = args->impl->num_unparsed_args;
}
if (args->impl->num_remap_rules) {
// Copy remap rules
args_out->impl->remap_rules = allocator.allocate(
sizeof(rcl_remap_t) * args->impl->num_remap_rules, allocator.state);
@ -537,6 +543,8 @@ rcl_arguments_copy(
return ret;
}
}
}
// Copy parameter files
if (args->impl->num_param_files_args) {
args_out->impl->parameter_files = allocator.allocate(
@ -558,8 +566,6 @@ rcl_arguments_copy(
return RCL_RET_BAD_ALLOC;
}
}
} else {
args_out->impl->parameter_files = NULL;
}
return RCL_RET_OK;
}

View file

@ -202,6 +202,36 @@ TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_copy) {
EXPECT_EQ(RCL_RET_OK, rcl_arguments_fini(&copied_args));
}
// Similar to the default allocator, but returns NULL when size is zero.
// This is useful for emulating systems where `malloc(0)` return NULL.
// TODO(jacobperron): Consider using this allocate function in other tests
static void *
__return_null_on_zero_allocate(size_t size, void * state)
{
RCUTILS_UNUSED(state);
if (size == 0) {
return NULL;
}
return malloc(size);
}
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_copy_no_args) {
rcl_allocator_t allocator = rcl_get_default_allocator();
allocator.allocate = __return_null_on_zero_allocate;
rcl_arguments_t parsed_args = rcl_get_zero_initialized_arguments();
rcl_ret_t ret = rcl_parse_arguments(0, NULL, allocator, &parsed_args);
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str;
EXPECT_EQ(0, rcl_arguments_get_count_unparsed(&parsed_args));
rcl_arguments_t copied_args = rcl_get_zero_initialized_arguments();
ret = rcl_arguments_copy(&parsed_args, &copied_args);
EXPECT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str;
EXPECT_EQ(0, rcl_arguments_get_count_unparsed(&copied_args));
EXPECT_EQ(RCL_RET_OK, rcl_arguments_fini(&parsed_args));
EXPECT_EQ(RCL_RET_OK, rcl_arguments_fini(&copied_args));
}
TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), test_two_namespace) {
const char * argv[] = {"process_name", "__ns:=/foo/bar", "__ns:=/fiz/buz"};
int argc = sizeof(argv) / sizeof(const char *);