diff options
Diffstat (limited to 'sys/compat')
| -rw-r--r-- | sys/compat/freebsd32/freebsd32_misc.c | 2 | ||||
| -rw-r--r-- | sys/compat/lindebugfs/lindebugfs.c | 83 | ||||
| -rw-r--r-- | sys/compat/linux/linux_misc.c | 1 | ||||
| -rw-r--r-- | sys/compat/linux/linux_socket.c | 61 | ||||
| -rw-r--r-- | sys/compat/linux/linux_socket.h | 1 | ||||
| -rw-r--r-- | sys/compat/linuxkpi/common/include/linux/fs.h | 34 | ||||
| -rw-r--r-- | sys/compat/linuxkpi/common/include/linux/hardirq.h | 1 | ||||
| -rw-r--r-- | sys/compat/linuxkpi/common/include/linux/highmem.h | 1 | ||||
| -rw-r--r-- | sys/compat/linuxkpi/common/include/linux/seq_file.h | 2 | ||||
| -rw-r--r-- | sys/compat/linuxkpi/common/include/linux/spinlock.h | 1 | ||||
| -rw-r--r-- | sys/compat/linuxkpi/common/include/linux/sysfs.h | 34 | ||||
| -rw-r--r-- | sys/compat/linuxkpi/common/src/linux_80211.c | 26 | ||||
| -rw-r--r-- | sys/compat/linuxkpi/common/src/linux_seq_file.c | 14 | ||||
| -rw-r--r-- | sys/compat/linuxkpi/common/src/linux_simple_attr.c | 48 |
14 files changed, 212 insertions, 97 deletions
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index 1064987c3abf..4ec6dd452b32 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -757,7 +757,7 @@ static int freebsd32_kevent_copyout(void *arg, struct kevent *kevp, int count) { struct freebsd32_kevent_args *uap; - struct kevent32 ks32[KQ_NEVENTS]; + struct kevent32 ks32[KQ_NEVENTS] = {}; int i, error; KASSERT(count <= KQ_NEVENTS, ("count (%d) > KQ_NEVENTS", count)); diff --git a/sys/compat/lindebugfs/lindebugfs.c b/sys/compat/lindebugfs/lindebugfs.c index 857546f61e55..88b92afd374a 100644 --- a/sys/compat/lindebugfs/lindebugfs.c +++ b/sys/compat/lindebugfs/lindebugfs.c @@ -117,9 +117,14 @@ debugfs_fill(PFS_FILL_ARGS) struct dentry_meta *d; struct linux_file lf = {}; struct vnode vn; - char *buf; - int rc; - off_t off = 0; + struct iovec *iov; + size_t cnt, orig_resid; + ssize_t rc; + off_t off; + + /* Linux file operations assume a pointer to a user buffer. */ + if (uio->uio_segflg != UIO_USERSPACE) + return (EOPNOTSUPP); if ((rc = linux_set_current_flags(curthread, M_NOWAIT))) return (rc); @@ -130,42 +135,64 @@ debugfs_fill(PFS_FILL_ARGS) rc = d->dm_fops->open(&vn, &lf); if (rc < 0) { #ifdef INVARIANTS - printf("%s:%d open failed with %d\n", __func__, __LINE__, rc); + printf("%s:%d open failed with %zd\n", __func__, __LINE__, rc); #endif return (-rc); } - rc = -ENODEV; - switch (uio->uio_rw) { - case UIO_READ: - if (d->dm_fops->read != NULL) { - rc = -ENOMEM; - buf = malloc(sb->s_size, M_DFSINT, M_ZERO | M_NOWAIT); - if (buf != NULL) { - rc = d->dm_fops->read(&lf, buf, sb->s_size, - &off); - if (rc > 0) - sbuf_bcpy(sb, buf, strlen(buf)); - - free(buf, M_DFSINT); - } + off = uio->uio_offset; + orig_resid = uio->uio_resid; + while (uio->uio_resid > 0) { + KASSERT(uio->uio_iovcnt > 0, + ("%s: uio %p iovcnt underflow", __func__, uio)); + + iov = uio->uio_iov; + cnt = iov->iov_len; + if (cnt == 0) { + uio->uio_iov++; + uio->uio_iovcnt--; + continue; } - break; - case UIO_WRITE: - if (d->dm_fops->write != NULL) { - sbuf_finish(sb); - rc = d->dm_fops->write(&lf, sbuf_data(sb), sbuf_len(sb), - &off); + if (cnt > uio->uio_resid) + cnt = uio->uio_resid; + + switch (uio->uio_rw) { + case UIO_READ: + if (d->dm_fops->read != NULL) + rc = d->dm_fops->read(&lf, iov->iov_base, cnt, + &off); + else + rc = -ENODEV; + break; + case UIO_WRITE: + if (d->dm_fops->write != NULL) + rc = d->dm_fops->write(&lf, iov->iov_base, cnt, + &off); + else + rc = -ENODEV; + break; } - break; + + if (rc <= 0) + break; + + iov->iov_base = (char *)iov->iov_base + rc; + iov->iov_len -= rc; + uio->uio_resid -= rc; + uio->uio_offset = off; } if (d->dm_fops->release) d->dm_fops->release(&vn, &lf); + /* Return success for short operations. */ + if (orig_resid != uio->uio_resid) + rc = 0; + if (rc < 0) { #ifdef INVARIANTS - printf("%s:%d read/write failed with %d\n", __func__, __LINE__, rc); + printf("%s:%d read/write failed with %zd\n", __func__, __LINE__, + rc); #endif return (-rc); } @@ -207,7 +234,7 @@ debugfs_create_file(const char *name, umode_t mode, flags = fops->write ? PFS_RDWR : PFS_RD; pfs_create_file(pnode, &dnode->d_pfs_node, name, debugfs_fill, - debugfs_attr, NULL, debugfs_destroy, flags | PFS_NOWAIT); + debugfs_attr, NULL, debugfs_destroy, flags | PFS_RAW | PFS_NOWAIT); if (dnode->d_pfs_node == NULL) { free(dm, M_DFSINT); return (NULL); @@ -671,7 +698,7 @@ fops_str_read(struct file *filp, char __user *ubuf, size_t read_size, } static ssize_t -fops_str_write(struct file *filp, const char *buf, size_t write_size, +fops_str_write(struct file *filp, const char __user *buf, size_t write_size, loff_t *ppos) { char *old, *new; diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c index 69fb9935a9ae..937b010c8435 100644 --- a/sys/compat/linux/linux_misc.c +++ b/sys/compat/linux/linux_misc.c @@ -750,6 +750,7 @@ linux_common_wait(struct thread *td, idtype_t idtype, int id, int *statusp, error = linux_copyout_rusage(&wru.wru_self, rup); if (error == 0 && infop != NULL && td->td_retval[0] != 0) { sig = bsd_to_linux_signal(siginfo.si_signo); + memset(&lsi, 0, sizeof(lsi)); siginfo_to_lsiginfo(&siginfo, &lsi, sig); error = copyout(&lsi, infop, sizeof(lsi)); } diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c index b1a483ce611c..023be1e6b885 100644 --- a/sys/compat/linux/linux_socket.c +++ b/sys/compat/linux/linux_socket.c @@ -594,10 +594,34 @@ linux_to_bsd_tcp_sockopt(int opt) return (-2); case LINUX_TCP_MD5SIG: return (TCP_MD5SIG); + case LINUX_TCP_USER_TIMEOUT: + return (TCP_MAXUNACKTIME); } return (-1); } +static u_int +linux_to_bsd_tcp_user_timeout(l_uint linux_timeout) +{ + + /* + * Linux exposes TCP_USER_TIMEOUT in milliseconds while + * TCP_MAXUNACKTIME uses whole seconds. Round up partial + * seconds so a non-zero Linux timeout never becomes zero. + */ + return (howmany(linux_timeout, 1000U)); +} + +static l_uint +bsd_to_linux_tcp_user_timeout(u_int bsd_timeout) +{ + + if (bsd_timeout > UINT_MAX / 1000U) + return (UINT_MAX); + + return (bsd_timeout * 1000U); +} + static int linux_to_bsd_msg_flags(int flags) { @@ -2057,8 +2081,10 @@ linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args) struct proc *p = td->td_proc; struct linux_pemuldata *pem; l_timeval linux_tv; + l_uint linux_timeout; struct sockaddr *sa; struct timeval tv; + u_int bsd_timeout; socklen_t len; int error, level, name, val; @@ -2130,6 +2156,24 @@ linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args) break; case IPPROTO_TCP: name = linux_to_bsd_tcp_sockopt(args->optname); + switch (name) { + case TCP_MAXUNACKTIME: + if (args->optlen < sizeof(linux_timeout)) + return (EINVAL); + + error = copyin(PTRIN(args->optval), &linux_timeout, + sizeof(linux_timeout)); + if (error != 0) + return (error); + + bsd_timeout = linux_to_bsd_tcp_user_timeout( + linux_timeout); + return (kern_setsockopt(td, args->s, level, name, + &bsd_timeout, UIO_SYSSPACE, + sizeof(bsd_timeout))); + default: + break; + } break; case SOL_NETLINK: name = args->optname; @@ -2279,10 +2323,12 @@ linux_getsockopt_so_linger(struct thread *td, int linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args) { + l_uint linux_timeout; l_timeval linux_tv; struct timeval tv; socklen_t tv_len, xulen, len; struct sockaddr *sa; + u_int bsd_timeout; struct xucred xu; struct l_ucred lxu; int error, level, name, newval; @@ -2373,6 +2419,21 @@ linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args) break; case IPPROTO_TCP: name = linux_to_bsd_tcp_sockopt(args->optname); + switch (name) { + case TCP_MAXUNACKTIME: + len = sizeof(bsd_timeout); + error = kern_getsockopt(td, args->s, level, name, + &bsd_timeout, UIO_SYSSPACE, &len); + if (error != 0) + return (error); + + linux_timeout = bsd_to_linux_tcp_user_timeout( + bsd_timeout); + return (linux_sockopt_copyout(td, &linux_timeout, + sizeof(linux_timeout), args)); + default: + break; + } break; default: name = -1; diff --git a/sys/compat/linux/linux_socket.h b/sys/compat/linux/linux_socket.h index 68176c3cc401..f2a96b3e7dcb 100644 --- a/sys/compat/linux/linux_socket.h +++ b/sys/compat/linux/linux_socket.h @@ -322,6 +322,7 @@ int linux_accept(struct thread *td, struct linux_accept_args *args); #define LINUX_TCP_KEEPCNT 6 #define LINUX_TCP_INFO 11 #define LINUX_TCP_MD5SIG 14 +#define LINUX_TCP_USER_TIMEOUT 18 struct l_ifmap { l_ulong mem_start; diff --git a/sys/compat/linuxkpi/common/include/linux/fs.h b/sys/compat/linuxkpi/common/include/linux/fs.h index f1568ad6282d..7e28be070850 100644 --- a/sys/compat/linuxkpi/common/include/linux/fs.h +++ b/sys/compat/linuxkpi/common/include/linux/fs.h @@ -364,27 +364,23 @@ static inline ssize_t simple_read_from_buffer(void __user *dest, size_t read_size, loff_t *ppos, void *orig, size_t buf_size) { - void *p, *read_pos = ((char *) orig) + *ppos; + void *read_pos = ((char *) orig) + *ppos; size_t buf_remain = buf_size - *ppos; + ssize_t num_read; - if (buf_remain < 0 || buf_remain > buf_size) - return -EINVAL; + if (*ppos >= buf_size || read_size == 0) + return (0); if (read_size > buf_remain) read_size = buf_remain; - /* - * XXX At time of commit only debugfs consumers could be - * identified. If others will use this function we may - * have to revise this: normally we would call copy_to_user() - * here but lindebugfs will return the result and the - * copyout is done elsewhere for us. - */ - p = memcpy(dest, read_pos, read_size); - if (p != NULL) - *ppos += read_size; - - return (read_size); + /* copy_to_user returns number of bytes NOT read */ + num_read = read_size - copy_to_user(dest, read_pos, read_size); + if (num_read == 0) + return -EFAULT; + *ppos += num_read; + + return (num_read); } MALLOC_DECLARE(M_LSATTR); @@ -415,11 +411,13 @@ int simple_attr_open(struct inode *inode, struct file *filp, int simple_attr_release(struct inode *inode, struct file *filp); -ssize_t simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos); +ssize_t simple_attr_read(struct file *filp, char __user *buf, size_t read_size, + loff_t *ppos); -ssize_t simple_attr_write(struct file *filp, const char *buf, size_t write_size, loff_t *ppos); +ssize_t simple_attr_write(struct file *filp, const char __user *buf, + size_t write_size, loff_t *ppos); -ssize_t simple_attr_write_signed(struct file *filp, const char *buf, +ssize_t simple_attr_write_signed(struct file *filp, const char __user *buf, size_t write_size, loff_t *ppos); #endif /* _LINUXKPI_LINUX_FS_H_ */ diff --git a/sys/compat/linuxkpi/common/include/linux/hardirq.h b/sys/compat/linuxkpi/common/include/linux/hardirq.h index f79451dd0d35..c6cbf1a34f14 100644 --- a/sys/compat/linuxkpi/common/include/linux/hardirq.h +++ b/sys/compat/linuxkpi/common/include/linux/hardirq.h @@ -31,6 +31,7 @@ #include <linux/types.h> #include <linux/lockdep.h> +#include <linux/preempt.h> #include <sys/param.h> #include <sys/bus.h> diff --git a/sys/compat/linuxkpi/common/include/linux/highmem.h b/sys/compat/linuxkpi/common/include/linux/highmem.h index 58a9cdcdf60f..dc1c4fe2f299 100644 --- a/sys/compat/linuxkpi/common/include/linux/highmem.h +++ b/sys/compat/linuxkpi/common/include/linux/highmem.h @@ -45,6 +45,7 @@ #include <linux/mm.h> #include <linux/page.h> +#include <linux/hardirq.h> #define PageHighMem(p) (0) diff --git a/sys/compat/linuxkpi/common/include/linux/seq_file.h b/sys/compat/linuxkpi/common/include/linux/seq_file.h index 3c7862890c67..786c09bd6a20 100644 --- a/sys/compat/linuxkpi/common/include/linux/seq_file.h +++ b/sys/compat/linuxkpi/common/include/linux/seq_file.h @@ -85,7 +85,7 @@ struct seq_operations { int (*show) (struct seq_file *m, void *v); }; -ssize_t seq_read(struct linux_file *, char *, size_t, off_t *); +ssize_t seq_read(struct linux_file *, char __user *, size_t, off_t *); int seq_write(struct seq_file *seq, const void *data, size_t len); void seq_putc(struct seq_file *m, char c); void seq_puts(struct seq_file *m, const char *str); diff --git a/sys/compat/linuxkpi/common/include/linux/spinlock.h b/sys/compat/linuxkpi/common/include/linux/spinlock.h index 63dc343d1461..a786cbab5e13 100644 --- a/sys/compat/linuxkpi/common/include/linux/spinlock.h +++ b/sys/compat/linuxkpi/common/include/linux/spinlock.h @@ -41,6 +41,7 @@ #include <linux/rwlock.h> #include <linux/bottom_half.h> #include <linux/lockdep.h> +#include <linux/preempt.h> typedef struct mtx spinlock_t; diff --git a/sys/compat/linuxkpi/common/include/linux/sysfs.h b/sys/compat/linuxkpi/common/include/linux/sysfs.h index 470c224a9778..7c8c4e2e32b9 100644 --- a/sys/compat/linuxkpi/common/include/linux/sysfs.h +++ b/sys/compat/linuxkpi/common/include/linux/sysfs.h @@ -43,13 +43,6 @@ struct sysfs_ops { size_t); }; -struct attribute_group { - const char *name; - mode_t (*is_visible)(struct kobject *, - struct attribute *, int); - struct attribute **attrs; -}; - struct bin_attribute { struct attribute attr; size_t size; @@ -59,6 +52,14 @@ struct bin_attribute { struct bin_attribute *, char *, loff_t, size_t); }; +struct attribute_group { + const char *name; + mode_t (*is_visible)(struct kobject *, + struct attribute *, int); + struct attribute **attrs; + struct bin_attribute **bin_attrs; +}; + #define __ATTR(_name, _mode, _show, _store) { \ .attr = { .name = __stringify(_name), .mode = _mode }, \ .show = _show, .store = _store, \ @@ -370,6 +371,7 @@ static inline int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) { struct attribute **attr; + struct bin_attribute **bin_attr; struct sysctl_oid *oidp; /* Don't create the group node if grp->name is undefined. */ @@ -378,11 +380,19 @@ sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) OID_AUTO, grp->name, CTLFLAG_RD|CTLFLAG_MPSAFE, NULL, grp->name); else oidp = kobj->oidp; - for (attr = grp->attrs; *attr != NULL; attr++) { + for (attr = grp->attrs; attr != NULL && *attr != NULL; attr++) { SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO, (*attr)->name, CTLTYPE_STRING|CTLFLAG_RW|CTLFLAG_MPSAFE, kobj, (uintptr_t)*attr, sysctl_handle_attr, "A", ""); } + for (bin_attr = grp->bin_attrs; + bin_attr != NULL && *bin_attr != NULL; + bin_attr++) { + SYSCTL_ADD_OID(NULL, SYSCTL_CHILDREN(oidp), OID_AUTO, + (*bin_attr)->attr.name, + CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_MPSAFE, + kobj, (uintptr_t)*bin_attr, sysctl_handle_bin_attr, "", ""); + } return (0); } @@ -434,14 +444,20 @@ static inline void sysfs_unmerge_group(struct kobject *kobj, const struct attribute_group *grp) { struct attribute **attr; + struct bin_attribute **bin_attr; struct sysctl_oid *oidp; SYSCTL_FOREACH(oidp, SYSCTL_CHILDREN(kobj->oidp)) { if (strcmp(oidp->oid_name, grp->name) != 0) continue; - for (attr = grp->attrs; *attr != NULL; attr++) { + for (attr = grp->attrs; attr != NULL && *attr != NULL; attr++) { sysctl_remove_name(oidp, (*attr)->name, 1, 1); } + for (bin_attr = grp->bin_attrs; + bin_attr != NULL && *bin_attr != NULL; + bin_attr++) { + sysctl_remove_name(oidp, (*bin_attr)->attr.name, 1, 1); + } } } diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c index 63f92b8afb2b..01347586ef63 100644 --- a/sys/compat/linuxkpi/common/src/linux_80211.c +++ b/sys/compat/linuxkpi/common/src/linux_80211.c @@ -3215,16 +3215,28 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int wiphy_lock(hw->wiphy); LKPI_80211_LVIF_LOCK(lvif); -#ifdef LINUXKPI_DEBUG_80211 - /* XXX-BZ KASSERT later; state going down so no action. */ - if (lvif->lvif_bss == NULL) - ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p " - "lvif_bss->ni %p synched %d\n", __func__, __LINE__, + /* + * XXX-BZ KASSERT later; state going down so no action in theory + * but try to avoid a NULL-pointer derref for now and gracefully + * fail for non-debug kernels. + */ + if (lvif->lvif_bss == NULL) { + ic_printf(vap->iv_ic, "%s:%d: ERROR: lvif %p vap %p iv_bss %p " + "lvif_bss %p lvif_bss->ni %p synched %d; " + "expect follow-up problems\n", __func__, __LINE__, lvif, vap, vap->iv_bss, lvif->lvif_bss, (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL, lvif->lvif_bss_synched); -#endif - + LKPI_80211_LVIF_UNLOCK(lvif); + /* + * This will likely lead to a firmware crash (if there + * was not one before already) and need a + * ieee80211_restart_hw() but still better than a panic + * for users as they can at least recover. + */ + error = ENOTRECOVERABLE; + goto out; + } lsta = lvif->lvif_bss; LKPI_80211_LVIF_UNLOCK(lvif); KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p " diff --git a/sys/compat/linuxkpi/common/src/linux_seq_file.c b/sys/compat/linuxkpi/common/src/linux_seq_file.c index 9c06fe27bebe..eae414ea696e 100644 --- a/sys/compat/linuxkpi/common/src/linux_seq_file.c +++ b/sys/compat/linuxkpi/common/src/linux_seq_file.c @@ -40,7 +40,7 @@ MALLOC_DEFINE(M_LSEQ, "seq_file", "seq_file"); ssize_t -seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos) +seq_read(struct linux_file *f, char __user *ubuf, size_t size, off_t *ppos) { struct seq_file *m; struct sbuf *sbuf; @@ -49,6 +49,7 @@ seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos) m = f->private_data; sbuf = m->buf; + sbuf_clear(sbuf); p = m->op->start(m, ppos); rc = m->op->show(m, p); @@ -59,15 +60,8 @@ seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos) if (rc) return (rc); - rc = sbuf_len(sbuf); - if (*ppos >= rc || size < 1) - return (-EINVAL); - - size = min(rc - *ppos, size); - memcpy(ubuf, sbuf_data(sbuf) + *ppos, size); - *ppos += size; - - return (size); + return (simple_read_from_buffer(ubuf, size, ppos, sbuf_data(sbuf), + sbuf_len(sbuf))); } int diff --git a/sys/compat/linuxkpi/common/src/linux_simple_attr.c b/sys/compat/linuxkpi/common/src/linux_simple_attr.c index 8cc9ec7ecbc9..e5514194cb33 100644 --- a/sys/compat/linuxkpi/common/src/linux_simple_attr.c +++ b/sys/compat/linuxkpi/common/src/linux_simple_attr.c @@ -99,7 +99,8 @@ simple_attr_release(struct inode *inode, struct file *filp) * On failure, negative signed ERRNO */ ssize_t -simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos) +simple_attr_read(struct file *filp, char __user *buf, size_t read_size, + loff_t *ppos) { struct simple_attr *sattr; uint64_t data; @@ -119,18 +120,9 @@ simple_attr_read(struct file *filp, char *buf, size_t read_size, loff_t *ppos) scnprintf(prebuf, sizeof(prebuf), sattr->fmt, data); - ret = strlen(prebuf) + 1; - if (*ppos >= ret || read_size < 1) { - ret = -EINVAL; - goto unlock; - } - - read_size = min(ret - *ppos, read_size); - ret = strscpy(buf, prebuf + *ppos, read_size); - /* add 1 for null terminator */ - if (ret > 0) - ret += 1; + ret = simple_read_from_buffer(buf, read_size, ppos, prebuf, + strlen(prebuf) + 1); unlock: mutex_unlock(&sattr->mutex); @@ -155,29 +147,38 @@ unlock: * On failure, negative signed ERRNO */ static ssize_t -simple_attr_write_common(struct file *filp, const char *buf, size_t write_size, - loff_t *ppos, bool is_signed) +simple_attr_write_common(struct file *filp, const char __user *ubuf, + size_t write_size, loff_t *ppos, bool is_signed) { struct simple_attr *sattr; unsigned long long data; - size_t bufsize; + char *buf; ssize_t ret; sattr = filp->private_data; - bufsize = strlen(buf) + 1; if (sattr->set == NULL) return (-EFAULT); - if (*ppos >= bufsize || write_size < 1) + if (*ppos != 0 || write_size < 1) return (-EINVAL); + buf = malloc(write_size, M_LSATTR, M_WAITOK); + if (copy_from_user(buf, ubuf, write_size) != 0) { + free(buf, M_LSATTR); + return (-EFAULT); + } + if (strnlen(buf, write_size) == write_size) { + free(buf, M_LSATTR); + return (-EINVAL); + } + mutex_lock(&sattr->mutex); if (is_signed) - ret = kstrtoll(buf + *ppos, 0, &data); + ret = kstrtoll(buf, 0, &data); else - ret = kstrtoull(buf + *ppos, 0, &data); + ret = kstrtoull(buf, 0, &data); if (ret) goto unlock; @@ -185,23 +186,24 @@ simple_attr_write_common(struct file *filp, const char *buf, size_t write_size, if (ret) goto unlock; - ret = bufsize - *ppos; + ret = write_size; unlock: mutex_unlock(&sattr->mutex); + free(buf, M_LSATTR); return (ret); } ssize_t -simple_attr_write(struct file *filp, const char *buf, size_t write_size, +simple_attr_write(struct file *filp, const char __user *buf, size_t write_size, loff_t *ppos) { return (simple_attr_write_common(filp, buf, write_size, ppos, false)); } ssize_t -simple_attr_write_signed(struct file *filp, const char *buf, size_t write_size, - loff_t *ppos) +simple_attr_write_signed(struct file *filp, const char __user *buf, + size_t write_size, loff_t *ppos) { return (simple_attr_write_common(filp, buf, write_size, ppos, true)); } |
