Yesterday on discord, fri3d(&stuff); kindly introduced me to the concepts of pointer provenance (the origin associated with a pointer) and leaking provenance (losing the origin, e.g. due to ptr → int → ptr conversions).
I needed to implement slice intersection. My original code (actually, already slightly improved) was the following:
pub fn intersection1(T: type, a: []const T, b: []const T) ?[]const T {
const start = @max(@intFromPtr(a.ptr), @intFromPtr(b.ptr));
const end = @min(@intFromPtr(a.ptr + a.len), @intFromPtr(b.ptr + b.len));
if (end <= start) return null;
const ptr: [*]T = @ptrFromInt(start);
return ptr[0 .. end - start];
}
fri3d(&stuff); pointed out that this implementation “leaks” (loses) pointer provenance. He suggested an alternative approach using [*c]
trick, which allows pointer comparison (which isn’t allowed for normal single/many-item pointers):
fn intersection2(T: type, a: []const T, b: []const T) ?[]const T {
const start = if (@as([*c]const T, a.ptr) > @as([*c]const T, b.ptr)) a.ptr else b.ptr;
const a_end = a.ptr + a.len;
const b_end = b.ptr + b.len;
const end = if (@as([*c]const T, a_end) < @as([*c]const T, b_end)) a_end else b_end;
return if (@as([*c]const T, start) < @as([*c]const T, end))
start[0 .. end - start]
else
null;
}
I then suggested: Why not just use @intFromPtr
if we’re essentially just comparing two numbers? So, I rewrote his version as follows:
fn intersection3(T: type, a: []const T, b: []const T) ?[]const T {
const start = if (@intFromPtr(a.ptr) > @intFromPtr(b.ptr)) a.ptr else b.ptr;
const a_end = a.ptr + a.len;
const b_end = b.ptr + b.len;
const end = if (@intFromPtr(a_end) < @intFromPtr(b_end)) a_end else b_end;
return if (@intFromPtr(start) < @intFromPtr(end))
start[0 .. end - start]
else
null;
}
He argued that this approach still loses provenance. I replied that I thought provenance was only lost at the explicit ptr → int → ptr conversion step:
const ptr: [*]T = @ptrFromInt(start);
return ptr[0 .. end - start];
But he responded:
The leakage at the comparison point might affect things far after it (or, it might not …
That was essentially the end of our discussion. However, I still can’t fully wrap my head around whether, assuming there is provenance tracking support (which is highly doubted), provenance is actually lost at the comparison step. What do you think?