Skip to content

Commit

Permalink
Fix atomics 64bits (#9)
Browse files Browse the repository at this point in the history
* Release micro-ROS Foxy (#8)

* micro-ROS changes over dashing

Feature/add security directory (#1)

* Added security directory

* Updated security directory

Feature/avoid filesystem and allocation (#2)

* Included RCUTILS_NO_FILESYSTEM and RCUTILS_AVOID_DYNAMIC_ALLOCATION

* Added no filesystem options

* Default allocators write access

* Avoid dynamic allocation and no filesytem on error handling

* Typo

* New flags for filesystem and avoid dynamic

* Error handling template

* New allocator approach

Add test_security_directory test from rcl. (#3)

Merge pull request #4 from micro-ROS/feature/zephyr_fixes

Feature/zephyr fixes

CMake refactor (#5)


Update approach (#6)

* Update approach

* Remove target_compile_definitions and refactor flags install

* Added RCUTILS_NO_FILESYSTEM on new functions

* Added RCUTILS_NO_FILESYSTEM on new functions

Co-authored-by: Pablo Garrido <pablogs9@gmail.com>

* Initial changes

* Add hashing and lock pool

* Updates

Co-authored-by: Jose Antonio Moral <joseantoniomoralparras@gmail.com>
  • Loading branch information
pablogs9 and jamoralp authored Sep 17, 2020
1 parent 96c0312 commit 4bee4ac
Show file tree
Hide file tree
Showing 5 changed files with 592 additions and 0 deletions.
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ project(rcutils)
option(RCUTILS_NO_THREAD_SUPPORT "Disable thread support." OFF)
option(RCUTILS_NO_FILESYSTEM "Disable filesystem usage." OFF)
option(RCUTILS_AVOID_DYNAMIC_ALLOCATION "Disable dynamic allocations." OFF)
option(RCUTILS_NO_64_ATOMIC "Disable support for 64 bits atomic operations." OFF)

# Default to C11
if(NOT CMAKE_C_STANDARD)
Expand Down Expand Up @@ -73,6 +74,8 @@ set(rcutils_sources
src/time.c
${time_impl_c}
src/uint8_array.c
src/security_directory.c
$<$<BOOL:RCUTILS_NO_64_ATOMIC>:src/atomic_64bits.c>
)

set_source_files_properties(
Expand Down Expand Up @@ -152,6 +155,9 @@ if(BUILD_TESTING)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
endif()

set(test_resources_dir_name "test/resources")
add_definitions(-DTEST_RESOURCES_DIRECTORY="${CMAKE_CURRENT_BINARY_DIR}/${test_resources_dir_name}")

find_package(ament_cmake_gmock REQUIRED)
find_package(ament_cmake_gtest REQUIRED)
find_package(ament_cmake_pytest REQUIRED)
Expand Down
92 changes: 92 additions & 0 deletions src/atomic_64bits.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdint.h>
#include <stdbool.h>

#define FLAGS_LEN 23

static bool * get_memory_lock(void *address)
{
static bool memory_locks[FLAGS_LEN] = { 0 };
uintptr_t a = (uintptr_t)(address);

// Public domain hash function taken from http://burtleburtle.net/bob/hash/integer.html
a = (a ^ 61) ^ (a >> 16);
a = a + (a << 3);
a = a ^ (a >> 4);
a = a * 0x27d4eb2d;
a = a ^ (a >> 15);

a = a % FLAGS_LEN;
return memory_locks + a;
}

void lock_memory(uint64_t *address){
bool * memory_lock = get_memory_lock(address);

while (__atomic_test_and_set(memory_lock, __ATOMIC_ACQUIRE) == 1);
}

void unlock_memory(uint64_t *address){
bool * memory_lock = get_memory_lock(address);

__atomic_clear(memory_lock, __ATOMIC_RELEASE);
}

uint64_t __atomic_load_8(uint64_t *mem, int model) {
(void) model;

lock_memory(mem);
uint64_t ret = *mem;
unlock_memory(mem);
return ret;
}

void __atomic_store_8(uint64_t *mem, uint64_t val, int model) {
(void) model;

lock_memory(mem);
*mem = val;
unlock_memory(mem);
}

uint64_t __atomic_exchange_8(uint64_t *mem, uint64_t val, int model) {
(void) model;

lock_memory(mem);
uint64_t ret = *mem;
*mem = val;
unlock_memory(mem);
return ret;
}

uint64_t __atomic_fetch_add_8(uint64_t *mem, uint64_t val, int model) {
(void) model;

lock_memory(mem);
uint64_t ret = *mem;
*mem += val;
unlock_memory(mem);
return ret;
}

#ifdef __cplusplus
}
#endif
278 changes: 278 additions & 0 deletions src/security_directory.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
// Copyright 2018 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "rcutils/security_directory.h"

#include "rcutils/error_handling.h"
#include "rcutils/filesystem.h"
#include "rcutils/get_env.h"
#include "rcutils/format_string.h"

#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wembedded-directive"
#endif

#ifndef RCUTILS_NO_FILESYSTEM
#include "tinydir/tinydir.h"
#endif

#ifdef __clang__
# pragma clang diagnostic pop
#endif

/**
* A security lookup function takes in the node's name, namespace, a security root directory and an allocator;
* It returns the relevant information required to load the security credentials,
* which is currently a path to a directory on the filesystem containing DDS Security permission files.
*/
typedef char * (* security_lookup_fn_t) (
const char * node_name,
const char * node_namespace,
const char * ros_secure_root_env,
const rcutils_allocator_t * allocator
);

char * exact_match_lookup(
const char * node_name,
const char * node_namespace,
const char * ros_secure_root_env,
const rcutils_allocator_t * allocator
);

char * prefix_match_lookup(
const char * node_name,
const char * node_namespace,
const char * ros_secure_root_env,
const rcutils_allocator_t * allocator
);

security_lookup_fn_t g_security_lookup_fns[] = {
NULL,
exact_match_lookup,
prefix_match_lookup,
};

typedef enum ros_security_lookup_type_e
{
ROS_SECURITY_LOOKUP_NODE_OVERRIDE = 0,
ROS_SECURITY_LOOKUP_MATCH_EXACT = 1,
ROS_SECURITY_LOOKUP_MATCH_PREFIX = 2,
} ros_security_lookup_type_t;

char * g_security_lookup_type_strings[] = {
"NODE_OVERRIDE",
"MATCH_EXACT",
"MATCH_PREFIX"
};

/// Return the directory whose name most closely matches node_name (longest-prefix match),
/// scanning under base_dir.
/**
* By using a prefix match, a node named e.g. "my_node_123" will be able to load and use the
* directory "my_node" if no better match exists.
* \param[in] base_dir
* \param[in] node_name
* \param[out] matched_name must be a valid memory address allocated with at least
* _TINYDIR_FILENAME_MAX characters.
* \return true if a match was found
*/
static bool get_best_matching_directory(
const char * base_dir,
const char * node_name,
char * matched_name)
{
#ifdef RCUTILS_NO_FILESYSTEM
RCUTILS_SET_ERROR_MSG("not available filesystem");
return false;
#else
size_t max_match_length = 0;
tinydir_dir dir;
if (NULL == base_dir || NULL == node_name || NULL == matched_name) {
return false;
}
if (-1 == tinydir_open(&dir, base_dir)) {
return false;
}
while (dir.has_next) {
tinydir_file file;
if (-1 == tinydir_readfile(&dir, &file)) {
goto cleanup;
}
if (file.is_dir) {
size_t matched_name_length = strnlen(file.name, sizeof(file.name) - 1);
if (0 ==
strncmp(file.name, node_name,
matched_name_length) && matched_name_length > max_match_length)
{
max_match_length = matched_name_length;
memcpy(matched_name, file.name, max_match_length);
}
}
if (-1 == tinydir_next(&dir)) {
goto cleanup;
}
}
cleanup:
tinydir_close(&dir);
return max_match_length > 0;
#endif // _RCUTILS_NO_FILESYSTEM
}

char * exact_match_lookup(
const char * node_name,
const char * node_namespace,
const char * ros_secure_root_env,
const rcutils_allocator_t * allocator)
{
// Perform an exact match for the node's name in directory <root dir>/<namespace>.
char * node_secure_root = NULL;
// "/" case when root namespace is explicitly passed in
if (1 == strlen(node_namespace)) {
node_secure_root = rcutils_join_path(ros_secure_root_env, node_name, *allocator);
} else {
char * node_fqn = NULL;
char * node_root_path = NULL;
// Combine node namespace with node name
// TODO(ros2team): remove the hard-coded value of the root namespace
node_fqn = rcutils_format_string(*allocator, "%s%s%s", node_namespace, "/", node_name);
// Get native path, ignore the leading forward slash
// TODO(ros2team): remove the hard-coded length, use the length of the root namespace instead
node_root_path = rcutils_to_native_path(node_fqn + 1, *allocator);
node_secure_root = rcutils_join_path(ros_secure_root_env, node_root_path, *allocator);
allocator->deallocate(node_fqn, allocator->state);
allocator->deallocate(node_root_path, allocator->state);
}
return node_secure_root;
}

char * prefix_match_lookup(
const char * node_name,
const char * node_namespace,
const char * ros_secure_root_env,
const rcutils_allocator_t * allocator)
{
#ifdef RCUTILS_NO_FILESYSTEM
RCUTILS_SET_ERROR_MSG("not available filesystem");
return false;
#else
// Perform longest prefix match for the node's name in directory <root dir>/<namespace>.
char * node_secure_root = NULL;
char matched_dir[_TINYDIR_FILENAME_MAX] = {0};
char * base_lookup_dir = NULL;
if (strlen(node_namespace) == 1) {
base_lookup_dir = (char *) ros_secure_root_env;
} else {
// TODO(ros2team): remove the hard-coded length, use the length of the root namespace instead.
base_lookup_dir = rcutils_join_path(ros_secure_root_env, node_namespace + 1, *allocator);
}
if (get_best_matching_directory(base_lookup_dir, node_name, matched_dir)) {
node_secure_root = rcutils_join_path(base_lookup_dir, matched_dir, *allocator);
}
if (base_lookup_dir != ros_secure_root_env && NULL != base_lookup_dir) {
allocator->deallocate(base_lookup_dir, allocator->state);
}
return node_secure_root;
#endif // _RCUTILS_NO_FILESYSTEM
}

char * rcutils_get_secure_root(
const char * node_name,
const char * node_namespace,
const rcutils_allocator_t * allocator)
{
bool ros_secure_node_override = true;

// find out if either of the configuration environment variables are set
const char * env_buf = NULL;
if (NULL == node_name) {
return NULL;
}
if (rcutils_get_env(ROS_SECURITY_NODE_DIRECTORY_VAR_NAME, &env_buf)) {
return NULL;
}
if (!env_buf) {
return NULL;
}
size_t ros_secure_root_size = strlen(env_buf);
if (!ros_secure_root_size) {
// check root directory if node directory environment variable is empty
if (rcutils_get_env(ROS_SECURITY_ROOT_DIRECTORY_VAR_NAME, &env_buf)) {
return NULL;
}
if (!env_buf) {
return NULL;
}
ros_secure_root_size = strlen(env_buf);
if (!ros_secure_root_size) {
return NULL; // environment variable was empty
} else {
ros_secure_node_override = false;
}
}

// found a usable environment variable, copy into our memory before overwriting with next lookup
char * ros_secure_root_env =
(char *)allocator->allocate(ros_secure_root_size + 1, allocator->state);
memcpy(ros_secure_root_env, env_buf, ros_secure_root_size + 1);
// TODO(ros2team): This make an assumption on the value and length of the root namespace.
// This should likely come from another (rcl/rmw?) function for reuse.
// If the namespace is the root namespace ("/"), the secure root is just the node name.

char * lookup_strategy = NULL;
char * node_secure_root = NULL;
if (ros_secure_node_override) {
node_secure_root = (char *)allocator->allocate(ros_secure_root_size + 1, allocator->state);
memcpy(node_secure_root, ros_secure_root_env, ros_secure_root_size + 1);
lookup_strategy = g_security_lookup_type_strings[ROS_SECURITY_LOOKUP_NODE_OVERRIDE];

} else {
// Check which lookup method to use and invoke the relevant function.
const char * ros_security_lookup_type = NULL;
if (rcutils_get_env(ROS_SECURITY_LOOKUP_TYPE_VAR_NAME, &ros_security_lookup_type)) {
allocator->deallocate(ros_secure_root_env, allocator->state);
return NULL;
}
if (0 == strcmp(ros_security_lookup_type,
g_security_lookup_type_strings[ROS_SECURITY_LOOKUP_MATCH_PREFIX]))
{
node_secure_root = g_security_lookup_fns[ROS_SECURITY_LOOKUP_MATCH_PREFIX]
(node_name, node_namespace, ros_secure_root_env, allocator);
lookup_strategy = g_security_lookup_type_strings[ROS_SECURITY_LOOKUP_MATCH_PREFIX];
} else { /* Default is MATCH_EXACT */
node_secure_root = g_security_lookup_fns[ROS_SECURITY_LOOKUP_MATCH_EXACT]
(node_name, node_namespace, ros_secure_root_env, allocator);
lookup_strategy = g_security_lookup_type_strings[ROS_SECURITY_LOOKUP_MATCH_EXACT];
}
}

if (NULL == node_secure_root || !rcutils_is_directory(node_secure_root)) {
// Check node_secure_root is not NULL before checking directory
if (NULL == node_secure_root) {
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING(
"SECURITY ERROR: unable to find a folder matching the node name in %s%s."
"Lookup strategy: %s",
ros_secure_root_env, node_namespace, lookup_strategy);
} else {
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING(
"SECURITY ERROR: directory %s does not exist. Lookup strategy: %s",
node_secure_root, lookup_strategy);
}
allocator->deallocate(ros_secure_root_env, allocator->state);
allocator->deallocate(node_secure_root, allocator->state);
return NULL;
}
allocator->deallocate(ros_secure_root_env, allocator->state);
return node_secure_root;
}
Empty file.
Loading

0 comments on commit 4bee4ac

Please sign in to comment.