From nobody Tue Mar 17 19:50:28 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4fb2cn5sHXz6VWFc for ; Tue, 17 Mar 2026 19:50:33 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R12" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4fb2cn4yP8z40j7 for ; Tue, 17 Mar 2026 19:50:33 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1773777033; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=agkotF/whVxtgayHAoP+BUUD/lwX2ZEPTl37dSLuw0Y=; b=CgY8OIUI4JI4MRSOmnLWxlUceMELNZ/AR9oxuASt6UquMdhkpW+bwUAmvpfCsNpY4loh1z lXzQyxgG6owi8IJshdp8PU5806vWTYm5bEmpcOu+cl8DRJ1hFr4GOR672nwAV2SF7qvTv0 rARrNSrwiGHfGT9Wh9k3bZMVRaGqwpZZvPR/WDLqyBzlb9XeLirNwaGFyIgbql36C+vURO 3Yu+HuJyUub8LkIek9z4u8agxo7KSSTNDFY74RNRq9q2Q8pI3BxIp+3xEd0PxoLhWaoQfq kJvHidgGZqYsrG7gsIlhsrXLf68hMoaojPvr0MdzAb3e7voE9m29k49oQxtCBg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1773777033; a=rsa-sha256; cv=none; b=u2O50SCkBgH0BW+UmrYa/MJM51TCAfJBUM1IE87lqER6ibM3MY0yRzTf3ubef/Xjq8yANl s6Mz6yrnQiY+E34oAOgswB+8R5hxZHo3weY2LzsqTZO1oib13i6JmfkVT4SO7YiEAmHW1k 5Ncon+bynO1jd1WriSnOYmLETJcFIfXSgsa4dgJJNhiBiCMLd0Yx+VmIEoFv2spDzpsEX5 cAAkb1dEMyLaVC/D8zhkmeZwzZOMwva8ev58c9hlZ2RU7qLhCZrJNBSPgPHOt8TX+znx/L MSiaZNPBdy+6VqpJm6faeuplCyfE+zGSPEq3O2yl8cGUvkYFXaHGatCmsLs2SQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1773777033; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=agkotF/whVxtgayHAoP+BUUD/lwX2ZEPTl37dSLuw0Y=; b=TVojkFOEE9/dH5LSjjrTLCfolI1SJHxfD5nvF+/7jG3yKYcvIyxuLrqmVVYzKXiavZhMJb 8mljr3Dkadjy4svhJHYL9032xjjxumrxwNbJ4/w31DsiPT8XnQUg2ctBIvvvvhPf+PxyJ8 3QiuOKmcogctmuOr2I5O5jH+rYCAEMGSduIMl/F2s4IRd4gkXLhgH2GJoa8RtSmDza316P g40yR1PcvUj6evin4E61erfsGMkKAQR3ugwPeI8IWrg6VTpOhQK8ez4ZSrcqwZvuqB75Vp 5GsPT2eYmOmAVsxDgnRrH3Ko9hzGOF2PbgiQoTsoDGm6J4oqcfqT7cFK0+NWPQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4fb2cn3yWWzcMH for ; Tue, 17 Mar 2026 19:50:33 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3b9bc by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 17 Mar 2026 19:50:28 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: John Baldwin Subject: git: 2cf15144daf7 - main - lindebugfs: Pass user buffer pointers to the read/write file operations List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: jhb X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 2cf15144daf7ec44cdcd9bf3ef007939b79c361e Auto-Submitted: auto-generated Date: Tue, 17 Mar 2026 19:50:28 +0000 Message-Id: <69b9b084.3b9bc.65156cd1@gitrepo.freebsd.org> The branch main has been updated by jhb: URL: https://cgit.FreeBSD.org/src/commit/?id=2cf15144daf7ec44cdcd9bf3ef007939b79c361e commit 2cf15144daf7ec44cdcd9bf3ef007939b79c361e Author: John Baldwin AuthorDate: 2026-03-17 19:45:34 +0000 Commit: John Baldwin CommitDate: 2026-03-17 19:45:34 +0000 lindebugfs: Pass user buffer pointers to the read/write file operations The Linux file_operations API expects the read and write operations to take a single user buffer pointer (along with the length and the file offset as an in/out parameter). However, the debugfs_fill function was violating this part of the contract as it was passing down kernel pointers instead. An earlier commit (5668c22a13c6befa9b8486387d38457c40ce7af4) hacked around this by modifying simple_read_from_buffer() to treat its user pointer argument as a kernel pointer instead. However, other commits keep tripping over this same API mismatch (e.g. 78e25e65bf381303c8bdac9a713ab7b26a854b8c passes a kernel pointer to copy_from_user in fops_str_write). Instead, change debugfs_fill to use the "raw" pseudofs mode where the uio is passed down to directly to the fill callback rather than an sbuf. debufs_fill now iterates over the iovec in the uio similar to the implementation of uiomove invoking the read or write operation on each user pointer. This also fixes a tiny bug where the initial file offset from uio_offset was ignored. Instead, the operations were always invoked with a file offset of 0. As part of this, revert the the changes to simple_read_from_buffer() from commit 5668c22a13c6befa9b8486387d38457c40ce7af4. Also as part of this, the simple_attr_read/write methods and seq_read now also need to accept and handle user pointers (also matching the API in Linux). For simple_attr_write*(), copy the user buffer into a kernel buffer before parsing. Also, do not permit writes at an offset as it's unclear what the semantics for those would even be (perhaps you would write out the formatted value into a buffer first and then allow the copy_from_user to overwrite/extend that buffer and then re-parse the integer value?). The old handling of *ppos for writes was definitely wrong before and only worked for an offset of 0 anyway. Reviewed by: bz Sponsored by: AFRL, DARPA Differential Revision: https://reviews.freebsd.org/D55833 --- sys/compat/lindebugfs/lindebugfs.c | 83 ++++++++++++++-------- sys/compat/linuxkpi/common/include/linux/fs.h | 30 ++++---- .../linuxkpi/common/include/linux/seq_file.h | 2 +- sys/compat/linuxkpi/common/src/linux_seq_file.c | 2 +- sys/compat/linuxkpi/common/src/linux_simple_attr.c | 35 +++++---- 5 files changed, 94 insertions(+), 58 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)); - - 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/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; - - 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/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)); }