-
Notifications
You must be signed in to change notification settings - Fork 611
i#1973: musl: Scan stack for kernel arguments when used as library #7266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
1affc20
9a41e6d
0a5730a
a5ce285
6d6c523
10b7f0e
fe3e877
6b12174
8875f7d
fb03426
f561bea
2d352c5
6cc679a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -788,6 +788,82 @@ static init_fn_t | |
| #else | ||
| /* If we're a normal shared object, then we override _init. | ||
| */ | ||
|
|
||
| # if defined(MUSL) | ||
| # define EFAULT 14 | ||
|
ziyao233 marked this conversation as resolved.
Outdated
|
||
| # define AT_EXECFN 31 | ||
|
ziyao233 marked this conversation as resolved.
Outdated
|
||
| static int | ||
| check_address_readable(void *addr) | ||
|
ziyao233 marked this conversation as resolved.
Outdated
|
||
| { | ||
| return dynamorio_syscall(SYS_rt_sigprocmask, ~0L, addr, NULL, | ||
| sizeof(kernel_sigset_t)) != EFAULT; | ||
| } | ||
|
|
||
| /* When entering the entry point, the stack layout looks like | ||
| * sp => argc | ||
| * argv | ||
| * NULL (end of argv) | ||
| * envp | ||
| * NULL (end of envp) | ||
| * auxv | ||
| * search_auxvector() walks towards the higher address and locate one of the | ||
|
ziyao233 marked this conversation as resolved.
Outdated
|
||
| * auxvector entry, then walk backwards and find the beginning of auxvector. */ | ||
|
ziyao233 marked this conversation as resolved.
Outdated
ziyao233 marked this conversation as resolved.
Outdated
ziyao233 marked this conversation as resolved.
Outdated
|
||
| static void * | ||
| search_auxvector(void *sp) | ||
| { | ||
| /* XXX: Check whether 64 * PAGE_SIZE is an appropriate limit */ | ||
| for (size_t offset = 0; offset < PAGE_SIZE * 64; offset += sizeof(ulong)) { | ||
| ELF_AUXV_TYPE *p = sp + offset; | ||
|
|
||
| if (((uintptr_t)(&p->a_un) & (PAGE_SIZE - 1)) == 0 && | ||
|
ziyao233 marked this conversation as resolved.
Outdated
|
||
| !check_address_readable(&p->a_un)) | ||
| return NULL; | ||
|
|
||
| /* Check for AT_EXECFN entry in the auxvector, which contains pathname | ||
| * of the program and should be a readable address. */ | ||
| if (p->a_type == AT_EXECFN && check_address_readable((void *)p->a_un.a_val)) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a strong enough check? An integer 31 adjacent to a valid pointer? It seems like this could happen to appear somewhere else on the stack.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Obviously it's not that strong. My original intention is to depend on prctl PR_GET_AUXV, which returns the content of auxvector, enabling an exact match. But it is relatively new (introduced in Linux 6.4). Alpine Linux started to ship 6.6 kernel from 3.19, two versions before current LTS, so it may not be that problematic. Do you think it's acceptable to use PR_GET_AUXV to do some exact address matching and fallback to current solution if it isn't available? Or bail out in case of absence of PR_GET_AUXV to prevent possible failures of the flaky solution?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since limited to ifdef MUSL that may be ok. Make sure to add a comment about it being new. And a comment about the fragility of the fallback if it's left in place. |
||
| for (; (void *)p > sp; p--) { | ||
| /* The maximum key in auxvector is much smaller than 0x400. | ||
| * This assumes envp contains much higher addresses. An auxvector | ||
| * entry with zero as key indicates the end, thus the only case | ||
| * that it's encountered when searching towards auxvector's | ||
| * start is an empty envp. */ | ||
| if ((p->a_type == 0 || p->a_type >= 0x400) && p->a_un.a_val == 0) | ||
| return p + 1; | ||
| } | ||
| return NULL; /* shouldn't reach here */ | ||
| } | ||
| } | ||
|
|
||
| return NULL; | ||
| } | ||
|
|
||
| static void | ||
| search_kernel_args_on_stack(int *argc, char ***argv, char ***envp) | ||
| { | ||
| ulong *sp; | ||
| GET_STACK_PTR(sp); | ||
|
|
||
| ulong *auxv = search_auxvector(sp); | ||
|
|
||
| ASSERT_MESSAGE(CHKLVL_ASSERTS, "failed to find auxv", auxv != NULL); | ||
|
|
||
| ulong *p = &auxv[-2]; | ||
| for (; p[-1] && &p[-1] > sp; p--) | ||
|
ziyao233 marked this conversation as resolved.
Outdated
|
||
| ; | ||
|
|
||
| ASSERT_MESSAGE(CHKLVL_ASSERTS, "failed to find envp", p != sp); | ||
|
|
||
| *envp = (char **)p; | ||
|
|
||
| /* XXX: It's hard to determine the start of argv b/c argc locates immediately | ||
| * before it. Luckily, our_init only makes use of envp. argc and argv are | ||
| * zeroed. */ | ||
| *argc = 0; | ||
| *argv = NULL; | ||
| } | ||
| # endif | ||
|
ziyao233 marked this conversation as resolved.
Outdated
|
||
|
|
||
| INITIALIZER_ATTRIBUTES int | ||
| _init(int argc, char **argv, char **envp) | ||
| { | ||
|
|
@@ -803,6 +879,11 @@ _init(int argc, char **argv, char **envp) | |
| envp = NULL; | ||
| } | ||
| ASSERT_MESSAGE(CHKLVL_ASSERTS, "failed to find envp", envp != NULL); | ||
| # endif | ||
| # ifdef MUSL | ||
| /* i#1973: musl passes nothing to library init routines. We scan the stack | ||
| * to find the arguments passed by kernel. */ | ||
| search_kernel_args_on_stack(&argc, &argv, &envp); | ||
| # endif | ||
| return our_init(argc, argv, envp); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that get_kernel_args() in loader_android.c for older Android finds argc, argv, and envp inside a structure pointed at by TLS which I believe is specific to Android.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC the stack layouts on starting a process between normal Linux kernels and Android ones should be the same, thus it should be okay to reuse it for Android.