Skip to content

dhcpv4: Fix AVL tree corruption bugs#381

Merged
openwrt-bot merged 2 commits intoopenwrt:masterfrom
mmmspatz:mspatz/fix_avl_page_fault
Mar 16, 2026
Merged

dhcpv4: Fix AVL tree corruption bugs#381
openwrt-bot merged 2 commits intoopenwrt:masterfrom
mmmspatz:mspatz/fix_avl_page_fault

Conversation

@mmmspatz
Copy link
Copy Markdown
Contributor

dhcpv4_free_lease already calls avl_delete() on the node it's freeing, and thus shouldn't be called inside an avl_remove_all_elements() loop.

Use avl_for_each_element_safe() instead. Its documentation says: "This loop can be used if the current element might be removed from the tree during the loop."

Fixes: #371

@mmmspatz
Copy link
Copy Markdown
Contributor Author

Supersedes #376

@mmmspatz mmmspatz force-pushed the mspatz/fix_avl_page_fault branch from 4bddf1a to f641760 Compare February 18, 2026 17:01
@mmmspatz mmmspatz changed the title dhcpv4: fix segfault when tearing down interface with active leases dhcpv4: fix segfault when disabling interface Feb 18, 2026
@mmmspatz
Copy link
Copy Markdown
Contributor Author

I note that this fix was suggested earlier by @Alphix here

@mmmspatz mmmspatz changed the title dhcpv4: fix segfault when disabling interface dhcpv4: Fix AVL tree corruption bugs Mar 4, 2026
@mmmspatz
Copy link
Copy Markdown
Contributor Author

mmmspatz commented Mar 4, 2026

I found another bug, see second commit.

Noltari pushed a commit to mmmspatz/odhcpd that referenced this pull request Mar 16, 2026
dhcpv4_free_lease() unconditionally calls avl_delete() to remove the
lease from the avl tree at lease->iface->dhcpv4_leases. This corrupts
the tree if the lease is not in it, which happens if dhcpv4_assign()
returns false during address reassignment (line ~611) or assignment
(line ~630).

The check for (lease->iface == NULL) before avl_delete() does not guard
against this and is not necessary, because lease->iface is populated by
dhcpv4_alloc_lease() and never cleared.

Fix by explicitly checking that the lease is in the tree before deleting
it.

Fixes: aa6870b ("dhcpv4: use an AVL to store leases")
Signed-off-by: Mark H. Spatz <mark.h.spatz@gmail.com>
Link: openwrt#381
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
@Noltari Noltari force-pushed the mspatz/fix_avl_page_fault branch from 3e2c1ff to 82169b1 Compare March 16, 2026 17:29
Noltari pushed a commit to mmmspatz/odhcpd that referenced this pull request Mar 16, 2026
dhcpv4_free_lease already calls avl_delete() on the node it's freeing,
and thus shouldn't be called inside an avl_remove_all_elements() loop.

Use avl_for_each_element_safe() instead. Its documentation says:
"This loop can be used if the current element might be removed from the
tree during the loop."

Fixes: openwrt#371
Signed-off-by: Mark H. Spatz <mark.h.spatz@gmail.com>
Link: openwrt#381
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
Noltari pushed a commit to mmmspatz/odhcpd that referenced this pull request Mar 16, 2026
dhcpv4_free_lease() unconditionally calls avl_delete() to remove the
lease from the avl tree at lease->iface->dhcpv4_leases. This corrupts
the tree if the lease is not in it, which happens if dhcpv4_assign()
returns false during address reassignment (line ~611) or assignment
(line ~630).

The check for (lease->iface == NULL) before avl_delete() does not guard
against this and is not necessary, because lease->iface is populated by
dhcpv4_alloc_lease() and never cleared.

Fix by explicitly checking that the lease is in the tree before deleting
it.

Fixes: aa6870b ("dhcpv4: use an AVL to store leases")
Signed-off-by: Mark H. Spatz <mark.h.spatz@gmail.com>
Link: openwrt#381
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
@Noltari Noltari force-pushed the mspatz/fix_avl_page_fault branch from 82169b1 to 5206a35 Compare March 16, 2026 17:32
dhcpv4_free_lease already calls avl_delete() on the node it's freeing,
and thus shouldn't be called inside an avl_remove_all_elements() loop.

Use avl_for_each_element_safe() instead. Its documentation says:
"This loop can be used if the current element might be removed from the
tree during the loop."

Closes: openwrt#371
Signed-off-by: Mark H. Spatz <mark.h.spatz@gmail.com>
Link: openwrt#381
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
dhcpv4_free_lease() unconditionally calls avl_delete() to remove the
lease from the avl tree at lease->iface->dhcpv4_leases. This corrupts
the tree if the lease is not in it, which happens if dhcpv4_assign()
returns false during address reassignment (line ~611) or assignment
(line ~630).

The check for (lease->iface == NULL) before avl_delete() does not guard
against this and is not necessary, because lease->iface is populated by
dhcpv4_alloc_lease() and never cleared.

Fix by explicitly checking that the lease is in the tree before deleting
it.

Fixes: aa6870b ("dhcpv4: use an AVL to store leases")
Signed-off-by: Mark H. Spatz <mark.h.spatz@gmail.com>
Link: openwrt#381
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
@Noltari Noltari force-pushed the mspatz/fix_avl_page_fault branch from 5206a35 to 4e26e13 Compare March 16, 2026 17:33
@openwrt-bot openwrt-bot merged commit 4e26e13 into openwrt:master Mar 16, 2026
9 checks passed
@Noltari
Copy link
Copy Markdown
Member

Noltari commented Mar 16, 2026

Merged, thanks @mmmspatz!

Noltari pushed a commit to Noltari/odhcpd that referenced this pull request Mar 16, 2026
dhcpv4_free_lease already calls avl_delete() on the node it's freeing,
and thus shouldn't be called inside an avl_remove_all_elements() loop.

Use avl_for_each_element_safe() instead. Its documentation says:
"This loop can be used if the current element might be removed from the
tree during the loop."

(cherry picked from commit ea5af5b)
Closes: openwrt#371
Signed-off-by: Mark H. Spatz <mark.h.spatz@gmail.com>
Link: openwrt#381
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
Noltari pushed a commit to Noltari/odhcpd that referenced this pull request Mar 16, 2026
dhcpv4_free_lease() unconditionally calls avl_delete() to remove the
lease from the avl tree at lease->iface->dhcpv4_leases. This corrupts
the tree if the lease is not in it, which happens if dhcpv4_assign()
returns false during address reassignment (line ~611) or assignment
(line ~630).

The check for (lease->iface == NULL) before avl_delete() does not guard
against this and is not necessary, because lease->iface is populated by
dhcpv4_alloc_lease() and never cleared.

Fix by explicitly checking that the lease is in the tree before deleting
it.

(cherry picked from commit 4e26e13)
Fixes: aa6870b ("dhcpv4: use an AVL to store leases")
Signed-off-by: Mark H. Spatz <mark.h.spatz@gmail.com>
Link: openwrt#381
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

odhcpd do_page_fault()

4 participants