1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <kunit/test.h>
4 struct bprm_stack_limits_result
{
5 struct linux_binprm bprm
;
7 unsigned long expected_argmin
;
10 static const struct bprm_stack_limits_result bprm_stack_limits_results
[] = {
11 /* Negative argc/envc counts produce -E2BIG */
12 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= ULONG_MAX
,
13 .argc
= INT_MIN
, .envc
= INT_MIN
}, .expected_rc
= -E2BIG
},
14 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= ULONG_MAX
,
15 .argc
= 5, .envc
= -1 }, .expected_rc
= -E2BIG
},
16 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= ULONG_MAX
,
17 .argc
= -1, .envc
= 10 }, .expected_rc
= -E2BIG
},
18 /* The max value of argc or envc is MAX_ARG_STRINGS. */
19 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= ULONG_MAX
,
20 .argc
= INT_MAX
, .envc
= INT_MAX
}, .expected_rc
= -E2BIG
},
21 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= ULONG_MAX
,
22 .argc
= MAX_ARG_STRINGS
, .envc
= MAX_ARG_STRINGS
}, .expected_rc
= -E2BIG
},
23 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= ULONG_MAX
,
24 .argc
= 0, .envc
= MAX_ARG_STRINGS
}, .expected_rc
= -E2BIG
},
25 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= ULONG_MAX
,
26 .argc
= MAX_ARG_STRINGS
, .envc
= 0 }, .expected_rc
= -E2BIG
},
28 * On 32-bit system these argc and envc counts, while likely impossible
29 * to represent within the associated TASK_SIZE, could overflow the
30 * limit calculation, and bypass the ptr_size <= limit check.
32 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= ULONG_MAX
,
33 .argc
= 0x20000001, .envc
= 0x20000001 }, .expected_rc
= -E2BIG
},
35 /* Make sure a pathological bprm->p doesn't cause an overflow. */
36 { { .p
= sizeof(void *), .rlim_stack
.rlim_cur
= ULONG_MAX
,
37 .argc
= 10, .envc
= 10 }, .expected_rc
= -E2BIG
},
40 * 0 rlim_stack will get raised to ARG_MAX. With 1 string pointer,
41 * we should see p - ARG_MAX + sizeof(void *).
43 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= 0,
44 .argc
= 1, .envc
= 0 }, .expected_argmin
= ULONG_MAX
- ARG_MAX
+ sizeof(void *)},
45 /* Validate that argc is always raised to a minimum of 1. */
46 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= 0,
47 .argc
= 0, .envc
= 0 }, .expected_argmin
= ULONG_MAX
- ARG_MAX
+ sizeof(void *)},
49 * 0 rlim_stack will get raised to ARG_MAX. With pointers filling ARG_MAX,
50 * we should see -E2BIG. (Note argc is always raised to at least 1.)
52 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= 0,
53 .argc
= ARG_MAX
/ sizeof(void *), .envc
= 0 }, .expected_rc
= -E2BIG
},
54 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= 0,
55 .argc
= 0, .envc
= ARG_MAX
/ sizeof(void *) - 1 }, .expected_rc
= -E2BIG
},
56 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= 0,
57 .argc
= ARG_MAX
/ sizeof(void *) + 1, .envc
= 0 }, .expected_rc
= -E2BIG
},
58 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= 0,
59 .argc
= 0, .envc
= ARG_MAX
/ sizeof(void *) }, .expected_rc
= -E2BIG
},
60 /* And with one less, we see space for exactly 1 pointer. */
61 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= 0,
62 .argc
= (ARG_MAX
/ sizeof(void *)) - 1, .envc
= 0 },
63 .expected_argmin
= ULONG_MAX
- sizeof(void *) },
64 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= 0,
65 .argc
= 0, .envc
= (ARG_MAX
/ sizeof(void *)) - 2, },
66 .expected_argmin
= ULONG_MAX
- sizeof(void *) },
67 /* If we raise rlim_stack / 4 to exactly ARG_MAX, nothing changes. */
68 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= ARG_MAX
* 4,
69 .argc
= ARG_MAX
/ sizeof(void *), .envc
= 0 }, .expected_rc
= -E2BIG
},
70 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= ARG_MAX
* 4,
71 .argc
= 0, .envc
= ARG_MAX
/ sizeof(void *) - 1 }, .expected_rc
= -E2BIG
},
72 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= ARG_MAX
* 4,
73 .argc
= ARG_MAX
/ sizeof(void *) + 1, .envc
= 0 }, .expected_rc
= -E2BIG
},
74 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= ARG_MAX
* 4,
75 .argc
= 0, .envc
= ARG_MAX
/ sizeof(void *) }, .expected_rc
= -E2BIG
},
76 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= ARG_MAX
* 4,
77 .argc
= (ARG_MAX
/ sizeof(void *)) - 1, .envc
= 0 },
78 .expected_argmin
= ULONG_MAX
- sizeof(void *) },
79 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= ARG_MAX
* 4,
80 .argc
= 0, .envc
= (ARG_MAX
/ sizeof(void *)) - 2, },
81 .expected_argmin
= ULONG_MAX
- sizeof(void *) },
82 /* But raising it another pointer * 4 will provide space for 1 more pointer. */
83 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= (ARG_MAX
+ sizeof(void *)) * 4,
84 .argc
= ARG_MAX
/ sizeof(void *), .envc
= 0 },
85 .expected_argmin
= ULONG_MAX
- sizeof(void *) },
86 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= (ARG_MAX
+ sizeof(void *)) * 4,
87 .argc
= 0, .envc
= ARG_MAX
/ sizeof(void *) - 1 },
88 .expected_argmin
= ULONG_MAX
- sizeof(void *) },
89 /* Raising rlim_stack / 4 to _STK_LIM / 4 * 3 will see more space. */
90 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= 4 * (_STK_LIM
/ 4 * 3),
91 .argc
= 0, .envc
= 0 },
92 .expected_argmin
= ULONG_MAX
- (_STK_LIM
/ 4 * 3) + sizeof(void *) },
93 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= 4 * (_STK_LIM
/ 4 * 3),
94 .argc
= 0, .envc
= 0 },
95 .expected_argmin
= ULONG_MAX
- (_STK_LIM
/ 4 * 3) + sizeof(void *) },
96 /* But raising it any further will see no increase. */
97 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= 4 * (_STK_LIM
/ 4 * 3 + sizeof(void *)),
98 .argc
= 0, .envc
= 0 },
99 .expected_argmin
= ULONG_MAX
- (_STK_LIM
/ 4 * 3) + sizeof(void *) },
100 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= 4 * (_STK_LIM
/ 4 * + sizeof(void *)),
101 .argc
= 0, .envc
= 0 },
102 .expected_argmin
= ULONG_MAX
- (_STK_LIM
/ 4 * 3) + sizeof(void *) },
103 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= 4 * _STK_LIM
,
104 .argc
= 0, .envc
= 0 },
105 .expected_argmin
= ULONG_MAX
- (_STK_LIM
/ 4 * 3) + sizeof(void *) },
106 { { .p
= ULONG_MAX
, .rlim_stack
.rlim_cur
= 4 * _STK_LIM
,
107 .argc
= 0, .envc
= 0 },
108 .expected_argmin
= ULONG_MAX
- (_STK_LIM
/ 4 * 3) + sizeof(void *) },
111 static void exec_test_bprm_stack_limits(struct kunit
*test
)
113 /* Double-check the constants. */
114 KUNIT_EXPECT_EQ(test
, _STK_LIM
, SZ_8M
);
115 KUNIT_EXPECT_EQ(test
, ARG_MAX
, 32 * SZ_4K
);
116 KUNIT_EXPECT_EQ(test
, MAX_ARG_STRINGS
, 0x7FFFFFFF);
118 for (int i
= 0; i
< ARRAY_SIZE(bprm_stack_limits_results
); i
++) {
119 const struct bprm_stack_limits_result
*result
= &bprm_stack_limits_results
[i
];
120 struct linux_binprm bprm
= result
->bprm
;
123 rc
= bprm_stack_limits(&bprm
);
124 KUNIT_EXPECT_EQ_MSG(test
, rc
, result
->expected_rc
, "on loop %d", i
);
126 KUNIT_EXPECT_EQ_MSG(test
, bprm
.argmin
, result
->expected_argmin
, "on loop %d", i
);
131 static struct kunit_case exec_test_cases
[] = {
132 KUNIT_CASE(exec_test_bprm_stack_limits
),
136 static struct kunit_suite exec_test_suite
= {
138 .test_cases
= exec_test_cases
,
141 kunit_test_suite(exec_test_suite
);