xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/getrlimit/getrlimit03.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2018 Google, Inc.
4  *
5  * Architectures may provide up to three syscalls that have been used to
6  * implement getrlimit(2) in different libc implementations.  These syscalls
7  * differ in the size and signedness of rlim_t:
8  *
9  * - __NR_getrlimit uses long or unsigned long, depending on the
10  *   architecture
11  *
12  * - __NR_ugetrlimit uses unsigned long, and only exists on
13  *   architectures where __NR_getrlimit is signed
14  *
15  * - __NR_prlimit64 uses uint64_t
16  *
17  * This test compares the results returned by all three syscalls, confirming
18  * that they either match or were appropriately capped at the respective
19  * RLIM_INFINITY constant.
20  */
21 
22 #include <inttypes.h>
23 #include <stdint.h>
24 #include <sys/time.h>
25 #include <sys/resource.h>
26 
27 #include "tst_test.h"
28 #include "lapi/syscalls.h"
29 #include "lapi/abisize.h"
30 
31 /**
32  * Linux provides an "old" getrlimit syscall handler that uses signed long,
33  * and a "new" getrlimit syscall handler that uses unsigned long.
34  *
35  * The underlying syscall names vary across architectures, depending on whether
36  * the architecture predates the "new" handler.  For clarity, this test
37  * will call them getrlimit_long and getlimit_ulong internally.
38  *
39  * __NR_getrlimit has been deprecated from arm EABI and moved to OABI_COMPAT,
40  * so the syscall on arm may or may not be available even if __NR_ugetrlimit
41  * exists.
42  */
43 #if __NR_ugetrlimit != __LTP__NR_INVALID_SYSCALL
44 # if !defined(__arm__) || __NR_getrlimit != __LTP__NR_INVALID_SYSCALL
45 #  define SIGNED_GETRLIMIT
46 # endif
47 # define __NR_getrlimit_ulong		__NR_ugetrlimit
48 # define __NR_getrlimit_ulong_str	"__NR_ugetrlimit"
49 #else
50 # define __NR_getrlimit_ulong		__NR_getrlimit
51 # define __NR_getrlimit_ulong_str	"__NR_getrlimit"
52 #endif
53 
54 #ifndef HAVE_STRUCT_RLIMIT64
55 struct rlimit64 {
56 	uint64_t rlim_cur;
57 	uint64_t rlim_max;
58 };
59 #endif
60 const uint64_t RLIM_INFINITY_U64 = UINT64_MAX;
61 
getrlimit_u64(int resource,struct rlimit64 * rlim)62 static int getrlimit_u64(int resource, struct rlimit64 *rlim)
63 {
64 	return tst_syscall(__NR_prlimit64, 0, resource, NULL, rlim);
65 }
66 
67 struct rlimit_ulong {
68 	unsigned long rlim_cur;
69 	unsigned long rlim_max;
70 };
71 
72 #if defined(__mips__) && defined(TST_ABI32)
73 	const unsigned long RLIM_INFINITY_UL = 0x7fffffffUL;
74 #else
75 	const unsigned long RLIM_INFINITY_UL = ULONG_MAX;
76 #endif
77 
getrlimit_ulong(int resource,struct rlimit_ulong * rlim)78 static int getrlimit_ulong(int resource, struct rlimit_ulong *rlim)
79 {
80 	return syscall(__NR_getrlimit_ulong, resource, rlim);
81 }
82 
83 const long RLIM_INFINITY_L = LONG_MAX;
84 
85 #ifdef SIGNED_GETRLIMIT
86 struct rlimit_long {
87 	long rlim_cur;
88 	long rlim_max;
89 };
90 
getrlimit_long(int resource,struct rlimit_long * rlim)91 static int getrlimit_long(int resource, struct rlimit_long *rlim)
92 {
93 	return syscall(__NR_getrlimit, resource, rlim);
94 }
95 #endif
96 
compare_retval(int resource,int ret_u64,int errno_u64,int ret_other,int errno_other,const char * other_syscall)97 static int compare_retval(int resource, int ret_u64, int errno_u64,
98 			  int ret_other, int errno_other,
99 			  const char *other_syscall)
100 {
101 	if (ret_u64 != ret_other || errno_u64 != errno_other) {
102 		tst_res(TFAIL, "__NR_prlimit64(%d) returned %d (%s) but %s(%d) returned %d (%s)",
103 			resource, ret_u64, tst_strerrno(errno_u64),
104 			other_syscall, resource, ret_other,
105 			tst_strerrno(errno_other));
106 		return -1;
107 	}
108 
109 	return 0;
110 }
111 
compare_u64_ulong(int resource,uint64_t val_u64,unsigned long val_ul,const char * kind)112 static int compare_u64_ulong(int resource, uint64_t val_u64,
113 			     unsigned long val_ul, const char *kind)
114 {
115 	if ((val_u64 > RLIM_INFINITY_UL && val_ul != RLIM_INFINITY_UL) ||
116 	    (val_u64 <= RLIM_INFINITY_UL && val_ul != val_u64)) {
117 		tst_res(TFAIL, "__NR_prlimit64(%d) had %s = %" PRIx64 " but " __NR_getrlimit_ulong_str "(%d) had %s = %lx",
118 			resource, kind, val_u64,
119 			resource, kind, val_ul);
120 		return -1;
121 	}
122 
123 	return 0;
124 }
125 
126 #ifdef SIGNED_GETRLIMIT
compare_u64_long(int resource,uint64_t val_u64,long val_l,const char * kind)127 static int compare_u64_long(int resource, uint64_t val_u64, long val_l,
128 			    const char *kind)
129 {
130 	if ((val_u64 > (uint64_t)RLIM_INFINITY_L && val_l != RLIM_INFINITY_L) ||
131 	    (val_u64 <= (uint64_t)RLIM_INFINITY_L && val_l != (long)val_u64)) {
132 		tst_res(TFAIL, "__NR_prlimit64(%d) had %s = %" PRIx64 " but __NR_getrlimit(%d) had %s = %lx",
133 			resource, kind, val_u64,
134 			resource, kind, val_l);
135 		return -1;
136 	}
137 
138 	return 0;
139 }
140 #endif
141 
run(unsigned int resource)142 static void run(unsigned int resource)
143 {
144 	struct rlimit64 rlim_u64;
145 	int ret_u64;
146 	int errno_u64;
147 
148 	struct rlimit_ulong rlim_ul;
149 	int ret_ul;
150 	int errno_ul;
151 
152 #ifdef SIGNED_GETRLIMIT
153 	struct rlimit_long rlim_l;
154 	int ret_l;
155 	int errno_l;
156 #endif
157 
158 	errno = 0;
159 	ret_u64 = getrlimit_u64(resource, &rlim_u64);
160 	errno_u64 = errno;
161 
162 	errno = 0;
163 	ret_ul = getrlimit_ulong(resource, &rlim_ul);
164 	errno_ul = errno;
165 
166 	if (compare_retval(resource, ret_u64, errno_u64, ret_ul, errno_ul,
167 			   __NR_getrlimit_ulong_str) ||
168 	    compare_u64_ulong(resource, rlim_u64.rlim_cur, rlim_ul.rlim_cur,
169 			      "rlim_cur") ||
170 	    compare_u64_ulong(resource, rlim_u64.rlim_max, rlim_ul.rlim_max,
171 			      "rlim_max"))
172 		return;
173 
174 	tst_res(TPASS, "__NR_prlimit64(%d) and %s(%d) gave consistent results",
175 		resource, __NR_getrlimit_ulong_str, resource);
176 
177 #ifdef SIGNED_GETRLIMIT
178 	errno = 0;
179 	ret_l = getrlimit_long(resource, &rlim_l);
180 	errno_l = errno;
181 	if (errno_l == ENOSYS) {
182 		tst_res(TCONF | TERRNO,
183 			"__NR_getrlimit(%d) not implemented", __NR_getrlimit);
184 		return;
185 	}
186 
187 	if (compare_retval(resource, ret_u64, errno_u64, ret_l, errno_l,
188 			   "__NR_getrlimit") ||
189 	    compare_u64_long(resource, rlim_u64.rlim_cur, rlim_l.rlim_cur,
190 			     "rlim_cur") ||
191 	    compare_u64_long(resource, rlim_u64.rlim_max, rlim_l.rlim_max,
192 			     "rlim_max"))
193 		return;
194 
195 	tst_res(TPASS, "__NR_prlimit64(%d) and __NR_getrlimit(%d) gave "
196 		"consistent results", resource, resource);
197 #endif
198 }
199 
200 static struct tst_test test = {
201 	.tcnt = RLIM_NLIMITS,
202 	.test = run,
203 };
204