Skip to content

The ::Real fallback for some special functions is vulnerable to infinite recursion #57789

@nsajko

Description

@nsajko

This is the code:

julia/base/math.jl

Lines 1539 to 1549 in 04922e7

for f in (:sin, :cos, :tan, :asin, :atan, :acos,
:sinh, :cosh, :tanh, :asinh, :acosh, :atanh,
:exp, :exp2, :exp10, :expm1, :log, :log2, :log10, :log1p,
:exponent, :sqrt, :cbrt, :sinpi, :cospi, :sincospi, :tanpi)
@eval function ($f)(x::Real)
xf = float(x)
x === xf && throw(MethodError($f, (x,)))
return ($f)(xf)
end
@eval $(f)(::Missing) = missing
end

This line is meant to guard against stack overflow:

x === xf && throw(MethodError($f, (x,)))

... however I think that's only correct for isbits(x). Assuming x is mutable, float(x) might perhaps return xf such that (x == xf) && (x !== xf). I'd change the line to:

(xf isa typeof(x)) && throw(MethodError($f, (x,)))

A reproducer is:

julia> mutable struct T <: Real end

julia> Base.float(::T) = T()

julia> sinpi(T())
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
ERROR: StackOverflowError:
Stacktrace:
 [1] sinpi(x::T)
   @ Base.Math ./math.jl:1543
 [2] sinpi(x::T) (repeats 79983 times)
   @ Base.Math ./math.jl:1546

This is somewhat realistic given that some of the thick Number-like types in the ecosystem, such as intervals of real numbers, or Dual from ForwardDiff, subtype Real even though such an object is not mathematically a real number.

Metadata

Metadata

Assignees

Labels

mathsMathematical functions

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions