Skip to content

Commit 17445fe

Browse files
authored
fix rawbigints OOB issues (#55917)
Fixes issues introduced in #50691 and found in #55906: * use `@inbounds` and `@boundscheck` macros in rawbigints, for catching OOB with `--check-bounds=yes` * fix OOB in `truncate`
1 parent 97ecdb8 commit 17445fe

File tree

2 files changed

+31
-9
lines changed

2 files changed

+31
-9
lines changed

base/rawbigints.jl

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,21 @@ reversed_index(n::Int, i::Int) = n - i - 1
2121
reversed_index(x, i::Int, v::Val) = reversed_index(elem_count(x, v), i)::Int
2222
split_bit_index(x::RawBigInt, i::Int) = divrem(i, word_length(x), RoundToZero)
2323

24+
function get_elem_words_raw(x::RawBigInt{T}, i::Int) where {T}
25+
@boundscheck if (i < 0) || (elem_count(x, Val(:words)) i)
26+
throw(BoundsError(x, i))
27+
end
28+
d = x.d
29+
j = i + 1
30+
(GC.@preserve d unsafe_load(Ptr{T}(pointer(d)), j))::T
31+
end
32+
2433
"""
2534
`i` is the zero-based index of the wanted word in `x`, starting from
2635
the less significant words.
2736
"""
28-
function get_elem(x::RawBigInt{T}, i::Int, ::Val{:words}, ::Val{:ascending}) where {T}
29-
# `i` must be non-negative and less than `x.word_count`
30-
d = x.d
31-
(GC.@preserve d unsafe_load(Ptr{T}(pointer(d)), i + 1))::T
37+
function get_elem(x::RawBigInt, i::Int, ::Val{:words}, ::Val{:ascending})
38+
@inbounds @inline get_elem_words_raw(x, i)
3239
end
3340

3441
function get_elem(x, i::Int, v::Val, ::Val{:descending})
@@ -96,25 +103,31 @@ end
96103

97104
"""
98105
Returns an integer of type `R`, consisting of the `len` most
99-
significant bits of `x`.
106+
significant bits of `x`. If there are less than `len` bits in `x`,
107+
the least significant bits are zeroed.
100108
"""
101109
function truncated(::Type{R}, x::RawBigInt, len::Int) where {R<:Integer}
102110
ret = zero(R)
103111
if 0 < len
104112
word_count, bit_count_in_word = split_bit_index(x, len)
105113
k = word_length(x)
106114
vals = (Val(:words), Val(:descending))
115+
lenx = elem_count(x, first(vals))
107116

108117
for w 0:(word_count - 1)
109118
ret <<= k
110-
word = get_elem(x, w, vals...)
111-
ret |= R(word)
119+
if w < lenx
120+
word = get_elem(x, w, vals...)
121+
ret |= R(word)
122+
end
112123
end
113124

114125
if !iszero(bit_count_in_word)
115126
ret <<= bit_count_in_word
116-
wrd = get_elem(x, word_count, vals...)
117-
ret |= R(wrd >>> (k - bit_count_in_word))
127+
if word_count < lenx
128+
wrd = get_elem(x, word_count, vals...)
129+
ret |= R(wrd >>> (k - bit_count_in_word))
130+
end
118131
end
119132
end
120133
ret::R

test/mpfr.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,3 +1088,12 @@ end
10881088
clear_flags()
10891089
end
10901090
end
1091+
1092+
@testset "RawBigInt truncation OOB read" begin
1093+
@testset "T: $T" for T (UInt8, UInt16, UInt32, UInt64, UInt128)
1094+
v = Base.RawBigInt{T}("a"^sizeof(T), 1)
1095+
@testset "bit_count: $bit_count" for bit_count (0:10:80)
1096+
@test Base.truncated(UInt128, v, bit_count) isa Any
1097+
end
1098+
end
1099+
end

0 commit comments

Comments
 (0)