{
int ret;
#if defined(__NR_io_pgetevents) || defined(__NR_io_pgetevents_time64)
- struct {
- unsigned long ss;
- unsigned long ss_len;
- } aio_sigset;
+ struct io_sigset aio_sigset;
+#ifndef __LP64__
+ struct io_sigset_compat aio_sigset_compat = { 0 };
+#endif
struct sys_timespec sys_ts;
if (aio_ring_is_empty(ctx, timeout))
ret = __io_pgetevents(ctx, min_nr, nr, events,
timeout ? &sys_ts : NULL, &aio_sigset);
+
+#ifndef __LP64__
+ /*
+ * The compat kernel syscall got introduced with a 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 ? &sys_ts : NULL, &aio_sigset_compat);
+#endif
#else
ret = -ENOSYS;
#endif