When a refcount(9) counter reaches INT_MAX+1 or underflows, maintain the
counter at a fixed value, causing refcount_release() to always return
false. This helps mitigate exploitation of refcount leaks. The trick
relies on reserving the range [INT_MIN, -1] of refcount values for
special purposes.
This increases kernel text size of amd64/GENERIC-NODEBUG by ~12KB.
Refcount acquisition now uses fetchadd instead of plain add. On amd64,
add can take an immediate operand, while xadd does not, so we pay five
bytes to load the immediate value into a register. The check of the
result uses test %eax,%eax followed by a js to the saturation
handler. Refcount release already uses fetchadd; now it checks for
underflow and saturation with a test %eax,%eax followed by a jle.
Once a refcount reaches the saturation point, subsequent operations on
the counter simply set the counter to the midpoint of the range
[INT_MIN, 0]. This is to help ensure that subsequent operations on the
counter keep it in the saturation range. There is a small race
however: if a thread increments a counter to INT_MAX+1, a subsequent
release will result in a call to refcount_update_saturated(). If the
releasing thread is preempted before setting the counter value to
(3U << 30), and 2^31 consecutive releases occur while the thread is
preempted, a use-after-free may occur. I think this possibility is
sufficiently small so as not to be a concern; we tolerate at least one
similar edge case in the seqc code.