Revision 9e8fe688c5e32bde3ab48bb71f9d4ab45ef272ee authored by Valentin Churavy on 24 November 2023, 22:35:51 UTC, committed by GitHub on 24 November 2023, 22:35:51 UTC
Fixes #52213

Overwritting methods during cache creation is currently not something
that the system
can support and can lead to surprising, counter-intuitive and fatal
errors.

In 1.10 we turned it from a warning to a strong error, with this PR it
remains
a strong error, but the precompilation system recognizes it and
essentially sets `__precompile__(false)`
for this module and all modules that depend on it.

Before:
```
julia> using OverwriteMethodError
[ Info: Precompiling OverwriteMethodError [top-level]
WARNING: Method definition +(Bool, Bool) in module Base at bool.jl:166 overwritten in module OverwriteMethodError at /home/vchuravy/src/julia2/OverwriteMethodError.jl:2.
ERROR: LoadError: Method overwriting is not permitted during Module precompile.
Stacktrace:
 [1] top-level scope
   @ ~/src/julia2/OverwriteMethodError.jl:2
 [2] include
   @ Base ./Base.jl:489 [inlined]
 [3] include_package_for_output(pkg::Base.PkgId, input::String, depot_path::Vector{String}, dl_load_path::Vector{String}, load_path::Vector{String}, concrete_deps::Vector{Pair{Base.PkgId, UInt128}}, source::Nothing)
   @ Base ./loading.jl:2216
 [4] top-level scope
   @ stdin:3
in expression starting at /home/vchuravy/src/julia2/OverwriteMethodError.jl:1
in expression starting at stdin:3
ERROR: Failed to precompile OverwriteMethodError [top-level] to "/home/vchuravy/.julia/compiled/v1.10/jl_guiuCQ".
Stacktrace:
  [1] error(s::String)
    @ Base ./error.jl:35
  [2] compilecache(pkg::Base.PkgId, path::String, internal_stderr::IO, internal_stdout::IO, keep_loaded_modules::Bool)
    @ Base ./loading.jl:2462
  [3] compilecache
    @ Base ./loading.jl:2334 [inlined]
  [4] (::Base.var"#968#969"{Base.PkgId})()
    @ Base ./loading.jl:1968
  [5] mkpidlock(f::Base.var"#968#969"{Base.PkgId}, at::String, pid::Int32; kwopts::@Kwargs{stale_age::Int64, wait::Bool})
    @ FileWatching.Pidfile ~/.julia/juliaup/julia-1.10.0-rc1+0.x64.linux.gnu/share/julia/stdlib/v1.10/FileWatching/src/pidfile.jl:93
  [6] #mkpidlock#6
    @ FileWatching.Pidfile ~/.julia/juliaup/julia-1.10.0-rc1+0.x64.linux.gnu/share/julia/stdlib/v1.10/FileWatching/src/pidfile.jl:88 [inlined]
  [7] trymkpidlock(::Function, ::Vararg{Any}; kwargs::@Kwargs{stale_age::Int64})
    @ FileWatching.Pidfile ~/.julia/juliaup/julia-1.10.0-rc1+0.x64.linux.gnu/share/julia/stdlib/v1.10/FileWatching/src/pidfile.jl:111
  [8] #invokelatest#2
    @ Base ./essentials.jl:889 [inlined]
  [9] invokelatest
    @ Base ./essentials.jl:884 [inlined]
 [10] maybe_cachefile_lock(f::Base.var"#968#969"{Base.PkgId}, pkg::Base.PkgId, srcpath::String; stale_age::Int64)
    @ Base ./loading.jl:2977
 [11] maybe_cachefile_lock
    @ Base ./loading.jl:2974 [inlined]
 [12] _require(pkg::Base.PkgId, env::String)
    @ Base ./loading.jl:1964
 [13] __require_prelocked(uuidkey::Base.PkgId, env::String)
    @ Base ./loading.jl:1806
 [14] #invoke_in_world#3
    @ Base ./essentials.jl:921 [inlined]
 [15] invoke_in_world
    @ Base ./essentials.jl:918 [inlined]
 [16] _require_prelocked(uuidkey::Base.PkgId, env::String)
    @ Base ./loading.jl:1797
 [17] macro expansion
    @ Base ./loading.jl:1784 [inlined]
 [18] macro expansion
    @ Base ./lock.jl:267 [inlined]
 [19] __require(into::Module, mod::Symbol)
    @ Base ./loading.jl:1747
 [20] #invoke_in_world#3
    @ Base ./essentials.jl:921 [inlined]
 [21] invoke_in_world
    @ Base ./essentials.jl:918 [inlined]
 [22] require(into::Module, mod::Symbol)
    @ Base ./loading.jl:1740
```

After:
```
julia> using OverwriteMethodError
┌ Info: Precompiling OverwriteMethodError [top-level]
└ @ Base loading.jl:2486
WARNING: Method definition +(Bool, Bool) in module Base at bool.jl:166 overwritten in module OverwriteMethodError at /home/vchuravy/src/julia2/OverwriteMethodError.jl:2.
ERROR: Method overwriting is not permitted during Module precompile.
┌ Info: Skipping precompilation since __precompile__(false). Importing OverwriteMethodError [top-level].
└ @ Base loading.jl:2084
```

---------

Co-authored-by: Kristoffer Carlsson <kcarlsson89@gmail.com>
1 parent 6e23543
Raw File
secretbuffer.jl
# This file is a part of Julia. License is MIT: https://julialang.org/license

using Base: SecretBuffer, SecretBuffer!, shred!, isshredded
using Test, Random

