Bug 22359 – joiner over an empty forward range object liable to segfault
Status
RESOLVED
Resolution
FIXED
Severity
normal
Priority
P1
Component
phobos
Product
D
Version
D2
Platform
x86_64
OS
Linux
Creation time
2021-10-06T01:51:05Z
Last change time
2021-10-09T09:17:59Z
Keywords
pull
Assigned to
No Owner
Creator
Adam D. Ruppe
Comments
Comment #0 by destructionator — 2021-10-06T01:51:05Z
There's some old bugs from a decade ago that are apparently related but not quite the same as their test case is one item deep and this requires something two items deep. Behold:
------
import std.range;
ForwardRange!int fnFlatten(int[][] r)
{
import std.range : inputRangeObject;
import std.algorithm : map, joiner;
auto range = inputRangeObject(r);
return range.map!(a =>inputRangeObject(a)).joiner.inputRangeObject;
}
void main() {
auto f = fnFlatten([[]]);
f.save();
}
------
In the `private enum popFrontEmptyElement` of joiner, there's a `_current = typeof(_current).init;`
If _current is some kind of class, that's null. Then when you call `save` on it, it is a null this deref.
Comment #1 by destructionator — 2021-10-06T01:53:16Z
There's also a
static if (isBidirectional && hasNested!Result)
_currentBack = typeof(_currentBack).init;
Same problem.
Comment #2 by destructionator — 2021-10-06T01:55:38Z
A patched function that can fix the issue:
static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
{
@property auto save()
{
static if(is(typeof(null) : typeof(_current))) {
auto r = Result(_items.save, _current is null ? null : _current.save);
} else
auto r = Result(_items.save, _current.save);
static if (isBidirectional)
{
static if(is(typeof(null) :
typeof(_currentBack))) {
r._currentBack = _currentBack is null ? null : _currentBack.save;
} else {
r._currentBack = _currentBack.save;
}
r.reachedFinalElement = reachedFinalElement;
}
return r;
}
}
Comment #3 by dlang-bot — 2021-10-06T02:14:25Z
@adamdruppe created dlang/phobos pull request #8263 "Fix issue #22359." fixing this issue:
- Fix issue #22359.
If you pass it a range of class-based ranges, the initialization to
`typeof(_current).init` will be `null`. Calling the `save` method
on `null` will naturally be a memory violation. This generic check
will handle null without harming any other type since save of any
init value will be another init value.
https://github.com/dlang/phobos/pull/8263
Comment #4 by dlang-bot — 2021-10-09T09:17:59Z
dlang/phobos pull request #8263 "Fix issue #22359 - joiner over an empty class range liable to segfault" was merged into master:
- ff6920bd8a461393dcdb7d01edb9b62bcb9bf4d1 by Adam D. Ruppe:
Fix issue #22359 - joiner over an empty forward range object liable to segfault
If you pass it a range of class-based ranges, the initialization to
`typeof(_current).init` will be `null`. Calling the `save` method
on `null` will naturally be a memory violation. This generic check
will handle null without harming any other type since save of any
init value will be another init value.
https://github.com/dlang/phobos/pull/8263