【问题标题】:strtok() overwrites its source stringstrtok() 覆盖其源字符串
【发布时间】:2016-09-25 19:29:32
【问题描述】:

我正在编写一个玩具 bash shell。我现在的目标是在环境中循环寻找可以找到特定命令的路径。现在我正在用“:”分隔路径(例如“/home/user/bin:home/user/.local/bin:/usr/local/sbin”等),并且对于给我的每个路径,复制那个新字符串finalPath的路径,然后将“/cmd”连接到末尾。

我的问题是,当我尝试将路径的内容复制到 finalPath 时,我对 finalPath 所做的任何更改都会反映到路径上。正如现在的代码一样,path 只会被设置为“home/user/bin”一次,循环并再次设置为相同的东西,然后标记器命中“NULL”并终止 while 循环。

这表明pathfinalPath 正在共享一个内存地址,但由于 strcpy 理论上会在内存中创建一个新副本,因此我的字符串和指针一定有问题。

知道是什么导致了这种意外行为吗?

编辑:当我注释掉 strcpy 时,此代码按预期执行

我的代码的精简版本如下:

int findpath(char* cmd, command_t* p_cmd) {
    char* path_var;

    path_var = getenv( "PATH" );

    char* path;
    char tempEnv[sizeof(path_var)];
    strcpy(tempEnv, path_var);
    path = strtok(tempEnv, ":");

    while(path != NULL) {
        char fullPath[1000];
        strcpy(finalPath, path);
        printf("path: %s\n", path);
        printf("finalPath: %s\n", finalPath);
        path = strtok(NULL, ":");
    }

【问题讨论】:

  • 是的,strtok 确实改变了源字符串。这是记录在案的行为。
  • sizeof(path_var) 不是path_var 的长度。这是指针大小。
  • Strtok 更改源字符串很好,我对 strcpy 感到困惑。编辑:一些澄清是,当我注释掉 strcpy 时,这段代码循环正常
  • finalPath 未在代码中的任何位置声明

标签: c bash pointers strtok


【解决方案1】:

BLUEPIXY 是对的:tempEnv 对您的字符串来说不够大。试试:

char *tempEnv;
tempEnv = malloc(strlen(path_var)+1);
strcpy(tempEnv, path_var);

最后

free(tempEnv);

附带条件这里到处都是漏洞。您应该使用更安全的字符串函数,例如,如 here 所述。例如,使用strnlen 强制对path_var 的长度设置一些合理的限制。确保 path_var 在该限制内以 NULL 结尾。使用strncpy 而不是strcpy。如有必要,在 strncpy 之后添加 NULL。还有许多其他规则,我在这里不包括在内,因为您的目标似乎是学习而不是生产代码。愉快的黑客攻击!

【讨论】:

  • 你认为path_var在什么情况下不会被空终止?程序怎么能告诉你?我觉得这个警告一点都不引人注目——getenv() 的输出是一个以空字符结尾的字符串。使用strncpy() 是一个混合包——当源比目标长时它不保证空终止,并且当源比目标短时它保证空填充到全长。这是一个奇怪的功能;对于安全问题,这不是一个普遍的答案(尽管如果你小心的话,它可以安全地使用)。
  • char tempEnv[strlen(path_var)+1]; 也可以
  • @JonathanLeffler 编辑澄清。我试图理解的是,当*c 不受程序控制时,while(*c++) i++; 是个坏主意。无论如何,我同意这个答案在安全字符串实践方面是不完整的——我把它留给比我更有经验的人。 :)
  • 作为更短的版本:tempEnv = strdup(path_var);。还有strndup()
  • 绝对更好。在实践中,整个环境(和命令行参数)的长度有一个相对较小的上限,有时高达 512 KiB,但通常小于此值。这意味着作为环境的一部分,过长的 PATH 并没有那么大的危险。但是,该建议很可能适用于其他限制较少的长字符串来源。这是 POSIX getline() 的一个问题,例如,您不能指定行长的上限。
猜你喜欢
  • 1970-01-01
  • 2017-01-24
  • 1970-01-01
  • 2012-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多