[PATCH v4 3/3] sysv: linux: Pass 64-bit version of semctl syscall
Stepan Golosunov
stepan@golosunov.pp.ru
Sat Mar 28 10:28:18 GMT 2020
27.03.2020 в 12:45:23 -0700 Alistair Francis написал:
> On Fri, Mar 27, 2020 at 11:25 AM Stepan Golosunov
> <stepan@golosunov.pp.ru> wrote:
> >
> > 26.03.2020 в 09:37:24 -0700 Alistair Francis написал:
> > > The semctl_syscall() function passes a union semun to the kernel. The
> > > union includes struct semid_ds as a member. On 32-bit architectures the
> > > Linux kernel provides a *_high version of the 32-bit sem_otime and
> > > sem_ctime values. These can be combined to get a 64-bit version of the
> > > time.
> > >
> > > This patch adjusts the struct semid_ds to support the *_high versions
> > > of sem_otime and sem_ctime. For 32-bit systems with a 64-bit time_t
> > > this can be used to get a 64-bit time from the two 32-bit values.
> > >
> > > Due to allignment differences between 64-bit and 32-bit variables we
> > > also need to set nsems to ensure it's correct.
> > > ---
> > > sysdeps/unix/sysv/linux/bits/semid_ds_t.h | 15 ++++++++++++
> > > .../unix/sysv/linux/hppa/bits/semid_ds_t.h | 15 ++++++++++++
> > > .../unix/sysv/linux/mips/bits/semid_ds_t.h | 13 ++++++++++
> > > .../unix/sysv/linux/powerpc/bits/semid_ds_t.h | 15 ++++++++++++
> > > sysdeps/unix/sysv/linux/semctl.c | 24 +++++++++++++++----
> > > .../unix/sysv/linux/sparc/bits/semid_ds_t.h | 15 ++++++++++++
> > > sysdeps/unix/sysv/linux/x86/bits/semid_ds_t.h | 15 ++++++++++++
> > > 7 files changed, 108 insertions(+), 4 deletions(-)
> > >
> >
> > > --- a/sysdeps/unix/sysv/linux/semctl.c
> > > +++ b/sysdeps/unix/sysv/linux/semctl.c
> > > @@ -29,6 +29,9 @@ union semun
> > > {
> > > int val; /* value for SETVAL */
> > > struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
> > > +#if __WORDSIZE == 32
> > > + struct __semid_ds32 *buf32; /* 32-bit buffer for IPC_STAT */
> > > +#endif
> > > unsigned short int *array; /* array for GETALL & SETALL */
> > > struct seminfo *__buf; /* buffer for IPC_INFO */
> > > };
> > > @@ -44,13 +47,26 @@ union semun
> > > static int
> > > semctl_syscall (int semid, int semnum, int cmd, union semun arg)
> > > {
> > > + int ret;
> > > #ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
> > > - return INLINE_SYSCALL_CALL (semctl, semid, semnum, cmd | __IPC_64,
> > > - arg.array);
> > > + ret = INLINE_SYSCALL_CALL (semctl, semid, semnum, cmd | __IPC_64,
> > > + arg.array);
> > > #else
> > > - return INLINE_SYSCALL_CALL (ipc, IPCOP_semctl, semid, semnum, cmd | __IPC_64,
> > > - SEMCTL_ARG_ADDRESS (arg));
> > > + ret = INLINE_SYSCALL_CALL (ipc, IPCOP_semctl, semid, semnum, cmd | __IPC_64,
> > > + SEMCTL_ARG_ADDRESS (arg));
> > > #endif
> > > +
> > > +#if __WORDSIZE == 32 && __TIMESIZE == 64
> >
> > This condition is wrong for x32 unless someone invents __semid_ds32
> > with 32-bit time fields there.
>
> Ah, good point. I guess there needs to be a && !defined(__ILP32__)
> added to this.
The ususal condition for 32-bit architectures without x32 seems to be
#if (__WORDSIZE == 32 \
&& (!defined __SYSCALL_WORDSIZE || __SYSCALL_WORDSIZE == 32))
> >
> > > + if (ret == 0 && cmd == IPC_STAT)
> >
> > Is cmd == IPC_STAT the only case when conversion is needed?
> > Brief glance at kernel sources suggests that SEM_STAT and SEM_STAT_ANY
> > are also relevant.
>
> I can double check.
>
> >
> > > + {
> > > + arg.buf->sem_nsems = arg.buf32->sem_nsems;
> > > + arg.buf->sem_otime = arg.buf32->sem_otime |
> > > + ((time_t) arg.buf32->sem_otime_high << 32);
> > > + arg.buf->sem_ctime = arg.buf32->sem_ctime |
> > > + ((time_t) arg.buf32->sem_ctime_high << 32);
> > > + }
> >
> > Note that this is supposed to do nothing on little-endian asm-generic
> > architectures (thus hiding bugs for big-endian ones).
> > It's also not obvious why it attempts to copy sem_nsems (if sem_nsems
> > ever changes it's location the assignment will likely clobber some of the
> > sem{o,c}time{,_high}).
>
> This is somewhat explained in the commit message.
>
> For RV32 (and probably others) due to alignment constraints being
> different between 32 and 64-bit types sem_nsems is actually at a
> different offset in bug32 then buf. The kernel fills it out at the
> alignment of buf32 so we need to move it to the alignment of buf.
>
> The same issues applies to both types, so this isn't just for
> big-endian machines.
But if there is padding before sem_otime added doesn't assignment to
arg.buf->sem_otime clobber arg.buf32->sem_ctime?
(And the situation will be even more complex when implementing y2038
support for older architectures like mips.)
This also seems to violate strict aliasing rules.
I guess it could be solved with something like
arg.buf->s = (struct semid_ds) {
.sem_perm = arg.buf->s32.sem_perm,
.sem_nsems = arg.buf->s32.sem_nsems,
.sem_otime = arg.buf->s32.sem_otime | …
…
}
> >
> > > +#endif
> > > + return ret;
> > > }
> > >
> > > int
> >
> > > --- a/sysdeps/unix/sysv/linux/x86/bits/semid_ds_t.h
> > > +++ b/sysdeps/unix/sysv/linux/x86/bits/semid_ds_t.h
> > > @@ -20,6 +20,21 @@
> > > # error "Never include <bits/semid_ds_t.h> directly; use <sys/sem.h> instead."
> > > #endif
> > >
> > > +#if __WORDSIZE == 32
> > > +/* This is the "new" y2038 types defined after the 5.1 kernel. It allows
> > > + * the kernel to use {o,c}time{_high} values to support a 64-bit time_t. */
> > > +struct __semid_ds32 {
> > > + struct ipc_perm sem_perm; /* operation permission struct */
> > > + __syscall_ulong_t sem_otime; /* last semop() time */
> > > + __syscall_ulong_t sem_otime_high; /* last semop() time high */
> > > + __syscall_ulong_t sem_ctime; /* last time changed by semctl() */
> > > + __syscall_ulong_t sem_ctime_high; /* last time changed by semctl() high */
> > > + __syscall_ulong_t sem_nsems; /* number of semaphores in set */
> > > + __syscall_ulong_t __glibc_reserved3;
> > > + __syscall_ulong_t __glibc_reserved4;
> > > +};
> > > +#endif
> >
> > This is wrong for x32 but might work by accident (as long as kernel
> > does not use its __unused1 and __unused2 fields).
>
> Do you know what it should be?
>
> I see this struct in: arch/x86/include/uapi/asm/sembuf.h
I think the condition should be changed to
#ifdef __i386__
x32 does not need __semid_ds32. If one was needed I guess it would
look like
struct __semid_ds32
{
struct ipc_perm sem_perm;
uint32_t __glibc_reserved0;
uint32_t sem_otime;
int32_t sem_otime_high;
__syscall_ulong_t __glibc_reserved1;
uint32_t sem_ctime;
int32_t sem_ctime_high;
__syscall_ulong_t __glibc_reserved2;
__syscall_ulong_t sem_nsems;
__syscall_ulong_t __glibc_reserved3;
__syscall_ulong_t __glibc_reserved4;
};
More information about the Libc-alpha
mailing list