Commit 999313b8d9 for frr
commit 999313b8d9f217e70f33c34bf7797223f5525a50
Author: Minghan Chan <mhchann082@gmail.com>
Date: Fri Nov 6 08:21:02 2026 +0800
bfdd: stop sbfd reflector discriminator ranges from looping forever
"sbfd reflector ... discriminator N-M" adds reflectors N to M with a
uint32_t loop counter, so a range that ends at 4294967295 never ends:
the counter wraps to 0 and bfdd keeps adding one reflector per pass
until it runs out of memory, without serving vty or BFD packets again.
The "from >= to" check only printed a warning, so the
"4294967295-4294967295" range from the issue ran the same loop.
The parser had more holes: atol() turned 4294967296 into reflector 0,
a word such as "-50" reused the end of the previous range, "150-150"
was called illegal but still added 150, and an illegal word printed
the cmd_token pointer as a string.
Parse each value with strtoull() and accept only 1-4294967295, which
is what the initiator's remote-discr and "no sbfd reflector" accept.
Reject reversed and empty ranges, and check every argument before
adding anything, so that a bad command changes nothing. Count with a
64-bit counter, and cap one command at 65536 discriminators: a valid
range such as 1-4294967294 would still make 4 billion allocations.
Fixes #24202
Signed-off-by: Minghan Chan <mhchann082@gmail.com>
diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c
index 2f98fdcf03..d2b50f5c55 100644
--- a/bfdd/bfdd_cli.c
+++ b/bfdd/bfdd_cli.c
@@ -1263,6 +1263,68 @@ DEFPY_YANG(bfd_peer_profile, bfd_peer_profile_cmd,
return nb_cli_apply_changes(vty, NULL);
}
+/*
+ * One "sbfd reflector" command adds at most this many discriminators. Each
+ * one is a separate reflector, so this bounds how long the command keeps
+ * bfdd busy and how much memory it takes.
+ */
+#define SBFD_REFLECTOR_DISCR_MAX 65536
+
+static bool sbfd_reflector_parse_value(const char *str, const char **end, uint32_t *discr)
+{
+ unsigned long long value;
+ char *endp;
+
+ /* strtoull() would also take blanks, a sign or a 0x prefix. */
+ if (!isdigit((unsigned char)str[0]))
+ return false;
+
+ errno = 0;
+ value = strtoull(str, &endp, 10);
+ if (errno != 0 || value == 0 || value > UINT32_MAX)
+ return false;
+
+ *discr = value;
+ *end = endp;
+ return true;
+}
+
+/*
+ * Parse one discriminator argument: a value "N" or a range "N-M" with N < M,
+ * all in 1-4294967295.
+ */
+static bool sbfd_reflector_parse_discr(struct vty *vty, const char *arg, uint32_t *from,
+ uint32_t *to)
+{
+ const char *end;
+
+ if (!sbfd_reflector_parse_value(arg, &end, from))
+ goto illegal;
+
+ if (*end == '\0') {
+ *to = *from;
+ return true;
+ }
+
+ if (*end != '-' || !sbfd_reflector_parse_value(end + 1, &end, to) || *end != '\0')
+ goto illegal;
+
+ if (*from >= *to) {
+ vty_out(vty,
+ "%% Discriminator range %s is illegal, the first value must be lower than the last\n",
+ arg);
+ return false;
+ }
+
+ return true;
+
+illegal:
+ vty_out(vty,
+ "%% Discriminator %s is illegal, use a value or a range of values in 1-4294967295\n",
+ arg);
+ return false;
+}
+
DEFPY(
sbfd_reflector, sbfd_reflector_cmd,
"sbfd reflector source-address X:X::X:X$srcip discriminator WORD...",
@@ -1275,42 +1337,32 @@ DEFPY(
{
int idx_discr = 5;
int i;
- uint32_t j;
- uint32_t discr = 0;
- uint32_t discr_from = 0;
- uint32_t discr_to = 0;
+ uint32_t from, to;
+ uint64_t discr, count = 0;
+ /*
+ * Check every argument before adding anything, so that a bad one
+ * leaves the configuration unchanged.
+ */
for (i = idx_discr; i < argc; i++) {
- /* check validity*/
- char *pstr = argv[i]->arg;
-
- /*single discr*/
- if (strspn(pstr, "0123456789") == strlen(pstr)) {
- discr = atol(pstr);
- sbfd_reflector_new(discr, &srcip);
- }
- /*discr segment*/
- else if (strspn(pstr, "0123456789-") == strlen(pstr)) {
- char *token = strtok(argv[i]->arg, "-");
+ if (!sbfd_reflector_parse_discr(vty, argv[i]->arg, &from, &to))
+ return CMD_WARNING_CONFIG_FAILED;
- if (token)
- discr_from = atol(token);
+ count += (uint64_t)to - from + 1;
+ }
- token = strtok(NULL, "-");
- if (token)
- discr_to = atol(token);
+ if (count > SBFD_REFLECTOR_DISCR_MAX) {
+ vty_out(vty, "%% Too many discriminators (%" PRIu64 "), the limit is %u\n", count,
+ SBFD_REFLECTOR_DISCR_MAX);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
- if (discr_from >= discr_to) {
- vty_out(vty, "input discriminator range %u-%u is illegal\n",
- discr_from, discr_to);
- }
+ for (i = idx_discr; i < argc; i++) {
+ (void)sbfd_reflector_parse_discr(vty, argv[i]->arg, &from, &to);
- for (j = discr_from; j <= discr_to; j++)
- sbfd_reflector_new(j, &srcip);
- }
- /*illegal input*/
- else
- vty_out(vty, "input discriminator %s is illegal\n", (char *)argv[i]);
+ /* 64 bits wide, so that a range that ends at UINT32_MAX ends. */
+ for (discr = from; discr <= to; discr++)
+ sbfd_reflector_new((uint32_t)discr, &srcip);
}
return CMD_SUCCESS;