From 5ab7f2b5a631f0e949b265244f82dcfa756d6728 Mon Sep 17 00:00:00 2001 From: "Jay L. Gao" Date: Thu, 19 Feb 2026 17:28:42 -0800 Subject: [PATCH 1/6] Add block_numbers ownership tracking to fix ION memory corruption This commit fixes a critical memory corruption issue when BSL is integrated with ION-DTN. The problem occurred because BSL was calling free() on memory that ION allocated using BSL_CALLOC(), causing munmap_chunk() errors. Changes: - Add bool block_numbers_owned field to BSL_PrimaryBlock_t structure This follows the same ownership pattern as BSL_Data_t.owned - Modify BSL_PrimaryBlock_deinit() to check ownership before freeing Only calls BSL_FREE() on block_numbers if BSL owns the memory - Update BSL mock BPA to set ownership flag after allocation Ensures BSL-allocated memory is properly tracked - Fix policy configuration file (policy_provider_test.json): * Change location from "clin" to "appin" for source role policies * Fix JSON keys: "src_eid"/"dst_eid" -> "src"/"dest" * Add EID patterns: "ipn:2.*" and "ipn:3.*" for test scenarios This fix resolves the crash in ION's bpsec-all-multinode-test.bsl test where bundles with security blocks would fail with memory corruption errors. Related ION changes (in ion-ios-dev repository): - bpv7/bsl/bsl.c sets block_numbers_owned = true after BSL_CALLOC calls Tested-by: Running bpsec-all-multinode-test.bsl without crashes --- mock-bpa-test/policy_provider_test.json | 14 +++++++++----- src/BPSecLib_Public.h | 6 ++++++ src/backend/PublicInterfaceImpl.c | 6 +++++- src/mock_bpa/agent.c | 1 + 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/mock-bpa-test/policy_provider_test.json b/mock-bpa-test/policy_provider_test.json index 5b9fb280..6d452cb7 100644 --- a/mock-bpa-test/policy_provider_test.json +++ b/mock-bpa-test/policy_provider_test.json @@ -2,12 +2,14 @@ "policyrule_set": [ { "policyrule": { - "desc": "Integrity source rule", + "desc": "Integrity source rule for outgoing bundles", "filter": { "rule_id": "1", "role": "s", + "src": "ipn:2.*", + "dest": "ipn:3.*", "tgt": 1, - "loc": "clin", + "loc": "appin", "sc_id": 1 }, "spec": { @@ -47,12 +49,14 @@ }, { "policyrule": { - "desc": "Confidentiality source rule", + "desc": "Confidentiality source rule for outgoing bundles", "filter": { "rule_id": "2", "role": "s", + "src": "ipn:2.*", + "dest": "ipn:3.*", "tgt": 1, - "loc": "clin", + "loc": "appin", "sc_id": 2 }, "spec": { @@ -90,4 +94,4 @@ ] } }] -} \ No newline at end of file +} diff --git a/src/BPSecLib_Public.h b/src/BPSecLib_Public.h index 831b94a2..376c6b96 100644 --- a/src/BPSecLib_Public.h +++ b/src/BPSecLib_Public.h @@ -235,6 +235,12 @@ typedef struct BSL_PrimaryBlock_s * the same order in which they appear in the bundle. */ uint64_t *block_numbers; + /** Flag indicating whether BSL owns the block_numbers array and should free it. + * Set to true if BSL allocated the array (via BSL_CALLOC/BSL_MALLOC). + * Set to false if the host BPA owns the memory (e.g., ION's MTAKE or shared memory). + * Matches the ownership pattern used by BSL_Data_t.owned. + */ + bool block_numbers_owned; /** The encoded form of the primary block as contiguous data. */ diff --git a/src/backend/PublicInterfaceImpl.c b/src/backend/PublicInterfaceImpl.c index e7601078..8eba76c3 100644 --- a/src/backend/PublicInterfaceImpl.c +++ b/src/backend/PublicInterfaceImpl.c @@ -91,7 +91,11 @@ void BSL_PrimaryBlock_deinit(BSL_PrimaryBlock_t *obj) { ASSERT_ARG_NONNULL(obj); - BSL_FREE(obj->block_numbers); + // Only free block_numbers if BSL owns the memory + if (obj->block_numbers_owned && obj->block_numbers) + { + BSL_FREE(obj->block_numbers); + } obj->block_numbers = NULL; BSL_Data_Deinit(&obj->encoded); diff --git a/src/mock_bpa/agent.c b/src/mock_bpa/agent.c index 32c8f141..0f1d9f8b 100644 --- a/src/mock_bpa/agent.c +++ b/src/mock_bpa/agent.c @@ -80,6 +80,7 @@ int MockBPA_GetBundleMetadata(const BSL_BundleRef_t *bundle_ref, BSL_PrimaryBloc { return -2; } + result_primary_block->block_numbers_owned = true; // BSL owns this memory size_t ix = 0; MockBPA_BlockList_it_t bit; for (MockBPA_BlockList_it(bit, bundle->blocks); !MockBPA_BlockList_end_p(bit); MockBPA_BlockList_next(bit)) From 6e509b4e4a563d5c0377ba326b2be944ab953151 Mon Sep 17 00:00:00 2001 From: "Jay L. Gao" Date: Fri, 20 Feb 2026 11:40:42 -0800 Subject: [PATCH 2/6] Add ION memory allocator integration to BSL - Add ionpatch.h/c for ION memory allocator wrappers - Add ION_INTEGRATION build flag and CMake support - Remove block_numbers_owned field (simplified to unconditional free) - Add macro guards for CHKVOID/CHKNULL to avoid conflicts with ION - Add build-for-ion.sh convenience script --- CMakeLists.txt | 45 ++++++++++++++++ build-for-ion.sh | 28 ++++++++++ src/BPSecLib_Private.h | 4 ++ src/BPSecLib_Public.h | 6 --- src/BSLConfig.h.in | 10 ++++ src/CMakeLists.txt | 15 ++++++ src/ION_integration/ionpatch.c | 90 +++++++++++++++++++++++++++++++ src/ION_integration/ionpatch.h | 37 +++++++++++++ src/backend/PublicInterfaceImpl.c | 4 +- src/mock_bpa/agent.c | 1 - 10 files changed, 231 insertions(+), 9 deletions(-) create mode 100755 build-for-ion.sh create mode 100644 src/ION_integration/ionpatch.c create mode 100644 src/ION_integration/ionpatch.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b4baafb5..7a4c5efc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ option(BUILD_UNITTEST "Enable building unit tests" ON) option(TEST_MEMCHECK "Enable test runtime memory checking" ON) option(BUILD_COVERAGE "Enable runtime coverage logging and reporting" OFF) option(BUILD_FUZZING "Enable building fuzzing executables" OFF) +option(ION_INTEGRATION "Build BSL with ION memory allocators" OFF) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") @@ -67,10 +68,54 @@ project(bsl VERSION ${PROJECT_VERSION} ) +# ION Integration - build with ION memory allocators +if(ION_INTEGRATION) + message(STATUS "Building BSL with ION memory allocators") + + # Define ION_INTEGRATION for C preprocessor + add_definitions(-DION_INTEGRATION) + + # Find ION installation + find_path(ION_INCLUDE_DIR ion.h + HINTS ${ION_ROOT}/include ${CMAKE_SOURCE_DIR}/../../ici/include /usr/local/include + REQUIRED) + find_library(ION_ICI_LIBRARY ici + HINTS ${ION_ROOT}/lib ${CMAKE_SOURCE_DIR}/../../ici/x86_64-linux/lib /usr/local/lib + REQUIRED) + + message(STATUS "ION headers: ${ION_INCLUDE_DIR}") + message(STATUS "ION libici: ${ION_ICI_LIBRARY}") + + # Add ionpatch.c as a static library + add_library(bsl_ionpatch STATIC + src/ION_integration/ionpatch.c) + target_include_directories(bsl_ionpatch PRIVATE ${ION_INCLUDE_DIR}) + target_link_libraries(bsl_ionpatch PRIVATE ${ION_ICI_LIBRARY}) + + # All BSL libraries must link against ionpatch + set(BSL_ION_DEPS bsl_ionpatch ${ION_ICI_LIBRARY}) +endif() + # Language options set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) +# Add platform flag BEFORE POSIX flags (required by ION's platform.h) +if(ION_INTEGRATION) + # Add _DEFAULT_SOURCE to make MAXPATHLEN and other system defs visible + add_definitions(-D_DEFAULT_SOURCE) + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + add_definitions(-Dlinux) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + add_definitions(-Ddarwin) + elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + add_definitions(-Dfreebsd) + elseif(CMAKE_SYSTEM_NAME STREQUAL "SunOS") + add_definitions(-Dsolaris) + elseif(WIN32) + add_definitions(-Dmingw) + endif() +endif() # Force specific POSIX compatibility version add_definitions( -D_XOPEN_SOURCE diff --git a/build-for-ion.sh b/build-for-ion.sh new file mode 100755 index 00000000..c51b743f --- /dev/null +++ b/build-for-ion.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Build BSL with ION memory allocators +set -e + +# Detect ION installation +ION_ROOT="${ION_ROOT:-$(cd ../../ && pwd)}" +echo "Using ION_ROOT: $ION_ROOT" + +# Clean previous build +./build.sh clean + +# Rebuild all dependencies +./build.sh deps + +# Prepare with ION integration enabled +./build.sh prep \ + -DION_INTEGRATION=ON \ + -DION_ROOT="$ION_ROOT" \ + -DBUILD_TESTING=OFF + +# Build +./build.sh + +# Install to testroot +./build.sh install + +echo "BSL built successfully with ION memory allocators" +echo "Libraries installed to: $(pwd)/testroot/usr/lib" diff --git a/src/BPSecLib_Private.h b/src/BPSecLib_Private.h index 4dc4ee5a..6960028a 100644 --- a/src/BPSecLib_Private.h +++ b/src/BPSecLib_Private.h @@ -135,9 +135,13 @@ typedef enum return val; \ } /// Return from void functions if condition fails. +#ifndef CHKVOID #define CHKVOID(cond) CHKRET(cond, ) +#endif /// Return a null pointer if condition fails. +#ifndef CHKNULL #define CHKNULL(cond) CHKRET(cond, NULL) +#endif /// Return false if condition fails. #define CHKFALSE(cond) CHKRET(cond, false) /// Return the error value 1 if condition fails. diff --git a/src/BPSecLib_Public.h b/src/BPSecLib_Public.h index 376c6b96..831b94a2 100644 --- a/src/BPSecLib_Public.h +++ b/src/BPSecLib_Public.h @@ -235,12 +235,6 @@ typedef struct BSL_PrimaryBlock_s * the same order in which they appear in the bundle. */ uint64_t *block_numbers; - /** Flag indicating whether BSL owns the block_numbers array and should free it. - * Set to true if BSL allocated the array (via BSL_CALLOC/BSL_MALLOC). - * Set to false if the host BPA owns the memory (e.g., ION's MTAKE or shared memory). - * Matches the ownership pattern used by BSL_Data_t.owned. - */ - bool block_numbers_owned; /** The encoded form of the primary block as contiguous data. */ diff --git a/src/BSLConfig.h.in b/src/BSLConfig.h.in index 0bdb33c1..0dd0c379 100644 --- a/src/BSLConfig.h.in +++ b/src/BSLConfig.h.in @@ -32,6 +32,16 @@ extern "C" { #endif +/* --------- Start of modifications for ION integration -----------*/ +#ifdef ION_INTEGRATION + /* Include ionpatch.h to redefine BSL memory allocators. + * This must come BEFORE the #ifndef guards for BSL_MALLOC, etc. + * Path is relative to the include directories set by CMake. + */ + #include "ION_integration/ionpatch.h" +#endif /* ION_INTEGRATION */ +/* ---------- End of modifications for ION integration ------------*/ + /** Part of POSIX.1-2008 */ #cmakedefine HAVE_CLOCK_GETTIME diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 800a9833..7653ce6a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -52,6 +52,9 @@ target_include_directories( $ $/include/bsl> ) +if(ION_INTEGRATION) + target_link_libraries(bsl_front PUBLIC ${BSL_ION_DEPS}) +endif() # Crypto Library set(BSL_CRYPTO_C @@ -71,6 +74,9 @@ target_link_libraries(bsl_crypto INTERFACE OpenSSL::Crypto) if(TARGET valgrind::valgrind) target_compile_definitions(bsl_crypto PRIVATE HAVE_VALGRIND) endif(TARGET valgrind::valgrind) +if(ION_INTEGRATION) + target_link_libraries(bsl_crypto PUBLIC ${BSL_ION_DEPS}) +endif() # Default Security Context library set(BSL_DEFAULT_SC_H @@ -95,6 +101,9 @@ set_target_properties(bsl_default_sc target_link_libraries(bsl_default_sc PUBLIC bsl_front) target_link_libraries(bsl_default_sc PUBLIC bsl_crypto) target_link_libraries(bsl_default_sc PUBLIC QCBOR::qcbor) +if(ION_INTEGRATION) + target_link_libraries(bsl_default_sc PUBLIC ${BSL_ION_DEPS}) +endif() # Example Policy Provider library set(BSL_SAMPLE_PP_H @@ -115,6 +124,9 @@ set_target_properties(bsl_sample_pp ) target_link_libraries(bsl_sample_pp PUBLIC bsl_front) target_link_libraries(bsl_sample_pp PUBLIC MLIB::mlib) +if(ION_INTEGRATION) + target_link_libraries(bsl_sample_pp PUBLIC ${BSL_ION_DEPS}) +endif() # Dynamic backend library set(BSL_DYNAMIC_H @@ -151,6 +163,9 @@ set_target_properties(bsl_dynamic target_link_libraries(bsl_dynamic PUBLIC bsl_front) target_link_libraries(bsl_dynamic PUBLIC MLIB::mlib) target_link_libraries(bsl_dynamic PUBLIC QCBOR::qcbor) +if(ION_INTEGRATION) + target_link_libraries(bsl_dynamic PUBLIC ${BSL_ION_DEPS}) +endif() add_subdirectory(mock_bpa) diff --git a/src/ION_integration/ionpatch.c b/src/ION_integration/ionpatch.c new file mode 100644 index 00000000..e56a5760 --- /dev/null +++ b/src/ION_integration/ionpatch.c @@ -0,0 +1,90 @@ +/* + * ionpatch.c: functions enabling the use of ION's + * private memory management system in + * the BPSec Library. + * + * Author: Scott Burleigh + * + * Copyright (c) 2025, California Institute of Technology. + * ALL RIGHTS RESERVED. U.S. Government Sponsorship acknowledged. + */ + +#include +#include "ion.h" +#include "ionpatch.h" + +extern void *ion_malloc(const char *file, int line, size_t size) +{ +#if 0 + writeMemo("[i] ION BSL_MALLOC called."); +#endif + return allocFromIonMemory(file, line, size); +} + +extern void *ion_calloc(const char *file, int line, size_t ct, size_t size) +{ +#if 0 + writeMemo("[i] ION BSL_CALLOC called."); +#endif + return allocFromIonMemory(file, line, ct * size); +} + +extern void *ion_realloc(const char *file, int line, void *mem, size_t size) +{ + void *newMem; +#if 0 + writeMemo("[?] ION's dubious function for BSL_REALLOC invoked."); + printStackTrace(); +#endif + /* Note that this function is of no use to the + * BSL_BundleCtx_ReallocBTSD function, since + ION stores extension blocks in the SDR heap + rather than in system memory. + + Note also that the ION implementation of this + function does not work in the way that standard + realloc() works. The space immediately adjacent + to a block of ION working memory will typically + NOT be unoccupied, i.e, will NOT be available + to be silently appended to that block. + + So the content of the original block is instead + copied to a newly allocated block of the requested + size; the address of that new block is returned. */ + + if (size == 0) + { + if (mem) + { + releaseToIonMemory(file, line, mem); + } + + newMem = NULL; + } + else + { + newMem = allocFromIonMemory(file, line, size); + if (newMem == NULL) + { + newMem = mem; /* Retain orginal data. */ + } + else /* New memory block was obtained. */ + { + if (mem) + { + memcpy(newMem, mem, size); + releaseToIonMemory(file, line, mem); + } + } + } + + return newMem; +} + +extern void ion_free(const char *file, int line, void *mem) +{ +#if 0 + writeMemo("[i] ION BSL_FREE called."); +#endif + releaseToIonMemory(file, line, mem); +} diff --git a/src/ION_integration/ionpatch.h b/src/ION_integration/ionpatch.h new file mode 100644 index 00000000..d8c3c35a --- /dev/null +++ b/src/ION_integration/ionpatch.h @@ -0,0 +1,37 @@ +/* + ionpatch.h: private definitions supporting the use + of ION's private memory management system + in the BPSec Library. + + Author: Scott Burleigh + + Copyright (c) 2025, California Institute of Technology. + ALL RIGHTS RESERVED. U.S. Government Sponsorship acknowledged. + */ +#ifndef ION_INTEGRATION_IONPATCH_H_ +#define ION_INTEGRATION_IONPATCH_H_ + +#include + +/* Forward declarations of ION memory functions. + * We don't include ion.h here to avoid header dependency issues. + * The actual implementation in ionpatch.c will include ion.h. */ + +extern void *ion_malloc(const char *, int, size_t); +extern void *ion_calloc(const char *, int, size_t, size_t); +extern void *ion_realloc(const char *, int, void *, size_t); +extern void ion_free(const char *, int, void *); + +/* Undefine BSL's default memory allocators (from BSLConfig.h) so we + * can redefine them to use ION's memory management system instead. */ +#undef BSL_MALLOC +#undef BSL_CALLOC +#undef BSL_REALLOC +#undef BSL_FREE + +#define BSL_MALLOC(size) ion_malloc(__FILE__, __LINE__, size) +#define BSL_CALLOC(count, size) ion_calloc(__FILE__, __LINE__, count, size) +#define BSL_REALLOC(mem, size) ion_realloc(__FILE__, __LINE__, mem, size) +#define BSL_FREE(mem) ion_free(__FILE__, __LINE__, mem) + +#endif /* ION_INTEGRATION_IONPATCH_H_ */ diff --git a/src/backend/PublicInterfaceImpl.c b/src/backend/PublicInterfaceImpl.c index 8eba76c3..c9123a8d 100644 --- a/src/backend/PublicInterfaceImpl.c +++ b/src/backend/PublicInterfaceImpl.c @@ -91,8 +91,8 @@ void BSL_PrimaryBlock_deinit(BSL_PrimaryBlock_t *obj) { ASSERT_ARG_NONNULL(obj); - // Only free block_numbers if BSL owns the memory - if (obj->block_numbers_owned && obj->block_numbers) + // Unconditionally free - BSL_FREE will use correct allocator + if (obj->block_numbers) { BSL_FREE(obj->block_numbers); } diff --git a/src/mock_bpa/agent.c b/src/mock_bpa/agent.c index 0f1d9f8b..32c8f141 100644 --- a/src/mock_bpa/agent.c +++ b/src/mock_bpa/agent.c @@ -80,7 +80,6 @@ int MockBPA_GetBundleMetadata(const BSL_BundleRef_t *bundle_ref, BSL_PrimaryBloc { return -2; } - result_primary_block->block_numbers_owned = true; // BSL owns this memory size_t ix = 0; MockBPA_BlockList_it_t bit; for (MockBPA_BlockList_it(bit, bundle->blocks); !MockBPA_BlockList_end_p(bit); MockBPA_BlockList_next(bit)) From f3d3b5f03d275afc1b50f65f3449b8de468f338e Mon Sep 17 00:00:00 2001 From: "Jay L. Gao" Date: Wed, 25 Feb 2026 15:58:03 -0800 Subject: [PATCH 3/6] Fix BSL_ExecBIBSource to set security block number in security operation When BSL creates a new BIB security block at the source node, it must store the created block number in the security operation structure so that the security context execution function can retrieve the block metadata. BSL_ExecBCBSource already had this assignment, but BSL_ExecBIBSource was missing it. --- src/backend/SecurityContext.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/backend/SecurityContext.c b/src/backend/SecurityContext.c index 5887443c..1a746621 100644 --- a/src/backend/SecurityContext.c +++ b/src/backend/SecurityContext.c @@ -101,6 +101,10 @@ static int BSL_ExecBIBSource(BSL_SecCtx_Execute_f sec_context_fn, BSL_LibCtx_t * CHK_PROPERTY(created_block_num > 1); + // Store the created block number in the security operation so the + // security context can find it + sec_oper->sec_block_num = created_block_num; + const int bib_result = (*sec_context_fn)(lib, bundle, sec_oper, outcome); if (bib_result != 0) // || outcome->is_success == false) { From f0f39e6011638a2a18b02d81c0216fe8fff68ca3 Mon Sep 17 00:00:00 2001 From: "Jay L. Gao" Date: Thu, 26 Feb 2026 11:06:01 -0800 Subject: [PATCH 4/6] Fix BSL_BundleCtx_WriteBTSD to call realloc before write (Bug #2) Bug #2: BSL_BundleCtx_WriteBTSD must call realloc callback before write Problem: - BSL_BundleCtx_WriteBTSD was calling the write callback directly without first ensuring the BTSD buffer was large enough - When ION creates extension blocks, they start with length=1 (placeholder) - BSL then attempted to write 82 bytes into the 1-byte buffer - This caused the ION realloc callback to be called during write, but write had already started with insufficient buffer space Root Cause: - BSL_BundleCtx_WriteBTSD (lines 131-137) immediately called: return HostDescriptorTable.block_write_btsd_fn(bundle, block_num, btsd_len); - No buffer size check or realloc call before writing - The write callback would fail when attempting to write beyond allocated space Fix: - Added realloc call before write in BSL_BundleCtx_WriteBTSD (lines 138-148) - Check if realloc callback is registered and btsd_len > 0 - Call block_realloc_btsd_fn to expand buffer to needed size - Return NULL if realloc fails (with error logging) - Only proceed to write if realloc succeeds Code: /* Ensure the BTSD buffer is large enough before writing */ if (btsd_len > 0 && HostDescriptorTable.block_realloc_btsd_fn) { int realloc_result = HostDescriptorTable.block_realloc_btsd_fn(bundle, block_num, btsd_len); if (realloc_result != 0) { BSL_LOG_ERR("Failed to realloc BTSD buffer: block=%llu size=%zu result=%d", (unsigned long long)block_num, btsd_len, realloc_result); return NULL; } } Impact: - BSL now properly expands BTSD buffers before writing - BCB encryption can now write full encrypted payloads - BIB can write full HMAC signatures - This is part of a series of fixes enabling BSL BCB encryption in ION Related ION integration fixes (separate ION commits): - Bug #3: Fix SDR violation in ion_bsl_ReallocBTSD - Bug #4: Add payload block special handling in ion_bsl_ReallocBTSD Test: ION tests/bpsec/bpsec-all-multinode-test.bsl Status: BSL operations now succeed with this fix WIP: bpsec/bpsec-all-multinode-test.bsl not yet passing. --- src/backend/HostInterface.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/backend/HostInterface.c b/src/backend/HostInterface.c index cf8ff3e9..04d7b4d4 100644 --- a/src/backend/HostInterface.c +++ b/src/backend/HostInterface.c @@ -134,6 +134,19 @@ BSL_SeqWriter_t *BSL_BundleCtx_WriteBTSD(BSL_BundleRef_t *bundle, uint64_t block { return NULL; } + + /* Ensure the BTSD buffer is large enough before writing */ + if (btsd_len > 0 && HostDescriptorTable.block_realloc_btsd_fn) + { + int realloc_result = HostDescriptorTable.block_realloc_btsd_fn(bundle, block_num, btsd_len); + if (realloc_result != 0) + { + BSL_LOG_ERR("Failed to realloc BTSD buffer: block=%llu size=%zu result=%d", + (unsigned long long)block_num, btsd_len, realloc_result); + return NULL; + } + } + return HostDescriptorTable.block_write_btsd_fn(bundle, block_num, btsd_len); } From 0da0aafd5add0e103e39bb66a5089ce8b312bcbf Mon Sep 17 00:00:00 2001 From: iondev33 Date: Sat, 7 Mar 2026 16:59:18 -0800 Subject: [PATCH 5/6] Add libtool .libs path to ION library search hints When ION is built with autotools/libtool, the compiled libraries are placed in the .libs/ subdirectory of the build tree before installation. Add ${ION_ROOT}/.libs to the find_library hints so that in-tree builds can locate libici without requiring a prior make install. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a4c5efc..4c8b448a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,7 +80,7 @@ if(ION_INTEGRATION) HINTS ${ION_ROOT}/include ${CMAKE_SOURCE_DIR}/../../ici/include /usr/local/include REQUIRED) find_library(ION_ICI_LIBRARY ici - HINTS ${ION_ROOT}/lib ${CMAKE_SOURCE_DIR}/../../ici/x86_64-linux/lib /usr/local/lib + HINTS ${ION_ROOT}/lib ${ION_ROOT}/.libs ${CMAKE_SOURCE_DIR}/../../ici/x86_64-linux/lib /usr/local/lib REQUIRED) message(STATUS "ION headers: ${ION_INCLUDE_DIR}") From 644a34fce12a7ac26af7ae0615f8949baa1d3488 Mon Sep 17 00:00:00 2001 From: iondev33 Date: Mon, 9 Mar 2026 23:33:08 -0700 Subject: [PATCH 6/6] Add non-destructive BCB verifier to prevent in-place decryption at CLIN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BSL's BCB dispatch routed both verifier and acceptor roles to BSL_ExecBCBAcceptor(), which calls the AES-GCM decrypt function and overwrites the ciphertext payload in-place. This is correct for acceptors but destructive for verifiers — downstream acceptors then fail because the payload is already plaintext while the BCB block still references the original ciphertext. Add BSL_ExecBCBVerifier() that validates the ASB is decodable, copies parameters to the security operation, and confirms a result exists for the target block — without calling the security context execute function. Update the BCB dispatch to route verifier role to this new function. Also remove a stale comment in PublicInterfaceImpl.c. --- src/backend/PublicInterfaceImpl.c | 2 +- src/backend/SecurityContext.c | 85 +++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/backend/PublicInterfaceImpl.c b/src/backend/PublicInterfaceImpl.c index c9123a8d..28381594 100644 --- a/src/backend/PublicInterfaceImpl.c +++ b/src/backend/PublicInterfaceImpl.c @@ -157,6 +157,7 @@ int BSL_API_QuerySecurity(const BSL_LibCtx_t *bsl, BSL_SecurityActionSet_t *outp BSL_LOG_WARNING("Failed to get block number %" PRIu64, primary_block.block_numbers[ix]); continue; } + BSL_SecActionList_it_t act_it; for (BSL_SecActionList_it(act_it, output_action_set->actions); !BSL_SecActionList_end_p(act_it); BSL_SecActionList_next(act_it)) @@ -170,7 +171,6 @@ int BSL_API_QuerySecurity(const BSL_LibCtx_t *bsl, BSL_SecurityActionSet_t *outp continue; } - // ASB decoder needs the whole BTSD now BSL_Data_t btsd_copy; BSL_Data_InitBuffer(&btsd_copy, block.btsd_len); diff --git a/src/backend/SecurityContext.c b/src/backend/SecurityContext.c index 1a746621..ab6ba5d1 100644 --- a/src/backend/SecurityContext.c +++ b/src/backend/SecurityContext.c @@ -273,6 +273,87 @@ static int BSL_ExecBIBAccept(BSL_SecCtx_Execute_f sec_context_fn, BSL_LibCtx_t * return auth_success ? BSL_SUCCESS : BSL_ERR_SECURITY_OPERATION_FAILED; } +/** Lightweight BCB verifier: validates ASB is decodable and parameters are + * present, but does NOT decrypt the payload. A verifier must not modify + * the bundle so that downstream acceptors can still process it. + */ +static int BSL_ExecBCBVerifier(BSL_LibCtx_t *lib, BSL_BundleRef_t *bundle, + BSL_SecOper_t *sec_oper) +{ + CHK_ARG_NONNULL(lib); + CHK_ARG_NONNULL(bundle); + CHK_PRECONDITION(BSL_SecOper_IsConsistent(sec_oper)); + + BSL_CanonicalBlock_t sec_blk = { 0 }; + if (BSL_BundleCtx_GetBlockMetadata(bundle, sec_oper->sec_block_num, &sec_blk) != BSL_SUCCESS) + { + BSL_LOG_ERR("BCB Verifier: could not get block metadata"); + BSL_TlmCounters_IncrementCounter(lib, BSL_TLM_SECOP_FAIL_COUNT, 1); + return BSL_ERR_HOST_CALLBACK_FAILED; + } + + /* Decode the ASB to validate it is well-formed. */ + BSL_Data_t btsd_copy; + BSL_Data_InitBuffer(&btsd_copy, sec_blk.btsd_len); + + BSL_SeqReader_t *btsd_read = BSL_BundleCtx_ReadBTSD(bundle, sec_blk.block_num); + BSL_SeqReader_Get(btsd_read, btsd_copy.ptr, &btsd_copy.len); + BSL_SeqReader_Destroy(btsd_read); + + BSL_AbsSecBlock_t abs_sec_block; + BSL_AbsSecBlock_InitEmpty(&abs_sec_block); + if (BSL_AbsSecBlock_DecodeFromCBOR(&abs_sec_block, &btsd_copy) != BSL_SUCCESS) + { + BSL_LOG_ERR("BCB Verifier: failed to parse ASB CBOR"); + BSL_AbsSecBlock_Deinit(&abs_sec_block); + BSL_Data_Deinit(&btsd_copy); + BSL_TlmCounters_IncrementCounter(lib, BSL_TLM_SECOP_FAIL_COUNT, 1); + return BSL_ERR_DECODING; + } + BSL_TlmCounters_IncrementCounter(lib, BSL_TLM_ASB_DECODE_BYTES, sec_blk.btsd_len); + BSL_TlmCounters_IncrementCounter(lib, BSL_TLM_ASB_DECODE_COUNT, 1); + BSL_Data_Deinit(&btsd_copy); + + CHK_PROPERTY(BSL_AbsSecBlock_IsConsistent(&abs_sec_block)); + + /* Copy ASB params to the security operation for policy inspection. */ + for (size_t i = 0; i < BSLB_SecParamList_size(abs_sec_block.params); i++) + { + const BSL_SecParam_t *param = BSLB_SecParamList_cget(abs_sec_block.params, i); + CHK_PROPERTY(BSL_SecParam_IsConsistent(param)); + BSLB_SecParamList_push_back(sec_oper->_param_list, *param); + } + + /* Intentionally do NOT call the security context execute function. + * Decryption would modify the payload in-place, which a verifier + * must never do. We only confirm the ASB is decodable and contains + * the expected results for our target block. */ + bool found_result = false; + for (size_t i = 0; i < BSLB_SecResultList_size(abs_sec_block.results); i++) + { + BSL_SecResult_t *result = BSLB_SecResultList_get(abs_sec_block.results, i); + if (result->target_block_num == sec_oper->target_block_num) + { + found_result = true; + break; + } + } + + BSL_AbsSecBlock_Deinit(&abs_sec_block); + + if (!found_result) + { + BSL_LOG_ERR("BCB Verifier: no result found for target block %" PRIu64, + sec_oper->target_block_num); + BSL_TlmCounters_IncrementCounter(lib, BSL_TLM_SECOP_FAIL_COUNT, 1); + return BSL_ERR_SECURITY_OPERATION_FAILED; + } + + BSL_LOG_INFO("BCB Verify SUCCESS"); + BSL_TlmCounters_IncrementCounter(lib, BSL_TLM_SECOP_VERIFIER_COUNT, 1); + return BSL_SUCCESS; +} + static int BSL_ExecBCBAcceptor(BSL_SecCtx_Execute_f sec_context_fn, BSL_LibCtx_t *lib, BSL_BundleRef_t *bundle, BSL_SecOper_t *sec_oper, BSL_SecOutcome_t *outcome) { @@ -515,6 +596,10 @@ int BSL_SecCtx_ExecutePolicyActionSet(BSL_LibCtx_t *lib, BSL_SecurityResponseSet { errcode = BSL_ExecBCBSource(sec_ctx->execute, lib, bundle, sec_oper, outcome); } + else if (BSL_SecOper_IsRoleVerifier(sec_oper)) + { + errcode = BSL_ExecBCBVerifier(lib, bundle, sec_oper); + } else { errcode = BSL_ExecBCBAcceptor(sec_ctx->execute, lib, bundle, sec_oper, outcome);