diff mbox series

[uclibc-ng-devel] setrlimit/getrlimit: fix prlimit64 syscall use for 32-bit CPUs

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

Commit Message

Pavel Kozlov Oct. 17, 2023, 10:01 a.m. UTC
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

Comments

Yann Sionneau Oct. 17, 2023, 1:36 p.m. UTC | #1
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;
Waldemar Brodkorb Oct. 21, 2023, 4:52 a.m. UTC | #2
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
Pavel Kozlov Oct. 24, 2023, 8:24 a.m. UTC | #3
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
Yann Sionneau Oct. 24, 2023, 8:35 a.m. UTC | #4
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
>
>
>
Waldemar Brodkorb Nov. 2, 2023, 8:42 a.m. UTC | #5
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 mbox series

Patch

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;