From: Laurent Vivier <laurent@vivier.eu>
To: Michael Tokarev <mjt@tls.msk.ru>, qemu-devel@nongnu.org
Subject: Re: [PATCH v2] linux-user: fix getgroups/setgroups allocations
Date: Wed, 25 Jan 2023 14:18:21 +0100 [thread overview]
Message-ID: <8ce38f3c-1f24-c3fb-bf49-deb265418163@vivier.eu> (raw)
In-Reply-To: <20221217093127.3085329-1-mjt@msgid.tls.msk.ru>
Le 17/12/2022 à 10:31, Michael Tokarev a écrit :
> linux-user getgroups(), setgroups(), getgroups32() and setgroups32()
> used alloca() to allocate grouplist arrays, with unchecked gidsetsize
> coming from the "guest". With NGROUPS_MAX being 65536 (linux, and it
> is common for an application to allocate NGROUPS_MAX for getgroups()),
> this means a typical allocation is half the megabyte on the stack.
> Which just overflows stack, which leads to immediate SIGSEGV in actual
> system getgroups() implementation.
>
> An example of such issue is aptitude, eg
> https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=811087#72
>
> Cap gidsetsize to NGROUPS_MAX (return EINVAL if it is larger than that),
> and use heap allocation for grouplist instead of alloca(). While at it,
> fix coding style and make all 4 implementations identical.
>
> Try to not impose random limits - for example, allow gidsetsize to be
> negative for getgroups() - just do not allocate negative-sized grouplist
> in this case but still do actual getgroups() call. But do not allow
> negative gidsetsize for setgroups() since its argument is unsigned.
>
> Capping by NGROUPS_MAX seems a bit arbitrary, - we can do more, it is
> not an error if set size will be NGROUPS_MAX+1. But we should not allow
> integer overflow for the array being allocated. Maybe it is enough to
> just call g_try_new() and return ENOMEM if it fails.
>
> Maybe there's also no need to convert setgroups() since this one is
> usually smaller and known beforehand (KERN_NGROUPS_MAX is actually 63, -
> this is apparently a kernel-imposed limit for runtime group set).
>
> The patch fixes aptitude segfault mentioned above.
>
> Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
> ---
> v2:
> - remove g_free, use g_autofree annotations instead,
> - a bit more coding style changes, makes checkpatch.pl happy
>
> linux-user/syscall.c | 99 ++++++++++++++++++++++++++++++--------------
> 1 file changed, 68 insertions(+), 31 deletions(-)
>
> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> index 24b25759be..8a2f49d18f 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -11433,39 +11433,58 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
> {
> int gidsetsize = arg1;
> target_id *target_grouplist;
> - gid_t *grouplist;
> + g_autofree gid_t *grouplist = NULL;
> int i;
>
> - grouplist = alloca(gidsetsize * sizeof(gid_t));
> + if (gidsetsize > NGROUPS_MAX) {
> + return -TARGET_EINVAL;
> + }
> + if (gidsetsize > 0) {
> + grouplist = g_try_new(gid_t, gidsetsize);
> + if (!grouplist) {
> + return -TARGET_ENOMEM;
> + }
> + }
> ret = get_errno(getgroups(gidsetsize, grouplist));
> - if (gidsetsize == 0)
> - return ret;
> - if (!is_error(ret)) {
> - target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * sizeof(target_id), 0);
> - if (!target_grouplist)
> + if (!is_error(ret) && ret > 0) {
> + target_grouplist = lock_user(VERIFY_WRITE, arg2,
> + gidsetsize * sizeof(target_id), 0);
> + if (!target_grouplist) {
> return -TARGET_EFAULT;
> - for(i = 0;i < ret; i++)
> + }
> + for (i = 0; i < ret; i++) {
> target_grouplist[i] = tswapid(high2lowgid(grouplist[i]));
> - unlock_user(target_grouplist, arg2, gidsetsize * sizeof(target_id));
> + }
> + unlock_user(target_grouplist, arg2,
> + gidsetsize * sizeof(target_id));
> }
> + return ret;
> }
> - return ret;
> case TARGET_NR_setgroups:
> {
> int gidsetsize = arg1;
> target_id *target_grouplist;
> - gid_t *grouplist = NULL;
> + g_autofree gid_t *grouplist = NULL;
> int i;
> - if (gidsetsize) {
> - grouplist = alloca(gidsetsize * sizeof(gid_t));
> - target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * sizeof(target_id), 1);
> +
> + if (gidsetsize > NGROUPS_MAX || gidsetsize < 0) {
> + return -TARGET_EINVAL;
> + }
> + if (gidsetsize > 0) {
> + grouplist = g_try_new(gid_t, gidsetsize);
> + if (!grouplist) {
> + return -TARGET_ENOMEM;
> + }
> + target_grouplist = lock_user(VERIFY_READ, arg2,
> + gidsetsize * sizeof(target_id), 1);
> if (!target_grouplist) {
> return -TARGET_EFAULT;
> }
> for (i = 0; i < gidsetsize; i++) {
> grouplist[i] = low2highgid(tswapid(target_grouplist[i]));
> }
> - unlock_user(target_grouplist, arg2, 0);
> + unlock_user(target_grouplist, arg2,
> + gidsetsize * sizeof(target_id));
> }
> return get_errno(setgroups(gidsetsize, grouplist));
> }
> @@ -11750,41 +11769,59 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
> {
> int gidsetsize = arg1;
> uint32_t *target_grouplist;
> - gid_t *grouplist;
> + g_autofree gid_t *grouplist = NULL;
> int i;
>
> - grouplist = alloca(gidsetsize * sizeof(gid_t));
> + if (gidsetsize > NGROUPS_MAX) {
> + return -TARGET_EINVAL;
> + }
> + if (gidsetsize > 0) {
> + grouplist = g_try_new(gid_t, gidsetsize);
> + if (!grouplist) {
> + return -TARGET_ENOMEM;
> + }
> + }
> ret = get_errno(getgroups(gidsetsize, grouplist));
> - if (gidsetsize == 0)
> - return ret;
> - if (!is_error(ret)) {
> - target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * 4, 0);
> + if (!is_error(ret) && ret > 0) {
> + target_grouplist = lock_user(VERIFY_WRITE, arg2,
> + gidsetsize * 4, 0);
> if (!target_grouplist) {
> return -TARGET_EFAULT;
> }
> - for(i = 0;i < ret; i++)
> + for (i = 0; i < ret; i++) {
> target_grouplist[i] = tswap32(grouplist[i]);
> + }
> unlock_user(target_grouplist, arg2, gidsetsize * 4);
> }
> + return ret;
> }
> - return ret;
> #endif
> #ifdef TARGET_NR_setgroups32
> case TARGET_NR_setgroups32:
> {
> int gidsetsize = arg1;
> uint32_t *target_grouplist;
> - gid_t *grouplist;
> + g_autofree gid_t *grouplist = NULL;
> int i;
>
> - grouplist = alloca(gidsetsize * sizeof(gid_t));
> - target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * 4, 1);
> - if (!target_grouplist) {
> - return -TARGET_EFAULT;
> + if (gidsetsize > NGROUPS_MAX || gidsetsize < 0) {
> + return -TARGET_EINVAL;
> + }
> + if (gidsetsize > 0) {
> + grouplist = g_try_new(gid_t, gidsetsize);
> + if (!grouplist) {
> + return -TARGET_ENOMEM;
> + }
> + target_grouplist = lock_user(VERIFY_READ, arg2,
> + gidsetsize * 4, 1);
> + if (!target_grouplist) {
> + return -TARGET_EFAULT;
> + }
> + for (i = 0; i < gidsetsize; i++) {
> + grouplist[i] = tswap32(target_grouplist[i]);
> + }
> + unlock_user(target_grouplist, arg2, 0);
> }
> - for(i = 0;i < gidsetsize; i++)
> - grouplist[i] = tswap32(target_grouplist[i]);
> - unlock_user(target_grouplist, arg2, 0);
> return get_errno(setgroups(gidsetsize, grouplist));
> }
> #endif
Applied to my linux-user-for-8.0 branch.
Thanks,
Laurent
next prev parent reply other threads:[~2023-01-25 13:18 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-12-17 9:31 [PATCH v2] linux-user: fix getgroups/setgroups allocations Michael Tokarev
2022-12-17 18:55 ` Richard Henderson
2023-01-25 13:18 ` Laurent Vivier [this message]
2023-02-03 21:49 ` Laurent Vivier
2023-02-03 21:57 ` Richard Henderson
2023-02-03 22:23 ` Laurent Vivier
2023-04-09 9:06 ` Michael Tokarev
[not found] ` <7a3beebe-5593-8fda-f8ec-7e08789da12f@tls.msk.ru>
2023-04-09 9:17 ` Michael Tokarev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=8ce38f3c-1f24-c3fb-bf49-deb265418163@vivier.eu \
--to=laurent@vivier.eu \
--cc=mjt@tls.msk.ru \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).