From fe0ec89c74b2976941c5fd44ae4c280448a5acf1 Mon Sep 17 00:00:00 2001 From: Martin Pitt Date: Thu, 2 Jan 2025 10:50:18 +0100 Subject: [PATCH] preload: Only emulate ioctls on emulated devices Don't invoke the remote ioctl handler for host devices. This fixes e.g. ioctls on /dev/tty or other standard devices which the tested program may want to do. This bug was hidden by the conditional check in t_usbfs_ioctl_static() which skipped the check if /dev/tty wasn't accessible -- which is the case in `meson test`. Move to a FIONREAD ioctl on /dev/stdout, which works on ttys and pipes, i.e. everywhere. Make that check unconditional. --- src/ioctl.vapi | 3 +-- src/libumockdev-preload.c | 33 ++++++++++++++------------------- tests/test-umockdev-vala.vala | 19 +++++++++---------- 3 files changed, 24 insertions(+), 31 deletions(-) diff --git a/src/ioctl.vapi b/src/ioctl.vapi index 1fa6dfa..adef5d1 100644 --- a/src/ioctl.vapi +++ b/src/ioctl.vapi @@ -25,7 +25,7 @@ namespace Ioctl { [CCode (cheader_filename = "sys/ioctl.h")] public const int USBDEVFS_RESETEP; [CCode (cheader_filename = "sys/ioctl.h")] - public const int TIOCSBRK; + public const int FIONREAD; [CCode (cheader_filename = "linux/usbdevice_fs.h")] public const int USBDEVFS_CAP_ZERO_PACKET; @@ -144,4 +144,3 @@ namespace Ioctl { uint8 data[0]; } } - diff --git a/src/libumockdev-preload.c b/src/libumockdev-preload.c index 8db8446..184dbdc 100644 --- a/src/libumockdev-preload.c +++ b/src/libumockdev-preload.c @@ -502,16 +502,16 @@ static fd_map ioctl_wrapped_fds; struct ioctl_fd_info { char *dev_path; int ioctl_sock; - int is_default; + bool is_emulated; pthread_mutex_t sock_lock; }; static void -ioctl_emulate_open(int fd, const char *dev_path, int must_exist) +ioctl_emulate_open(int fd, const char *dev_path, bool is_emulated) { libc_func(socket, int, int, int, int); libc_func(connect, int, int, const struct sockaddr *, socklen_t); - int is_default = 0; + bool is_default = false; int sock; int ret; struct ioctl_fd_info *fdinfo; @@ -525,27 +525,22 @@ ioctl_emulate_open(int fd, const char *dev_path, int must_exist) if (path_exists (addr.sun_path) != 0) { snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/ioctl/_default", getenv("UMOCKDEV_DIR")); - is_default = 1; + is_default = true; } int orig_errno = errno; sock = _socket(AF_UNIX, SOCK_STREAM, 0); if (sock == -1) { - if (must_exist) { - fprintf(stderr, "ERROR: libumockdev-preload: Failed to open ioctl socket for %s", - dev_path); - exit(1); - } else { - errno = orig_errno; - return; - } + DBG(DBG_IOCTL, "ioctl_emulate_open fd %i (%s): not emulated\n", fd, dev_path); + errno = orig_errno; + return; } ret = _connect(sock, (const struct sockaddr *) &addr, sizeof(addr)); if (ret == -1) { - if (must_exist) { - fprintf(stderr, "ERROR: libumockdev-preload: Failed to connect to ioctl socket for %s", - dev_path); + if (!is_default || errno != ENOENT) { + fprintf(stderr, "ERROR: libumockdev-preload: Failed to connect to ioctl socket %s for %s: %m\n", + addr.sun_path, dev_path); exit(1); } else { errno = orig_errno; @@ -554,13 +549,13 @@ ioctl_emulate_open(int fd, const char *dev_path, int must_exist) } fdinfo = mallocx(sizeof(struct ioctl_fd_info)); + fdinfo->is_emulated = is_emulated; fdinfo->ioctl_sock = sock; fdinfo->dev_path = strdupx(dev_path); - fdinfo->is_default = is_default; pthread_mutex_init(&fdinfo->sock_lock, NULL); fd_map_add(&ioctl_wrapped_fds, fd, fdinfo); - DBG(DBG_IOCTL, "ioctl_emulate_open fd %i (%s): connected ioctl sockert\n", fd, dev_path); + DBG(DBG_IOCTL, "ioctl_emulate_open fd %i (%s): connected ioctl socket\n", fd, dev_path); errno = orig_errno; } @@ -625,8 +620,8 @@ remote_emulate(int fd, int cmd, long arg1, long arg2) } IOCTL_UNLOCK; - /* Only pass on ioctl requests for the default handler. */ - if (fdinfo->is_default && cmd != IOCTL_REQ_IOCTL) { + /* Only pass on ioctl requests for emulated devices */ + if (!fdinfo->is_emulated) { pthread_sigmask(SIG_SETMASK, &sig_restore, NULL); return UNHANDLED; } diff --git a/tests/test-umockdev-vala.vala b/tests/test-umockdev-vala.vala index 5c19140..86a2b15 100644 --- a/tests/test-umockdev-vala.vala +++ b/tests/test-umockdev-vala.vala @@ -269,23 +269,22 @@ E: SUBSYSTEM=usb /* no ioctl tree loaded */ var ci = Ioctl.usbdevfs_connectinfo(); assert_cmpint (Posix.ioctl (fd, Ioctl.USBDEVFS_CONNECTINFO, ref ci), CompareOperator.EQ, -1); - // usually ENOTTY, but seem to be EINVAL - assert_cmpint (Posix.errno, CompareOperator.GE, 22); + assert_cmpint (Posix.errno, CompareOperator.GE, Posix.EINVAL); Posix.errno = 0; // unknown ioctls don't work on an emulated device - assert_cmpint (Posix.ioctl (fd, Ioctl.TIOCSBRK, 0), CompareOperator.EQ, -1); + int argp; + assert_cmpint (Posix.ioctl (fd, Ioctl.FIONREAD, &argp), CompareOperator.EQ, -1); assert_cmpint (Posix.errno, CompareOperator.EQ, Posix.ENOTTY); + Posix.close (fd); Posix.errno = 0; // unknown ioctls do work on non-emulated devices - int fd2 = Posix.open ("/dev/tty", Posix.O_RDWR, 0); - if (fd2 > 0) { - assert_cmpint (Posix.ioctl (fd2, Ioctl.TIOCSBRK, 0), CompareOperator.EQ, 0); - assert_cmpint (Posix.errno, CompareOperator.EQ, 0); - Posix.close (fd2); - } - + fd = Posix.open ("/dev/stdout", Posix.O_WRONLY, 0); + assert_cmpint (fd, CompareOperator.GE, 0); + assert_cmpint (Posix.errno, CompareOperator.EQ, 0); + assert_cmpint (Posix.ioctl (fd, Ioctl.FIONREAD, out argp), CompareOperator.EQ, 0); + assert_cmpint (Posix.errno, CompareOperator.EQ, 0); Posix.close (fd); }