From: Tom Hughes Date: Thu, 1 Mar 2012 13:42:18 +0000 (+0000) Subject: Handle prlimit64 the same way we do getrlimit and setrlimit, with X-Git-Tag: svn/VALGRIND_3_8_0~442 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=35cc294c290018dd67ee94fbd014c48f8eb4f8bd;p=thirdparty%2Fvalgrind.git Handle prlimit64 the same way we do getrlimit and setrlimit, with some requests trapped and handled by valgrind. Patch from Matthias Schwarzott via BZ#294047. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@12411 --- diff --git a/coregrind/m_syswrap/syswrap-generic.c b/coregrind/m_syswrap/syswrap-generic.c index 59d138e9b8..0f538d37a0 100644 --- a/coregrind/m_syswrap/syswrap-generic.c +++ b/coregrind/m_syswrap/syswrap-generic.c @@ -3917,7 +3917,11 @@ PRE(sys_setrlimit) arg1 &= ~_RLIMIT_POSIX_FLAG; #endif - if (arg1 == VKI_RLIMIT_NOFILE) { + if (ARG2 && + ((struct vki_rlimit *)ARG2)->rlim_cur > ((struct vki_rlimit *)ARG2)->rlim_max) { + SET_STATUS_Failure( VKI_EINVAL ); + } + else if (arg1 == VKI_RLIMIT_NOFILE) { if (((struct vki_rlimit *)ARG2)->rlim_cur > VG_(fd_hard_limit) || ((struct vki_rlimit *)ARG2)->rlim_max != VG_(fd_hard_limit)) { SET_STATUS_Failure( VKI_EPERM ); diff --git a/coregrind/m_syswrap/syswrap-linux.c b/coregrind/m_syswrap/syswrap-linux.c index 9b587d857d..cea1ffdb96 100644 --- a/coregrind/m_syswrap/syswrap-linux.c +++ b/coregrind/m_syswrap/syswrap-linux.c @@ -1285,32 +1285,76 @@ PRE(sys_prlimit64) PRE_MEM_READ( "rlimit64(new_rlim)", ARG3, sizeof(struct vki_rlimit64) ); if (ARG4) PRE_MEM_WRITE( "rlimit64(old_rlim)", ARG4, sizeof(struct vki_rlimit64) ); -} - -POST(sys_prlimit64) -{ - if (ARG4) { - POST_MEM_WRITE( ARG4, sizeof(struct vki_rlimit64) ); + if (ARG3 && + ((struct vki_rlimit64 *)ARG3)->rlim_cur > ((struct vki_rlimit64 *)ARG3)->rlim_max) { + SET_STATUS_Failure( VKI_EINVAL ); + } + else if (ARG1 == 0 || ARG1 == VG_(getpid)()) { switch (ARG2) { case VKI_RLIMIT_NOFILE: - ((struct vki_rlimit64 *)ARG4)->rlim_cur = VG_(fd_soft_limit); - ((struct vki_rlimit64 *)ARG4)->rlim_max = VG_(fd_hard_limit); + SET_STATUS_Success( 0 ); + if (ARG4) { + ((struct vki_rlimit64 *)ARG4)->rlim_cur = VG_(fd_soft_limit); + ((struct vki_rlimit64 *)ARG4)->rlim_max = VG_(fd_hard_limit); + } + if (ARG3) { + if (((struct vki_rlimit64 *)ARG3)->rlim_cur > VG_(fd_hard_limit) || + ((struct vki_rlimit64 *)ARG3)->rlim_max != VG_(fd_hard_limit)) { + SET_STATUS_Failure( VKI_EPERM ); + } + else { + VG_(fd_soft_limit) = ((struct vki_rlimit64 *)ARG3)->rlim_cur; + } + } break; case VKI_RLIMIT_DATA: - ((struct vki_rlimit64 *)ARG4)->rlim_cur = VG_(client_rlimit_data).rlim_cur; - ((struct vki_rlimit64 *)ARG4)->rlim_max = VG_(client_rlimit_data).rlim_max; + SET_STATUS_Success( 0 ); + if (ARG4) { + ((struct vki_rlimit64 *)ARG4)->rlim_cur = VG_(client_rlimit_data).rlim_cur; + ((struct vki_rlimit64 *)ARG4)->rlim_max = VG_(client_rlimit_data).rlim_max; + } + if (ARG3) { + if (((struct vki_rlimit64 *)ARG3)->rlim_cur > VG_(client_rlimit_data).rlim_max || + ((struct vki_rlimit64 *)ARG3)->rlim_max > VG_(client_rlimit_data).rlim_max) { + SET_STATUS_Failure( VKI_EPERM ); + } + else { + VG_(client_rlimit_data).rlim_cur = ((struct vki_rlimit64 *)ARG3)->rlim_cur; + VG_(client_rlimit_data).rlim_max = ((struct vki_rlimit64 *)ARG3)->rlim_max; + } + } break; case VKI_RLIMIT_STACK: - ((struct vki_rlimit64 *)ARG4)->rlim_cur = VG_(client_rlimit_stack).rlim_cur; - ((struct vki_rlimit64 *)ARG4)->rlim_max = VG_(client_rlimit_stack).rlim_max; + SET_STATUS_Success( 0 ); + if (ARG4) { + ((struct vki_rlimit64 *)ARG4)->rlim_cur = VG_(client_rlimit_stack).rlim_cur; + ((struct vki_rlimit64 *)ARG4)->rlim_max = VG_(client_rlimit_stack).rlim_max; + } + if (ARG3) { + if (((struct vki_rlimit64 *)ARG3)->rlim_cur > VG_(client_rlimit_stack).rlim_max || + ((struct vki_rlimit64 *)ARG3)->rlim_max > VG_(client_rlimit_stack).rlim_max) { + SET_STATUS_Failure( VKI_EPERM ); + } + else { + VG_(threads)[tid].client_stack_szB = ((struct vki_rlimit64 *)ARG3)->rlim_cur; + VG_(client_rlimit_stack).rlim_cur = ((struct vki_rlimit64 *)ARG3)->rlim_cur; + VG_(client_rlimit_stack).rlim_max = ((struct vki_rlimit64 *)ARG3)->rlim_max; + } + } break; } } } +POST(sys_prlimit64) +{ + if (ARG4) + POST_MEM_WRITE( ARG4, sizeof(struct vki_rlimit64) ); +} + /* --------------------------------------------------------------------- tid-related wrappers ------------------------------------------------------------------ */ diff --git a/none/tests/Makefile.am b/none/tests/Makefile.am index d0804f29a4..16456a7f71 100644 --- a/none/tests/Makefile.am +++ b/none/tests/Makefile.am @@ -130,6 +130,7 @@ EXTRA_DIST = \ res_search.stderr.exp res_search.stdout.exp res_search.vgtest \ resolv.stderr.exp resolv.stdout.exp resolv.vgtest \ rlimit_nofile.stderr.exp rlimit_nofile.stdout.exp rlimit_nofile.vgtest \ + rlimit64_nofile.stderr.exp rlimit64_nofile.stdout.exp rlimit64_nofile.vgtest \ selfrun.stderr.exp selfrun.stdout.exp selfrun.vgtest \ sem.stderr.exp sem.stdout.exp sem.vgtest \ semlimit.stderr.exp semlimit.stdout.exp semlimit.vgtest \ @@ -185,7 +186,7 @@ check_PROGRAMS = \ rcrl readline1 \ require-text-symbol \ res_search resolv \ - rlimit_nofile selfrun sem semlimit sha1_test \ + rlimit_nofile rlimit64_nofile selfrun sem semlimit sha1_test \ shortpush shorts stackgrowth sigstackgrowth \ syscall-restart1 syscall-restart2 \ syslog \ diff --git a/none/tests/rlimit64_nofile.c b/none/tests/rlimit64_nofile.c new file mode 100644 index 0000000000..54c3edf73a --- /dev/null +++ b/none/tests/rlimit64_nofile.c @@ -0,0 +1,105 @@ +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#include +#include +#include +#include +#include +#include +#include "fdleak.h" + +int main(int argc, char **argv) +{ + struct rlimit64 oldrlim; + struct rlimit64 newrlim; + int fd; + + CLOSE_INHERITED_FDS; + + if (getrlimit64(RLIMIT_NOFILE, &oldrlim) < 0) + { + perror("getrlimit"); + exit(1); + } + + newrlim.rlim_cur = oldrlim.rlim_max+1; + newrlim.rlim_max = oldrlim.rlim_max; + if (setrlimit64(RLIMIT_NOFILE, &newrlim) == -1) + { + if (errno != EINVAL) { + fprintf(stderr, "setrlimit64 exceeding hardlimit must set errno=EINVAL\n"); + exit(1); + } + } + else + { + fprintf(stderr, "setrlimit64 exceeding hardlimit must return -1\n"); + exit(1); + } + + newrlim.rlim_cur = oldrlim.rlim_max; + newrlim.rlim_max = oldrlim.rlim_max+1; + if (setrlimit64(RLIMIT_NOFILE, &newrlim) == -1) + { + if (errno != EPERM) { + fprintf(stderr, "setrlimit64 changing hardlimit must set errno=EPERM\n"); + exit(1); + } + } + else + { + fprintf(stderr, "setrlimit64 changing hardlimit must return -1\n"); + exit(1); + } + + newrlim.rlim_cur = oldrlim.rlim_cur / 2; + newrlim.rlim_max = oldrlim.rlim_max; + + if (setrlimit64(RLIMIT_NOFILE, &newrlim) < 0) + { + perror("setrlimit64"); + exit(1); + } + + if (getrlimit64(RLIMIT_NOFILE, &newrlim) < 0) + { + perror("getrlimit"); + exit(1); + } + + if (newrlim.rlim_cur != oldrlim.rlim_cur / 2) + { + fprintf(stderr, "rlim_cur is %llu (should be %llu)\n", + (unsigned long long)newrlim.rlim_cur, + (unsigned long long)oldrlim.rlim_cur / 2); + } + + if (newrlim.rlim_max != oldrlim.rlim_max) + { + fprintf(stderr, "rlim_max is %llu (should be %llu)\n", + (unsigned long long)newrlim.rlim_max, + (unsigned long long)oldrlim.rlim_max); + } + + newrlim.rlim_cur -= 3; /* allow for stdin, stdout and stderr */ + + while (newrlim.rlim_cur-- > 0) + { + if (open("/dev/null", O_RDONLY) < 0) + { + perror("open"); + } + } + + if ((fd = open("/dev/null", O_RDONLY)) >= 0) + { + fprintf(stderr, "open succeeded with fd %d - it should have failed!\n", fd); + } + else if (errno != EMFILE) + { + perror("open"); + } + + exit(0); +} diff --git a/none/tests/rlimit64_nofile.stderr.exp b/none/tests/rlimit64_nofile.stderr.exp new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/none/tests/rlimit64_nofile.stderr.exp @@ -0,0 +1,2 @@ + + diff --git a/none/tests/rlimit64_nofile.stdout.exp b/none/tests/rlimit64_nofile.stdout.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/none/tests/rlimit64_nofile.vgtest b/none/tests/rlimit64_nofile.vgtest new file mode 100644 index 0000000000..de86a2164a --- /dev/null +++ b/none/tests/rlimit64_nofile.vgtest @@ -0,0 +1 @@ +prog: rlimit64_nofile diff --git a/none/tests/rlimit_nofile.c b/none/tests/rlimit_nofile.c index c5a0a410b0..135f1a14e9 100644 --- a/none/tests/rlimit_nofile.c +++ b/none/tests/rlimit_nofile.c @@ -20,6 +20,36 @@ int main(int argc, char **argv) exit(1); } + newrlim.rlim_cur = oldrlim.rlim_max+1; + newrlim.rlim_max = oldrlim.rlim_max; + if (setrlimit(RLIMIT_NOFILE, &newrlim) == -1) + { + if (errno != EINVAL) { + fprintf(stderr, "setrlimit exceeding hardlimit must set errno=EINVAL\n"); + exit(1); + } + } + else + { + fprintf(stderr, "setrlimit exceeding hardlimit must return -1\n"); + exit(1); + } + + newrlim.rlim_cur = oldrlim.rlim_max; + newrlim.rlim_max = oldrlim.rlim_max+1; + if (setrlimit(RLIMIT_NOFILE, &newrlim) == -1) + { + if (errno != EPERM) { + fprintf(stderr, "setrlimit changing hardlimit must set errno=EPERM\n"); + exit(1); + } + } + else + { + fprintf(stderr, "setrlimit changing hardlimit must return -1\n"); + exit(1); + } + newrlim.rlim_cur = oldrlim.rlim_cur / 2; newrlim.rlim_max = oldrlim.rlim_max;