Skip to contentSkip to conversation

thp01: Find largest arguments size

PATCH New Richard Palethorpe 3 participants 3 messages Sep 22, 2017 – Sep 25, 2017

lore
Share & tools
lore
#0 lore raw

PATCH v4 1/1 thp01: Find largest arguments size

Because of kernel commit da029c11e6b1 the arguments size for exec has been reduced considerably. This causes exec to fail with E2BIG, so we call it repeatedly with a decreasing number of arguments until it is successful.

As far as I know, there is no other reliable and easy way to determine the maximum argument length.

---

V4 - Change the binary search implementation and add some comments.

 testcases/kernel/mem/thp/thp01.c | 116 ++++++++++++++++++++++++++++-----------
 1 file changed, 84 insertions(+), 32 deletions(-)
testcases/kernel/mem/thp/thp01.c +84 -32
index 101a9b5c8..be114e0bf 100644
@@ -28,8 +28,15 @@
  * invalid opcode: 0000 [#1] SMP
  * last sysfs file: /sys/devices/system/cpu/cpu23/cache/index2/shared_cpu_map
  * ....
+ *
+ * Due to commit da029c11e6b1 which reduced the stack size considerably, we
+ * now perform a binary search to find the largest possible argument we can
+ * use. Only the first iteration of the test performs the search; subsequent
+ * iterations use the result of the search which is stored in some shared
+ * memory.
  */
 
+#include <errno.h>
 #include <sys/types.h>
 #include <sys/resource.h>
 #include <sys/wait.h>
@@ -37,57 +44,102 @@
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include "tst_test.h"
 #include "mem.h"
+#include "tst_minmax.h"
+
+#define ARGS_SZ	(256 * 32)
 
-#define ARRAY_SZ	256
+static struct bisection {
+	long left;
+	long right;
+	long mid;
+} *bst;
 
-static int ps;
-static long length;
-static char *array[ARRAY_SZ];
+static char *args[ARGS_SZ];
 static char *arg;
-static struct rlimit rl = {
-	.rlim_cur = RLIM_INFINITY,
-	.rlim_max = RLIM_INFINITY,
-};
 
 static void thp_test(void)
 {
-	int i;
-	pid_t pid;
-
-	switch (pid = SAFE_FORK()) {
-	case 0:
-		memset(arg, 'c', length - 1);
-		arg[length - 1] = '\0';
-		array[0] = "true";
-		for (i = 1; i < ARRAY_SZ - 1; i++)
-			array[i] = arg;
-		array[ARRAY_SZ - 1] = NULL;
-		if (setrlimit(RLIMIT_STACK, &rl) == -1) {
-			perror("setrlimit");
-			exit(1);
-		}
-		if (execvp("true", array) == -1) {
-			perror("execvp");
-			exit(1);
+	long prev_left;
+	int pid;
+
+	while (bst->right - bst->left > 1) {
+		pid_t pid = SAFE_FORK();
+
+		if (!pid) {
+			/* We set mid to left assuming exec will succeed. If
+			 * exec fails with E2BIG (and thus returns) then we
+			 * restore left and set right to mid instead.
+			 */
+			prev_left = bst->left;
+			bst->mid = (bst->left + bst->right) / 2;
+			bst->left = bst->mid;
+			args[bst->mid] = NULL;
+
+			TEST(execvp("true", args));
+			if (TEST_ERRNO != E2BIG)
+				tst_brk(TBROK | TTERRNO, "execvp(\"true\", ...)");
+			bst->left = prev_left;
+			bst->right = bst->mid;
+			exit(0);
 		}
-	default:
+
 		tst_reap_children();
+		tst_res(TINFO, "left: %ld, right: %ld, mid: %ld",
+			bst->left, bst->right, bst->mid);
+	}
+
+	/* We end with mid == right or mid == left where right - left =
+	 * 1. Regardless we must use left because right is only set to values
+	 * which are too large.
+	 */
+	pid = SAFE_FORK();
+	if (pid == 0) {
+		args[bst->left] = NULL;
+		TEST(execvp("true", args));
+		if (TEST_ERRNO != E2BIG)
+			tst_brk(TBROK | TTERRNO, "execvp(\"true\", ...)");
+		exit(0);
 	}
+	tst_reap_children();
 
-	tst_res(TPASS, "system didn't crash, pass.");
+	tst_res(TPASS, "system didn't crash.");
 }
 
 static void setup(void)
 {
-	ps = sysconf(_SC_PAGESIZE);
-	length = 32 * ps;
-	arg = SAFE_MALLOC(length);
+	struct rlimit rl = {
+		.rlim_cur = RLIM_INFINITY,
+		.rlim_max = RLIM_INFINITY,
+	};
+	int i;
+	long arg_len, arg_count;
+
+	bst = SAFE_MMAP(NULL, sizeof(*bst),
+			   PROT_READ | PROT_WRITE,
+			   MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+	bst->left = 0;
+	bst->right = ARGS_SZ;
+
+	arg_len = sysconf(_SC_PAGESIZE);
+	arg = SAFE_MALLOC(arg_len);
+	memset(arg, 'c', arg_len - 1);
+	arg[arg_len - 1] = '\0';
+
+	args[0] = "true";
+	arg_count = ARGS_SZ;
+	tst_res(TINFO, "Using %ld args of size %ld", arg_count, arg_len);
+	for (i = 1; i < arg_count; i++)
+		args[i] = arg;
+
+	SAFE_SETRLIMIT(RLIMIT_STACK, &rl);
 }
 
 static void cleanup(void)
 {
-	free(arg);
+	if (arg)
+		free(arg);
 }
 
 static struct tst_test test = {
Signature · 1 line
2.14.1
#1 lore raw

PATCH v4 1/1 thp01: Find largest arguments size

Quoted 9 lines · Richard Palethorpe

Because of kernel commit da029c11e6b1 the arguments size for exec has been reduced considerably. This causes exec to fail with E2BIG, so we call it repeatedly with a decreasing number of arguments until it is successful.

As far as I know, there is no other reliable and easy way to determine the maximum argument length.

---

Looks good! V4 get pass on my two platforms:

kernel-4.9.13-v7.1.el7 armv7l
kernel-4.13.0+ x86_64

Signature · 2 lines
Li Wang
[email protected]
#2 lore raw

PATCH v4 1/1 thp01: Find largest arguments size

In reply to Richard Palethorpe

Hi!

testcases/kernel/mem/thp/thp01.c line 142

Quoted 5 lines

static void cleanup(void)
{

testcases/kernel/mem/thp/thp01.c +2 -1
-	free(arg);
+	if (arg)
+		free(arg);

free(NULL) is no-op, I've removed this if and pushed, thanks.

Signature · 2 lines
Cyril Hrubis
[email protected]