rfc3315: fix integer underflow and heap overflow in log6_opts STATUS_CODE

In log6_opts(), the OPTION6_STATUS_CODE branch did:
  memcpy(daemon->namebuff + len, opt6_ptr(opt, 2), opt6_len(opt)-2);
  daemon->namebuff[len + opt6_len(opt) - 2] = 0;

Two bugs:
1. Integer underflow: if opt6_len(opt) < 2, the subtraction wraps to a
   huge size_t, making memcpy write gigabytes and crash with SIGSEGV.

2. Heap overflow: no upper-bound check.  opt6_len() can return up to
   65535, but daemon->namebuff is only (MAXDNAME*2)+1 = 2051 bytes.
   A STATUS_CODE payload larger than ~2045 bytes would overflow the
   heap buffer.

Exploitability: log6_opts() is only ever called on daemon->outpacket,
the outgoing DHCPv6 reply built by dnsmasq itself — NOT on incoming
client packets.  Consequently, these bugs are NOT directly triggerable
by a remote attacker under the current code.  The fix is defensive:
put_opt6_short() (which writes STATUS_CODE options in outgoing packets)
always produces opt6_len >= 2, so neither bug fires in practice today.
However, the unchecked arithmetic is a latent hazard that could become
exploitable if future refactoring passes incoming option data through
log6_opts():
- Guard the block with opt6_len(opt) >= 2 to prevent underflow.
- Cap slen to the remaining space in daemon->namebuff.

Signed-off-by: Dominik <dl6er@dl6er.de>
This commit is contained in:
Dominik authored and Simon Kelley committed 2026-03-30 18:13:01 +01:00
1 parent ca509cb611
commit 3ea905b98b
1 file changed
+9 -3
+9 -3
View File
@@ -2037,9 +2037,15 @@ static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_op
}
else if (type == OPTION6_STATUS_CODE)
{
int len = sprintf(daemon->namebuff, "%u ", opt6_uint(opt, 0, 2));
memcpy(daemon->namebuff + len, opt6_ptr(opt, 2), opt6_len(opt)-2);
daemon->namebuff[len + opt6_len(opt) - 2] = 0;
if (opt6_len(opt) >= 2)
{
int slen = opt6_len(opt) - 2;
int len = sprintf(daemon->namebuff, "%u ", opt6_uint(opt, 0, 2));
if (slen > (MAXDNAME * 2) - len - 1)
slen = (MAXDNAME * 2) - len - 1;
memcpy(daemon->namebuff + len, opt6_ptr(opt, 2), slen);
daemon->namebuff[len + slen] = 0;
}
optname = "status";
}
else