/* * Copyright (C) 2014 Philippe Gerum * * Xenomai is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Xenomai is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include int sys32_get_timespec(struct timespec64 *ts, const struct old_timespec32 __user *u_cts) { struct old_timespec32 cts; if (u_cts == NULL || !access_rok(u_cts, sizeof(*u_cts))) return -EFAULT; if (__xn_get_user(cts.tv_sec, &u_cts->tv_sec) || __xn_get_user(cts.tv_nsec, &u_cts->tv_nsec)) return -EFAULT; ts->tv_sec = cts.tv_sec; ts->tv_nsec = cts.tv_nsec; return 0; } EXPORT_SYMBOL_GPL(sys32_get_timespec); int sys32_put_timespec(struct old_timespec32 __user *u_cts, const struct timespec64 *ts) { struct old_timespec32 cts; if (u_cts == NULL || !access_wok(u_cts, sizeof(*u_cts))) return -EFAULT; cts.tv_sec = ts->tv_sec; cts.tv_nsec = ts->tv_nsec; if (__xn_put_user(cts.tv_sec, &u_cts->tv_sec) || __xn_put_user(cts.tv_nsec, &u_cts->tv_nsec)) return -EFAULT; return 0; } EXPORT_SYMBOL_GPL(sys32_put_timespec); int sys32_get_itimerspec(struct itimerspec64 *its, const struct old_itimerspec32 __user *cits) { int ret = sys32_get_timespec(&its->it_value, &cits->it_value); return ret ?: sys32_get_timespec(&its->it_interval, &cits->it_interval); } EXPORT_SYMBOL_GPL(sys32_get_itimerspec); int sys32_put_itimerspec(struct old_itimerspec32 __user *cits, const struct itimerspec64 *its) { int ret = sys32_put_timespec(&cits->it_value, &its->it_value); return ret ?: sys32_put_timespec(&cits->it_interval, &its->it_interval); } EXPORT_SYMBOL_GPL(sys32_put_itimerspec); int sys32_get_timeval(struct __kernel_old_timeval *tv, const struct old_timeval32 __user *ctv) { return (ctv == NULL || !access_rok(ctv, sizeof(*ctv)) || __xn_get_user(tv->tv_sec, &ctv->tv_sec) || __xn_get_user(tv->tv_usec, &ctv->tv_usec)) ? -EFAULT : 0; } EXPORT_SYMBOL_GPL(sys32_get_timeval); int sys32_put_timeval(struct old_timeval32 __user *ctv, const struct __kernel_old_timeval *tv) { return (ctv == NULL || !access_wok(ctv, sizeof(*ctv)) || __xn_put_user(tv->tv_sec, &ctv->tv_sec) || __xn_put_user(tv->tv_usec, &ctv->tv_usec)) ? -EFAULT : 0; } EXPORT_SYMBOL_GPL(sys32_put_timeval); int sys32_get_timex(struct __kernel_timex *tx, const struct old_timex32 __user *ctx) { struct __kernel_old_timeval time; int ret; memset(tx, 0, sizeof(*tx)); ret = sys32_get_timeval(&time, &ctx->time); if (ret) return ret; tx->time.tv_sec = time.tv_sec; tx->time.tv_usec = time.tv_usec; if (!access_rok(ctx, sizeof(*ctx)) || __xn_get_user(tx->modes, &ctx->modes) || __xn_get_user(tx->offset, &ctx->offset) || __xn_get_user(tx->freq, &ctx->freq) || __xn_get_user(tx->maxerror, &ctx->maxerror) || __xn_get_user(tx->esterror, &ctx->esterror) || __xn_get_user(tx->status, &ctx->status) || __xn_get_user(tx->constant, &ctx->constant) || __xn_get_user(tx->precision, &ctx->precision) || __xn_get_user(tx->tolerance, &ctx->tolerance) || __xn_get_user(tx->tick, &ctx->tick) || __xn_get_user(tx->ppsfreq, &ctx->ppsfreq) || __xn_get_user(tx->jitter, &ctx->jitter) || __xn_get_user(tx->shift, &ctx->shift) || __xn_get_user(tx->stabil, &ctx->stabil) || __xn_get_user(tx->jitcnt, &ctx->jitcnt) || __xn_get_user(tx->calcnt, &ctx->calcnt) || __xn_get_user(tx->errcnt, &ctx->errcnt) || __xn_get_user(tx->stbcnt, &ctx->stbcnt)) return -EFAULT; return 0; } EXPORT_SYMBOL_GPL(sys32_get_timex); int sys32_put_timex(struct old_timex32 __user *ctx, const struct __kernel_timex *tx) { struct __kernel_old_timeval time; int ret; time.tv_sec = tx->time.tv_sec; time.tv_usec = tx->time.tv_usec; ret = sys32_put_timeval(&ctx->time, &time); if (ret) return ret; if (!access_wok(ctx, sizeof(*ctx)) || __xn_put_user(tx->modes, &ctx->modes) || __xn_put_user(tx->offset, &ctx->offset) || __xn_put_user(tx->freq, &ctx->freq) || __xn_put_user(tx->maxerror, &ctx->maxerror) || __xn_put_user(tx->esterror, &ctx->esterror) || __xn_put_user(tx->status, &ctx->status) || __xn_put_user(tx->constant, &ctx->constant) || __xn_put_user(tx->precision, &ctx->precision) || __xn_put_user(tx->tolerance, &ctx->tolerance) || __xn_put_user(tx->tick, &ctx->tick) || __xn_put_user(tx->ppsfreq, &ctx->ppsfreq) || __xn_put_user(tx->jitter, &ctx->jitter) || __xn_put_user(tx->shift, &ctx->shift) || __xn_put_user(tx->stabil, &ctx->stabil) || __xn_put_user(tx->jitcnt, &ctx->jitcnt) || __xn_put_user(tx->calcnt, &ctx->calcnt) || __xn_put_user(tx->errcnt, &ctx->errcnt) || __xn_put_user(tx->stbcnt, &ctx->stbcnt)) return -EFAULT; return 0; } EXPORT_SYMBOL_GPL(sys32_put_timex); int sys32_get_fdset(fd_set *fds, const compat_fd_set __user *cfds, size_t cfdsize) { int rdpos, wrpos, rdlim = cfdsize / sizeof(compat_ulong_t); if (cfds == NULL || !access_rok(cfds, cfdsize)) return -EFAULT; for (rdpos = 0, wrpos = 0; rdpos < rdlim; rdpos++, wrpos++) if (__xn_get_user(fds->fds_bits[wrpos], cfds->fds_bits + rdpos)) return -EFAULT; return 0; } int sys32_put_fdset(compat_fd_set __user *cfds, const fd_set *fds, size_t fdsize) { int rdpos, wrpos, wrlim = fdsize / sizeof(long); if (cfds == NULL || !access_wok(cfds, wrlim * sizeof(compat_ulong_t))) return -EFAULT; for (rdpos = 0, wrpos = 0; wrpos < wrlim; rdpos++, wrpos++) if (__xn_put_user(fds->fds_bits[rdpos], cfds->fds_bits + wrpos)) return -EFAULT; return 0; } int sys32_get_param_ex(int policy, struct sched_param_ex *p, const struct compat_sched_param_ex __user *u_cp) { struct compat_sched_param_ex cpex; if (u_cp == NULL || cobalt_copy_from_user(&cpex, u_cp, sizeof(cpex))) return -EFAULT; p->sched_priority = cpex.sched_priority; switch (policy) { case SCHED_SPORADIC: p->sched_ss_low_priority = cpex.sched_ss_low_priority; p->sched_ss_max_repl = cpex.sched_ss_max_repl; p->sched_ss_repl_period.tv_sec = cpex.sched_ss_repl_period.tv_sec; p->sched_ss_repl_period.tv_nsec = cpex.sched_ss_repl_period.tv_nsec; p->sched_ss_init_budget.tv_sec = cpex.sched_ss_init_budget.tv_sec; p->sched_ss_init_budget.tv_nsec = cpex.sched_ss_init_budget.tv_nsec; break; case SCHED_RR: p->sched_rr_quantum.tv_sec = cpex.sched_rr_quantum.tv_sec; p->sched_rr_quantum.tv_nsec = cpex.sched_rr_quantum.tv_nsec; break; case SCHED_TP: p->sched_tp_partition = cpex.sched_tp_partition; break; case SCHED_QUOTA: p->sched_quota_group = cpex.sched_quota_group; break; } return 0; } EXPORT_SYMBOL_GPL(sys32_get_param_ex); int sys32_put_param_ex(int policy, struct compat_sched_param_ex __user *u_cp, const struct sched_param_ex *p) { struct compat_sched_param_ex cpex; if (u_cp == NULL) return -EFAULT; cpex.sched_priority = p->sched_priority; switch (policy) { case SCHED_SPORADIC: cpex.sched_ss_low_priority = p->sched_ss_low_priority; cpex.sched_ss_max_repl = p->sched_ss_max_repl; cpex.sched_ss_repl_period.tv_sec = p->sched_ss_repl_period.tv_sec; cpex.sched_ss_repl_period.tv_nsec = p->sched_ss_repl_period.tv_nsec; cpex.sched_ss_init_budget.tv_sec = p->sched_ss_init_budget.tv_sec; cpex.sched_ss_init_budget.tv_nsec = p->sched_ss_init_budget.tv_nsec; break; case SCHED_RR: cpex.sched_rr_quantum.tv_sec = p->sched_rr_quantum.tv_sec; cpex.sched_rr_quantum.tv_nsec = p->sched_rr_quantum.tv_nsec; break; case SCHED_TP: cpex.sched_tp_partition = p->sched_tp_partition; break; case SCHED_QUOTA: cpex.sched_quota_group = p->sched_quota_group; break; } return cobalt_copy_to_user(u_cp, &cpex, sizeof(cpex)); } EXPORT_SYMBOL_GPL(sys32_put_param_ex); int sys32_get_mqattr(struct mq_attr *ap, const struct compat_mq_attr __user *u_cap) { struct compat_mq_attr cattr; if (u_cap == NULL || cobalt_copy_from_user(&cattr, u_cap, sizeof(cattr))) return -EFAULT; ap->mq_flags = cattr.mq_flags; ap->mq_maxmsg = cattr.mq_maxmsg; ap->mq_msgsize = cattr.mq_msgsize; ap->mq_curmsgs = cattr.mq_curmsgs; return 0; } EXPORT_SYMBOL_GPL(sys32_get_mqattr); int sys32_put_mqattr(struct compat_mq_attr __user *u_cap, const struct mq_attr *ap) { struct compat_mq_attr cattr; cattr.mq_flags = ap->mq_flags; cattr.mq_maxmsg = ap->mq_maxmsg; cattr.mq_msgsize = ap->mq_msgsize; cattr.mq_curmsgs = ap->mq_curmsgs; return u_cap == NULL ? -EFAULT : cobalt_copy_to_user(u_cap, &cattr, sizeof(cattr)); } EXPORT_SYMBOL_GPL(sys32_put_mqattr); int sys32_get_sigevent(struct sigevent *ev, const struct compat_sigevent *__user u_cev) { struct compat_sigevent cev; compat_int_t *cp; int ret, *p; if (u_cev == NULL) return -EFAULT; ret = cobalt_copy_from_user(&cev, u_cev, sizeof(cev)); if (ret) return ret; memset(ev, 0, sizeof(*ev)); ev->sigev_value.sival_ptr = compat_ptr(cev.sigev_value.sival_ptr); ev->sigev_signo = cev.sigev_signo; ev->sigev_notify = cev.sigev_notify; /* * Extensions may define extra fields we don't know about in * the padding area, so we have to load it entirely. */ p = ev->_sigev_un._pad; cp = cev._sigev_un._pad; while (p < &ev->_sigev_un._pad[ARRAY_SIZE(ev->_sigev_un._pad)] && cp < &cev._sigev_un._pad[ARRAY_SIZE(cev._sigev_un._pad)]) *p++ = *cp++; return 0; } EXPORT_SYMBOL_GPL(sys32_get_sigevent); int sys32_get_sigset(sigset_t *set, const compat_sigset_t *u_cset) { #ifdef __BIG_ENDIAN compat_sigset_t v; if (cobalt_copy_from_user(&v, u_cset, sizeof(compat_sigset_t))) return -EFAULT; switch (_NSIG_WORDS) { case 4: set->sig[3] = v.sig[6] | (((long)v.sig[7]) << 32 ); case 3: set->sig[2] = v.sig[4] | (((long)v.sig[5]) << 32 ); case 2: set->sig[1] = v.sig[2] | (((long)v.sig[3]) << 32 ); case 1: set->sig[0] = v.sig[0] | (((long)v.sig[1]) << 32 ); } #else if (cobalt_copy_from_user(set, u_cset, sizeof(compat_sigset_t))) return -EFAULT; #endif return 0; } EXPORT_SYMBOL_GPL(sys32_get_sigset); int sys32_put_sigset(compat_sigset_t *u_cset, const sigset_t *set) { #ifdef __BIG_ENDIAN compat_sigset_t v; switch (_NSIG_WORDS) { case 4: v.sig[7] = (set->sig[3] >> 32); v.sig[6] = set->sig[3]; case 3: v.sig[5] = (set->sig[2] >> 32); v.sig[4] = set->sig[2]; case 2: v.sig[3] = (set->sig[1] >> 32); v.sig[2] = set->sig[1]; case 1: v.sig[1] = (set->sig[0] >> 32); v.sig[0] = set->sig[0]; } return cobalt_copy_to_user(u_cset, &v, sizeof(*u_cset)) ? -EFAULT : 0; #else return cobalt_copy_to_user(u_cset, set, sizeof(*u_cset)) ? -EFAULT : 0; #endif } EXPORT_SYMBOL_GPL(sys32_put_sigset); int sys32_get_sigval(union sigval *val, const union compat_sigval *u_cval) { union compat_sigval cval; int ret; if (u_cval == NULL) return -EFAULT; ret = cobalt_copy_from_user(&cval, u_cval, sizeof(cval)); if (ret) return ret; val->sival_ptr = compat_ptr(cval.sival_ptr); return 0; } EXPORT_SYMBOL_GPL(sys32_get_sigval); int sys32_put_siginfo(void __user *u_si, const struct siginfo *si, int overrun) { struct compat_siginfo __user *u_p = u_si; int ret; if (u_p == NULL) return -EFAULT; ret = __xn_put_user(si->si_signo, &u_p->si_signo); ret |= __xn_put_user(si->si_errno, &u_p->si_errno); ret |= __xn_put_user(si->si_code, &u_p->si_code); /* * Copy the generic/standard siginfo bits to userland. */ switch (si->si_code) { case SI_TIMER: ret |= __xn_put_user(si->si_tid, &u_p->si_tid); ret |= __xn_put_user(ptr_to_compat(si->si_ptr), &u_p->si_ptr); ret |= __xn_put_user(overrun, &u_p->si_overrun); break; case SI_QUEUE: case SI_MESGQ: ret |= __xn_put_user(ptr_to_compat(si->si_ptr), &u_p->si_ptr); fallthrough; case SI_USER: ret |= __xn_put_user(si->si_pid, &u_p->si_pid); ret |= __xn_put_user(si->si_uid, &u_p->si_uid); } return ret; } EXPORT_SYMBOL_GPL(sys32_put_siginfo); int sys32_get_msghdr(struct user_msghdr *msg, const struct compat_msghdr __user *u_cmsg) { compat_uptr_t tmp1, tmp2, tmp3; if (u_cmsg == NULL || !access_rok(u_cmsg, sizeof(*u_cmsg)) || __xn_get_user(tmp1, &u_cmsg->msg_name) || __xn_get_user(msg->msg_namelen, &u_cmsg->msg_namelen) || __xn_get_user(tmp2, &u_cmsg->msg_iov) || __xn_get_user(msg->msg_iovlen, &u_cmsg->msg_iovlen) || __xn_get_user(tmp3, &u_cmsg->msg_control) || __xn_get_user(msg->msg_controllen, &u_cmsg->msg_controllen) || __xn_get_user(msg->msg_flags, &u_cmsg->msg_flags)) return -EFAULT; if (msg->msg_namelen > sizeof(struct sockaddr_storage)) msg->msg_namelen = sizeof(struct sockaddr_storage); msg->msg_name = compat_ptr(tmp1); msg->msg_iov = compat_ptr(tmp2); msg->msg_control = compat_ptr(tmp3); return 0; } EXPORT_SYMBOL_GPL(sys32_get_msghdr); int sys32_get_mmsghdr(struct mmsghdr *mmsg, const struct compat_mmsghdr __user *u_cmmsg) { if (u_cmmsg == NULL || !access_rok(u_cmmsg, sizeof(*u_cmmsg)) || __xn_get_user(mmsg->msg_len, &u_cmmsg->msg_len)) return -EFAULT; return sys32_get_msghdr(&mmsg->msg_hdr, &u_cmmsg->msg_hdr); } EXPORT_SYMBOL_GPL(sys32_get_mmsghdr); int sys32_put_msghdr(struct compat_msghdr __user *u_cmsg, const struct user_msghdr *msg) { if (u_cmsg == NULL || !access_wok(u_cmsg, sizeof(*u_cmsg)) || __xn_put_user(ptr_to_compat(msg->msg_name), &u_cmsg->msg_name) || __xn_put_user(msg->msg_namelen, &u_cmsg->msg_namelen) || __xn_put_user(ptr_to_compat(msg->msg_iov), &u_cmsg->msg_iov) || __xn_put_user(msg->msg_iovlen, &u_cmsg->msg_iovlen) || __xn_put_user(ptr_to_compat(msg->msg_control), &u_cmsg->msg_control) || __xn_put_user(msg->msg_controllen, &u_cmsg->msg_controllen) || __xn_put_user(msg->msg_flags, &u_cmsg->msg_flags)) return -EFAULT; return 0; } EXPORT_SYMBOL_GPL(sys32_put_msghdr); int sys32_put_mmsghdr(struct compat_mmsghdr __user *u_cmmsg, const struct mmsghdr *mmsg) { if (u_cmmsg == NULL || !access_wok(u_cmmsg, sizeof(*u_cmmsg)) || __xn_put_user(mmsg->msg_len, &u_cmmsg->msg_len)) return -EFAULT; return sys32_put_msghdr(&u_cmmsg->msg_hdr, &mmsg->msg_hdr); } EXPORT_SYMBOL_GPL(sys32_put_mmsghdr); int sys32_get_iovec(struct iovec *iov, const struct compat_iovec __user *u_ciov, int ciovlen) { const struct compat_iovec __user *p; struct compat_iovec ciov; int ret, n; for (n = 0, p = u_ciov; n < ciovlen; n++, p++) { ret = cobalt_copy_from_user(&ciov, p, sizeof(ciov)); if (ret) return ret; iov[n].iov_base = compat_ptr(ciov.iov_base); iov[n].iov_len = ciov.iov_len; } return 0; } EXPORT_SYMBOL_GPL(sys32_get_iovec); int sys32_put_iovec(struct compat_iovec __user *u_ciov, const struct iovec *iov, int iovlen) { struct compat_iovec __user *p; struct compat_iovec ciov; int ret, n; for (n = 0, p = u_ciov; n < iovlen; n++, p++) { ciov.iov_base = ptr_to_compat(iov[n].iov_base); ciov.iov_len = iov[n].iov_len; ret = cobalt_copy_to_user(p, &ciov, sizeof(*p)); if (ret) return ret; } return 0; } EXPORT_SYMBOL_GPL(sys32_put_iovec);