• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JuliaLang / julia / #37474

pending completion
#37474

push

local

web-flow
irinterp: Allow setting all IR flags (#48993)

Currently, `IR_FLAG_NOTHROW` is the only flag that irinterp is allowed to
set on statements, under the assumption that in order for a call to
be irinterp-eligible, it must have been proven `:foldable`, thus `:effect_free`,
and thus `IR_FLAG_EFFECT_FREE` was assumed to have been set. That reasoning
was sound at the time this code was written, but have since introduced
`EFFECT_FREE_IF_INACCESSIBLEMEMONLY`, which breaks the reasoning that
an `:effect_free` inference for the whole function implies the flag on
every statement. As a result, we were failing to DCE otherwise dead
statements if the IR came from irinterp.

3 of 3 new or added lines in 1 file covered. (100.0%)

70258 of 82316 relevant lines covered (85.35%)

32461773.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.13
/base/secretbuffer.jl
1
# This file is a part of Julia. License is MIT: https://julialang.org/license
2

3
"""
4
    Base.SecretBuffer()
5

6
An [`IOBuffer`](@ref)-like object where the contents will be securely wiped when garbage collected.
7

8
It is considered best practice to wipe the buffer using `Base.shred!(::SecretBuffer)` as
9
soon as the secure data are no longer required. When initializing with existing data, the
10
`SecretBuffer!` method is highly recommended to securely zero the passed argument. Avoid
11
initializing with and converting to `String`s as they are unable to be securely zeroed.
12

13
# Examples
14
```jldoctest
15
julia> s = Base.SecretBuffer()
16
SecretBuffer("*******")
17

18
julia> write(s, 's', 'e', 'c', 'r', 'e', 't')
19
6
20

21
julia> seek(s, 0); Char(read(s, UInt8))
22
's': ASCII/Unicode U+0073 (category Ll: Letter, lowercase)
23

24
julia> Base.shred!(s)
25
SecretBuffer("*******")
26

27
julia> eof(s)
28
true
29
```
30
"""
31
mutable struct SecretBuffer <: IO
32
    data::Vector{UInt8}
33
    size::Int
34
    ptr::Int
35

36
    function SecretBuffer(; sizehint=128)
19✔
37
        s = new(Vector{UInt8}(undef, sizehint), 0, 1)
19✔
38
        finalizer(final_shred!, s)
19✔
39
        return s
×
40
    end
41
end
42

43
"""
44
    SecretBuffer(str::AbstractString)
45

46
A convenience constructor to initialize a `SecretBuffer` from a non-secret string.
47

48
Strings are bad at keeping secrets because they are unable to be securely
49
zeroed or destroyed. Therefore, avoid using this constructor with secret data.
50
Instead of starting with a string, either construct the `SecretBuffer`
51
incrementally with `SecretBuffer()` and [`write`](@ref), or use a `Vector{UInt8}` with
52
the `Base.SecretBuffer!(::Vector{UInt8})` constructor.
53
"""
54
SecretBuffer(str::AbstractString) = SecretBuffer(String(str))
1✔
55
function SecretBuffer(str::String)
14✔
56
    buf = codeunits(str)
×
57
    s = SecretBuffer(sizehint=length(buf))
14✔
58
    for c in buf
14✔
59
        write(s, c)
96✔
60
    end
96✔
61
    seek(s, 0)
14✔
62
    s
14✔
63
end
64
convert(::Type{SecretBuffer}, s::AbstractString) = SecretBuffer(String(s))
4✔
65

66
"""
67
    SecretBuffer!(data::Vector{UInt8})
68

69
Initialize a new `SecretBuffer` from `data`, securely zeroing `data` afterwards.
70
"""
71
function SecretBuffer!(d::Vector{UInt8})
3✔
72
    len = length(d)
3✔
73
    s = SecretBuffer(sizehint=len)
3✔
74
    for i in 1:len
6✔
75
        write(s, d[i])
18✔
76
    end
33✔
77
    seek(s, 0)
3✔
78
    securezero!(d)
3✔
79
    s
3✔
80
end
81

82
function unsafe_SecretBuffer!(s::Cstring)
2✔
83
    if s == C_NULL
2✔
84
        throw(ArgumentError("cannot convert NULL to SecretBuffer"))
1✔
85
    end
86
    len = Int(ccall(:strlen, Csize_t, (Cstring,), s))
1✔
87
    unsafe_SecretBuffer!(convert(Ptr{UInt8}, s), len)
1✔
88
end
89
function unsafe_SecretBuffer!(p::Ptr{UInt8}, len=1)
4✔
90
    if p == C_NULL
4✔
91
        throw(ArgumentError("cannot convert NULL to SecretBuffer"))
2✔
92
    end
93
    s = SecretBuffer(sizehint=len)
1✔
94
    for i in 1:len
2✔
95
        write(s, unsafe_load(p, i))
15✔
96
    end
29✔
97
    seek(s, 0)
1✔
98
    unsafe_securezero!(p, len)
1✔
99
    s
1✔
100
end
101

102

103
show(io::IO, s::SecretBuffer) = print(io, "SecretBuffer(\"*******\")")
1✔
104

105
# Unlike other IO objects, equality is computed by value for convenience
106
==(s1::SecretBuffer, s2::SecretBuffer) = (s1.ptr == s2.ptr) && (s1.size == s2.size) && (UInt8(0) == _bufcmp(s1.data, s2.data, min(s1.size, s2.size)))
4✔
107
# Also attempt a constant time buffer comparison algorithm — the length of the secret might be
108
# inferred by a timing attack, but not its values.
109
@noinline function _bufcmp(data1::Vector{UInt8}, data2::Vector{UInt8}, sz::Int)
4✔
110
    res = UInt8(0)
4✔
111
    for i = 1:sz
8✔
112
        res |= xor(data1[i], data2[i])
50✔
113
    end
96✔
114
    return res
4✔
115
end
116
# All SecretBuffers hash the same to avoid leaking information or breaking consistency with ==
117
const _sb_hash = UInt === UInt32 ? 0x111c0925 : 0xb06061e370557428
118
hash(s::SecretBuffer, h::UInt) = hash(_sb_hash, h)
2✔
119

120

121
function write(io::SecretBuffer, b::UInt8)
36✔
122
    if io.ptr > length(io.data)
133✔
123
        # We need to resize! the array: do this manually to ensure no copies are left behind
124
        newdata = Vector{UInt8}(undef, (io.size+16)*2)
2✔
125
        copyto!(newdata, io.data)
2✔
126
        securezero!(io.data)
2✔
127
        io.data = newdata
2✔
128
    end
129
    io.size == io.ptr-1 && (io.size += 1)
133✔
130
    io.data[io.ptr] = b
133✔
131
    io.ptr += 1
133✔
132
    return 1
133✔
133
end
134

135
function write(io::IO, s::SecretBuffer)
×
136
    nb = 0
×
137
    for i in 1:s.size
×
138
        nb += write(io, s.data[i])
×
139
    end
×
140
    return nb
×
141
end
142

143
cconvert(::Type{Cstring}, s::SecretBuffer) = unsafe_convert(Cstring, s)
9✔
144
function unsafe_convert(::Type{Cstring}, s::SecretBuffer)
×
145
    # Ensure that no nuls appear in the valid region
146
    if any(==(0x00), s.data[i] for i in 1:s.size)
9✔
147
        throw(ArgumentError("`SecretBuffers` containing nul bytes cannot be converted to C strings"))
1✔
148
    end
149
    # Add a hidden nul byte just past the end of the valid region
150
    p = s.ptr
1✔
151
    s.ptr = s.size + 1
1✔
152
    write(s, '\0')
1✔
153
    s.ptr = p
1✔
154
    s.size -= 1
1✔
155
    return Cstring(unsafe_convert(Ptr{Cchar}, s.data))
1✔
156
end
157

158
seek(io::SecretBuffer, n::Integer) = (io.ptr = max(min(n+1, io.size+1), 1); io)
36✔
159
seekend(io::SecretBuffer) = seek(io, io.size+1)
3✔
160
skip(io::SecretBuffer, n::Integer) = seek(io, position(io) + n)
2✔
161

162
bytesavailable(io::SecretBuffer) = io.size - io.ptr + 1
3✔
163
position(io::SecretBuffer) = io.ptr-1
8✔
164
eof(io::SecretBuffer) = io.ptr > io.size
23✔
165
isempty(io::SecretBuffer) = io.size == 0
2✔
166
function peek(io::SecretBuffer, ::Type{UInt8})
1✔
167
    eof(io) && throw(EOFError())
1✔
168
    return io.data[io.ptr]
1✔
169
end
170
function read(io::SecretBuffer, ::Type{UInt8})
9✔
171
    eof(io) && throw(EOFError())
9✔
172
    byte = io.data[io.ptr]
9✔
173
    io.ptr += 1
9✔
174
    return byte
9✔
175
end
176

177
function final_shred!(s::SecretBuffer)
19✔
178
    !isshredded(s) && @async @warn("a SecretBuffer was `shred!`ed by the GC; use `shred!` manually after use to minimize exposure.")
35✔
179
    shred!(s)
19✔
180
end
181

182
function shred!(s::SecretBuffer)
15✔
183
    securezero!(s.data)
38✔
184
    s.ptr = 1
38✔
185
    s.size = 0
38✔
186
    return s
15✔
187
end
188

189
isshredded(s::SecretBuffer) = all(iszero, s.data)
37✔
190

191
function shred!(f::Function, x)
×
192
    try
×
193
        f(x)
×
194
    finally
195
        shred!(x)
×
196
    end
197
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc