Skip to contentSkip to conversation

getrlimit/getrlimit03: new test for underlying syscall variants

PATCH New Greg Hackmann 4 participants 7 messages Nov 6, 2018 – Nov 27, 2018

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 getrlimit/getrlimit03: new test for underlying syscall variants

Depending on the architecture, Linux provides up to three syscalls that have been used to implement getrlimit(2) in different libc implementations. These syscalls differ in the size and signedness of rlim_t.

This test compares the results returned by all three syscalls, confirming that they either match or were appropriately capped at the respective RLIM_INFINITY constant.

---
 .../kernel/syscalls/getrlimit/getrlimit03.c   | 195 ++++++++++++++++++
 1 file changed, 195 insertions(+)
 create mode 100644 testcases/kernel/syscalls/getrlimit/getrlimit03.c
testcases/kernel/syscalls/getrlimit/getrlimit03.c new file +195 -0
new file mode 100644
index 000000000..8fa275eeb
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2018 Google, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program, if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Architectures may provide up to three syscalls that have been used to
+ * implement getrlimit(2) in different libc implementations.  These syscalls
+ * differ in the size and signedness of rlim_t:
+ *
+ * - __NR_getrlimit uses long or unsigned long, depending on the
+ *   architecture
+ *
+ * - __NR_ugetrlimit uses unsigned long, and only exists on
+ *   architectures where __NR_getrlimit is signed
+ *
+ * - __NR_prlimit64 uses uint64_t
+ *
+ * This test compares the results returned by all three syscalls, confirming
+ * that they either match or were appropriately capped at the respective
+ * RLIM_INFINITY constant.
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "tst_test.h"
+#include "lapi/syscalls.h"
+
+/**
+ * Linux provides an "old" getrlimit syscall handler that uses signed long,
+ * and a "new" getrlimit syscall handler that uses unsigned long.
+ *
+ * The underlying syscall names vary across architectures, depending on whether
+ * the architecture predates the "new" handler.  For clarity, this test
+ * will call them getrlimit_long and getlimit_ulong internally.
+ */
+#define SIGNED_GETRLIMIT (__NR_ugetrlimit != __LTP__NR_INVALID_SYSCALL)
+#if SIGNED_GETRLIMIT
+#define __NR_getrlimit_ulong		__NR_ugetrlimit
+#define __NR_getrlimit_ulong_str	"__NR_ugetrlimit"
+#else
+#define __NR_getrlimit_ulong		__NR_getrlimit
+#define __NR_getrlimit_ulong_str	"__NR_getrlimit"
+#endif
+
+struct rlimit64 {
+	uint64_t rlim_cur;
+	uint64_t rlim_max;
+};
+const uint64_t RLIM_INFINITY_U64 = UINT64_MAX;
+
+static int getrlimit_u64(int resource, struct rlimit64 *rlim)
+{
+	return syscall(__NR_prlimit64, 0, resource, NULL, rlim);
+}
+
+struct rlimit_ulong {
+	unsigned long rlim_cur;
+	unsigned long rlim_max;
+};
+const unsigned long RLIM_INFINITY_UL = ULONG_MAX;
+
+static int getrlimit_ulong(int resource, struct rlimit_ulong *rlim)
+{
+	return syscall(__NR_getrlimit_ulong, resource, rlim);
+}
+
+#if SIGNED_GETRLIMIT
+struct rlimit_long {
+	long rlim_cur;
+	long rlim_max;
+};
+const long RLIM_INFINITY_L = LONG_MAX;
+
+static int getrlimit_long(int resource, struct rlimit_long *rlim)
+{
+	return syscall(__NR_getrlimit, resource, rlim);
+}
+#endif
+
+static int compare_retval(int resource, int ret_u64, int errno_u64,
+			  int ret_other, int errno_other,
+			  const char *other_syscall)
+{
+	if (ret_u64 != ret_other || errno_u64 != errno_other) {
+		tst_res(TFAIL, "__NR_prlimit64(%d) returned %d (%s) but %s(%d) returned %d (%s)",
+			resource, ret_u64, tst_strerrno(errno_u64),
+			other_syscall, resource, ret_other,
+			tst_strerrno(errno_other));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int compare_u64_ulong(int resource, uint64_t val_u64,
+			     unsigned long val_ul, const char *kind)
+{
+	if ((val_u64 > RLIM_INFINITY_UL && val_ul != RLIM_INFINITY_UL) ||
+	    (val_u64 <= RLIM_INFINITY_UL && val_ul != val_u64)) {
+		tst_res(TFAIL, "__NR_prlimit64(%d) had %s = %" PRIx64 " but " __NR_getrlimit_ulong_str "(%d) had %s = %lx",
+			resource, kind, val_u64,
+			resource, kind, val_ul);
+		return -1;
+	}
+
+	return 0;
+}
+
+#if SIGNED_GETRLIMIT
+static int compare_u64_long(int resource, uint64_t val_u64, long val_l,
+			    const char *kind)
+{
+	if ((val_u64 > (uint64_t)RLIM_INFINITY_L && val_l != RLIM_INFINITY_L) ||
+	    (val_u64 <= (uint64_t)RLIM_INFINITY_L && val_l != (long)val_u64)) {
+		tst_res(TFAIL, "__NR_prlimit64(%d) had %s = %" PRIx64 " but __NR_getrlimit(%d) had %s = %lx",
+			resource, kind, val_u64,
+			resource, kind, val_l);
+		return -1;
+	}
+
+	return 0;
+}
+#endif
+
+static void run(unsigned int resource)
+{
+	struct rlimit64 rlim_u64;
+	int ret_u64;
+	int errno_u64;
+
+	struct rlimit_ulong rlim_ul;
+	int ret_ul;
+	int errno_ul;
+
+#if SIGNED_GETRLIMIT
+	struct rlimit_long rlim_l;
+	int ret_l;
+	int errno_l;
+#endif
+
+	errno = 0;
+	ret_u64 = getrlimit_u64(resource, &rlim_u64);
+	errno_u64 = errno;
+
+	errno = 0;
+	ret_ul = getrlimit_ulong(resource, &rlim_ul);
+	errno_ul = errno;
+
+	if (compare_retval(resource, ret_u64, errno_u64, ret_ul, errno_ul,
+			   __NR_getrlimit_ulong_str) ||
+	    compare_u64_ulong(resource, rlim_u64.rlim_cur, rlim_ul.rlim_cur,
+			      "rlim_cur") ||
+	    compare_u64_ulong(resource, rlim_u64.rlim_max, rlim_ul.rlim_max,
+			      "rlim_max"))
+		return;
+
+#if SIGNED_GETRLIMIT
+	errno = 0;
+	ret_l = getrlimit_long(resource, &rlim_l);
+	errno_l = errno;
+
+	if (compare_retval(resource, ret_u64, errno_u64, ret_l, errno_l,
+			   "__NR_getrlimit") ||
+	    compare_u64_long(resource, rlim_u64.rlim_cur, rlim_l.rlim_cur,
+			     "rlim_cur") ||
+	    compare_u64_long(resource, rlim_u64.rlim_max, rlim_l.rlim_max,
+			     "rlim_max"))
+		return;
+#endif
+
+	tst_res(TPASS, "getrlimit(%u) was consistent during all syscalls",
+		resource);
+}
+
+static struct tst_test test = {
+	.tcnt = RLIM_NLIMITS,
+	.test = run,
+};
+
Signature · 1 line
2.19.1.930.g4563a0d9d0-goog
#1 lore raw

PATCH 1/1 getrlimit/getrlimit03: new test for underlying syscall variants

----- Original Message -----
<snip>

testcases/kernel/syscalls/getrlimit/getrlimit03.c line 69

Quoted 5 lines
testcases/kernel/syscalls/getrlimit/getrlimit03.c new file +5 -0
+const uint64_t RLIM_INFINITY_U64 = UINT64_MAX;
+
+static int getrlimit_u64(int resource, struct rlimit64 *rlim)
+{
+	return syscall(__NR_prlimit64, 0, resource, NULL, rlim);

Hi,

(Likely an issue for old distro kernels, but)
consider using ltp_syscall here, or in setup(). Syscall __NR defines come from LTP, so those will be always present, but syscall can still be unsupported by kernel, so test can end up comparing with 'ENOSYS'.

Regards,
Jan

#2 lore raw

PATCH 1/1 getrlimit/getrlimit03: new test for underlying syscall variants

Hi!

Quoted 10 lines

testcases/kernel/syscalls/getrlimit/getrlimit03.c line 69

Quoted 5 lines
testcases/kernel/syscalls/getrlimit/getrlimit03.c new file +5 -0
+const uint64_t RLIM_INFINITY_U64 = UINT64_MAX;
+
+static int getrlimit_u64(int resource, struct rlimit64 *rlim)
+{
+	return syscall(__NR_prlimit64, 0, resource, NULL, rlim);

Hi,

(Likely an issue for old distro kernels, but)
consider using ltp_syscall here, or in setup(). Syscall __NR defines

^
That would be tst_syscall() for new tst_test.h library

Quoted 2 lines

come from LTP, so those will be always present, but syscall can still be unsupported by kernel, so test can end up comparing with 'ENOSYS'.

Signature · 2 lines
Cyril Hrubis
[email protected]
#3 lore raw

PATCH 1/1 getrlimit/getrlimit03: new test for underlying syscall variants

Quoted 9 lines · Cyril Hrubis

Hi!

Quoted 8 lines

testcases/kernel/syscalls/getrlimit/getrlimit03.c line 69

Quoted 5 lines
testcases/kernel/syscalls/getrlimit/getrlimit03.c new file +5 -0
+const uint64_t RLIM_INFINITY_U64 = UINT64_MAX;
+
+static int getrlimit_u64(int resource, struct rlimit64 *rlim)
+{
+	return syscall(__NR_prlimit64, 0, resource, NULL, rlim);

Hi,

Quoted 9 lines
Quoted 2 lines

(Likely an issue for old distro kernels, but)
consider using ltp_syscall here, or in setup(). Syscall __NR defines

^
That would be tst_syscall() for new tst_test.h library

Quoted 2 lines

come from LTP, so those will be always present, but syscall can still be unsupported by kernel, so test can end up comparing with 'ENOSYS'.

Thanks for the feedback. I'll send a v2 with this change shortly.

#4 lore raw

PATCH v2 1/1 getrlimit/getrlimit03: new test for underlying syscall variants

In reply to Cyril Hrubis

Depending on the architecture, Linux provides up to three syscalls that have been used to implement getrlimit(2) in different libc implementations. These syscalls differ in the size and signedness of rlim_t.

This test compares the results returned by all three syscalls, confirming that they either match or were appropriately capped at the respective RLIM_INFINITY constant.

---
v2: use tst_syscall() to correctly handle kernels that don't have the prlimit64 syscall

 .../kernel/syscalls/getrlimit/getrlimit03.c   | 195 ++++++++++++++++++
 1 file changed, 195 insertions(+)
 create mode 100644 testcases/kernel/syscalls/getrlimit/getrlimit03.c
testcases/kernel/syscalls/getrlimit/getrlimit03.c new file +195 -0
new file mode 100644
index 000000000..2f08e14b7
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2018 Google, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program, if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Architectures may provide up to three syscalls that have been used to
+ * implement getrlimit(2) in different libc implementations.  These syscalls
+ * differ in the size and signedness of rlim_t:
+ *
+ * - __NR_getrlimit uses long or unsigned long, depending on the
+ *   architecture
+ *
+ * - __NR_ugetrlimit uses unsigned long, and only exists on
+ *   architectures where __NR_getrlimit is signed
+ *
+ * - __NR_prlimit64 uses uint64_t
+ *
+ * This test compares the results returned by all three syscalls, confirming
+ * that they either match or were appropriately capped at the respective
+ * RLIM_INFINITY constant.
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "tst_test.h"
+#include "lapi/syscalls.h"
+
+/**
+ * Linux provides an "old" getrlimit syscall handler that uses signed long,
+ * and a "new" getrlimit syscall handler that uses unsigned long.
+ *
+ * The underlying syscall names vary across architectures, depending on whether
+ * the architecture predates the "new" handler.  For clarity, this test
+ * will call them getrlimit_long and getlimit_ulong internally.
+ */
+#define SIGNED_GETRLIMIT (__NR_ugetrlimit != __LTP__NR_INVALID_SYSCALL)
+#if SIGNED_GETRLIMIT
+#define __NR_getrlimit_ulong		__NR_ugetrlimit
+#define __NR_getrlimit_ulong_str	"__NR_ugetrlimit"
+#else
+#define __NR_getrlimit_ulong		__NR_getrlimit
+#define __NR_getrlimit_ulong_str	"__NR_getrlimit"
+#endif
+
+struct rlimit64 {
+	uint64_t rlim_cur;
+	uint64_t rlim_max;
+};
+const uint64_t RLIM_INFINITY_U64 = UINT64_MAX;
+
+static int getrlimit_u64(int resource, struct rlimit64 *rlim)
+{
+	return tst_syscall(__NR_prlimit64, 0, resource, NULL, rlim);
+}
+
+struct rlimit_ulong {
+	unsigned long rlim_cur;
+	unsigned long rlim_max;
+};
+const unsigned long RLIM_INFINITY_UL = ULONG_MAX;
+
+static int getrlimit_ulong(int resource, struct rlimit_ulong *rlim)
+{
+	return syscall(__NR_getrlimit_ulong, resource, rlim);
+}
+
+#if SIGNED_GETRLIMIT
+struct rlimit_long {
+	long rlim_cur;
+	long rlim_max;
+};
+const long RLIM_INFINITY_L = LONG_MAX;
+
+static int getrlimit_long(int resource, struct rlimit_long *rlim)
+{
+	return syscall(__NR_getrlimit, resource, rlim);
+}
+#endif
+
+static int compare_retval(int resource, int ret_u64, int errno_u64,
+			  int ret_other, int errno_other,
+			  const char *other_syscall)
+{
+	if (ret_u64 != ret_other || errno_u64 != errno_other) {
+		tst_res(TFAIL, "__NR_prlimit64(%d) returned %d (%s) but %s(%d) returned %d (%s)",
+			resource, ret_u64, tst_strerrno(errno_u64),
+			other_syscall, resource, ret_other,
+			tst_strerrno(errno_other));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int compare_u64_ulong(int resource, uint64_t val_u64,
+			     unsigned long val_ul, const char *kind)
+{
+	if ((val_u64 > RLIM_INFINITY_UL && val_ul != RLIM_INFINITY_UL) ||
+	    (val_u64 <= RLIM_INFINITY_UL && val_ul != val_u64)) {
+		tst_res(TFAIL, "__NR_prlimit64(%d) had %s = %" PRIx64 " but " __NR_getrlimit_ulong_str "(%d) had %s = %lx",
+			resource, kind, val_u64,
+			resource, kind, val_ul);
+		return -1;
+	}
+
+	return 0;
+}
+
+#if SIGNED_GETRLIMIT
+static int compare_u64_long(int resource, uint64_t val_u64, long val_l,
+			    const char *kind)
+{
+	if ((val_u64 > (uint64_t)RLIM_INFINITY_L && val_l != RLIM_INFINITY_L) ||
+	    (val_u64 <= (uint64_t)RLIM_INFINITY_L && val_l != (long)val_u64)) {
+		tst_res(TFAIL, "__NR_prlimit64(%d) had %s = %" PRIx64 " but __NR_getrlimit(%d) had %s = %lx",
+			resource, kind, val_u64,
+			resource, kind, val_l);
+		return -1;
+	}
+
+	return 0;
+}
+#endif
+
+static void run(unsigned int resource)
+{
+	struct rlimit64 rlim_u64;
+	int ret_u64;
+	int errno_u64;
+
+	struct rlimit_ulong rlim_ul;
+	int ret_ul;
+	int errno_ul;
+
+#if SIGNED_GETRLIMIT
+	struct rlimit_long rlim_l;
+	int ret_l;
+	int errno_l;
+#endif
+
+	errno = 0;
+	ret_u64 = getrlimit_u64(resource, &rlim_u64);
+	errno_u64 = errno;
+
+	errno = 0;
+	ret_ul = getrlimit_ulong(resource, &rlim_ul);
+	errno_ul = errno;
+
+	if (compare_retval(resource, ret_u64, errno_u64, ret_ul, errno_ul,
+			   __NR_getrlimit_ulong_str) ||
+	    compare_u64_ulong(resource, rlim_u64.rlim_cur, rlim_ul.rlim_cur,
+			      "rlim_cur") ||
+	    compare_u64_ulong(resource, rlim_u64.rlim_max, rlim_ul.rlim_max,
+			      "rlim_max"))
+		return;
+
+#if SIGNED_GETRLIMIT
+	errno = 0;
+	ret_l = getrlimit_long(resource, &rlim_l);
+	errno_l = errno;
+
+	if (compare_retval(resource, ret_u64, errno_u64, ret_l, errno_l,
+			   "__NR_getrlimit") ||
+	    compare_u64_long(resource, rlim_u64.rlim_cur, rlim_l.rlim_cur,
+			     "rlim_cur") ||
+	    compare_u64_long(resource, rlim_u64.rlim_max, rlim_l.rlim_max,
+			     "rlim_max"))
+		return;
+#endif
+
+	tst_res(TPASS, "getrlimit(%u) was consistent during all syscalls",
+		resource);
+}
+
+static struct tst_test test = {
+	.tcnt = RLIM_NLIMITS,
+	.test = run,
+};
+
Signature · 1 line
2.19.1.930.g4563a0d9d0-goog
#5 lore raw

PATCH v2 1/1 getrlimit/getrlimit03: new test for underlying syscall variants

----- Original Message -----

Quoted 10 lines

Depending on the architecture, Linux provides up to three syscalls that have been used to implement getrlimit(2) in different libc implementations. These syscalls differ in the size and signedness of rlim_t.

This test compares the results returned by all three syscalls, confirming that they either match or were appropriately capped at the respective RLIM_INFINITY constant.

Looks good to me, worked fine across number of old/new RHEL distros.

Regards,
Jan

#6 lore raw

PATCH v2 1/1 getrlimit/getrlimit03: new test for underlying syscall variants

In reply to Greg Hackmann

Hi Greg,

Quoted 11 lines

Depending on the architecture, Linux provides up to three syscalls that have been used to implement getrlimit(2) in different libc implementations. These syscalls differ in the size and signedness of rlim_t.

This test compares the results returned by all three syscalls, confirming that they either match or were appropriately capped at the respective RLIM_INFINITY constant.

---

Thanks for your patch, pushed with minor GPL related change (replaced GPL text with SPDX-License-Identifier).

Kind regards,
Petr