依赖PATH_MAX可能导致溢出

时间:2019-07-07 10:03:02

标签: c linux security x86 buffer-overflow

如您所知,

PATH_MAX是用于表示环境变量(在这种情况下为PATH变量)的最大大小的值,但是在大多数情况下并不是这样,请看下面的代码用于将环境变量复制到大小为PATH_MAX

的数组中
  char env[PATH_MAX]; 
  strcpy(path, getenv("PATH"));

如果您可以看到它很容易溢出,可以说使用strncpy这样可以避免溢出,但是如果您使用它并且PATH很大,那么MAX_PATH我们将无法完成PATH env变量,这让我开始思考如何在不应该​​溢出也不丢失数据的前提下获取PATH env变量

2 个答案:

答案 0 :(得分:5)

通常如果您根本需要复制未知长度的字符串,请使用 strncpy 复制到具有大小限制的固定大小的缓冲区中(但请注意,如果字符串太长,则不终止该字符串会带来不便。)

或使用 strdup 分配副本。


  

您知道PATH_MAX是值,用于指示环境变量(在这种情况下为PATH变量)的最大大小

不,不是;不是。与$PATH或环境无关!

这是单个文件系统路径名的最大长度。例如/foo/bar/verylongdirectoryname/x/y.txt的长度保证不超过PATH_MAX。否则,您可能无法使用绝对路径读取它,而不得不将chdir插入该树并使用相对路径。

PATH环境就像其他任何env var一样是一个平面字符串;内核不会将它们裁剪为固定长度。或当然不会限制为PATH_MAX

  

在不应该溢出也不丢失数据的前提下,如何获取PATH env变量

嗯,您已经拥有getenv("PATH") 。这将返回一个指向进程内存空间中现有字符串的指针。它在静态存储中有效;您不需要复制它,以后对getenv的调用也不会破坏指针。 (在Linux上,env vars在进程启动时位于argv上方的堆栈上。在一切移动堆栈指针之前运行的CRT启动代码将指向它们的指针存储在全局envp中,其中getenv可以找到它以后。)

如果您要为一个环境变量复制,以便可以延长它或修改它而不更改原始缓冲区,最简单的选择是strdup(3) from <string.h>来分配一个复制。(POSIX 2008,以及之前的BSD,GNU C和其他各种系统)。或strndup设置要复制的字节数限制。

GNU C甚至有一个基于alloca的版本,而不是基于malloc的版本,以防您需要便宜的自动存储临时版本。


PATH_MAX

PATH_MAX与POSIX函数(例如getwd(3) )相关,例如,您提供缓冲区但没有长度限制。 (但实际上您应该改用char *getcwd(char *buf, size_t size);请参阅与getwd相同的链接。手册页还显示:

  

请注意,在某些系统上,PATH_MAX          可能不是编译时常量;此外,其价值可能取决于          在文件系统上,请参阅pathconf(3)。


Linux readdir(3)手册页没有这么说,但是struct dirent成员char d_name[256]; /* Null-terminated filename */的大小是这样确定的,特别是因为PATH_MAX =255。(或更准确地说,由于readdir的大小限制,PATH_MAX为255。)


此外,允许chdir(2)open(2)之类的系统调用在长度超过PATH_MAXENAMETOOLONG)的路径上失败:

  

ENAMETOOLONG
                路径名或路径名的组成部分太长。

readlink(2)采用缓冲区大小,因此不需要限制链接目标的长度。只会在您提供的路径中提及ENAMETOOLONG作为可能的错误,而不是链接名称。


即使在现代Linux上,单个文件名的255个字节也是一个硬限制。但是IIRC确实选择允许更长的路径名(具有多个目录组件)。 POSIX规范在长路径上不会open()出现 require 错误,因此OS可以给出相当小的PATH_MAX,但在可能的情况下仍然可以在更长的路径上工作。

答案 1 :(得分:3)

避免假设已定义$ PATH或长度受限,这是您解决问题的方法,请使用下面的通用代码。

char *path = NULL; 
  const char *temp = getenv("PATH");
  if (temp != NULL) {
    path = (char*) malloc(strlen(temp) + 1);
    if (path == NULL) {
      /* Handle error condition */
    } else {
      strcpy(path, temp);
    }
    /* Use path */
  }