Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit 72fda6c

Browse files
committed
Merge tag 'execve-v6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux
Pull execve updates from Kees Cook: - Use value of kernel.randomize_va_space once per exec (Alexey Dobriyan) - Honor PT_LOAD alignment for static PIE - Make bprm->argmin only visible under CONFIG_MMU - Add KUnit testing of bprm_stack_limits() * tag 'execve-v6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux: exec: Avoid pathological argc, envc, and bprm->p values execve: Keep bprm->argmin behind CONFIG_MMU ELF: fix kernel.randomize_va_space double read exec: Add KUnit test for bprm_stack_limits() binfmt_elf: Honor PT_LOAD alignment for static PIE binfmt_elf: Calculate total_size earlier selftests/exec: Build both static and non-static load_address tests
2 parents f83e38f + 21f9310 commit 72fda6c

File tree

8 files changed

+327
-60
lines changed

8 files changed

+327
-60
lines changed

MAINTAINERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8285,7 +8285,9 @@ S: Supported
82858285
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/execve
82868286
F: Documentation/userspace-api/ELF.rst
82878287
F: fs/*binfmt_*.c
8288+
F: fs/Kconfig.binfmt
82888289
F: fs/exec.c
8290+
F: fs/exec_test.c
82898291
F: include/linux/binfmts.h
82908292
F: include/linux/elf.h
82918293
F: include/uapi/linux/binfmts.h

fs/Kconfig.binfmt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,12 @@ config COREDUMP
176176
certainly want to say Y here. Not necessary on systems that never
177177
need debugging or only ever run flawless code.
178178

179+
config EXEC_KUNIT_TEST
180+
bool "Build execve tests" if !KUNIT_ALL_TESTS
181+
depends on KUNIT=y
182+
default KUNIT_ALL_TESTS
183+
help
184+
This builds the exec KUnit tests, which tests boundary conditions
185+
of various aspects of the exec internals.
186+
179187
endmenu

fs/binfmt_elf.c

Lines changed: 67 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,7 +1003,8 @@ static int load_elf_binary(struct linux_binprm *bprm)
10031003
if (elf_read_implies_exec(*elf_ex, executable_stack))
10041004
current->personality |= READ_IMPLIES_EXEC;
10051005

1006-
if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
1006+
const int snapshot_randomize_va_space = READ_ONCE(randomize_va_space);
1007+
if (!(current->personality & ADDR_NO_RANDOMIZE) && snapshot_randomize_va_space)
10071008
current->flags |= PF_RANDOMIZE;
10081009

10091010
setup_new_exec(bprm);
@@ -1061,10 +1062,40 @@ static int load_elf_binary(struct linux_binprm *bprm)
10611062
* Header for ET_DYN binaries to calculate the
10621063
* randomization (load_bias) for all the LOAD
10631064
* Program Headers.
1065+
*/
1066+
1067+
/*
1068+
* Calculate the entire size of the ELF mapping
1069+
* (total_size), used for the initial mapping,
1070+
* due to load_addr_set which is set to true later
1071+
* once the initial mapping is performed.
1072+
*
1073+
* Note that this is only sensible when the LOAD
1074+
* segments are contiguous (or overlapping). If
1075+
* used for LOADs that are far apart, this would
1076+
* cause the holes between LOADs to be mapped,
1077+
* running the risk of having the mapping fail,
1078+
* as it would be larger than the ELF file itself.
10641079
*
1080+
* As a result, only ET_DYN does this, since
1081+
* some ET_EXEC (e.g. ia64) may have large virtual
1082+
* memory holes between LOADs.
1083+
*
1084+
*/
1085+
total_size = total_mapping_size(elf_phdata,
1086+
elf_ex->e_phnum);
1087+
if (!total_size) {
1088+
retval = -EINVAL;
1089+
goto out_free_dentry;
1090+
}
1091+
1092+
/* Calculate any requested alignment. */
1093+
alignment = maximum_alignment(elf_phdata, elf_ex->e_phnum);
1094+
1095+
/*
10651096
* There are effectively two types of ET_DYN
1066-
* binaries: programs (i.e. PIE: ET_DYN with INTERP)
1067-
* and loaders (ET_DYN without INTERP, since they
1097+
* binaries: programs (i.e. PIE: ET_DYN with PT_INTERP)
1098+
* and loaders (ET_DYN without PT_INTERP, since they
10681099
* _are_ the ELF interpreter). The loaders must
10691100
* be loaded away from programs since the program
10701101
* may otherwise collide with the loader (especially
@@ -1084,15 +1115,44 @@ static int load_elf_binary(struct linux_binprm *bprm)
10841115
* without MAP_FIXED nor MAP_FIXED_NOREPLACE).
10851116
*/
10861117
if (interpreter) {
1118+
/* On ET_DYN with PT_INTERP, we do the ASLR. */
10871119
load_bias = ELF_ET_DYN_BASE;
10881120
if (current->flags & PF_RANDOMIZE)
10891121
load_bias += arch_mmap_rnd();
1090-
alignment = maximum_alignment(elf_phdata, elf_ex->e_phnum);
1122+
/* Adjust alignment as requested. */
10911123
if (alignment)
10921124
load_bias &= ~(alignment - 1);
10931125
elf_flags |= MAP_FIXED_NOREPLACE;
1094-
} else
1095-
load_bias = 0;
1126+
} else {
1127+
/*
1128+
* For ET_DYN without PT_INTERP, we rely on
1129+
* the architectures's (potentially ASLR) mmap
1130+
* base address (via a load_bias of 0).
1131+
*
1132+
* When a large alignment is requested, we
1133+
* must do the allocation at address "0" right
1134+
* now to discover where things will load so
1135+
* that we can adjust the resulting alignment.
1136+
* In this case (load_bias != 0), we can use
1137+
* MAP_FIXED_NOREPLACE to make sure the mapping
1138+
* doesn't collide with anything.
1139+
*/
1140+
if (alignment > ELF_MIN_ALIGN) {
1141+
load_bias = elf_load(bprm->file, 0, elf_ppnt,
1142+
elf_prot, elf_flags, total_size);
1143+
if (BAD_ADDR(load_bias)) {
1144+
retval = IS_ERR_VALUE(load_bias) ?
1145+
PTR_ERR((void*)load_bias) : -EINVAL;
1146+
goto out_free_dentry;
1147+
}
1148+
vm_munmap(load_bias, total_size);
1149+
/* Adjust alignment as requested. */
1150+
if (alignment)
1151+
load_bias &= ~(alignment - 1);
1152+
elf_flags |= MAP_FIXED_NOREPLACE;
1153+
} else
1154+
load_bias = 0;
1155+
}
10961156

