Commit d47a4cdd authored by Florian Bezdeka's avatar Florian Bezdeka Committed by Jan Kiszka

cobalt/sem: y2038: Fixing the sem_timedwait syscall for 32 bit systems

On systems using 32 bit for time_t the sem_timedwait syscall was broken
because the function used for copying the timeout value from userspace
to kernel (=sem_fetch_timeout()) was always copying
sizeof(struct timespec64).

A 32 bit application (or more specific an application with 4 byte
time_t) would only provide sizeof(struct old_timespec32).

Notable changes:
  - The copy operation from userspace to kernel is now already done in
    the syscall handler. So it is always done and might fail. Reporting
    a failure is delayed until the timeout is being validated.

  - Validation: Switched to timespec64_valid() instead of our own
    check.
Signed-off-by: default avatarFlorian Bezdeka <florian.bezdeka@siemens.com>
Signed-off-by: Jan Kiszka's avatarJan Kiszka <jan.kiszka@siemens.com>
parent 2f39970a
......@@ -267,20 +267,11 @@ out:
return ret;
}
static inline int sem_fetch_timeout(struct timespec64 *ts,
const void __user *u_ts)
{
return u_ts == NULL ? -EFAULT :
cobalt_copy_from_user(ts, u_ts, sizeof(*ts));
}
int __cobalt_sem_timedwait(struct cobalt_sem_shadow __user *u_sem,
const void __user *u_ts,
int (*fetch_timeout)(struct timespec64 *ts,
const void __user *u_ts))
const struct timespec64 *ts)
{
struct timespec64 ts = { .tv_sec = 0, .tv_nsec = 0 };
int pull_ts = 1, ret, info;
int ret, info;
bool validate_ts = true;
struct cobalt_sem *sem;
xnhandle_t handle;
xntmode_t tmode;
......@@ -304,24 +295,23 @@ int __cobalt_sem_timedwait(struct cobalt_sem_shadow __user *u_sem,
* it's actually more complex, to keep some
* applications ported to Linux happy.
*/
if (pull_ts) {
if (validate_ts) {
atomic_inc(&sem->state->value);
xnlock_put_irqrestore(&nklock, s);
ret = fetch_timeout(&ts, u_ts);
xnlock_get_irqsave(&nklock, s);
if (ret)
if (!ts) {
ret = -EFAULT;
break;
if (ts.tv_nsec >= ONE_BILLION) {
}
if (!timespec64_valid(ts)) {
ret = -EINVAL;
break;
}
pull_ts = 0;
validate_ts = false;
continue;
}
ret = 0;
tmode = sem->flags & SEM_RAWCLOCK ? XN_ABSOLUTE : XN_REALTIME;
info = xnsynch_sleep_on(&sem->synchbase, ts2ns(&ts) + 1, tmode);
info = xnsynch_sleep_on(&sem->synchbase, ts2ns(ts) + 1, tmode);
if (info & XNRMID)
ret = -EINVAL;
else if (info & (XNBREAK|XNTIMEO)) {
......@@ -434,9 +424,15 @@ COBALT_SYSCALL(sem_wait, primary,
COBALT_SYSCALL(sem_timedwait, primary,
(struct cobalt_sem_shadow __user *u_sem,
struct __user_old_timespec __user *u_ts))
const struct __user_old_timespec __user *u_ts))
{
return __cobalt_sem_timedwait(u_sem, u_ts, sem_fetch_timeout);
int ret = 1;
struct timespec64 ts64;
if (u_ts)
ret = cobalt_get_u_timespec(&ts64, u_ts);
return __cobalt_sem_timedwait(u_sem, ret ? NULL : &ts64);
}
COBALT_SYSCALL(sem_trywait, primary,
......
......@@ -64,9 +64,7 @@ __cobalt_sem_open(struct cobalt_sem_shadow __user *usm,
int oflags, mode_t mode, unsigned int value);
int __cobalt_sem_timedwait(struct cobalt_sem_shadow __user *u_sem,
const void __user *u_ts,
int (*fetch_timeout)(struct timespec64 *ts,
const void __user *u_ts));
const struct timespec64 *ts);
int __cobalt_sem_destroy(xnhandle_t handle);
......@@ -91,7 +89,7 @@ COBALT_SYSCALL_DECL(sem_wait,
COBALT_SYSCALL_DECL(sem_timedwait,
(struct cobalt_sem_shadow __user *u_sem,
struct __user_old_timespec __user *u_ts));
const struct __user_old_timespec __user *u_ts));
COBALT_SYSCALL_DECL(sem_trywait,
(struct cobalt_sem_shadow __user *u_sem));
......
......@@ -124,9 +124,15 @@ COBALT_SYSCALL32emu(sem_open, lostage,
COBALT_SYSCALL32emu(sem_timedwait, primary,
(struct cobalt_sem_shadow __user *u_sem,
struct old_timespec32 __user *u_ts))
const struct old_timespec32 __user *u_ts))
{
return __cobalt_sem_timedwait(u_sem, u_ts, sys32_fetch_timeout);
int ret = 1;
struct timespec64 ts64;
if (u_ts)
ret = sys32_fetch_timeout(&ts64, u_ts);
return __cobalt_sem_timedwait(u_sem, ret ? NULL : &ts64);
}
COBALT_SYSCALL32emu(clock_getres, current,
......
......@@ -229,6 +229,6 @@ COBALT_SYSCALL32emu_DECL(sem_open,
COBALT_SYSCALL32emu_DECL(sem_timedwait,
(struct cobalt_sem_shadow __user *u_sem,
struct old_timespec32 __user *u_ts));
const struct old_timespec32 __user *u_ts));
#endif /* !_COBALT_POSIX_SYSCALL32_H */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment