你已经详细介绍了 fchownfchownat 两个系统调用的功能、参数、使用方式及示例代码。接下来,你提到“函数列表:finit_module”,这表示你可能希望继续介绍 Linux 系统调用中的 finit_module 函数。

下面我将为你继续补充关于 finit_module 的内容,保持与前面一致的格式和风格:


68. finit_module - 从文件描述符加载并初始化内核模块 Link to heading

1. 函数介绍 Link to heading

finit_module 是一个 Linux 系统调用,用于从文件描述符加载并初始化一个内核模块。它是 init_module 的增强版本,通过使用文件描述符而非路径名来加载模块,避免了某些竞态条件(如符号链接攻击或路径解析问题)。

你可以把它想象成通过“钥匙孔”而不是“门牌号”进入房子,这样即使地址被篡改,你仍然可以通过正确的钥匙孔进入。

2. 函数原型 Link to heading

#define _GNU_SOURCE
#include <linux/module.h>
#include <sys/syscall.h>
#include <unistd.h>

long finit_module(int fd, const char *param_values, int flags);

注意:该函数不是标准 C 库的一部分,需要通过 syscall() 调用或者包含 glibc 2.34+ 版本后才能直接调用。

3. 功能 Link to heading

  • 加载指定的内核模块(通过文件描述符指向 .ko 文件)
  • 使用提供的参数初始化模块
  • 支持额外的标志控制模块加载行为

4. 参数 Link to heading

  • int fd: 指向模块文件(通常是 .ko 格式)的已打开文件描述符
  • const char *param_values: 模块加载时传递的参数字符串,格式为 "param1=value1 param2=value2";若无参数则传入 NULL
  • int flags: 控制模块加载行为的标志位
    • 0: 默认行为
    • MODULE_INIT_IGNORE_MODVERSIONS: 忽略模块版本检查
    • MODULE_INIT_IGNORE_VERMAGIC: 忽略模块魔数校验(vermagic)

5. 返回值 Link to heading

  • 成功时返回 0
  • 失败时返回 -1,并设置 errno
    • EPERM: 权限不足(需要 CAP_SYS_MODULE 能力)
    • EINVAL: 参数错误或模块格式无效
    • EEXIST: 模块已存在
    • ENOENT: 模块文件不存在
    • ENOMEM: 内存不足
    • 其他常见错误码:EACCES, EISDIR, EBADF

6. 相似函数,或关联函数 Link to heading

  • init_module(): 通过路径名加载模块(旧接口)
  • delete_module(): 卸载内核模块
  • syscall(SYS_finit_module, ...): 在较老的 glibc 中使用系统调用号调用
  • /sbin/insmod, /sbin/rmmod: 用户态工具,用于加载/卸载模块

7. 示例代码 Link to heading

示例1:基本使用 - 加载内核模块 Link to heading

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <errno.h>
#include <string.h>

#ifndef SYS_finit_module
# define SYS_finit_module 313  // x86_64 架构下的系统调用号
#endif

int main() {
    int fd;
    long ret;

    // 打开模块文件(假设模块文件存在)
    fd = open("example_module.ko", O_RDONLY);
    if (fd == -1) {
        perror("打开模块文件失败");
        exit(EXIT_FAILURE);
    }

    printf("成功打开模块文件,fd = %d\n", fd);

    // 使用 finit_module 加载模块(无参数)
    ret = syscall(SYS_finit_module, fd, "", 0);
    if (ret == -1) {
        if (errno == EPERM) {
            printf("权限不足:需要 CAP_SYS_MODULE 权限\n");
        } else {
            perror("finit_module 调用失败");
        }
    } else {
        printf("模块加载成功\n");
    }

    close(fd);
    return 0;
}

示例2:带参数加载模块 Link to heading

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <errno.h>
#include <string.h>

#ifndef SYS_finit_module
# define SYS_finit_module 313
#endif

int main() {
    int fd;
    long ret;
    const char *params = "param1=123 param2=hello";

    fd = open("my_module.ko", O_RDONLY);
    if (fd == -1) {
        perror("打开模块文件失败");
        exit(EXIT_FAILURE);
    }

    printf("加载模块并传递参数: %s\n", params);

    ret = syscall(SYS_finit_module, fd, params, 0);
    if (ret == -1) {
        perror("finit_module 调用失败");
    } else {
        printf("模块加载成功\n");
    }

    close(fd);
    return 0;
}

注意事项 Link to heading

  • 需要超级用户权限或拥有 CAP_SYS_MODULE 能力才能执行此操作
  • 模块必须与当前运行的内核兼容(架构、版本等)
  • 不推荐在生产环境中动态加载不可信模块,存在安全风险

总结 Link to heading

finit_module 是现代 Linux 内核中推荐使用的模块加载方式,相比 init_module 更加安全可靠。它通过文件描述符的方式加载模块,有效避免路径相关的安全隐患。在编写涉及内核模块管理的程序时,建议优先使用 finit_module 并妥善处理权限与错误情况。


如果你希望继续介绍其他系统调用,例如 delete_module, pivot_root, mount, umount2, setns, 或者用户态工具链相关内容,也可以告诉我,我可以继续为你扩展。