10971157
/*
10981158
* Since load_bias is used for all subsequent loading
@@ -1102,31 +1162,6 @@ static int load_elf_binary(struct linux_binprm *bprm)
11021162
* is then page aligned.
11031163
*/
11041164
load_bias = ELF_PAGESTART(load_bias - vaddr);
1105-
1106-
/*
1107-
* Calculate the entire size of the ELF mapping
1108-
* (total_size), used for the initial mapping,
1109-
* due to load_addr_set which is set to true later
1110-
* once the initial mapping is performed.
1111-
*
1112-
* Note that this is only sensible when the LOAD
1113-
* segments are contiguous (or overlapping). If
1114-
* used for LOADs that are far apart, this would
1115-
* cause the holes between LOADs to be mapped,
1116-
* running the risk of having the mapping fail,
1117-
* as it would be larger than the ELF file itself.
1118-
*
1119-
* As a result, only ET_DYN does this, since
1120-
* some ET_EXEC (e.g. ia64) may have large virtual
1121-
* memory holes between LOADs.
1122-
*
1123-
*/
1124-
total_size = total_mapping_size(elf_phdata,
1125-
elf_ex->e_phnum);
1126-
if (!total_size) {
1127-
retval = -EINVAL;
1128-
goto out_free_dentry;
1129-
}
11301165
}
11311166

