summaryrefslogtreecommitdiffstats
path: root/debian/patches/0001-treewide-fix-unaligned-access.patch
blob: 05a42ee5215b47366a075293951780059c2adafc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
From ce48e5a8da49d25f5490cc1be6a54f8d9db5f520 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, 6 Apr 2021 17:28:52 +0200
Subject: [PATCH] treewide: fix unaligned access

Some less common HW (not x86, usually ARM) doesn't tolerate unaligned
access to memory and it's breakage of C as well.

It's easiest to check by meson's -Db_sanitize=undefined (on any HW).
I pushed millions of real-life QNAME+QTYPE queries over UDP in default
mode and the sanitizer seems clear now.
---
 lib/cache/impl.h    | 16 +++++++++++-----
 lib/cache/nsec1.c   | 14 ++++++++++----
 lib/layer/iterate.c |  4 +++-
 lib/selection.c     |  4 ++--
 4 files changed, 26 insertions(+), 12 deletions(-)

diff --git a/lib/cache/impl.h b/lib/cache/impl.h
index b884c361..fca8653c 100644
--- a/lib/cache/impl.h
+++ b/lib/cache/impl.h
@@ -45,7 +45,10 @@ struct entry_h {
 	bool has_optout : 1;	/**< Only for packets; persisted DNSSEC_OPTOUT. */
 	uint8_t _pad;		/**< We need even alignment for data now. */
 	uint8_t data[];
-};
+/* Well, we don't really need packing or alignment changes,
+ * but due to LMDB the whole structure may not be stored at an aligned address,
+ * and we need compilers (for non-x86) to know it to avoid SIGBUS (test: UBSAN). */
+} __attribute__ ((packed,aligned(1)));
 struct entry_apex;
 
 /** Check basic consistency of entry_h for 'E' entries, not looking into ->data.
@@ -303,10 +306,13 @@ static inline int rdataset_dematerialized_size(const uint8_t *data, uint16_t *rd
 	assert(sizeof(count) == KR_CACHE_RR_COUNT_SIZE);
 	memcpy(&count, data, sizeof(count));
 	const uint8_t *rdata = data + sizeof(count);
-	if (rdataset_count)
-		*rdataset_count = count;
-	for (int i = 0; i < count; ++i)
-		rdata += knot_rdata_size(((knot_rdata_t *)rdata)->len);
+	if (rdataset_count) // memcpy is safe for unaligned case (on non-x86)
+		memcpy(rdataset_count, &count, sizeof(count));
+	for (int i = 0; i < count; ++i) {
+		__typeof__(((knot_rdata_t *)NULL)->len) len; // memcpy as above
+		memcpy(&len, rdata + offsetof(knot_rdata_t, len), sizeof(len));
+		rdata += knot_rdata_size(len);
+	}
 	return rdata - (data + sizeof(count));
 }
 
diff --git a/lib/cache/nsec1.c b/lib/cache/nsec1.c
index 3985ca3b..ed242ca8 100644
--- a/lib/cache/nsec1.c
+++ b/lib/cache/nsec1.c
@@ -197,8 +197,14 @@ static const char * find_leq_NSEC1(struct kr_cache *cache, const struct kr_query
 	/* We know it starts before sname, so let's check the other end.
 	 * 1. construct the key for the next name - kwz_hi. */
 	/* it's *full* name ATM */
-	const knot_rdata_t *next = (const knot_rdata_t *)
-				(eh->data + KR_CACHE_RR_COUNT_SIZE);
+	/* Technical complication: memcpy is safe for unaligned case (on non-x86) */
+	__typeof__(((knot_rdata_t *)NULL)->len) next_len;
+	const uint8_t *next_data;
+	{	/* next points to knot_rdata_t but possibly unaligned */
+		const uint8_t *next = eh->data + KR_CACHE_RR_COUNT_SIZE;
+		memcpy(&next_len, next + offsetof(knot_rdata_t, len), sizeof(next_len));
+		next_data = next + offsetof(knot_rdata_t, data);
+	}
 	if (KR_CACHE_RR_COUNT_SIZE != 2 || get_uint16(eh->data) == 0) {
 		assert(false);
 		return "ERROR";
@@ -220,8 +226,8 @@ static const char * find_leq_NSEC1(struct kr_cache *cache, const struct kr_query
 		/* Lower-case chs; see also RFC 6840 5.1.
 		 * LATER(optim.): we do lots of copying etc. */
 		knot_dname_t lower_buf[KNOT_DNAME_MAXLEN];
-		ret = knot_dname_to_wire(lower_buf, next->data,
-					 MIN(next->len, KNOT_DNAME_MAXLEN));
+		ret = knot_dname_to_wire(lower_buf, next_data,
+					 MIN(next_len, KNOT_DNAME_MAXLEN));
 		if (ret < 0) { /* _ESPACE */
 			return "range search found record with incorrect contents";
 		}
diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c
index 94342cfb..8fa8010e 100644
--- a/lib/layer/iterate.c
+++ b/lib/layer/iterate.c
@@ -132,7 +132,9 @@ static bool is_valid_addr(const uint8_t *addr, size_t len)
 {
 	if (len == sizeof(struct in_addr)) {
 		/* Filter ANY and 127.0.0.0/8 */
-		uint32_t ip_host = ntohl(*(const uint32_t *)(addr));
+		uint32_t ip_host; /* Memcpy is safe for unaligned case (on non-x86) */
+		memcpy(&ip_host, addr, sizeof(ip_host));
+		ip_host = ntohl(ip_host);
 		if (ip_host == 0 || (ip_host & 0xff000000) == 0x7f000000) {
 			return false;
 		}
diff --git a/lib/selection.c b/lib/selection.c
index 56530cba..4b516bb8 100644
--- a/lib/selection.c
+++ b/lib/selection.c
@@ -155,8 +155,8 @@ struct rtt_state get_rtt_state(const uint8_t *ip, size_t len,
 	} else if (value.len != sizeof(struct rtt_state)) {
 		assert(false); // shouldn't happen but let's be more robust
 		state = default_rtt_state;
-	} else {
-		state = *(struct rtt_state *)value.data;
+	} else { // memcpy is safe for unaligned case (on non-x86)
+		memcpy(&state, value.data, sizeof(state));
 	}
 
 	free(key.data);
-- 
2.31.0