Subject: | Invalid write in XS_Socket_unpack_sockaddr_un() |
Fix added in version 2.000:
Zero(&addr+sockaddrlen, sizeof(addr)-sockaddrlen, char);
segfaults sometimes (depends on memory layout). We can reproduce it with
.25 probability by running mysql test suite (mysql-test-run script).
Simple test "unpack_sockaddr_un(pack_sockaddr_un(q{/tmp/foo})" does not
exhibit this issue.
The back-trace is:
Program terminated with signal 11, Segmentation fault.
#0 __memset_sse2 () at ../sysdeps/x86_64/memset.S:333
333 L(P0Q7): mov %rdx,-0x38(%rdi)
(gdb) bt full
#0 __memset_sse2 () at ../sysdeps/x86_64/memset.S:333
No locals.
#1 0x00007ff47a847a10 in memset (__len=56, __ch=0, __dest=0x7fff42cbe264)
at /usr/include/bits/string3.h:85
No locals.
#2 XS_Socket_unpack_sockaddr_un (my_perl=0x20b2010, cv=<optimized out>)
at Socket.xs:715
addr = {sun_family = 1,
sun_path = "/tmp/5r1KpX7RwQ/", 'x' <repeats 35 times>, '\000'
<repeats 19 times>"\200,
\212\271\002\000\000\000\000\b\000\000\000\000\000\000\000\320B\216\002\000\000\000\000\b\030\271\002\000\000\000\000xſ\002\000"}
sockaddrlen = 54
sun_ad = <optimized out>
addr_len = <optimized out>
sun_sv = <optimized out>
sp = <optimized out>
ax = <optimized out>
mark = <optimized out>
items = <optimized out>
(gdb) fram 2
#2 XS_Socket_unpack_sockaddr_un (my_perl=0x20b2010, cv=<optimized out>)
at Socket.xs:715
715 Zero(&addr+sockaddrlen, sizeof(addr)-sockaddrlen, char);
(gdb) info locals
addr = {sun_family = 1,
sun_path = "/tmp/5r1KpX7RwQ/", 'x' <repeats 35 times>, '\000' <repeats
19 times>"\200,
\212\271\002\000\000\000\000\b\000\000\000\000\000\000\000\320B\216\002\000\000\000\000\b\030\271\002\000\000\000\000xſ\002\000"}
sockaddrlen = 54
sun_ad = <optimized out>
addr_len = <optimized out>
sun_sv = <optimized out>
sp = <optimized out>
ax = <optimized out>
mark = <optimized out>
items = <optimized out>
(gdb) print sizeof(addr)
$1 = 110
(gdb) print sockaddrlen
$2 = 54
The problem is in pointer arithmetics:
(gdb) print &addr
$3 = (struct sockaddr_un *) 0x7fff42cbcb30
(gdb) print &addr+1
$4 = (struct sockaddr_un *) 0x7fff42cbcb9e
(gdb) print ((char*)&addr)+1
$5 = 0x7fff42cbcb31 ""
You should call:
Zero( ((char*)&addr) + sockaddrlen, sizeof(addr)-sockaddrlen, char);
instead of:
Zero( &addr + sockaddrlen, sizeof(addr)-sockaddrlen, char);
otherwise you start zeroing far behind addr structure.
See <https://bugzilla.redhat.com/show_bug.cgi?id=806543> for downstream
report.