https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=297633
Bug ID: 297633
Summary: ipfw: addr:hash hangs when a 32768-bucket hash
attempts to grow to 65536
Product: Base System
Version: CURRENT
Hardware: Any
OS: Any
Status: New
Severity: Affects Many People
Priority: ---
Component: kern
Assignee: [email protected]
Reporter: [email protected]
Created attachment 273874
--> https://bugs.freebsd.org/bugzilla/attachment.cgi?id=273874&action=edit
ip_fw_table_algo.c.patch
Environment
===========
Observed on FreeBSD 15.0-RELEASE-p11 amd64 GENERIC.
The same addr:hash resize logic is present in FreeBSD 15.1-RELEASE
and in main as inspected on 2026-08-16, so this is not specific to
15.0.
Summary
=======
An ipfw addr:hash lookup table enters an unbounded loop when its IPv4
hash has reached 32768 buckets and needs to grow.
When cfg->size4 is 32768 and cfg->items4 exceeds that value,
ta_need_modify_chash() attempts to request a new hash size of 65536.
However, the addr:hash modification path encodes the requested IPv4
and IPv6 hash sizes using 16-bit fields. The value 65536 (0x10000)
cannot be represented in 16 bits and is decoded as zero by
ta_prepare_mod_chash().
No replacement hash is allocated for the affected address family, but
the modification path reports success. cfg->size4 therefore remains
32768.
On the next iteration, cfg->items4 is still greater than cfg->size4,
so ta_need_modify_chash() requests the same resize again.
check_table_space() therefore repeats the operation indefinitely and
does not return to userland.
The same problem exists independently for the IPv6 hash size.
Entry count versus hash size
============================
32768 is the hash bucket count at which the problem occurs. It is not
a strict limit on the number of entries that may exist in the table.
When multiple entries are added in a single operation, the number of
items may cross 32768 before the grow/rehash condition is evaluated.
In the observed failure the table contained 32800 entries, and the
kernel stack showed:
add_table_entry (..., count=200)
This is consistent with a batch operation causing the number of items
to exceed the current 32768-bucket hash size.
The relevant condition is therefore:
cfg->size4 == 32768
cfg->items4 > cfg->size4
rather than the table necessarily stopping at exactly 32768 entries.
Root cause
==========
The problem is in:
sys/netpfil/ipfw/ip_fw_table_algo.c
ta_need_modify_chash() contains:
if (cfg->items4 > cfg->size4 && cfg->size4 < 65536)
data |= (cfg->size4 * 2) << 16;
if (cfg->items6 > cfg->size6 && cfg->size6 < 65536)
data |= cfg->size6 * 2;
The requested sizes are decoded by ta_prepare_mod_chash() as:
mi->size = (*pflags >> 16) & 0xFFFF;
mi->size6 = *pflags & 0xFFFF;
For IPv4, when:
cfg->size4 = 32768
the next requested size is:
32768 * 2 = 65536 = 0x10000
After decoding the 16-bit field:
0x10000 & 0xFFFF == 0
so:
mi->size == 0
ta_prepare_mod_chash() therefore skips allocation for the IPv4 hash
and returns success.
ta_fill_mod_chash() also returns success, and ta_modify_chash() does
not replace the IPv4 hash because its corresponding operation is
guarded by mi->size > 0.
Consequently:
cfg->size4 == 32768
remains unchanged.
ta_need_modify_chash() is then called again, observes that items4 is
still greater than size4, requests 65536 again, and the cycle repeats.
The same truncation applies to size6 for IPv6 tables.
Reproducer
==========
ipfw table t create type addr algo addr:hash
awk 'BEGIN {
for (i = 0; i < 40000; i++)
printf "10.%d.%d.%d\n",
int(i / 65536) % 256,
int(i / 256) % 256,
i % 256
}' | xargs -n 1 ipfw -q table t add
Once the 32768-bucket hash needs to grow, the ipfw operation does not
return.
After the condition is triggered, further configuration operations
involving the affected table, including flush and destroy, block.
Expected behavior
=================
The addr:hash implementation must not request a hash size that cannot
be represented by its modification parameter encoding.
Once the largest hash size supported by the current encoding has been
reached, automatic hash growth should stop and table operations should
continue normally.
Actual behavior
===============
When a 32768-bucket hash needs to grow, ta_need_modify_chash()
requests 65536 buckets.
That value is decoded as zero by the 16-bit encoding used by the
addr:hash modification path.
No resize takes place, but the modification callbacks report success.
The table remains in the same state that caused the resize request, so
check_table_space() repeatedly attempts the same operation without
making progress or returning to userland.
Observed panic on 15.0-RELEASE-p11
==================================
During reproduction on FreeBSD 15.0-RELEASE-p11, the machine
eventually became unresponsive and panicked with:
panic: page fault
#8 turnstile_broadcast (ts=0x0, queue=1) subr_turnstile.c:901
#9 __rw_wunlock_hard () kern_rwlock.c:1285
#10 add_table_entry (..., count=200) ip_fw_table.c:699
#11 manage_table_ent_v1 () ip_fw_table.c:988
#12 ipfw_ctl3 () ip_fw_sockopt.c:3281
A concurrent thread unlocking ch->uh_lock encountered a NULL
turnstile.
This appears to be a secondary consequence of the pathological loop
and the locking implementation used by 15.0 rather than the root
defect.
The ipfw upper-half locking implementation has changed in main, so
the exact panic signature should not necessarily be expected on
CURRENT. The non-terminating addr:hash resize loop is the underlying
defect.
Proposed fix
============
The current addr:hash implementation cannot represent a requested
hash size of 65536 in its 16-bit size fields.
Since hash sizes grow by powers of two, 32768 is the largest
power-of-two hash size representable by the current encoding.
A minimal fix is to stop automatic hash growth once the hash reaches
32768 buckets.
This preserves the existing pflags encoding and modification callbacks
while preventing ta_need_modify_chash() from generating the invalid
65536 resize request.
The fix has been tested on the affected system and prevents the
non-terminating resize loop.
The tested patch is attached as:
ip_fw_table_algo.c.patch
Impact
======
The condition is triggerable using normal documented ipfw table
operations by an administrator permitted to modify the firewall
configuration.
A sufficiently large addr:hash table can cause a kernel operation to
loop indefinitely once a 32768-bucket hash attempts to grow.
On the tested 15.0 system this eventually made the machine
unresponsive and was followed by a kernel panic.
Further configuration operations involving the affected ipfw state
may also block.
No malformed packets or direct kernel memory access are required to
trigger the condition.
--
You are receiving this mail because:
You are the assignee for the bug.
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.