Votre binaire manque PT_GNU_STACK
. En tant que tel, ce changement semble avoir été causé par le commit 9fccc5c0c99f238aa1b0460fccbdb30a887e7036
:
From 9fccc5c0c99f238aa1b0460fccbdb30a887e7036 Mon Sep 17 00:00:00 2001
From: Kees Cook <[email protected]>
Date: Thu, 26 Mar 2020 23:48:17 -0700
Subject: x86/elf: Disable automatic READ_IMPLIES_EXEC on 64-bit
With modern x86 64-bit environments, there should never be a need for
automatic READ_IMPLIES_EXEC, as the architecture is intended to always
be execute-bit aware (as in, the default memory protection should be NX
unless a region explicitly requests to be executable).
There were very old x86_64 systems that lacked the NX bit, but for those,
the NX bit is, obviously, unenforceable, so these changes should have
no impact on them.
Suggested-by: Hector Marco-Gisbert <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
Signed-off-by: Borislav Petkov <[email protected]>
Reviewed-by: Jason Gunthorpe <[email protected]>
Link: https://lkml.kernel.org/r/[email protected]
---
arch/x86/include/asm/elf.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
index 397a1c74433ec..452beed7892bb 100644
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -287,7 +287,7 @@ extern u32 elf_hwcap2;
* CPU: | lacks NX* | has NX, ia32 | has NX, x86_64 |
* ELF: | | | |
* ---------------------|------------|------------------|----------------|
- * missing PT_GNU_STACK | exec-all | exec-all | exec-all |
+ * missing PT_GNU_STACK | exec-all | exec-all | exec-none |
* PT_GNU_STACK == RWX | exec-stack | exec-stack | exec-stack |
* PT_GNU_STACK == RW | exec-none | exec-none | exec-none |
*
@@ -303,7 +303,7 @@ extern u32 elf_hwcap2;
*
*/
#define elf_read_implies_exec(ex, executable_stack) \
- (executable_stack == EXSTACK_DEFAULT)
+ (mmap_is_ia32() && executable_stack == EXSTACK_DEFAULT)
struct task_struct;
--
cgit 1.2.3-1.el7
C'était d'abord présent dans la série 5.8. Voir aussi Autorisation d'exécution inattendue de mmap lorsque des fichiers d'assemblage sont inclus dans le projet.
Ce n'est qu'une supposition :Je pense que le coupable est le READ_IMPLIES_EXEC
personnalité qui était définie automatiquement en l'absence de PT_GNU_STACK
segment.
Dans les sources du noyau 5.4, nous pouvons trouver ce morceau de code :
SET_PERSONALITY2(loc->elf_ex, &arch_state);
if (elf_read_implies_exec(loc->elf_ex, executable_stack))
current->personality |= READ_IMPLIES_EXEC;
C'est la seule chose qui puisse transformer une section RW en une section RWX. Toute autre utilisation de PROC_EXEC
ne semblait pas être changé ou pertinent pour cette question, pour moi.
Le executable_stack
est défini ici :
for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
switch (elf_ppnt->p_type) {
case PT_GNU_STACK:
if (elf_ppnt->p_flags & PF_X)
executable_stack = EXSTACK_ENABLE_X;
else
executable_stack = EXSTACK_DISABLE_X;
break;
Mais si le PT_GNU_STACK
segment n'est pas présent, cette variable conserve sa valeur par défaut :
int executable_stack = EXSTACK_DEFAULT;
Maintenant, ce flux de travail est identique dans la version 5.4 et la dernière source du noyau, ce qui a changé est la définition de elf_read_implies_exec
:
Linux 5.4 :
/*
* An executable for which elf_read_implies_exec() returns TRUE will
* have the READ_IMPLIES_EXEC personality flag set automatically.
*/
#define elf_read_implies_exec(ex, executable_stack) \
(executable_stack != EXSTACK_DISABLE_X)
Linux le plus récent :
/*
* An executable for which elf_read_implies_exec() returns TRUE will
* have the READ_IMPLIES_EXEC personality flag set automatically.
*
* The decision process for determining the results are:
*
* CPU: | lacks NX* | has NX, ia32 | has NX, x86_64 |
* ELF: | | | |
* ---------------------|------------|------------------|----------------|
* missing PT_GNU_STACK | exec-all | exec-all | exec-none |
* PT_GNU_STACK == RWX | exec-stack | exec-stack | exec-stack |
* PT_GNU_STACK == RW | exec-none | exec-none | exec-none |
*
* exec-all : all PROT_READ user mappings are executable, except when
* backed by files on a noexec-filesystem.
* exec-none : only PROT_EXEC user mappings are executable.
* exec-stack: only the stack and PROT_EXEC user mappings are executable.
*
* *this column has no architectural effect: NX markings are ignored by
* hardware, but may have behavioral effects when "wants X" collides with
* "cannot be X" constraints in memory permission flags, as in
* https://lkml.kernel.org/r/[email protected]
*
*/
#define elf_read_implies_exec(ex, executable_stack) \
(mmap_is_ia32() && executable_stack == EXSTACK_DEFAULT)
Notez comment dans la version 5.4 le elf_read_implies_exec
a renvoyé une valeur vraie si la pile n'était pas explicitement marqué comme non exécutable (via le PT_GNU_STACK
segment).
Dans la dernière source, la vérification est maintenant plus défensive :le elf_read_implies_exec
est vrai uniquement sur un exécutable 32 bits, dans le cas où aucun PT_GNU_STACK
segment a été trouvé dans le binaire ELF.
J'ai assemblé votre programme, l'ai lié et n'ai trouvé aucun PT_GNU_STACK
segment, donc cela peut être la raison.
Si tel est bien le problème et si j'ai bien suivi le code, si vous définissez la pile comme non exécutable dans le binaire, sa section de données ne devrait plus être mappée exécutable (même pas sous Linux 5.4).