-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gnrc_dhcpv6_client: Fix out-of-bounds access during option parsing #18307
gnrc_dhcpv6_client: Fix out-of-bounds access during option parsing #18307
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing and reasoning provided seem sensible, however, when testing with
USE_DHCPV6=1 make -C examples/gnrc_border_router -j flash term
I don't get a prefix from the Kea server, even when waiting for a while or after a reboot. On current master I basically get a prefix immediately.
(I ran the application on |
The _parse_reply function iterates over the DHCPv6 message options twice but only performs sanity checks on the option length in the first iteration. As such, both loop iterations need to be identical. Unfortunately, there aren't without this commit as (1) they use different maximum length values and (2) the first iteration stops parsing as soon as it encounters a zero option while the second doesn't. As such, it is possible for out-of-bounds read to be performed by the second loop iteration. This commit fixes this.
d59b4ab
to
f073dcd
Compare
Thanks testing this and pointing me to this test case. The problem was: I didn't update the I am still not sure why this special handling for options with option type zero is needed though. I couldn't find a mention of this option type in RFC 8415. However, with the changes I just pushed I do seem to be able to retrieve a lease from Kea again. |
It was introduced in #17736, so you have to ask @benpicco and @JKRhb where they got this from. My hunch: a try to hotfix the issue you are trying to fix proper here. |
Confirmed. |
The added |
Documenting my offline discussion with @benpicco: The full explanation can be found here #17736 (review) |
At least in Wireshark, I do not see the 0 type option you claim Kea emits in #17736 (comment). |
Then this might indeed have been an artifact of the bogus len |
I open an issue, so we can get ahead with this PR. |
Thanks, we can still remove the zero option check later on. This PR should fix the undefined behavior in the option parser and not removing the zero check for now might also make it easier to backport this to the last release. |
See #18309. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's merge it as is for now and investigate #18309 later.
For good measure, I also ran the tests on make -C tests/gnrc_dhcpv6_client flash test-with-config
make -C tests/gnrc_dhcpv6_client_6lbr flash test-as-root
make -C tests/gnrc_dhcpv6_client_stateless flash test-as-root
make -C tests/gnrc_dhcpv6_relay flash test-as-root All still succeed. |
Thanks for the fix @nmeum! |
As far as I can tell, no DHCPv6 RFC specifies this option. The handling for the zero option was added in RIOT-OS#17736 as @benpicco while trying to retrieve a DHCHPv6 lease with KEA. However, I strongly suspect that the zero option was encountered due to an out-of-bounds read performed in the dhcpv6 client implementation (i.e. the option parsing loop read beyond the packet bounds). This issue was fixed in RIOT-OS#18307 and I strongly suspect that it should also fix the issue @benpicco originally encountered in RIOT-OS#17736. As such, I propose that we remove the if statement which treats the zero option as an end-of-payload marker. Fixes RIOT-OS#18309
As far as I can tell, no DHCPv6 RFC specifies this option. The handling for the zero option was added in RIOT-OS#17736 by @benpicco to fix issues encountered while trying to retrieve a DHCHPv6 lease. However, I strongly suspect that the zero option was encountered in this case due to an out-of-bounds read performed in RIOT's DHCPv6 client implementation (i.e. the option parsing loop read beyond the packet bounds). This issue was fixed in RIOT-OS#18307 and I strongly suspect that it should also fix the issue @benpicco originally encountered in RIOT-OS#17736. As such, I propose that we remove the if statement which treats the zero option as an end-of-payload marker. Fixes RIOT-OS#18309
Contribution description
The
_parse_reply
function provided bygnrc_dhcpv6_client
has two parsing loops for the DHCPv6 message options:RIOT/sys/net/application_layer/dhcpv6/client.c
Lines 991 to 1023 in 37a4fff
and:
RIOT/sys/net/application_layer/dhcpv6/client.c
Lines 1053 to 1082 in 37a4fff
Only the first parsing loop performs a sanity check on the length field of the DHCPv6 option:
RIOT/sys/net/application_layer/dhcpv6/client.c
Lines 993 to 996 in 37a4fff
In the second parsing loop it is assumed that all options have a valid length field and no further sanity checks are performed. However, there are two problems with this assumption:
orig_len
bysizeof(dhcpv6_msg_t)
. The first parsing loop doesn't do this which is wrong since it also (correctly) skips the message type and transaction ID headers when parsing the options. As such, both loops operate on differentlen
values. I believe this to be an oversight which was missed in sys/net/dhcpv6: fixes option length handling in client implementation #13622.Testing procedure
This is somewhat difficult to test since transaction ID etc. need to match.
However, I can demonstrate this issue through "unit testing" of
_parse_reply
.Compile the following application:
With the following
Makefile
:and make the following modifications to
_parse_reply
to disable some sanity checks on the transaction ID etc:Afterwards, run:
Issues/PRs references