Reducing buffer_ext module with most of the API now in the SDK
What changed, and why it matters
This commit removes a local set of helper functions for reading and writing memory buffers and switches the Ledger Bitcoin app to use equivalent functions now provided by the Ledger SDK. It is a code cleanup and modernization change, not a fix for a known security bug. The only functional code change is removing unnecessary type casts when accessing buffer memory, which does not introduce a vulnerability.
Treat as a routine refactoring commit. Verify that the target Ledger SDK version provides the removed APIs with compatible behavior, and run the unit tests and integration tests to ensure no regressions in buffer parsing paths.
Security signals we found
Large deletion of local buffer helper code with migration to SDK equivalents
Removal of const-discarding casts in buffer access patterns
No change to bounds-checking logic or cryptographic operations
Evidence from the diff
The change deletes most of the custom buffer_ext module (buffer_peek, buffer_read/write helpers, buffer_create, buffer_snapshot/restore, etc.) because the Ledger SDK’s buffer.h now supplies them. Call sites across the app stop including buffer_ext.h and rely on the SDK. A few pointer casts like (uint8_t *) (buffer->ptr + offset) are removed because the SDK’s buffer_t.ptr is already uint8_t *. The remaining buffer_ext code is only buffer_alloc and buffer_is_cur_aligned. No bounds checks, error handling, or security semantics are altered.
Changed components
src/common/buffer_ext.csrc/common/buffer_ext.hsrc/common/parser_ext.csrc/handler/get_wallet_address.csrc/handler/lib/check_merkle_tree_sorted.csrc/handler/lib/get_merkle_leaf_hash.csrc/handler/lib/get_merkle_preimage.csrc/handler/lib/get_merkleized_map.csrc/handler/lib/get_preimage.csrc/handler/lib/policy.csrc/handler/lib/psbt_parse_rawtx.csrc/handler/lib/stream_preimage.csrc/handler/register_wallet.csrc/handler/sign_message.csrc/handler/sign_psbt.csrc/handler/sign_psbt/extract_bip32_derivation.csrc/handler/sign_psbt/txhashes.cunit-tests/test_buffer.cInspect captured patch +10 / −335
diff --git a/src/boilerplate/dispatcher.c b/src/boilerplate/dispatcher.c
index 405e863..da9ff6c 100644
--- a/src/boilerplate/dispatcher.c
+++ b/src/boilerplate/dispatcher.c
@@ -24,7 +24,6 @@
#include "buffer.h"
/* Local headers */
-#include "buffer_ext.h"
#include "constants.h"
#include "globals.h"
#include "io_ext.h"
diff --git a/src/common/buffer_ext.c b/src/common/buffer_ext.c
index 6344474..65ee2f1 100644
--- a/src/common/buffer_ext.c
+++ b/src/common/buffer_ext.c
@@ -13,12 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*****************************************************************************/
-
-/*
- * NOTE: The SDK's buffer_t type uses `const uint8_t *ptr` for both read and write.
- * Write functions cast away const; callers must ensure underlying memory is mutable.
- */
-
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@@ -33,98 +27,6 @@
#include "varint.h"
#include "write.h"
-bool buffer_peek(const buffer_t *buffer, uint8_t *value) {
- return buffer_peek_n(buffer, 0, value);
-}
-
-bool buffer_peek_n(const buffer_t *buffer, size_t n, uint8_t *value) {
- if (!buffer_can_read(buffer, n + 1)) {
- return false;
- }
-
- *value = buffer->ptr[buffer->offset + n];
-
- return true;
-}
-
-bool buffer_read_bytes(buffer_t *buffer, uint8_t *out, size_t n) {
- if (buffer->size - buffer->offset < n) {
- return false;
- }
-
- memmove(out, buffer->ptr + buffer->offset, n);
- buffer_seek_cur(buffer, n);
-
- return true;
-}
-
-bool buffer_write_u8(buffer_t *buffer, uint8_t value) {
- if (!buffer_can_read(buffer, 1)) {
- return false;
- }
-
- ((uint8_t *) buffer->ptr)[buffer->offset] = value;
- buffer_seek_cur(buffer, 1);
-
- return true;
-}
-
-bool buffer_write_u16(buffer_t *buffer, uint16_t value, endianness_t endianness) {
- if (!buffer_can_read(buffer, 2)) {
- return false;
- }
-
- if (endianness == BE) {
- write_u16_be((uint8_t *) buffer->ptr, buffer->offset, value);
- } else {
- write_u16_le((uint8_t *) buffer->ptr, buffer->offset, value);
- }
- buffer_seek_cur(buffer, 2);
-
- return true;
-}
-
-bool buffer_write_u32(buffer_t *buffer, uint32_t value, endianness_t endianness) {
- if (!buffer_can_read(buffer, 4)) {
- return false;
- }
-
- if (endianness == BE) {
- write_u32_be((uint8_t *) buffer->ptr, buffer->offset, value);
- } else {
- write_u32_le((uint8_t *) buffer->ptr, buffer->offset, value);
- }
- buffer_seek_cur(buffer, 4);
-
- return true;
-}
-
-bool buffer_write_u64(buffer_t *buffer, uint64_t value, endianness_t endianness) {
- if (!buffer_can_read(buffer, 8)) {
- return false;
- }
-
- if (endianness == BE) {
- write_u64_be((uint8_t *) buffer->ptr, buffer->offset, value);
- } else {
- write_u64_le((uint8_t *) buffer->ptr, buffer->offset, value);
- }
-
- buffer_seek_cur(buffer, 8);
-
- return true;
-}
-
-bool buffer_write_bytes(buffer_t *buffer, const uint8_t *data, size_t n) {
- if (!buffer_can_read(buffer, n)) {
- return false;
- }
-
- memmove((uint8_t *) (buffer->ptr + buffer->offset), data, n);
- buffer_seek_cur(buffer, n);
- return true;
-}
-
void *buffer_alloc(buffer_t *buffer, size_t size, bool aligned) {
size_t padding_size = 0;
diff --git a/src/common/buffer_ext.h b/src/common/buffer_ext.h
index 19ccf66..27d7a61 100644
--- a/src/common/buffer_ext.h
+++ b/src/common/buffer_ext.h
@@ -7,168 +7,6 @@
/* SDK headers */
#include "buffer.h"
-typedef size_t buffer_snapshot_t;
-
-/**
- * Returns the pointer to byte in the current position of the buffer.
- *
- * @param[in] buffer
- * Pointer to input buffer struct.
- *
- * @return the pointer to the current position.
- *
- */
-static inline uint8_t *buffer_get_cur(const buffer_t *buffer) {
- return (uint8_t *) (buffer->ptr + buffer->offset);
-}
-
-/**
- * Read 1 byte from buffer into uint8_t.
- *
- * @param[in,out] buffer
- * Pointer to input buffer struct.
- * @param[out] value
- * Pointer to 8-bit unsigned integer read from buffer.
- *
- * @return true if success, false otherwise.
- *
- */
-bool buffer_read_u8(buffer_t *buffer, uint8_t *value);
-
-/**
- * Read 1 byte from buffer into uint8_t without advancing the current position in the buffer.
- * Returns `true` on success, `false` if the buffer was empty; `value` is not changed in case of
- * failure.
- *
- * @param[in] buffer
- * Pointer to input buffer struct.
- * @param[out] value
- * Pointer to 8-bit unsigned integer read from buffer.
- *
- * @return true if success, false otherwise.
- */
-bool buffer_peek(const buffer_t *buffer, uint8_t *value);
-
-/**
- * Read 1 byte at position `n` from buffer into uint8_t without advancing the current position in
- * the buffer. Returns `true` on success, `false` if the buffer is not large enough; `value` is not
- * changed in case of failure.
- *
- * @param[in] buffer
- * Pointer to input buffer struct.
- * @param[out] n
- * Index of the byte to read, where the immediate next byte has index 0.
- * @param[out] value
- * Pointer to 8-bit unsigned integer read from buffer.
- *
- * @return true if success, false otherwise.
- */
-bool buffer_peek_n(const buffer_t *buffer, size_t n, uint8_t *value);
-
-/**
- * Read n bytes from buffer, and stores them in out.
- *
- * @param[in,out] buffer
- * Pointer to input buffer struct.
- * @param[out] out
- * Pointer to output buffer. It is the responsibility of the caller to make sure that the output
- * buffer is at least n bytes long.
- * @param[in] n
- * Number of bytes to read from buffer.
- *
- * @return true if success, false otherwise.
- *
- */
-bool buffer_read_bytes(buffer_t *buffer, uint8_t *out, size_t n);
-
-/**
- * Write a uint8_t into a buffer.
- *
- * @param[in,out] buffer
- * Pointer to output buffer struct.
- * @param[out] value
- * Value to be written.
- *
- * @return true if success, false if not enough space left in the buffer.
- *
- */
-bool buffer_write_u8(buffer_t *buffer, uint8_t value);
-
-/**
- * Write a uint16_t into the buffer as 2 bytes, with the given endianness.
- *
- * @param[in,out] buffer
- * Pointer to output buffer struct.
- * @param[out] value
- * Value to be written.
- * @param[in] endianness
- * Either BE (Big Endian) or LE (Little Endian).
- *
- * @return true if success, false if not enough space left in the buffer.
- *
- */
-bool buffer_write_u16(buffer_t *buffer, uint16_t value, endianness_t endianness);
-
-/**
- * Write a uint32_t into the buffer as 4 bytes, with the given endianness.
- *
- * @param[in,out] buffer
- * Pointer to output buffer struct.
- * @param[out] value
- * Value to be written.
- * @param[in] endianness
- * Either BE (Big Endian) or LE (Little Endian).
- *
- * @return true if success, false if not enough space left in the buffer.
- *
- */
-bool buffer_write_u32(buffer_t *buffer, uint32_t value, endianness_t endianness);
-
-/**
- * Write a uint64_t into the buffer as 8 bytes, with the given endianness.
- *
- * @param[in,out] buffer
- * Pointer to output buffer struct.
- * @param[out] value
- * Value to be written.
- * @param[in] endianness
- * Either BE (Big Endian) or LE (Little Endian).
- *
- * @return true if success, false if not enough space left in the buffer.
- *
- */
-bool buffer_write_u64(buffer_t *buffer, uint64_t value, endianness_t endianness);
-
-/**
- * Write a number of bytes to a buffer.
- *
- * @param[in,out] buffer
- * Pointer to output buffer struct.
- * @param[in] data
- * Pointer to bytes to be written.
- * @param[in] n
- * Size of bytes to be written.
- *
- * @return true if success, false if not enough space left in the buffer.
- *
- */
-bool buffer_write_bytes(buffer_t *buffer, const uint8_t *data, size_t n);
-
-/**
- * Creates a buffer pointing at ptr and with the given size; the initial offset is 0.
- *
- * @param[in,out] ptr
- * Pointer to the buffer's data.
- * @param[in] size
- * Size of the buffer.
- *
- * @return the new buffer with the given pointer and size.
- *
- */
-static inline buffer_t buffer_create(void *ptr, size_t size) {
- return (buffer_t){.ptr = ptr, .size = size, .offset = 0};
-}
-
/**
* Returns a pointer to the current position in the buffer if at least `size` bytes are available in
* the buffer (possibly after skipping some bytes to guarantee alignment), or NULL otherwise. On
@@ -194,24 +32,3 @@ void *buffer_alloc(buffer_t *buffer, size_t size, bool aligned);
static inline bool buffer_is_cur_aligned(const buffer_t *buffer) {
return (size_t) (buffer->ptr + buffer->offset) % 4 == 0;
}
-
-/**
- * Saves a snapshot of the current position within the buffer.
- *
- * @param[in] buffer The buffer whose position is saved.
- *
- * @return a snapshot that can be restored with `buffer_restore`.
- */
-static inline buffer_snapshot_t buffer_snapshot(const buffer_t *buffer) {
- return buffer->offset;
-}
-
-/**
- * Restores a previously taken snapshot of the buffer.
- *
- * @param[in,out] snapshot The snapshot previously returned by a call to `buffer_snapshot` on the
- * same buffer. The behavior is undefined if any other value is passed as `snapshot`.
- */
-static inline void buffer_restore(buffer_t *buffer, buffer_snapshot_t snapshot) {
- buffer->offset = snapshot;
-}
diff --git a/src/common/parser_ext.c b/src/common/parser_ext.c
index 0f54bab..767fd66 100644
--- a/src/common/parser_ext.c
+++ b/src/common/parser_ext.c
@@ -5,9 +5,6 @@
/* SDK headers */
#include "read.h"
-/* Local headers */
-#include "buffer_ext.h"
-
size_t dbuffer_get_length(buffer_t *buffers[2]) {
return (buffers[0]->size - buffers[0]->offset) + (buffers[1]->size - buffers[1]->offset);
}
@@ -117,8 +114,8 @@ bool parser_consolidate_buffers(buffer_t *buffers[2], size_t max_size) {
return false;
}
- memmove((uint8_t *) buffers[0]->ptr, buffers[0]->ptr + buffers[0]->offset, length0);
- memmove((uint8_t *) buffers[0]->ptr + length0, buffers[1]->ptr + buffers[1]->offset, length1);
+ memmove(buffers[0]->ptr, buffers[0]->ptr + buffers[0]->offset, length0);
+ memmove(buffers[0]->ptr + length0, buffers[1]->ptr + buffers[1]->offset, length1);
buffers[0]->offset = 0;
buffers[0]->size = length0 + length1;
return true;
diff --git a/src/handler/get_wallet_address.c b/src/handler/get_wallet_address.c
index a9aeb65..6da86b2 100644
--- a/src/handler/get_wallet_address.c
+++ b/src/handler/get_wallet_address.c
@@ -24,7 +24,6 @@
#include "read.h"
/* Local headers */
-#include "buffer_ext.h"
#include "client_commands.h"
#include "commands.h"
#include "constants.h"
diff --git a/src/handler/lib/check_merkle_tree_sorted.c b/src/handler/lib/check_merkle_tree_sorted.c
index 550dcb4..3946704 100644
--- a/src/handler/lib/check_merkle_tree_sorted.c
+++ b/src/handler/lib/check_merkle_tree_sorted.c
@@ -3,7 +3,6 @@
#include "check_merkle_tree_sorted.h"
/* Local headers */
-#include "buffer_ext.h"
#include "get_merkle_leaf_element.h"
#include "merkle.h"
diff --git a/src/handler/lib/get_merkle_leaf_hash.c b/src/handler/lib/get_merkle_leaf_hash.c
index 183e14d..d791266 100644
--- a/src/handler/lib/get_merkle_leaf_hash.c
+++ b/src/handler/lib/get_merkle_leaf_hash.c
@@ -8,7 +8,6 @@
#include "write.h"
/* Local headers */
-#include "buffer_ext.h"
#include "client_commands.h"
#include "debug.h"
#include "merkle.h"
diff --git a/src/handler/lib/get_merkle_preimage.c b/src/handler/lib/get_merkle_preimage.c
index 2f03645..8a7e149 100644
--- a/src/handler/lib/get_merkle_preimage.c
+++ b/src/handler/lib/get_merkle_preimage.c
@@ -6,7 +6,6 @@
#include "buffer.h"
/* Local headers */
-#include "buffer_ext.h"
#include "client_commands.h"
#include "crypto.h"
#include "debug.h"
@@ -59,7 +58,7 @@ int call_get_merkle_preimage(dispatcher_context_t *dispatcher_context,
}
uint8_t *data_ptr =
- (uint8_t *) (dispatcher_context->read_buffer.ptr + dispatcher_context->read_buffer.offset);
+ dispatcher_context->read_buffer.ptr + dispatcher_context->read_buffer.offset;
cx_sha256_t hash_context;
@@ -100,8 +99,7 @@ int call_get_merkle_preimage(dispatcher_context_t *dispatcher_context,
return -9;
}
- data_ptr = (uint8_t *) dispatcher_context->read_buffer.ptr +
- dispatcher_context->read_buffer.offset;
+ data_ptr = dispatcher_context->read_buffer.ptr + dispatcher_context->read_buffer.offset;
// update hash
crypto_hash_update(&hash_context.header, data_ptr, n_bytes);
diff --git a/src/handler/lib/get_merkleized_map.c b/src/handler/lib/get_merkleized_map.c
index f11ae5b..93e48c4 100644
--- a/src/handler/lib/get_merkleized_map.c
+++ b/src/handler/lib/get_merkleized_map.c
@@ -6,7 +6,6 @@
#include "buffer.h"
/* Local headers */
-#include "buffer_ext.h"
#include "check_merkle_tree_sorted.h"
#include "get_merkle_leaf_element.h"
diff --git a/src/handler/lib/get_preimage.c b/src/handler/lib/get_preimage.c
index aac7cf1..357b932 100644
--- a/src/handler/lib/get_preimage.c
+++ b/src/handler/lib/get_preimage.c
@@ -1,7 +1,6 @@
#include <string.h>
/* Local headers */
-#include "buffer_ext.h"
#include "client_commands.h"
#include "crypto.h"
#include "stream_preimage.h"
@@ -47,7 +46,7 @@ int call_get_preimage(dispatcher_context_t *dispatcher_context,
buffer_t buffer_out = buffer_create(out, out_len);
uint8_t *data_ptr =
- (uint8_t *) (dispatcher_context->read_buffer.ptr + dispatcher_context->read_buffer.offset);
+ dispatcher_context->read_buffer.ptr + dispatcher_context->read_buffer.offset;
cx_sha256_t hash_context;
cx_sha256_init(&hash_context);
@@ -87,8 +86,7 @@ int call_get_preimage(dispatcher_context_t *dispatcher_context,
return -8;
}
- data_ptr = (uint8_t *) (dispatcher_context->read_buffer.ptr +
- dispatcher_context->read_buffer.offset);
+ data_ptr = dispatcher_context->read_buffer.ptr + dispatcher_context->read_buffer.offset;
// update hash
crypto_hash_update(&hash_context.header, data_ptr, n_bytes);
diff --git a/src/handler/lib/policy.c b/src/handler/lib/policy.c
index 5e3eaac..efae9ec 100644
--- a/src/handler/lib/policy.c
+++ b/src/handler/lib/policy.c
@@ -9,7 +9,6 @@
/* Local headers */
#include "bitvector.h"
-#include "buffer_ext.h"
#include "crypto.h"
#include "debug.h"
#include "get_merkle_leaf_element.h"
diff --git a/src/handler/lib/psbt_parse_rawtx.c b/src/handler/lib/psbt_parse_rawtx.c
index 05808eb..7380e42 100644
--- a/src/handler/lib/psbt_parse_rawtx.c
+++ b/src/handler/lib/psbt_parse_rawtx.c
@@ -10,7 +10,6 @@
#include "varint.h"
/* Local headers */
-#include "buffer_ext.h"
#include "crypto.h"
#include "dispatcher.h"
#include "get_merkleized_map_value_hash.h"
diff --git a/src/handler/lib/stream_preimage.c b/src/handler/lib/stream_preimage.c
index 09f8990..58996ec 100644
--- a/src/handler/lib/stream_preimage.c
+++ b/src/handler/lib/stream_preimage.c
@@ -3,7 +3,6 @@
#include "stream_preimage.h"
/* Local headers */
-#include "buffer_ext.h"
#include "client_commands.h"
#include "crypto.h"
#include "sw.h"
@@ -51,7 +50,7 @@ int call_stream_preimage(dispatcher_context_t *dispatcher_context,
}
uint8_t *data_ptr =
- (uint8_t *) (dispatcher_context->read_buffer.ptr + dispatcher_context->read_buffer.offset);
+ dispatcher_context->read_buffer.ptr + dispatcher_context->read_buffer.offset;
cx_sha256_t hash_context;
cx_sha256_init(&hash_context);
@@ -92,8 +91,7 @@ int call_stream_preimage(dispatcher_context_t *dispatcher_context,
return -8;
}
- data_ptr = (uint8_t *) dispatcher_context->read_buffer.ptr +
- dispatcher_context->read_buffer.offset;
+ data_ptr = dispatcher_context->read_buffer.ptr + dispatcher_context->read_buffer.offset;
// update hash
crypto_hash_update(&hash_context.header, data_ptr, n_bytes);
diff --git a/src/handler/register_wallet.c b/src/handler/register_wallet.c
index 5a7b5d1..fe1d684 100644
--- a/src/handler/register_wallet.c
+++ b/src/handler/register_wallet.c
@@ -26,7 +26,6 @@
#include "write.h"
/* Local headers */
-#include "buffer_ext.h"
#include "client_commands.h"
#include "commands.h"
#include "constants.h"
diff --git a/src/handler/sign_message.c b/src/handler/sign_message.c
index d4e4786..89e3341 100644
--- a/src/handler/sign_message.c
+++ b/src/handler/sign_message.c
@@ -21,7 +21,6 @@
#include "bip32.h"
/* Local headers */
-#include "buffer_ext.h"
#include "commands.h"
#include "constants.h"
#include "crypto.h"
diff --git a/src/handler/sign_psbt.c b/src/handler/sign_psbt.c
index 3cad8be..a1401cc 100644
--- a/src/handler/sign_psbt.c
+++ b/src/handler/sign_psbt.c
@@ -29,7 +29,6 @@
/* Local headers */
#include "amount_from_psbt.h"
#include "bitvector.h"
-#include "buffer_ext.h"
#include "check_merkle_tree_sorted.h"
#include "client_commands.h"
#include "commands.h"
diff --git a/src/handler/sign_psbt/extract_bip32_derivation.c b/src/handler/sign_psbt/extract_bip32_derivation.c
index b17739a..7071747 100644
--- a/src/handler/sign_psbt/extract_bip32_derivation.c
+++ b/src/handler/sign_psbt/extract_bip32_derivation.c
@@ -8,7 +8,6 @@
#include "varint.h"
/* Local headers */
-#include "buffer_ext.h"
#include "psbt.h"
#include "stream_merkle_leaf_element.h"
diff --git a/src/handler/sign_psbt/txhashes.c b/src/handler/sign_psbt/txhashes.c
index a3f9e11..4849b00 100644
--- a/src/handler/sign_psbt/txhashes.c
+++ b/src/handler/sign_psbt/txhashes.c
@@ -28,7 +28,7 @@ static void cb_process_data(buffer_t *data, void *cb_state) {
callback_state_t *state = (callback_state_t *) cb_state;
size_t data_len = data->size - data->offset;
- uint8_t *data_start_ptr = (uint8_t *) data->ptr + data->offset;
+ uint8_t *data_start_ptr = data->ptr + data->offset;
if (state->hash_prefixed != NULL) {
crypto_hash_update(state->hash_prefixed, data_start_ptr, data_len);
diff --git a/unit-tests/test_buffer.c b/unit-tests/test_buffer.c
index 554969e..c88ec88 100644
--- a/unit-tests/test_buffer.c
+++ b/unit-tests/test_buffer.c
@@ -427,29 +427,6 @@ static void test_buffer_is_cur_aligned(void **state) {
assert_false(buffer_is_cur_aligned(&buf)); //9
}
-// tests the buffer_snapshot/buffer_restore functions
-static void test_buffer_snapshot_restore(void **state) {
- (void) state;
-
- uint8_t data[32];
-
- buffer_snapshot_t snap;
- buffer_t buf;
- buffer_t buf_correct;
-
- buf = buffer_create(data, sizeof(data));
- buf_correct = buf;
-
- snap = buffer_snapshot(&buf);
- buffer_alloc(&buf, 11, false);
- buffer_restore(&buf, snap);
-
- assert_int_equal(buf.offset, buf_correct.offset);
- assert_ptr_equal(buf.ptr, buf_correct.ptr);
- assert_int_equal(buf.size, buf_correct.size);
-}
-
-
int main() {
const struct CMUnitTest tests[] = {cmocka_unit_test(test_buffer_get_cur),
cmocka_unit_test(test_buffer_read),
@@ -458,8 +435,7 @@ int main() {
cmocka_unit_test(test_buffer_write),
cmocka_unit_test(test_buffer_create),
cmocka_unit_test(test_buffer_alloc),
- cmocka_unit_test(test_buffer_is_cur_aligned),
- cmocka_unit_test(test_buffer_snapshot_restore)};
+ cmocka_unit_test(test_buffer_is_cur_aligned)};
return cmocka_run_group_tests(tests, NULL, NULL);
}
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.