Skip to content

Commit

Permalink
fix read_data bug & use in read_standard_header (#112)
Browse files Browse the repository at this point in the history
Also make sure that buffer views are constructed with `Int` ranges.
Otherwise on 32-bit systems a 64-bit buffer view cannot be converted to
a pointer, which causes problems. This entails checking for too large
size values in a few places and casting to `Int` in a few more places
where we know a value has to fit into a native `Int`.
  • Loading branch information
StefanKarpinski authored May 11, 2021
1 parent f06ba79 commit fdeec31
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 20 deletions.
13 changes: 13 additions & 0 deletions src/Tar.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ function Base.skip(io::Union{Base.Process, Base.ProcessChain}, n::Integer)
end
const skip_buffer = UInt8[]

# method for this exists in Base since Julia 1.4 but not before
!hasmethod(read!, Tuple{IO, AbstractArray}) &&
function Base.read!(s::IO, a::AbstractArray{T}) where T
if isbitstype(T) && (a isa Array || a isa Base.FastContiguousSubArray{T,<:Any,<:Array{T}})
GC.@preserve a unsafe_read(s, pointer(a), sizeof(a))
else
for i in eachindex(a)
a[i] = read(s, T)
end
end
return a
end

function can_symlink(dir::AbstractString)
# guaranteed to be an empty directory
link_path = joinpath(dir, "link")
Expand Down
34 changes: 15 additions & 19 deletions src/extract.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import SHA

@static if VERSION < v"1.4.0-DEV"
view_read!(io, buf::SubArray{UInt8}) = readbytes!(io, buf, sizeof(buf))
else
view_read!(io, buf::SubArray{UInt8}) = read!(io, buf)
end

function iterate_headers(
callback::Function,
tar::IO;
Expand Down Expand Up @@ -301,10 +295,10 @@ function git_file_hash(
# where you write data to an IO object and it maintains a hash
padded_size = round_up(size)
while padded_size > 0
max_read_len = min(padded_size, length(buf))
max_read_len = Int(min(padded_size, length(buf)))
read_len = readbytes!(tar, buf, max_read_len)
read_len < max_read_len && eof(tar) && throw(EOFError())
nonpadded_view = view(buf, 1:min(read_len, size))
nonpadded_view = view(buf, 1:Int(min(read_len, size)))
SHA.update!(ctx, nonpadded_view)
size -= length(nonpadded_view)
padded_size -= read_len
Expand Down Expand Up @@ -433,7 +427,7 @@ function read_header(
metadata = copy(globals)
while true
if hdr.type in (:g, :x) # POSIX extended headers
let hdr=hdr #15276
let hdr=hdr # https://github.com/JuliaLang/julia/issues/15276
read_extended_metadata(io, hdr.size, buf=buf, tee=tee) do key, val
if key in ("size", "path", "linkpath")
if hdr.type == :g
Expand Down Expand Up @@ -480,6 +474,9 @@ function read_extended_metadata(
buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
tee::IO = devnull,
)
size > typemax(Int32) &&
error("read_extended_metadata called with too large size: $size")
size = Int(size)
data = read_data(io, size=size, buf=buf, tee=tee)
malformed() = error("malformed extended header metadata: $(repr(String(data)))")
i = 0
Expand Down Expand Up @@ -553,9 +550,7 @@ function read_standard_header(
buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
tee::IO = devnull,
)
header_view = view(buf, 1:512)
view_read!(io, header_view)
write(tee, header_view)
header_view = read_data(io, size=512, buf=buf, tee=tee)
if all(iszero, header_view)
if tee !== devnull
while !eof(io)
Expand Down Expand Up @@ -637,11 +632,11 @@ function read_data(
)::Nothing
padded_size = round_up(size)
while padded_size > 0
max_read_len = min(padded_size, length(buf))
max_read_len = Int(min(padded_size, length(buf)))
read_len = readbytes!(tar, buf, max_read_len)
write(tee, view(buf, 1:read_len))
read_len < max_read_len && eof(tar) && throw(EOFError())
size -= write(file, view(buf, 1:min(read_len, size)))
size -= write(file, view(buf, 1:Int(min(read_len, size))))
padded_size -= read_len
end
@assert size == padded_size == 0
Expand All @@ -668,10 +663,11 @@ function read_data(
buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
tee::IO = devnull,
)::AbstractVector{UInt8}
n = round_up(size)
length(buf) < n && resize!(buf, nextpow(2, n))
r = readbytes!(tar, buf, n)
write(tee, view(buf, 1:r))
r < n && throw(EOFError())
padded_size = round_up(size)
padded_size > typemax(Int32) &&
throw(ArgumentError("read_data(tar; size) called with too large size: $size"))
padded_size = Int(padded_size)
length(buf) < padded_size && resize!(buf, nextpow(2, padded_size))
write(tee, read!(tar, view(buf, 1:padded_size)))
return view(buf, 1:size)
end
1 change: 0 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,6 @@ end
end
end

VERSION v"1.4" && # no EOFError due to 1.3 bug
@testset "header truncated" begin
for h in [511, 256, 1]
open(tarball, "a") do io
Expand Down

0 comments on commit fdeec31

Please sign in to comment.