Fix a vulnerability in the timsort algorithm where an algorithmic problem caused an

invariant to no longer hold for certain inputs, potentially leading to a read beyond
an array boundary (result in a segfault under our implementation).
See http://www.envisage-project.eu/proving-android-java-and-python-sorting-algorithm-is-broken-and-how-to-fix-it/
for an in-depth explanation of the problem. Also: ‘Yeah!’ for formal verification!


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@38391 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
thebeing 2015-03-10 11:43:03 +00:00
parent 0d742c1913
commit e5a29578ad
2 changed files with 28 additions and 23 deletions

View file

@ -1,3 +1,10 @@
2015-03-10 Niels Grewe <niels.grewe@halbordnung.de>
* Source/GSTimSort.m: Fix a DoS vulnerability discovered in the
Timsort algorithm. For information about the problem please refer to
http://www.envisage-project.eu/proving-android-java-and-python-sorting
-algorithm-is-broken-and-how-to-fix-it/
2015-03-08 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSFileHandle.m: ([-sslHandshakeEstablished:outgoing:])

View file

@ -615,38 +615,36 @@ descriptorOrComparator: (id)descriptorOrComparator
NSDebugMLLog(@"GSTimSort", @"Pushing run: %@", NSStringFromRange(r));
}
/**
* Ensure that the invariant enabling the algorithm holds for the stack.
*
* see: http://www.envisage-project.eu/proving-android-java-and-python-sorting-algorithm-is-broken-and-how-to-fix-it/#sec3
*/
- (void) suggestMerge
{
while (stackSize > 1)
{
NSInteger n = stackSize -2;
if (n > 0)
if ( (n >= 1
&& runStack[n-1].length <= (runStack[n].length
+ runStack[n+1].length)
)
|| (n >= 2
&& runStack[n-2].length <= (runStack[n].length
+ runStack[n-1].length)
)
)
{
NSUInteger topLen = runStack[n+1].length;
NSUInteger midLen = runStack[n].length;
NSUInteger botLen = runStack[n-1].length;
if (botLen <= (midLen + topLen))
if (runStack[n-1].length < runStack[n+1].length)
{
if (botLen < topLen)
{
n--;
}
GS_TIMSORT_MERGE_AT_INDEX(self, n);
}
else if (midLen <= topLen)
{
GS_TIMSORT_MERGE_AT_INDEX(self, n);
}
else
{
break;
n--;
}
}
else
{
break;
}
else if (runStack[n].length > runStack[n+1].length)
{
break; //invariant reached
}
GS_TIMSORT_MERGE_AT_INDEX(self, n);
}
}