Skip to content

Commit

Permalink
iox-#1431: add unsafe_auto_raw_access and unsafe_ raw_access to iox::…
Browse files Browse the repository at this point in the history
…string

Introduce two methods to directly access the string pointer. This
function enables checking the string's size and capacity after each
call, minimizing the risk of encountering invalid strings.

The first one auto compute the string size, the second one set the size
passed by the user checking if the terminator position is fine.

Signed-off-by: Luca Bartoli <[email protected]>
  • Loading branch information
lucabart97 committed Nov 18, 2023
1 parent 51a018a commit c9a8d8f
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 0 deletions.
1 change: 1 addition & 0 deletions doc/website/release-notes/iceoryx-unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
- Create iceoryx version header for the C-binding [#1014](https://github.com/eclipse-iceoryx/iceoryx/issues/1014)
- Create macros to deprecate header and code constructs [#2057](https://github.com/eclipse-iceoryx/iceoryx/issues/2057)
- Switch to C++17 on all platforms [#2066](https://github.com/eclipse-iceoryx/iceoryx/issues/2066)
- Implement `unsafe_raw_access`, `unsafe_auto_raw_access` in `iox::string` and add `BufferInfo` struct [#1431](https://github.com/eclipse-iceoryx/iceoryx/issues/1431)

**Bugfixes:**

Expand Down
32 changes: 32 additions & 0 deletions iceoryx_hoofs/buffer/include/iox/buffer_info.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) 2021 - 2022 by Apex.AI Inc. All rights reserved.
//
// 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.
//
// SPDX-License-Identifier: Apache-2.0
#ifndef IOX_HOOFS_BUFFER_INFO_HPP
#define IOX_HOOFS_BUFFER_INFO_HPP

#include <cstdint>

namespace iox
{
/// @brief struct used to define a containter for buffer size and capacity
struct BufferInfo
{
uint64_t used_size{0};
uint64_t total_size{0};
};

} // namespace iox

#endif
92 changes: 92 additions & 0 deletions iceoryx_hoofs/test/moduletests/test_vocabulary_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "iceoryx_hoofs/testing/fatal_failure.hpp"
#include "iox/string.hpp"
#include "test.hpp"
#include <cstring>

namespace
{
Expand Down Expand Up @@ -643,6 +644,97 @@ TYPED_TEST(stringTyped_test, UnsafeAssignOfNullptrFails)
EXPECT_THAT(this->testSubject.unsafe_assign(nullptr), Eq(false));
}

/// @note void unsafe_auto_raw_access(const std::function<void(char*, const uint64_t, const uint64_t)>& func) noexcept
TYPED_TEST(stringTyped_test, UnsafeAutoRawAccessOfCStringOfSize0ResultsInSize0)
{
::testing::Test::RecordProperty("TEST_ID", "c1fcb14a-89ce-4c2c-8c9a-abc627adfbc3");
this->testSubject.unsafe_auto_raw_access([this](char* str, const auto info) {
//NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy,-warnings-as-errors)
strcpy(str, "");
EXPECT_THAT(info.used_size, this->testSubject.size());
using MyString = typename TestFixture::stringType;
EXPECT_THAT(info.total_size, MyString::capacity());
});
EXPECT_THAT(this->testSubject.size(), Eq(0U));
EXPECT_THAT(this->testSubject.c_str(), StrEq(""));
}

TYPED_TEST(stringTyped_test, UnsafeAutoRawAccessOfCStringOfSize1ResultsInSize1)
{
::testing::Test::RecordProperty("TEST_ID", "30f42371-52e1-450f-8bac-b3933ef6539b");
this->testSubject.unsafe_auto_raw_access([this](char* str, const auto info) {
//NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy,-warnings-as-errors)
strcpy(str, "M");
EXPECT_THAT(info.used_size, this->testSubject.size());
using MyString = typename TestFixture::stringType;
EXPECT_THAT(info.total_size, MyString::capacity());
});
EXPECT_THAT(this->testSubject.size(), Eq(1U));
EXPECT_THAT(this->testSubject.c_str(), StrEq("M"));
}

TYPED_TEST(stringTyped_test, UnsafeAutoRawAccessCStringOfSizeCapaResultsInSizeCapa)
{
::testing::Test::RecordProperty("TEST_ID", "343e1e43-5500-4255-899c-31f1109340f1");
using MyString = typename TestFixture::stringType;
constexpr auto STRINGCAP = MyString::capacity();
std::vector<char> testCharstring(STRINGCAP, 'M');
testCharstring.emplace_back('\0');
this->testSubject.unsafe_auto_raw_access([&testCharstring](char* str, auto) {
//NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy,-warnings-as-errors)
strcpy(str, testCharstring.data());
});
EXPECT_THAT(this->testSubject.unsafe_assign(testCharstring.data()), Eq(true));
EXPECT_THAT(this->testSubject.size(), Eq(STRINGCAP));
}

/// @note void unsafe_raw_access(const std::function<void(char*, const uint64_t, const uint64_t)>& func) noexcept
TYPED_TEST(stringTyped_test, UnsafeRawAccessOfCStringOfSize0ResultsInSize0)
{
::testing::Test::RecordProperty("TEST_ID", "43e10399-445d-42af-80b1-25071590de0a");
this->testSubject.unsafe_raw_access([this](char* str, const auto info) -> uint64_t {
//NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy,-warnings-as-errors)
strcpy(str, "");
EXPECT_THAT(info.used_size, this->testSubject.size());
using MyString = typename TestFixture::stringType;
EXPECT_THAT(info.total_size, MyString::capacity());
return 0U;
});
EXPECT_THAT(this->testSubject.size(), Eq(0U));
EXPECT_THAT(this->testSubject.c_str(), StrEq(""));
}

TYPED_TEST(stringTyped_test, UnsafeRawAccessOfCStringOfSize1ResultsInSize1)
{
::testing::Test::RecordProperty("TEST_ID", "a3a3395e-2b69-400c-876a-1fdf70cf2d4a");
this->testSubject.unsafe_raw_access([this](char* str, const auto info) -> uint64_t {
//NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy,-warnings-as-errors)
strcpy(str, "M");
EXPECT_THAT(info.used_size, this->testSubject.size());
using MyString = typename TestFixture::stringType;
EXPECT_THAT(info.total_size, MyString::capacity());
return 1U;
});
EXPECT_THAT(this->testSubject.size(), Eq(1U));
EXPECT_THAT(this->testSubject.c_str(), StrEq("M"));
}

TYPED_TEST(stringTyped_test, UnsafeRawAccessCStringOfSizeCapaResultsInSizeCapa)
{
::testing::Test::RecordProperty("TEST_ID", "49faad68-52fa-4024-993c-49b05e7cb971");
using MyString = typename TestFixture::stringType;
constexpr auto STRINGCAP = MyString::capacity();
std::vector<char> testCharstring(STRINGCAP, 'M');
testCharstring.emplace_back('\0');
this->testSubject.unsafe_raw_access([&testCharstring](char* str, auto) -> uint64_t {
//NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.strcpy,-warnings-as-errors)
strcpy(str, testCharstring.data());
return STRINGCAP;
});
EXPECT_THAT(this->testSubject.unsafe_assign(testCharstring.data()), Eq(true));
EXPECT_THAT(this->testSubject.size(), Eq(STRINGCAP));
}

/// @note template <uint64_t N>
/// int64_t compare(const string<N>& other) const noexcept
TYPED_TEST(stringTyped_test, CompareEqStringsResultsInZero)
Expand Down
29 changes: 29 additions & 0 deletions iceoryx_hoofs/vocabulary/include/iox/detail/string.inl
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,35 @@ inline bool string<Capacity>::unsafe_assign(const char* const str) noexcept
return true;
}

template <uint64_t Capacity>
inline void string<Capacity>::unsafe_auto_raw_access(
const iox::function_ref<void(char*, const iox::BufferInfo info)>& func) noexcept
{
iox::BufferInfo info{m_rawstringSize, Capacity};
func(m_rawstring, info);

const uint64_t strSize{strnlen(m_rawstring, Capacity + 1U)};
IOX_EXPECTS_WITH_MSG(Capacity >= strSize,
"unsafe_auto_raw_access failed. Data wrote outside the maximun string capacity of "
<< Capacity);
m_rawstringSize = strSize;
}

template <uint64_t Capacity>
inline void
string<Capacity>::unsafe_raw_access(const iox::function_ref<uint64_t(char*, const iox::BufferInfo info)>& func) noexcept
{
iox::BufferInfo info{m_rawstringSize, Capacity};
uint64_t len = func(m_rawstring, info);

IOX_EXPECTS_WITH_MSG(Capacity >= len,
"unsafe_auto_raw_access failed. Data wrote outside the maximun string capacity of "
<< Capacity);
IOX_EXPECTS_WITH_MSG(m_rawstring[len] == '\0', "String does not have the terminator at the returned size");
m_rawstringSize = len;
}


template <uint64_t Capacity>
template <typename T>
inline IsStringOrCharArray<T, int64_t> string<Capacity>::compare(const T& other) const noexcept
Expand Down
32 changes: 32 additions & 0 deletions iceoryx_hoofs/vocabulary/include/iox/string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
#ifndef IOX_HOOFS_VOCABULARY_STRING_HPP
#define IOX_HOOFS_VOCABULARY_STRING_HPP

#include "iox/buffer_info.hpp"
#include "iox/detail/string_internal.hpp"
#include "iox/detail/string_type_traits.hpp"
#include "iox/function.hpp"
#include "iox/log/logstream.hpp"
#include "iox/optional.hpp"
#include "iox/type_traits.hpp"
Expand Down Expand Up @@ -423,6 +425,36 @@ class string final
template <typename T>
IsStringOrCharArrayOrChar<T, bool> unsafe_append(const T& str) noexcept;

/// @brief direct access to the string's raw pointer. The access resizes the data correctly after the function call.
/// If the data written in the function exceeds the capacity, and the code does not segfault beforehand due to an
/// invalid access, a FATAL error occurs.
///
/// @param [in] function func is a function composed by the raw data pointer and the BufferInfo with current size
/// and capacity
///
/// @code
/// iox::string<100> s;
/// s.unsafe_auto_raw_access([] (auto* str, const auto info) {
/// strncpy(str, "Hello World", info.total_size);
/// });
/// @endcode
void unsafe_auto_raw_access(const iox::function_ref<void(char*, const iox::BufferInfo info)>& func) noexcept;

/// @brief direct access to the string's raw pointer. The access resizes the data with the value returned by the
/// passed function. If the data written has not the terminator at the returned size, a FATAL error occurs.
///
/// @param [in] function func is a function composed by the raw data pointer and the BufferInfo with current size
/// and capacity. The return value is the string lenght.
///
/// @code
/// iox::string<100> s;
/// s.unsafe_raw_access([] (auto* str, const auto info) {
/// strncpy(str, "Hello World", info.total_size);
/// return strlen("Hello World");
/// });
/// @endcode
void unsafe_raw_access(const iox::function_ref<uint64_t(char*, const iox::BufferInfo info)>& func) noexcept;

/// @brief inserts a iox::string or char array in the range [str[0], str[count]) at position pos. The insertion
/// fails if the string capacity would be exceeded or pos is greater than the string size or count is greater than
/// the string to be inserted.
Expand Down

0 comments on commit c9a8d8f

Please sign in to comment.