[PATCH v2] linux-user: Add most IFTUN ioctls

Shu-Chun Weng posted 1 patch 3 years, 9 months ago
Test docker-quick@centos7 failed
Test docker-mingw@fedora failed
Test checkpatch failed
Test FreeBSD failed
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20200723231020.769893-1-scw@google.com
Maintainers: Laurent Vivier <laurent@vivier.eu>
There is a newer version of this series
linux-user/ioctls.h       | 45 +++++++++++++++++++++++++++++++++++++++
linux-user/syscall.c      | 36 +++++++++++++++++++++++++++++++
linux-user/syscall_defs.h | 32 ++++++++++++++++++++++++++++
3 files changed, 113 insertions(+)
[PATCH v2] linux-user: Add most IFTUN ioctls
Posted by Shu-Chun Weng 3 years, 9 months ago
The three options handling `struct sock_fprog` (TUNATTACHFILTER,
TUNDETACHFILTER, and TUNGETFILTER) are not implemented. Linux kernel
keeps a user space pointer in them which we cannot correctly handle.

Signed-off-by: Josh Kunz <jkz@google.com>
Signed-off-by: Shu-Chun Weng <scw@google.com>
---
v2:
  Title changed from "linux-user: Add several IFTUN ioctls"

  Properly specify the argument types for various options, including a custom
  implementation for TUNSETTXFILTER.

  #ifdef guards for macros introduced up to 5 years ago.

 linux-user/ioctls.h       | 45 +++++++++++++++++++++++++++++++++++++++
 linux-user/syscall.c      | 36 +++++++++++++++++++++++++++++++
 linux-user/syscall_defs.h | 32 ++++++++++++++++++++++++++++
 3 files changed, 113 insertions(+)

diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
index 0713ae1311..b9fb01f558 100644
--- a/linux-user/ioctls.h
+++ b/linux-user/ioctls.h
@@ -593,3 +593,48 @@
   IOCTL(KCOV_DISABLE, 0, TYPE_NULL)
   IOCTL(KCOV_INIT_TRACE, IOC_R, TYPE_ULONG)
 #endif
