Skip to content

Commit e715c9f

Browse files
c42fstaticfloat
authored andcommitted
Version check for serialized data headers (#35376)
Check that the serialization data format is compatible before attempting to read a serialized stream. New versions of Serialization are assumed to be able to read old serialized data, but attempting to read newer data with an older version of Serialization will fail with an error.
1 parent e5a1326 commit e715c9f

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

stdlib/Serialization/src/Serialization.jl

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,38 @@ function writeheader(s::AbstractSerializer)
685685
nothing
686686
end
687687

688+
function readheader(s::AbstractSerializer)
689+
# Tag already read
690+
io = s.io
691+
m1 = read(io, UInt8)
692+
m2 = read(io, UInt8)
693+
if m1 != UInt8('J') || m2 != UInt8('L')
694+
error("Unsupported serialization format (got header magic bytes $m1 $m2)")
695+
end
696+
version = read(io, UInt8)
697+
flags = read(io, UInt8)
698+
reserved1 = read(io, UInt8)
699+
reserved2 = read(io, UInt8)
700+
reserved3 = read(io, UInt8)
701+
endianflag = flags & 0x3
702+
wordflag = (flags >> 2) & 0x3
703+
wordsize = wordflag == 0 ? 4 :
704+
wordflag == 1 ? 8 :
705+
error("Unknown word size flag in header")
706+
endian_bom = endianflag == 0 ? 0x04030201 :
707+
endianflag == 1 ? 0x01020304 :
708+
error("Unknown endianness flag in header")
709+
# Check protocol compatibility.
710+
endian_bom == ENDIAN_BOM || error("Serialized byte order mismatch ($(repr(endian_bom)))")
711+
# We don't check wordsize == sizeof(Int) here, as Int is encoded concretely
712+
# as Int32 or Int64, which should be enough to correctly deserialize a range
713+
# of data structures between Julia versions.
714+
if version > ser_version
715+
error("""Cannot read stream serialized with a newer version of Julia.
716+
Got data version $version > current version $ser_version""")
717+
end
718+
end
719+
688720
"""
689721
serialize(stream::IO, value)
690722
@@ -843,9 +875,7 @@ function handle_deserialize(s::AbstractSerializer, b::Int32)
843875
elseif b == LONGSYMBOL_TAG
844876
return deserialize_symbol(s, Int(read(s.io, Int32)::Int32))
845877
elseif b == HEADER_TAG
846-
for _ = 1:7
847-
read(s.io, UInt8)
848-
end
878+
readheader(s)
849879
return deserialize(s)
850880
elseif b == INT8_TAG
851881
return read(s.io, Int8)

stdlib/Serialization/test/runtests.jl

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -529,8 +529,8 @@ let x = T20324[T20324(1) for i = 1:2]
529529
@test y == x
530530
end
531531

532-
# serializer header
533-
let io = IOBuffer()
532+
@testset "serializer header" begin
533+
io = IOBuffer()
534534
serialize(io, ())
535535
seekstart(io)
536536
b = read(io)
@@ -541,6 +541,25 @@ let io = IOBuffer()
541541
@test ((b[5] & 0xc)>>2) == (sizeof(Int) == 8)
542542
@test (b[5] & 0xf0) == 0
543543
@test all(b[6:8] .== 0)
544+
545+
# Detection of incompatible binary serializations
546+
function corrupt_header(bytes, offset, val)
547+
b = copy(bytes)
548+
b[offset] = val
549+
IOBuffer(b)
550+
end
551+
@test_throws(
552+
ErrorException("""Cannot read stream serialized with a newer version of Julia.
553+
Got data version 255 > current version $(Serialization.ser_version)"""),
554+
deserialize(corrupt_header(b, 4, 0xff)))
555+
@test_throws(ErrorException("Unknown word size flag in header"),
556+
deserialize(corrupt_header(b, 5, 2<<2)))
557+
@test_throws(ErrorException("Unknown endianness flag in header"),
558+
deserialize(corrupt_header(b, 5, 2)))
559+
other_wordsize = sizeof(Int) == 8 ? 4 : 8
560+
other_endianness = bswap(ENDIAN_BOM)
561+
@test_throws(ErrorException("Serialized byte order mismatch ($(repr(other_endianness)))"),
562+
deserialize(corrupt_header(b, 5, UInt8(ENDIAN_BOM != 0x01020304))))
544563
end
545564

546565
# issue #26979

0 commit comments

Comments
 (0)