@testset "SecretBuffer" begin
    @testset "original unmodified" begin
        str = "foobar"
        secret = SecretBuffer(str)

        @test read(secret, String) == str
        seekstart(secret)

        @test peek(secret) == 0x66

        @test shred!(secret) === secret
        @test read(secret, String) == ""
        @test str == "foobar"
    end

    @testset "finalizer" begin
        v = UInt8[1, 2]
        secret_a = SecretBuffer!(v)
        secret_b = secret_a

        secret_a = nothing
        GC.gc()

        @test all(iszero, v)
        @test !isshredded(secret_b)

        # TODO: ideally we'd test that the finalizer warns from GC.gc(), but that is harder
        @test_logs (:warn, r".*SecretBuffer was `shred!`ed by the GC.*") begin
            finalize(secret_b)
            # Allow the async task which produces the SecretBuffer warning to run.
            # This is a hack, but we don't have a way to get a handle to that
            # task in order to `wait` on it.
            for i=1:1000
                yield()
            end
        end
        @test isshredded(secret_b)
        secret_b = nothing
        GC.gc()
    end

    @testset "initializers" begin
        s1 = SecretBuffer("setec astronomy")
        data2 = [0x73, 0x65, 0x74, 0x65, 0x63, 0x20, 0x61, 0x73, 0x74, 0x72, 0x6f, 0x6e, 0x6f, 0x6d, 0x79]
        s2 = SecretBuffer!(data2)
        @test all(==(0x0), data2)
        @test s1 == s2

        ptr3 = Base.unsafe_convert(Cstring, "setec astronomy")
        s3 = Base.unsafe_SecretBuffer!(ptr3)
        @test Base.unsafe_string(ptr3) == ""
        @test s1 == s2 == s3

        s4 = SecretBuffer(split("setec astronomy", " ")[1]) # initialize from SubString
        s5 = convert(SecretBuffer, split("setec astronomy", " ")[1]) # initialize from SubString
        @test s4 == s5
        shred!(s1); shred!(s2); shred!(s3); shred!(s4), shred!(s5);
    end
    @testset "basics" begin
        s1 = SecretBuffer("setec astronomy")
        @test sprint(show, s1) == "SecretBuffer(\"*******\")"
        @test !isempty(s1)
        shred!(s1)
        s2 = SecretBuffer!([0x00])
        @test_throws ArgumentError Base.cconvert(Cstring, s2)
        shred!(s2)
    end
    @testset "write! past data size" begin
        sb = SecretBuffer(sizehint=2)
        # data vector will not grow
        bits = typemax(UInt8)
        write(sb, bits)
        write(sb, bits)
        # data vector must grow
        write(sb, bits)
        seek(sb, 0)
        @test read(sb, String) == "\xff\xff\xff"
        shred!(sb)
    end
    @testset "bytes available" begin
        sb = SecretBuffer("secret")
        @test bytesavailable(sb) == sb.size
        seek(sb, 3)
        @test bytesavailable(sb) == sb.size - 3
        seekend(sb)
        @test bytesavailable(sb) == 0
        shred!(sb)
    end
    @testset "testing the skip function" begin
        sb = SecretBuffer("computer")
        skip(sb, 2)
        @test position(sb) == 2
        seek(sb, 0)
        @test position(sb) == 0
        skip(sb, sb.size)
        @test position(sb) == sb.size
        shred!(sb)
    end
    @testset "seekend" begin
        sb = SecretBuffer("hello")
        seekend(sb)
        @test read(sb, String) == ""
        shred!(sb)
    end
    @testset "position" begin
        sb = SecretBuffer("Julia")
        initial_pos = (position(sb))
        seek(sb,2)
        mid_pos = position(sb)
        seekend(sb)
        @test initial_pos == 0 && mid_pos == 2 && position(sb)==sb.size
        shred!(sb)
    end
    @testset "hashing secret buffers" begin
        sb1 = SecretBuffer("hello")
        sb2 = SecretBuffer("juliaisawesome")
        @test hash(sb1, UInt(5)) === hash(sb2, UInt(5))
        shred!(sb1); shred!(sb2)
    end
    @testset "NULL initialization" begin
        null_ptr = Cstring(C_NULL)
        @test_throws ArgumentError Base.unsafe_SecretBuffer!(null_ptr)
        null_ptr = Ptr{UInt8}(C_NULL)
        @test_throws ArgumentError Base.unsafe_SecretBuffer!(null_ptr)
        @test_throws ArgumentError Base.unsafe_SecretBuffer!(null_ptr, 0)
    end

    @testset "copiers" begin
        s1 = SecretBuffer()
        write(s1, "hello world")
        seekstart(s1)

        s2 = copy(s1)
        write(s2, 'c')
        seekstart(s2)

        @test read(s1) == codeunits("hello world")
        @test read(s2) == codeunits("cello world")

        shred!(s1)
        @test isshredded(s1)
        @test !isshredded(s2)
        shred!(s2)

        # Copying into a bigger destination
        s3 = SecretBuffer()
        s4 = SecretBuffer()
        write(s3, "original")
        seekstart(s3)
        write(s4, randstring(1234))
        s4data = s4.data
        copy!(s4, s3)
        @test s3.data == s4.data
        @test read(s3) == read(s4) == codeunits("original")
        @test all(iszero, s4data)
        shred!(s3); shred!(s4)

        # Copying into a smaller destination
        s5 = SecretBuffer()
        s6 = SecretBuffer("sekrit")
        str = randstring(321)
        write(s5, str)
        seekstart(s5)
        copy!(s6, s5)
        @test read(s5) == read(s6) == codeunits(str)
        shred!(s5); shred!(s6)
    end
end
back to top