123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- From 70bed6aad97ad28d3bfc6dc8bf3c8f9aed77282b Mon Sep 17 00:00:00 2001
- From: Guillem Jover <guillem@hadrons.org>
- Date: Fri, 16 Aug 2019 02:33:46 +0200
- Subject: [PATCH libaio] Fix io_pgetevents() syscall wrapper on 32-bit userland
- on 64-bit kernels
- The kernel compat syscall in the kernel got introduced with a broken
- layout, which requires a pointer to the actual sigset_t variable but
- with the size of the running kernel, not the size of the compat code.
- This means that when the wrapper sends the expected compat (32-bit)
- pointer, the kernel reads a 64-bit pointer, eating with it also the
- sigset size member. And then proceeds to fail the comparison of the
- sigset_t size and returns EINVAL.
- This really needs to be fixed in the kernel, as there's no apparent
- user of the broken compat layout (from codesearch.debian.org, nor a
- quick github.com search). But we have to workaround it in libaio so
- that we can use kernels that have not yet been fixed.
- We do that, by trying the non-broken layout (that would be used with
- a 32-bit userland on a 32-bit kernel), and if that fails with -EINVAL
- we retry with a structure padded to what the kernel expects.
- Signed-off-by: Guillem Jover <guillem@hadrons.org>
- ---
- src/io_pgetevents.c | 38 +++++++++++++++++++++++++++++++-------
- src/libaio.h | 10 ++++++++++
- 2 files changed, 41 insertions(+), 7 deletions(-)
- diff --git a/src/io_pgetevents.c b/src/io_pgetevents.c
- index e6b0614..b2515f2 100644
- --- a/src/io_pgetevents.c
- +++ b/src/io_pgetevents.c
- @@ -33,17 +33,41 @@ int io_pgetevents(io_context_t ctx, long min_nr, long nr,
- struct io_event *events, struct timespec *timeout,
- sigset_t *sigmask)
- {
- - struct {
- - unsigned long ss;
- - unsigned long ss_len;
- - } data;
- + struct io_sigset aio_sigset;
- +#ifndef __LP64__
- + struct io_sigset_compat aio_sigset_compat = { 0 };
- +#endif
- + int ret;
-
- if (aio_ring_is_empty(ctx, timeout))
- return 0;
-
- - data.ss = (unsigned long)sigmask;
- - data.ss_len = _NSIG / 8;
- - return __io_pgetevents(ctx, min_nr, nr, events, timeout, &data);
- + aio_sigset.ss = (unsigned long)sigmask;
- + aio_sigset.ss_len = _NSIG / 8;
- + ret = __io_pgetevents(ctx, min_nr, nr, events, timeout, &aio_sigset);
- +
- +#ifndef __LP64__
- + /*
- + * The compat kernel syscall got introduced with an broken layout for
- + * its sigset argument, expecting it to contain a pointer for the
- + * non-compat pointer size.
- + *
- + * To cope with this on unfixed kernels, in case we are built as a
- + * 32-bit library (which could run on a kernel with compat code) and
- + * when the syscall returns EINVAL due to the kernel not finding the
- + * sigset size member when unpacking the structure, we retry with
- + * the fixed up compat layout, which requires the padding to be
- + * zero-filled, otherwise the 64-bit pointer will contain garbage.
- + */
- + if (ret != -EINVAL)
- + return ret;
- +
- + aio_sigset_compat.ss = (unsigned long)sigmask;
- + aio_sigset_compat.ss_len = _NSIG / 8;
- + ret = __io_pgetevents(ctx, min_nr, nr, events, timeout, &aio_sigset_compat);
- +#endif
- +
- + return ret;
- }
- #else
- int io_pgetevents(io_context_t ctx, long min_nr, long nr,
- diff --git a/src/libaio.h b/src/libaio.h
- index ce3aad1..36ab9bc 100644
- --- a/src/libaio.h
- +++ b/src/libaio.h
- @@ -152,6 +152,16 @@ struct io_event {
- PADDEDul(res2, __pad4);
- };
-
- +struct io_sigset {
- + unsigned long ss;
- + unsigned long ss_len;
- +};
- +
- +struct io_sigset_compat {
- + PADDEDptr(unsigned long ss, __ss_pad);
- + unsigned long ss_len;
- +};
- +
- #undef PADDED
- #undef PADDEDptr
- #undef PADDEDul
- --
- 2.26.0.292.g33ef6b2f38
|