diff --git a/sys/kern/kern_umtx.c b/sys/kern/kern_umtx.c index dc2f820bc349..9e0a38709ce3 100644 --- a/sys/kern/kern_umtx.c +++ b/sys/kern/kern_umtx.c @@ -75,8 +75,8 @@ __FBSDID("$FreeBSD$"); #include #include -#ifdef COMPAT_FREEBSD32 #include +#ifdef COMPAT_FREEBSD32 #include #endif @@ -232,7 +232,6 @@ struct umtx_copyops { const bool compat32; }; -#ifdef COMPAT_FREEBSD32 struct umtx_time32 { struct timespec32 _timeout; uint32_t _flags; @@ -257,7 +256,6 @@ struct umutex32 { _Static_assert(sizeof(struct umutex) == sizeof(struct umutex32), "umutex32"); _Static_assert(__offsetof(struct umutex, m_spare[0]) == __offsetof(struct umutex32, m_spare[0]), "m_spare32"); -#endif int umtx_shm_vnobj_persistent = 0; SYSCTL_INT(_kern_ipc, OID_AUTO, umtx_vnode_persistent, CTLFLAG_RWTUN, @@ -4167,6 +4165,15 @@ __umtx_op_robust_lists(struct thread *td, struct _umtx_op_args *uap, struct umtx_robust_lists_params rb; int error; + if (ops->compat32) { + if ((td->td_pflags2 & TDP2_COMPAT32RB) == 0 && + (td->td_rb_list != 0 || td->td_rbp_list != 0 || + td->td_rb_inact != 0)) + return (EBUSY); + } else if ((td->td_pflags2 & TDP2_COMPAT32RB) != 0) { + return (EBUSY); + } + bzero(&rb, sizeof(rb)); error = ops->copyin_robust_lists(uap->uaddr1, uap->val, &rb); if (error != 0) @@ -4174,8 +4181,6 @@ __umtx_op_robust_lists(struct thread *td, struct _umtx_op_args *uap, if (ops->compat32) td->td_pflags2 |= TDP2_COMPAT32RB; - else if ((td->td_pflags2 & TDP2_COMPAT32RB) != 0) - return (EINVAL); td->td_rb_list = rb.robust_list_offset; td->td_rbp_list = rb.robust_priv_list_offset; @@ -4183,49 +4188,45 @@ __umtx_op_robust_lists(struct thread *td, struct _umtx_op_args *uap, return (0); } -#ifdef COMPAT_FREEBSD32 -static inline int -umtx_copyin_timeout32(const void *uaddr, struct timespec *tsp) -{ - struct timespec32 ts32; - int error; +#if defined(__i386__) || defined(__amd64__) +/* + * Provide the standard 32-bit definitions for x86, since native/compat32 use a + * 32-bit time_t there. Other architectures just need the i386 definitions + * along with their standard compat32. + */ +struct timespecx32 { + int64_t tv_sec; + int32_t tv_nsec; +}; - error = copyin(uaddr, &ts32, sizeof(ts32)); - if (error == 0) { - if (ts32.tv_sec < 0 || - ts32.tv_nsec >= 1000000000 || - ts32.tv_nsec < 0) - error = EINVAL; - else { - CP(ts32, *tsp, tv_sec); - CP(ts32, *tsp, tv_nsec); - } - } - return (error); -} +struct umtx_timex32 { + struct timespecx32 _timeout; + uint32_t _flags; + uint32_t _clockid; +}; -static inline int -umtx_copyin_umtx_time32(const void *uaddr, size_t size, struct _umtx_time *tp) -{ - struct umtx_time32 t32; - int error; +#ifndef __i386__ +#define timespeci386 timespec32 +#define umtx_timei386 umtx_time32 +#endif +#else /* !__i386__ && !__amd64__ */ +/* 32-bit architectures can emulate i386, so define these almost everywhere. */ +struct timespeci386 { + int32_t tv_sec; + int32_t tv_nsec; +}; - t32._clockid = CLOCK_REALTIME; - t32._flags = 0; - if (size <= sizeof(t32._timeout)) - error = copyin(uaddr, &t32._timeout, sizeof(t32._timeout)); - else - error = copyin(uaddr, &t32, sizeof(t32)); - if (error != 0) - return (error); - if (t32._timeout.tv_sec < 0 || - t32._timeout.tv_nsec >= 1000000000 || t32._timeout.tv_nsec < 0) - return (EINVAL); - TS_CP(t32, *tp, _timeout); - CP(t32, *tp, _flags); - CP(t32, *tp, _clockid); - return (0); -} +struct umtx_timei386 { + struct timespeci386 _timeout; + uint32_t _flags; + uint32_t _clockid; +}; + +#if defined(__LP64__) +#define timespecx32 timespec32 +#define umtx_timex32 umtx_time32 +#endif +#endif static int umtx_copyin_robust_lists32(const void *uaddr, size_t size, @@ -4246,10 +4247,54 @@ umtx_copyin_robust_lists32(const void *uaddr, size_t size, return (0); } -static int -umtx_copyout_timeout32(void *uaddr, size_t sz, struct timespec *tsp) +#ifndef __i386__ +static inline int +umtx_copyin_timeouti386(const void *uaddr, struct timespec *tsp) { - struct timespec32 remain32 = { + struct timespeci386 ts32; + int error; + + error = copyin(uaddr, &ts32, sizeof(ts32)); + if (error == 0) { + if (ts32.tv_sec < 0 || + ts32.tv_nsec >= 1000000000 || + ts32.tv_nsec < 0) + error = EINVAL; + else { + CP(ts32, *tsp, tv_sec); + CP(ts32, *tsp, tv_nsec); + } + } + return (error); +} + +static inline int +umtx_copyin_umtx_timei386(const void *uaddr, size_t size, struct _umtx_time *tp) +{ + struct umtx_timei386 t32; + int error; + + t32._clockid = CLOCK_REALTIME; + t32._flags = 0; + if (size <= sizeof(t32._timeout)) + error = copyin(uaddr, &t32._timeout, sizeof(t32._timeout)); + else + error = copyin(uaddr, &t32, sizeof(t32)); + if (error != 0) + return (error); + if (t32._timeout.tv_sec < 0 || + t32._timeout.tv_nsec >= 1000000000 || t32._timeout.tv_nsec < 0) + return (EINVAL); + TS_CP(t32, *tp, _timeout); + CP(t32, *tp, _flags); + CP(t32, *tp, _clockid); + return (0); +} + +static int +umtx_copyout_timeouti386(void *uaddr, size_t sz, struct timespec *tsp) +{ + struct timespeci386 remain32 = { .tv_sec = tsp->tv_sec, .tv_nsec = tsp->tv_nsec, }; @@ -4264,7 +4309,71 @@ umtx_copyout_timeout32(void *uaddr, size_t sz, struct timespec *tsp) return (copyout(&remain32, uaddr, sizeof(remain32))); } -#endif /* COMPAT_FREEBSD32 */ +#endif /* !__i386__ */ + +#if defined(__i386__) || defined(__LP64__) +static inline int +umtx_copyin_timeoutx32(const void *uaddr, struct timespec *tsp) +{ + struct timespecx32 ts32; + int error; + + error = copyin(uaddr, &ts32, sizeof(ts32)); + if (error == 0) { + if (ts32.tv_sec < 0 || + ts32.tv_nsec >= 1000000000 || + ts32.tv_nsec < 0) + error = EINVAL; + else { + CP(ts32, *tsp, tv_sec); + CP(ts32, *tsp, tv_nsec); + } + } + return (error); +} + +static inline int +umtx_copyin_umtx_timex32(const void *uaddr, size_t size, struct _umtx_time *tp) +{ + struct umtx_timex32 t32; + int error; + + t32._clockid = CLOCK_REALTIME; + t32._flags = 0; + if (size <= sizeof(t32._timeout)) + error = copyin(uaddr, &t32._timeout, sizeof(t32._timeout)); + else + error = copyin(uaddr, &t32, sizeof(t32)); + if (error != 0) + return (error); + if (t32._timeout.tv_sec < 0 || + t32._timeout.tv_nsec >= 1000000000 || t32._timeout.tv_nsec < 0) + return (EINVAL); + TS_CP(t32, *tp, _timeout); + CP(t32, *tp, _flags); + CP(t32, *tp, _clockid); + return (0); +} + +static int +umtx_copyout_timeoutx32(void *uaddr, size_t sz, struct timespec *tsp) +{ + struct timespecx32 remain32 = { + .tv_sec = tsp->tv_sec, + .tv_nsec = tsp->tv_nsec, + }; + + /* + * Should be guaranteed by the caller, sz == uaddr1 - sizeof(_umtx_time) + * and we're only called if sz >= sizeof(timespec) as supplied in the + * copyops. + */ + KASSERT(sz >= sizeof(remain32), + ("umtx_copyops specifies incorrect sizes")); + + return (copyout(&remain32, uaddr, sizeof(remain32))); +} +#endif /* __i386__ || __LP64__ */ typedef int (*_umtx_op_func)(struct thread *td, struct _umtx_op_args *uap, const struct umtx_copyops *umtx_ops); @@ -4313,25 +4422,48 @@ static const struct umtx_copyops umtx_native_ops = { .umtx_time_sz = sizeof(struct _umtx_time), }; -#ifdef COMPAT_FREEBSD32 -const struct umtx_copyops umtx_native_ops32 = { - .copyin_timeout = umtx_copyin_timeout32, - .copyin_umtx_time = umtx_copyin_umtx_time32, +#ifndef __i386__ +static const struct umtx_copyops umtx_native_opsi386 = { + .copyin_timeout = umtx_copyin_timeouti386, + .copyin_umtx_time = umtx_copyin_umtx_timei386, .copyin_robust_lists = umtx_copyin_robust_lists32, - .copyout_timeout = umtx_copyout_timeout32, - .timespec_sz = sizeof(struct timespec32), - .umtx_time_sz = sizeof(struct umtx_time32), + .copyout_timeout = umtx_copyout_timeouti386, + .timespec_sz = sizeof(struct timespeci386), + .umtx_time_sz = sizeof(struct umtx_timei386), .compat32 = true, }; #endif +#if defined(__i386__) || defined(__LP64__) +/* i386 can emulate other 32-bit archs, too! */ +static const struct umtx_copyops umtx_native_opsx32 = { + .copyin_timeout = umtx_copyin_timeoutx32, + .copyin_umtx_time = umtx_copyin_umtx_timex32, + .copyin_robust_lists = umtx_copyin_robust_lists32, + .copyout_timeout = umtx_copyout_timeoutx32, + .timespec_sz = sizeof(struct timespecx32), + .umtx_time_sz = sizeof(struct umtx_timex32), + .compat32 = true, +}; + +#ifdef COMPAT_FREEBSD32 +#ifdef __amd64__ +#define umtx_native_ops32 umtx_native_opsi386 +#else +#define umtx_native_ops32 umtx_native_opsx32 +#endif +#endif /* COMPAT_FREEBSD32 */ +#endif /* __i386__ || __LP64__ */ + +#define UMTX_OP__FLAGS (UMTX_OP__32BIT | UMTX_OP__I386) + static int kern__umtx_op(struct thread *td, void *obj, int op, unsigned long val, void *uaddr1, void *uaddr2, const struct umtx_copyops *ops) { struct _umtx_op_args uap = { .obj = obj, - .op = op, + .op = op & ~UMTX_OP__FLAGS, .val = val, .uaddr1 = uaddr1, .uaddr2 = uaddr2 @@ -4345,9 +4477,27 @@ kern__umtx_op(struct thread *td, void *obj, int op, unsigned long val, int sys__umtx_op(struct thread *td, struct _umtx_op_args *uap) { + static const struct umtx_copyops *umtx_ops; + umtx_ops = &umtx_native_ops; +#ifdef __LP64__ + if ((uap->op & (UMTX_OP__32BIT | UMTX_OP__I386)) != 0) { + if ((uap->op & UMTX_OP__I386) != 0) + umtx_ops = &umtx_native_opsi386; + else + umtx_ops = &umtx_native_opsx32; + } +#elif !defined(__i386__) + /* We consider UMTX_OP__32BIT a nop on !i386 ILP32. */ + if ((uap->op & UMTX_OP__I386) != 0) + umtx_ops = &umtx_native_opsi386; +#else + /* Likewise, UMTX_OP__I386 is a nop on i386. */ + if ((uap->op & UMTX_OP__32BIT) != 0) + umtx_ops = &umtx_native_opsx32; +#endif return (kern__umtx_op(td, uap->obj, uap->op, uap->val, uap->uaddr1, - uap->uaddr2, &umtx_native_ops)); + uap->uaddr2, umtx_ops)); } #ifdef COMPAT_FREEBSD32 @@ -4435,19 +4585,14 @@ static int umtx_read_uptr(struct thread *td, uintptr_t ptr, uintptr_t *res, bool compat32) { u_long res1; -#ifdef COMPAT_FREEBSD32 uint32_t res32; -#endif int error; -#ifdef COMPAT_FREEBSD32 if (compat32) { error = fueword32((void *)ptr, &res32); if (error == 0) res1 = res32; - } else -#endif - { + } else { error = fueword((void *)ptr, &res1); } if (error == 0) @@ -4461,15 +4606,14 @@ static void umtx_read_rb_list(struct thread *td, struct umutex *m, uintptr_t *rb_list, bool compat32) { -#ifdef COMPAT_FREEBSD32 struct umutex32 m32; if (compat32) { memcpy(&m32, m, sizeof(m32)); *rb_list = m32.m_rb_lnk; - } else -#endif + } else { *rb_list = m->m_rb_lnk; + } } static int diff --git a/sys/sys/umtx.h b/sys/sys/umtx.h index f0a34e0aed67..6753a6217688 100644 --- a/sys/sys/umtx.h +++ b/sys/sys/umtx.h @@ -101,6 +101,13 @@ #define UMTX_OP_SHM 25 #define UMTX_OP_ROBUST_LISTS 26 +/* + * Flags for ops; the double-underbar convention must be maintained for future + * additions for the sake of libsysdecode. + */ +#define UMTX_OP__I386 0x40000000 +#define UMTX_OP__32BIT 0x80000000 + /* Flags for UMTX_OP_CV_WAIT */ #define CVWAIT_CHECK_UNPARKING 0x01 #define CVWAIT_ABSTIME 0x02