* [PATCH] tools/nolibc: align signature of ioctl() with POSIX @ 2025-01-23 13:04 Thomas Weißschuh 2025-01-23 13:16 ` Willy Tarreau 0 siblings, 1 reply; 5+ messages in thread From: Thomas Weißschuh @ 2025-01-23 13:04 UTC (permalink / raw) To: Willy Tarreau; +Cc: linux-kernel, Thomas Weißschuh POSIX defines the signature of ioctl() as follows, to allow passing a pointer or integer without casting: int ioctl(int fildes, int request, ... /* arg */); Nolibc ioctl() expects a pointer, forcing the user to manually cast. Using va_arg to make the signature more flexible would work but seems to prevent inlining of the function. Instead use a macro. "fd" and "req" will still be typechecked through sys_ioctl(). Signed-off-by: Thomas Weißschuh <linux@weissschuh.net> --- tools/include/nolibc/sys.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h index d4a5c2399a66b200ebf7ab249569cce2285481a5..5cb2c66cc8cccc4d4a1126acfd0b30a6efc886c3 100644 --- a/tools/include/nolibc/sys.h +++ b/tools/include/nolibc/sys.h @@ -532,7 +532,7 @@ uid_t getuid(void) /* - * int ioctl(int fd, unsigned long req, void *value); + * int ioctl(int fd, unsigned long req, ... value); */ static __attribute__((unused)) @@ -541,11 +541,7 @@ int sys_ioctl(int fd, unsigned long req, void *value) return my_syscall3(__NR_ioctl, fd, req, value); } -static __attribute__((unused)) -int ioctl(int fd, unsigned long req, void *value) -{ - return __sysret(sys_ioctl(fd, req, value)); -} +#define ioctl(fd, req, value) __sysret(sys_ioctl(fd, req, (void *)(value))) /* * int kill(pid_t pid, int signal); --- base-commit: 21266b8df5224c4f677acf9f353eecc9094731f0 change-id: 20250123-nolibc-ioctl-03e86c20049a Best regards, -- Thomas Weißschuh <linux@weissschuh.net> ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH] tools/nolibc: align signature of ioctl() with POSIX 2025-01-23 13:04 [PATCH] tools/nolibc: align signature of ioctl() with POSIX Thomas Weißschuh @ 2025-01-23 13:16 ` Willy Tarreau 2025-01-23 13:24 ` Thomas Weißschuh 0 siblings, 1 reply; 5+ messages in thread From: Willy Tarreau @ 2025-01-23 13:16 UTC (permalink / raw) To: Thomas Weißschuh; +Cc: linux-kernel Hi Thomas, On Thu, Jan 23, 2025 at 02:04:29PM +0100, Thomas Weißschuh wrote: > POSIX defines the signature of ioctl() as follows, > to allow passing a pointer or integer without casting: > int ioctl(int fildes, int request, ... /* arg */); > > Nolibc ioctl() expects a pointer, forcing the user to manually cast. > Using va_arg to make the signature more flexible would work but seems to > prevent inlining of the function. Instead use a macro. "fd" and "req" > will still be typechecked through sys_ioctl(). > > Signed-off-by: Thomas Weißschuh <linux@weissschuh.net> > --- > tools/include/nolibc/sys.h | 8 ++------ > 1 file changed, 2 insertions(+), 6 deletions(-) > > diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h > index d4a5c2399a66b200ebf7ab249569cce2285481a5..5cb2c66cc8cccc4d4a1126acfd0b30a6efc886c3 100644 > --- a/tools/include/nolibc/sys.h > +++ b/tools/include/nolibc/sys.h > @@ -532,7 +532,7 @@ uid_t getuid(void) > > > /* > - * int ioctl(int fd, unsigned long req, void *value); > + * int ioctl(int fd, unsigned long req, ... value); > */ > > static __attribute__((unused)) > @@ -541,11 +541,7 @@ int sys_ioctl(int fd, unsigned long req, void *value) > return my_syscall3(__NR_ioctl, fd, req, value); > } > > -static __attribute__((unused)) > -int ioctl(int fd, unsigned long req, void *value) > -{ > - return __sysret(sys_ioctl(fd, req, value)); > -} > +#define ioctl(fd, req, value) __sysret(sys_ioctl(fd, req, (void *)(value))) You risk to get a warning about casting a pointer from an integer of a different size if you pass an int there. I think should should perform a double cast instead: #define ioctl(fd, req, value) __sysret(sys_ioctl(fd, req, (void *)(uintptr_t)(value))) That way any int should cast fine, and pointers should as well. Willy ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] tools/nolibc: align signature of ioctl() with POSIX 2025-01-23 13:16 ` Willy Tarreau @ 2025-01-23 13:24 ` Thomas Weißschuh 2025-01-23 13:51 ` Willy Tarreau 0 siblings, 1 reply; 5+ messages in thread From: Thomas Weißschuh @ 2025-01-23 13:24 UTC (permalink / raw) To: Willy Tarreau; +Cc: linux-kernel On 2025-01-23 14:16:25+0100, Willy Tarreau wrote: > Hi Thomas, > > On Thu, Jan 23, 2025 at 02:04:29PM +0100, Thomas Weißschuh wrote: > > POSIX defines the signature of ioctl() as follows, > > to allow passing a pointer or integer without casting: > > int ioctl(int fildes, int request, ... /* arg */); > > > > Nolibc ioctl() expects a pointer, forcing the user to manually cast. > > Using va_arg to make the signature more flexible would work but seems to > > prevent inlining of the function. Instead use a macro. "fd" and "req" > > will still be typechecked through sys_ioctl(). > > > > Signed-off-by: Thomas Weißschuh <linux@weissschuh.net> > > --- > > tools/include/nolibc/sys.h | 8 ++------ > > 1 file changed, 2 insertions(+), 6 deletions(-) > > > > diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h > > index d4a5c2399a66b200ebf7ab249569cce2285481a5..5cb2c66cc8cccc4d4a1126acfd0b30a6efc886c3 100644 > > --- a/tools/include/nolibc/sys.h > > +++ b/tools/include/nolibc/sys.h > > @@ -532,7 +532,7 @@ uid_t getuid(void) > > > > > > /* > > - * int ioctl(int fd, unsigned long req, void *value); > > + * int ioctl(int fd, unsigned long req, ... value); > > */ > > > > static __attribute__((unused)) > > @@ -541,11 +541,7 @@ int sys_ioctl(int fd, unsigned long req, void *value) > > return my_syscall3(__NR_ioctl, fd, req, value); > > } > > > > -static __attribute__((unused)) > > -int ioctl(int fd, unsigned long req, void *value) > > -{ > > - return __sysret(sys_ioctl(fd, req, value)); > > -} > > +#define ioctl(fd, req, value) __sysret(sys_ioctl(fd, req, (void *)(value))) > > You risk to get a warning about casting a pointer from an integer of > a different size if you pass an int there. I think should should perform > a double cast instead: > > #define ioctl(fd, req, value) __sysret(sys_ioctl(fd, req, (void *)(uintptr_t)(value))) > > That way any int should cast fine, and pointers should as well. I don't think this should ever happen. A warning there is actually useful. The POSIX signature forces users to pass something that is compatible with (void *), otherwise the vararg handling would be invalid. Thomas ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] tools/nolibc: align signature of ioctl() with POSIX 2025-01-23 13:24 ` Thomas Weißschuh @ 2025-01-23 13:51 ` Willy Tarreau 2025-01-23 16:50 ` Thomas Weißschuh 0 siblings, 1 reply; 5+ messages in thread From: Willy Tarreau @ 2025-01-23 13:51 UTC (permalink / raw) To: Thomas Weißschuh; +Cc: linux-kernel On Thu, Jan 23, 2025 at 02:24:13PM +0100, Thomas Weißschuh wrote: > On 2025-01-23 14:16:25+0100, Willy Tarreau wrote: > > Hi Thomas, > > > > On Thu, Jan 23, 2025 at 02:04:29PM +0100, Thomas Weißschuh wrote: > > > POSIX defines the signature of ioctl() as follows, > > > to allow passing a pointer or integer without casting: > > > int ioctl(int fildes, int request, ... /* arg */); > > > > > > Nolibc ioctl() expects a pointer, forcing the user to manually cast. > > > Using va_arg to make the signature more flexible would work but seems to > > > prevent inlining of the function. Instead use a macro. "fd" and "req" > > > will still be typechecked through sys_ioctl(). > > > > > > Signed-off-by: Thomas Weißschuh <linux@weissschuh.net> > > > --- > > > tools/include/nolibc/sys.h | 8 ++------ > > > 1 file changed, 2 insertions(+), 6 deletions(-) > > > > > > diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h > > > index d4a5c2399a66b200ebf7ab249569cce2285481a5..5cb2c66cc8cccc4d4a1126acfd0b30a6efc886c3 100644 > > > --- a/tools/include/nolibc/sys.h > > > +++ b/tools/include/nolibc/sys.h > > > @@ -532,7 +532,7 @@ uid_t getuid(void) > > > > > > > > > /* > > > - * int ioctl(int fd, unsigned long req, void *value); > > > + * int ioctl(int fd, unsigned long req, ... value); > > > */ > > > > > > static __attribute__((unused)) > > > @@ -541,11 +541,7 @@ int sys_ioctl(int fd, unsigned long req, void *value) > > > return my_syscall3(__NR_ioctl, fd, req, value); > > > } > > > > > > -static __attribute__((unused)) > > > -int ioctl(int fd, unsigned long req, void *value) > > > -{ > > > - return __sysret(sys_ioctl(fd, req, value)); > > > -} > > > +#define ioctl(fd, req, value) __sysret(sys_ioctl(fd, req, (void *)(value))) > > > > You risk to get a warning about casting a pointer from an integer of > > a different size if you pass an int there. I think should should perform > > a double cast instead: > > > > #define ioctl(fd, req, value) __sysret(sys_ioctl(fd, req, (void *)(uintptr_t)(value))) > > > > That way any int should cast fine, and pointers should as well. > > I don't think this should ever happen. A warning there is actually > useful. The POSIX signature forces users to pass something that is > compatible with (void *), otherwise the vararg handling would be > invalid. I'm a bit confused because while my man page says: The third argument is an untyped pointer to memory. what I'm finding in POSIX is: The type of arg depends upon the particular control request, but it shall be either an integer or a pointer to a device-specific data structure. So I'm not seeing anything insisting on the int being the size of a pointer. Even the FreeBSD man explicitly mentions char *, caddr_t or int. Thus I do think that int is expressly permitted and might exist. I mean, I don't care much, but if we try to fix something that requires a cast, to end up with users having to write "(long)i" in their code, we won't have made much progress :-/ Willy ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] tools/nolibc: align signature of ioctl() with POSIX 2025-01-23 13:51 ` Willy Tarreau @ 2025-01-23 16:50 ` Thomas Weißschuh 0 siblings, 0 replies; 5+ messages in thread From: Thomas Weißschuh @ 2025-01-23 16:50 UTC (permalink / raw) To: Willy Tarreau; +Cc: linux-kernel On 2025-01-23 14:51:47+0100, Willy Tarreau wrote: > On Thu, Jan 23, 2025 at 02:24:13PM +0100, Thomas Weißschuh wrote: > > On 2025-01-23 14:16:25+0100, Willy Tarreau wrote: > > > Hi Thomas, > > > > > > On Thu, Jan 23, 2025 at 02:04:29PM +0100, Thomas Weißschuh wrote: > > > > POSIX defines the signature of ioctl() as follows, > > > > to allow passing a pointer or integer without casting: > > > > int ioctl(int fildes, int request, ... /* arg */); > > > > > > > > Nolibc ioctl() expects a pointer, forcing the user to manually cast. > > > > Using va_arg to make the signature more flexible would work but seems to > > > > prevent inlining of the function. Instead use a macro. "fd" and "req" > > > > will still be typechecked through sys_ioctl(). > > > > > > > > Signed-off-by: Thomas Weißschuh <linux@weissschuh.net> > > > > --- > > > > tools/include/nolibc/sys.h | 8 ++------ > > > > 1 file changed, 2 insertions(+), 6 deletions(-) > > > > > > > > diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h > > > > index d4a5c2399a66b200ebf7ab249569cce2285481a5..5cb2c66cc8cccc4d4a1126acfd0b30a6efc886c3 100644 > > > > --- a/tools/include/nolibc/sys.h > > > > +++ b/tools/include/nolibc/sys.h > > > > @@ -532,7 +532,7 @@ uid_t getuid(void) > > > > > > > > > > > > /* > > > > - * int ioctl(int fd, unsigned long req, void *value); > > > > + * int ioctl(int fd, unsigned long req, ... value); > > > > */ > > > > > > > > static __attribute__((unused)) > > > > @@ -541,11 +541,7 @@ int sys_ioctl(int fd, unsigned long req, void *value) > > > > return my_syscall3(__NR_ioctl, fd, req, value); > > > > } > > > > > > > > -static __attribute__((unused)) > > > > -int ioctl(int fd, unsigned long req, void *value) > > > > -{ > > > > - return __sysret(sys_ioctl(fd, req, value)); > > > > -} > > > > +#define ioctl(fd, req, value) __sysret(sys_ioctl(fd, req, (void *)(value))) > > > > > > You risk to get a warning about casting a pointer from an integer of > > > a different size if you pass an int there. I think should should perform > > > a double cast instead: > > > > > > #define ioctl(fd, req, value) __sysret(sys_ioctl(fd, req, (void *)(uintptr_t)(value))) > > > > > > That way any int should cast fine, and pointers should as well. > > > > I don't think this should ever happen. A warning there is actually > > useful. The POSIX signature forces users to pass something that is > > compatible with (void *), otherwise the vararg handling would be > > invalid. > > I'm a bit confused because while my man page says: > > The third argument is an untyped pointer to memory. > > what I'm finding in POSIX is: > > The type of arg depends upon the particular control request, but it shall > be either an integer or a pointer to a device-specific data structure. > > So I'm not seeing anything insisting on the int being the size of a > pointer. Even the FreeBSD man explicitly mentions char *, caddr_t or > int. Thus I do think that int is expressly permitted and might exist. Normally ioctl() is implemented with va_arg. Inside ioctl() a type has to be passed to va_arg(). This is a pointer/uintptr_t. In stdarg(3) the following note is present for va_arg(): If there is no next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), random errors will occur. This would be the case if an "int" argument is interpreted by va_arg(void *). I can observe this with the test program below on arm64. va_list used pointers internally and dereferencing an int pointer as long pointer also yields random other bytes. *But* the ioctl handler knows that the argument should have been an int all along and should cast away the extra garbage bytes. > I mean, I don't care much, but if we try to fix something that requires > a cast, to end up with users having to write "(long)i" in their code, > we won't have made much progress :-/ Instead of a double cast, we should change the type of sys_ioctl to long sys_ioctl(unsigned int, unsigned int, unsigned long); which is the actual syscall signature. Then a single cast is enough for pointers and integers: #define ioctl(fd, req, value) __sysret(sys_ioctl(fd, req, (unsigned long)(value))) Test program: #include <stdarg.h> #include <stdio.h> static void foo(int count, ...) { unsigned long val; va_list ap; va_start(ap, count); for (int i = 0; i < count; i ++) { val = va_arg(ap, unsigned long); if (val != 0x05060708) printf("%d: 0x%lx\n", i, val); } va_end(ap); } int main(void) { unsigned int vals[] = { 0x01020304, 0x05060708, 0x090B0E01, }; foo(20, vals[1], vals[1], vals[1], vals[1], vals[1], vals[1], vals[1], vals[1], vals[1], vals[1], vals[1], vals[1], vals[1], vals[1], vals[1], vals[1], vals[1], vals[1], vals[1], vals[1] ); } ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-01-23 16:50 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-01-23 13:04 [PATCH] tools/nolibc: align signature of ioctl() with POSIX Thomas Weißschuh 2025-01-23 13:16 ` Willy Tarreau 2025-01-23 13:24 ` Thomas Weißschuh 2025-01-23 13:51 ` Willy Tarreau 2025-01-23 16:50 ` Thomas Weißschuh
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.