+
+  IOCTL(TUNSETDEBUG,     IOC_W, TYPE_INT)
+  IOCTL(TUNSETIFF,       IOC_RW, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
+  IOCTL(TUNSETPERSIST,   IOC_W, TYPE_INT)
+  IOCTL(TUNSETOWNER,     IOC_W, TYPE_INT)
+  IOCTL(TUNSETLINK,      IOC_W, TYPE_INT)
+  IOCTL(TUNSETGROUP,     IOC_W, TYPE_INT)
+  IOCTL(TUNGETFEATURES,  IOC_R, MK_PTR(TYPE_INT))
+  IOCTL(TUNSETOFFLOAD,   IOC_W, TYPE_LONG)
+  IOCTL_SPECIAL(TUNSETTXFILTER, IOC_W, do_ioctl_TUNSETTXFILTER,
+                /*
+                 * We can't represent `struct tun_filter` in thunk so leaving
+                 * this empty. do_ioctl_TUNSETTXFILTER will do the conversion.
+                 */
+                TYPE_NULL)
+  IOCTL(TUNGETIFF,       IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
+  IOCTL(TUNGETSNDBUF,    IOC_R, MK_PTR(TYPE_INT))
+  IOCTL(TUNSETSNDBUF,    IOC_W, MK_PTR(TYPE_INT))
+  /*
+   * TUNATTACHFILTER and TUNDETACHFILTER are not supported. Linux kernel keeps a
+   * user pointer in TUNATTACHFILTER, which we are not able to correctly handle.
+   */
+  IOCTL(TUNGETVNETHDRSZ, IOC_R, MK_PTR(TYPE_INT))
+  IOCTL(TUNSETVNETHDRSZ, IOC_W, MK_PTR(TYPE_INT))
+  IOCTL(TUNSETQUEUE,     IOC_W, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
+  IOCTL(TUNSETIFINDEX ,  IOC_W, MK_PTR(TYPE_INT))
+  /* TUNGETFILTER is not supported: see TUNATTACHFILTER. */
+  IOCTL(TUNSETVNETLE,    IOC_W, MK_PTR(TYPE_INT))
+  IOCTL(TUNGETVNETLE,    IOC_R, MK_PTR(TYPE_INT))
+#ifdef TUNSETVNETBE
+  IOCTL(TUNSETVNETBE,    IOC_W, MK_PTR(TYPE_INT))
+  IOCTL(TUNGETVNETBE,    IOC_R, MK_PTR(TYPE_INT))
+#endif
+#ifdef TUNSETSTEERINGEBPF
+  IOCTL(TUNSETSTEERINGEBPF, IOC_W, MK_PTR(TYPE_INT))
+#endif
+#ifdef TUNSETFILTEREBPF
+  IOCTL(TUNSETFILTEREBPF, IOC_W, MK_PTR(TYPE_INT))
+#endif
+#ifdef TUNSETCARRIER
+  IOCTL(TUNSETCARRIER,   IOC_W, MK_PTR(TYPE_INT))
+#endif
+#ifdef TUNGETDEVNETNS
+  IOCTL(TUNGETDEVNETNS,  IOC_R, TYPE_NULL)
+#endif
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 1211e759c2..7f1efed189 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -56,6 +56,7 @@
 #include <linux/wireless.h>
 #include <linux/icmp.h>
 #include <linux/icmpv6.h>
+#include <linux/if_tun.h>
 #include <linux/errqueue.h>
 #include <linux/random.h>
 #ifdef CONFIG_TIMERFD
@@ -5422,6 +5423,41 @@ static abi_long do_ioctl_drm(const IOCTLEntry *ie, uint8_t *buf_temp,
 
 #endif
 
+static abi_long do_ioctl_TUNSETTXFILTER(const IOCTLEntry *ie, uint8_t *buf_temp,
+                                        int fd, int cmd, abi_long arg)
+{
+    struct tun_filter *filter = (struct tun_filter *)buf_temp;
+    struct tun_filter *target_filter;
+    char *target_addr;
+
+    assert(ie->access == IOC_W);
+
+    target_filter = lock_user(VERIFY_READ, arg, sizeof(*filter), 1);
+    if (!target_filter) {
+        return -TARGET_EFAULT;
+    }
+    filter->flags = tswap16(target_filter->flags);
+    filter->count = tswap16(target_filter->count);
+    unlock_user(target_filter, arg, sizeof(*filter));
+
+    if (filter->count) {
+        if (sizeof(*filter) + filter->count * ETH_ALEN > MAX_STRUCT_SIZE) {
+            return -TARGET_EFAULT;
+        }
+
+        target_addr = lock_user(VERIFY_READ, arg + sizeof(*filter),
+                                filter->count * ETH_ALEN, 1);
+        if (!target_addr) {
+            return -TARGET_EFAULT;
+        }
+        memcpy(filter->addr, target_addr, filter->count * ETH_ALEN);
+        unlock_user(target_addr, arg + sizeof(*filter),
+                    filter->count * ETH_ALEN);
+    }
+
+    return get_errno(safe_ioctl(fd, ie->host_cmd, filter));
+}
+
 IOCTLEntry ioctl_entries[] = {
 #define IOCTL(cmd, access, ...) \
     { TARGET_ ## cmd, cmd, #cmd, access, 0, {  __VA_ARGS__ } },
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index 3c261cff0e..7ef0ff0328 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -891,6 +891,38 @@ struct target_rtc_pll_info {
 
 #define TARGET_SIOCGIWNAME     0x8B01          /* get name == wireless protocol */
 
+/* From <linux/if_tun.h> */
+
+#define TARGET_TUNSETDEBUG        TARGET_IOW('T', 201, int)
+#define TARGET_TUNSETIFF          TARGET_IOW('T', 202, int)
+#define TARGET_TUNSETPERSIST      TARGET_IOW('T', 203, int)
+#define TARGET_TUNSETOWNER        TARGET_IOW('T', 204, int)
+#define TARGET_TUNSETLINK         TARGET_IOW('T', 205, int)
+#define TARGET_TUNSETGROUP        TARGET_IOW('T', 206, int)
+#define TARGET_TUNGETFEATURES     TARGET_IOR('T', 207, unsigned int)
+#define TARGET_TUNSETOFFLOAD      TARGET_IOW('T', 208, unsigned int)
+#define TARGET_TUNSETTXFILTER     TARGET_IOW('T', 209, unsigned int)
+#define TARGET_TUNGETIFF          TARGET_IOR('T', 210, unsigned int)
+#define TARGET_TUNGETSNDBUF       TARGET_IOR('T', 211, int)
+#define TARGET_TUNSETSNDBUF       TARGET_IOW('T', 212, int)
+/*
+ * TUNATTACHFILTER and TUNDETACHFILTER are not supported. Linux kernel keeps a
+ * user pointer in TUNATTACHFILTER, which we are not able to correctly handle.
+ */
+#define TARGET_TUNGETVNETHDRSZ    TARGET_IOR('T', 215, int)
+#define TARGET_TUNSETVNETHDRSZ    TARGET_IOW('T', 216, int)
+#define TARGET_TUNSETQUEUE        TARGET_IOW('T', 217, int)
+#define TARGET_TUNSETIFINDEX      TARGET_IOW('T', 218, unsigned int)
+/* TUNGETFILTER is not supported: see TUNATTACHFILTER. */
+#define TARGET_TUNSETVNETLE       TARGET_IOW('T', 220, int)
+#define TARGET_TUNGETVNETLE       TARGET_IOR('T', 221, int)
+#define TARGET_TUNSETVNETBE       TARGET_IOW('T', 222, int)
+#define TARGET_TUNGETVNETBE       TARGET_IOR('T', 223, int)
+#define TARGET_TUNSETSTEERINGEBPF TARGET_IOR('T', 224, int)
+#define TARGET_TUNSETFILTEREBPF   TARGET_IOR('T', 225, int)
+#define TARGET_TUNSETCARRIER      TARGET_IOW('T', 226, int)
+#define TARGET_TUNGETDEVNETNS     TARGET_IO('T', 227)
+
 /* From <linux/random.h> */
 
 #define TARGET_RNDGETENTCNT    TARGET_IOR('R', 0x00, int)
-- 
2.28.0.rc0.142.g3c755180ce-goog


Re: [PATCH v2] linux-user: Add most IFTUN ioctls
Posted by Laurent Vivier 3 years, 7 months ago
Le 24/07/2020 à 01:10, Shu-Chun Weng a écrit :
> The three options handling `struct sock_fprog` (TUNATTACHFILTER,
> TUNDETACHFILTER, and TUNGETFILTER) are not implemented. Linux kernel
> keeps a user space pointer in them which we cannot correctly handle.
> 
> Signed-off-by: Josh Kunz <jkz@google.com>
> Signed-off-by: Shu-Chun Weng <scw@google.com>
> ---
> v2:
>   Title changed from "linux-user: Add several IFTUN ioctls"
> 
>   Properly specify the argument types for various options, including a custom
>   implementation for TUNSETTXFILTER.
> 
>   #ifdef guards for macros introduced up to 5 years ago.
> 
>  linux-user/ioctls.h       | 45 +++++++++++++++++++++++++++++++++++++++
>  linux-user/syscall.c      | 36 +++++++++++++++++++++++++++++++
>  linux-user/syscall_defs.h | 32 ++++++++++++++++++++++++++++
>  3 files changed, 113 insertions(+)
> 
> diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
> index 0713ae1311..b9fb01f558 100644
> --- a/linux-user/ioctls.h
> +++ b/linux-user/ioctls.h
> @@ -593,3 +593,48 @@
>    IOCTL(KCOV_DISABLE, 0, TYPE_NULL)
>    IOCTL(KCOV_INIT_TRACE, IOC_R, TYPE_ULONG)
>  #endif
> +
> +  IOCTL(TUNSETDEBUG,     IOC_W, TYPE_INT)
> +  IOCTL(TUNSETIFF,       IOC_RW, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))

Why is this IOC_RW?

> +  IOCTL(TUNSETPERSIST,   IOC_W, TYPE_INT)
> +  IOCTL(TUNSETOWNER,     IOC_W, TYPE_INT)
> +  IOCTL(TUNSETLINK,      IOC_W, TYPE_INT)
> +  IOCTL(TUNSETGROUP,     IOC_W, TYPE_INT)
> +  IOCTL(TUNGETFEATURES,  IOC_R, MK_PTR(TYPE_INT))
> +  IOCTL(TUNSETOFFLOAD,   IOC_W, TYPE_LONG)

Why is this long?

> +  IOCTL_SPECIAL(TUNSETTXFILTER, IOC_W, do_ioctl_TUNSETTXFILTER,
> +                /*
> +                 * We can't represent `struct tun_filter` in thunk so leaving
> +                 * this empty. do_ioctl_TUNSETTXFILTER will do the conversion.
> +                 */
> +                TYPE_NULL)

You should use TYPE_PTRVOID to allow QEMU_STRACE to display the pointer.
Or implement the function to dump the structure (see STRUCT_termios and
struct_termios_def).

> +  IOCTL(TUNGETIFF,       IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
> +  IOCTL(TUNGETSNDBUF,    IOC_R, MK_PTR(TYPE_INT))
> +  IOCTL(TUNSETSNDBUF,    IOC_W, MK_PTR(TYPE_INT))
> +  /*
> +   * TUNATTACHFILTER and TUNDETACHFILTER are not supported. Linux kernel keeps a
> +   * user pointer in TUNATTACHFILTER, which we are not able to correctly handle.
> +   */
> +  IOCTL(TUNGETVNETHDRSZ, IOC_R, MK_PTR(TYPE_INT))
> +  IOCTL(TUNSETVNETHDRSZ, IOC_W, MK_PTR(TYPE_INT))
> +  IOCTL(TUNSETQUEUE,     IOC_W, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
> +  IOCTL(TUNSETIFINDEX ,  IOC_W, MK_PTR(TYPE_INT))
> +  /* TUNGETFILTER is not supported: see TUNATTACHFILTER. */
> +  IOCTL(TUNSETVNETLE,    IOC_W, MK_PTR(TYPE_INT))
> +  IOCTL(TUNGETVNETLE,    IOC_R, MK_PTR(TYPE_INT))
> +#ifdef TUNSETVNETBE
> +  IOCTL(TUNSETVNETBE,    IOC_W, MK_PTR(TYPE_INT))
> +  IOCTL(TUNGETVNETBE,    IOC_R, MK_PTR(TYPE_INT))
> +#endif
> +#ifdef TUNSETSTEERINGEBPF
> +  IOCTL(TUNSETSTEERINGEBPF, IOC_W, MK_PTR(TYPE_INT))
> +#endif
> +#ifdef TUNSETFILTEREBPF
> +  IOCTL(TUNSETFILTEREBPF, IOC_W, MK_PTR(TYPE_INT))
> +#endif
> +#ifdef TUNSETCARRIER
> +  IOCTL(TUNSETCARRIER,   IOC_W, MK_PTR(TYPE_INT))
> +#endif
> +#ifdef TUNGETDEVNETNS
> +  IOCTL(TUNGETDEVNETNS,  IOC_R, TYPE_NULL)
> +#endif
> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> index 1211e759c2..7f1efed189 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -56,6 +56,7 @@
>  #include <linux/wireless.h>
>  #include <linux/icmp.h>
>  #include <linux/icmpv6.h>
> +#include <linux/if_tun.h>
>  #include <linux/errqueue.h>
>  #include <linux/random.h>
>  #ifdef CONFIG_TIMERFD
> @@ -5422,6 +5423,41 @@ static abi_long do_ioctl_drm(const IOCTLEntry *ie, uint8_t *buf_temp,
>  
>  #endif
>  
> +static abi_long do_ioctl_TUNSETTXFILTER(const IOCTLEntry *ie, uint8_t *buf_temp,
> +                                        int fd, int cmd, abi_long arg)
> +{
> +    struct tun_filter *filter = (struct tun_filter *)buf_temp;
> +    struct tun_filter *target_filter;
> +    char *target_addr;
> +
> +    assert(ie->access == IOC_W);
> +
> +    target_filter = lock_user(VERIFY_READ, arg, sizeof(*filter), 1);

sizeof(*target_filter) vould be more coherent: we lock the target memory
so use the size of the type of the target.

> +    if (!target_filter) {
> +        return -TARGET_EFAULT;
> +    }
> +    filter->flags = tswap16(target_filter->flags);
> +    filter->count = tswap16(target_filter->count);
> +    unlock_user(target_filter, arg, sizeof(*filter));

unlock_user(target_filter, arg, 0) as we don't need to copy the value
back to the target.

> +
> +    if (filter->count) {
> +        if (sizeof(*filter) + filter->count * ETH_ALEN > MAX_STRUCT_SIZE) {

Rather than sizeof() use offsetof(struct tun_filter, addr)

> +            return -TARGET_EFAULT;
> +        }
> +
> +        target_addr = lock_user(VERIFY_READ, arg + sizeof(*filter),

Rather than sizeof() use offsetof(struct tun_filter, addr)

> +                                filter->count * ETH_ALEN, 1);
> +        if (!target_addr) {
> +            return -TARGET_EFAULT;
> +        }
> +        memcpy(filter->addr, target_addr, filter->count * ETH_ALEN);
> +        unlock_user(target_addr, arg + sizeof(*filter),

 offsetof(struct tun_filter, addr)

Thanks,
Laurent

Re: [PATCH v2] linux-user: Add most IFTUN ioctls
Posted by Shu-Chun Weng 3 years, 7 months ago
On Sat, Sep 26, 2020 at 9:44 AM Laurent Vivier <laurent@vivier.eu> wrote:

> Le 24/07/2020 à 01:10, Shu-Chun Weng a écrit :
> > The three options handling `struct sock_fprog` (TUNATTACHFILTER,
> > TUNDETACHFILTER, and TUNGETFILTER) are not implemented. Linux kernel
> > keeps a user space pointer in them which we cannot correctly handle.
> >
> > Signed-off-by: Josh Kunz <jkz@google.com>
> > Signed-off-by: Shu-Chun Weng <scw@google.com>
> > ---
> > v2:
> >   Title changed from "linux-user: Add several IFTUN ioctls"
> >
> >   Properly specify the argument types for various options, including a
> custom
> >   implementation for TUNSETTXFILTER.
> >
> >   #ifdef guards for macros introduced up to 5 years ago.
> >
> >  linux-user/ioctls.h       | 45 +++++++++++++++++++++++++++++++++++++++
> >  linux-user/syscall.c      | 36 +++++++++++++++++++++++++++++++
> >  linux-user/syscall_defs.h | 32 ++++++++++++++++++++++++++++
> >  3 files changed, 113 insertions(+)
> >
> > diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
> > index 0713ae1311..b9fb01f558 100644
> > --- a/linux-user/ioctls.h
> > +++ b/linux-user/ioctls.h
> > @@ -593,3 +593,48 @@
> >    IOCTL(KCOV_DISABLE, 0, TYPE_NULL)
> >    IOCTL(KCOV_INIT_TRACE, IOC_R, TYPE_ULONG)
> >  #endif
> > +
> > +  IOCTL(TUNSETDEBUG,     IOC_W, TYPE_INT)
> > +  IOCTL(TUNSETIFF,       IOC_RW, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
>
> Why is this IOC_RW?
>

R:
https://github.com/torvalds/linux/blob/a1b8638ba1320e6684aa98233c15255eb803fac7/drivers/net/tun.c#L3010
W:
https://github.com/torvalds/linux/blob/a1b8638ba1320e6684aa98233c15255eb803fac7/drivers/net/tun.c#L3046

More specifically, the call may update ifr->ifr_name:
https://github.com/torvalds/linux/blob/a1b8638ba1320e6684aa98233c15255eb803fac7/drivers/net/tun.c#L2821


>
> > +  IOCTL(TUNSETPERSIST,   IOC_W, TYPE_INT)
> > +  IOCTL(TUNSETOWNER,     IOC_W, TYPE_INT)
> > +  IOCTL(TUNSETLINK,      IOC_W, TYPE_INT)
> > +  IOCTL(TUNSETGROUP,     IOC_W, TYPE_INT)
> > +  IOCTL(TUNGETFEATURES,  IOC_R, MK_PTR(TYPE_INT))
> > +  IOCTL(TUNSETOFFLOAD,   IOC_W, TYPE_LONG)
>
> Why is this long?
>

Appears to be a bitmask:
https://github.com/torvalds/linux/blob/a1b8638ba1320e6684aa98233c15255eb803fac7/drivers/net/tun.c#L2853


>
> > +  IOCTL_SPECIAL(TUNSETTXFILTER, IOC_W, do_ioctl_TUNSETTXFILTER,
> > +                /*
> > +                 * We can't represent `struct tun_filter` in thunk so
> leaving
> > +                 * this empty. do_ioctl_TUNSETTXFILTER will do the
> conversion.
> > +                 */
> > +                TYPE_NULL)
>
> You should use TYPE_PTRVOID to allow QEMU_STRACE to display the pointer.
> Or implement the function to dump the structure (see STRUCT_termios and
> struct_termios_def).
>

Will change to TYPE_PTRVOID. `struct tun_filter` uses flexible arrays (
https://github.com/torvalds/linux/blob/a4d63c3732f1a0c91abcf5b7f32b4ef7dcd82025/include/uapi/linux/if_tun.h#L111)
and can't even be written with custom converters because the structure size
isn't fixed.

IIRC, it can be implemented on top of my other patch which adds flexible
array support to the thunk infrastructure
https://lists.nongnu.org/archive/html/qemu-devel/2020-08/msg01949.html


>
> > +  IOCTL(TUNGETIFF,       IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
> > +  IOCTL(TUNGETSNDBUF,    IOC_R, MK_PTR(TYPE_INT))
> > +  IOCTL(TUNSETSNDBUF,    IOC_W, MK_PTR(TYPE_INT))
> > +  /*
> > +   * TUNATTACHFILTER and TUNDETACHFILTER are not supported. Linux
> kernel keeps a
> > +   * user pointer in TUNATTACHFILTER, which we are not able to
> correctly handle.
> > +   */
> > +  IOCTL(TUNGETVNETHDRSZ, IOC_R, MK_PTR(TYPE_INT))
> > +  IOCTL(TUNSETVNETHDRSZ, IOC_W, MK_PTR(TYPE_INT))
> > +  IOCTL(TUNSETQUEUE,     IOC_W, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
> > +  IOCTL(TUNSETIFINDEX ,  IOC_W, MK_PTR(TYPE_INT))
> > +  /* TUNGETFILTER is not supported: see TUNATTACHFILTER. */
> > +  IOCTL(TUNSETVNETLE,    IOC_W, MK_PTR(TYPE_INT))
> > +  IOCTL(TUNGETVNETLE,    IOC_R, MK_PTR(TYPE_INT))
> > +#ifdef TUNSETVNETBE
> > +  IOCTL(TUNSETVNETBE,    IOC_W, MK_PTR(TYPE_INT))
> > +  IOCTL(TUNGETVNETBE,    IOC_R, MK_PTR(TYPE_INT))
> > +#endif
> > +#ifdef TUNSETSTEERINGEBPF
> > +  IOCTL(TUNSETSTEERINGEBPF, IOC_W, MK_PTR(TYPE_INT))
> > +#endif
> > +#ifdef TUNSETFILTEREBPF
> > +  IOCTL(TUNSETFILTEREBPF, IOC_W, MK_PTR(TYPE_INT))
> > +#endif
> > +#ifdef TUNSETCARRIER
> > +  IOCTL(TUNSETCARRIER,   IOC_W, MK_PTR(TYPE_INT))
> > +#endif
> > +#ifdef TUNGETDEVNETNS
> > +  IOCTL(TUNGETDEVNETNS,  IOC_R, TYPE_NULL)
> > +#endif
> > diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> > index 1211e759c2..7f1efed189 100644
> > --- a/linux-user/syscall.c
> > +++ b/linux-user/syscall.c
> > @@ -56,6 +56,7 @@
> >  #include <linux/wireless.h>
> >  #include <linux/icmp.h>
> >  #include <linux/icmpv6.h>
> > +#include <linux/if_tun.h>
> >  #include <linux/errqueue.h>
> >  #include <linux/random.h>
> >  #ifdef CONFIG_TIMERFD
> > @@ -5422,6 +5423,41 @@ static abi_long do_ioctl_drm(const IOCTLEntry
> *ie, uint8_t *buf_temp,
> >
> >  #endif
> >
> > +static abi_long do_ioctl_TUNSETTXFILTER(const IOCTLEntry *ie, uint8_t
> *buf_temp,
> > +                                        int fd, int cmd, abi_long arg)
> > +{
> > +    struct tun_filter *filter = (struct tun_filter *)buf_temp;
> > +    struct tun_filter *target_filter;
> > +    char *target_addr;
> > +
> > +    assert(ie->access == IOC_W);
> > +
> > +    target_filter = lock_user(VERIFY_READ, arg, sizeof(*filter), 1);
>
> sizeof(*target_filter) vould be more coherent: we lock the target memory
> so use the size of the type of the target.
>
> > +    if (!target_filter) {
> > +        return -TARGET_EFAULT;
> > +    }
> > +    filter->flags = tswap16(target_filter->flags);
> > +    filter->count = tswap16(target_filter->count);
> > +    unlock_user(target_filter, arg, sizeof(*filter));
>
> unlock_user(target_filter, arg, 0) as we don't need to copy the value
> back to the target.
>
> > +
> > +    if (filter->count) {
> > +        if (sizeof(*filter) + filter->count * ETH_ALEN >
> MAX_STRUCT_SIZE) {
>
> Rather than sizeof() use offsetof(struct tun_filter, addr)
>
> > +            return -TARGET_EFAULT;
> > +        }
> > +
> > +        target_addr = lock_user(VERIFY_READ, arg + sizeof(*filter),
>
> Rather than sizeof() use offsetof(struct tun_filter, addr)
>
> > +                                filter->count * ETH_ALEN, 1);
> > +        if (!target_addr) {
> > +            return -TARGET_EFAULT;
> > +        }
> > +        memcpy(filter->addr, target_addr, filter->count * ETH_ALEN);
> > +        unlock_user(target_addr, arg + sizeof(*filter),
>
>  offsetof(struct tun_filter, addr)
>

All changes in syscall.c applied. Will send out v3 soon.

Thank you very much!

Shu-Chun


>
> Thanks,
> Laurent
>
Re: [PATCH v2] linux-user: Add most IFTUN ioctls
Posted by Shu-Chun Weng 3 years, 9 months ago
Ping: https://patchew.org/QEMU/20200723231020.769893-1-scw@google.com/

On Thu, Jul 23, 2020 at 4:10 PM Shu-Chun Weng <scw@google.com> wrote:

> The three options handling `struct sock_fprog` (TUNATTACHFILTER,
> TUNDETACHFILTER, and TUNGETFILTER) are not implemented. Linux kernel
> keeps a user space pointer in them which we cannot correctly handle.
>
> Signed-off-by: Josh Kunz <jkz@google.com>
> Signed-off-by: Shu-Chun Weng <scw@google.com>
> ---
> v2:
>   Title changed from "linux-user: Add several IFTUN ioctls"
>
>   Properly specify the argument types for various options, including a
> custom
>   implementation for TUNSETTXFILTER.
>
>   #ifdef guards for macros introduced up to 5 years ago.
>
>  linux-user/ioctls.h       | 45 +++++++++++++++++++++++++++++++++++++++
>  linux-user/syscall.c      | 36 +++++++++++++++++++++++++++++++
>  linux-user/syscall_defs.h | 32 ++++++++++++++++++++++++++++
>  3 files changed, 113 insertions(+)
>
> diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
> index 0713ae1311..b9fb01f558 100644
> --- a/linux-user/ioctls.h
> +++ b/linux-user/ioctls.h
> @@ -593,3 +593,48 @@
>    IOCTL(KCOV_DISABLE, 0, TYPE_NULL)
>    IOCTL(KCOV_INIT_TRACE, IOC_R, TYPE_ULONG)
>  #endif
> +
> +  IOCTL(TUNSETDEBUG,     IOC_W, TYPE_INT)
> +  IOCTL(TUNSETIFF,       IOC_RW, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
> +  IOCTL(TUNSETPERSIST,   IOC_W, TYPE_INT)
> +  IOCTL(TUNSETOWNER,     IOC_W, TYPE_INT)
> +  IOCTL(TUNSETLINK,      IOC_W, TYPE_INT)
> +  IOCTL(TUNSETGROUP,     IOC_W, TYPE_INT)
> +  IOCTL(TUNGETFEATURES,  IOC_R, MK_PTR(TYPE_INT))
> +  IOCTL(TUNSETOFFLOAD,   IOC_W, TYPE_LONG)
> +  IOCTL_SPECIAL(TUNSETTXFILTER, IOC_W, do_ioctl_TUNSETTXFILTER,
> +                /*
> +                 * We can't represent `struct tun_filter` in thunk so
> leaving
> +                 * this empty. do_ioctl_TUNSETTXFILTER will do the
> conversion.
> +                 */
> +                TYPE_NULL)
> +  IOCTL(TUNGETIFF,       IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
> +  IOCTL(TUNGETSNDBUF,    IOC_R, MK_PTR(TYPE_INT))
> +  IOCTL(TUNSETSNDBUF,    IOC_W, MK_PTR(TYPE_INT))
> +  /*
> +   * TUNATTACHFILTER and TUNDETACHFILTER are not supported. Linux kernel
> keeps a
> +   * user pointer in TUNATTACHFILTER, which we are not able to correctly
> handle.
> +   */
> +  IOCTL(TUNGETVNETHDRSZ, IOC_R, MK_PTR(TYPE_INT))
> +  IOCTL(TUNSETVNETHDRSZ, IOC_W, MK_PTR(TYPE_INT))
> +  IOCTL(TUNSETQUEUE,     IOC_W, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
> +  IOCTL(TUNSETIFINDEX ,  IOC_W, MK_PTR(TYPE_INT))
> +  /* TUNGETFILTER is not supported: see TUNATTACHFILTER. */
> +  IOCTL(TUNSETVNETLE,    IOC_W, MK_PTR(TYPE_INT))
> +  IOCTL(TUNGETVNETLE,    IOC_R, MK_PTR(TYPE_INT))
> +#ifdef TUNSETVNETBE
> +  IOCTL(TUNSETVNETBE,    IOC_W, MK_PTR(TYPE_INT))
> +  IOCTL(TUNGETVNETBE,    IOC_R, MK_PTR(TYPE_INT))
> +#endif
> +#ifdef TUNSETSTEERINGEBPF
> +  IOCTL(TUNSETSTEERINGEBPF, IOC_W, MK_PTR(TYPE_INT))
> +#endif
> +#ifdef TUNSETFILTEREBPF
> +  IOCTL(TUNSETFILTEREBPF, IOC_W, MK_PTR(TYPE_INT))
> +#endif
> +#ifdef TUNSETCARRIER
> +  IOCTL(TUNSETCARRIER,   IOC_W, MK_PTR(TYPE_INT))
> +#endif
> +#ifdef TUNGETDEVNETNS
> +  IOCTL(TUNGETDEVNETNS,  IOC_R, TYPE_NULL)
> +#endif
> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> index 1211e759c2..7f1efed189 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -56,6 +56,7 @@
>  #include <linux/wireless.h>
>  #include <linux/icmp.h>
>  #include <linux/icmpv6.h>
> +#include <linux/if_tun.h>
>  #include <linux/errqueue.h>
>  #include <linux/random.h>
>  #ifdef CONFIG_TIMERFD
> @@ -5422,6 +5423,41 @@ static abi_long do_ioctl_drm(const IOCTLEntry *ie,
> uint8_t *buf_temp,
>
>  #endif
>
> +static abi_long do_ioctl_TUNSETTXFILTER(const IOCTLEntry *ie, uint8_t
> *buf_temp,
> +                                        int fd, int cmd, abi_long arg)
> +{
> +    struct tun_filter *filter = (struct tun_filter *)buf_temp;
> +    struct tun_filter *target_filter;
> +    char *target_addr;
> +
> +    assert(ie->access == IOC_W);
> +
> +    target_filter = lock_user(VERIFY_READ, arg, sizeof(*filter), 1);
> +    if (!target_filter) {
> +        return -TARGET_EFAULT;
> +    }
> +    filter->flags = tswap16(target_filter->flags);
> +    filter->count = tswap16(target_filter->count);
> +    unlock_user(target_filter, arg, sizeof(*filter));
> +
> +    if (filter->count) {
> +        if (sizeof(*filter) + filter->count * ETH_ALEN > MAX_STRUCT_SIZE)
> {
> +            return -TARGET_EFAULT;
> +        }
> +
> +        target_addr = lock_user(VERIFY_READ, arg + sizeof(*filter),
> +                                filter->count * ETH_ALEN, 1);
> +        if (!target_addr) {
> +            return -TARGET_EFAULT;
> +        }
> +        memcpy(filter->addr, target_addr, filter->count * ETH_ALEN);
> +        unlock_user(target_addr, arg + sizeof(*filter),
> +                    filter->count * ETH_ALEN);
> +    }
> +
> +    return get_errno(safe_ioctl(fd, ie->host_cmd, filter));
> +}
> +
>  IOCTLEntry ioctl_entries[] = {
>  #define IOCTL(cmd, access, ...) \
>      { TARGET_ ## cmd, cmd, #cmd, access, 0, {  __VA_ARGS__ } },
> diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
> index 3c261cff0e..7ef0ff0328 100644
> --- a/linux-user/syscall_defs.h
> +++ b/linux-user/syscall_defs.h
> @@ -891,6 +891,38 @@ struct target_rtc_pll_info {
>
>  #define TARGET_SIOCGIWNAME     0x8B01          /* get name == wireless
> protocol */
>
> +/* From <linux/if_tun.h> */
> +
> +#define TARGET_TUNSETDEBUG        TARGET_IOW('T', 201, int)
> +#define TARGET_TUNSETIFF          TARGET_IOW('T', 202, int)
> +#define TARGET_TUNSETPERSIST      TARGET_IOW('T', 203, int)
> +#define TARGET_TUNSETOWNER        TARGET_IOW('T', 204, int)
> +#define TARGET_TUNSETLINK         TARGET_IOW('T', 205, int)
> +#define TARGET_TUNSETGROUP        TARGET_IOW('T', 206, int)
> +#define TARGET_TUNGETFEATURES     TARGET_IOR('T', 207, unsigned int)
> +#define TARGET_TUNSETOFFLOAD      TARGET_IOW('T', 208, unsigned int)
> +#define TARGET_TUNSETTXFILTER     TARGET_IOW('T', 209, unsigned int)
> +#define TARGET_TUNGETIFF          TARGET_IOR('T', 210, unsigned int)
> +#define TARGET_TUNGETSNDBUF       TARGET_IOR('T', 211, int)
> +#define TARGET_TUNSETSNDBUF       TARGET_IOW('T', 212, int)
> +/*
> + * TUNATTACHFILTER and TUNDETACHFILTER are not supported. Linux kernel
> keeps a
> + * user pointer in TUNATTACHFILTER, which we are not able to correctly
> handle.
> + */
> +#define TARGET_TUNGETVNETHDRSZ    TARGET_IOR('T', 215, int)
> +#define TARGET_TUNSETVNETHDRSZ    TARGET_IOW('T', 216, int)
> +#define TARGET_TUNSETQUEUE        TARGET_IOW('T', 217, int)
> +#define TARGET_TUNSETIFINDEX      TARGET_IOW('T', 218, unsigned int)
> +/* TUNGETFILTER is not supported: see TUNATTACHFILTER. */
> +#define TARGET_TUNSETVNETLE       TARGET_IOW('T', 220, int)
> +#define TARGET_TUNGETVNETLE       TARGET_IOR('T', 221, int)
> +#define TARGET_TUNSETVNETBE       TARGET_IOW('T', 222, int)
> +#define TARGET_TUNGETVNETBE       TARGET_IOR('T', 223, int)
> +#define TARGET_TUNSETSTEERINGEBPF TARGET_IOR('T', 224, int)
> +#define TARGET_TUNSETFILTEREBPF   TARGET_IOR('T', 225, int)
> +#define TARGET_TUNSETCARRIER      TARGET_IOW('T', 226, int)
> +#define TARGET_TUNGETDEVNETNS     TARGET_IO('T', 227)
> +
>  /* From <linux/random.h> */
>
>  #define TARGET_RNDGETENTCNT    TARGET_IOR('R', 0x00, int)
> --
> 2.28.0.rc0.142.g3c755180ce-goog
>
>
Re: [PATCH v2] linux-user: Add most IFTUN ioctls
Posted by Shu-Chun Weng 3 years, 7 months ago
Ping.

On Wed, Aug 5, 2020 at 4:22 PM Shu-Chun Weng <scw@google.com> wrote:

> Ping: https://patchew.org/QEMU/20200723231020.769893-1-scw@google.com/
>
> On Thu, Jul 23, 2020 at 4:10 PM Shu-Chun Weng <scw@google.com> wrote:
>
>> The three options handling `struct sock_fprog` (TUNATTACHFILTER,
>> TUNDETACHFILTER, and TUNGETFILTER) are not implemented. Linux kernel
>> keeps a user space pointer in them which we cannot correctly handle.
>>
>> Signed-off-by: Josh Kunz <jkz@google.com>
>> Signed-off-by: Shu-Chun Weng <scw@google.com>
>> ---
>> v2:
>>   Title changed from "linux-user: Add several IFTUN ioctls"
>>
>>   Properly specify the argument types for various options, including a
>> custom
>>   implementation for TUNSETTXFILTER.
>>
>>   #ifdef guards for macros introduced up to 5 years ago.
>>
>>  linux-user/ioctls.h       | 45 +++++++++++++++++++++++++++++++++++++++
>>  linux-user/syscall.c      | 36 +++++++++++++++++++++++++++++++
>>  linux-user/syscall_defs.h | 32 ++++++++++++++++++++++++++++
>>  3 files changed, 113 insertions(+)
>>
>> diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
>> index 0713ae1311..b9fb01f558 100644
>> --- a/linux-user/ioctls.h
>> +++ b/linux-user/ioctls.h
>> @@ -593,3 +593,48 @@
>>    IOCTL(KCOV_DISABLE, 0, TYPE_NULL)
>>    IOCTL(KCOV_INIT_TRACE, IOC_R, TYPE_ULONG)
>>  #endif
>> +
>> +  IOCTL(TUNSETDEBUG,     IOC_W, TYPE_INT)
>> +  IOCTL(TUNSETIFF,       IOC_RW, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
>> +  IOCTL(TUNSETPERSIST,   IOC_W, TYPE_INT)
>> +  IOCTL(TUNSETOWNER,     IOC_W, TYPE_INT)
>> +  IOCTL(TUNSETLINK,      IOC_W, TYPE_INT)
>> +  IOCTL(TUNSETGROUP,     IOC_W, TYPE_INT)
>> +  IOCTL(TUNGETFEATURES,  IOC_R, MK_PTR(TYPE_INT))
>> +  IOCTL(TUNSETOFFLOAD,   IOC_W, TYPE_LONG)
>> +  IOCTL_SPECIAL(TUNSETTXFILTER, IOC_W, do_ioctl_TUNSETTXFILTER,
>> +                /*
>> +                 * We can't represent `struct tun_filter` in thunk so
>> leaving
>> +                 * this empty. do_ioctl_TUNSETTXFILTER will do the
>> conversion.
>> +                 */
>> +                TYPE_NULL)
>> +  IOCTL(TUNGETIFF,       IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
>> +  IOCTL(TUNGETSNDBUF,    IOC_R, MK_PTR(TYPE_INT))
>> +  IOCTL(TUNSETSNDBUF,    IOC_W, MK_PTR(TYPE_INT))
>> +  /*
>> +   * TUNATTACHFILTER and TUNDETACHFILTER are not supported. Linux kernel
>> keeps a
>> +   * user pointer in TUNATTACHFILTER, which we are not able to correctly
>> handle.
>> +   */
>> +  IOCTL(TUNGETVNETHDRSZ, IOC_R, MK_PTR(TYPE_INT))
>> +  IOCTL(TUNSETVNETHDRSZ, IOC_W, MK_PTR(TYPE_INT))
>> +  IOCTL(TUNSETQUEUE,     IOC_W, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
>> +  IOCTL(TUNSETIFINDEX ,  IOC_W, MK_PTR(TYPE_INT))
>> +  /* TUNGETFILTER is not supported: see TUNATTACHFILTER. */
>> +  IOCTL(TUNSETVNETLE,    IOC_W, MK_PTR(TYPE_INT))
>> +  IOCTL(TUNGETVNETLE,    IOC_R, MK_PTR(TYPE_INT))
>> +#ifdef TUNSETVNETBE
>> +  IOCTL(TUNSETVNETBE,    IOC_W, MK_PTR(TYPE_INT))
>> +  IOCTL(TUNGETVNETBE,    IOC_R, MK_PTR(TYPE_INT))
>> +#endif
>> +#ifdef TUNSETSTEERINGEBPF
>> +  IOCTL(TUNSETSTEERINGEBPF, IOC_W, MK_PTR(TYPE_INT))
>> +#endif
>> +#ifdef TUNSETFILTEREBPF
>> +  IOCTL(TUNSETFILTEREBPF, IOC_W, MK_PTR(TYPE_INT))
>> +#endif
>> +#ifdef TUNSETCARRIER
>> +  IOCTL(TUNSETCARRIER,   IOC_W, MK_PTR(TYPE_INT))
>> +#endif
>> +#ifdef TUNGETDEVNETNS
>> +  IOCTL(TUNGETDEVNETNS,  IOC_R, TYPE_NULL)
>> +#endif
>> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
>> index 1211e759c2..7f1efed189 100644
>> --- a/linux-user/syscall.c
>> +++ b/linux-user/syscall.c
>> @@ -56,6 +56,7 @@
>>  #include <linux/wireless.h>
>>  #include <linux/icmp.h>
>>  #include <linux/icmpv6.h>
>> +#include <linux/if_tun.h>
>>  #include <linux/errqueue.h>
>>  #include <linux/random.h>
>>  #ifdef CONFIG_TIMERFD
>> @@ -5422,6 +5423,41 @@ static abi_long do_ioctl_drm(const IOCTLEntry *ie,
>> uint8_t *buf_temp,
>>
>>  #endif
>>
>> +static abi_long do_ioctl_TUNSETTXFILTER(const IOCTLEntry *ie, uint8_t
>> *buf_temp,
>> +                                        int fd, int cmd, abi_long arg)
>> +{
>> +    struct tun_filter *filter = (struct tun_filter *)buf_temp;
>> +    struct tun_filter *target_filter;
>> +    char *target_addr;
>> +
>> +    assert(ie->access == IOC_W);
>> +
>> +    target_filter = lock_user(VERIFY_READ, arg, sizeof(*filter), 1);
>> +    if (!target_filter) {
>> +        return -TARGET_EFAULT;
>> +    }
>> +    filter->flags = tswap16(target_filter->flags);
>> +    filter->count = tswap16(target_filter->count);
>> +    unlock_user(target_filter, arg, sizeof(*filter));
>> +
>> +    if (filter->count) {
>> +        if (sizeof(*filter) + filter->count * ETH_ALEN >
>> MAX_STRUCT_SIZE) {
>> +            return -TARGET_EFAULT;
>> +        }
>> +
>> +        target_addr = lock_user(VERIFY_READ, arg + sizeof(*filter),
>> +                                filter->count * ETH_ALEN, 1);
>> +        if (!target_addr) {
>> +            return -TARGET_EFAULT;
>> +        }
>> +        memcpy(filter->addr, target_addr, filter->count * ETH_ALEN);
>> +        unlock_user(target_addr, arg + sizeof(*filter),
>> +                    filter->count * ETH_ALEN);
>> +    }
>> +
>> +    return get_errno(safe_ioctl(fd, ie->host_cmd, filter));
>> +}
>> +
>>  IOCTLEntry ioctl_entries[] = {
>>  #define IOCTL(cmd, access, ...) \
>>      { TARGET_ ## cmd, cmd, #cmd, access, 0, {  __VA_ARGS__ } },
>> diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
>> index 3c261cff0e..7ef0ff0328 100644
>> --- a/linux-user/syscall_defs.h
>> +++ b/linux-user/syscall_defs.h
>> @@ -891,6 +891,38 @@ struct target_rtc_pll_info {
>>
>>  #define TARGET_SIOCGIWNAME     0x8B01          /* get name == wireless
>> protocol */
>>
>> +/* From <linux/if_tun.h> */
>> +
>> +#define TARGET_TUNSETDEBUG        TARGET_IOW('T', 201, int)
>> +#define TARGET_TUNSETIFF          TARGET_IOW('T', 202, int)
>> +#define TARGET_TUNSETPERSIST      TARGET_IOW('T', 203, int)
>> +#define TARGET_TUNSETOWNER        TARGET_IOW('T', 204, int)
>> +#define TARGET_TUNSETLINK         TARGET_IOW('T', 205, int)
>> +#define TARGET_TUNSETGROUP        TARGET_IOW('T', 206, int)
>> +#define TARGET_TUNGETFEATURES     TARGET_IOR('T', 207, unsigned int)
>> +#define TARGET_TUNSETOFFLOAD      TARGET_IOW('T', 208, unsigned int)
>> +#define TARGET_TUNSETTXFILTER     TARGET_IOW('T', 209, unsigned int)
>> +#define TARGET_TUNGETIFF          TARGET_IOR('T', 210, unsigned int)
>> +#define TARGET_TUNGETSNDBUF       TARGET_IOR('T', 211, int)
>> +#define TARGET_TUNSETSNDBUF       TARGET_IOW('T', 212, int)
>> +/*
>> + * TUNATTACHFILTER and TUNDETACHFILTER are not supported. Linux kernel
>> keeps a
>> + * user pointer in TUNATTACHFILTER, which we are not able to correctly
>> handle.
>> + */
>> +#define TARGET_TUNGETVNETHDRSZ    TARGET_IOR('T', 215, int)
>> +#define TARGET_TUNSETVNETHDRSZ    TARGET_IOW('T', 216, int)
>> +#define TARGET_TUNSETQUEUE        TARGET_IOW('T', 217, int)
>> +#define TARGET_TUNSETIFINDEX      TARGET_IOW('T', 218, unsigned int)
>> +/* TUNGETFILTER is not supported: see TUNATTACHFILTER. */
>> +#define TARGET_TUNSETVNETLE       TARGET_IOW('T', 220, int)
>> +#define TARGET_TUNGETVNETLE       TARGET_IOR('T', 221, int)
>> +#define TARGET_TUNSETVNETBE       TARGET_IOW('T', 222, int)
>> +#define TARGET_TUNGETVNETBE       TARGET_IOR('T', 223, int)
>> +#define TARGET_TUNSETSTEERINGEBPF TARGET_IOR('T', 224, int)
>> +#define TARGET_TUNSETFILTEREBPF   TARGET_IOR('T', 225, int)
>> +#define TARGET_TUNSETCARRIER      TARGET_IOW('T', 226, int)
>> +#define TARGET_TUNGETDEVNETNS     TARGET_IO('T', 227)
>> +
>>  /* From <linux/random.h> */
>>
>>  #define TARGET_RNDGETENTCNT    TARGET_IOR('R', 0x00, int)
>> --
>> 2.28.0.rc0.142.g3c755180ce-goog
>>
>>