pp_accept: fix potential out-of-bounds read on oversized accept() addrlen#24434
pp_accept: fix potential out-of-bounds read on oversized accept() addrlen#24434crystarm wants to merge 1 commit into
Conversation
|
Please run |
|
When this situation arises, is the value
|
Or change the declaration of I think |
|
I am not so sure this is true. It might be for the syscall but that doesn't mean it's true for the library call. But this could easily be experimentally proven. |
from man accept on Linux: so yes, it looks like the resulting addrlen can be larger than the supplied buffer (which I wasn't aware of.) https://news.ycombinator.com/item?id=22868968 claims some systems can have much longer paths for unix sockets than sockaddr_un fits. If this is a problem, it also appears to be a problem for getpeername() and getsockname(). The main problem I can see is that it will silently truncate the address if it doesn't fit, which I don't see a simple fix for. We could loop on getpeername() if we see it truncated, but implementations vary enough I'm not sure I'd trust that.
From looking at the glibc source accept() in glibc is just the system call. HP-UX (yeah, it's old) has a similar note POSIX:
|
Thanks, I'll do it before updating the PR. |
|
@richardleach I dug a bit into this and made a few small C reproducers. I'll attach them here as perl5-24434-experiments.zip In short: yes, the returned So I don't think I agree that this does not solve silent address truncation. If the OS truncated the address into the supplied buffer, then Perl cannot recover the lost bytes by clamping. Returning an error on truncation, or trying a follow-up I also agree that I'll update the PR to clamp against the originally supplied length rather than directly against |
|
On
So I think these are two separate concerns:
Even if we later change the buffer type, the clamp is still needed defensively because the socket APIs allow the returned length to exceed the supplied buffer length. |
|
@Leont
This does not prove every libc/platform behaves the same way, but it does prove that the behaviour is observable through the normal library calls at least on glibc. On platforms that instead return the stored/truncated length, the clamp is simply a no-op. |
|
@tonycoz I also reproduced this with the library calls on Linux/glibc. With an intentionally tiny sockaddr buffer, You're right that this is not limited to
|
224da34 to
789e6bf
Compare
Updated the PR.The fix now clamps returned socket address lengths against the originally supplied buffer length, rather than against I also covered the same copy-out pattern in This remains a memory-safety fix only: it does not attempt to change Perl’s behaviour on truncated socket addresses. Validation:
Full test result: |
| #else | ||
| len = 256; | ||
| #endif | ||
| namesize = len; |
There was a problem hiding this comment.
We're C99 now, you can declare namesize here.
| if (bufsize >= 256) | ||
| bufsize = 255; | ||
| #endif | ||
| namesize = bufsize; |
There was a problem hiding this comment.
Make declaration part of the initialization.
We haven't typically gone through old code to use C99 mixed declarations and code, but updates can certainly use them.
|
LGTM except for the "written like it's 1990" bit, which is pretty minor 😄 |
789e6bf to
3e22b26
Compare
|
@tonycoz |
|
I can't help wondering: should this warn? |
Summary: This PR fixes a potential out-of-bounds read in
pp_accept(pp_sys.c).Problem:
PerlSock_accept_cloexec()(viaaccept()/accept4()) can return anaddrlenlarger than the supplied buffer size when the peer address is truncated. The previous code used the returnedlendirectly inPUSHp(namebuf, len).Fix: Clamp
lentosizeof(namebuf)before callingPUSHp.Impact: No behavior change for valid lengths. Prevents reading past
namebufwhenaddrlenis oversized.Context: Change was motivated by static analysis finding. BUFFER_OVERFLOW.PROC pp_sys.c:[2631:10].log
This set of changes does not require a perldelta entry.