我想打印传递给linux
系统调用的所有参数值。例如,在ioctl()
的情况下,我有以下原型和print语句。
asmlinkage long our_sys_ioctl(unsigned int fd , unsigned int cmd , unsigned long arg)
{
printk ("fd=%u, cmd=%u and arg=%lu \n ", fd, cmd, arg);
return original_call_ioctl(fd , cmd , arg);
}
据我所知,fd
是驱动程序文件的文件描述符,cmd
定义了驱动程序,ioctl编号,操作类型和参数大小。但我对arg
参数感到困惑,要么它是指向内存的指针,要么只是大多数文档称之为立即值的指针。
通过使用此arg
参数,如果将内存内容传递为上面给出的unsigned long arg
而不是指针,我该如何获取内存?
答案 0 :(得分:5)
ioctl的arg
参数在通用vfs级别是不透明的。如何解释它取决于实际处理它的驱动程序或文件系统。所以它可能是指向用户空间内存的指针,也可能是索引,标志等等。它甚至可能未被使用,并且通常在0中传递。
例如,查看TCSBRKP
中drivers/tty/tty_io.c
ioctl的实现:
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
//...
case TCSBRKP: /* support for POSIX tcsendbreak() */
return send_break(tty, arg ? arg*100 : 250);
您可以查看ioctl_list(2)
手册页以查看各种ioctl所采用的参数;该列表中具有int
或其他非指针参数的所有条目都是其他示例。
所以你可以做类似
的事情 void __user *argp = (void __user *) arg;
然后使用copy_from_user()
或get_user()
来读取arg
指向的内存,但如果参数不是指针则可能会失败。在通用的ioctl系统调用中,你可能真的不想拥有一个包含每个可能的ioctl的大表。
答案 1 :(得分:3)
This document,也与this answer相关联,应该在部门中更多地阐明这一点。一个有趣的摘录(由我添加粗体文字)可能如下:
在用户空间中,ioctl系统调用具有以下原型:
int ioctl(int fd, unsigned long cmd, ...);
由于点的原因,原型在Unix系统调用列表中脱颖而出,这通常将函数标记为具有可变数量的参数。但是,在实际系统中,系统调用实际上可以具有可变数量的参数。系统调用必须有一个明确定义的原型,因为用户程序只能通过硬件和门来访问它们。"因此,原型中的点表示不是可变数量的参数,而是单个可选参数,传统上标识为
char *argp
。这些点只是以防止在编译期间进行类型检查。 第三个参数的实际性质取决于发出的特定控制命令(第二个参数)。有些命令采用无参数,有些采用整数值,有些采用指向其他数据的指针。使用指针是将任意数据传递给 ioctl 调用的方法;然后,设备可以与用户空间交换任意数量的数据。ioctl 调用的非结构化性质导致它在内核开发人员中失宠。每个 ioctl 命令基本上都是一个单独的,通常是未记录的系统调用,并且无法在任何类型的综合中审核这些调用方式。使非结构化ioctl参数在所有系统上的工作方式相同也很困难;例如,考虑具有以32位模式运行的用户空间进程的64位系统。结果,通过几乎任何其他手段实施杂项控制操作的压力很大。可能的替代方法包括将命令嵌入到数据流中(我们将在本章后面讨论这种方法)或使用虚拟文件系统,sysfs或特定于驱动程序的文件系统。 (我们将在第14章中讨论sysfs。)然而,事实仍然是 ioctl 通常是真正的设备操作的最简单和最直接的选择。
这意味着没有任何方法可以理解如何将ioctl参数解释为外部观察者,而无需深入了解设备驱动程序约定/内部。从用户空间的角度看,ioctl参数是无类型,并且在内核空间中以某种方式松散地键入,因为它只是作为unsigned long
来处理以保留空间为了它。它是一个纯粹的'适合unsigned long integer
空间的数字或任何位序列,可以用作(非常短的)字符串,(小)字符数组,(小)结构 - 但要注意字节顺序和特定于体系结构尺寸 - 可以代表设备的板载芯片的操作码,甚至可以通过类型打击来处理浮动!
此外,这意味着通过将不一致的数据传递给驱动程序(不仅仅是错误的数据,而是错误类型的错误数据!),很容易弄乱,最终导致设备的未定义行为,或损坏用户空间内存(例如,通过将指针传递给读取ioctl中错误大小的结构)。
还有几行:
[...]
cmd
参数从用户传递不变,可选的arg
参数以unsigned long
的形式传递,无论是否给出由用户作为整数或指针。如果调用程序没有传递第三个参数,则驱动程序操作接收的arg
值是未定义的。因为在额外参数上禁用了类型检查,所以如果将无效参数传递给 ioctl ,编译器就不会发出警告,并且很难发现任何相关的错误。
无论如何,如果你想尝试盲人'审计设备驱动程序ioctl调用,而不查看头文件和源文件,可以尝试首先将arg作为指针处理,使用copy_from_user(),失败的可能性是立即值(或发生错误) ),然后人们可以尝试记录它的值,看看并尝试解释它(但为什么要反转ioctl而不是学习驱动程序代码?);在成功,白化知识,不同大小的内存可以被读取和记录为解码尝试(再次,毫无意义,只要有源可用,他们应该)。
更感兴趣的操作肯定会解码ioctl代码(cmd),因为它可以指向正确的方向来查找其数值绑定的驱动程序 - 应该只有一个,如果约定应用ioctl定义,无论如何,允许不同的驱动程序使用相同的魔术字符,因此使用正则表达式来grep包含' r的所有内核源文件#define
。或者' D'或类似的东西可以挑选出一些文件来检查ioctl定义,而不是匹配函数编号应该排除一些或全部错误的文件,并且寻找正确的参数大小将完成搜索。
问候。
答案 2 :(得分:2)
请记住,ioctl
的原型如下所示:
int ioctl(int fildes, unsigned long request, ...);
您只知道前两个参数是什么。根据{{3}}:
其他参数是可选的,可以从一个设备上的ioctl实现到另一个设备上的实现不等。据我所知,第三个论点总是存在,我还没有找到超过三分之一。第三个参数通常似乎是指向结构的指针。这允许在两个方向上传递任意数量的数据,数据由指针引用的结构定义,只需传递指针即可。
...但即使假设只有第三个参数,你仍然不知道它是文字值还是指向结构的指针(缺少对预期参数的请求的显式映射)。