Regarding Rc safety and performance

I've just read this paper, "Towards Linux Kernel Memory Safety":

It describes a reference counting scheme for the Linux Kernel:

The main challenge in designing the refcount_t API was how to deal with an event that would otherwise cause the counter to overflow. One approach would be to simply ignore the event, such that the counter remains at its maximum value. However, this means that the number of references to the object will be greater than the maximum counter value. If the counter is subsequently decremented, it will reach zero and the object will be freed before all references have been released (i.e., leading to a use-after-free error). To overcome this challenge, the refcount_t API instead saturates the counter, such that it remains at its maximum value, even if there are subsequent increment or decrement events.<

I've seen this from Rust std Rc:

fn inc_strong(&self) {
    self.inner().strong.set(self.strong().checked_add(1).unwrap_or_else(|| unsafe { abort() }));
}

I am not expert about such matters, but is it a good idea to handle the counter with sticking saturation as described in the article?

So intentionally leak the memory and sweep the issue under the rug? I'm not sure that should be the default behavior; presumably to reach the point where you're overflowing the counter, something horribly wrong has occurred. I don't think the default behavior should be to just leak (that may have other unintended consequences since the Rc might be holding on to something that other code expects to be released at some point in order to make forward progress).

IMO whatever runs quicker. Since I agree with

no real program should ever experience this.

On the other hand leaking is safe, and use after free is not.

Right. Rc aborts once overflow is detected so I think that’s better than cementing the refcount forever and leaking. Or did I misunderstand?

Saturation is not safe, since these counts will be decremented at some point, and saturated value will be decremented to 0 sooner than it should be.

On 32-bit having a check is probably wise. On 64-bit I would be OK just betting it never happens.

I'd be ok with abort or with cementing-leaking but not with saturation with its freeing memory to early. Leaking is considered safe. There is std::mem::forget, and even if that wasn't you could make one in safe code with Rc loops.

@kornel and @Eh2406, I think @leonardo was talking about a sticky saturation - the refcount is frozen on overflow so it’s just a leak (clone/drop no longer affect it).

3 Likes