aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2026-03-17 19:45:34 +0000
committerJohn Baldwin <jhb@FreeBSD.org>2026-03-17 19:45:34 +0000
commit2cf15144daf7ec44cdcd9bf3ef007939b79c361e (patch)
tree931c84bfeb0d37d569716ee0938db43e6bb052aa
parent14e97448fcebbe4b038eaf5628933abe5f9e690d (diff)
-rw-r--r--sys/compat/lindebugfs/lindebugfs.c81
-rw-r--r--sys/compat/linuxkpi/common/include/linux/fs.h28
-rw-r--r--sys/compat/linuxkpi/common/include/linux/seq_file.h2
-rw-r--r--sys/compat/linuxkpi/common/src/linux_seq_file.c2
-rw-r--r--sys/compat/linuxkpi/common/src/linux_simple_attr.c35
5 files changed, 92 insertions, 56 deletions
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));
+ 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));
- free(buf, M_DFSINT);
- }
+ 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/linuxkpi/common/include/linux/fs.h b/sys/compat/linuxkpi/common/include/linux/fs.h
index f1568ad6282d..40e1b396fe86 100644
--- a/sys/compat/linuxkpi/common/include/linux/fs.h
+++ b/sys/compat/linuxkpi/common/include/linux/fs.h
@@ -364,8 +364,9 @@ 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;
@@ -373,18 +374,13 @@ simple_read_from_buffer(void __user *dest, size_t read_size, loff_t *ppos,
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;
+ /* 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 (read_size);
+ 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/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/src/linux_seq_file.c b/sys/compat/linuxkpi/common/src/linux_seq_file.c
index b1d53aa2db60..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;
diff --git a/sys/compat/linuxkpi/common/src/linux_simple_attr.c b/sys/compat/linuxkpi/common/src/linux_simple_attr.c
index 88fa908e47bb..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;
@@ -146,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;
@@ -176,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));
}