From: Ahmad Gani <reyuki@gnuweeb.org>
To: Ammar Faizi <ammarfaizi2@gnuweeb.org>
Cc: Ahmad Gani <reyuki@gnuweeb.org>,
Alviro Iskandar Setiawan <alviro.iskandar@gnuweeb.org>,
GNU/Weeb Mailing List <gwml@vger.gnuweeb.org>
Subject: [PATCH gwproxy v3 6/9] dnsparser: Fix serialize_answ function
Date: Tue, 5 Aug 2025 13:49:26 +0700 [thread overview]
Message-ID: <20250805064933.109080-7-reyuki@gnuweeb.org> (raw)
In-Reply-To: <20250805064933.109080-1-reyuki@gnuweeb.org>
This changes ensure memory read access is always valid. It fixes a logic
bug in serialize_answ that can lead to a memory error: invalid read.
Use array indexing instead of pointer arithmetic.
It also changes the handling of variable-length fields received from the
network payload. Because the payload originates from an external source,
we avoid trusting it blindly and return EINVAL instead of EAGAIN.
If EAGAIN is encountered, it's supposed to resend the request, but
the current implementation hasn't done it yet.
return EINVAL if calculate_question_len == 0
Signed-off-by: Ahmad Gani <reyuki@gnuweeb.org>
---
src/gwproxy/dnsparser.c | 91 +++++++++++++++++++++++++++--------------
1 file changed, 60 insertions(+), 31 deletions(-)
diff --git a/src/gwproxy/dnsparser.c b/src/gwproxy/dnsparser.c
index f65450fa922c..744dc61581ef 100644
--- a/src/gwproxy/dnsparser.c
+++ b/src/gwproxy/dnsparser.c
@@ -45,8 +45,10 @@ static ssize_t calculate_question_len(uint8_t *in, size_t in_len)
tot_len = 0;
while (true) {
- if (*p == 0x0)
+ if (*p == 0x0) {
+ tot_len++;
break;
+ }
if (tot_len >= in_len)
return -ENAMETOOLONG;
@@ -56,25 +58,27 @@ static ssize_t calculate_question_len(uint8_t *in, size_t in_len)
p += advance_len;
}
+ tot_len += 4;
+
return tot_len;
}
int serialize_answ(uint16_t txid, uint8_t *in, size_t in_len, gwdns_answ_data *out)
{
- size_t advance_len, first_len;
+ size_t idx;
gwdns_header_pkt *hdr;
uint16_t raw_flags;
int ret;
- advance_len = sizeof(*hdr);
- if (in_len < advance_len)
+ idx = sizeof(*hdr);
+ if (idx >= in_len)
return -EAGAIN;
hdr = (void *)in;
if (memcmp(&txid, &hdr->id, sizeof(txid)))
return -EINVAL;
- memcpy(&raw_flags, &in[2], sizeof(raw_flags));
+ memcpy(&raw_flags, &hdr->flags, sizeof(raw_flags));
raw_flags = ntohs(raw_flags);
/* QR MUST 1 = response from dns server */
if (!DNS_QR(raw_flags))
@@ -93,25 +97,26 @@ int serialize_answ(uint16_t txid, uint8_t *in, size_t in_len, gwdns_answ_data *o
if (!hdr->ancount)
return -ENODATA;
- in += advance_len;
- in_len -= advance_len;
-
- first_len = 1 + in[0];
- advance_len = first_len + 1 + 2 + 2;
- if (in_len < advance_len)
- return -EAGAIN;
+ /*
+ * Check the sizes upfront.
+ *
+ * 1 bytes for variable-length
+ * in[idx] for the length of first name
+ * 1 bytes for null terminator
+ * 2 bytes for qtype
+ * 2 bytes for qclass
+ */
+ if ((size_t)(1 + in[idx] + 1 + 2 + 2) >= in_len)
+ return -EINVAL;
- ret = calculate_question_len(in, in_len);
- if (ret < 0)
+ ret = calculate_question_len(&in[idx], in_len - idx);
+ if (ret <= 0)
return -EINVAL;
- advance_len -= first_len;
- advance_len += ret;
- if (in_len < advance_len)
+ idx += ret;
+ if (idx >= in_len)
return -EAGAIN;
- in += advance_len;
- in_len -= advance_len;
out->hdr.ancount = 0;
out->rr_answ = malloc(hdr->ancount * sizeof(uint8_t *));
if (!out->rr_answ)
@@ -127,22 +132,38 @@ int serialize_answ(uint16_t txid, uint8_t *in, size_t in_len, gwdns_answ_data *o
out->rr_answ[i] = item;
- memcpy(&is_compressed, in, sizeof(is_compressed));
+ memcpy(&is_compressed, &in[idx], sizeof(is_compressed));
is_compressed = DNS_IS_COMPRESSED(ntohs(is_compressed));
assert(is_compressed);
- in += 2; // NAME
+ idx += 2; // NAME
+ if (idx >= in_len) {
+ ret = -EAGAIN;
+ goto exit_free;
+ }
- memcpy(&item->rr_type, in, 2);
+ memcpy(&item->rr_type, &in[idx], 2);
item->rr_type = ntohs(item->rr_type);
- in += 2; // TYPE
- memcpy(&item->rr_class, in, 2);
+ idx += 2; // TYPE
+ if (idx >= in_len) {
+ ret = -EAGAIN;
+ goto exit_free;
+ }
+ memcpy(&item->rr_class, &in[idx], 2);
item->rr_class = ntohs(item->rr_class);
- in += 2; // CLASS
- memcpy(&item->ttl, in, 4);
+ idx += 2; // CLASS
+ if (idx >= in_len) {
+ ret = -EAGAIN;
+ goto exit_free;
+ }
+ memcpy(&item->ttl, &in[idx], 4);
item->ttl = be32toh(item->ttl);
- in += 4; // TTL
+ idx += 4; // TTL
+ if (idx >= in_len) {
+ ret = -EAGAIN;
+ goto exit_free;
+ }
- memcpy(&rdlength, in, sizeof(rdlength));
+ memcpy(&rdlength, &in[idx], sizeof(rdlength));
rdlength = ntohs(rdlength);
if (item->rr_type != TYPE_AAAA && item->rr_type != TYPE_A) {
ret = -EINVAL;
@@ -160,7 +181,11 @@ int serialize_answ(uint16_t txid, uint8_t *in, size_t in_len, gwdns_answ_data *o
goto exit_free;
}
item->rdlength = rdlength;
- in += 2;
+ idx += 2;
+ if (idx >= in_len) {
+ ret = -EAGAIN;
+ goto exit_free;
+ }
/*
* considering if condition above,
@@ -175,8 +200,12 @@ int serialize_answ(uint16_t txid, uint8_t *in, size_t in_len, gwdns_answ_data *o
free(item);
goto exit_free;
}
- memcpy(item->rdata, in, rdlength);
- in += rdlength;
+ memcpy(item->rdata, &in[idx], rdlength);
+ idx += rdlength;
+ if (idx > in_len) {
+ ret = -EINVAL;
+ goto exit_free;
+ }
out->hdr.ancount++;
}
--
Ahmad Gani
next prev parent reply other threads:[~2025-08-05 6:50 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-05 6:49 [PATCH gwproxy v3 0/9] Initial work for DNS lookup implementation Ahmad Gani
2025-08-05 6:49 ` [PATCH gwproxy v3 1/9] dnslookup: Split common functionality and struct into net.h and net.c Ahmad Gani
2025-08-05 9:26 ` Ammar Faizi
2025-08-05 6:49 ` [PATCH gwproxy v3 2/9] dnslookup: Add a new parameter, default_port Ahmad Gani
2025-08-05 9:26 ` Ammar Faizi
2025-08-05 10:15 ` reyuki
2025-08-05 10:26 ` Ammar Faizi
2025-08-05 10:43 ` Ahmad Gani
2025-08-05 10:46 ` Ammar Faizi
2025-08-05 12:45 ` Ahmad Gani
2025-08-05 6:49 ` [PATCH gwproxy v3 3/9] dnslookup: Allow only port string number Ahmad Gani
2025-08-05 6:49 ` [PATCH gwproxy v3 4/9] dnslookup: Initial work for implementation of C-ares-like getaddrinfo function Ahmad Gani
2025-08-05 6:49 ` [PATCH gwproxy v3 5/9] dnsparser: Update unit test of dns parser Ahmad Gani
2025-08-05 9:27 ` Ammar Faizi
2025-08-05 6:49 ` Ahmad Gani [this message]
2025-08-05 9:26 ` [PATCH gwproxy v3 6/9] dnsparser: Fix serialize_answ function Ammar Faizi
2025-08-05 12:47 ` Ahmad Gani
2025-08-05 13:04 ` Ammar Faizi
2025-08-05 13:12 ` Ahmad Gani
2025-08-05 13:51 ` Ahmad Gani
2025-08-05 14:02 ` Ammar Faizi
2025-08-05 6:49 ` [PATCH gwproxy v3 7/9] dnsparser: Transaction id creation is delegated to caller Ahmad Gani
2025-08-05 6:49 ` [PATCH gwproxy v3 8/9] dnslookup: Make gw_ares_getaddrinfo asynchronous Ahmad Gani
2025-08-05 6:49 ` [PATCH gwproxy v3 9/9] dnslookup: code style changes Ahmad Gani
2025-08-05 9:26 ` Ammar Faizi
2025-08-05 13:22 ` [PATCH gwproxy v3 0/9] Initial work for DNS lookup implementation Ammar Faizi
2025-08-05 13:28 ` Ahmad Gani
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250805064933.109080-7-reyuki@gnuweeb.org \
--to=reyuki@gnuweeb.org \
--cc=alviro.iskandar@gnuweeb.org \
--cc=ammarfaizi2@gnuweeb.org \
--cc=gwml@vger.gnuweeb.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox