Message ID | 20231017100122.2184-1-kozlov@synopsys.com |
---|---|
State | Accepted |
Headers | show |
Series | [uclibc-ng-devel] setrlimit/getrlimit: fix prlimit64 syscall use for 32-bit CPUs | expand |
Hi pavel, On 10/17/23 12:01, Pavel Kozlov wrote: > From: Pavel Kozlov <pavel.kozlov@synopsys.com> > > Commit 95e38b37 ("add support for systems without legacy setrlimit/getrlimit > syscalls") has added use of the prlimit64 syscall in getrlimit and setrlimit > functions. This change causes memory corruption on getrlimit call for 32-bit > CPUs like ARC, as ARC doesn't have ugetrlimit syscall and uses prlimit64. > Also, setrlimit has been broken by prlimit64 call on 32-bit CPUs like, i386, > ARM, ARC. Oopsy! Sorry about that, indeed I guess I broke 32 bit CPUs. I've started a CI job to test your patch to see if it breaks our arch, I'll keep the list informed on whether this works or not. Thanks a lot! > For the prlimit64 syscall the kernel expects an rlimit struct with 64-bit fields, > but on 32-bit CPUs without _FILE_OFFSET_BITS=64 the struct rlimit has 32-bit > fields. > > Add safe implementations of getrlimit, setrlimit, prlimit for 32-bit CPUs with a > local struct rlimit64 variable for use in the prlimit64 syscall. > For 64-bit CPUs and configurations with _FILE_OFFSET_BITS=64 use > getrlimit, setrlimit, prlimit as aliases to getrlimit64, setrlimit64 and > prlimit64. Add a new function prlimit64. > > Tested on aarch64, arm, i386, arc. > > Fixes: 95e38b37 ("add support for systems without legacy setrlimit/getrlimit syscalls") > Signed-off-by: Pavel Kozlov <pavel.kozlov@synopsys.com> > --- > include/sys/resource.h | 4 ++ > libc/sysdeps/linux/common/getrlimit.c | 55 ++++++++++++++++++------- > libc/sysdeps/linux/common/getrlimit64.c | 26 ++++++++++-- > libc/sysdeps/linux/common/prlimit.c | 53 +++++++++++++++++++++--- > libc/sysdeps/linux/common/prlimit64.c | 36 ++++++++++++++++ > libc/sysdeps/linux/common/setrlimit.c | 41 +++++++++++------- > libc/sysdeps/linux/common/setrlimit64.c | 24 +++++++++-- > 7 files changed, 197 insertions(+), 42 deletions(-) > create mode 100644 libc/sysdeps/linux/common/prlimit64.c > > diff --git a/include/sys/resource.h b/include/sys/resource.h > index 00c63ff0f1e5..e9fac2c656bf 100644 > --- a/include/sys/resource.h > +++ b/include/sys/resource.h > @@ -106,6 +106,10 @@ libc_hidden_proto(setpriority) > extern int prlimit (__pid_t __pid, enum __rlimit_resource __resource, > const struct rlimit *__new_limit, > struct rlimit *__old_limit) __THROW; > + > +extern int prlimit64 (__pid_t __pid, enum __rlimit_resource __resource, > + const struct rlimit64 *__new_limit, > + struct rlimit64 *__old_limit) __THROW; > #endif > > __END_DECLS > diff --git a/libc/sysdeps/linux/common/getrlimit.c b/libc/sysdeps/linux/common/getrlimit.c > index ad3f4a0e494d..46726fcbd94f 100644 > --- a/libc/sysdeps/linux/common/getrlimit.c > +++ b/libc/sysdeps/linux/common/getrlimit.c > @@ -24,21 +24,53 @@ int getrlimit(__rlimit_resource_t resource, struct rlimit *rlimits) > { > return __syscall_ugetrlimit(resource, rlimits); > } > +libc_hidden_def(getrlimit) > > -#else > - > -# if !defined(__UCLIBC_HANDLE_OLDER_RLIMIT__) > +#elif defined(__NR_prlimit64) > +/* Use prlimit64 if present, the prlimit64 syscall is free from a back > + compatibility stuff for an old getrlimit */ > > -# if defined(__NR_prlimit64) > +# if __WORDSIZE == 32 && !defined(__USE_FILE_OFFSET64) > +/* If struct rlimit has 64-bit fields (if __WORDSIZE == 64 or __USE_FILE_OFFSET64 > + is defined), then use getrlimit as an alias to getrlimit64, see getrlimit64.c */ > int getrlimit(__rlimit_resource_t resource, struct rlimit *rlimits) > { > - return INLINE_SYSCALL (prlimit64, 4, 0, resource, NULL, rlimits); > + struct rlimit64 rlimits64; > + int res = INLINE_SYSCALL (prlimit64, 4, 0, resource, NULL, &rlimits64); > + > + if (res == 0) { > + /* If the syscall succeeds but the values do not fit into a > + rlimit structure set EOVERFLOW errno and retrun -1. */ > + rlimits->rlim_cur = rlimits64.rlim_cur; > + if (rlimits64.rlim_cur != rlimits->rlim_cur) { > + if (rlimits64.rlim_cur != RLIM64_INFINITY) { > + __set_errno(EOVERFLOW); > + return -1; > + } > + rlimits->rlim_cur = RLIM_INFINITY; > + } > + > + rlimits->rlim_max = rlimits64.rlim_max; > + if (rlimits64.rlim_max != rlimits->rlim_max) { > + if (rlimits64.rlim_max != RLIM64_INFINITY) { > + __set_errno(EOVERFLOW); > + return -1; > + } > + rlimits->rlim_max = RLIM_INFINITY; > + } > + } > + return res; > } > -# else > +libc_hidden_def(getrlimit) > +# endif > + > +#else > + > +# if !defined(__UCLIBC_HANDLE_OLDER_RLIMIT__) > + > /* We don't need to wrap getrlimit() */ > _syscall2(int, getrlimit, __rlimit_resource_t, resource, > struct rlimit *, rlim) > -# endif > > # else > > @@ -51,11 +83,7 @@ int getrlimit(__rlimit_resource_t resource, struct rlimit *rlimits) > { > int result; > > -# if defined(__NR_prlimit64) > - result = INLINE_SYSCALL (prlimit64, 4, 0, resource, NULL, rlimits); > -# else > result = __syscall_getrlimit(resource, rlimits); > -# endif > > if (result == -1) > return result; > @@ -69,9 +97,6 @@ int getrlimit(__rlimit_resource_t resource, struct rlimit *rlimits) > return result; > } > # endif > -#endif > -libc_hidden_def(getrlimit) > > -#if __WORDSIZE == 64 > -strong_alias_untyped(getrlimit, getrlimit64) > +libc_hidden_def(getrlimit) > #endif > diff --git a/libc/sysdeps/linux/common/getrlimit64.c b/libc/sysdeps/linux/common/getrlimit64.c > index be98098a1ee0..47f1410fb3d9 100644 > --- a/libc/sysdeps/linux/common/getrlimit64.c > +++ b/libc/sysdeps/linux/common/getrlimit64.c > @@ -17,14 +17,31 @@ > > #include <_lfs_64.h> > #include <bits/wordsize.h> > +#include <sys/resource.h> > +#include <sys/syscall.h> > +#include <stddef.h> // needed for NULL to be defined > > -/* the regular getrlimit will work just fine for 64bit users */ > -#if __WORDSIZE == 32 > > -# include <sys/resource.h> > +#if defined(__NR_prlimit64) > + > +/* the regular prlimit64 will work just fine for 64-bit users */ > +int getrlimit64 (__rlimit_resource_t resource, struct rlimit64 *rlimits) > +{ > + return INLINE_SYSCALL (prlimit64, 4, 0, resource, NULL, rlimits); > +} > + > +# if !defined(__NR_ugetrlimit) && (__WORDSIZE == 64 || defined (__USE_FILE_OFFSET64)) > +/* If getrlimit is not implemented through the __NR_ugetrlimit and size of > + rlimit_t == rlimit64_t then use getrlimit as an alias to getrlimit64 */ > +strong_alias_untyped(getrlimit64, getrlimit) > +libc_hidden_def(getrlimit) > +# endif > + > +#else > > /* Put the soft and hard limits for RESOURCE in *RLIMITS. > - Returns 0 if successful, -1 if not (and sets errno). */ > + Returns 0 if successful, -1 if not (and sets errno). > + The regular getrlimit will work just fine for 64-bit users */ > int getrlimit64 (__rlimit_resource_t resource, struct rlimit64 *rlimits) > { > struct rlimit rlimits32; > @@ -44,3 +61,4 @@ int getrlimit64 (__rlimit_resource_t resource, struct rlimit64 *rlimits) > return 0; > } > #endif > + > diff --git a/libc/sysdeps/linux/common/prlimit.c b/libc/sysdeps/linux/common/prlimit.c > index f44dc166492e..f59ade3a379c 100644 > --- a/libc/sysdeps/linux/common/prlimit.c > +++ b/libc/sysdeps/linux/common/prlimit.c > @@ -17,14 +17,57 @@ > > #include <sys/resource.h> > #include <sysdep.h> > -#include <bits/kernel-features.h> > +#include <stddef.h> // needed for NULL to be defined > > -#if defined __ASSUME_PRLIMIT64 > +#if defined(__NR_prlimit64) && __WORDSIZE == 32 && !defined(__USE_FILE_OFFSET64) > int > prlimit (__pid_t pid, enum __rlimit_resource resource, > - const struct rlimit *new_rlimit, struct rlimit *old_rlimit) > + const struct rlimit *new_rlimit, struct rlimit *old_rlimit) > { > - return INLINE_SYSCALL (prlimit64, 4, pid, resource, new_rlimit, > - old_rlimit); > + struct rlimit64 new_rlimit64; > + struct rlimit64 old_rlimit64; > + int res; > + > + if (new_rlimit != NULL) { > + if (new_rlimit->rlim_cur == RLIM_INFINITY) > + new_rlimit64.rlim_cur = RLIM64_INFINITY; > + else > + new_rlimit64.rlim_cur = new_rlimit->rlim_cur; > + if (new_rlimit->rlim_max == RLIM_INFINITY) > + new_rlimit64.rlim_max = RLIM64_INFINITY; > + else > + new_rlimit64.rlim_max = new_rlimit->rlim_max; > + } > + > + res = INLINE_SYSCALL (prlimit64, 4, pid, resource, &new_rlimit64, > + &old_rlimit64); > + > + if (res == 0 && old_rlimit != NULL) { > + /* If the syscall succeeds but the values do not fit into a > + rlimit structure set EOVERFLOW errno and retrun -1. > + With current Linux implementation of the prlimit64 syscall, > + overflow can't happen. An extra condition has been added to get > + the same behavior as in glibc for future potential overflows. */ > + old_rlimit->rlim_cur = old_rlimit64.rlim_cur; > + if (old_rlimit64.rlim_cur != old_rlimit->rlim_cur) { > + if (new_rlimit == NULL && > + old_rlimit64.rlim_cur != RLIM64_INFINITY) { > + __set_errno(EOVERFLOW); > + return -1; > + } > + old_rlimit->rlim_cur = RLIM_INFINITY; > + } > + old_rlimit->rlim_max = old_rlimit64.rlim_max; > + if (old_rlimit64.rlim_max != old_rlimit->rlim_max) { > + if (new_rlimit == NULL && > + old_rlimit64.rlim_max != RLIM64_INFINITY) { > + __set_errno(EOVERFLOW); > + return -1; > + } > + old_rlimit->rlim_cur = RLIM_INFINITY; > + } > + } > + > + return res; > } > #endif > diff --git a/libc/sysdeps/linux/common/prlimit64.c b/libc/sysdeps/linux/common/prlimit64.c > new file mode 100644 > index 000000000000..6f57b939eeca > --- /dev/null > +++ b/libc/sysdeps/linux/common/prlimit64.c > @@ -0,0 +1,36 @@ > +/* Copyright (C) 2023 uClibc-ng > + * An prlimit64() - get/set resource limits Linux specific syscall. > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Library General Public > + * License as published by the Free Software Foundation; either > + * version 2 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Library General Public License for more details. > + * > + * You should have received a copy of the GNU Library General Public > + * License along with this library; if not, see > + * <http://www.gnu.org/licenses/>. > + */ > + > +#include <sys/resource.h> > +#include <sysdep.h> > + > +#if defined(__NR_prlimit64) > + > +int > +prlimit64 (__pid_t pid, enum __rlimit_resource resource, > + const struct rlimit64 *new_rlimit, struct rlimit64 *old_rlimit) > +{ > + return INLINE_SYSCALL (prlimit64, 4, pid, resource, new_rlimit, > + old_rlimit); > +} > + > +# if __WORDSIZE == 64 || defined (__USE_FILE_OFFSET64) > +strong_alias_untyped(prlimit64, prlimit) > +# endif > + > +#endif > \ No newline at end of file > diff --git a/libc/sysdeps/linux/common/setrlimit.c b/libc/sysdeps/linux/common/setrlimit.c > index 8381afc617fd..9c6707235f3e 100644 > --- a/libc/sysdeps/linux/common/setrlimit.c > +++ b/libc/sysdeps/linux/common/setrlimit.c > @@ -23,21 +23,41 @@ int setrlimit(__rlimit_resource_t resource, const struct rlimit *rlimits) > { > return __syscall_usetrlimit(resource, rlimits); > } > +libc_hidden_def(setrlimit) > > -#else > +#elif defined(__NR_prlimit64) > > -# if !defined(__UCLIBC_HANDLE_OLDER_RLIMIT__) > +/* Use prlimit64 if present, the prlimit64 syscall is free from a back > + compatibility stuff for setrlimit */ > > -# if defined(__NR_prlimit64) > + # if __WORDSIZE == 32 && !defined(__USE_FILE_OFFSET64) > +/* If struct rlimit has 64-bit fields (if __WORDSIZE == 64 or __USE_FILE_OFFSET64 > + is defined), then use setrlimit as an alias to setrlimit64, see setrlimit64.c */ > int setrlimit(__rlimit_resource_t resource, const struct rlimit *rlimits) > { > - return INLINE_SYSCALL (prlimit64, 4, 0, resource, rlimits, NULL); > + struct rlimit64 rlimits64; > + > + if (rlimits->rlim_cur == RLIM_INFINITY) > + rlimits64.rlim_cur = RLIM64_INFINITY; > + else > + rlimits64.rlim_cur = rlimits->rlim_cur; > + if (rlimits->rlim_max == RLIM_INFINITY) > + rlimits64.rlim_max = RLIM64_INFINITY; > + else > + rlimits64.rlim_max = rlimits->rlim_max; > + > + return INLINE_SYSCALL (prlimit64, 4, 0, resource, &rlimits64, NULL); > } > -# else > +libc_hidden_def(setrlimit) > +# endif > + > +#else > + > +# if !defined(__UCLIBC_HANDLE_OLDER_RLIMIT__) > + > /* We don't need to wrap setrlimit() */ > _syscall2(int, setrlimit, __rlimit_resource_t, resource, > const struct rlimit *, rlim) > -# endif > > # else > > @@ -66,16 +86,9 @@ int setrlimit(__rlimit_resource_t resource, const struct rlimit *rlimits) > RLIM_INFINITY >> 1); > rlimits_small.rlim_max = MIN((unsigned long int) rlimits->rlim_max, > RLIM_INFINITY >> 1); > -# if defined(__NR_prlimit64) > - return INLINE_SYSCALL (prlimit64, 4, 0, resource, &rlimits_small, NULL); > -# else > return __syscall_setrlimit(resource, &rlimits_small); > -# endif > } > # endif > -#endif > -libc_hidden_def(setrlimit) > > -#if __WORDSIZE == 64 > -strong_alias_untyped(setrlimit, setrlimit64) > +libc_hidden_def(setrlimit) > #endif > diff --git a/libc/sysdeps/linux/common/setrlimit64.c b/libc/sysdeps/linux/common/setrlimit64.c > index fee14f4ad4b2..3446c58fee12 100644 > --- a/libc/sysdeps/linux/common/setrlimit64.c > +++ b/libc/sysdeps/linux/common/setrlimit64.c > @@ -17,15 +17,31 @@ > > #include <_lfs_64.h> > #include <bits/wordsize.h> > +#include <sys/resource.h> > +#include <sys/syscall.h> > +#include <stddef.h> // needed for NULL to be defined > > -/* the regular setrlimit will work just fine for 64bit users */ > -#if __WORDSIZE == 32 > > -# include <sys/resource.h> > +#if defined(__NR_prlimit64) > + > +int setrlimit64 (__rlimit_resource_t resource, const struct rlimit64 *rlimits) > +{ > + return INLINE_SYSCALL (prlimit64, 4, 0, resource, rlimits, NULL); > +} > + > +# if !defined(__NR_usetrlimit) && (__WORDSIZE == 64 || defined (__USE_FILE_OFFSET64)) > +/* If setrlimit is not implemented through the __NR_usetrlimit and size of > + rlimit_t == rlimit64_t then use setrlimit as an alias to setrlimit64 */ > +strong_alias_untyped(setrlimit64, setrlimit) > +libc_hidden_def(setrlimit) > +# endif > + > +#else > > /* Set the soft and hard limits for RESOURCE to *RLIMITS. > Only the super-user can increase hard limits. > - Return 0 if successful, -1 if not (and sets errno). */ > + Return 0 if successful, -1 if not (and sets errno). > + The regular setrlimit will work just fine for 64bit users */ > int setrlimit64 (__rlimit_resource_t resource, const struct rlimit64 *rlimits) > { > struct rlimit rlimits32;
Hi Yann, Yann Sionneau wrote, > Hi pavel, > > On 10/17/23 12:01, Pavel Kozlov wrote: > > From: Pavel Kozlov <pavel.kozlov@synopsys.com> > > > > Commit 95e38b37 ("add support for systems without legacy setrlimit/getrlimit > > syscalls") has added use of the prlimit64 syscall in getrlimit and setrlimit > > functions. This change causes memory corruption on getrlimit call for 32-bit > > CPUs like ARC, as ARC doesn't have ugetrlimit syscall and uses prlimit64. > > Also, setrlimit has been broken by prlimit64 call on 32-bit CPUs like, i386, > > ARM, ARC. > > Oopsy! Sorry about that, indeed I guess I broke 32 bit CPUs. > > I've started a CI job to test your patch to see if it breaks our arch, I'll > keep the list informed on whether this works or not. > > Thanks a lot! Any news from your side? @pavel: Do you have a test application showing the memory corruption for 32 Bit CPU's which could be added to the uclibc-ng-testsuite? best regards Waldemar
Hi Waldemar, >> >> On 10/17/23 12:01, Pavel Kozlov wrote: >> > From: Pavel Kozlov <pavel.kozlov@synopsys.com> >> > >> > Commit 95e38b37 ("add support for systems without legacy setrlimit/getrlimit >> > syscalls") has added use of the prlimit64 syscall in getrlimit and setrlimit >> > functions. This change causes memory corruption on getrlimit call for 32-bit >> > CPUs like ARC, as ARC doesn't have ugetrlimit syscall and uses prlimit64. >> > Also, setrlimit has been broken by prlimit64 call on 32-bit CPUs like, i386, >> > ARM, ARC. >> >> Oopsy! Sorry about that, indeed I guess I broke 32 bit CPUs. >> >> I've started a CI job to test your patch to see if it breaks our arch, I'll >> keep the list informed on whether this works or not. >> >> Thanks a lot! > > Any news from your side? > @pavel: Do you have a test application showing the memory corruption > for 32 Bit CPU's which could be added to the uclibc-ng-testsuite? No, I don't have a test to reproduce memory corruption. In my configuration for ARC CPU this issue prevented the shell start. The sell crashed while polling input (in poll() function). Actually, it was a fault in return from getdtablesize() function (called inside poll function). This function calls getlimit(), but prlimit64 syscall corrupted the stack and overwrote the return address. As a result I got: --- zebu_hs login: root # potentially unexpected fatal signal 11. Path: /bin/busybox CPU: 0 PID: 75 Comm: sh Not tainted 5.16.0 #11 Insn could not be fetched @No matching VMA found ECR: 0x00040000 EFA: 0x00001000 ERET: 0x00001000 STAT: 0x80081a82 [IE U ] BTA: 0x20021360 SP: 0x5fe0baf4 FP: 0x5fe0bb18 BLK: 0x1000 LPS: 0x20037500 LPE: 0x20037508 LPC: 0x00000000 r00: 0x00000400 r01: 0x00000007 r02: 0x00000000 r03: 0x5fe0bae8 r04: 0x01010101 r05: 0x00000020 r06: 0x20077380 r07: 0x01010101 r08: 0x00000105 r09: 0x00000a3b r10: 0x7f1c0300 r11: 0x01000415 r12: 0x2001f1fc r13: 0xffffffff r14: 0xffffffff r15: 0x5fe0bb68 r16: 0x00000001 r17: 0x00000001 r18: 0x5fe0bbc9 r19: 0xffffffff r20: 0x5fe0bb68 r21: 0x5fe0bc24 r22: 0x5fe0bbc8 r23: 0x200722c0 r24: 0x00000400 r25: 0x2000ab68 --- Any test that calls poll() function of can be used to verify this issue on ARC CPU. On other CPUs the consequences of stack corruption may vary. I just have a small test to ensure that getrlimit/setrlimit/prlimit are working as expected and can read/write limits with a patch, but in functionality it is something like ulimit. -Pavel
Hi Waldemar, On 10/24/23 10:24, Pavel Kozlov wrote: > Hi Waldemar, > >>> On 10/17/23 12:01, Pavel Kozlov wrote: >>>> From: Pavel Kozlov <pavel.kozlov@synopsys.com> >>>> >>>> Commit 95e38b37 ("add support for systems without legacy setrlimit/getrlimit >>>> syscalls") has added use of the prlimit64 syscall in getrlimit and setrlimit >>>> functions. This change causes memory corruption on getrlimit call for 32-bit >>>> CPUs like ARC, as ARC doesn't have ugetrlimit syscall and uses prlimit64. >>>> Also, setrlimit has been broken by prlimit64 call on 32-bit CPUs like, i386, >>>> ARM, ARC. >>> Oopsy! Sorry about that, indeed I guess I broke 32 bit CPUs. >>> >>> I've started a CI job to test your patch to see if it breaks our arch, I'll >>> keep the list informed on whether this works or not. >>> >>> Thanks a lot! >> Any news from your side? I have some issues with my CI but it passed basic tests so let's go I can also say that openrisc shell does not start either with current uClibc-ng master branch, I guess it's because of the same issue. Tested-by: Yann Sionneau <ysionneau@kalrayinc.com> >> @pavel: Do you have a test application showing the memory corruption >> for 32 Bit CPU's which could be added to the uclibc-ng-testsuite? > No, I don't have a test to reproduce memory corruption. In my configuration for ARC > CPU this issue prevented the shell start. The sell crashed while polling input > (in poll() function). Actually, it was a fault in return from getdtablesize() function > (called inside poll function). This function calls getlimit(), but prlimit64 syscall corrupted > the stack and overwrote the return address. As a result I got: > --- > zebu_hs login: root > # potentially unexpected fatal signal 11. > Path: /bin/busybox > CPU: 0 PID: 75 Comm: sh Not tainted 5.16.0 #11 > Insn could not be fetched > @No matching VMA found > ECR: 0x00040000 EFA: 0x00001000 ERET: 0x00001000 > STAT: 0x80081a82 [IE U ] BTA: 0x20021360 > SP: 0x5fe0baf4 FP: 0x5fe0bb18 BLK: 0x1000 > LPS: 0x20037500 LPE: 0x20037508 LPC: 0x00000000 > r00: 0x00000400 r01: 0x00000007 r02: 0x00000000 > r03: 0x5fe0bae8 r04: 0x01010101 r05: 0x00000020 > r06: 0x20077380 r07: 0x01010101 r08: 0x00000105 > r09: 0x00000a3b r10: 0x7f1c0300 r11: 0x01000415 > r12: 0x2001f1fc r13: 0xffffffff r14: 0xffffffff > r15: 0x5fe0bb68 r16: 0x00000001 r17: 0x00000001 > r18: 0x5fe0bbc9 r19: 0xffffffff r20: 0x5fe0bb68 > r21: 0x5fe0bc24 r22: 0x5fe0bbc8 r23: 0x200722c0 > r24: 0x00000400 r25: 0x2000ab68 > --- > Any test that calls poll() function of can be used to verify this issue on ARC CPU. On > other CPUs the consequences of stack corruption may vary. > > I just have a small test to ensure that getrlimit/setrlimit/prlimit are working as > expected and can read/write limits with a patch, but in functionality it is something > like ulimit. > > -Pavel > > >
Hi Pavel, committed and pushed, thanks Waldemar Pavel Kozlov wrote, > From: Pavel Kozlov <pavel.kozlov@synopsys.com> > > Commit 95e38b37 ("add support for systems without legacy setrlimit/getrlimit > syscalls") has added use of the prlimit64 syscall in getrlimit and setrlimit > functions. This change causes memory corruption on getrlimit call for 32-bit > CPUs like ARC, as ARC doesn't have ugetrlimit syscall and uses prlimit64. > Also, setrlimit has been broken by prlimit64 call on 32-bit CPUs like, i386, > ARM, ARC. > > For the prlimit64 syscall the kernel expects an rlimit struct with 64-bit fields, > but on 32-bit CPUs without _FILE_OFFSET_BITS=64 the struct rlimit has 32-bit > fields. > > Add safe implementations of getrlimit, setrlimit, prlimit for 32-bit CPUs with a > local struct rlimit64 variable for use in the prlimit64 syscall. > For 64-bit CPUs and configurations with _FILE_OFFSET_BITS=64 use > getrlimit, setrlimit, prlimit as aliases to getrlimit64, setrlimit64 and > prlimit64. Add a new function prlimit64. > > Tested on aarch64, arm, i386, arc. > > Fixes: 95e38b37 ("add support for systems without legacy setrlimit/getrlimit syscalls") > Signed-off-by: Pavel Kozlov <pavel.kozlov@synopsys.com> > --- > include/sys/resource.h | 4 ++ > libc/sysdeps/linux/common/getrlimit.c | 55 ++++++++++++++++++------- > libc/sysdeps/linux/common/getrlimit64.c | 26 ++++++++++-- > libc/sysdeps/linux/common/prlimit.c | 53 +++++++++++++++++++++--- > libc/sysdeps/linux/common/prlimit64.c | 36 ++++++++++++++++ > libc/sysdeps/linux/common/setrlimit.c | 41 +++++++++++------- > libc/sysdeps/linux/common/setrlimit64.c | 24 +++++++++-- > 7 files changed, 197 insertions(+), 42 deletions(-) > create mode 100644 libc/sysdeps/linux/common/prlimit64.c > > diff --git a/include/sys/resource.h b/include/sys/resource.h > index 00c63ff0f1e5..e9fac2c656bf 100644 > --- a/include/sys/resource.h > +++ b/include/sys/resource.h > @@ -106,6 +106,10 @@ libc_hidden_proto(setpriority) > extern int prlimit (__pid_t __pid, enum __rlimit_resource __resource, > const struct rlimit *__new_limit, > struct rlimit *__old_limit) __THROW; > + > +extern int prlimit64 (__pid_t __pid, enum __rlimit_resource __resource, > + const struct rlimit64 *__new_limit, > + struct rlimit64 *__old_limit) __THROW; > #endif > > __END_DECLS > diff --git a/libc/sysdeps/linux/common/getrlimit.c b/libc/sysdeps/linux/common/getrlimit.c > index ad3f4a0e494d..46726fcbd94f 100644 > --- a/libc/sysdeps/linux/common/getrlimit.c > +++ b/libc/sysdeps/linux/common/getrlimit.c > @@ -24,21 +24,53 @@ int getrlimit(__rlimit_resource_t resource, struct rlimit *rlimits) > { > return __syscall_ugetrlimit(resource, rlimits); > } > +libc_hidden_def(getrlimit) > > -#else > - > -# if !defined(__UCLIBC_HANDLE_OLDER_RLIMIT__) > +#elif defined(__NR_prlimit64) > +/* Use prlimit64 if present, the prlimit64 syscall is free from a back > + compatibility stuff for an old getrlimit */ > > -# if defined(__NR_prlimit64) > +# if __WORDSIZE == 32 && !defined(__USE_FILE_OFFSET64) > +/* If struct rlimit has 64-bit fields (if __WORDSIZE == 64 or __USE_FILE_OFFSET64 > + is defined), then use getrlimit as an alias to getrlimit64, see getrlimit64.c */ > int getrlimit(__rlimit_resource_t resource, struct rlimit *rlimits) > { > - return INLINE_SYSCALL (prlimit64, 4, 0, resource, NULL, rlimits); > + struct rlimit64 rlimits64; > + int res = INLINE_SYSCALL (prlimit64, 4, 0, resource, NULL, &rlimits64); > + > + if (res == 0) { > + /* If the syscall succeeds but the values do not fit into a > + rlimit structure set EOVERFLOW errno and retrun -1. */ > + rlimits->rlim_cur = rlimits64.rlim_cur; > + if (rlimits64.rlim_cur != rlimits->rlim_cur) { > + if (rlimits64.rlim_cur != RLIM64_INFINITY) { > + __set_errno(EOVERFLOW); > + return -1; > + } > + rlimits->rlim_cur = RLIM_INFINITY; > + } > + > + rlimits->rlim_max = rlimits64.rlim_max; > + if (rlimits64.rlim_max != rlimits->rlim_max) { > + if (rlimits64.rlim_max != RLIM64_INFINITY) { > + __set_errno(EOVERFLOW); > + return -1; > + } > + rlimits->rlim_max = RLIM_INFINITY; > + } > + } > + return res; > } > -# else > +libc_hidden_def(getrlimit) > +# endif > + > +#else > + > +# if !defined(__UCLIBC_HANDLE_OLDER_RLIMIT__) > + > /* We don't need to wrap getrlimit() */ > _syscall2(int, getrlimit, __rlimit_resource_t, resource, > struct rlimit *, rlim) > -# endif > > # else > > @@ -51,11 +83,7 @@ int getrlimit(__rlimit_resource_t resource, struct rlimit *rlimits) > { > int result; > > -# if defined(__NR_prlimit64) > - result = INLINE_SYSCALL (prlimit64, 4, 0, resource, NULL, rlimits); > -# else > result = __syscall_getrlimit(resource, rlimits); > -# endif > > if (result == -1) > return result; > @@ -69,9 +97,6 @@ int getrlimit(__rlimit_resource_t resource, struct rlimit *rlimits) > return result; > } > # endif > -#endif > -libc_hidden_def(getrlimit) > > -#if __WORDSIZE == 64 > -strong_alias_untyped(getrlimit, getrlimit64) > +libc_hidden_def(getrlimit) > #endif > diff --git a/libc/sysdeps/linux/common/getrlimit64.c b/libc/sysdeps/linux/common/getrlimit64.c > index be98098a1ee0..47f1410fb3d9 100644 > --- a/libc/sysdeps/linux/common/getrlimit64.c > +++ b/libc/sysdeps/linux/common/getrlimit64.c > @@ -17,14 +17,31 @@ > > #include <_lfs_64.h> > #include <bits/wordsize.h> > +#include <sys/resource.h> > +#include <sys/syscall.h> > +#include <stddef.h> // needed for NULL to be defined > > -/* the regular getrlimit will work just fine for 64bit users */ > -#if __WORDSIZE == 32 > > -# include <sys/resource.h> > +#if defined(__NR_prlimit64) > + > +/* the regular prlimit64 will work just fine for 64-bit users */ > +int getrlimit64 (__rlimit_resource_t resource, struct rlimit64 *rlimits) > +{ > + return INLINE_SYSCALL (prlimit64, 4, 0, resource, NULL, rlimits); > +} > + > +# if !defined(__NR_ugetrlimit) && (__WORDSIZE == 64 || defined (__USE_FILE_OFFSET64)) > +/* If getrlimit is not implemented through the __NR_ugetrlimit and size of > + rlimit_t == rlimit64_t then use getrlimit as an alias to getrlimit64 */ > +strong_alias_untyped(getrlimit64, getrlimit) > +libc_hidden_def(getrlimit) > +# endif > + > +#else > > /* Put the soft and hard limits for RESOURCE in *RLIMITS. > - Returns 0 if successful, -1 if not (and sets errno). */ > + Returns 0 if successful, -1 if not (and sets errno). > + The regular getrlimit will work just fine for 64-bit users */ > int getrlimit64 (__rlimit_resource_t resource, struct rlimit64 *rlimits) > { > struct rlimit rlimits32; > @@ -44,3 +61,4 @@ int getrlimit64 (__rlimit_resource_t resource, struct rlimit64 *rlimits) > return 0; > } > #endif > + > diff --git a/libc/sysdeps/linux/common/prlimit.c b/libc/sysdeps/linux/common/prlimit.c > index f44dc166492e..f59ade3a379c 100644 > --- a/libc/sysdeps/linux/common/prlimit.c > +++ b/libc/sysdeps/linux/common/prlimit.c > @@ -17,14 +17,57 @@ > > #include <sys/resource.h> > #include <sysdep.h> > -#include <bits/kernel-features.h> > +#include <stddef.h> // needed for NULL to be defined > > -#if defined __ASSUME_PRLIMIT64 > +#if defined(__NR_prlimit64) && __WORDSIZE == 32 && !defined(__USE_FILE_OFFSET64) > int > prlimit (__pid_t pid, enum __rlimit_resource resource, > - const struct rlimit *new_rlimit, struct rlimit *old_rlimit) > + const struct rlimit *new_rlimit, struct rlimit *old_rlimit) > { > - return INLINE_SYSCALL (prlimit64, 4, pid, resource, new_rlimit, > - old_rlimit); > + struct rlimit64 new_rlimit64; > + struct rlimit64 old_rlimit64; > + int res; > + > + if (new_rlimit != NULL) { > + if (new_rlimit->rlim_cur == RLIM_INFINITY) > + new_rlimit64.rlim_cur = RLIM64_INFINITY; > + else > + new_rlimit64.rlim_cur = new_rlimit->rlim_cur; > + if (new_rlimit->rlim_max == RLIM_INFINITY) > + new_rlimit64.rlim_max = RLIM64_INFINITY; > + else > + new_rlimit64.rlim_max = new_rlimit->rlim_max; > + } > + > + res = INLINE_SYSCALL (prlimit64, 4, pid, resource, &new_rlimit64, > + &old_rlimit64); > + > + if (res == 0 && old_rlimit != NULL) { > + /* If the syscall succeeds but the values do not fit into a > + rlimit structure set EOVERFLOW errno and retrun -1. > + With current Linux implementation of the prlimit64 syscall, > + overflow can't happen. An extra condition has been added to get > + the same behavior as in glibc for future potential overflows. */ > + old_rlimit->rlim_cur = old_rlimit64.rlim_cur; > + if (old_rlimit64.rlim_cur != old_rlimit->rlim_cur) { > + if (new_rlimit == NULL && > + old_rlimit64.rlim_cur != RLIM64_INFINITY) { > + __set_errno(EOVERFLOW); > + return -1; > + } > + old_rlimit->rlim_cur = RLIM_INFINITY; > + } > + old_rlimit->rlim_max = old_rlimit64.rlim_max; > + if (old_rlimit64.rlim_max != old_rlimit->rlim_max) { > + if (new_rlimit == NULL && > + old_rlimit64.rlim_max != RLIM64_INFINITY) { > + __set_errno(EOVERFLOW); > + return -1; > + } > + old_rlimit->rlim_cur = RLIM_INFINITY; > + } > + } > + > + return res; > } > #endif > diff --git a/libc/sysdeps/linux/common/prlimit64.c b/libc/sysdeps/linux/common/prlimit64.c > new file mode 100644 > index 000000000000..6f57b939eeca > --- /dev/null > +++ b/libc/sysdeps/linux/common/prlimit64.c > @@ -0,0 +1,36 @@ > +/* Copyright (C) 2023 uClibc-ng > + * An prlimit64() - get/set resource limits Linux specific syscall. > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Library General Public > + * License as published by the Free Software Foundation; either > + * version 2 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Library General Public License for more details. > + * > + * You should have received a copy of the GNU Library General Public > + * License along with this library; if not, see > + * <http://www.gnu.org/licenses/>. > + */ > + > +#include <sys/resource.h> > +#include <sysdep.h> > + > +#if defined(__NR_prlimit64) > + > +int > +prlimit64 (__pid_t pid, enum __rlimit_resource resource, > + const struct rlimit64 *new_rlimit, struct rlimit64 *old_rlimit) > +{ > + return INLINE_SYSCALL (prlimit64, 4, pid, resource, new_rlimit, > + old_rlimit); > +} > + > +# if __WORDSIZE == 64 || defined (__USE_FILE_OFFSET64) > +strong_alias_untyped(prlimit64, prlimit) > +# endif > + > +#endif > \ No newline at end of file > diff --git a/libc/sysdeps/linux/common/setrlimit.c b/libc/sysdeps/linux/common/setrlimit.c > index 8381afc617fd..9c6707235f3e 100644 > --- a/libc/sysdeps/linux/common/setrlimit.c > +++ b/libc/sysdeps/linux/common/setrlimit.c > @@ -23,21 +23,41 @@ int setrlimit(__rlimit_resource_t resource, const struct rlimit *rlimits) > { > return __syscall_usetrlimit(resource, rlimits); > } > +libc_hidden_def(setrlimit) > > -#else > +#elif defined(__NR_prlimit64) > > -# if !defined(__UCLIBC_HANDLE_OLDER_RLIMIT__) > +/* Use prlimit64 if present, the prlimit64 syscall is free from a back > + compatibility stuff for setrlimit */ > > -# if defined(__NR_prlimit64) > + # if __WORDSIZE == 32 && !defined(__USE_FILE_OFFSET64) > +/* If struct rlimit has 64-bit fields (if __WORDSIZE == 64 or __USE_FILE_OFFSET64 > + is defined), then use setrlimit as an alias to setrlimit64, see setrlimit64.c */ > int setrlimit(__rlimit_resource_t resource, const struct rlimit *rlimits) > { > - return INLINE_SYSCALL (prlimit64, 4, 0, resource, rlimits, NULL); > + struct rlimit64 rlimits64; > + > + if (rlimits->rlim_cur == RLIM_INFINITY) > + rlimits64.rlim_cur = RLIM64_INFINITY; > + else > + rlimits64.rlim_cur = rlimits->rlim_cur; > + if (rlimits->rlim_max == RLIM_INFINITY) > + rlimits64.rlim_max = RLIM64_INFINITY; > + else > + rlimits64.rlim_max = rlimits->rlim_max; > + > + return INLINE_SYSCALL (prlimit64, 4, 0, resource, &rlimits64, NULL); > } > -# else > +libc_hidden_def(setrlimit) > +# endif > + > +#else > + > +# if !defined(__UCLIBC_HANDLE_OLDER_RLIMIT__) > + > /* We don't need to wrap setrlimit() */ > _syscall2(int, setrlimit, __rlimit_resource_t, resource, > const struct rlimit *, rlim) > -# endif > > # else > > @@ -66,16 +86,9 @@ int setrlimit(__rlimit_resource_t resource, const struct rlimit *rlimits) > RLIM_INFINITY >> 1); > rlimits_small.rlim_max = MIN((unsigned long int) rlimits->rlim_max, > RLIM_INFINITY >> 1); > -# if defined(__NR_prlimit64) > - return INLINE_SYSCALL (prlimit64, 4, 0, resource, &rlimits_small, NULL); > -# else > return __syscall_setrlimit(resource, &rlimits_small); > -# endif > } > # endif > -#endif > -libc_hidden_def(setrlimit) > > -#if __WORDSIZE == 64 > -strong_alias_untyped(setrlimit, setrlimit64) > +libc_hidden_def(setrlimit) > #endif > diff --git a/libc/sysdeps/linux/common/setrlimit64.c b/libc/sysdeps/linux/common/setrlimit64.c > index fee14f4ad4b2..3446c58fee12 100644 > --- a/libc/sysdeps/linux/common/setrlimit64.c > +++ b/libc/sysdeps/linux/common/setrlimit64.c > @@ -17,15 +17,31 @@ > > #include <_lfs_64.h> > #include <bits/wordsize.h> > +#include <sys/resource.h> > +#include <sys/syscall.h> > +#include <stddef.h> // needed for NULL to be defined > > -/* the regular setrlimit will work just fine for 64bit users */ > -#if __WORDSIZE == 32 > > -# include <sys/resource.h> > +#if defined(__NR_prlimit64) > + > +int setrlimit64 (__rlimit_resource_t resource, const struct rlimit64 *rlimits) > +{ > + return INLINE_SYSCALL (prlimit64, 4, 0, resource, rlimits, NULL); > +} > + > +# if !defined(__NR_usetrlimit) && (__WORDSIZE == 64 || defined (__USE_FILE_OFFSET64)) > +/* If setrlimit is not implemented through the __NR_usetrlimit and size of > + rlimit_t == rlimit64_t then use setrlimit as an alias to setrlimit64 */ > +strong_alias_untyped(setrlimit64, setrlimit) > +libc_hidden_def(setrlimit) > +# endif > + > +#else > > /* Set the soft and hard limits for RESOURCE to *RLIMITS. > Only the super-user can increase hard limits. > - Return 0 if successful, -1 if not (and sets errno). */ > + Return 0 if successful, -1 if not (and sets errno). > + The regular setrlimit will work just fine for 64bit users */ > int setrlimit64 (__rlimit_resource_t resource, const struct rlimit64 *rlimits) > { > struct rlimit rlimits32; > -- > 2.25.1 > > _______________________________________________ > devel mailing list -- devel@uclibc-ng.org > To unsubscribe send an email to devel-leave@uclibc-ng.org >
diff --git a/include/sys/resource.h b/include/sys/resource.h index 00c63ff0f1e5..e9fac2c656bf 100644 --- a/include/sys/resource.h +++ b/include/sys/resource.h @@ -106,6 +106,10 @@ libc_hidden_proto(setpriority) extern int prlimit (__pid_t __pid, enum __rlimit_resource __resource, const struct rlimit *__new_limit, struct rlimit *__old_limit) __THROW; + +extern int prlimit64 (__pid_t __pid, enum __rlimit_resource __resource, + const struct rlimit64 *__new_limit, + struct rlimit64 *__old_limit) __THROW; #endif __END_DECLS diff --git a/libc/sysdeps/linux/common/getrlimit.c b/libc/sysdeps/linux/common/getrlimit.c index ad3f4a0e494d..46726fcbd94f 100644 --- a/libc/sysdeps/linux/common/getrlimit.c +++ b/libc/sysdeps/linux/common/getrlimit.c @@ -24,21 +24,53 @@ int getrlimit(__rlimit_resource_t resource, struct rlimit *rlimits) { return __syscall_ugetrlimit(resource, rlimits); } +libc_hidden_def(getrlimit) -#else - -# if !defined(__UCLIBC_HANDLE_OLDER_RLIMIT__) +#elif defined(__NR_prlimit64) +/* Use prlimit64 if present, the prlimit64 syscall is free from a back + compatibility stuff for an old getrlimit */ -# if defined(__NR_prlimit64) +# if __WORDSIZE == 32 && !defined(__USE_FILE_OFFSET64) +/* If struct rlimit has 64-bit fields (if __WORDSIZE == 64 or __USE_FILE_OFFSET64 + is defined), then use getrlimit as an alias to getrlimit64, see getrlimit64.c */ int getrlimit(__rlimit_resource_t resource, struct rlimit *rlimits) { - return INLINE_SYSCALL (prlimit64, 4, 0, resource, NULL, rlimits); + struct rlimit64 rlimits64; + int res = INLINE_SYSCALL (prlimit64, 4, 0, resource, NULL, &rlimits64); + + if (res == 0) { + /* If the syscall succeeds but the values do not fit into a + rlimit structure set EOVERFLOW errno and retrun -1. */ + rlimits->rlim_cur = rlimits64.rlim_cur; + if (rlimits64.rlim_cur != rlimits->rlim_cur) { + if (rlimits64.rlim_cur != RLIM64_INFINITY) { + __set_errno(EOVERFLOW); + return -1; + } + rlimits->rlim_cur = RLIM_INFINITY; + } + + rlimits->rlim_max = rlimits64.rlim_max; + if (rlimits64.rlim_max != rlimits->rlim_max) { + if (rlimits64.rlim_max != RLIM64_INFINITY) { + __set_errno(EOVERFLOW); + return -1; + } + rlimits->rlim_max = RLIM_INFINITY; + } + } + return res; } -# else +libc_hidden_def(getrlimit) +# endif + +#else + +# if !defined(__UCLIBC_HANDLE_OLDER_RLIMIT__) + /* We don't need to wrap getrlimit() */ _syscall2(int, getrlimit, __rlimit_resource_t, resource, struct rlimit *, rlim) -# endif # else @@ -51,11 +83,7 @@ int getrlimit(__rlimit_resource_t resource, struct rlimit *rlimits) { int result; -# if defined(__NR_prlimit64) - result = INLINE_SYSCALL (prlimit64, 4, 0, resource, NULL, rlimits); -# else result = __syscall_getrlimit(resource, rlimits); -# endif if (result == -1) return result; @@ -69,9 +97,6 @@ int getrlimit(__rlimit_resource_t resource, struct rlimit *rlimits) return result; } # endif -#endif -libc_hidden_def(getrlimit) -#if __WORDSIZE == 64 -strong_alias_untyped(getrlimit, getrlimit64) +libc_hidden_def(getrlimit) #endif diff --git a/libc/sysdeps/linux/common/getrlimit64.c b/libc/sysdeps/linux/common/getrlimit64.c index be98098a1ee0..47f1410fb3d9 100644 --- a/libc/sysdeps/linux/common/getrlimit64.c +++ b/libc/sysdeps/linux/common/getrlimit64.c @@ -17,14 +17,31 @@ #include <_lfs_64.h> #include <bits/wordsize.h> +#include <sys/resource.h> +#include <sys/syscall.h> +#include <stddef.h> // needed for NULL to be defined -/* the regular getrlimit will work just fine for 64bit users */ -#if __WORDSIZE == 32 -# include <sys/resource.h> +#if defined(__NR_prlimit64) + +/* the regular prlimit64 will work just fine for 64-bit users */ +int getrlimit64 (__rlimit_resource_t resource, struct rlimit64 *rlimits) +{ + return INLINE_SYSCALL (prlimit64, 4, 0, resource, NULL, rlimits); +} + +# if !defined(__NR_ugetrlimit) && (__WORDSIZE == 64 || defined (__USE_FILE_OFFSET64)) +/* If getrlimit is not implemented through the __NR_ugetrlimit and size of + rlimit_t == rlimit64_t then use getrlimit as an alias to getrlimit64 */ +strong_alias_untyped(getrlimit64, getrlimit) +libc_hidden_def(getrlimit) +# endif + +#else /* Put the soft and hard limits for RESOURCE in *RLIMITS. - Returns 0 if successful, -1 if not (and sets errno). */ + Returns 0 if successful, -1 if not (and sets errno). + The regular getrlimit will work just fine for 64-bit users */ int getrlimit64 (__rlimit_resource_t resource, struct rlimit64 *rlimits) { struct rlimit rlimits32; @@ -44,3 +61,4 @@ int getrlimit64 (__rlimit_resource_t resource, struct rlimit64 *rlimits) return 0; } #endif + diff --git a/libc/sysdeps/linux/common/prlimit.c b/libc/sysdeps/linux/common/prlimit.c index f44dc166492e..f59ade3a379c 100644 --- a/libc/sysdeps/linux/common/prlimit.c +++ b/libc/sysdeps/linux/common/prlimit.c @@ -17,14 +17,57 @@ #include <sys/resource.h> #include <sysdep.h> -#include <bits/kernel-features.h> +#include <stddef.h> // needed for NULL to be defined -#if defined __ASSUME_PRLIMIT64 +#if defined(__NR_prlimit64) && __WORDSIZE == 32 && !defined(__USE_FILE_OFFSET64) int prlimit (__pid_t pid, enum __rlimit_resource resource, - const struct rlimit *new_rlimit, struct rlimit *old_rlimit) + const struct rlimit *new_rlimit, struct rlimit *old_rlimit) { - return INLINE_SYSCALL (prlimit64, 4, pid, resource, new_rlimit, - old_rlimit); + struct rlimit64 new_rlimit64; + struct rlimit64 old_rlimit64; + int res; + + if (new_rlimit != NULL) { + if (new_rlimit->rlim_cur == RLIM_INFINITY) + new_rlimit64.rlim_cur = RLIM64_INFINITY; + else + new_rlimit64.rlim_cur = new_rlimit->rlim_cur; + if (new_rlimit->rlim_max == RLIM_INFINITY) + new_rlimit64.rlim_max = RLIM64_INFINITY; + else + new_rlimit64.rlim_max = new_rlimit->rlim_max; + } + + res = INLINE_SYSCALL (prlimit64, 4, pid, resource, &new_rlimit64, + &old_rlimit64); + + if (res == 0 && old_rlimit != NULL) { + /* If the syscall succeeds but the values do not fit into a + rlimit structure set EOVERFLOW errno and retrun -1. + With current Linux implementation of the prlimit64 syscall, + overflow can't happen. An extra condition has been added to get + the same behavior as in glibc for future potential overflows. */ + old_rlimit->rlim_cur = old_rlimit64.rlim_cur; + if (old_rlimit64.rlim_cur != old_rlimit->rlim_cur) { + if (new_rlimit == NULL && + old_rlimit64.rlim_cur != RLIM64_INFINITY) { + __set_errno(EOVERFLOW); + return -1; + } + old_rlimit->rlim_cur = RLIM_INFINITY; + } + old_rlimit->rlim_max = old_rlimit64.rlim_max; + if (old_rlimit64.rlim_max != old_rlimit->rlim_max) { + if (new_rlimit == NULL && + old_rlimit64.rlim_max != RLIM64_INFINITY) { + __set_errno(EOVERFLOW); + return -1; + } + old_rlimit->rlim_cur = RLIM_INFINITY; + } + } + + return res; } #endif diff --git a/libc/sysdeps/linux/common/prlimit64.c b/libc/sysdeps/linux/common/prlimit64.c new file mode 100644 index 000000000000..6f57b939eeca --- /dev/null +++ b/libc/sysdeps/linux/common/prlimit64.c @@ -0,0 +1,36 @@ +/* Copyright (C) 2023 uClibc-ng + * An prlimit64() - get/set resource limits Linux specific syscall. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <sys/resource.h> +#include <sysdep.h> + +#if defined(__NR_prlimit64) + +int +prlimit64 (__pid_t pid, enum __rlimit_resource resource, + const struct rlimit64 *new_rlimit, struct rlimit64 *old_rlimit) +{ + return INLINE_SYSCALL (prlimit64, 4, pid, resource, new_rlimit, + old_rlimit); +} + +# if __WORDSIZE == 64 || defined (__USE_FILE_OFFSET64) +strong_alias_untyped(prlimit64, prlimit) +# endif + +#endif \ No newline at end of file diff --git a/libc/sysdeps/linux/common/setrlimit.c b/libc/sysdeps/linux/common/setrlimit.c index 8381afc617fd..9c6707235f3e 100644 --- a/libc/sysdeps/linux/common/setrlimit.c +++ b/libc/sysdeps/linux/common/setrlimit.c @@ -23,21 +23,41 @@ int setrlimit(__rlimit_resource_t resource, const struct rlimit *rlimits) { return __syscall_usetrlimit(resource, rlimits); } +libc_hidden_def(setrlimit) -#else +#elif defined(__NR_prlimit64) -# if !defined(__UCLIBC_HANDLE_OLDER_RLIMIT__) +/* Use prlimit64 if present, the prlimit64 syscall is free from a back + compatibility stuff for setrlimit */ -# if defined(__NR_prlimit64) + # if __WORDSIZE == 32 && !defined(__USE_FILE_OFFSET64) +/* If struct rlimit has 64-bit fields (if __WORDSIZE == 64 or __USE_FILE_OFFSET64 + is defined), then use setrlimit as an alias to setrlimit64, see setrlimit64.c */ int setrlimit(__rlimit_resource_t resource, const struct rlimit *rlimits) { - return INLINE_SYSCALL (prlimit64, 4, 0, resource, rlimits, NULL); + struct rlimit64 rlimits64; + + if (rlimits->rlim_cur == RLIM_INFINITY) + rlimits64.rlim_cur = RLIM64_INFINITY; + else + rlimits64.rlim_cur = rlimits->rlim_cur; + if (rlimits->rlim_max == RLIM_INFINITY) + rlimits64.rlim_max = RLIM64_INFINITY; + else + rlimits64.rlim_max = rlimits->rlim_max; + + return INLINE_SYSCALL (prlimit64, 4, 0, resource, &rlimits64, NULL); } -# else +libc_hidden_def(setrlimit) +# endif + +#else + +# if !defined(__UCLIBC_HANDLE_OLDER_RLIMIT__) + /* We don't need to wrap setrlimit() */ _syscall2(int, setrlimit, __rlimit_resource_t, resource, const struct rlimit *, rlim) -# endif # else @@ -66,16 +86,9 @@ int setrlimit(__rlimit_resource_t resource, const struct rlimit *rlimits) RLIM_INFINITY >> 1); rlimits_small.rlim_max = MIN((unsigned long int) rlimits->rlim_max, RLIM_INFINITY >> 1); -# if defined(__NR_prlimit64) - return INLINE_SYSCALL (prlimit64, 4, 0, resource, &rlimits_small, NULL); -# else return __syscall_setrlimit(resource, &rlimits_small); -# endif } # endif -#endif -libc_hidden_def(setrlimit) -#if __WORDSIZE == 64 -strong_alias_untyped(setrlimit, setrlimit64) +libc_hidden_def(setrlimit) #endif diff --git a/libc/sysdeps/linux/common/setrlimit64.c b/libc/sysdeps/linux/common/setrlimit64.c index fee14f4ad4b2..3446c58fee12 100644 --- a/libc/sysdeps/linux/common/setrlimit64.c +++ b/libc/sysdeps/linux/common/setrlimit64.c @@ -17,15 +17,31 @@ #include <_lfs_64.h> #include <bits/wordsize.h> +#include <sys/resource.h> +#include <sys/syscall.h> +#include <stddef.h> // needed for NULL to be defined -/* the regular setrlimit will work just fine for 64bit users */ -#if __WORDSIZE == 32 -# include <sys/resource.h> +#if defined(__NR_prlimit64) + +int setrlimit64 (__rlimit_resource_t resource, const struct rlimit64 *rlimits) +{ + return INLINE_SYSCALL (prlimit64, 4, 0, resource, rlimits, NULL); +} + +# if !defined(__NR_usetrlimit) && (__WORDSIZE == 64 || defined (__USE_FILE_OFFSET64)) +/* If setrlimit is not implemented through the __NR_usetrlimit and size of + rlimit_t == rlimit64_t then use setrlimit as an alias to setrlimit64 */ +strong_alias_untyped(setrlimit64, setrlimit) +libc_hidden_def(setrlimit) +# endif + +#else /* Set the soft and hard limits for RESOURCE to *RLIMITS. Only the super-user can increase hard limits. - Return 0 if successful, -1 if not (and sets errno). */ + Return 0 if successful, -1 if not (and sets errno). + The regular setrlimit will work just fine for 64bit users */ int setrlimit64 (__rlimit_resource_t resource, const struct rlimit64 *rlimits) { struct rlimit rlimits32;