r/Kotlin - An old unnoticed bug causes memory safety issue in Kotlin Native

This is the code for those who don’t want to open reddit:

open class Base<out T>
class Derive<T>(var value: T) : Base<T>()

fun unsound() {
    val d = Derive(0)
    val b: Base<Any> = d
    if (b is Derive) {
        b.value = "0123456789"
    }
    println(d.value + 2)
}

That definition has the wrong variance. It should be open class Base<T> (and not open class Base<out T>).
You’re stating that T is an out type which means you can get instances of T from Base. But in your example you’re writing to it, this is what’s wrong here.

Or, conversely, Derived is a bad implementation of Base, since it doesn’t respect its type variance.

That’s not how safety works. There are some C++ fans often say something like “C++ is actually safe. It’s all because you doing it wrong. If you blablablabla, then the program will be safe.” The problem is, safety is about that, even you do it “wrong”, the program will either not compile or throw a runtime exception instead of performing unsafe operation.

1 Like

Here’s a runnable (and EDITABLE right from the forum, try it out!) example for people that don’t wont to copy the code to another editor:

open class Base<out T>
class Derive<T>(var value: T) : Base<T>()

fun unsound() {
    val d = Derive(0)
    val b: Base<Any> = d
    if (b is Derive) {
        b.value = "0123456789"
    }
    println(d.value + 2)
}

fun main() = unsound()
1 Like

You are incorrect in your assumption that the compiler implicitly casts from Derived<Int> to Derived<Any> as a stepping stone to Base<Any>. Instead, the stepping stone is Base<Int>, ie., the casting goes Derived<Int> → Base<Int> → Base<Any>. And this is fine, and perfectly sound.

The real problem lies within the body of the if-statement. Here, we know that b both has type Derived<*> and Base<Any>. It then seems that the compiler incorrectly concludes that it must have type Derived<Any>. Instead, it should have treated Base<Any> to be equivalent to Base<out Any> and the conclusion should have been that b has type Derived<out Any> after the if statement. That would have caused the assignment to be rejected by the compiler.

3 Likes

I have edited the reddit. Thanks for the correction.