强制关闭文件描述符的最安全方法

强制关闭文件描述符的最安全方法

有时您需要卸载文件系统或分离循环设备,但这是busy因为打开的文件描述符,也许是因为smb服务器进程。

要强制卸载,您可以终止有问题的进程(或尝试kill -SIGTERM),但这会关闭smb连接(即使它打开的某些文件不需要关闭)。

描述了一种强制进程关闭给定文件描述符的巧妙方法这里用于gdb调用close(fd).然而,这似乎很危险。如果关闭的描述符被回收怎么办?该进程可能会使用旧的存储描述符,但没有意识到它现在引用了一个完全不同的文件。

我有一个想法,但不知道它有什么样的缺陷:使用gdb,打开/dev/nullO_WRONLY编辑:建议作为更好的替代方案的评论O_PATH),然后dup2关闭有问题的文件描述符并重用其描述符/dev/null。这样,对文件描述符的任何读取或写入都将失败。

像这样:

sudo gdb -p 234532
(gdb) set $dummy_fd = open("/dev/null", 0x200000) // O_PATH
(gdb) p dup2($dummy_fd, offending_fd)
(gdb) p close($dummy_fd)
(gdb) detach
(gdb) quit

可能会出什么问题?

答案1

摆弄流程gdb几乎从来都不安全,但如果出现紧急情况并且流程需要保持开放并且了解所有风险和涉及的代码,则可能有必要。

大多数情况下,我会简单地终止该进程,尽管某些情况可能有所不同,并且可能取决于环境、谁拥有所涉及的相关系统和进程、该进程正在做什么、是否有关于“可以杀死它”或“的文档”不,先联系某某”等等。一旦尘埃落定,这些细节可能需要在事后分析会议上制定。如果有计划的迁移,最好提前检查是否有任何进程打开了有问题的文件描述符,以便可以在非紧急情况下处理这些文件描述符(cron 作业或仅在迁移时凌晨运行的其他计划任务)如果您只在白天检查,可能会很容易错过)。

只写与读取与读写

您重新打开文件描述符的想法O_WRONLY是有问题的,因为并非所有文件描述符都是只写的。 John Viega 和 Matt Messier 在《C 和 C++ 安全编程指南》一书中采用了更细致的方法,以不同于标准输出和标准错误的方式处理标准输入(第 25 页,“安全管理文件描述符”):

static int open_devnull(int fd) {
  FILE *f = 0;

  if (!fd) f = freopen(_PATH_DEVNULL, "rb", stdin);
  else if (fd == 1) f = freopen(_PATH_DEVNULL, "wb", stdout);
  else if (fd == 2) f = freopen(_PATH_DEVNULL, "wb", stderr);
  return (f && fileno(f) == fd);
}

在这种情况下,需要检查gdb描述符(或句柄)是否是只读、读写或只写,并在 上打开适当的替换。如果不是,如果进程尝试从中读取,曾经只读的句柄现在变成了只写,将会导致不必要的错误。FILE */dev/null

可能会出现什么问题?

当其文件描述符(也可能是 FILE *句柄)在幕后被篡改时,进程的行为究竟如何将取决于进程,并且如果该描述符永远不会用于“噩梦模式”,则该描述符将与“没什么大不了”有所不同。由于未刷新的数据、没有文件正确关闭指示器或其他一些意外问题,某处的文件已损坏。

对于句柄,在关闭句柄之前FILE *添加调用可能会有所帮助,或者可能会导致双缓冲或其他一些问题;这是 在不确切知道源代码的作用和期望的情况下fflush(3)进行随机调用的几个危险之一。软件还可能在 描述符或句柄gdb之上构建额外的复杂层,这些层也可能需要处理。猴子修补代码可以很容易地变成活动扳手。fdFILE *

概括

向进程发送标准终止信号应该给它一个正确关闭资源的机会,就像系统正常关闭一样。摆弄流程gdb可能不会正确地结束事情,并且可能会使情况变得更糟。

答案2

使用 O_WRONLY 打开 /dev/null,然后使用 dup2 关闭有问题的文件描述符并将其描述符重新用于 /dev/null。这样,对文件描述符的任何读取或写入都将失败。

如果您将描述符复制到/dev/null,则任何写入都不会失败, 但成功,并且读取将成功并返回 0 (eof)。

这可能是也可能不是您想要的。

O_WRONLY|O_RDWR在 Linux 上,您还可以使用 flags = 3(又名)打开文件,O_NOACCESS这将导致任何读取或写入失败,并显示EBADF.

该文件仅适用于 ioctl——这带来了其他答案和评论中未提及的危险:读取和写入并不是对文件描述符进行的唯一操作。 (lseek或者呢ftruncate?)。

更新:

我发现了比未记录的更好的东西O_WRONLY|O_RDWRO_PATH = 010000000 / 0x200000。根据 open(2) 联机帮助页:

O_PATH (since Linux 2.6.39)
     Obtain a file descriptor that can be used for two  purposes:  to
     indicate a location in the filesystem tree and to perform opera-
     tions that act purely at the file descriptor  level.   The  file
     itself  is not opened, and other file operations (e.g., read(2),
     write(2), fchmod(2), fchown(2), fgetxattr(2), mmap(2)) fail with
     the error EBADF.

    The  following operations can be performed on the resulting file
     descriptor:

    *  close(2); fchdir(2) (since Linux 3.5); fstat(2) (since  Linux
        3.6).

    *  Duplicating  the  file  descriptor (dup(2), fcntl(2) F_DUPFD,
        etc.).

相关内容