[PATCH v3 0/5] Fixes for Fedora 43
While away, I updated my laptop to Fedora 43. Naturally a bunch of things in the passt tests broke. First there were the usual breakages due to a new static checker version (also one real bug). Then several other problems. Changes in v3: * Fixed a really stupid mistake in applying the change to 2/5 Changes in v2: * Trivial whitespace fix from Laurent * Used more natural array iteration in 2/5 (suggested by Laurent) * Added Laurent's R-b tags David Gibson (5): util: Be more defensive about buffer overruns in read_file() migrate: Don't use terminator element for versions[] array treewide: Don't rely on terminator records in ip[46].dns arrays test: Handle Operating System Command escapes in terminal output test: Include sshd-auth in mbuto guest image conf.c | 8 ++++++-- dhcp.c | 4 +++- dhcpv6.c | 4 +++- migrate.c | 9 ++++----- ndp.c | 4 +++- passt.h | 4 ++-- test/lib/term | 4 ++-- test/passt.mbuto | 6 ++++++ util.c | 2 +- 9 files changed, 30 insertions(+), 15 deletions(-) -- 2.52.0
Our "old style" test script stuff uses raw terminal output scraped from
tmux. This might include terminal escape sequences from the shell, or
whatever we run in it: they think they're talking to a terminal emulator
not a script, and they're not entirely incorrect.
In several places we filter out ANSI ESC-[ sequences to make this work.
However, this doesn't include ESC-] "Operating System Command" sequences.
Something I've updated in Fedora 43 generates heaps of these, which break
everything.
Add more hairy regexps to filter these out as well.
Signed-off-by: David Gibson
When scanning the versions[] array we use a dummy entry to detect when
we're finished. Use ARRAY_SIZE() instead, which is almost as easy, a
little safer, and doesn't cause clang-tidy 21.1.7 to complain.
Signed-off-by: David Gibson
OpenSSH has split itself into several binaries for greater security. We
already have logic to make sure we include the sshd-session binary.
OpenSSH 10 has added another: sshd-auth which we also need for a working
sshd within the guest.
Signed-off-by: David Gibson
In our arrays of DNS resolvers to pass to the guest we use a blank entry
to indicate the end of the list. We rely on this when scanning the array,
not having separate bounds checking. clang-tidy 21.1.7 has fancier
checking for array overruns in loops, but it's not able to reason that
there's always a terminating entry, so complains.
Indeed, it's correct to do so in this case. Although we allow space in the
arrays for the terminator (size MAXNS + 1), add_dns[46]() check only for
idx >= ARRAY_SIZE()
before adding an entry. This allows it to consume the last slot with a
"real" entry, meaning the places where we scan really could overrun.
Fix the bug, and make it easier to reason about (for both clang-tidy and
people) by using ARRAY_SIZE() base bounds checking. Treat the terminator
explicitly as an early exit case using 'break'.
Signed-off-by: David Gibson
On Wed, 7 Jan 2026 12:46:04 +1100
David Gibson
In our arrays of DNS resolvers to pass to the guest we use a blank entry to indicate the end of the list. We rely on this when scanning the array, not having separate bounds checking. clang-tidy 21.1.7 has fancier checking for array overruns in loops, but it's not able to reason that there's always a terminating entry, so complains.
Indeed, it's correct to do so in this case. Although we allow space in the arrays for the terminator (size MAXNS + 1), add_dns[46]() check only for idx >= ARRAY_SIZE() before adding an entry. This allows it to consume the last slot with a "real" entry, meaning the places where we scan really could overrun.
Fix the bug, and make it easier to reason about (for both clang-tidy and people)
I'm not really sure about people. This change turns some for loops from "iterate until we find a terminator" to "iterate n times: while iterating, if we find a terminator, exit the loop". In any case, the annoyance is minor enough, at least to me, so let's make clang-tidy happy by all means.
by using ARRAY_SIZE() base bounds checking. Treat the terminator explicitly as an early exit case using 'break'.
Signed-off-by: David Gibson
Reviewed-by: Laurent Vivier --- conf.c | 8 ++++++-- dhcp.c | 4 +++- dhcpv6.c | 4 +++- ndp.c | 4 +++- passt.h | 4 ++-- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/conf.c b/conf.c index 84ae12b2..b1fc4b9f 100644 --- a/conf.c +++ b/conf.c @@ -1159,7 +1159,9 @@ static void conf_print(const struct ctx *c) buf4, sizeof(buf4))); }
- for (i = 0; !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns[i]); i++) { + for (i = 0; i < ARRAY_SIZE(c->ip4.dns); i++) { + if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns[i])) + break; if (!i) info("DNS:"); inet_ntop(AF_INET, &c->ip4.dns[i], buf4, sizeof(buf4)); @@ -1197,7 +1199,9 @@ static void conf_print(const struct ctx *c) buf6, sizeof(buf6)));
dns6: - for (i = 0; !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[i]); i++) { + for (i = 0; i < ARRAY_SIZE(c->ip6.dns); i++) { + if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[i])) + break; if (!i) info("DNS:"); inet_ntop(AF_INET6, &c->ip6.dns[i], buf6, sizeof(buf6)); diff --git a/dhcp.c b/dhcp.c index 6b9c2e3b..1ff8cba9 100644 --- a/dhcp.c +++ b/dhcp.c @@ -430,7 +430,9 @@ int dhcp(const struct ctx *c, struct iov_tail *data) }
for (i = 0, opts[6].slen = 0; - !c->no_dhcp_dns && !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns[i]); i++) { + !c->no_dhcp_dns && i < ARRAY_SIZE(c->ip4.dns); i++) { + if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns[i])) + break; ((struct in_addr *)opts[6].s)[i] = c->ip4.dns[i]; opts[6].slen += sizeof(uint32_t); } diff --git a/dhcpv6.c b/dhcpv6.c index e4df0db5..d94be23a 100644 --- a/dhcpv6.c +++ b/dhcpv6.c @@ -425,7 +425,9 @@ static size_t dhcpv6_dns_fill(const struct ctx *c, char *buf, int offset) if (c->no_dhcp_dns) goto search;
- for (i = 0; !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[i]); i++) { + for (i = 0; i < ARRAY_SIZE(c->ip6.dns); i++) { + if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[i])) + break; if (!i) { srv = (struct opt_dns_servers *)(buf + offset); offset += sizeof(struct opt_hdr); diff --git a/ndp.c b/ndp.c index eb9e3139..1f2bcb0c 100644 --- a/ndp.c +++ b/ndp.c @@ -285,7 +285,9 @@ static void ndp_ra(const struct ctx *c, const struct in6_addr *dst) size_t dns_s_len = 0; int i, n;
- for (n = 0; !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[n]); n++); + for (n = 0; n < ARRAY_SIZE(c->ip6.dns); n++) + if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[n])) + break; if (n) { struct opt_rdnss *rdnss = (struct opt_rdnss *)ptr; *rdnss = (struct opt_rdnss) { diff --git a/passt.h b/passt.h index 79d01ddb..87da76d3 100644 --- a/passt.h +++ b/passt.h @@ -91,7 +91,7 @@ struct ip4_ctx { struct in_addr guest_gw; struct in_addr map_host_loopback; struct in_addr map_guest_addr; - struct in_addr dns[MAXNS + 1]; + struct in_addr dns[MAXNS];
The comment still says: * @dns: DNS addresses for DHCP, zero-terminated
struct in_addr dns_match; struct in_addr our_tap_addr;
@@ -132,7 +132,7 @@ struct ip6_ctx { struct in6_addr guest_gw; struct in6_addr map_host_loopback; struct in6_addr map_guest_addr; - struct in6_addr dns[MAXNS + 1]; + struct in6_addr dns[MAXNS];
...same here. But as this is the only remark I have on the whole series, I took the liberty to fix up the comment directly on merge.
struct in6_addr dns_match; struct in6_addr our_tap_ll;
-- Stefano
On Wed, 7 Jan 2026 12:46:01 +1100
David Gibson
While away, I updated my laptop to Fedora 43. Naturally a bunch of things in the passt tests broke. First there were the usual breakages due to a new static checker version (also one real bug). Then several other problems.
Changes in v3: * Fixed a really stupid mistake in applying the change to 2/5 Changes in v2: * Trivial whitespace fix from Laurent * Used more natural array iteration in 2/5 (suggested by Laurent) * Added Laurent's R-b tags
David Gibson (5): util: Be more defensive about buffer overruns in read_file() migrate: Don't use terminator element for versions[] array treewide: Don't rely on terminator records in ip[46].dns arrays test: Handle Operating System Command escapes in terminal output test: Include sshd-auth in mbuto guest image
Applied. -- Stefano
On Sun, Jan 11, 2026 at 12:33:00AM +0100, Stefano Brivio wrote:
On Wed, 7 Jan 2026 12:46:04 +1100 David Gibson
wrote: In our arrays of DNS resolvers to pass to the guest we use a blank entry to indicate the end of the list. We rely on this when scanning the array, not having separate bounds checking. clang-tidy 21.1.7 has fancier checking for array overruns in loops, but it's not able to reason that there's always a terminating entry, so complains.
Indeed, it's correct to do so in this case. Although we allow space in the arrays for the terminator (size MAXNS + 1), add_dns[46]() check only for idx >= ARRAY_SIZE() before adding an entry. This allows it to consume the last slot with a "real" entry, meaning the places where we scan really could overrun.
Fix the bug, and make it easier to reason about (for both clang-tidy and people)
I'm not really sure about people. This change turns some for loops from "iterate until we find a terminator" to "iterate n times: while iterating, if we find a terminator, exit the loop".
That's true of the loop itself. But to convince myself it can't overrun the buffer, I have to go look elsewhere to see if the terminator is always applied. In this case, it wasn't. With the explicitly bounded iteration it's immediately clear it can't overrun.
In any case, the annoyance is minor enough, at least to me, so let's make clang-tidy happy by all means.
by using ARRAY_SIZE() base bounds checking. Treat the terminator explicitly as an early exit case using 'break'.
Signed-off-by: David Gibson
Reviewed-by: Laurent Vivier --- conf.c | 8 ++++++-- dhcp.c | 4 +++- dhcpv6.c | 4 +++- ndp.c | 4 +++- passt.h | 4 ++-- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/conf.c b/conf.c index 84ae12b2..b1fc4b9f 100644 --- a/conf.c +++ b/conf.c @@ -1159,7 +1159,9 @@ static void conf_print(const struct ctx *c) buf4, sizeof(buf4))); }
- for (i = 0; !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns[i]); i++) { + for (i = 0; i < ARRAY_SIZE(c->ip4.dns); i++) { + if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns[i])) + break; if (!i) info("DNS:"); inet_ntop(AF_INET, &c->ip4.dns[i], buf4, sizeof(buf4)); @@ -1197,7 +1199,9 @@ static void conf_print(const struct ctx *c) buf6, sizeof(buf6)));
dns6: - for (i = 0; !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[i]); i++) { + for (i = 0; i < ARRAY_SIZE(c->ip6.dns); i++) { + if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[i])) + break; if (!i) info("DNS:"); inet_ntop(AF_INET6, &c->ip6.dns[i], buf6, sizeof(buf6)); diff --git a/dhcp.c b/dhcp.c index 6b9c2e3b..1ff8cba9 100644 --- a/dhcp.c +++ b/dhcp.c @@ -430,7 +430,9 @@ int dhcp(const struct ctx *c, struct iov_tail *data) }
for (i = 0, opts[6].slen = 0; - !c->no_dhcp_dns && !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns[i]); i++) { + !c->no_dhcp_dns && i < ARRAY_SIZE(c->ip4.dns); i++) { + if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.dns[i])) + break; ((struct in_addr *)opts[6].s)[i] = c->ip4.dns[i]; opts[6].slen += sizeof(uint32_t); } diff --git a/dhcpv6.c b/dhcpv6.c index e4df0db5..d94be23a 100644 --- a/dhcpv6.c +++ b/dhcpv6.c @@ -425,7 +425,9 @@ static size_t dhcpv6_dns_fill(const struct ctx *c, char *buf, int offset) if (c->no_dhcp_dns) goto search;
- for (i = 0; !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[i]); i++) { + for (i = 0; i < ARRAY_SIZE(c->ip6.dns); i++) { + if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[i])) + break; if (!i) { srv = (struct opt_dns_servers *)(buf + offset); offset += sizeof(struct opt_hdr); diff --git a/ndp.c b/ndp.c index eb9e3139..1f2bcb0c 100644 --- a/ndp.c +++ b/ndp.c @@ -285,7 +285,9 @@ static void ndp_ra(const struct ctx *c, const struct in6_addr *dst) size_t dns_s_len = 0; int i, n;
- for (n = 0; !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[n]); n++); + for (n = 0; n < ARRAY_SIZE(c->ip6.dns); n++) + if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.dns[n])) + break; if (n) { struct opt_rdnss *rdnss = (struct opt_rdnss *)ptr; *rdnss = (struct opt_rdnss) { diff --git a/passt.h b/passt.h index 79d01ddb..87da76d3 100644 --- a/passt.h +++ b/passt.h @@ -91,7 +91,7 @@ struct ip4_ctx { struct in_addr guest_gw; struct in_addr map_host_loopback; struct in_addr map_guest_addr; - struct in_addr dns[MAXNS + 1]; + struct in_addr dns[MAXNS];
The comment still says:
* @dns: DNS addresses for DHCP, zero-terminated
struct in_addr dns_match; struct in_addr our_tap_addr;
@@ -132,7 +132,7 @@ struct ip6_ctx { struct in6_addr guest_gw; struct in6_addr map_host_loopback; struct in6_addr map_guest_addr; - struct in6_addr dns[MAXNS + 1]; + struct in6_addr dns[MAXNS];
...same here. But as this is the only remark I have on the whole series, I took the liberty to fix up the comment directly on merge.
Ah, thanks.
struct in6_addr dns_match; struct in6_addr our_tap_ll;
-- Stefano
-- David Gibson (he or they) | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you, not the other way | around. http://www.ozlabs.org/~dgibson
participants (2)
-
David Gibson
-
Stefano Brivio