The Problem: When rlim_t Cannot Hold the Value
In some programming environments, the rlim_t data type is too small to represent a resource limit value that the kernel actually holds. This is a portability problem that arises specifically on 32-bit systems compiled with large-file support. It leads to silent incorrect behaviour where setting a resource limit appears to succeed but the value stored in the kernel is different from what you asked for.
How the Problem Arises
rlim_t is defined to be the same size as off_t. On a 32-bit system with standard compilation, both are 32 bits. But if you compile with _FILE_OFFSET_BITS=64 (large-file support), off_t becomes 64 bits — and so does rlim_t in the C library.
The problem is that the Linux kernel on x86-32 represents resource limits internally as unsigned long, which is only 32 bits. So you have a 64-bit glibc wrapper talking to a 32-bit kernel representation.
rlim_t = 64 bits, kernel = 64 bits. No mismatch. RLIM_SAVED_CUR and RLIM_SAVED_MAX are defined equal to RLIM_INFINITY (meaning all values are representable).
glibc rlim_t = 64 bits, kernel unsigned long = 32 bits. Mismatch! Values larger than 2^32-1 cannot be correctly passed to the kernel.
RLIM_SAVED_CUR and RLIM_SAVED_MAX
SUSv3 defines two special constants for portable handling of this situation:
- RLIM_SAVED_CUR — returned by getrlimit() in rlim_cur when the soft limit cannot be represented in the current rlim_t type.
- RLIM_SAVED_MAX — returned by getrlimit() in rlim_max when the hard limit cannot be represented.
On 64-bit Linux, these constants are defined equal to RLIM_INFINITY because there is no representability problem — all values fit. On 32-bit Linux with large-file support, they would theoretically indicate an unrepresentable value, but glibc takes a different approach (see below).
/* Portable reading of a limit */
struct rlimit rl;
getrlimit(RLIMIT_FSIZE, &rl);
if (rl.rlim_cur == RLIM_INFINITY)
printf("No file size limit\n");
#ifdef RLIM_SAVED_CUR
else if (rl.rlim_cur == RLIM_SAVED_CUR)
printf("Limit unrepresentable in this environment\n");
#endif
else
printf("File size limit: %lld bytes\n", (long long)rl.rlim_cur);
glibc’s Silent Conversion Behaviour on x86-32
On 32-bit x86 with _FILE_OFFSET_BITS=64, the glibc setrlimit() wrapper handles the overflow differently from what RLIM_SAVED_CUR would suggest. The behaviour is:
_FILE_OFFSET_BITS=64 tries to set a resource limit to a value larger than can fit in a 32-bit unsigned long (i.e. > 0xFFFFFFFF), the glibc setrlimit() wrapper silently converts that value to RLIM_INFINITY instead of returning an error. In other words, the limit you requested is silently not honored.This is a design compromise the glibc developers made. Ideally setrlimit() would return EINVAL, but the fundamental problem is a kernel limitation. Returning success while not honoring the request is arguably worse, but it is the current behaviour.
This affects not just application programmers but end users — many file-handling utilities on x86-32 distributions are compiled with _FILE_OFFSET_BITS=64 and thus silently fail to set large resource limits.
Code Example — Checking for Unrepresentable Values
#include <stdio.h>
#include <sys/resource.h>
/* Portable limit printing that handles all three cases */
void print_rlimit_value(const char *label, rlim_t val)
{
printf("%s: ", label);
if (val == RLIM_INFINITY) {
printf("unlimited (RLIM_INFINITY)\n");
return;
}
#ifdef RLIM_SAVED_CUR
/* On some 32-bit systems this may differ from RLIM_INFINITY */
if (val == RLIM_SAVED_CUR || val == RLIM_SAVED_MAX) {
printf("UNREPRESENTABLE in this rlim_t type\n");
return;
}
#endif
printf("%lld\n", (long long)val);
}
int main(void)
{
struct rlimit rl;
getrlimit(RLIMIT_FSIZE, &rl);
print_rlimit_value("RLIMIT_FSIZE soft", rl.rlim_cur);
print_rlimit_value("RLIMIT_FSIZE hard", rl.rlim_max);
getrlimit(RLIMIT_AS, &rl);
print_rlimit_value("RLIMIT_AS soft", rl.rlim_cur);
print_rlimit_value("RLIMIT_AS hard", rl.rlim_max);
return 0;
}
/* On 64-bit Linux: RLIM_SAVED_CUR == RLIM_INFINITY so the
#ifdef block is never entered — all values are representable. */
Interview Questions
RLIM_SAVED_CUR is an SUSv3-defined constant returned by getrlimit() in the rlim_cur field when the current soft limit cannot be represented in the rlim_t data type of the calling program’s environment. It indicates an “unrepresentable” limit value. On 64-bit Linux, RLIM_SAVED_CUR is defined equal to RLIM_INFINITY because all values fit in the 64-bit rlim_t.
On 64-bit Linux, both glibc’s rlim_t and the kernel’s internal representation are 64 bits wide — there is no mismatch. On 32-bit x86 with _FILE_OFFSET_BITS=64, glibc’s rlim_t becomes 64 bits wide but the kernel still uses a 32-bit unsigned long internally, creating a size mismatch where large values cannot be passed correctly.
If a value larger than a 32-bit unsigned long is passed to setrlimit() on an x86-32 system compiled with _FILE_OFFSET_BITS=64, the glibc wrapper silently converts the value to RLIM_INFINITY. It does not return an error. This means the requested limit is silently not honored — the kernel sees “unlimited” instead of the large value you specified.
