powerpc: Use correct procedure call standard for getrandom vDSO call (bug 32440)

A plain indirect function call does not work on POWER because
success and failure are signaled through a flag register, and
not via the usual Linux negative return value convention.

This has potential security impact, in two ways: the return value
could be out of bounds (EAGAIN is 11 on powerpc6le), and no
random bytes have been written despite the non-error return value.

Fixes commit 461cab1de7 ("linux: Add
support for getrandom vDSO").

Reported-by: Ján Stanček <jstancek@redhat.com>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
This commit is contained in:
Florian Weimer 2024-12-10 16:17:06 +01:00
parent b79f257533
commit 4f5704ea34
3 changed files with 49 additions and 6 deletions

View File

@ -281,6 +281,7 @@ tests := \
tst-getenv-thread \ tst-getenv-thread \
tst-getenv-unsetenv \ tst-getenv-unsetenv \
tst-getrandom \ tst-getrandom \
tst-getrandom-errno \
tst-getrandom2 \ tst-getrandom2 \
tst-labs \ tst-labs \
tst-limits \ tst-limits \

View File

@ -0,0 +1,37 @@
/* Test errno handling in getrandom (bug 32440).
Copyright (C) 2024 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <errno.h>
#include <stdlib.h>
#include <support/check.h>
#include <sys/random.h>
static
int do_test (void)
{
errno = -1181968554; /* Just a random value. */
char buf[4];
int ret = getrandom (buf, sizeof (buf), -1); /* All flags set. */
if (errno != ENOSYS)
TEST_COMPARE (errno, EINVAL);
TEST_COMPARE (ret, -1);
return 0;
}
#include <support/test-driver.c>

View File

@ -20,6 +20,8 @@
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <sysdep-cancel.h> #include <sysdep-cancel.h>
#include <sysdep.h>
#include <sysdep-vdso.h>
static inline ssize_t static inline ssize_t
getrandom_syscall (void *buffer, size_t length, unsigned int flags, getrandom_syscall (void *buffer, size_t length, unsigned int flags,
@ -201,7 +203,8 @@ getrandom_vdso (void *buffer, size_t length, unsigned int flags, bool cancel)
cancellation bridge (__syscall_cancel_arch), use GRND_NONBLOCK so there cancellation bridge (__syscall_cancel_arch), use GRND_NONBLOCK so there
is no potential unbounded blocking in the kernel. It should be a rare is no potential unbounded blocking in the kernel. It should be a rare
situation, only at system startup when RNG is not initialized. */ situation, only at system startup when RNG is not initialized. */
ssize_t ret = GLRO (dl_vdso_getrandom) (buffer, long int ret = INTERNAL_VSYSCALL_CALL (GLRO (dl_vdso_getrandom), 5,
buffer,
length, length,
flags | GRND_NONBLOCK, flags | GRND_NONBLOCK,
state, state,
@ -241,7 +244,9 @@ __getrandom_early_init (_Bool initial)
uint32_t mmap_flags; uint32_t mmap_flags;
uint32_t reserved[13]; uint32_t reserved[13];
} params; } params;
if (GLRO(dl_vdso_getrandom) (NULL, 0, 0, &params, ~0UL) == 0) long int ret = INTERNAL_VSYSCALL_CALL (GLRO(dl_vdso_getrandom),
5, NULL, 0, 0, &params, ~0UL);
if (! INTERNAL_SYSCALL_ERROR_P (ret))
{ {
/* Align each opaque state to L1 data cache size to avoid false /* Align each opaque state to L1 data cache size to avoid false
sharing. If the size can not be obtained, use the kernel sharing. If the size can not be obtained, use the kernel