diff --git a/ChangeLog b/ChangeLog index 97a87b34e..7b278b224 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2015-03-10 Niels Grewe + + * 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 * Source/NSFileHandle.m: ([-sslHandshakeEstablished:outgoing:]) diff --git a/Source/GSTimSort.m b/Source/GSTimSort.m index 836fb90b3..30f251b07 100644 --- a/Source/GSTimSort.m +++ b/Source/GSTimSort.m @@ -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); } }