realtek: l3: refuse a route id the table cannot hold

find_first_zero_bit() returns the size it was given when every bit is
set, and neither route allocator tests for that. On a full table the id
becomes MAX_ROUTES or MAX_HOST_ROUTES and set_bit() writes one word past
the bitmap: the two bitmaps sit next to each other in struct
otto_l3_ctrl, followed by the interface array, so overflowing the prefix
one marks host route slot 0 as used and overflowing the host one sets a
bit in the first interface pointer.

A prefix route id is also the row the route is written at, so it goes on
to ask for row 512 of a 512-row table; otto_table_index_ok() refuses the
access and warns once per boot, while otto_l3_fib_add_v4() reports
success and marks the route offloaded.

Say the table is full and return NULL, which the caller already handles:
it turns a failed allocation into -ENOSPC.

No-op on RTL838x and RTL931x, whose notifiers return early because
cfg->setup is NULL. Compile-tested only: reaching either branch takes
512 prefix or 1536 host routes offloaded at once, which the bench cannot
produce.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Gennaro Cimmino <gcimmino@rayonra.net>
Link: https://github.com/openwrt/openwrt/pull/25194
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
This commit is contained in:
Gennaro Cimmino authored and Markus Stockhausen committed 2026-09-15 11:40:10 +02:00
1 parent 7f4f824691
commit 2337fa244e
1 file changed
+12
@@ -1013,6 +1013,12 @@ static struct otto_l3_route *otto_l3_host_route_alloc(struct otto_l3_ctrl *ctrl,
mutex_lock(ctrl->lock);
idx = find_first_zero_bit(ctrl->host_route_use_bm, MAX_HOST_ROUTES);
if (idx >= MAX_HOST_ROUTES) {
dev_err(ctrl->dev, "host route table full, %d entries in use\n",
MAX_HOST_ROUTES);
mutex_unlock(ctrl->lock);
return NULL;
}
dev_dbg(ctrl->dev, "id: %d, ip %pI4\n", idx, &ip);
r = kzalloc(sizeof(*r), GFP_KERNEL);
@@ -1057,6 +1063,12 @@ static struct otto_l3_route *otto_l3_route_alloc(struct otto_l3_ctrl *ctrl, u32
mutex_lock(ctrl->lock);
idx = find_first_zero_bit(ctrl->route_use_bm, MAX_ROUTES);
if (idx >= MAX_ROUTES) {
dev_err(ctrl->dev, "prefix route table full, %d entries in use\n",
MAX_ROUTES);
mutex_unlock(ctrl->lock);
return NULL;
}
dev_dbg(ctrl->dev, "id: %d, ip %pI4\n", idx, &ip);
r = kzalloc(sizeof(*r), GFP_KERNEL);