From 74615de950c5c95c6e3a85a23719bc6f2115ea76 Mon Sep 17 00:00:00 2001 From: Nathan Zimmerberg <39104088+nhz2@users.noreply.github.com> Date: Wed, 17 Apr 2024 05:03:58 -0400 Subject: [PATCH] Fix integer overflow in `skip(s::IOBuffer, typemax(Int64))` (#54070) Fixes #53908 by clamping before doing addition. This also fixes an issue with negative skips if `io.offset` isn't zero. I am assuming that `io.size+1` cannot overflow. (cherry picked from commit 306124c0be088836bf962fd7aa6be6bef738ebd9) --- base/iobuffer.jl | 24 +++++++++++++++++------- test/iobuffer.jl | 25 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/base/iobuffer.jl b/base/iobuffer.jl index 109eaa96a3de4..6b8d694ec03fb 100644 --- a/base/iobuffer.jl +++ b/base/iobuffer.jl @@ -260,22 +260,32 @@ bytesavailable(io::GenericIOBuffer) = io.size - io.ptr + 1 position(io::GenericIOBuffer) = io.ptr - io.offset - 1 function skip(io::GenericIOBuffer, n::Integer) - seekto = io.ptr + n - n < 0 && return seek(io, seekto-1) # Does error checking - io.ptr = min(seekto, io.size+1) - return io + skip(io, clamp(n, Int)) +end +function skip(io::GenericIOBuffer, n::Int) + if signbit(n) + seekto = clamp(widen(position(io)) + widen(n), Int) + seek(io, seekto) # Does error checking + else + n_max = io.size + 1 - io.ptr + io.ptr += min(n, n_max) + io + end end function seek(io::GenericIOBuffer, n::Integer) + seek(io, clamp(n, Int)) +end +function seek(io::GenericIOBuffer, n::Int) if !io.seekable ismarked(io) || throw(ArgumentError("seek failed, IOBuffer is not seekable and is not marked")) n == io.mark || throw(ArgumentError("seek failed, IOBuffer is not seekable and n != mark")) end # TODO: REPL.jl relies on the fact that this does not throw (by seeking past the beginning or end # of an GenericIOBuffer), so that would need to be fixed in order to throw an error here - #(n < 0 || n > io.size) && throw(ArgumentError("Attempted to seek outside IOBuffer boundaries.")) - #io.ptr = n+1 - io.ptr = min(max(0, n)+io.offset, io.size)+1 + #(n < 0 || n > io.size - io.offset) && throw(ArgumentError("Attempted to seek outside IOBuffer boundaries.")) + #io.ptr = n + io.offset + 1 + io.ptr = clamp(n, 0, io.size - io.offset) + io.offset + 1 return io end diff --git a/test/iobuffer.jl b/test/iobuffer.jl index 6151f90f297ee..8c49b7ec83327 100644 --- a/test/iobuffer.jl +++ b/test/iobuffer.jl @@ -196,6 +196,31 @@ end @test position(skip(io, -3)) == 0 end +@testset "issue #53908" begin + @testset "offset $first" for first in (false, true) + b = collect(0x01:0x05) + sizehint!(b, 100; first) # make offset non zero + io = IOBuffer(b) + @test position(skip(io, 4)) == 4 + @test position(skip(io, typemax(Int))) == 5 + @test position(skip(io, typemax(Int128))) == 5 + @test position(skip(io, typemax(Int32))) == 5 + @test position(skip(io, typemin(Int))) == 0 + @test position(skip(io, typemin(Int128))) == 0 + @test position(skip(io, typemin(Int32))) == 0 + @test position(skip(io, 4)) == 4 + @test position(skip(io, -2)) == 2 + @test position(skip(io, -2)) == 0 + @test position(seek(io, -2)) == 0 + @test position(seek(io, typemax(Int))) == 5 + @test position(seek(io, typemax(Int128))) == 5 + @test position(seek(io, typemax(Int32))) == 5 + @test position(seek(io, typemin(Int))) == 0 + @test position(seek(io, typemin(Int128))) == 0 + @test position(seek(io, typemin(Int32))) == 0 + end +end + @testset "pr #11554" begin io = IOBuffer(SubString("***αhelloworldω***", 4, 16)) io2 = IOBuffer(Vector{UInt8}(b"goodnightmoon"), read=true, write=true)