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,