summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Fedoryshchenko <denys.f@collabora.com>2026-10-28 20:47:22 +0200
committerDenys Fedoryshchenko <denys.f@collabora.com>2026-10-28 20:50:54 +0200
commit69cfe9cc7c03f663bcbc430291bc3a9460e433ba (patch)
treeeecd2b24d4dd85cd0bf76c2b68107f0a29820523
parentf36902fa3110277d93f809e04a71cc427a2c6dbc (diff)
downloadaccel-ppp-69cfe9cc7c03f663bcbc430291bc3a9460e433ba.tar.gz
accel-ppp-69cfe9cc7c03f663bcbc430291bc3a9460e433ba.zip
tests: fix DHCPv6 fixtures after DNS refactoring and add CI coverage
Use the shared DNS storage and production allocator in the DHCPv6 fixture. Check bounded large DNS replies, preserve the repeated-ORO overflow regression, and check domain-list allocation failure in both reply builders. Link ipv6_dns.c and stub the truncation warning. Run the standalone suites and CTest in sanitizer CI, and replace the stale documented failure with current instructions. Validation: all six standalone suites pass with ASan/UBSan (LCP uses UBSan); both CTest checks pass. Signed-off-by: Denys Fedoryshchenko <denys.f@collabora.com>
-rw-r--r--.github/workflows/run-tests-asan-ubsan.yml2
-rw-r--r--CHANGELOG.md8
-rw-r--r--docs/ng40-safeguards-review.md8
-rw-r--r--tests/README.md12
-rw-r--r--tests/unit/dhcpv6_safeguards_test.c69
-rw-r--r--tests/unit/run_safeguards.sh6
6 files changed, 86 insertions, 19 deletions
diff --git a/.github/workflows/run-tests-asan-ubsan.yml b/.github/workflows/run-tests-asan-ubsan.yml
index 0ee2c755..77586419 100644
--- a/.github/workflows/run-tests-asan-ubsan.yml
+++ b/.github/workflows/run-tests-asan-ubsan.yml
@@ -64,6 +64,7 @@ jobs:
env:
${{ matrix.env_name }}: ${{ matrix.env_value }}
run: |
+ ctest --test-dir build --output-on-failure
gcc -O1 -g -Wall -fno-strict-aliasing -D_GNU_SOURCE \
-DOPENSSL_API_COMPAT=0x10100000L \
-fsanitize=${{ matrix.sanitizer }} -fno-sanitize-recover=all \
@@ -80,6 +81,7 @@ jobs:
/tmp/ipv6_dns_test
gcc -O2 -Wall -o /tmp/bitpool_test accel-pppd/extra/bitpool_test.c
/tmp/bitpool_test
+ sh tests/unit/run_safeguards.sh build
- name: Insert and check kernel modules (ipoe, vlan-mon, ppposeq)
# if: ${{ false }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 536bb024..27e13264 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -97,13 +97,13 @@
- Added contribution guidelines and a safeguard integration review.
- Corrected RADIUS option examples, shaper units and directions, kernel
module requirements, metrics freshness guidance, and the accel-cmd
- timeout synopsis. Updated test setup instructions and documented the
- current DHCPv6 safeguard fixture limitation.
+ timeout synopsis. Updated test setup instructions.
### Tests / CI
- Added focused suites for DHCPv6, RADIUS, VRF, LCP, memory pools, logging,
- L2TP, IPv6 DNS, and bitmap allocation. See `tests/README.md` for the
- current standalone safeguard runner limitation.
+ L2TP, IPv6 DNS, and bitmap allocation. Updated the DHCPv6 safeguard fixture
+ for shared DNS storage and bounded replies, and run all six standalone
+ safeguard suites in sanitizer CI.
- Added metrics, IPoE stale-interface cleanup and link deletion, PPPoE
negotiation, and kernel-module installation regression coverage.
- Added Ubuntu 26.04 coverage and removed Debian 11 jobs. Continue testing
diff --git a/docs/ng40-safeguards-review.md b/docs/ng40-safeguards-review.md
index 8dfbe32e..9ba08977 100644
--- a/docs/ng40-safeguards-review.md
+++ b/docs/ng40-safeguards-review.md
@@ -107,10 +107,10 @@ git diff --check
```
For current checkouts, see the [focused unit test instructions](../tests/README.md#focused-unit-tests).
-The later IPv6 DNS storage refactor removed `conf_dns_count`, which the
-DHCPv6 fixture still references, and the runner needs to link `ipv6_dns.c`.
-The full command above therefore does not currently compile on master. The
-historical results below do not establish that the current full suite passes.
+The DHCPv6 fixture now uses the shared IPv6 DNS storage and its production
+implementation. It checks safe truncation of oversized DNS replies and
+failure propagation when domain-list options exhaust reply space. The
+sanitizer workflow also runs the complete standalone safeguard suite.
Build, the existing IPv6 DNS selection test, and five standalone suites passed. Tests compile production code with
small external-service stubs. DHCPv6, RADIUS, VRF, and pool tests use ASan,
diff --git a/tests/README.md b/tests/README.md
index abf3704e..d89958e1 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -21,18 +21,14 @@ These tests do not require installed kernel modules or root privileges. The
sanitizer runtime needs an environment that permits its process inspection.
The runner has six suites: `dhcpv6`, `radius`, `vrf`, `lcp`, `mempool`, and
-`log_line`. With no names it attempts all six. The DHCPv6 fixture currently
-refers to the removed `conf_dns_count` variable, and the runner does not link
-the `ipv6_dns.c` implementation now used by the reply builder. That fixture
-and its build command must be updated before a full run can pass.
-
-The other five suites can be run explicitly while that regression is being fixed:
+`log_line`. With no names it runs all six:
```bash
-sh tests/unit/run_safeguards.sh build radius vrf lcp mempool log_line
+sh tests/unit/run_safeguards.sh build
```
-A successful subset run does not validate the DHCPv6 safeguards. The
+The DHCPv6 suite checks bounded DNS replies and failure propagation when
+non-truncatable options exhaust reply space. The
[integration validation record](../docs/ng40-safeguards-review.md#validation)
describes results from the revision at which those safeguards were introduced.
diff --git a/tests/unit/dhcpv6_safeguards_test.c b/tests/unit/dhcpv6_safeguards_test.c
index 63692ee3..0938047c 100644
--- a/tests/unit/dhcpv6_safeguards_test.c
+++ b/tests/unit/dhcpv6_safeguards_test.c
@@ -14,6 +14,7 @@ static size_t response_len;
void log_warn(const char *fmt, ...) {}
void log_emerg(const char *fmt, ...) {}
void log_ppp_error(const char *fmt, ...) {}
+void log_ppp_warn(const char *fmt, ...) {}
void log_ppp_info2(const char *fmt, ...) {}
int ip6addr_add_peer(int i, struct in6_addr *a, struct in6_addr *p) { return 0; }
int ip6route_add(int i, const struct in6_addr *a, int p, const struct in6_addr *g, int m, uint32_t prio, const char *v) { return 0; }
@@ -60,10 +61,36 @@ static size_t relay(unsigned char *buf, size_t len)
return len + hdrlen + 4;
}
+static void check_dns_reply(unsigned int requested)
+{
+ struct dhcpv6_packet *pkt;
+ struct dhcpv6_option *opt;
+ unsigned int count = 0;
+ size_t offset, len;
+
+ assert(sent == 1);
+ assert(response_len <= sizeof(response));
+ pkt = dhcpv6_packet_parse(response, response_len);
+ assert(pkt);
+ list_for_each_entry(opt, &pkt->opt_list, entry) {
+ if (ntohs(opt->hdr->code) != D6_OPTION_DNS_SERVERS)
+ continue;
+ len = ntohs(opt->hdr->len);
+ assert(len && len % sizeof(struct in6_addr) == 0);
+ for (offset = 0; offset < len; offset += sizeof(struct in6_addr))
+ assert(!memcmp(opt->hdr->data + offset, &in6addr_loopback,
+ sizeof(in6addr_loopback)));
+ count += len / sizeof(struct in6_addr);
+ }
+ assert(count > 0 && count < requested);
+ dhcpv6_packet_free(pkt);
+}
+
int main(void)
{
unsigned char buf[16000], oro[4000], name[256];
const unsigned char valid_name[] = {2, 'g', 'w', 0};
+ unsigned char domain[] = {1, 'x', 0};
struct dhcpv6_packet *pkt, *reply;
struct dhcpv6_option *opt;
struct ap_session ses = {0};
@@ -132,8 +159,27 @@ int main(void)
}
assert(!dhcpv6_packet_parse(buf, len));
- /* An ORO can amplify a small request into more than 4096 response bytes. */
- conf_dns_count = 1;
+ /* A large DNS list is truncated to the reply buffer capacity. */
+ assert(ipv6_dns_reserve(&conf_dns, 300) == 0);
+ for (i = 0; i < 300; i++)
+ conf_dns.addr[i] = in6addr_loopback;
+ conf_dns.count = 300;
+ u_write_be16(oro, D6_OPTION_DNS_SERVERS);
+ len = request(buf);
+ len += option(buf + len, D6_OPTION_ORO, oro, 2);
+ pkt = dhcpv6_packet_parse(buf, len);
+ assert(pkt);
+ pkt->ses = &ses;
+ sent = 0;
+ dhcpv6_send_reply(pkt, &pd, D6_REPLY);
+ check_dns_reply(conf_dns.count);
+ sent = 0;
+ dhcpv6_send_reply2(pkt, &pd, D6_REPLY);
+ check_dns_reply(conf_dns.count);
+ dhcpv6_packet_free(pkt);
+
+ /* Repeated ORO entries leave no room for the final Preference option. */
+ conf_dns.count = 1;
for (i = 0; i < sizeof(oro); i += 2)
u_write_be16(oro + i, D6_OPTION_DNS_SERVERS);
len = request(buf);
@@ -147,6 +193,25 @@ int main(void)
dhcpv6_send_reply2(pkt, &pd, D6_REPLY);
assert(!sent);
dhcpv6_packet_free(pkt);
+ free(conf_dns.addr);
+ memset(&conf_dns, 0, sizeof(conf_dns));
+
+ /* Domain-list options cannot be shortened; exhaustion drops the reply. */
+ conf_dnssl = domain;
+ conf_dnssl_size = sizeof(domain);
+ for (i = 0; i < sizeof(oro); i += 2)
+ u_write_be16(oro + i, D6_OPTION_DOMAIN_LIST);
+ len = request(buf);
+ len += option(buf + len, D6_OPTION_ORO, oro, sizeof(oro));
+ pkt = dhcpv6_packet_parse(buf, len);
+ assert(pkt);
+ pkt->ses = &ses;
+ sent = 0;
+ dhcpv6_send_reply(pkt, &pd, D6_REPLY);
+ assert(!sent);
+ dhcpv6_send_reply2(pkt, &pd, D6_REPLY);
+ assert(!sent);
+ dhcpv6_packet_free(pkt);
puts("DHCPv6 safeguards: PASS");
return 0;
}
diff --git a/tests/unit/run_safeguards.sh b/tests/unit/run_safeguards.sh
index 7f18c492..001cd19e 100644
--- a/tests/unit/run_safeguards.sh
+++ b/tests/unit/run_safeguards.sh
@@ -13,11 +13,15 @@ for name in $names; do
# LCP's global layer table retains the whole daemon under ASan; its test
# checks wire lengths with UBSan. The packet/VRF tests also use ASan.
if [ "$name" = lcp ]; then extra=-fno-sanitize=address; fi
+ set -- "tests/unit/${name}_safeguards_test.c"
+ if [ "$name" = dhcpv6 ]; then
+ set -- "$@" accel-pppd/ipv6_dns.c
+ fi
${CC:-cc} -O1 -g -Wall -Wno-unused-function -Wno-unused-result -D_GNU_SOURCE -DAP_SESSIONID_LEN=16 \
-DOPENSSL_API_COMPAT=0x10100000L -fno-strict-aliasing \
-ffunction-sections -fdata-sections -Wl,--gc-sections \
-fsanitize=address,undefined -fno-sanitize-recover=all $extra \
-I "$build" -I accel-pppd -I accel-pppd/include -I accel-pppd/triton \
- -o "$out/$name" "tests/unit/${name}_safeguards_test.c" -lcrypto -lpthread
+ -o "$out/$name" "$@" -lcrypto -lpthread
"$out/$name"
done