How to Get Argv from "Struct Linux_Binprm"

How can I get argv from struct linux_binprm?

. If you want to get the full command line before the binary loader executing in do_execve_common(), you can try following:
there is one argument *argv in the function do_execve_common() parameter table, why bother to get the argv from "struct linux_binprm"? You can use the *argv directly with following codes. In the do_execve_common(), insert some codes as following:

argc = count(argv, MAX_ARG_STRINGS);
i = 0;
while (i < argc)
{
const char __user *str;
int len;

ret = -EFAULT;
str = get_user_arg_ptr(argv, i);
if (IS_ERR(str))
goto out;

len = strnlen_user(str, MAX_ARG_STRLEN);
if (!len)
goto out;

//copy the str to kernel temporary storage
//NOTE: tmp[] is a string array,
// the memory should have been allocated already for strings storage,
// each string is ended with \0
memcpy(tmp[i], str, len)
}

After executing these codes, I think the argv strings will be all saved in tmp[] array.

. While if you want to get the full command line after binary loader executing, I think at this time the argument page has been setup correctly, then you can try following approach to get the full command line:
There is a function proc_pid_cmdline() in ./fs/proc/base.c file, you can re-use most codes in proc_pid_cmdline() function to get the full command line from the argument page.

Kernel: getting command line and pid_parent in do_execve_common?

I think by the time calling do_execve, the pid & real_parent & comm fields in task_struct are already initialized.

So you can use kernel functions: getpid(), getppid() and get_task_comm() to get what you want. I am not sure these functions are there in your Linux Kernel version. If not, there should be some similar functions.

How to get around the Linux Too Many Arguments limit


edit:

I was finally able to pass <= 256 KB as a single command line argument (see edit (4) in the bottom). However, please read carefully how I did it and decide for yourself if this is a way you want to go. At least you should be able to understand why you are 'stuck' otherwise from what I found out.


With the coupling of ARG_MAX to ulim -s / 4 came the introduction of MAX_ARG_STRLEN as max. length of an argument:

/*
* linux/fs/exec.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/

...

#ifdef CONFIG_MMU
/*
* The nascent bprm->mm is not visible until exec_mmap() but it can
* use a lot of memory, account these pages in current->mm temporary
* for oom_badness()->get_mm_rss(). Once exec succeeds or fails, we
* change the counter back via acct_arg_size(0).
*/

...

static bool valid_arg_len(struct linux_binprm *bprm, long len)
{
return len <= MAX_ARG_STRLEN;
}

...

#else

...

static bool valid_arg_len(struct linux_binprm *bprm, long len)
{
return len <= bprm->p;
}

#endif /* CONFIG_MMU */

...

static int copy_strings(int argc, struct user_arg_ptr argv,
struct linux_binprm *bprm)
{

...

    str = get_user_arg_ptr(argv, argc);

...

    len = strnlen_user(str, MAX_ARG_STRLEN);
if (!len)
goto out;

ret = -E2BIG;
if (!valid_arg_len(bprm, len))
goto out;

...

}

...

MAX_ARG_STRLEN is defined as 32 times the page size in linux/include/uapi/linux/binfmts.h:

...

/*
* These are the maximum length and maximum number of strings passed to the
* execve() system call. MAX_ARG_STRLEN is essentially random but serves to
* prevent the kernel from being unduly impacted by misaddressed pointers.
* MAX_ARG_STRINGS is chosen to fit in a signed 32-bit integer.
*/
#define MAX_ARG_STRLEN (PAGE_SIZE * 32)
#define MAX_ARG_STRINGS 0x7FFFFFFF

...

The default page size is 4 KB so you cannot pass arguments longer than 128 KB.

I can't try it now but maybe switching to huge page mode (page size 4 MB) if possible on your system solves this problem.

For more detailed information and references see this answer to a similar question on Unix & Linux SE.


edits:

(1)
According to this answer one can change the page size of x86_64 Linux to 1 MB by enabling CONFIG_TRANSPARENT_HUGEPAGE and setting CONFIG_TRANSPARENT_HUGEPAGE_MADVISE to n in the kernel config.

(2)
After recompiling my kernel with the above configuration changes getconf PAGESIZE still returns 4096.
According to this answer CONFIG_HUGETLB_PAGE is also needed which I could pull in via CONFIG_HUGETLBFS. I am recompiling now and will test again.

(3)
I recompiled my kernel with CONFIG_HUGETLBFS enabled and now /proc/meminfo contains the corresponding HugePages_* entries mentioned in the corresponding section of the kernel documentation.
However, the page size according to getconf PAGESIZE is still unchanged. So while I should be able now to request huge pages via mmap calls, the kernel's default page size determining MAX_ARG_STRLEN is still fixed at 4 KB.

(4)
I modified linux/include/uapi/linux/binfmts.h to #define MAX_ARG_STRLEN (PAGE_SIZE * 64), recompiled my kernel and now your code produces:

...

117037
123196
123196
129680
129680
136505
143689
151251
159211

...

227982
227982
239981
239981
252611
252611
265906
./testCL: line 11: ./foo: Argument list too long
279901
./testCL: line 11: ./foo: Argument list too long
294632
./testCL: line 11: ./foo: Argument list too long

So now the limit moved from 128 KB to 256 KB as expected.
I don't know about potential side effects though.
As far as I can tell, my system seems to run just fine.



Related Topics



Leave a reply



Submit