git: 91413e8e6311 - main - linuxkpi ioctl handler: restore the user data pointer
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 23 Jun 2026 21:06:17 UTC
The branch main has been updated by kib:
URL: https://cgit.FreeBSD.org/src/commit/?id=91413e8e6311b4d4891dc2299d144feb31e04628
commit 91413e8e6311b4d4891dc2299d144feb31e04628
Author: Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-06-16 22:12:01 +0000
Commit: Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-06-23 21:06:04 +0000
linuxkpi ioctl handler: restore the user data pointer
instead of trying to hack around it with LINUX_IOCTL_MIN_PTR. Since
linux file ioctl methods expect the user address in the data argument,
this should work for all ioctls, including the variable-length cases
like ibcore.
Only do it for the FreeBSD ABI, where we know how to reliably access the
original syscall arguments.
Reviewed by: Ariel Ehrenberg <aehrenberg@nvidia.com>, markj
Discussed with: zishun.yi.dev@gmail.com
Sponsored by: NVidia networking
MFC after: 1 week
Differential revision: https://reviews.freebsd.org/D57612
---
sys/compat/linuxkpi/common/src/linux_compat.c | 48 ++++++++++++++++++++++++---
1 file changed, 44 insertions(+), 4 deletions(-)
diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c
index 2fd502990eb3..e93963428195 100644
--- a/sys/compat/linuxkpi/common/src/linux_compat.c
+++ b/sys/compat/linuxkpi/common/src/linux_compat.c
@@ -51,6 +51,7 @@
#include <sys/mman.h>
#include <sys/stack.h>
#include <sys/stdarg.h>
+#include <sys/syscall.h>
#include <sys/sysent.h>
#include <sys/time.h>
#include <sys/user.h>
@@ -926,19 +927,39 @@ linux_file_ioctl_sub(struct file *fp, struct linux_file *filp,
struct task_struct *task = current;
unsigned size;
int error;
+ bool direct;
size = IOCPARM_LEN(cmd);
/* refer to logic in sys_ioctl() */
+ direct = false;
if (size > 0) {
/*
* Setup hint for linux_copyin() and linux_copyout().
*
- * Background: Linux code expects a user-space address
- * while FreeBSD supplies a kernel-space address.
+ * Background: Linux kernel code expects to operate on
+ * userspace addresses, but FreeBSD's kern_ioctl()
+ * will generally provide a kernel address. For the
+ * native process ABI, where we know how to find the
+ * original address, we reach directly into the system
+ * call args to get it. Then, if the Linux driver
+ * copied out to that address, we copy the whole block
+ * back into the kernel buffer allocated by
+ * kern_ioctl() so that kern_ioctl() itself doesn't
+ * clobber the driver's data.
+ *
+ * Otherwise, fall back to the LINUX_IOCTL_MIN_PTR
+ * hack.
*/
task->bsd_ioctl_data = data;
task->bsd_ioctl_len = size;
- data = (void *)LINUX_IOCTL_MIN_PTR;
+ if ((td->td_pflags & TDP_KTHREAD) == 0 &&
+ SV_PROC_ABI(td->td_proc) == SV_ABI_FREEBSD &&
+ td->td_sa.code == SYS_ioctl) {
+ direct = true;
+ data = (void *)(uintptr_t)td->td_sa.args[2];
+ } else {
+ data = (void *)LINUX_IOCTL_MIN_PTR;
+ }
} else {
/* fetch user-space pointer */
data = *(void **)data;
@@ -968,11 +989,30 @@ linux_file_ioctl_sub(struct file *fp, struct linux_file *filp,
error = ENOTTY;
}
}
+ if (error == 0 && size > 0 && (cmd & IOC_OUT) != 0 && direct) {
+ void *xdata;
+ int error1;
+
+ /*
+ * Ensure that the copyout in sys_generic.c copies
+ * over the data which is possibly modified by the
+ * driver. A possible error from the copyin() is
+ * ignored since it is formally possible for the memory
+ * to become unaccessible in the meantime. Do the copying
+ * through the intermediate buffer instead of copying
+ * directly to bsd_ioctl_data, to ensure atomicity of
+ * the change with respect to the error.
+ */
+ xdata = malloc(size, M_TEMP, M_WAITOK);
+ error1 = copyin(data, xdata, size);
+ if (error1 == 0)
+ memcpy(task->bsd_ioctl_data, xdata, size);
+ free(xdata, M_TEMP);
+ }
if (size > 0) {
task->bsd_ioctl_data = NULL;
task->bsd_ioctl_len = 0;
}
-
if (error == EWOULDBLOCK) {
/* update kqfilter status, if any */
linux_file_kqfilter_poll(filp,