So, not only will a trickle DoS other clients, each byte will also force an O(n) traversal of $buf (burning CPU). Granted, buf is only 1000 bytes, but that's not great.
It looks like a request with no space could force you to walk (`repne scasb`) through invalid memory after $buf. Also maybe corrupt it (unescape_request_path).
It will also fail to correctly parse HTTP/0.9 (not a big deal, but part of spec). The parsing code ignores the existence of verbs other than GET. (Doesn't check that the verb is GET either.)
We don't validate that paths start with /, we just skip that byte. Okay:
mov (path), %al
...
cmp $'/, %al
je badreq
Since valid GETs are of the form: GET /foo.txt HTTP/1.0
^-- path=buf+5
As you point out, a client close will cause SIGPIPE causing a crash (DoS).That's all I see. But I'm not an asm expert and I'm sure I've missed something.
You would think, but actually Apache managed to be vulnerable to XSS by including bits of the request URL in its error paegs, if I remember right. Last millennium, I think.
> So, not only will a trickle DoS other clients, each byte will also force an O(n) traversal of $buf (burning CPU). Granted, buf is only 1000 bytes, but that's not great.
Hmm, while I hadn't thought about that, and I should have, I think that's probably okay; basically you're saying that you can get the machine to burn up to, say, 2048 cycles by sending it a small TCP packet. Which means that a 4-core 2GHz server machine can't handle more than about four million packets per second (well, one million until I parallelize), which is about 85 megabytes per second, or 680 megabits per second. There are probably other bottlenecks in the code, the kernel, or your data center that will kick in first. It's probably more effective to DoS the server by just requesting files from it.
> It looks like a request with no space could force you to walk (`repne scasb`) through invalid memory after $buf.
It's possible I could have gotten this wrong, but I did try to limit the number of bytes it would scan to the bytes that it had actually read, by doing
mov (bufp), %ecx
before the repne scasb. Did I screw that up?> HTTP/0.9 ...verbs other than GET.
Yes, those are unimplemented features, and you're right that their lack makes the server behave incorrectly; hopefully they don't result in security bugs. I think they don't matter in practice, since nobody sends HTTP/0.9 requests or HEAD requests, except by hand, do they?
> We don't validate that paths start with /, we just skip that byte.
Right. And the $'/ check below is to keep you from saying
GET //etc/passwd HTTP/1.0
and getting /etc/passwd. In case that matters in 2013.Thank you very much for looking over it!
I know it does this by a given flag, but in some tests I have seen some HEADs between my GETs. I haven't used ab for long time, so don't quote me on that. Have u tried httpress[1] as a benchmark tool?
How about a simple check against the first byte equals G (DEC 71) if it is a GET? Shouldn't be that expensive, I think.
Thanks for creating it.
Ah, it's possible repne scasb halts when ecx drops to zero (that would explain some of the string length asm code I found when I googled it). I'm not very familiar with x86 mneumonics apart from the basics ('mov').
Why is this more than 4 million bytes per second (4 MB/s)? A packet can contain a single byte.