What changed, and why it matters
This commit only adds a new automated test file that checks how a helper function (str_to_u64) converts text strings to unsigned 64-bit integers. It does not change any production code, so it cannot by itself introduce a security vulnerability or fix one. The tests verify that the function correctly handles normal numbers, overflow, invalid characters, and leading zeros.
No security action required; treat as routine test coverage. If reviewing for security, inspect the existing str_to_u64 implementation separately to confirm it satisfies the tested behavior.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff creates common/test/run-utils-str_to_u64.c, a unit-test harness with autogenerated mocks for unrelated dependencies. It exercises str_to_u64 with cases for empty input, single/multi-digit values, exact-length prefixes, UINT64_MAX, overflow, invalid characters, leading zeros, and failure-path output preservation. No implementation of str_to_u64 is modified or added.
Changed components
common/test/run-utils-str_to_u64.cInspect captured patch +203 / −0
diff --git a/common/test/run-utils-str_to_u64.c b/common/test/run-utils-str_to_u64.c
new file mode 100644
index 00000000..31b8e27b
--- /dev/null
+++ b/common/test/run-utils-str_to_u64.c
@@ -0,0 +1,203 @@
+#include "config.h"
+#include <assert.h>
+#include <common/amount.h>
+#include <common/pseudorand.h>
+#include <common/setup.h>
+#include <common/utils.h>
+#include <stdio.h>
+#include <wire/wire.h>
+
+/* AUTOGENERATED MOCKS START */
+/* Generated stub for amount_asset_is_main */
+bool amount_asset_is_main(struct amount_asset *asset UNNEEDED)
+{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); }
+/* Generated stub for amount_asset_to_sat */
+struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED)
+{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); }
+/* Generated stub for amount_feerate */
+ bool amount_feerate(u32 *feerate UNNEEDED, struct amount_sat fee UNNEEDED, size_t weight UNNEEDED)
+{ fprintf(stderr, "amount_feerate called!\n"); abort(); }
+/* Generated stub for amount_sat */
+struct amount_sat amount_sat(u64 satoshis UNNEEDED)
+{ fprintf(stderr, "amount_sat called!\n"); abort(); }
+/* Generated stub for amount_sat_add */
+ bool amount_sat_add(struct amount_sat *val UNNEEDED,
+ struct amount_sat a UNNEEDED,
+ struct amount_sat b UNNEEDED)
+{ fprintf(stderr, "amount_sat_add called!\n"); abort(); }
+/* Generated stub for amount_sat_eq */
+bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED)
+{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); }
+/* Generated stub for amount_sat_greater_eq */
+bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED)
+{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); }
+/* Generated stub for amount_sat_sub */
+ bool amount_sat_sub(struct amount_sat *val UNNEEDED,
+ struct amount_sat a UNNEEDED,
+ struct amount_sat b UNNEEDED)
+{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); }
+/* Generated stub for amount_sat_to_asset */
+struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED)
+{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); }
+/* Generated stub for amount_tx_fee */
+struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED)
+{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); }
+/* Generated stub for fromwire */
+const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED)
+{ fprintf(stderr, "fromwire called!\n"); abort(); }
+/* Generated stub for fromwire_bool */
+bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
+{ fprintf(stderr, "fromwire_bool called!\n"); abort(); }
+/* Generated stub for fromwire_fail */
+void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
+{ fprintf(stderr, "fromwire_fail called!\n"); abort(); }
+/* Generated stub for fromwire_secp256k1_ecdsa_signature */
+void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
+ secp256k1_ecdsa_signature *signature UNNEEDED)
+{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); }
+/* Generated stub for fromwire_sha256 */
+void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED)
+{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); }
+/* Generated stub for fromwire_tal_arrn */
+u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED,
+ const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED)
+{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); }
+/* Generated stub for fromwire_u32 */
+u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
+{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); }
+/* Generated stub for fromwire_u64 */
+u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
+{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); }
+/* Generated stub for fromwire_u8 */
+u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
+{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); }
+/* Generated stub for fromwire_u8_array */
+void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED)
+{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); }
+/* Generated stub for siphash_seed */
+const struct siphash_seed *siphash_seed(void)
+{ fprintf(stderr, "siphash_seed called!\n"); abort(); }
+/* Generated stub for towire */
+void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED)
+{ fprintf(stderr, "towire called!\n"); abort(); }
+/* Generated stub for towire_bool */
+void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED)
+{ fprintf(stderr, "towire_bool called!\n"); abort(); }
+/* Generated stub for towire_secp256k1_ecdsa_signature */
+void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED,
+ const secp256k1_ecdsa_signature *signature UNNEEDED)
+{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); }
+/* Generated stub for towire_sha256 */
+void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED)
+{ fprintf(stderr, "towire_sha256 called!\n"); abort(); }
+/* Generated stub for towire_u32 */
+void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED)
+{ fprintf(stderr, "towire_u32 called!\n"); abort(); }
+/* Generated stub for towire_u64 */
+void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED)
+{ fprintf(stderr, "towire_u64 called!\n"); abort(); }
+/* Generated stub for towire_u8 */
+void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED)
+{ fprintf(stderr, "towire_u8 called!\n"); abort(); }
+/* Generated stub for towire_u8_array */
+void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED)
+{ fprintf(stderr, "towire_u8_array called!\n"); abort(); }
+/* AUTOGENERATED MOCKS END */
+
+static void test_str_to_u64(void)
+{
+ u64 val;
+
+ /* Empty input */
+ assert(!str_to_u64("", 0, &val));
+
+ /* Single digit */
+ assert(str_to_u64("0", 1, &val) && val == 0);
+ assert(str_to_u64("5", 1, &val) && val == 5);
+ assert(str_to_u64("9", 1, &val) && val == 9);
+
+ /* Multi-digit */
+ assert(str_to_u64("123", 3, &val) && val == 123);
+ assert(str_to_u64("000", 3, &val) && val == 0);
+ assert(str_to_u64("000123", 6, &val) && val == 123);
+
+ /* Exact length prefix */
+ assert(str_to_u64("1234", 2, &val) && val == 12);
+
+ /* Max value (2^64-1) */
+ assert(str_to_u64("18446744073709551615", 20, &val) &&
+ val == 18446744073709551615ULL);
+
+ /* Overflow */
+ assert(!str_to_u64("18446744073709551616", 20, &val));
+ assert(!str_to_u64("184467440737095516150", 21, &val));
+
+ /* Invalid characters */
+ assert(!str_to_u64("12a3", 4, &val));
+ assert(!str_to_u64("-123", 4, &val));
+ assert(!str_to_u64("1.23", 4, &val));
+ assert(!str_to_u64("123 ", 4, &val));
+ assert(!str_to_u64(" 123", 4, &val));
+
+ /* Non-digit at various positions */
+ assert(!str_to_u64("x", 1, &val));
+ assert(!str_to_u64("1x", 2, &val));
+ assert(!str_to_u64("12x", 3, &val));
+
+ /* Long string of zeros (21) must succeed as 0 (leading zeros are
+ * allowed) */
+ assert(str_to_u64("000000000000000000000", 21, &val) && val == 0);
+
+ /* Max value with leading zeros */
+ assert(str_to_u64("0018446744073709551615", 22, &val) &&
+ val == 18446744073709551615ULL);
+ assert(str_to_u64("0000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000018446744073709551615",
+ 100, &val) &&
+ val == 18446744073709551615ULL);
+
+ /* 1 followed by 20 zeros (10^20), must overflow */
+ assert(!str_to_u64("100000000000000000000", 21, &val));
+
+ /* 1 followed by 19 zeros (10^19), fits */
+ assert(str_to_u64("10000000000000000000", 20, &val) &&
+ val == 10000000000000000000ULL);
+
+ /* 999...9 (20 times) overflows */
+ assert(!str_to_u64("99999999999999999999", 20, &val));
+
+ /* Just below UINT64_MAX */
+ assert(str_to_u64("18446744073709551614", 20, &val) &&
+ val == 18446744073709551614ULL);
+
+ /* Prefix of the max (first 19 chars of the 20-digit max) */
+ assert(str_to_u64("18446744073709551615", 19, &val) &&
+ val == 1844674407370955161ULL);
+
+ /* Ensure we do not write *num on failure paths */
+ {
+ u64 before = 0xdeadbeefcafebabeULL;
+ val = before;
+
+ assert(!str_to_u64("12a3", 4, &val));
+ assert(val == before);
+
+ assert(!str_to_u64("", 0, &val));
+ assert(val == before);
+
+ assert(!str_to_u64("18446744073709551616", 20, &val));
+ assert(val == before);
+
+ assert(!str_to_u64("1234567890", 0, &val));
+ assert(val == before);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ common_setup(argv[0]);
+
+ test_str_to_u64();
+
+ common_shutdown();
+}
Why this scored 15/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.