CCAN: import version with htable_lock.
What changed, and why it matters
This commit imports a newer version of a low-level utility library (CCAN) used by Core Lightning. The main change adds a 'lock' feature to hash tables that lets developers mark a hash table as read-only during iteration. If code tries to add an entry to a locked table, the program will deliberately crash with an assertion failure rather than silently corrupting iteration. It is a defensive programming improvement, not a fix for a known exploitable bug in this commit itself.
Review subsequent commits that adopt htable_lock/unlock in Core Lightning to confirm unsafe iteration patterns are actually protected; this commit alone is a library import and requires no immediate action.
Security signals we found
Adds assertion-based guard against adding entries during hash-table iteration
Imports updated CCAN dependency version
No caller adoption in this commit; defensive API only
Evidence from the diff
The patch updates CCAN to init-2611-g050dc66d and adds htable_lock()/htable_unlock() APIs to ccan/htable. A new ‘locked’ counter is added to struct htable; htable_add_() asserts that locked == 0. The typed htable wrapper gains matching lock/unlock helpers. This is a hardening mechanism to catch unsafe mutations while iterating over a hash table. The commit does not change any Core Lightning caller code to use these new APIs, so it does not by itself fix any concrete vulnerability in the product.
Changed components
ccan/ccan/htable/htable.cccan/ccan/htable/htable.hccan/ccan/htable/htable_type.hccan/READMEInspect captured patch +50 / −4
diff --git a/ccan/README b/ccan/README
index e15812c1..047cf78a 100644
--- a/ccan/README
+++ b/ccan/README
@@ -1,3 +1,3 @@
CCAN imported from http://ccodearchive.net.
-CCAN version: init-2608-gb35fabb6
+CCAN version: init-2611-g050dc66d
diff --git a/ccan/ccan/htable/htable.c b/ccan/ccan/htable/htable.c
index f631ffeb..0b515b94 100644
--- a/ccan/ccan/htable/htable.c
+++ b/ccan/ccan/htable/htable.c
@@ -136,9 +136,21 @@ bool htable_copy_(struct htable *dst, const struct htable *src)
*dst = *src;
dst->table = htable;
memcpy(dst->table, src->table, sizeof(size_t) << src->bits);
+ dst->locked = 0;
return true;
}
+void htable_lock(struct htable *ht)
+{
+ ht->locked++;
+}
+
+void htable_unlock(struct htable *ht)
+{
+ assert(ht->locked != 0);
+ ht->locked--;
+}
+
static size_t hash_bucket(const struct htable *ht, size_t h)
{
return h & ((1 << ht->bits)-1);
@@ -380,6 +392,7 @@ bool htable_add_(struct htable *ht, size_t hash, const void *p)
/* Cannot insert NULL, or (void *)1. */
assert(p);
assert(entry_is_valid((uintptr_t)p));
+ assert(ht->locked == 0);
/* Getting too full? */
if (ht->elems+1 + ht->deleted > ht_max(ht)) {
diff --git a/ccan/ccan/htable/htable.h b/ccan/ccan/htable/htable.h
index faaf541b..8d0e63f9 100644
--- a/ccan/ccan/htable/htable.h
+++ b/ccan/ccan/htable/htable.h
@@ -25,7 +25,7 @@ struct htable {
size_t (*rehash)(const void *elem, void *priv);
void *priv;
unsigned int bits, perfect_bitnum;
- size_t elems, deleted;
+ size_t elems, deleted, locked;
/* These are the bits which are the same in all pointers. */
uintptr_t common_mask, common_bits;
uintptr_t *table;
@@ -49,7 +49,7 @@ struct htable {
* static struct htable ht = HTABLE_INITIALIZER(ht, rehash, NULL);
*/
#define HTABLE_INITIALIZER(name, rehash, priv) \
- { rehash, priv, 0, 0, 0, 0, -1, 0, &name.common_bits }
+ { rehash, priv, 0, 0, 0, 0, 0, -1, 0, &name.common_bits }
/**
* htable_init - initialize an empty hash table.
@@ -113,7 +113,7 @@ struct htable *htable_check(const struct htable *ht, const char *abortstr);
* @dst: the hash table to overwrite
* @src: the hash table to copy
*
- * Only fails on out-of-memory.
+ * Only fails on out-of-memory. Note that the copy is not locked (see htable_lock()).
*
* Equivalent to (but faster than):
* if (!htable_init_sized(dst, src->rehash, src->priv, 1U << src->bits))
@@ -128,6 +128,24 @@ struct htable *htable_check(const struct htable *ht, const char *abortstr);
#define htable_copy(dst, src) htable_copy_(dst, htable_debug(src, HTABLE_LOC))
bool htable_copy_(struct htable *dst, const struct htable *src);
+/**
+ * htable_lock - prevent additions to the hash table.
+ * @ht: the hash table
+ *
+ * Causes an assertion on htable_add. This is useful to enforce restrictions
+ * during iteration. This function nests, so you can htable_lock() multiple
+ * times before calling htable_unlock() multiple times.
+ */
+void htable_lock(struct htable *ht);
+
+/**
+ * htable_unlock - allow additions to the hash table.
+ * @ht: the locked hash table
+ *
+ * See htable_lock().
+ */
+void htable_unlock(struct htable *ht);
+
/**
* htable_add - add a pointer into a hash table.
* @ht: the htable
@@ -136,6 +154,8 @@ bool htable_copy_(struct htable *dst, const struct htable *src);
*
* Also note that this can only fail due to allocation failure. Otherwise, it
* returns true.
+ *
+ * Note that iteration is NOT safe over htable_add, as the table could be resized.
*/
#define htable_add(ht, hash, p) \
htable_add_(htable_debug(ht, HTABLE_LOC), hash, p)
@@ -148,6 +168,7 @@ bool htable_add_(struct htable *ht, size_t hash, const void *p);
* @p: the pointer
*
* Returns true if the pointer was found (and deleted).
+ * Note that iteration is safe over htable_del.
*/
#define htable_del(ht, hash, p) \
htable_del_(htable_debug(ht, HTABLE_LOC), hash, p)
diff --git a/ccan/ccan/htable/htable_type.h b/ccan/ccan/htable/htable_type.h
index 103d2ff1..c2461fa3 100644
--- a/ccan/ccan/htable/htable_type.h
+++ b/ccan/ccan/htable/htable_type.h
@@ -32,6 +32,10 @@
* Count entries:
* size_t <name>_count(const struct <name> *ht);
*
+ * Lock and unlock (to prevent adds):
+ * void <name>_lock(struct <name> *ht);
+ * void <name>_unlock(struct <name> *ht);
+ *
* Add function only fails if we run out of memory:
* bool <name>_add(struct <name> *ht, const <type> *e);
*
@@ -87,6 +91,14 @@
{ \
return htable_count(&ht->raw); \
} \
+ static inline UNNEEDED void name##_lock(struct name *ht) \
+ { \
+ htable_lock(&ht->raw); \
+ } \
+ static inline UNNEEDED void name##_unlock(struct name *ht) \
+ { \
+ htable_unlock(&ht->raw); \
+ } \
static inline UNNEEDED void name##_clear(struct name *ht) \
{ \
htable_clear(&ht->raw); \
Why this scored 20/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.