bitfield: fix out-of-bounds read in find_last_bit() - #1068
Open
gargamel778 wants to merge 1 commit into
Open
gargamel778 wants to merge 1 commit into
gargamel778 wants to merge 1 commit into
Conversation
The loop started at b == max and tested ptr[b >> 5] before decrementing, so for the only caller -- rs_get_max_rate_from_mask(), which passes an 8-byte object with max == BITS_PER_LONG == 64 -- the first iteration read ptr[2], eight bytes past the end of the object, then ptr[1] (always zero: rate masks are <= 17 bits), then exited at b == 0 without ever examining ptr[0], where every bit of the mask lives. The return value therefore depends on whatever follows the mask on the stack: 64 when ptr[2] is zero or has bit 0 set, and by luck the correct answer otherwise. 64 is not IWL_RATE_INVALID (== IWL_RATE_COUNT == 17), so it passes the guards in rs_get_max_allowed_rate()'s callers and is used to index expected_tpt_tbl[], a second out-of-bounds read -- 94 bytes past the end of expected_tpt_legacy[IWL_RATE_COUNT] for the RS_LEGACY column. rs_init_optimal_rate() also compares max_mimo2_rate_idx against IWL_RATE_INVALID, so a value pinned at 64 always selects the MIMO2 branch. Scan downwards from max - 1 instead, which restores Linux's contract: "the bit number of the last set bit, or size".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
find_last_bit()never reads the word it is asked to search, and reads one word past the end of the caller's object instead.The bug
The only caller is
rs_get_max_rate_from_mask()(hal_iwm/rs.cpp:1213), which passes&rate_mask— an 8-byteunsigned longlocal — withmax = BITS_PER_LONG = 64:b = 64ptr[64>>5]=ptr[2]b = 32ptr[1]b > 0failsptr[0], where the whole mask lives, is never examinedIt returns
max(64), so the result is decided by whatever the compiler placed after the local on the stack.Checking it without any hardware
rs.cpp:2988already logs the result:The maximum legal index is
IWL_RATE_COUNT - 1= 16. On an unpatched build here it prints:and with the patch:
Occurrences of
MAX RATE ...=64went from 23 of 50 station rate-inits to 0 of 33 across a full test protocol on an Intel 7265.Why 64 is worse than an error
IWL_RATE_INVALID == IWL_RATE_COUNT == 17(rs.h:1007), so 64 is not caught by the invalid-index guards. It is then used to indexexpected_tpt_tbl[]— a second out-of-bounds read — andrs_init_optimal_rate()comparesmax_mimo2_rate_idxagainstIWL_RATE_INVALID, so an index pinned at 64 always takes the MIMO2 branch.Standalone reproducer
Compiled against the unpatched function, varying only the stack word that follows the mask:
The fix
Scan downwards from
max - 1, which restores Linux's documented contract — "the bit number of the last set bit, or size". Eight lines, header-only, one caller.Scope: this is a correctness and memory-safety fix, evidenced by the wrong value in the driver's own log line. I am not claiming it improves throughput or fixes disconnects — I have no measurement supporting that, and I would rather not imply one.