* [LTP] [PATCH] Revert "pkey01: Adding test for PKEY_DISABLE_EXECUTE"
@ 2024-11-15 15:44 Martin Doucha
2024-11-15 16:06 ` Martin Doucha
0 siblings, 1 reply; 12+ messages in thread
From: Martin Doucha @ 2024-11-15 15:44 UTC (permalink / raw)
To: ltp
This reverts commit d2b8a476c29d52c484b387454082bbc906b0b4f8.
Remove the PKEY_DISABLE_EXECUTE subtest. The function_size() code
is broken in a way that I cannot easily fix. The function tries
to calculate the size of a function by finding the first RET
instruction. However, in 32bit LTP builds, the code gets compiled
to this:
0804b690 <function_size>:
804b690: 8b 4c 24 04 mov 0x4(%esp),%ecx
804b694: 0f b6 01 movzbl (%ecx),%eax
804b697: 83 c0 3e add $0x3e,%eax
804b69a: 3c 01 cmp $0x1,%al
804b69c: 76 1a jbe 804b6b8 <function_size+0x28>
804b69e: 89 c8 mov %ecx,%eax
804b6a0: 83 c0 01 add $0x1,%eax
804b6a3: 0f b6 10 movzbl (%eax),%edx
804b6a6: 83 c2 3e add $0x3e,%edx
804b6a9: 80 fa 01 cmp $0x1,%dl
804b6ac: 77 f2 ja 804b6a0 <function_size+0x10>
804b6ae: 29 c8 sub %ecx,%eax
804b6b0: 83 c0 10 add $0x10,%eax
804b6b3: c3 ret
804b6b4: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
804b6b8: b8 10 00 00 00 mov $0x10,%eax
804b6bd: c3 ret
804b6be: 66 90 xchg %ax,%ax
If you look closely enough, you'll notice a C2 byte in add $0x3e,%edx
instruction on address 804b6a6. The function will assume this byte is
a RET instruction, return a size that's 22 bytes too short and then
the code execution inside the executable buffer will run past the end
of buffer, resulting in a segfault.
Signed-off-by: Martin Doucha <mdoucha@suse.cz>
---
testcases/kernel/syscalls/pkeys/pkey01.c | 52 ++----------------------
1 file changed, 3 insertions(+), 49 deletions(-)
diff --git a/testcases/kernel/syscalls/pkeys/pkey01.c b/testcases/kernel/syscalls/pkeys/pkey01.c
index c041cbcfd..e49e48846 100644
--- a/testcases/kernel/syscalls/pkeys/pkey01.c
+++ b/testcases/kernel/syscalls/pkeys/pkey01.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * Copyright (c) 2019-2024 Red Hat, Inc.
+ * Copyright (c) 2019 Red Hat, Inc.
*/
/*\
@@ -41,7 +41,6 @@
#define PATH_VM_NRHPS "/proc/sys/vm/nr_hugepages"
static int size;
-static int execute_supported = 1;
#define PERM_NAME(x) .access_rights = x, .name = #x
static struct tcase {
@@ -51,26 +50,14 @@ static struct tcase {
} tcases[] = {
{PERM_NAME(PKEY_DISABLE_ACCESS)},
{PERM_NAME(PKEY_DISABLE_WRITE)},
- {PERM_NAME(PKEY_DISABLE_EXECUTE)} /* keep it the last */
};
static void setup(void)
{
- int i, fd, pkey;
+ int i, fd;
check_pkey_support();
- pkey = pkey_alloc(0, PKEY_DISABLE_EXECUTE);
- if (pkey == -1) {
- if (errno == EINVAL) {
- tst_res(TINFO, "PKEY_DISABLE_EXECUTE not implemented");
- execute_supported = 0;
- } else {
- tst_brk(TBROK | TERRNO, "pkey_alloc failed");
- }
- }
- pkey_free(pkey);
-
if (tst_hugepages == test.hugepages.number)
size = SAFE_READ_MEMINFO("Hugepagesize:") * 1024;
else
@@ -144,17 +131,6 @@ static char *flag_to_str(int flags)
}
}
-static size_t function_size(void (*func)(void))
-{
- unsigned char *start = (unsigned char *)func;
- unsigned char *end = start;
-
- while (*end != 0xC3 && *end != 0xC2)
- end++;
-
- return (size_t)(end - start + 1);
-}
-
/*
* return: 1 if it's safe to quit testing on failure (all following would be
* TCONF, O otherwise.
@@ -165,13 +141,6 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
char *buffer;
int pkey, status;
int fd = mpa->fd;
- size_t (*func)();
- size_t func_size = 0;
-
- if (!execute_supported && (tc->access_rights == PKEY_DISABLE_EXECUTE)) {
- tst_res(TCONF, "skip PKEY_DISABLE_EXECUTE test");
- return 1;
- }
if (!tst_hugepages && (mpa->flags & MAP_HUGETLB)) {
tst_res(TCONF, "Skip test on (%s) buffer", flag_to_str(mpa->flags));
@@ -183,11 +152,6 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
buffer = SAFE_MMAP(NULL, size, mpa->prot, mpa->flags, fd, 0);
- if (mpa->prot == (PROT_READ | PROT_WRITE | PROT_EXEC)) {
- func_size = function_size((void (*)(void))function_size);
- memcpy(buffer, (void *)function_size, func_size);
- }
-
pkey = pkey_alloc(tc->flags, tc->access_rights);
if (pkey == -1)
tst_brk(TBROK | TERRNO, "pkey_alloc failed");
@@ -210,10 +174,6 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
tst_res(TFAIL | TERRNO,
"Write buffer success, buffer[0] = %d", *buffer);
break;
- case PKEY_DISABLE_EXECUTE:
- func = (size_t (*)())buffer;
- tst_res(TFAIL | TERRNO, "Execute buffer result = %zi", func(func));
- break;
}
exit(0);
}
@@ -238,16 +198,10 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
tst_res(TPASS, "Write buffer success, buffer[0] = %d", *buffer);
break;
case PROT_READ | PROT_WRITE:
+ case PROT_READ | PROT_WRITE | PROT_EXEC:
*buffer = 'a';
tst_res(TPASS, "Read & Write buffer success, buffer[0] = %d", *buffer);
break;
- case PROT_READ | PROT_WRITE | PROT_EXEC:
- func = (size_t (*)())buffer;;
- if (func_size == func(func))
- tst_res(TPASS, "Execute buffer success, result = %zi", func_size);
- else
- tst_res(TFAIL, "Execute buffer with unexpected result: %zi", func(func));
- break;
}
if (fd >= 0)
--
2.46.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [LTP] [PATCH] Revert "pkey01: Adding test for PKEY_DISABLE_EXECUTE"
2024-11-15 15:44 [LTP] [PATCH] Revert "pkey01: Adding test for PKEY_DISABLE_EXECUTE" Martin Doucha
@ 2024-11-15 16:06 ` Martin Doucha
2024-11-15 18:58 ` Petr Vorel
0 siblings, 1 reply; 12+ messages in thread
From: Martin Doucha @ 2024-11-15 16:06 UTC (permalink / raw)
To: ltp
Sorry, I forgot to CC Li in the patch.
On 15. 11. 24 16:44, Martin Doucha wrote:
> This reverts commit d2b8a476c29d52c484b387454082bbc906b0b4f8.
>
> Remove the PKEY_DISABLE_EXECUTE subtest. The function_size() code
> is broken in a way that I cannot easily fix. The function tries
> to calculate the size of a function by finding the first RET
> instruction. However, in 32bit LTP builds, the code gets compiled
> to this:
>
> 0804b690 <function_size>:
> 804b690: 8b 4c 24 04 mov 0x4(%esp),%ecx
> 804b694: 0f b6 01 movzbl (%ecx),%eax
> 804b697: 83 c0 3e add $0x3e,%eax
> 804b69a: 3c 01 cmp $0x1,%al
> 804b69c: 76 1a jbe 804b6b8 <function_size+0x28>
> 804b69e: 89 c8 mov %ecx,%eax
> 804b6a0: 83 c0 01 add $0x1,%eax
> 804b6a3: 0f b6 10 movzbl (%eax),%edx
> 804b6a6: 83 c2 3e add $0x3e,%edx
> 804b6a9: 80 fa 01 cmp $0x1,%dl
> 804b6ac: 77 f2 ja 804b6a0 <function_size+0x10>
> 804b6ae: 29 c8 sub %ecx,%eax
> 804b6b0: 83 c0 10 add $0x10,%eax
> 804b6b3: c3 ret
> 804b6b4: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
> 804b6b8: b8 10 00 00 00 mov $0x10,%eax
> 804b6bd: c3 ret
> 804b6be: 66 90 xchg %ax,%ax
>
> If you look closely enough, you'll notice a C2 byte in add $0x3e,%edx
> instruction on address 804b6a6. The function will assume this byte is
> a RET instruction, return a size that's 22 bytes too short and then
> the code execution inside the executable buffer will run past the end
> of buffer, resulting in a segfault.
>
> Signed-off-by: Martin Doucha <mdoucha@suse.cz>
> ---
> testcases/kernel/syscalls/pkeys/pkey01.c | 52 ++----------------------
> 1 file changed, 3 insertions(+), 49 deletions(-)
>
> diff --git a/testcases/kernel/syscalls/pkeys/pkey01.c b/testcases/kernel/syscalls/pkeys/pkey01.c
> index c041cbcfd..e49e48846 100644
> --- a/testcases/kernel/syscalls/pkeys/pkey01.c
> +++ b/testcases/kernel/syscalls/pkeys/pkey01.c
> @@ -1,6 +1,6 @@
> // SPDX-License-Identifier: GPL-2.0-or-later
> /*
> - * Copyright (c) 2019-2024 Red Hat, Inc.
> + * Copyright (c) 2019 Red Hat, Inc.
> */
>
> /*\
> @@ -41,7 +41,6 @@
> #define PATH_VM_NRHPS "/proc/sys/vm/nr_hugepages"
>
> static int size;
> -static int execute_supported = 1;
>
> #define PERM_NAME(x) .access_rights = x, .name = #x
> static struct tcase {
> @@ -51,26 +50,14 @@ static struct tcase {
> } tcases[] = {
> {PERM_NAME(PKEY_DISABLE_ACCESS)},
> {PERM_NAME(PKEY_DISABLE_WRITE)},
> - {PERM_NAME(PKEY_DISABLE_EXECUTE)} /* keep it the last */
> };
>
> static void setup(void)
> {
> - int i, fd, pkey;
> + int i, fd;
>
> check_pkey_support();
>
> - pkey = pkey_alloc(0, PKEY_DISABLE_EXECUTE);
> - if (pkey == -1) {
> - if (errno == EINVAL) {
> - tst_res(TINFO, "PKEY_DISABLE_EXECUTE not implemented");
> - execute_supported = 0;
> - } else {
> - tst_brk(TBROK | TERRNO, "pkey_alloc failed");
> - }
> - }
> - pkey_free(pkey);
> -
> if (tst_hugepages == test.hugepages.number)
> size = SAFE_READ_MEMINFO("Hugepagesize:") * 1024;
> else
> @@ -144,17 +131,6 @@ static char *flag_to_str(int flags)
> }
> }
>
> -static size_t function_size(void (*func)(void))
> -{
> - unsigned char *start = (unsigned char *)func;
> - unsigned char *end = start;
> -
> - while (*end != 0xC3 && *end != 0xC2)
> - end++;
> -
> - return (size_t)(end - start + 1);
> -}
> -
> /*
> * return: 1 if it's safe to quit testing on failure (all following would be
> * TCONF, O otherwise.
> @@ -165,13 +141,6 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
> char *buffer;
> int pkey, status;
> int fd = mpa->fd;
> - size_t (*func)();
> - size_t func_size = 0;
> -
> - if (!execute_supported && (tc->access_rights == PKEY_DISABLE_EXECUTE)) {
> - tst_res(TCONF, "skip PKEY_DISABLE_EXECUTE test");
> - return 1;
> - }
>
> if (!tst_hugepages && (mpa->flags & MAP_HUGETLB)) {
> tst_res(TCONF, "Skip test on (%s) buffer", flag_to_str(mpa->flags));
> @@ -183,11 +152,6 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
>
> buffer = SAFE_MMAP(NULL, size, mpa->prot, mpa->flags, fd, 0);
>
> - if (mpa->prot == (PROT_READ | PROT_WRITE | PROT_EXEC)) {
> - func_size = function_size((void (*)(void))function_size);
> - memcpy(buffer, (void *)function_size, func_size);
> - }
> -
> pkey = pkey_alloc(tc->flags, tc->access_rights);
> if (pkey == -1)
> tst_brk(TBROK | TERRNO, "pkey_alloc failed");
> @@ -210,10 +174,6 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
> tst_res(TFAIL | TERRNO,
> "Write buffer success, buffer[0] = %d", *buffer);
> break;
> - case PKEY_DISABLE_EXECUTE:
> - func = (size_t (*)())buffer;
> - tst_res(TFAIL | TERRNO, "Execute buffer result = %zi", func(func));
> - break;
> }
> exit(0);
> }
> @@ -238,16 +198,10 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
> tst_res(TPASS, "Write buffer success, buffer[0] = %d", *buffer);
> break;
> case PROT_READ | PROT_WRITE:
> + case PROT_READ | PROT_WRITE | PROT_EXEC:
> *buffer = 'a';
> tst_res(TPASS, "Read & Write buffer success, buffer[0] = %d", *buffer);
> break;
> - case PROT_READ | PROT_WRITE | PROT_EXEC:
> - func = (size_t (*)())buffer;;
> - if (func_size == func(func))
> - tst_res(TPASS, "Execute buffer success, result = %zi", func_size);
> - else
> - tst_res(TFAIL, "Execute buffer with unexpected result: %zi", func(func));
> - break;
> }
>
> if (fd >= 0)
--
Martin Doucha mdoucha@suse.cz
SW Quality Engineer
SUSE LINUX, s.r.o.
CORSO IIa
Krizikova 148/34
186 00 Prague 8
Czech Republic
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [LTP] [PATCH] Revert "pkey01: Adding test for PKEY_DISABLE_EXECUTE"
2024-11-15 16:06 ` Martin Doucha
@ 2024-11-15 18:58 ` Petr Vorel
2024-11-19 13:11 ` Jan Stancek
0 siblings, 1 reply; 12+ messages in thread
From: Petr Vorel @ 2024-11-15 18:58 UTC (permalink / raw)
To: Martin Doucha; +Cc: ltp
Hi Li, Martin,
> Sorry, I forgot to CC Li in the patch.
> On 15. 11. 24 16:44, Martin Doucha wrote:
> > This reverts commit d2b8a476c29d52c484b387454082bbc906b0b4f8.
> > Remove the PKEY_DISABLE_EXECUTE subtest. The function_size() code
> > is broken in a way that I cannot easily fix. The function tries
> > to calculate the size of a function by finding the first RET
> > instruction. However, in 32bit LTP builds, the code gets compiled
> > to this:
> > 0804b690 <function_size>:
> > 804b690: 8b 4c 24 04 mov 0x4(%esp),%ecx
> > 804b694: 0f b6 01 movzbl (%ecx),%eax
> > 804b697: 83 c0 3e add $0x3e,%eax
> > 804b69a: 3c 01 cmp $0x1,%al
> > 804b69c: 76 1a jbe 804b6b8 <function_size+0x28>
> > 804b69e: 89 c8 mov %ecx,%eax
> > 804b6a0: 83 c0 01 add $0x1,%eax
> > 804b6a3: 0f b6 10 movzbl (%eax),%edx
> > 804b6a6: 83 c2 3e add $0x3e,%edx
> > 804b6a9: 80 fa 01 cmp $0x1,%dl
> > 804b6ac: 77 f2 ja 804b6a0 <function_size+0x10>
> > 804b6ae: 29 c8 sub %ecx,%eax
> > 804b6b0: 83 c0 10 add $0x10,%eax
> > 804b6b3: c3 ret
> > 804b6b4: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
> > 804b6b8: b8 10 00 00 00 mov $0x10,%eax
> > 804b6bd: c3 ret
> > 804b6be: 66 90 xchg %ax,%ax
> > If you look closely enough, you'll notice a C2 byte in add $0x3e,%edx
> > instruction on address 804b6a6. The function will assume this byte is
> > a RET instruction, return a size that's 22 bytes too short and then
> > the code execution inside the executable buffer will run past the end
> > of buffer, resulting in a segfault.
Martin, thanks a lot for debugging!
Acked-by: Petr Vorel <pvorel@suse.cz>
Other option would be to disable this test only for 32bit (keep it for 64bit).
Li, any change you could have look into it?
Kind regards,
Petr
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [LTP] [PATCH] Revert "pkey01: Adding test for PKEY_DISABLE_EXECUTE"
2024-11-15 18:58 ` Petr Vorel
@ 2024-11-19 13:11 ` Jan Stancek
2024-11-19 13:31 ` Jan Stancek
0 siblings, 1 reply; 12+ messages in thread
From: Jan Stancek @ 2024-11-19 13:11 UTC (permalink / raw)
To: Petr Vorel; +Cc: ltp
On Fri, Nov 15, 2024 at 7:58 PM Petr Vorel <pvorel@suse.cz> wrote:
>
> Hi Li, Martin,
>
> > Sorry, I forgot to CC Li in the patch.
>
> > On 15. 11. 24 16:44, Martin Doucha wrote:
> > > This reverts commit d2b8a476c29d52c484b387454082bbc906b0b4f8.
>
> > > Remove the PKEY_DISABLE_EXECUTE subtest. The function_size() code
> > > is broken in a way that I cannot easily fix. The function tries
> > > to calculate the size of a function by finding the first RET
> > > instruction. However, in 32bit LTP builds, the code gets compiled
> > > to this:
>
> > > 0804b690 <function_size>:
> > > 804b690: 8b 4c 24 04 mov 0x4(%esp),%ecx
> > > 804b694: 0f b6 01 movzbl (%ecx),%eax
> > > 804b697: 83 c0 3e add $0x3e,%eax
> > > 804b69a: 3c 01 cmp $0x1,%al
> > > 804b69c: 76 1a jbe 804b6b8 <function_size+0x28>
> > > 804b69e: 89 c8 mov %ecx,%eax
> > > 804b6a0: 83 c0 01 add $0x1,%eax
> > > 804b6a3: 0f b6 10 movzbl (%eax),%edx
> > > 804b6a6: 83 c2 3e add $0x3e,%edx
> > > 804b6a9: 80 fa 01 cmp $0x1,%dl
> > > 804b6ac: 77 f2 ja 804b6a0 <function_size+0x10>
> > > 804b6ae: 29 c8 sub %ecx,%eax
> > > 804b6b0: 83 c0 10 add $0x10,%eax
> > > 804b6b3: c3 ret
> > > 804b6b4: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
> > > 804b6b8: b8 10 00 00 00 mov $0x10,%eax
> > > 804b6bd: c3 ret
> > > 804b6be: 66 90 xchg %ax,%ax
>
> > > If you look closely enough, you'll notice a C2 byte in add $0x3e,%edx
> > > instruction on address 804b6a6. The function will assume this byte is
> > > a RET instruction, return a size that's 22 bytes too short and then
> > > the code execution inside the executable buffer will run past the end
> > > of buffer, resulting in a segfault.
>
> Martin, thanks a lot for debugging!
>
> Acked-by: Petr Vorel <pvorel@suse.cz>
>
> Other option would be to disable this test only for 32bit (keep it for 64bit).
> Li, any change you could have look into it?
I vaguely recall we already dealt with something similar for a
different test, which
copied function, then changed protection to EXEC and finally ran it.
I'll see if I can find it.
>
> Kind regards,
> Petr
>
> --
> Mailing list info: https://lists.linux.it/listinfo/ltp
>
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [LTP] [PATCH] Revert "pkey01: Adding test for PKEY_DISABLE_EXECUTE"
2024-11-19 13:11 ` Jan Stancek
@ 2024-11-19 13:31 ` Jan Stancek
2024-11-19 15:47 ` [LTP] [PATCH] syscalls/pkeys01: use a dummy function instead of function_size Jan Stancek
2024-11-20 12:57 ` [LTP] [PATCH] Revert "pkey01: Adding test for PKEY_DISABLE_EXECUTE" Li Wang
0 siblings, 2 replies; 12+ messages in thread
From: Jan Stancek @ 2024-11-19 13:31 UTC (permalink / raw)
To: Petr Vorel; +Cc: ltp
On Tue, Nov 19, 2024 at 2:11 PM Jan Stancek <jstancek@redhat.com> wrote:
>
> On Fri, Nov 15, 2024 at 7:58 PM Petr Vorel <pvorel@suse.cz> wrote:
> >
> > Hi Li, Martin,
> >
> > > Sorry, I forgot to CC Li in the patch.
> >
> > > On 15. 11. 24 16:44, Martin Doucha wrote:
> > > > This reverts commit d2b8a476c29d52c484b387454082bbc906b0b4f8.
> >
> > > > Remove the PKEY_DISABLE_EXECUTE subtest. The function_size() code
> > > > is broken in a way that I cannot easily fix. The function tries
> > > > to calculate the size of a function by finding the first RET
> > > > instruction. However, in 32bit LTP builds, the code gets compiled
> > > > to this:
> >
> > > > 0804b690 <function_size>:
> > > > 804b690: 8b 4c 24 04 mov 0x4(%esp),%ecx
> > > > 804b694: 0f b6 01 movzbl (%ecx),%eax
> > > > 804b697: 83 c0 3e add $0x3e,%eax
> > > > 804b69a: 3c 01 cmp $0x1,%al
> > > > 804b69c: 76 1a jbe 804b6b8 <function_size+0x28>
> > > > 804b69e: 89 c8 mov %ecx,%eax
> > > > 804b6a0: 83 c0 01 add $0x1,%eax
> > > > 804b6a3: 0f b6 10 movzbl (%eax),%edx
> > > > 804b6a6: 83 c2 3e add $0x3e,%edx
> > > > 804b6a9: 80 fa 01 cmp $0x1,%dl
> > > > 804b6ac: 77 f2 ja 804b6a0 <function_size+0x10>
> > > > 804b6ae: 29 c8 sub %ecx,%eax
> > > > 804b6b0: 83 c0 10 add $0x10,%eax
> > > > 804b6b3: c3 ret
> > > > 804b6b4: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
> > > > 804b6b8: b8 10 00 00 00 mov $0x10,%eax
> > > > 804b6bd: c3 ret
> > > > 804b6be: 66 90 xchg %ax,%ax
> >
> > > > If you look closely enough, you'll notice a C2 byte in add $0x3e,%edx
> > > > instruction on address 804b6a6. The function will assume this byte is
> > > > a RET instruction, return a size that's 22 bytes too short and then
> > > > the code execution inside the executable buffer will run past the end
> > > > of buffer, resulting in a segfault.
> >
> > Martin, thanks a lot for debugging!
> >
> > Acked-by: Petr Vorel <pvorel@suse.cz>
> >
> > Other option would be to disable this test only for 32bit (keep it for 64bit).
> > Li, any change you could have look into it?
>
> I vaguely recall we already dealt with something similar for a
> different test, which
> copied function, then changed protection to EXEC and finally ran it.
> I'll see if I can find it.
Do we need function_size() to return accurate number? In mprotect04
we ended up using CFLAGS += -falign-functions=64 and copying everything
until end of page. Function was dummy, so it always fit and it's not an
issue if we copied more.
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread
* [LTP] [PATCH] syscalls/pkeys01: use a dummy function instead of function_size
2024-11-19 13:31 ` Jan Stancek
@ 2024-11-19 15:47 ` Jan Stancek
2024-11-20 14:56 ` Jan Stancek
` (2 more replies)
2024-11-20 12:57 ` [LTP] [PATCH] Revert "pkey01: Adding test for PKEY_DISABLE_EXECUTE" Li Wang
1 sibling, 3 replies; 12+ messages in thread
From: Jan Stancek @ 2024-11-19 15:47 UTC (permalink / raw)
To: ltp
As Martin found:
The function_size() code
is broken in a way that I cannot easily fix. The function tries
to calculate the size of a function by finding the first RET
instruction. However, in 32bit LTP builds, the code gets compiled
to this:
0804b690 <function_size>:
804b690: 8b 4c 24 04 mov 0x4(%esp),%ecx
804b694: 0f b6 01 movzbl (%ecx),%eax
804b697: 83 c0 3e add $0x3e,%eax
804b69a: 3c 01 cmp $0x1,%al
804b69c: 76 1a jbe 804b6b8 <function_size+0x28>
804b69e: 89 c8 mov %ecx,%eax
804b6a0: 83 c0 01 add $0x1,%eax
804b6a3: 0f b6 10 movzbl (%eax),%edx
804b6a6: 83 c2 3e add $0x3e,%edx
804b6a9: 80 fa 01 cmp $0x1,%dl
804b6ac: 77 f2 ja 804b6a0 <function_size+0x10>
804b6ae: 29 c8 sub %ecx,%eax
804b6b0: 83 c0 10 add $0x10,%eax
804b6b3: c3 ret
804b6b4: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
804b6b8: b8 10 00 00 00 mov $0x10,%eax
804b6bd: c3 ret
804b6be: 66 90 xchg %ax,%ax
If you look closely enough, you'll notice a C2 byte in add $0x3e,%edx
instruction on address 804b6a6. The function will assume this byte is
a RET instruction, return a size that's 22 bytes too short and then
the code execution inside the executable buffer will run past the end
of buffer, resulting in a segfault.
Use a dummy function and copy entire page, similar to what we do
in mprotect04.
Signed-off-by: Jan Stancek <jstancek@redhat.com>
---
testcases/kernel/syscalls/pkeys/Makefile | 2 ++
testcases/kernel/syscalls/pkeys/pkey01.c | 31 ++++++++++--------------
2 files changed, 15 insertions(+), 18 deletions(-)
Notes:
This could be an alternative to reverting PKEY_DISABLE_EXECUTE test.
I haven't tested it yet on 32bit, but since it doesn't rely on any
instruction codes I don't expect it to break. An important test
would also be ppc64le.
diff --git a/testcases/kernel/syscalls/pkeys/Makefile b/testcases/kernel/syscalls/pkeys/Makefile
index 9ee2c2ea57b0..814593f3c720 100644
--- a/testcases/kernel/syscalls/pkeys/Makefile
+++ b/testcases/kernel/syscalls/pkeys/Makefile
@@ -5,4 +5,6 @@ top_srcdir ?= ../../../..
include $(top_srcdir)/include/mk/testcases.mk
+pkey01: CFLAGS += -falign-functions=64
+
include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/syscalls/pkeys/pkey01.c b/testcases/kernel/syscalls/pkeys/pkey01.c
index c041cbcfd969..b10760b5bd2f 100644
--- a/testcases/kernel/syscalls/pkeys/pkey01.c
+++ b/testcases/kernel/syscalls/pkeys/pkey01.c
@@ -144,15 +144,9 @@ static char *flag_to_str(int flags)
}
}
-static size_t function_size(void (*func)(void))
+static long __attribute__ ((noinline)) dummy_func(void)
{
- unsigned char *start = (unsigned char *)func;
- unsigned char *end = start;
-
- while (*end != 0xC3 && *end != 0xC2)
- end++;
-
- return (size_t)(end - start + 1);
+ return 0xdead;
}
/*
@@ -165,8 +159,11 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
char *buffer;
int pkey, status;
int fd = mpa->fd;
- size_t (*func)();
- size_t func_size = 0;
+ long (*func)(void) = 0;
+ uintptr_t page_mask = ~(getpagesize() - 1);
+ uintptr_t offset_mask = (getpagesize() - 1);
+ uintptr_t func_page_offset = (uintptr_t)&dummy_func & offset_mask;
+ void *page_to_copy = (void *)((uintptr_t)&dummy_func & page_mask);
if (!execute_supported && (tc->access_rights == PKEY_DISABLE_EXECUTE)) {
tst_res(TCONF, "skip PKEY_DISABLE_EXECUTE test");
@@ -184,8 +181,8 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
buffer = SAFE_MMAP(NULL, size, mpa->prot, mpa->flags, fd, 0);
if (mpa->prot == (PROT_READ | PROT_WRITE | PROT_EXEC)) {
- func_size = function_size((void (*)(void))function_size);
- memcpy(buffer, (void *)function_size, func_size);
+ memcpy(buffer, page_to_copy, getpagesize());
+ func = (long (*)(void))(buffer + func_page_offset);
}
pkey = pkey_alloc(tc->flags, tc->access_rights);
@@ -211,8 +208,7 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
"Write buffer success, buffer[0] = %d", *buffer);
break;
case PKEY_DISABLE_EXECUTE:
- func = (size_t (*)())buffer;
- tst_res(TFAIL | TERRNO, "Execute buffer result = %zi", func(func));
+ tst_res(TFAIL | TERRNO, "Execute buffer result = %ld", func());
break;
}
exit(0);
@@ -242,11 +238,10 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
tst_res(TPASS, "Read & Write buffer success, buffer[0] = %d", *buffer);
break;
case PROT_READ | PROT_WRITE | PROT_EXEC:
- func = (size_t (*)())buffer;;
- if (func_size == func(func))
- tst_res(TPASS, "Execute buffer success, result = %zi", func_size);
+ if (dummy_func() == func())
+ tst_res(TPASS, "Execute buffer success, result = %ld", dummy_func());
else
- tst_res(TFAIL, "Execute buffer with unexpected result: %zi", func(func));
+ tst_res(TFAIL, "Execute buffer with unexpected result: %zi", func());
break;
}
--
2.43.0
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply related [flat|nested] 12+ messages in thread* Re: [LTP] [PATCH] syscalls/pkeys01: use a dummy function instead of function_size
2024-11-19 15:47 ` [LTP] [PATCH] syscalls/pkeys01: use a dummy function instead of function_size Jan Stancek
@ 2024-11-20 14:56 ` Jan Stancek
2024-11-20 17:07 ` Petr Vorel
2024-11-25 3:43 ` Li Wang
2024-11-25 16:20 ` Martin Doucha
2 siblings, 1 reply; 12+ messages in thread
From: Jan Stancek @ 2024-11-20 14:56 UTC (permalink / raw)
To: ltp, Li Wang, Petr Vorel, Martin Doucha
On Tue, Nov 19, 2024 at 4:48 PM Jan Stancek <jstancek@redhat.com> wrote:
>
> As Martin found:
> The function_size() code
> is broken in a way that I cannot easily fix. The function tries
> to calculate the size of a function by finding the first RET
> instruction. However, in 32bit LTP builds, the code gets compiled
> to this:
>
> 0804b690 <function_size>:
> 804b690: 8b 4c 24 04 mov 0x4(%esp),%ecx
> 804b694: 0f b6 01 movzbl (%ecx),%eax
> 804b697: 83 c0 3e add $0x3e,%eax
> 804b69a: 3c 01 cmp $0x1,%al
> 804b69c: 76 1a jbe 804b6b8 <function_size+0x28>
> 804b69e: 89 c8 mov %ecx,%eax
> 804b6a0: 83 c0 01 add $0x1,%eax
> 804b6a3: 0f b6 10 movzbl (%eax),%edx
> 804b6a6: 83 c2 3e add $0x3e,%edx
> 804b6a9: 80 fa 01 cmp $0x1,%dl
> 804b6ac: 77 f2 ja 804b6a0 <function_size+0x10>
> 804b6ae: 29 c8 sub %ecx,%eax
> 804b6b0: 83 c0 10 add $0x10,%eax
> 804b6b3: c3 ret
> 804b6b4: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
> 804b6b8: b8 10 00 00 00 mov $0x10,%eax
> 804b6bd: c3 ret
> 804b6be: 66 90 xchg %ax,%ax
>
> If you look closely enough, you'll notice a C2 byte in add $0x3e,%edx
> instruction on address 804b6a6. The function will assume this byte is
> a RET instruction, return a size that's 22 bytes too short and then
> the code execution inside the executable buffer will run past the end
> of buffer, resulting in a segfault.
>
> Use a dummy function and copy entire page, similar to what we do
> in mprotect04.
>
> Signed-off-by: Jan Stancek <jstancek@redhat.com>
> ---
> testcases/kernel/syscalls/pkeys/Makefile | 2 ++
> testcases/kernel/syscalls/pkeys/pkey01.c | 31 ++++++++++--------------
> 2 files changed, 15 insertions(+), 18 deletions(-)
>
> Notes:
>
> This could be an alternative to reverting PKEY_DISABLE_EXECUTE test.
> I haven't tested it yet on 32bit, but since it doesn't rely on any
> instruction codes I don't expect it to break. An important test
> would also be ppc64le.
I tested 32bit and ppc64le, both worked fine, so I propose we give this
a consideration as alternative to Martin's revert.
>
> diff --git a/testcases/kernel/syscalls/pkeys/Makefile b/testcases/kernel/syscalls/pkeys/Makefile
> index 9ee2c2ea57b0..814593f3c720 100644
> --- a/testcases/kernel/syscalls/pkeys/Makefile
> +++ b/testcases/kernel/syscalls/pkeys/Makefile
> @@ -5,4 +5,6 @@ top_srcdir ?= ../../../..
>
> include $(top_srcdir)/include/mk/testcases.mk
>
> +pkey01: CFLAGS += -falign-functions=64
> +
> include $(top_srcdir)/include/mk/generic_leaf_target.mk
> diff --git a/testcases/kernel/syscalls/pkeys/pkey01.c b/testcases/kernel/syscalls/pkeys/pkey01.c
> index c041cbcfd969..b10760b5bd2f 100644
> --- a/testcases/kernel/syscalls/pkeys/pkey01.c
> +++ b/testcases/kernel/syscalls/pkeys/pkey01.c
> @@ -144,15 +144,9 @@ static char *flag_to_str(int flags)
> }
> }
>
> -static size_t function_size(void (*func)(void))
> +static long __attribute__ ((noinline)) dummy_func(void)
> {
> - unsigned char *start = (unsigned char *)func;
> - unsigned char *end = start;
> -
> - while (*end != 0xC3 && *end != 0xC2)
> - end++;
> -
> - return (size_t)(end - start + 1);
> + return 0xdead;
> }
>
> /*
> @@ -165,8 +159,11 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
> char *buffer;
> int pkey, status;
> int fd = mpa->fd;
> - size_t (*func)();
> - size_t func_size = 0;
> + long (*func)(void) = 0;
> + uintptr_t page_mask = ~(getpagesize() - 1);
> + uintptr_t offset_mask = (getpagesize() - 1);
> + uintptr_t func_page_offset = (uintptr_t)&dummy_func & offset_mask;
> + void *page_to_copy = (void *)((uintptr_t)&dummy_func & page_mask);
>
> if (!execute_supported && (tc->access_rights == PKEY_DISABLE_EXECUTE)) {
> tst_res(TCONF, "skip PKEY_DISABLE_EXECUTE test");
> @@ -184,8 +181,8 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
> buffer = SAFE_MMAP(NULL, size, mpa->prot, mpa->flags, fd, 0);
>
> if (mpa->prot == (PROT_READ | PROT_WRITE | PROT_EXEC)) {
> - func_size = function_size((void (*)(void))function_size);
> - memcpy(buffer, (void *)function_size, func_size);
> + memcpy(buffer, page_to_copy, getpagesize());
> + func = (long (*)(void))(buffer + func_page_offset);
> }
>
> pkey = pkey_alloc(tc->flags, tc->access_rights);
> @@ -211,8 +208,7 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
> "Write buffer success, buffer[0] = %d", *buffer);
> break;
> case PKEY_DISABLE_EXECUTE:
> - func = (size_t (*)())buffer;
> - tst_res(TFAIL | TERRNO, "Execute buffer result = %zi", func(func));
> + tst_res(TFAIL | TERRNO, "Execute buffer result = %ld", func());
> break;
> }
> exit(0);
> @@ -242,11 +238,10 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
> tst_res(TPASS, "Read & Write buffer success, buffer[0] = %d", *buffer);
> break;
> case PROT_READ | PROT_WRITE | PROT_EXEC:
> - func = (size_t (*)())buffer;;
> - if (func_size == func(func))
> - tst_res(TPASS, "Execute buffer success, result = %zi", func_size);
> + if (dummy_func() == func())
> + tst_res(TPASS, "Execute buffer success, result = %ld", dummy_func());
> else
> - tst_res(TFAIL, "Execute buffer with unexpected result: %zi", func(func));
> + tst_res(TFAIL, "Execute buffer with unexpected result: %zi", func());
> break;
> }
>
> --
> 2.43.0
>
>
> --
> Mailing list info: https://lists.linux.it/listinfo/ltp
>
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [LTP] [PATCH] syscalls/pkeys01: use a dummy function instead of function_size
2024-11-20 14:56 ` Jan Stancek
@ 2024-11-20 17:07 ` Petr Vorel
0 siblings, 0 replies; 12+ messages in thread
From: Petr Vorel @ 2024-11-20 17:07 UTC (permalink / raw)
To: Jan Stancek; +Cc: ltp
Hi Jan,
...
> > This could be an alternative to reverting PKEY_DISABLE_EXECUTE test.
> > I haven't tested it yet on 32bit, but since it doesn't rely on any
> > instruction codes I don't expect it to break. An important test
> > would also be ppc64le.
Reviewed-by: Petr Vorel <pvorel@suse.cz>
LGTM.
NOTE: we have hackweek this week, I guess you get Martin's feedback next week.
> I tested 32bit and ppc64le, both worked fine, so I propose we give this
> a consideration as alternative to Martin's revert.
Thanks!
Kind regards,
Petr
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [LTP] [PATCH] syscalls/pkeys01: use a dummy function instead of function_size
2024-11-19 15:47 ` [LTP] [PATCH] syscalls/pkeys01: use a dummy function instead of function_size Jan Stancek
2024-11-20 14:56 ` Jan Stancek
@ 2024-11-25 3:43 ` Li Wang
2024-11-25 16:20 ` Martin Doucha
2 siblings, 0 replies; 12+ messages in thread
From: Li Wang @ 2024-11-25 3:43 UTC (permalink / raw)
To: Jan Stancek; +Cc: ltp
On Tue, Nov 19, 2024 at 11:48 PM Jan Stancek <jstancek@redhat.com> wrote:
> As Martin found:
> The function_size() code
> is broken in a way that I cannot easily fix. The function tries
> to calculate the size of a function by finding the first RET
> instruction. However, in 32bit LTP builds, the code gets compiled
> to this:
>
> 0804b690 <function_size>:
> 804b690: 8b 4c 24 04 mov 0x4(%esp),%ecx
> 804b694: 0f b6 01 movzbl (%ecx),%eax
> 804b697: 83 c0 3e add $0x3e,%eax
> 804b69a: 3c 01 cmp $0x1,%al
> 804b69c: 76 1a jbe 804b6b8
> <function_size+0x28>
> 804b69e: 89 c8 mov %ecx,%eax
> 804b6a0: 83 c0 01 add $0x1,%eax
> 804b6a3: 0f b6 10 movzbl (%eax),%edx
> 804b6a6: 83 c2 3e add $0x3e,%edx
> 804b6a9: 80 fa 01 cmp $0x1,%dl
> 804b6ac: 77 f2 ja 804b6a0
> <function_size+0x10>
> 804b6ae: 29 c8 sub %ecx,%eax
> 804b6b0: 83 c0 10 add $0x10,%eax
> 804b6b3: c3 ret
> 804b6b4: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
> 804b6b8: b8 10 00 00 00 mov $0x10,%eax
> 804b6bd: c3 ret
> 804b6be: 66 90 xchg %ax,%ax
>
> If you look closely enough, you'll notice a C2 byte in add $0x3e,%edx
> instruction on address 804b6a6. The function will assume this byte is
> a RET instruction, return a size that's 22 bytes too short and then
> the code execution inside the executable buffer will run past the end
> of buffer, resulting in a segfault.
>
> Use a dummy function and copy entire page, similar to what we do
> in mprotect04.
>
> Signed-off-by: Jan Stancek <jstancek@redhat.com>
>
This way is better than reverting, thanks!
Reviewed-by: Li Wang <liwang@redhat.com>
--
Regards,
Li Wang
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [LTP] [PATCH] syscalls/pkeys01: use a dummy function instead of function_size
2024-11-19 15:47 ` [LTP] [PATCH] syscalls/pkeys01: use a dummy function instead of function_size Jan Stancek
2024-11-20 14:56 ` Jan Stancek
2024-11-25 3:43 ` Li Wang
@ 2024-11-25 16:20 ` Martin Doucha
2024-11-26 8:05 ` Jan Stancek
2 siblings, 1 reply; 12+ messages in thread
From: Martin Doucha @ 2024-11-25 16:20 UTC (permalink / raw)
To: Jan Stancek, ltp
Hi,
I've successfully tested your patch on both x86 (both 32 and 64bit) and
PPC64. The extra alignment should ensure that the dummy function doesn't
get split between two pages so this is a good fix. There's just one
minor printf formatting issue at the end that can be fixed during merge.
Reviewed-by: Martin Doucha <mdoucha@suse.cz>
On 19. 11. 24 16:47, Jan Stancek wrote:
> As Martin found:
> The function_size() code
> is broken in a way that I cannot easily fix. The function tries
> to calculate the size of a function by finding the first RET
> instruction. However, in 32bit LTP builds, the code gets compiled
> to this:
>
> 0804b690 <function_size>:
> 804b690: 8b 4c 24 04 mov 0x4(%esp),%ecx
> 804b694: 0f b6 01 movzbl (%ecx),%eax
> 804b697: 83 c0 3e add $0x3e,%eax
> 804b69a: 3c 01 cmp $0x1,%al
> 804b69c: 76 1a jbe 804b6b8 <function_size+0x28>
> 804b69e: 89 c8 mov %ecx,%eax
> 804b6a0: 83 c0 01 add $0x1,%eax
> 804b6a3: 0f b6 10 movzbl (%eax),%edx
> 804b6a6: 83 c2 3e add $0x3e,%edx
> 804b6a9: 80 fa 01 cmp $0x1,%dl
> 804b6ac: 77 f2 ja 804b6a0 <function_size+0x10>
> 804b6ae: 29 c8 sub %ecx,%eax
> 804b6b0: 83 c0 10 add $0x10,%eax
> 804b6b3: c3 ret
> 804b6b4: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
> 804b6b8: b8 10 00 00 00 mov $0x10,%eax
> 804b6bd: c3 ret
> 804b6be: 66 90 xchg %ax,%ax
>
> If you look closely enough, you'll notice a C2 byte in add $0x3e,%edx
> instruction on address 804b6a6. The function will assume this byte is
> a RET instruction, return a size that's 22 bytes too short and then
> the code execution inside the executable buffer will run past the end
> of buffer, resulting in a segfault.
>
> Use a dummy function and copy entire page, similar to what we do
> in mprotect04.
>
> Signed-off-by: Jan Stancek <jstancek@redhat.com>
> ---
> testcases/kernel/syscalls/pkeys/Makefile | 2 ++
> testcases/kernel/syscalls/pkeys/pkey01.c | 31 ++++++++++--------------
> 2 files changed, 15 insertions(+), 18 deletions(-)
>
> Notes:
>
> This could be an alternative to reverting PKEY_DISABLE_EXECUTE test.
> I haven't tested it yet on 32bit, but since it doesn't rely on any
> instruction codes I don't expect it to break. An important test
> would also be ppc64le.
>
> diff --git a/testcases/kernel/syscalls/pkeys/Makefile b/testcases/kernel/syscalls/pkeys/Makefile
> index 9ee2c2ea57b0..814593f3c720 100644
> --- a/testcases/kernel/syscalls/pkeys/Makefile
> +++ b/testcases/kernel/syscalls/pkeys/Makefile
> @@ -5,4 +5,6 @@ top_srcdir ?= ../../../..
>
> include $(top_srcdir)/include/mk/testcases.mk
>
> +pkey01: CFLAGS += -falign-functions=64
> +
> include $(top_srcdir)/include/mk/generic_leaf_target.mk
> diff --git a/testcases/kernel/syscalls/pkeys/pkey01.c b/testcases/kernel/syscalls/pkeys/pkey01.c
> index c041cbcfd969..b10760b5bd2f 100644
> --- a/testcases/kernel/syscalls/pkeys/pkey01.c
> +++ b/testcases/kernel/syscalls/pkeys/pkey01.c
> @@ -144,15 +144,9 @@ static char *flag_to_str(int flags)
> }
> }
>
> -static size_t function_size(void (*func)(void))
> +static long __attribute__ ((noinline)) dummy_func(void)
> {
> - unsigned char *start = (unsigned char *)func;
> - unsigned char *end = start;
> -
> - while (*end != 0xC3 && *end != 0xC2)
> - end++;
> -
> - return (size_t)(end - start + 1);
> + return 0xdead;
> }
>
> /*
> @@ -165,8 +159,11 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
> char *buffer;
> int pkey, status;
> int fd = mpa->fd;
> - size_t (*func)();
> - size_t func_size = 0;
> + long (*func)(void) = 0;
> + uintptr_t page_mask = ~(getpagesize() - 1);
> + uintptr_t offset_mask = (getpagesize() - 1);
> + uintptr_t func_page_offset = (uintptr_t)&dummy_func & offset_mask;
> + void *page_to_copy = (void *)((uintptr_t)&dummy_func & page_mask);
>
> if (!execute_supported && (tc->access_rights == PKEY_DISABLE_EXECUTE)) {
> tst_res(TCONF, "skip PKEY_DISABLE_EXECUTE test");
> @@ -184,8 +181,8 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
> buffer = SAFE_MMAP(NULL, size, mpa->prot, mpa->flags, fd, 0);
>
> if (mpa->prot == (PROT_READ | PROT_WRITE | PROT_EXEC)) {
> - func_size = function_size((void (*)(void))function_size);
> - memcpy(buffer, (void *)function_size, func_size);
> + memcpy(buffer, page_to_copy, getpagesize());
> + func = (long (*)(void))(buffer + func_page_offset);
> }
>
> pkey = pkey_alloc(tc->flags, tc->access_rights);
> @@ -211,8 +208,7 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
> "Write buffer success, buffer[0] = %d", *buffer);
> break;
> case PKEY_DISABLE_EXECUTE:
> - func = (size_t (*)())buffer;
> - tst_res(TFAIL | TERRNO, "Execute buffer result = %zi", func(func));
> + tst_res(TFAIL | TERRNO, "Execute buffer result = %ld", func());
> break;
> }
> exit(0);
> @@ -242,11 +238,10 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
> tst_res(TPASS, "Read & Write buffer success, buffer[0] = %d", *buffer);
> break;
> case PROT_READ | PROT_WRITE | PROT_EXEC:
> - func = (size_t (*)())buffer;;
> - if (func_size == func(func))
> - tst_res(TPASS, "Execute buffer success, result = %zi", func_size);
> + if (dummy_func() == func())
> + tst_res(TPASS, "Execute buffer success, result = %ld", dummy_func());
> else
> - tst_res(TFAIL, "Execute buffer with unexpected result: %zi", func(func));
> + tst_res(TFAIL, "Execute buffer with unexpected result: %zi", func());
The format here should be %ld.
> break;
> }
>
--
Martin Doucha mdoucha@suse.cz
SW Quality Engineer
SUSE LINUX, s.r.o.
CORSO IIa
Krizikova 148/34
186 00 Prague 8
Czech Republic
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [LTP] [PATCH] syscalls/pkeys01: use a dummy function instead of function_size
2024-11-25 16:20 ` Martin Doucha
@ 2024-11-26 8:05 ` Jan Stancek
0 siblings, 0 replies; 12+ messages in thread
From: Jan Stancek @ 2024-11-26 8:05 UTC (permalink / raw)
To: Martin Doucha, Petr Vorel, Li Wang; +Cc: ltp
On Mon, Nov 25, 2024 at 5:21 PM Martin Doucha <mdoucha@suse.cz> wrote:
>
> Hi,
> I've successfully tested your patch on both x86 (both 32 and 64bit) and
> PPC64. The extra alignment should ensure that the dummy function doesn't
> get split between two pages so this is a good fix. There's just one
> minor printf formatting issue at the end that can be fixed during merge.
>
> Reviewed-by: Martin Doucha <mdoucha@suse.cz>
>
> On 19. 11. 24 16:47, Jan Stancek wrote:
> > As Martin found:
> > The function_size() code
> > is broken in a way that I cannot easily fix. The function tries
> > to calculate the size of a function by finding the first RET
> > instruction. However, in 32bit LTP builds, the code gets compiled
> > to this:
> >
> > 0804b690 <function_size>:
> > 804b690: 8b 4c 24 04 mov 0x4(%esp),%ecx
> > 804b694: 0f b6 01 movzbl (%ecx),%eax
> > 804b697: 83 c0 3e add $0x3e,%eax
> > 804b69a: 3c 01 cmp $0x1,%al
> > 804b69c: 76 1a jbe 804b6b8 <function_size+0x28>
> > 804b69e: 89 c8 mov %ecx,%eax
> > 804b6a0: 83 c0 01 add $0x1,%eax
> > 804b6a3: 0f b6 10 movzbl (%eax),%edx
> > 804b6a6: 83 c2 3e add $0x3e,%edx
> > 804b6a9: 80 fa 01 cmp $0x1,%dl
> > 804b6ac: 77 f2 ja 804b6a0 <function_size+0x10>
> > 804b6ae: 29 c8 sub %ecx,%eax
> > 804b6b0: 83 c0 10 add $0x10,%eax
> > 804b6b3: c3 ret
> > 804b6b4: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
> > 804b6b8: b8 10 00 00 00 mov $0x10,%eax
> > 804b6bd: c3 ret
> > 804b6be: 66 90 xchg %ax,%ax
> >
> > If you look closely enough, you'll notice a C2 byte in add $0x3e,%edx
> > instruction on address 804b6a6. The function will assume this byte is
> > a RET instruction, return a size that's 22 bytes too short and then
> > the code execution inside the executable buffer will run past the end
> > of buffer, resulting in a segfault.
> >
> > Use a dummy function and copy entire page, similar to what we do
> > in mprotect04.
> >
> > Signed-off-by: Jan Stancek <jstancek@redhat.com>
> > ---
> > testcases/kernel/syscalls/pkeys/Makefile | 2 ++
> > testcases/kernel/syscalls/pkeys/pkey01.c | 31 ++++++++++--------------
> > 2 files changed, 15 insertions(+), 18 deletions(-)
> >
> > Notes:
> >
> > This could be an alternative to reverting PKEY_DISABLE_EXECUTE test.
> > I haven't tested it yet on 32bit, but since it doesn't rely on any
> > instruction codes I don't expect it to break. An important test
> > would also be ppc64le.
> >
> > diff --git a/testcases/kernel/syscalls/pkeys/Makefile b/testcases/kernel/syscalls/pkeys/Makefile
> > index 9ee2c2ea57b0..814593f3c720 100644
> > --- a/testcases/kernel/syscalls/pkeys/Makefile
> > +++ b/testcases/kernel/syscalls/pkeys/Makefile
> > @@ -5,4 +5,6 @@ top_srcdir ?= ../../../..
> >
> > include $(top_srcdir)/include/mk/testcases.mk
> >
> > +pkey01: CFLAGS += -falign-functions=64
> > +
> > include $(top_srcdir)/include/mk/generic_leaf_target.mk
> > diff --git a/testcases/kernel/syscalls/pkeys/pkey01.c b/testcases/kernel/syscalls/pkeys/pkey01.c
> > index c041cbcfd969..b10760b5bd2f 100644
> > --- a/testcases/kernel/syscalls/pkeys/pkey01.c
> > +++ b/testcases/kernel/syscalls/pkeys/pkey01.c
> > @@ -144,15 +144,9 @@ static char *flag_to_str(int flags)
> > }
> > }
> >
> > -static size_t function_size(void (*func)(void))
> > +static long __attribute__ ((noinline)) dummy_func(void)
> > {
> > - unsigned char *start = (unsigned char *)func;
> > - unsigned char *end = start;
> > -
> > - while (*end != 0xC3 && *end != 0xC2)
> > - end++;
> > -
> > - return (size_t)(end - start + 1);
> > + return 0xdead;
> > }
> >
> > /*
> > @@ -165,8 +159,11 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
> > char *buffer;
> > int pkey, status;
> > int fd = mpa->fd;
> > - size_t (*func)();
> > - size_t func_size = 0;
> > + long (*func)(void) = 0;
> > + uintptr_t page_mask = ~(getpagesize() - 1);
> > + uintptr_t offset_mask = (getpagesize() - 1);
> > + uintptr_t func_page_offset = (uintptr_t)&dummy_func & offset_mask;
> > + void *page_to_copy = (void *)((uintptr_t)&dummy_func & page_mask);
> >
> > if (!execute_supported && (tc->access_rights == PKEY_DISABLE_EXECUTE)) {
> > tst_res(TCONF, "skip PKEY_DISABLE_EXECUTE test");
> > @@ -184,8 +181,8 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
> > buffer = SAFE_MMAP(NULL, size, mpa->prot, mpa->flags, fd, 0);
> >
> > if (mpa->prot == (PROT_READ | PROT_WRITE | PROT_EXEC)) {
> > - func_size = function_size((void (*)(void))function_size);
> > - memcpy(buffer, (void *)function_size, func_size);
> > + memcpy(buffer, page_to_copy, getpagesize());
> > + func = (long (*)(void))(buffer + func_page_offset);
> > }
> >
> > pkey = pkey_alloc(tc->flags, tc->access_rights);
> > @@ -211,8 +208,7 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
> > "Write buffer success, buffer[0] = %d", *buffer);
> > break;
> > case PKEY_DISABLE_EXECUTE:
> > - func = (size_t (*)())buffer;
> > - tst_res(TFAIL | TERRNO, "Execute buffer result = %zi", func(func));
> > + tst_res(TFAIL | TERRNO, "Execute buffer result = %ld", func());
> > break;
> > }
> > exit(0);
> > @@ -242,11 +238,10 @@ static int pkey_test(struct tcase *tc, struct mmap_param *mpa)
> > tst_res(TPASS, "Read & Write buffer success, buffer[0] = %d", *buffer);
> > break;
> > case PROT_READ | PROT_WRITE | PROT_EXEC:
> > - func = (size_t (*)())buffer;;
> > - if (func_size == func(func))
> > - tst_res(TPASS, "Execute buffer success, result = %zi", func_size);
> > + if (dummy_func() == func())
> > + tst_res(TPASS, "Execute buffer success, result = %ld", dummy_func());
> > else
> > - tst_res(TFAIL, "Execute buffer with unexpected result: %zi", func(func));
> > + tst_res(TFAIL, "Execute buffer with unexpected result: %zi", func());
>
> The format here should be %ld.
Pushed with formatting fixed.
Thanks,
Jan
>
> > break;
> > }
> >
>
> --
> Martin Doucha mdoucha@suse.cz
> SW Quality Engineer
> SUSE LINUX, s.r.o.
> CORSO IIa
> Krizikova 148/34
> 186 00 Prague 8
> Czech Republic
>
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [LTP] [PATCH] Revert "pkey01: Adding test for PKEY_DISABLE_EXECUTE"
2024-11-19 13:31 ` Jan Stancek
2024-11-19 15:47 ` [LTP] [PATCH] syscalls/pkeys01: use a dummy function instead of function_size Jan Stancek
@ 2024-11-20 12:57 ` Li Wang
1 sibling, 0 replies; 12+ messages in thread
From: Li Wang @ 2024-11-20 12:57 UTC (permalink / raw)
To: Jan Stancek; +Cc: ltp
On Tue, Nov 19, 2024 at 9:32 PM Jan Stancek <jstancek@redhat.com> wrote:
> On Tue, Nov 19, 2024 at 2:11 PM Jan Stancek <jstancek@redhat.com> wrote:
> >
> > On Fri, Nov 15, 2024 at 7:58 PM Petr Vorel <pvorel@suse.cz> wrote:
> > >
> > > Hi Li, Martin,
> > >
> > > > Sorry, I forgot to CC Li in the patch.
> > >
> > > > On 15. 11. 24 16:44, Martin Doucha wrote:
> > > > > This reverts commit d2b8a476c29d52c484b387454082bbc906b0b4f8.
> > >
> > > > > Remove the PKEY_DISABLE_EXECUTE subtest. The function_size() code
> > > > > is broken in a way that I cannot easily fix. The function tries
> > > > > to calculate the size of a function by finding the first RET
> > > > > instruction. However, in 32bit LTP builds, the code gets compiled
> > > > > to this:
> > >
> > > > > 0804b690 <function_size>:
> > > > > 804b690: 8b 4c 24 04 mov 0x4(%esp),%ecx
> > > > > 804b694: 0f b6 01 movzbl (%ecx),%eax
> > > > > 804b697: 83 c0 3e add $0x3e,%eax
> > > > > 804b69a: 3c 01 cmp $0x1,%al
> > > > > 804b69c: 76 1a jbe 804b6b8
> <function_size+0x28>
> > > > > 804b69e: 89 c8 mov %ecx,%eax
> > > > > 804b6a0: 83 c0 01 add $0x1,%eax
> > > > > 804b6a3: 0f b6 10 movzbl (%eax),%edx
> > > > > 804b6a6: 83 c2 3e add $0x3e,%edx
> > > > > 804b6a9: 80 fa 01 cmp $0x1,%dl
> > > > > 804b6ac: 77 f2 ja 804b6a0
> <function_size+0x10>
> > > > > 804b6ae: 29 c8 sub %ecx,%eax
> > > > > 804b6b0: 83 c0 10 add $0x10,%eax
> > > > > 804b6b3: c3 ret
> > > > > 804b6b4: 8d 74 26 00 lea
> 0x0(%esi,%eiz,1),%esi
> > > > > 804b6b8: b8 10 00 00 00 mov $0x10,%eax
> > > > > 804b6bd: c3 ret
> > > > > 804b6be: 66 90 xchg %ax,%ax
> > >
> > > > > If you look closely enough, you'll notice a C2 byte in add
> $0x3e,%edx
> > > > > instruction on address 804b6a6. The function will assume this byte
> is
> > > > > a RET instruction, return a size that's 22 bytes too short and then
> > > > > the code execution inside the executable buffer will run past the
> end
> > > > > of buffer, resulting in a segfault.
> > >
> > > Martin, thanks a lot for debugging!
> > >
> > > Acked-by: Petr Vorel <pvorel@suse.cz>
> > >
> > > Other option would be to disable this test only for 32bit (keep it for
> 64bit).
> > > Li, any change you could have look into it?
> >
> > I vaguely recall we already dealt with something similar for a
> > different test, which
> > copied function, then changed protection to EXEC and finally ran it.
> > I'll see if I can find it.
>
> Do we need function_size() to return accurate number? In mprotect04
>
That is a simple method to check that the buffer can be executed correctly.
> we ended up using CFLAGS += -falign-functions=64 and copying everything
> until end of page. Function was dummy, so it always fit and it's not an
> issue if we copied more.
>
Yes, this could work as well, thanks for improving it.
P.s I am on traveling this week, so I wouldn't find a machine to test the
patch.
--
Regards,
Li Wang
--
Mailing list info: https://lists.linux.it/listinfo/ltp
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2024-11-26 8:06 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-15 15:44 [LTP] [PATCH] Revert "pkey01: Adding test for PKEY_DISABLE_EXECUTE" Martin Doucha
2024-11-15 16:06 ` Martin Doucha
2024-11-15 18:58 ` Petr Vorel
2024-11-19 13:11 ` Jan Stancek
2024-11-19 13:31 ` Jan Stancek
2024-11-19 15:47 ` [LTP] [PATCH] syscalls/pkeys01: use a dummy function instead of function_size Jan Stancek
2024-11-20 14:56 ` Jan Stancek
2024-11-20 17:07 ` Petr Vorel
2024-11-25 3:43 ` Li Wang
2024-11-25 16:20 ` Martin Doucha
2024-11-26 8:05 ` Jan Stancek
2024-11-20 12:57 ` [LTP] [PATCH] Revert "pkey01: Adding test for PKEY_DISABLE_EXECUTE" Li Wang
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.