Bug 17363 – @safety hole due to $ caching in slice expressions

Status
RESOLVED
Resolution
INVALID
Severity
normal
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2017-05-01T18:36:41Z
Last change time
2018-03-11T05:16:10Z
Keywords
safe
Assigned to
No Owner
Creator
kinke
See also
https://issues.dlang.org/show_bug.cgi?id=17364

Comments

Comment #0 by kinke — 2017-05-01T18:36:41Z
When loading and caching $ once for a slice expression before evaluating the bounds expressions, it isn't updated due to potential side effects on the slicee when evaluating upper and lower bounds expressions, leading to invalid bounds checks and memory corruption potential in @safe code: ``` @safe: int[] globalArray; int getLowerBound() { globalArray = [ 666 ]; return 0; } void main() { globalArray = new int[256]; auto r = globalArray[getLowerBound() .. $]; assert(r[0] == 666); assert(r.length == 256); // BUG, should be 1 r[] = 123; // oops } ``` GDC and LDC don't cache $ and thus don't suffer from this issue.
Comment #1 by dfj1esp02 — 2017-05-03T09:25:35Z
The slice should be loaded before evaluation of slice arguments, then length is safe to cache, see issue 17364.
Comment #2 by kinke — 2017-05-03T19:21:20Z
> The slice should be loaded before evaluation of slice arguments, then length > is safe to cache, see issue 17364. Your argument for loading & caching length and pointer before evaluating the bounds expressions being? I mean it's perfectly safe and IMHO less awkward the other way around, loading the current length for each $ and loading the base pointer after evaluating the bounds expressions, with full side effects visibility for lvalue slicees. People do come up with (arguably bad) code where this matters: https://github.com/ldc-developers/ldc/issues/1433
Comment #3 by dfj1esp02 — 2017-05-04T09:36:44Z
Because you can't resolve $ before loading the array, therefore loading before resolution is the only way to do it.
Comment #4 by bugzilla — 2018-03-11T00:47:30Z
The only safe solution is for s[a..b] is to evaluate a and b first, then s. Then the slice will be array bounds checked safely if it got resized. (Currently, s is evaluated first.)
Comment #5 by bugzilla — 2018-03-11T05:16:10Z
I don't think there is a solution to this problem. Consider: a()[b($)..c($)] where a, b, c are functions with side effects that change the length of the array, and the calls to b and c depend on the $, the length of the array. Each function can only be called once, because they have side effects. In order to get $, a() has to be called. But b($) and c($) both change the value of $, so no ordering is correct. However, it is not memory unsafe, because reallocating the array is memory safe even if other references to that array remain. I'm marking this as INVALID because 1) there is no solution, cache or no cache, and 2) it is not memory unsafe. It's just buggy code.