Skip to content

Make container_cleaner empty-safe#56

Open
kozyilmaz wants to merge 1 commit into
fireblocks:mainfrom
kozyilmaz:container-cleaner-empty-safe
Open

Make container_cleaner empty-safe#56
kozyilmaz wants to merge 1 commit into
fireblocks:mainfrom
kozyilmaz:container-cleaner-empty-safe

Conversation

@kozyilmaz

Copy link
Copy Markdown
Contributor

container_cleaner's destructor wiped its buffer with OPENSSL_cleanse(&_secret[0], _secret.size()). On an empty std::vector, &_secret[0] is out-of-bounds operator[] -- undefined behaviour, and a hardened build (_GLIBCXX_ASSERTIONS) or UBSan aborts on it (the std::string case is benign only by the s[0]==NUL special case).

Guard on empty and take the address via data() instead of &_secret[0]: 'if (!_secret.empty()) OPENSSL_cleanse(_secret.data(), _secret.size());'

No behaviour change when non-empty; a clean no-op when empty.

Tests are kept in a separate branch (kozyilmaz@aa45120) but the fix is here. You can see the details below:

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 26.04 LTS
Release:	26.04
Codename:	resolute

$ uname -a
Linux utgard 7.0.0-22-generic #22-Ubuntu SMP PREEMPT_DYNAMIC Mon May 25 15:37:49 UTC 2026 aarch64 GNU/Linux

The tests are in another branch not to pollute the main tree but you can find the tests here: kozyilmaz@aa45120

# Configure for instrumentation
$ cmake -S . -B build-harden \
  -DCMAKE_CXX_FLAGS="-D_GLIBCXX_ASSERTIONS -fsanitize=undefined -g" \
  -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=undefined"

# Build
$ cmake --build build-harden --target string_utils_test -j"$(nproc)"

# Run the test
$ UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 ./build-harden/test/utils/string_utils_test
/usr/include/c++/15/bits/stl_vector.h:1263: std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::operator[](size_type) [with _Tp = unsigned char; _Alloc = std::allocator<unsigned char>; reference = unsigned char&; size_type = long unsigned int]: Assertion '__n < this->size()' failed.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
string_utils_test is a Catch v2.13.8 host application.
Run with -? for options

-------------------------------------------------------------------------------
container_cleaner is empty-safe
-------------------------------------------------------------------------------
/home/loki/devel/mpc-lib/test/utils/tests.cpp:17
...............................................................................

/home/loki/devel/mpc-lib/test/utils/tests.cpp:17: FAILED:
due to a fatal error condition:
  SIGABRT - Abort (abnormal termination) signal

===============================================================================
test cases: 1 | 1 failed
assertions: 1 | 1 failed

Aborted                    (core dumped) UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 ./build-harden/test/utils/string_utils_test

after the fix the hardened test passes

$ UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 ./build-harden/test/utils/string_utils_test
===============================================================================
All tests passed (3 assertions in 2 test cases)

container_cleaner's destructor wiped its buffer with OPENSSL_cleanse(&_secret[0], _secret.size()). On an empty std::vector, &_secret[0] is out-of-bounds operator[] -- undefined behaviour, and a hardened build (_GLIBCXX_ASSERTIONS) or UBSan aborts on it (the std::string case is benign only by the s[0]==NUL special case).

Guard on empty and take the address via data() instead of &_secret[0]:
'if (!_secret.empty()) OPENSSL_cleanse(_secret.data(), _secret.size());'

No behaviour change when non-empty; a clean no-op when empty.

Signed-off-by: kozyilmaz <kazim@monolytic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant