在执行exec()之前,程序有时需要确保关闭某些特定的文件描述符。尤其是在特权进程中调用exec()来启动一个未知程序时(并非自己编写),抑或是启动程序并不需要使用这些已打开的文件描述符时,从安全编程的角度出发,应当在加载新程序之前确保关闭那些不必要的文件描述符。对所有此类描述符施以close()调用就可达到这一目的,然而这一做法存在如下局限性。
- 某些描述符可能是有库函数打开的。但库函数无法使主程序在执行exec()之前关闭相应的文件描述符。作为基本原则,库函数应总是为其打开的文件设置执行时关闭(close-on-exec)标志
- 如果exec()因某种原因而调用失败,可能还需要时描述符保持打开状态。如果这些描述符已然关闭,讲他们重新打开并指向相同文件的难度很大,基本上不太可能。
为此,内核为每个文件描述符提供了执行时关闭标志。如果设置了这一标志,那么在成功执行exec()时,会自动关闭该文件描述符,如果调用exec()失败,文件描述符则会保存打开状态。
方法:
- 通过fcntl()系统调用来访问执行时关闭标志。例如:
int flags; flags = fcntl(fd, F_GETFD); if (flags == -1) { perror("fcntl"); } flags |= FD_CLOEXEC; if (fcntl(fd, F_SETFD, flags) == -1) { perror("fcntl"); }
- 调用open时在flags参数使用O_CLOEXEC标识位,为新的文件描述符启用close-on-flag标志。在open中使用的O_CLOEXEC和fcntl中使用的FD_CLOEXEC的作用是一样的,但是使用O_CLOEXEC标志,相对比较简单,而且在多线程程序中执行fcntl()的F_GETFD和F_SETFD操作有可能导致竞争状态。可能引发竞争的场景是:线程某甲打开一文件描述符,尝试为该描述符标记close-on-exec标志,与此同时,线程某乙执行fork()调用,然后调用exec()执行任意一个程序。(假设在某甲打开文件描述符和调用fcntl()设置close-on-exec标志之间,某乙成功执行了fork()和exec()操作。)此类竞争可能会在无意间打开的文件描述符泄露给不安全的程序。