News for hackers. Stuff that matters.
Re:Proving that Android’s, Java’s and Python’s sorting algor... by Arnt on Tuesday February 24, 2015 @08:41AM (#9100409)
Quicksort has poor worst-case behaviour, so it's easy to carry out a DoS attack on network services that use it. Language maintainers don't like restricting their library to input from friendly users, at least not when there are other algorithms that work reasonably with unfriendly input.
Re:Proving that Android’s, Java’s and Python’s sorting algor... by jonesetc on Tuesday February 24, 2015 @08:50AM (#9100457)
Isn't the malicious attack fought by just picking a random pivot?
Re:Proving that Android’s, Java’s and Python’s sorting algor... by Freaky on Tuesday February 24, 2015 @09:04AM (#9100535)
There are still inputs that will make it go quadratic, that just obscures it slightly. A proper fix is to fall back on a different sort if it looks like quicksort is doing that: https://en.wikipedia.org/wiki/Introsort
Re:Proving that Android’s, Java’s and Python’s sorting algor... by thaumasiotes on Tuesday February 24, 2015 @09:56AM (#9100828)
> There are still inputs that will make it go quadratic

Can you elaborate on this? To force quicksort into a quadratic running time, you need to ensure that each pivot splits off a bounded number of elements (e.g. no more than three, or no more than twenty million) from the rest of the list. If the pivot is being chosen at random, then it looks to me like the guarantee you'd need to make is "every single element of this list [because any of them might be chosen] is larger, and smaller, than no more than k other elements of the list". But as the size of the list grows, that condition forces it to be mostly composed of the same element repeated over and over again, which is really easy to sort, and in particular is really easy for quicksort to handle.

Re:Proving that Android’s, Java’s and Python’s sorting algor... by beagle3 on Tuesday February 24, 2015 @04:06PM (#9103521)
Depends on how you interact with the quicksort, really.

If you have control of the comparison routine, there's Doug McIlroy's classic "quicksort killer"[0]. If you have information about the state of the random number generator used to pick the pivot, then you can do the same without having to actually interact through the comparison routine. Many libraries use an LCG[1] with 32 to 64 bit state, which is trivial to reconstruct through a small sample of its output not much longer than the internal state.

I don't know about you, but requiring my sort() routine to have access to a cryptographically secure random number generator doesn't seem right; I much prefer any algorithm (e.g. HeapSort, Mergesort or TimSort) that can guarantee n\*log(n) behavior with deterministic (and especially, no secure random generator requirement!) behavior.

[0] http://www.cs.dartmouth.edu/~doug/mdmspe.pdf

[1] http://en.wikipedia.org/wiki/Linear_congruential_generator

Re:Proving that Android’s, Java’s and Python’s sorting algor... by nightcracker on Tuesday February 24, 2015 @05:39PM (#9104039)
You might be interested in my new sorting algorithm, Pattern-defeating Quicksort (pdqsort): https://github.com/orlp/pdqsort

It's fully deterministic, as fast as Quicksort in the average case, O(n) in the best case (all equal, but it scales smoothly as the number of unique elements decreases), is optimized for common inputs like ascending/descending (both O(n)) and uses a novel idea of guaranteeing O(n log n) worst case, that guarantees the worst case is no slower than three times the average case, assuming heapsort is twice as slow as Quicksort.

I'm still tweaking the final details and working on the paper, so it's not ready for a full release yet, but a sneak peek here and there is nice :)

This is a benchmark comparing pdqsort against introsort (std::sort), heap sort and timsort: http://i.imgur.com/TSvXnG5.png .

Re:Proving that Android’s, Java’s and Python’s sorting algor... by beagle3 on Wednesday February 25, 2015 @01:59AM (#9105635)
Which timsort implementation did you compare to? (your profile.cpp on github doesn't include it).

Heapsort implementation vary widely in their cache coherence. The vast majority are extremely simple but result in essentially complete incoherence. I was once able to speed a heapsort about 2x by rearranging the scan order into a cache-oblivious one (Mostly the heap building part; I'm not aware of a way to make the extraction part cache-friendy).

Heapsorts, quicksorts and bubble sorts have the ability to sort just the "top-n" and stop there (unlike mergesort), which is often useful, and a significat speed up (goes from nlog(n) to n+mlog(n) to get top m, or from n^2 to n times m for bubble sort) - I wish library routines actually provided that as part of a standard interface. Perhaps you could be the torchbearer in your publication?

Personally, I think just about every standard library except APL/J/K got sorting wrong. The primary sort operation should be "compute stable ordering permutation", with "sort this array" (basically only available operation in most libraries) being at most a shortcut when you don't care about the permutation.

Re:Proving that Android’s, Java’s and Python’s sorting algor... by nightcracker on Wednesday February 25, 2015 @02:24AM (#9105689)
I'm sorry, the profiling code for timsort wasn't included in my remote repository yet. I profiled against https://github.com/gfx/cpp-TimSort .

Also, C++ has always had the "top-n" interface called partial sort: http://en.cppreference.com/w/cpp/algorithm/partial_sort .

Re:Proving that Android’s, Java’s and Python’s sorting algor... by Freaky on Thursday February 26, 2015 @07:07AM (#9112140)
I wrote a partial quicksort for Newzbin - once I saw how simple it was to add I was surprised it wasn't more widespread: https://github.com/Freaky/pqsort
Re:Proving that Android’s, Java’s and Python’s sorting algor... by qnaal on Wednesday February 25, 2015 @03:19AM (#9105816)
>pdqsort
Re:Proving that Android’s, Java’s and Python’s sorting algor... by thaumasiotes on Tuesday February 24, 2015 @07:15PM (#9104478)
OK. But if you have control of the comparison routine, you can easily force mergesort to perform a quadratic number of comparisons by having the comparison function call itself an appropriate number of times. It feels a little unfair to call that a vulnerability in quicksort but not in mergesort.
Re:Proving that Android’s, Java’s and Python’s sorting algor... by beagle3 on Wednesday February 25, 2015 @01:39AM (#9105586)
Control comes in many flavors - e.g. you might be able to influence random element selection but not general code flow. (e.g - you might be able to set the seed before the call to quicksort).

And I only mentioned control because that's what McIlroy's example required to use as-is. But as I already stated, it is enough to know in advance the output of the (pseudo) random used to select a pivot to make it go quadratic, which is usually easy -- unless it is cryptographically secure (I've never seen qsort use one. have you?)

So, no, it is completely fair: There is no way to cause mergesort to go quadratic. However, it is quite easy to cause quicksort to go quadratic if you know how it selects it pivots (whether or not you can directly influence it during runtime or not).

Re:Proving that Android’s, Java’s and Python’s sorting algor... by nightcracker on Tuesday February 24, 2015 @10:16AM (#9100964)
See my bug report on libc++: http://llvm.org/bugs/show_bug.cgi?id=20837 .
Re:Proving that Android’s, Java’s and Python’s sorting algor... by thaumasiotes on Tuesday February 24, 2015 @10:59AM (#9101347)
But that adversary works by causing the sorting agent to run arbitrary, adversary-supplied code every time it makes a comparison. It has to do that because it invents the values in its list on the fly when it detects them being accessed by the sort (also, in order to detect them being accessed by the sort). That's not really the same thing. I mean, if you want to tie up a process, and you can already make it run arbitrary code that you supply, just give it something like while(1); .

If this adversary were forced to realize all the values it fed to the sorter before the sorter did any work, or if it were unable to supply its own code to the sorter, random pivot selection would be a defense.

edit:

I feel like pointing out that a quicksort implementation could defeat this adversary, without hurting its O(n log n) running time, by just comparing the first element of the sublist it was working with to every other element in the sublist -- and throwing away the results -- and then proceeding as normal. This is O(n) comparisons, which violates the vulnerability criterion of making only O(1) comparisons per call, but doesn't affect the big-O running time at all. What it does do, with an eye to this particular adversary, is realize all the values before doing any sorting work. It still doesn't fix the actual vulnerability the paper identifies, which is that you're running adversary-supplied code. I'm growing to feel like your bug report was frivolous.

Re:Proving that Android’s, Java’s and Python’s sorting algor... by nightcracker on Tuesday February 24, 2015 @11:34AM (#9101655)
What you fail to realize is that this attack is simply an academic proof that shows the libc++ implementation is broken and can have quadratic performance. This is a bug, because the C++ standard mandates a worst case of O(n log n).

What you also don't seem to realize is that this attack merely uses a comparison function to find the worst case. Once the worst case is found you can feed this input to any program using libc++'s std::sort, without comparison function, and trigger the worst case.

So no, this is not frivolous at all.

Re:Proving that Android’s, Java’s and Python’s sorting algor... by thaumasiotes on Tuesday February 24, 2015 @06:50PM (#9104370)
> What you also don't seem to realize is that this attack merely uses a comparison function to find the worst case. Once the worst case is found you can feed this input to any program using libc++'s std::sort, without comparison function, and trigger the worst case.

This is true iff pivots are selected deterministically. In which case, why did you post it as a response to "how can an adversary force quadratic behavior when pivots are chosen randomly?"

Re:Proving that Android’s, Java’s and Python’s sorting algor... by tveita on Tuesday February 24, 2015 @12:12PM (#9101955)
> This is a bug, because the C++ standard mandates a worst case of O(n log n).

Specifically, this is a requirement in C++11. Earlier C++ standards only require it to be average-case O(n log n).

Re:Proving that Android’s, Java’s and Python’s sorting algor... by netheril96 on Tuesday February 24, 2015 @09:10PM (#9104890)
So, another algorithmic complexity bug in libc++. I found one about `std::make_heap` before when testing one of my codes before (http://llvm.org/bugs/show_bug.cgi?id=20161). So much about the bug always being in your own code nowadays.
Re:Proving that Android’s, Java’s and Python’s sorting algor... by scythe on Tuesday February 24, 2015 @02:25PM (#9102890)
libc++ doesn't use randomized quicksort so that bug is irrelevant. It is an attack on a clever pivot selection method, but the exploit derives from the fact that the pivot isn't actually random, but deterministically pseudorandom. A truly random quicksort isn't vulnerable (since the pivots will be chosen in a different order every time).

The real problem with random quicksort is that randomness is really, really hard, and good RNGs are slow. Also, timsort is adaptive, while quicksort is not.

Re:Proving that Android’s, Java’s and Python’s sorting algor... by Robin_Message on Tuesday February 24, 2015 @09:00AM (#9100512)
Putting a cryptographically secure pseudo-random number generator into a sorting library is a bit awkward, and if it's not a good random number generator, then it's still DoS-able.
Re:Proving that Android’s, Java’s and Python’s sorting algor... by rifung on Tuesday February 24, 2015 @05:20PM (#9103935)
I don't think so.. If you implement quicksort the way I've normally seen it implemented (elements less than pivot to left, others to right), you could just give the array elements that are all the same value.

Then, you would easily get the O(n^2) runtime because your pivot would always be the left most element.

Re:Proving that Android’s, Java’s and Python’s sorting algor... by Arnt on Tuesday February 24, 2015 @09:24AM (#9100659)
Pretend you're the maintainer of OpenJDK. Do you want to go down that road or do you want to change to another algorithm? Remember that randomness can be difficult to find and that the malevolent has a copy of the JDK to use for experiments.

Is Quicksort that much better, or do you just change?

Re:Proving that Android’s, Java’s and Python’s sorting algor... by stonemetal on Tuesday February 24, 2015 @11:32AM (#9101642)
Only if you use a secure random, otherwise you are just obscuring the problem and are still vulnerable.
Re:Proving that Android’s, Java’s and Python’s sorting algor... by nightcracker on Tuesday February 24, 2015 @10:15AM (#9100957)
No, it is fought by switching to heapsort if the recursion depth exceeds 2*log2(n).
Re:Proving that Android’s, Java’s and Python’s sorting algor... by sbi on Tuesday February 24, 2015 @10:49AM (#9101244)
Heapsort has poor cache performance though. Its asymptotic complexity depends on the assumption that memory access is O(1), which is not borne out in practice.
Re:Proving that Android’s, Java’s and Python’s sorting algor... by orlp on Tuesday February 24, 2015 @11:08AM (#9101446)
This is true, but the worst case of quicksort is so rare that this does not matter for average performance.
Re:Proving that Android’s, Java’s and Python’s sorting algor... by maxlybbert on Tuesday February 24, 2015 @10:55AM (#9101305)
It doesn't have to have poor worst-case behavior: https://www.cs.princeton.edu/~rs/talks/QuicksortIsOptimal.pd... (from the author of the Algorithms textbook used at my college).
Re:Proving that Android’s, Java’s and Python’s sorting algor... by ufo on Tuesday February 24, 2015 @01:20PM (#9102445)
Correct me if I'm wrong but I got the impression that prof Sedgewick's slides were about quicksort behaviour when the input has many equal keys. That would still result in quadratic runtime against maliciously crafted input.
Re:Proving that Android’s, Java’s and Python’s sorting algor... by maxlybbert on Tuesday February 24, 2015 @03:25PM (#9103250)
I may have oversold Sedgewick's claim: he doesn't actually say that a properly implemented quicksort will never go quadradic; only that "The average number of compares per element ... is always within a constant factor of the entropy" and "No comparison-based algorithm can do better" (page 16).

I've never looked into whether a Sedgewick-approved Quicksort can avoid worst case behavior against antiquicksort ( http://www.cs.dartmouth.edu/~doug/aqsort.c , http://www.cs.dartmouth.edu/~doug/mdmspe.pdf ).

Re:Proving that Android’s, Java’s and Python’s sorting algor... by IsTom on Tuesday February 24, 2015 @01:37PM (#9102576)
You can always pick median element in O(n) time and it makes quicksort's worst case be O(nlogn)
Re:Proving that Android’s, Java’s and Python’s sorting algor... by andrewstuart2 on Tuesday February 24, 2015 @09:34AM (#9100706)
Got it. That definitely makes sense :-)