From b85a7f3b9525a3b4cd84f301b2c7b100348242fe Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Sat, 24 Jan 2015 15:20:31 +0000 Subject: [PATCH] Impove struct cmsghdr decoding Print all cmsghdr structures in msg_control array, not just the first one. Change output format to be consistent with other parts of strace where structures and arrays are printed. Implement decoder for SCM_SECURITY message type. * net.c (print_scm_rights, print_scm_creds, print_scm_security): New functions. (printcmsghdr): Use them. Iterate over all members of the array. * xlat/scmvals.in: Add SCM_SECURITY. * tests/scm_rights.c (main): Pass one more descriptor to the receiver. Set SO_PASSCRED on the receiver part. Reserve enough space to receive SCM_CREDENTIALS and SCM_RIGHTS. * tests/scm_rights-fd.test: Update. --- net.c | 180 ++++++++++++++++++++++++++------------- tests/scm_rights-fd.test | 8 +- tests/scm_rights.c | 52 ++++++----- xlat/scmvals.in | 1 + 4 files changed, 162 insertions(+), 79 deletions(-) diff --git a/net.c b/net.c index c235116c..829f3ad8 100644 --- a/net.c +++ b/net.c @@ -339,7 +339,10 @@ printsock(struct tcb *tcp, long addr, int addrlen) } #if HAVE_SENDMSG -#include "xlat/scmvals.h" +# ifndef SCM_SECURITY +# define SCM_SECURITY 0x03 +# endif +# include "xlat/scmvals.h" #if SUPPORTED_PERSONALITIES > 1 && SIZEOF_LONG > 4 struct cmsghdr32 { @@ -350,91 +353,152 @@ struct cmsghdr32 { #endif typedef union { - char *buf; + char *ptr; struct cmsghdr *cmsg; #if SUPPORTED_PERSONALITIES > 1 && SIZEOF_LONG > 4 struct cmsghdr32 *cmsg32; #endif } union_cmsghdr; -static void -printcmsghdr(struct tcb *tcp, unsigned long addr, unsigned long len) +static bool +print_scm_rights(struct tcb *tcp, size_t cmsg_size, char *ptr, size_t cmsg_len) { - union_cmsghdr u; - size_t cmsg_size; - unsigned long cmsg_len; - int cmsg_level; - int cmsg_type; + if (cmsg_size + sizeof(int) > cmsg_len) + return false; + + int *fds = (int *) (ptr + cmsg_size); + bool seen = false; + + tprints(", ["); + while ((char *) fds < (ptr + cmsg_len)) { + if (seen) + tprints(", "); + else + seen = true; + printfd(tcp, *fds++); + } + tprints("]}"); + return true; +} + +static bool +print_scm_creds(struct tcb *tcp, size_t cmsg_size, char *ptr, size_t cmsg_len) +{ + if (cmsg_size + sizeof(struct ucred) > cmsg_len) + return false; + + const struct ucred *uc = (void *) (ptr + cmsg_size); + + tprintf(", {pid=%u, uid=%u, gid=%u}}", + (unsigned) uc->pid, (unsigned) uc->uid, (unsigned) uc->gid); + return true; +} + +static bool +print_scm_security(struct tcb *tcp, size_t cmsg_size, char *ptr, size_t cmsg_len) +{ + if (cmsg_size + sizeof(char) > cmsg_len) + return false; + + const char *label = (const char *) (ptr + cmsg_size); + const size_t label_len = cmsg_len - cmsg_size; + char *outstr; + const size_t alloc_len = 4 * label_len + 3; - cmsg_size = + if (label_len != alloc_len / 4 || + !(outstr = malloc(alloc_len))) + return false; + + string_quote(label, outstr, 0, label_len); + tprintf(", %s}", outstr); + + free(outstr); + return true; +} + +static void +printcmsghdr(struct tcb *tcp, unsigned long addr, size_t len) +{ + const size_t cmsg_size = #if SUPPORTED_PERSONALITIES > 1 && SIZEOF_LONG > 4 (current_wordsize < sizeof(long)) ? sizeof(struct cmsghdr32) : #endif sizeof(struct cmsghdr); - u.buf = len < cmsg_size ? NULL : malloc(len); - if (!u.buf || umoven(tcp, addr, len, u.buf) < 0) { + char *buf = len < cmsg_size ? NULL : malloc(len); + if (!buf || umoven(tcp, addr, len, buf) < 0) { tprintf(", msg_control=%#lx", addr); - free(u.buf); + free(buf); return; } - cmsg_len = + union_cmsghdr u = { .ptr = buf }; + + tprints(", ["); + while (len >= cmsg_size) { + size_t cmsg_len = #if SUPPORTED_PERSONALITIES > 1 && SIZEOF_LONG > 4 - (current_wordsize < sizeof(long)) ? u.cmsg32->cmsg_len : + (current_wordsize < sizeof(long)) ? u.cmsg32->cmsg_len : #endif - u.cmsg->cmsg_len; - cmsg_level = + u.cmsg->cmsg_len; + int cmsg_level = #if SUPPORTED_PERSONALITIES > 1 && SIZEOF_LONG > 4 - (current_wordsize < sizeof(long)) ? u.cmsg32->cmsg_level : + (current_wordsize < sizeof(long)) ? u.cmsg32->cmsg_level : #endif - u.cmsg->cmsg_level; - cmsg_type = + u.cmsg->cmsg_level; + int cmsg_type = #if SUPPORTED_PERSONALITIES > 1 && SIZEOF_LONG > 4 - (current_wordsize < sizeof(long)) ? u.cmsg32->cmsg_type : + (current_wordsize < sizeof(long)) ? u.cmsg32->cmsg_type : #endif - u.cmsg->cmsg_type; - - tprintf(", {cmsg_len=%lu, cmsg_level=", cmsg_len); - printxval(socketlayers, cmsg_level, "SOL_???"); - tprints(", cmsg_type="); - - if (cmsg_len > len) - cmsg_len = len; - - if (cmsg_level == SOL_SOCKET) { - printxval(scmvals, cmsg_type, "SCM_???"); - - if (cmsg_type == SCM_RIGHTS - && cmsg_size + sizeof(int) <= cmsg_len) { - int *fds = (int *) (u.buf + cmsg_size); - int first = 1; + u.cmsg->cmsg_type; - tprints(", {"); - while ((char *) fds < (u.buf + cmsg_len)) { - if (!first) - tprints(", "); - printfd(tcp, *fds++); - first = 0; + if (u.ptr != buf) + tprints(", "); + tprintf("{cmsg_len=%lu, cmsg_level=", (unsigned long) cmsg_len); + printxval(socketlayers, cmsg_level, "SOL_???"); + tprints(", cmsg_type="); + + if (cmsg_len > len) + cmsg_len = len; + + if (cmsg_level == SOL_SOCKET) { + printxval(scmvals, cmsg_type, "SCM_???"); + switch (cmsg_type) { + case SCM_RIGHTS: + if (print_scm_rights(tcp, cmsg_size, u.ptr, cmsg_len)) + goto next_cmsg; + break; + case SCM_CREDENTIALS: + if (print_scm_creds(tcp, cmsg_size, u.ptr, cmsg_len)) + goto next_cmsg; + break; + case SCM_SECURITY: + if (print_scm_security(tcp, cmsg_size, u.ptr, cmsg_len)) + goto next_cmsg; + break; } - tprints("}}"); - free(u.buf); - return; + } else { + tprintf("%u", cmsg_type); } - if (cmsg_type == SCM_CREDENTIALS - && cmsg_size + sizeof(struct ucred) <= cmsg_len) { - struct ucred *uc = (void *) (u.buf + cmsg_size); - - tprintf("{pid=%ld, uid=%ld, gid=%ld}}", - (long)uc->pid, (long)uc->uid, (long)uc->gid); - free(u.buf); - return; + tprints(", ...}"); +next_cmsg: + if (cmsg_len < cmsg_size) { + len -= cmsg_size; + break; } - } else { - tprintf("%u", cmsg_type); + cmsg_len = (cmsg_len + current_wordsize - 1) & + (size_t) ~(current_wordsize - 1); + if (cmsg_len >= len) { + len = 0; + break; + } + u.ptr += cmsg_len; + len -= cmsg_len; } - free(u.buf); - tprints(", ...}"); + if (len) + tprints(", ..."); + tprints("]"); + free(buf); } static void diff --git a/tests/scm_rights-fd.test b/tests/scm_rights-fd.test index ecf44750..c4d94509 100755 --- a/tests/scm_rights-fd.test +++ b/tests/scm_rights-fd.test @@ -36,7 +36,11 @@ grep_log() } } -grep_log sendmsg '\(1, \{msg_name\(0\)=NULL, msg_iov\(1\)=\[\{"\\x00\\x00\\x00\\x00[^"]*", [1-9][0-9]*\}\], msg_controllen=[1-9][0-9]*, \{cmsg_len=[1-9][0-9]*, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, \{3\}\}, msg_flags=0\}, 0\) += [1-9][0-9]*' -grep_log recvmsg '\(0, \{msg_name\(0\)=NULL, msg_iov\(1\)=\[\{"\\x00\\x00\\x00\\x00[^"]*", [1-9][0-9]*\}\], msg_controllen=[1-9][0-9]*, \{cmsg_len=[1-9][0-9]*, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, \{3\}\}, msg_flags=0\}, 0\) += [1-9][0-9]*' +n='[1-9][0-9]*' +msg='\{msg_name\(0\)=NULL, msg_iov\(1\)=\[\{"\\x00\\x00\\x00\\x00[^"]*", '"$n"'\}\], msg_controllen='"$n" +rights='\{cmsg_len='"$n"', cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, \[3, 4\]\}' +creds='\{cmsg_len='"$n"', cmsg_level=SOL_SOCKET, cmsg_type=SCM_CREDENTIALS, \{pid='"$n"', uid=[0-9]+, gid=[0-9]+\}\}' +grep_log sendmsg '\(1, '"$msg"', \['"$rights"'\], msg_flags=0\}, 0\) += '"$n" +grep_log recvmsg '\(0, '"$msg"', \['"$creds"', '"$rights"'\], msg_flags=0\}, 0\) += '"$n" exit 0 diff --git a/tests/scm_rights.c b/tests/scm_rights.c index 0cced9be..fa6e8f04 100644 --- a/tests/scm_rights.c +++ b/tests/scm_rights.c @@ -9,11 +9,6 @@ int main(void) { - union { - struct cmsghdr cmsghdr; - char buf[CMSG_SPACE(sizeof(int))]; - } control = {}; - int fd; int data = 0; struct iovec iov = { @@ -21,19 +16,14 @@ int main(void) .iov_len = sizeof(iov) }; - struct msghdr mh = { - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = &control, - .msg_controllen = sizeof(control) - }; - while ((fd = open("/dev/null", O_RDWR)) < 3) assert(fd >= 0); (void) close(3); int sv[2]; assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0); + int one = 1; + assert(setsockopt(sv[0], SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) == 0); pid_t pid = fork(); assert(pid >= 0); @@ -43,14 +33,29 @@ int main(void) assert(dup2(sv[1], 1) == 1); assert(close(sv[1]) == 0); - assert((fd = open("/dev/null", O_RDWR)) == 3); + int fds[2]; + assert((fds[0] = open("/dev/null", O_RDWR)) == 3); + assert((fds[1] = open("/dev/zero", O_RDONLY)) == 4); + + union { + struct cmsghdr cmsg; + char buf[CMSG_LEN(sizeof(fds))]; + } control = { + .cmsg = { + .cmsg_level = SOL_SOCKET, + .cmsg_type = SCM_RIGHTS, + .cmsg_len = CMSG_LEN(sizeof(fds)) + } + }; - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&mh); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof fd); - memcpy(CMSG_DATA(cmsg), &fd, sizeof fd); - mh.msg_controllen = cmsg->cmsg_len; + memcpy(CMSG_DATA(&control.cmsg), fds, sizeof(fds)); + + struct msghdr mh = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control) + }; assert(sendmsg(1, &mh, 0) == sizeof(iov)); assert(close(1) == 0); @@ -63,6 +68,15 @@ int main(void) assert(dup2(sv[0], 0) == 0); assert(close(sv[0]) == 0); + struct cmsghdr control[4]; + + struct msghdr mh = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = control, + .msg_controllen = sizeof(control) + }; + assert(recvmsg(0, &mh, 0) == sizeof(iov)); assert(close(0) == 0); } diff --git a/xlat/scmvals.in b/xlat/scmvals.in index 04322e84..86d9a15b 100644 --- a/xlat/scmvals.in +++ b/xlat/scmvals.in @@ -1,2 +1,3 @@ SCM_RIGHTS SCM_CREDENTIALS +SCM_SECURITY -- 2.40.0