11321167
error = elf_load(bprm->file, load_bias + vaddr, elf_ppnt,
@@ -1250,7 +1285,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
12501285
mm->end_data = end_data;
12511286
mm->start_stack = bprm->p;
12521287

1253-
if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) {
1288+
if ((current->flags & PF_RANDOMIZE) && (snapshot_randomize_va_space > 1)) {
12541289
/*
12551290
* For architectures with ELF randomization, when executing
12561291
* a loader directly (i.e. no interpreter listed in ELF

fs/exec.c

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,35 @@ static int count_strings_kernel(const char *const *argv)
486486
return i;
487487
}
488488

489+
static inline int bprm_set_stack_limit(struct linux_binprm *bprm,
490+
unsigned long limit)
491+
{
492+
#ifdef CONFIG_MMU
493+
/* Avoid a pathological bprm->p. */
494+
if (bprm->p < limit)
495+
return -E2BIG;
496+
bprm->argmin = bprm->p - limit;
497+
#endif
498+
return 0;
499+
}
500+
static inline bool bprm_hit_stack_limit(struct linux_binprm *bprm)
501+
{
502+
#ifdef CONFIG_MMU
503+
return bprm->p < bprm->argmin;
504+
#else
505+
return false;
506+
#endif
507+
}
508+
509+
/*
510+
* Calculate bprm->argmin from:
511+
* - _STK_LIM
512+
* - ARG_MAX
513+
* - bprm->rlim_stack.rlim_cur
514+
* - bprm->argc
515+
* - bprm->envc
516+
* - bprm->p
517+
*/
489518
static int bprm_stack_limits(struct linux_binprm *bprm)
490519
{
491520
unsigned long limit, ptr_size;
@@ -505,6 +534,9 @@ static int bprm_stack_limits(struct linux_binprm *bprm)
505534
* of argument strings even with small stacks
506535
*/
507536
limit = max_t(unsigned long, limit, ARG_MAX);
537+
/* Reject totally pathological counts. */
538+
if (bprm->argc < 0 || bprm->envc < 0)
539+
return -E2BIG;
508540
/*
509541
* We must account for the size of all the argv and envp pointers to
510542
* the argv and envp strings, since they will also take up space in
@@ -518,13 +550,14 @@ static int bprm_stack_limits(struct linux_binprm *bprm)
518550
* argc can never be 0, to keep them from walking envp by accident.
519551
* See do_execveat_common().
520552
*/
521-
ptr_size = (max(bprm->argc, 1) + bprm->envc) * sizeof(void *);
553+
if (check_add_overflow(max(bprm->argc, 1), bprm->envc, &ptr_size) ||
554+
check_mul_overflow(ptr_size, sizeof(void *), &ptr_size))
555+
return -E2BIG;
522556
if (limit <= ptr_size)
523557
return -E2BIG;
524558
limit -= ptr_size;
525559

526-
bprm->argmin = bprm->p - limit;
527-
return 0;
560+
return bprm_set_stack_limit(bprm, limit);
528561
}
529562

530563
/*
@@ -562,10 +595,8 @@ static int copy_strings(int argc, struct user_arg_ptr argv,
562595
pos = bprm->p;
563596
str += len;
564597
bprm->p -= len;
565-
#ifdef CONFIG_MMU
566-
if (bprm->p < bprm->argmin)
598+
if (bprm_hit_stack_limit(bprm))
567599
goto out;
568-
#endif
569600

570601
while (len > 0) {
571602
int offset, bytes_to_copy;
@@ -640,7 +671,7 @@ int copy_string_kernel(const char *arg, struct linux_binprm *bprm)
640671
/* We're going to work our way backwards. */
641672
arg += len;
642673
bprm->p -= len;
643-
if (IS_ENABLED(CONFIG_MMU) && bprm->p < bprm->argmin)
674+
if (bprm_hit_stack_limit(bprm))
644675
return -E2BIG;
645676

646677
while (len > 0) {
@@ -2203,3 +2234,7 @@ static int __init init_fs_exec_sysctls(void)
22032234

22042235
fs_initcall(init_fs_exec_sysctls);
22052236
#endif /* CONFIG_SYSCTL */
2237+
2238+
#ifdef CONFIG_EXEC_KUNIT_TEST
2239+
#include "exec_test.c"
2240+
#endif

fs/exec_test.c

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
#include <kunit/test.h>
3+
4+
struct bprm_stack_limits_result {
5+
struct linux_binprm bprm;
6+
int expected_rc;
7+
unsigned long expected_argmin;
8+
};
9+
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 },
27+
/*
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.
31+
*/
32+
{ { .p = ULONG_MAX, .rlim_stack.rlim_cur = ULONG_MAX,
33+
.argc = 0x20000001, .envc = 0x20000001 }, .expected_rc = -E2BIG },
34+
#ifdef CONFIG_MMU
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 },
38+
#endif
39+
/*
40+
* 0 rlim_stack will get raised to ARG_MAX. With 1 string pointer,
41+
* we should see p - ARG_MAX + sizeof(void *).
42+
*/
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 *)},
48+
/*
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.)
51+
*/
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 *) },
109+
};
110+
111+
static void exec_test_bprm_stack_limits(struct kunit *test)
112+
{
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);
117+
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;
121+
int rc;
122+
123+
rc = bprm_stack_limits(&bprm);
124+
KUNIT_EXPECT_EQ_MSG(test, rc, result->expected_rc, "on loop %d", i);
125+
#ifdef CONFIG_MMU
126+
KUNIT_EXPECT_EQ_MSG(test, bprm.argmin, result->expected_argmin, "on loop %d", i);
127+
#endif
128+
}
129+
}
130+
131+
static struct kunit_case exec_test_cases[] = {
132+
KUNIT_CASE(exec_test_bprm_stack_limits),
133+
{},
134+
};
135+
136+
static struct kunit_suite exec_test_suite = {
137+
.name = "exec",
138+
.test_cases = exec_test_cases,
139+
};
140+
141+
kunit_test_suite(exec_test_suite);

0 commit comments

Comments
 (0)