diff options
author | Dmitry V. Levin <ldv@altlinux.org> | 2016-04-19 01:59:52 +0000 |
---|---|---|
committer | Dmitry V. Levin <ldv@altlinux.org> | 2016-04-20 01:08:13 +0000 |
commit | 5e9944df2c8b75a8fec46a597d34934c7525d4ca (patch) | |
tree | e571e53a7930a5aef99f9a0232e5b21fdda08e0c | |
parent | ba210af0600a57fc2a5f58d2da31aa557b798268 (diff) | |
download | strace-5e9944df2c8b75a8fec46a597d34934c7525d4ca.tar.gz |
Fix corner cases of getgroups and setgroups syscall decoders
* uid.c (print_groups): New function.
(SYS_FUNC(setgroups), SYS_FUNC(getgroups)): Use it.
Print first syscall argument using %u format.
* tests/getgroups.c: New file.
* tests/getgroups.test: New test.
* tests/getgroups32.c: New file.
* tests/getgroups32.test: New test.
* tests/setgroups.c: New file.
* tests/setgroups.test: New test.
* tests/setgroups32.c: New file.
* tests/setgroups32.test: New test.
* tests/.gitignore: Add getgroups, getgroups32, setgroups,
and setgroups32.
* tests/Makefile.am (check_PROGRAMS): Likewise.
(DECODER_TESTS): Add getgroups.test, getgroups32.test,
setgroups.test, and setgroups32.test.
-rw-r--r-- | tests/.gitignore | 4 | ||||
-rw-r--r-- | tests/Makefile.am | 8 | ||||
-rw-r--r-- | tests/getgroups.c | 161 | ||||
-rwxr-xr-x | tests/getgroups.test | 6 | ||||
-rw-r--r-- | tests/getgroups32.c | 12 | ||||
-rwxr-xr-x | tests/getgroups32.test | 6 | ||||
-rw-r--r-- | tests/setgroups.c | 186 | ||||
-rwxr-xr-x | tests/setgroups.test | 6 | ||||
-rw-r--r-- | tests/setgroups32.c | 12 | ||||
-rwxr-xr-x | tests/setgroups32.test | 6 | ||||
-rw-r--r-- | uid.c | 136 |
11 files changed, 454 insertions, 89 deletions
diff --git a/tests/.gitignore b/tests/.gitignore index d6621e053..af1de088e 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -59,6 +59,8 @@ ftruncate64 getcwd getdents getdents64 +getgroups +getgroups32 getrandom getrusage getxxid @@ -148,6 +150,8 @@ setfsuid setfsuid32 setgid setgid32 +setgroups +setgroups32 sethostname setregid setregid32 diff --git a/tests/Makefile.am b/tests/Makefile.am index f051fc96c..6234711e5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -111,6 +111,8 @@ check_PROGRAMS = \ getcwd \ getdents \ getdents64 \ + getgroups \ + getgroups32 \ getrandom \ getrusage \ getxxid \ @@ -198,6 +200,8 @@ check_PROGRAMS = \ setfsuid32 \ setgid \ setgid32 \ + setgroups \ + setgroups32 \ sethostname \ setregid \ setregid32 \ @@ -340,6 +344,8 @@ DECODER_TESTS = \ getcwd.test \ getdents.test \ getdents64.test \ + getgroups.test \ + getgroups32.test \ getrandom.test \ getrusage.test \ getxxid.test \ @@ -421,6 +427,8 @@ DECODER_TESTS = \ setfsuid32.test \ setgid.test \ setgid32.test \ + setgroups.test \ + setgroups32.test \ sethostname.test \ setregid.test \ setregid32.test \ diff --git a/tests/getgroups.c b/tests/getgroups.c new file mode 100644 index 000000000..e853b25c5 --- /dev/null +++ b/tests/getgroups.c @@ -0,0 +1,161 @@ +/* + * Check decoding of getgroups/getgroups32 syscalls. + * + * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __NR_getgroups32 + +# define SYSCALL_NR __NR_getgroups32 +# define SYSCALL_NAME "getgroups32" +# define GID_TYPE unsigned int + +#else + +# include "tests.h" +# include <sys/syscall.h> + +# ifdef __NR_getgroups + +# define SYSCALL_NR __NR_getgroups +# define SYSCALL_NAME "getgroups" +# if defined __NR_getgroups32 && __NR_getgroups != __NR_getgroups32 +# define GID_TYPE unsigned short +# else +# define GID_TYPE unsigned int +# endif + +# endif + +#endif + +#ifdef GID_TYPE + +# include <errno.h> +# include <stdio.h> +# include <unistd.h> + +static const char * +errno2str(void) +{ + switch (errno) { +#define CASE(x) case x: return #x + CASE(EINVAL); + CASE(EFAULT); + default: perror_msg_and_fail(SYSCALL_NAME); + } +} + +#define MAX_STRLEN 32 +static long ngroups; + +static void +get_groups(const long size, GID_TYPE *const g) +{ + long i = syscall(SYSCALL_NR, size, g); + if (i != ngroups) + perror_msg_and_fail("%s(%#lx, %p)", SYSCALL_NAME, size, g); + + printf("%s(%u, [", SYSCALL_NAME, (unsigned) size); + for (i = 0; i < ngroups; ++i) { + if (i) + printf(", "); + if (i >= MAX_STRLEN) { + printf("..."); + break; + } + printf("%u", (unsigned int) g[i]); + } + printf("]) = %ld\n", ngroups); +} + +int +main(void) +{ + long rc; + + /* check how the first argument is decoded */ + ngroups = syscall(SYSCALL_NR, 0, 0); + printf("%s(0, NULL) = %ld\n", SYSCALL_NAME, ngroups); + if (ngroups < 0) + perror_msg_and_fail(SYSCALL_NAME); + + rc = syscall(SYSCALL_NR, (long) 0xffffffff00000000ULL, 0); + printf("%s(0, NULL) = %ld\n", SYSCALL_NAME, rc); + + rc = syscall(SYSCALL_NR, -1U, 0); + printf("%s(%u, NULL) = %ld %s (%m)\n", + SYSCALL_NAME, -1U, rc, errno2str()); + + rc = syscall(SYSCALL_NR, -1L, 0); + printf("%s(%u, NULL) = %ld %s (%m)\n", + SYSCALL_NAME, -1U, rc, errno2str()); + + const unsigned int ngroups_max = sysconf(_SC_NGROUPS_MAX); + + rc = syscall(SYSCALL_NR, ngroups_max, 0); + if (rc < 0) + printf("%s(%u, NULL) = %ld %s (%m)\n", + SYSCALL_NAME, ngroups_max, rc, errno2str()); + else + printf("%s(%u, NULL) = %ld\n", + SYSCALL_NAME, ngroups_max, rc); + + rc = syscall(SYSCALL_NR, (long) 0xffffffff00000000ULL | ngroups_max, 0); + if (rc < 0) + printf("%s(%u, NULL) = %ld %s (%m)\n", + SYSCALL_NAME, ngroups_max, rc, errno2str()); + else + printf("%s(%u, NULL) = %ld\n", + SYSCALL_NAME, ngroups_max, rc); + + /* check how the second argument is decoded */ + GID_TYPE *const g1 = + tail_alloc(ngroups ? sizeof(*g1) * ngroups : 1); + GID_TYPE *const g2 = tail_alloc(sizeof(*g2) * (ngroups + 1)); + void *efault = g2 + ngroups + 1; + (void) tail_alloc(1); + + get_groups(ngroups, g1); + get_groups(ngroups + 1, g1); + get_groups(ngroups + 1, g2); + + if (ngroups) { + rc = syscall(SYSCALL_NR, ngroups, efault); + printf("%s(%u, %p) = %ld %s (%m)\n", + SYSCALL_NAME, (unsigned) ngroups, efault, + rc, errno2str()); + } + + puts("+++ exited with 0 +++"); + return 0; +} + +#else + +SKIP_MAIN_UNDEFINED("__NR_getgroups") + +#endif diff --git a/tests/getgroups.test b/tests/getgroups.test new file mode 100755 index 000000000..2eb3056d9 --- /dev/null +++ b/tests/getgroups.test @@ -0,0 +1,6 @@ +#!/bin/sh + +# Check getgroups syscall decoding. + +. "${srcdir=.}/init.sh" +run_strace_match_diff -a17 diff --git a/tests/getgroups32.c b/tests/getgroups32.c new file mode 100644 index 000000000..2576548b6 --- /dev/null +++ b/tests/getgroups32.c @@ -0,0 +1,12 @@ +#include "tests.h" +#include <sys/syscall.h> + +#ifdef __NR_getgroups32 + +# include "getgroups.c" + +#else + +SKIP_MAIN_UNDEFINED("__NR_getgroups32") + +#endif diff --git a/tests/getgroups32.test b/tests/getgroups32.test new file mode 100755 index 000000000..12a8bcc0d --- /dev/null +++ b/tests/getgroups32.test @@ -0,0 +1,6 @@ +#!/bin/sh + +# Check getgroups32 syscall decoding. + +. "${srcdir=.}/init.sh" +run_strace_match_diff -a19 diff --git a/tests/setgroups.c b/tests/setgroups.c new file mode 100644 index 000000000..ca3584853 --- /dev/null +++ b/tests/setgroups.c @@ -0,0 +1,186 @@ +/* + * Check decoding of setgroups/setgroups32 syscalls. + * + * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __NR_setgroups32 + +# define SYSCALL_NR __NR_setgroups32 +# define SYSCALL_NAME "setgroups32" +# define GID_TYPE unsigned int + +#else + +# include "tests.h" +# include <sys/syscall.h> + +# ifdef __NR_setgroups + +# define SYSCALL_NR __NR_setgroups +# define SYSCALL_NAME "setgroups" +# if defined __NR_setgroups32 && __NR_setgroups != __NR_setgroups32 +# define GID_TYPE unsigned short +# else +# define GID_TYPE unsigned int +# endif + +# endif + +#endif + +#ifdef GID_TYPE + +# include <errno.h> +# include <stdio.h> +# include <unistd.h> + +static const char * +errno2str(void) +{ + switch (errno) { +#define CASE(x) case x: return #x + CASE(EPERM); + CASE(EINVAL); + CASE(ENOMEM); + CASE(EFAULT); + default: perror_msg_and_fail(SYSCALL_NAME); + } +} + +int +main(void) +{ + /* check how the first argument is decoded */ + if (syscall(SYSCALL_NR, 0, 0)) + printf("%s(0, NULL) = -1 %s (%m)\n", SYSCALL_NAME, errno2str()); + else + printf("%s(0, NULL) = 0\n", SYSCALL_NAME); + + if (syscall(SYSCALL_NR, (long) 0xffffffff00000000ULL, 0)) + printf("%s(0, NULL) = -1 %s (%m)\n", + SYSCALL_NAME, errno2str()); + else + printf("%s(0, NULL) = 0\n", SYSCALL_NAME); + + syscall(SYSCALL_NR, 1, 0); + printf("%s(1, NULL) = -1 %s (%m)\n", SYSCALL_NAME, errno2str()); + + syscall(SYSCALL_NR, (long) 0xffffffff00000001ULL, 0); + printf("%s(1, NULL) = -1 %s (%m)\n", SYSCALL_NAME, errno2str()); + + syscall(SYSCALL_NR, -1U, 0); + printf("%s(%u, NULL) = -1 %s (%m)\n", SYSCALL_NAME, -1U, errno2str()); + + syscall(SYSCALL_NR, -1L, 0); + printf("%s(%u, NULL) = -1 %s (%m)\n", SYSCALL_NAME, -1U, errno2str()); + + /* check how the second argument is decoded */ + const GID_TYPE *const g1 = tail_alloc(sizeof(*g1)); + GID_TYPE *const g2 = tail_alloc(sizeof(*g2) * 2); + GID_TYPE *const g3 = tail_alloc(sizeof(*g3) * 3); + (void) tail_alloc(1); + + if (syscall(SYSCALL_NR, 0, g1 + 1)) + printf("%s(0, []) = -1 %s (%m)\n", + SYSCALL_NAME, errno2str()); + else + printf("%s(0, []) = 0\n", SYSCALL_NAME); + + if (syscall(SYSCALL_NR, 1, g1)) + printf("%s(1, [%u]) = -1 %s (%m)\n", + SYSCALL_NAME, (unsigned) *g1, errno2str()); + else + printf("%s(1, [%u]) = 0\n", + SYSCALL_NAME, (unsigned) *g1); + + syscall(SYSCALL_NR, 1, g1 + 1); + printf("%s(1, %p) = -1 %s (%m)\n", + SYSCALL_NAME, g1 + 1, errno2str()); + + syscall(SYSCALL_NR, 1, -1L); + printf("%s(1, %#lx) = -1 %s (%m)\n", SYSCALL_NAME, -1L, errno2str()); + + syscall(SYSCALL_NR, 2, g1); + printf("%s(2, [%u, %p]) = -1 %s (%m)\n", + SYSCALL_NAME, (unsigned) *g1, g1 + 1, errno2str()); + + g2[0] = -2; + g2[1] = -3; + if (syscall(SYSCALL_NR, 2, g2)) + printf("%s(2, [%u, %u]) = -1 %s (%m)\n", SYSCALL_NAME, + (unsigned) g2[0], (unsigned) g2[1], errno2str()); + else + printf("%s(2, [%u, %u]) = 0\n", SYSCALL_NAME, + (unsigned) g2[0], (unsigned) g2[1]); + + syscall(SYSCALL_NR, 3, g2); + printf("%s(3, [%u, %u, %p]) = -1 %s (%m)\n", SYSCALL_NAME, + (unsigned) g2[0], (unsigned) g2[1], g2 + 2, errno2str()); + + g3[0] = 0; + g3[1] = 1; + if (syscall(SYSCALL_NR, 3, g3)) + printf("%s(3, [%u, %u, ...]) = -1 %s (%m)\n", SYSCALL_NAME, + (unsigned) g3[0], (unsigned) g3[1], errno2str()); + else + printf("%s(3, [%u, %u]) = 0\n", SYSCALL_NAME, + (unsigned) g3[0], (unsigned) g3[1]); + + syscall(SYSCALL_NR, 4, g3); + printf("%s(4, [%u, %u, ...]) = -1 %s (%m)\n", SYSCALL_NAME, + (unsigned) g3[0], (unsigned) g3[1], errno2str()); + + long rc = sysconf(_SC_NGROUPS_MAX); + const unsigned ngroups_max = rc; + + if ((unsigned long) rc == ngroups_max && (int) ngroups_max > 0) { + syscall(SYSCALL_NR, ngroups_max, g3); + printf("%s(%u, [%u, %u, ...]) = -1 %s (%m)\n", SYSCALL_NAME, + ngroups_max, (unsigned) g3[0], (unsigned) g3[1], + errno2str()); + + const unsigned long size = + (unsigned long) 0xffffffff00000000ULL | ngroups_max; + syscall(SYSCALL_NR, size, g3); + printf("%s(%u, [%u, %u, ...]) = -1 %s (%m)\n", SYSCALL_NAME, + ngroups_max, (unsigned) g3[0], (unsigned) g3[1], + errno2str()); + + syscall(SYSCALL_NR, ngroups_max + 1, g3); + printf("%s(%u, %p) = -1 %s (%m)\n", SYSCALL_NAME, + ngroups_max + 1, g3, errno2str()); + } + + puts("+++ exited with 0 +++"); + return 0; +} + +#else + +SKIP_MAIN_UNDEFINED("__NR_setgroups") + +#endif diff --git a/tests/setgroups.test b/tests/setgroups.test new file mode 100755 index 000000000..0dcc8f756 --- /dev/null +++ b/tests/setgroups.test @@ -0,0 +1,6 @@ +#!/bin/sh + +# Check setgroups syscall decoding. + +. "${srcdir=.}/init.sh" +run_strace_match_diff -s2 -a17 diff --git a/tests/setgroups32.c b/tests/setgroups32.c new file mode 100644 index 000000000..bd0a1fdec --- /dev/null +++ b/tests/setgroups32.c @@ -0,0 +1,12 @@ +#include "tests.h" +#include <sys/syscall.h> + +#ifdef __NR_setgroups32 + +# include "setgroups.c" + +#else + +SKIP_MAIN_UNDEFINED("__NR_setgroups32") + +#endif diff --git a/tests/setgroups32.test b/tests/setgroups32.test new file mode 100755 index 000000000..ee4750d1b --- /dev/null +++ b/tests/setgroups32.test @@ -0,0 +1,6 @@ +#!/bin/sh + +# Check setgroups32 syscall decoding. + +. "${srcdir=.}/init.sh" +run_strace_match_diff -s2 -a19 @@ -3,7 +3,7 @@ * Copyright (c) 1993 Branko Lankester <branko@hacktic.nl> * Copyright (c) 1993-1996 Rick Sladkey <jrs@world.std.com> * Copyright (c) 1996-1999 Wichert Akkerman <wichert@cistron.nl> - * Copyright (c) 2003-2015 Dmitry V. Levin <ldv@altlinux.org> + * Copyright (c) 2003-2016 Dmitry V. Levin <ldv@altlinux.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -153,111 +153,69 @@ printuid(const char *text, const unsigned int uid) tprintf("%s%u", text, (uid_t) uid); } -SYS_FUNC(setgroups) +static void +print_groups(struct tcb *tcp, const unsigned int len, const unsigned long addr) { - unsigned long cur, abbrev_end; - uid_t gid; - int failed = 0; - const unsigned long len = tcp->u_arg[0]; - const unsigned long start = tcp->u_arg[1]; - const unsigned long size = len * sizeof(gid); - const unsigned long end = start + size; - - tprintf("%lu, ", len); + static unsigned long ngroups_max; + if (!ngroups_max) + ngroups_max = sysconf(_SC_NGROUPS_MAX); + + const unsigned int size = len * sizeof(uid_t); + const unsigned long end = addr + size; + if (!addr || !verbose(tcp) || size / sizeof(uid_t) != len + || len > ngroups_max || end < addr) { + printaddr(addr); + return; + } + if (len == 0) { tprints("[]"); - return RVAL_DECODED; - } - if (!start || !verbose(tcp) || - size / sizeof(gid) != len || end < start) { - printaddr(start); - return RVAL_DECODED; + return; } - if (abbrev(tcp)) { - abbrev_end = start + max_strlen * sizeof(gid); - if (abbrev_end < start) - abbrev_end = end; - } else { - abbrev_end = end; - } - tprints("["); - for (cur = start; cur < end; cur += sizeof(gid)) { - if (cur > start) + + const unsigned long abbrev_end = + (abbrev(tcp) && max_strlen < len) ? + addr + max_strlen * sizeof(uid_t) : end; + + unsigned long cur; + for (cur = addr; cur < end; cur += sizeof(uid_t)) { + if (cur != addr) tprints(", "); + + uid_t gid; + if (umove_or_printaddr(tcp, cur, &gid)) + break; + + if (cur == addr) + tprints("["); + if (cur >= abbrev_end) { tprints("..."); + cur = end; break; } - if (umoven(tcp, cur, sizeof(gid), &gid) < 0) { - tprints("?"); - failed = 1; - break; - } + tprintf("%u", (unsigned int) gid); } - tprints("]"); - if (failed) { - tprints(" "); - printaddr(start); - } + if (cur != addr) + tprints("]"); +} + +SYS_FUNC(setgroups) +{ + const unsigned int len = tcp->u_arg[0]; + tprintf("%u, ", len); + print_groups(tcp, len, tcp->u_arg[1]); return RVAL_DECODED; } SYS_FUNC(getgroups) { - if (entering(tcp)) { - tprintf("%lu, ", tcp->u_arg[0]); - } else { - unsigned long cur, abbrev_end; - uid_t gid; - int failed = 0; - const unsigned long len = tcp->u_rval; - const unsigned long size = len * sizeof(gid); - const unsigned long start = tcp->u_arg[1]; - const unsigned long end = start + size; - - if (!start) { - printaddr(start); - return 0; - } - if (len == 0) { - tprints("[]"); - return 0; - } - if (!verbose(tcp) || syserror(tcp) || - size / sizeof(gid) != len || end < start) { - printaddr(start); - return 0; - } - if (abbrev(tcp)) { - abbrev_end = start + max_strlen * sizeof(gid); - if (abbrev_end < start) - abbrev_end = end; - } else { - abbrev_end = end; - } - tprints("["); - for (cur = start; cur < end; cur += sizeof(gid)) { - if (cur > start) - tprints(", "); - if (cur >= abbrev_end) { - tprints("..."); - break; - } - if (umoven(tcp, cur, sizeof(gid), &gid) < 0) { - tprints("?"); - failed = 1; - break; - } - tprintf("%u", (unsigned int) gid); - } - tprints("]"); - if (failed) { - tprints(" "); - printaddr(start); - } - } + if (entering(tcp)) + tprintf("%u, ", (unsigned int) tcp->u_arg[0]); + else + print_groups(tcp, tcp->u_rval, tcp->u_arg[1]); return 0; } |