From 0b3cecb50af18695def7b58c06f32cbed8d14c19 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Vladim=C3=ADr=20=C4=8Cun=C3=A1t?= <vladimir.cunat@nic.cz>
Date: Tue, 19 Feb 2019 19:41:19 +0100
Subject: [PATCH] lib/generic/lru: fix alignment of struct lru

---
 daemon/engine.c   |  8 ++++----
 lib/generic/lru.c | 10 ++++++++++
 lib/generic/lru.h |  1 +
 lib/utils.c       | 12 ++++++++++++
 lib/utils.h       | 16 ++++++++++++++++
 5 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/daemon/engine.c b/daemon/engine.c
index 486bb610c..618a33570 100644
--- a/daemon/engine.c
+++ b/daemon/engine.c
@@ -576,9 +576,9 @@ static int init_resolver(struct engine *engine)
 	/* Empty init; filled via ./lua/config.lua */
 	kr_zonecut_init(&engine->resolver.root_hints, (const uint8_t *)"", engine->pool);
 	/* Open NS rtt + reputation cache */
-	lru_create(&engine->resolver.cache_rtt, LRU_RTT_SIZE, engine->pool, NULL);
-	lru_create(&engine->resolver.cache_rep, LRU_REP_SIZE, engine->pool, NULL);
-	lru_create(&engine->resolver.cache_cookie, LRU_COOKIES_SIZE, engine->pool, NULL);
+	lru_create(&engine->resolver.cache_rtt, LRU_RTT_SIZE, NULL, NULL);
+	lru_create(&engine->resolver.cache_rep, LRU_REP_SIZE, NULL, NULL);
+	lru_create(&engine->resolver.cache_cookie, LRU_COOKIES_SIZE, NULL, NULL);
 
 	/* Load basic modules */
 	engine_register(engine, "iterate", NULL, NULL);
@@ -711,7 +711,7 @@ void engine_deinit(struct engine *engine)
 	kr_zonecut_deinit(&engine->resolver.root_hints);
 	kr_cache_close(&engine->resolver.cache);
 
-	/* The lru keys are currently malloc-ated and need to be freed. */
+	/* The LRUs are currently malloc-ated and need to be freed. */
 	lru_free(engine->resolver.cache_rtt);
 	lru_free(engine->resolver.cache_rep);
 	lru_free(engine->resolver.cache_cookie);
diff --git a/lib/generic/lru.c b/lib/generic/lru.c
index 12bba4ed7..98d9033e0 100644
--- a/lib/generic/lru.c
+++ b/lib/generic/lru.c
@@ -16,6 +16,7 @@
 
 #include "lib/generic/lru.h"
 #include "contrib/murmurhash3/murmurhash3.h"
+#include "contrib/ucw/mempool.h"
 
 typedef struct lru_group lru_group_t;
 
@@ -108,6 +109,15 @@ KR_EXPORT struct lru * lru_create_impl(uint max_slots, knot_mm_t *mm_array, knot
 	group_count = 1 << log_groups;
 	assert(max_slots <= group_count * LRU_ASSOC && group_count * LRU_ASSOC < 2 * max_slots);
 
+	/* Get a sufficiently aligning mm_array if NULL is passed. */
+	if (!mm_array) {
+		static knot_mm_t mm_array_default = { 0 };
+		if (!mm_array_default.ctx)
+			mm_ctx_init_aligned(&mm_array_default, __alignof(struct lru));
+		mm_array = &mm_array_default;
+	}
+	assert(mm_array->alloc != mm_malloc && mm_array->alloc != (knot_mm_alloc_t)mp_alloc);
+
 	size_t size = offsetof(struct lru, groups[group_count]);
 	struct lru *lru = mm_alloc(mm_array, size);
 	if (unlikely(lru == NULL))
diff --git a/lib/generic/lru.h b/lib/generic/lru.h
index b5c9bcd69..1e195c6dc 100644
--- a/lib/generic/lru.h
+++ b/lib/generic/lru.h
@@ -88,6 +88,7 @@
  * @param ptable pointer to a pointer to the LRU
  * @param max_slots number of slots
  * @param mm_ctx_array memory context to use for the huge array, NULL for default
+ * 	If you pass your own, it needs to produce CACHE_ALIGNED allocations (ubsan).
  * @param mm_ctx memory context to use for individual key-value pairs, NULL for default
  *
  * @note The pointers to memory contexts need to remain valid
diff --git a/lib/utils.c b/lib/utils.c
index 6e63f9971..09194bf0e 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -71,6 +71,18 @@ void *mm_malloc(void *ctx, size_t n)
 	(void)ctx;
 	return malloc(n);
 }
+void *mm_malloc_aligned(void *ctx, size_t n)
+{
+	size_t alignment = (size_t)ctx;
+	void *res;
+	int err = posix_memalign(&res, alignment, n);
+	if (err == 0) {
+		return res;
+	} else {
+		assert(err == -1 && errno == ENOMEM);
+		return NULL;
+	}
+}
 
 /*
  * Macros.
diff --git a/lib/utils.h b/lib/utils.h
index 8deef4efe..8dabd05da 100644
--- a/lib/utils.h
+++ b/lib/utils.h
@@ -135,6 +135,8 @@ void *mm_realloc(knot_mm_t *mm, void *what, size_t size, size_t prev_size);
 
 /** Trivial malloc() wrapper. */
 void *mm_malloc(void *ctx, size_t n);
+/** posix_memalign() wrapper. */
+void *mm_malloc_aligned(void *ctx, size_t n);
 
 /** Initialize mm with standard malloc+free. */
 static inline void mm_ctx_init(knot_mm_t *mm)
@@ -143,6 +145,20 @@ static inline void mm_ctx_init(knot_mm_t *mm)
 	mm->alloc = mm_malloc;
 	mm->free = free;
 }
+
+/** Initialize mm with malloc+free with higher alignment (a power of two). */
+static inline void mm_ctx_init_aligned(knot_mm_t *mm, size_t alignment)
+{
+	assert(__builtin_popcount(alignment) == 1);
+	mm->ctx = (uint8_t *)NULL + alignment; /*< roundabout to satisfy linters */
+	/* posix_memalign() doesn't allow alignment < sizeof(void*),
+	 * and there's no point in using it for small values anyway,
+	 * as plain malloc() guarantees at least max_align_t.
+	 * Nitpick: we might use that type when assuming C11. */
+	mm->alloc = alignment > sizeof(void*) ? mm_malloc_aligned : mm_malloc;
+	mm->free = free;
+}
+
 /* @endcond */
 
 /** A strcmp() variant directly usable for qsort() on an array of strings. */
-- 
GitLab