lsp_plugin: add reversed feature-bit check
What changed, and why it matters
This commit is a small cleanup in a Core Lightning plugin. It moves the logic for reversing a list of feature bits into a shared helper function so callers don't have to remember to reverse the bytes themselves. The change is defensive: it reduces the chance of future mistakes when checking whether a peer supports a particular Lightning feature. There is no direct evidence in the commit of an active security bug being fixed, but the change is security-relevant because incorrect feature-bit checks could cause the plugin to misidentify peer capabilities.
No urgent action required. Reviewers should verify that is_feature_bit_set_reversed's documented semantics match Core Lightning's actual feature-bit encoding, and consider auditing other callers of is_feature_bit_set to ensure they use the correct helper.
Security signals we found
Feature-bit parsing is security-relevant in Lightning because misinterpreting feature bits can lead to incorrect capability negotiation
Manual byte reversal by callers is an error-prone pattern that could cause feature bits to be read from the wrong position
The commit adds a defensive helper with explicit documentation of byte-order semantics
Evidence from the diff
The LSPS plugin previously required callers to manually reverse a hex-decoded feature-bit byte slice before calling util::is_feature_bit_set. The commit introduces util::is_feature_bit_set_reversed, which performs the reversal internally and is documented to interpret the bitmap as big-endian across bytes while keeping LSB-first bit order within each byte. The client.rs caller is updated to use the new helper. This is a hardening/refactoring change that removes an error-prone manual step; the diff does not show a corrected bug, only prevention of potential future bugs.
Changed components
plugins/lsps-plugin/src/client.rsplugins/lsps-plugin/src/util.rsInspect captured patch +27 / −3
diff --git a/plugins/lsps-plugin/src/client.rs b/plugins/lsps-plugin/src/client.rs
index fafa36c4..6116fa4a 100644
--- a/plugins/lsps-plugin/src/client.rs
+++ b/plugins/lsps-plugin/src/client.rs
@@ -591,9 +591,7 @@ async fn ensure_lsp_connected(cln_client: &mut ClnRpc, lsp_id: &str) -> Result<(
// Check that feature bit is set
peer.features.as_deref().map_or(false, |f_str| {
if let Some(feature_bits) = hex::decode(f_str).ok() {
- let mut fb = feature_bits.clone();
- fb.reverse();
- util::is_feature_bit_set(&fb, LSP_FEATURE_BIT)
+ util::is_feature_bit_set_reversed(&feature_bits, LSP_FEATURE_BIT)
} else {
false
}
diff --git a/plugins/lsps-plugin/src/util.rs b/plugins/lsps-plugin/src/util.rs
index fe61bb37..06784911 100644
--- a/plugins/lsps-plugin/src/util.rs
+++ b/plugins/lsps-plugin/src/util.rs
@@ -5,6 +5,32 @@ use core::fmt;
use serde_json::Value;
use std::str::FromStr;
+/// Checks whether a feature bit is set in a bitmap interpreted as
+/// **big-endian across bytes**, while keeping **LSB-first within each byte**.
+///
+/// This function creates a reversed copy of `bitmap` (so the least-significant
+/// byte becomes last), then calls the simple LSB-first `is_feature_bit_set` on it.
+/// No mutation of the caller’s slice occurs.
+///
+/// In other words:
+/// - byte order: **reversed** (big-endian across the slice)
+/// - bit order within a byte: **LSB-first** (unchanged)
+///
+/// If you need *full* MSB-first (also within a byte), don’t use this helper—
+/// rewrite the mask as `1u8 << (7 - bit_index)` instead.
+///
+/// # Arguments
+/// * `bitmap` – byte slice containing the bitfield (original order, not modified)
+/// * `feature_bit` – zero-based bit index across the entire bitmap
+///
+/// # Returns
+/// `true` if the bit is set; `false` if the bit is unset or out of bounds
+pub fn is_feature_bit_set_reversed(bitmap: &[u8], feature_bit: usize) -> bool {
+ let mut reversed = bitmap.to_vec();
+ reversed.reverse();
+ is_feature_bit_set(&reversed, feature_bit)
+}
+
/// Checks if the feature bit is set in the provided bitmap.
/// Returns true if the `feature_bit` is set in the `bitmap`. Returns false if
/// the `feature_bit` is unset or our ouf bounds.
Why this scored 30/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.