common: make encode_scriptpubkey_to_addr take explicit len.
What changed, and why it matters
This change refactors a helper function that turns Bitcoin scriptPubKeys into human-readable addresses. Previously the function measured the length itself using a memory-tracking helper (tal_bytelen). Now callers pass the length explicitly. This is mostly a code-cleanup to support data that isn't tracked by that helper, such as internal libwally transaction buffers. There is no direct security bug being fixed, but the old pattern could theoretically lead to incorrect length handling if a non-tal buffer were passed in future code.
No urgent action required. Treat as routine refactoring with minor defensive value. Reviewers should verify that all call sites pass a correct length and that no caller now passes an unchecked attacker-controlled length without validation.
Security signals we found
API contract hardened: length now explicit rather than inferred from tal metadata
Potential future misuse reduced: non-tal buffers can now be encoded safely
No bounds-check or overflow fix present in diff
No memory allocation bug fixed in diff
No cryptographic bug fixed in diff
Evidence from the diff
The commit changes encode_scriptpubkey_to_addr() to accept an explicit scriptpubkey_len parameter instead of calling tal_bytelen(scriptpubkey) internally. All call sites are updated to pass tal_bytelen(…) or an already-known length. The fuzz harness is adjusted to pass raw fuzzer data directly without wrapping it in a tal array. The change is defensive: it removes an implicit assumption that every scriptpubkey buffer is a tal-allocated array, which is important for callers that work with wally_tx internals. There is no patch of an active vulnerability; it is an API hardening/refactoring commit.
Changed components
common/addr.ccommon/addr.hcommon/bolt11_json.clightningd/opening_control.clightningd/peer_control.cplugins/spender/splice.ctests/fuzz/fuzz-addr.cwallet/walletrpc.cInspect captured patch +25 / −20
diff --git a/common/addr.c b/common/addr.c
index e7ff17c3..27aeacef 100644
--- a/common/addr.c
+++ b/common/addr.c
@@ -7,31 +7,31 @@
char *encode_scriptpubkey_to_addr(const tal_t *ctx,
const struct chainparams *chainparams,
- const u8 *scriptpubkey)
+ const u8 *scriptpubkey,
+ size_t scriptpubkey_len)
{
char *out;
- const size_t script_len = tal_bytelen(scriptpubkey);
struct bitcoin_address pkh;
struct ripemd160 sh;
int witver;
- if (is_p2pkh(scriptpubkey, script_len, &pkh))
+ if (is_p2pkh(scriptpubkey, scriptpubkey_len, &pkh))
return bitcoin_to_base58(ctx, chainparams, &pkh);
- if (is_p2sh(scriptpubkey, script_len, &sh))
+ if (is_p2sh(scriptpubkey, scriptpubkey_len, &sh))
return p2sh_to_base58(ctx, chainparams, &sh);
- if (is_p2tr(scriptpubkey, script_len, NULL))
+ if (is_p2tr(scriptpubkey, scriptpubkey_len, NULL))
witver = 1;
- else if (is_p2wpkh(scriptpubkey, script_len, NULL)
- || is_p2wsh(scriptpubkey, script_len, NULL))
+ else if (is_p2wpkh(scriptpubkey, scriptpubkey_len, NULL)
+ || is_p2wsh(scriptpubkey, scriptpubkey_len, NULL))
witver = 0;
else {
return NULL;
}
out = tal_arr(ctx, char, 73 + strlen(chainparams->onchain_hrp));
if (!segwit_addr_encode(out, chainparams->onchain_hrp, witver,
- scriptpubkey + 2, script_len - 2))
+ scriptpubkey + 2, scriptpubkey_len - 2))
return tal_free(out);
return out;
diff --git a/common/addr.h b/common/addr.h
index 7ab70f50..e5056990 100644
--- a/common/addr.h
+++ b/common/addr.h
@@ -6,7 +6,8 @@
/* Given a scriptPubkey, return an encoded address for p2pkh/p2w{pkh,sh}/p2tr */
char *encode_scriptpubkey_to_addr(const tal_t *ctx,
const struct chainparams *chainparams,
- const u8 *scriptpubkey);
+ const u8 *scriptpubkey,
+ size_t scriptpubkey_len);
bool decode_scriptpubkey_from_addr(const tal_t *ctx,
const struct chainparams *chainparams,
diff --git a/common/bolt11_json.c b/common/bolt11_json.c
index bdc91c12..c1891c23 100644
--- a/common/bolt11_json.c
+++ b/common/bolt11_json.c
@@ -28,7 +28,7 @@ static void json_add_fallback(struct json_stream *response,
json_add_string(response, "type", "P2TR");
}
- addr = encode_scriptpubkey_to_addr(tmpctx, chain, fallback);
+ addr = encode_scriptpubkey_to_addr(tmpctx, chain, fallback, fallback_len);
if (addr)
json_add_string(response, "addr", addr);
json_add_hex_talarr(response, "hex", fallback);
diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c
index ae302630..9263ae6c 100644
--- a/lightningd/opening_control.c
+++ b/lightningd/opening_control.c
@@ -311,7 +311,8 @@ static void funding_started_success(struct funding_channel *fc)
response = json_stream_success(cmd);
out = encode_scriptpubkey_to_addr(cmd,
chainparams,
- fc->funding_scriptpubkey);
+ fc->funding_scriptpubkey,
+ tal_bytelen(fc->funding_scriptpubkey));
if (out) {
json_add_string(response, "funding_address", out);
json_add_hex_talarr(response, "scriptpubkey",
diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c
index 94fb9b85..52247378 100644
--- a/lightningd/peer_control.c
+++ b/lightningd/peer_control.c
@@ -1097,7 +1097,8 @@ static void NON_NULL_ARGS(1, 2, 4, 5) json_add_channel(struct command *cmd,
if (channel->shutdown_scriptpubkey[LOCAL]) {
char *addr = encode_scriptpubkey_to_addr(tmpctx,
chainparams,
- channel->shutdown_scriptpubkey[LOCAL]);
+ channel->shutdown_scriptpubkey[LOCAL],
+ tal_bytelen(channel->shutdown_scriptpubkey[LOCAL]));
if (addr)
json_add_string(response, "close_to_addr", addr);
json_add_hex_talarr(response, "close_to",
diff --git a/plugins/spender/splice.c b/plugins/spender/splice.c
index 4c83f982..986380dd 100644
--- a/plugins/spender/splice.c
+++ b/plugins/spender/splice.c
@@ -1861,7 +1861,8 @@ static struct command_result *execute_splice(struct command *cmd,
/* Reencode scriptpubkey to addr for verification */
bitcoin_address = encode_scriptpubkey_to_addr(tmpctx,
chainparams,
- scriptpubkey);
+ scriptpubkey,
+ tal_bytelen(scriptpubkey));
if (!bitcoin_address)
return do_fail(cmd, splice_cmd,
JSONRPC2_INVALID_PARAMS,
diff --git a/tests/fuzz/fuzz-addr.c b/tests/fuzz/fuzz-addr.c
index a833bc29..3b456b89 100644
--- a/tests/fuzz/fuzz-addr.c
+++ b/tests/fuzz/fuzz-addr.c
@@ -1,6 +1,7 @@
#include "config.h"
#include <assert.h>
+#include <ccan/mem/mem.h>
#include <common/addr.h>
#include <common/setup.h>
#include <common/utils.h>
@@ -16,13 +17,11 @@ void init(int *argc, char ***argv)
void run(const uint8_t *data, size_t size)
{
- uint8_t *script_pubkey = tal_dup_arr(tmpctx, uint8_t, data, size, 0);
-
- char *addr = encode_scriptpubkey_to_addr(tmpctx, chainparams, script_pubkey);
+ char *addr = encode_scriptpubkey_to_addr(tmpctx, chainparams, data, size);
if (addr) {
uint8_t *decoded_script_pubkey;
assert(decode_scriptpubkey_from_addr(tmpctx, chainparams, addr, &decoded_script_pubkey));
- assert(tal_arr_eq(script_pubkey, decoded_script_pubkey));
+ assert(memeq(data, size, decoded_script_pubkey, tal_bytelen(decoded_script_pubkey)));
}
clean_tmpctx();
diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c
index 68b0d80c..700367cc 100644
--- a/wallet/walletrpc.c
+++ b/wallet/walletrpc.c
@@ -213,7 +213,7 @@ static struct command_result *json_listaddresses(struct command *cmd,
NULL))
return command_param_failed();
- addr = encode_scriptpubkey_to_addr(tmpctx, chainparams, scriptpubkey);
+ addr = encode_scriptpubkey_to_addr(tmpctx, chainparams, scriptpubkey, tal_bytelen(scriptpubkey));
if (*liststart == 0) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
@@ -378,7 +378,8 @@ static void json_add_utxo(struct json_stream *response,
json_add_hex_talarr(response, "scriptpubkey", utxo->scriptPubkey);
out = encode_scriptpubkey_to_addr(tmpctx, chainparams,
- utxo->scriptPubkey);
+ utxo->scriptPubkey,
+ tal_bytelen(utxo->scriptPubkey));
if (!out)
log_broken(wallet->log,
"Could not encode utxo %s%s!",
@@ -1266,7 +1267,8 @@ json_signmessagewithkey(struct command *cmd, const char *buffer,
/* FIXME: we already had the address from the input */
char *addr;
- addr = encode_scriptpubkey_to_addr(tmpctx, chainparams, scriptpubkey);
+ addr = encode_scriptpubkey_to_addr(tmpctx, chainparams, scriptpubkey,
+ script_len);
if (!is_p2wpkh(scriptpubkey, script_len, NULL)) {
/* FIXME add support for BIP 322 */
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.