使 VLA 比 argv[1] 稍长是否安全?

is it safe to make a VLA slightly longer than argv[1]?

示例代码,将 argv[1] 视为文件路径,将其扩展名替换为 .png 或在找到 none 时附加它:

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv) {
    if (argc != 2)
        return 1;

    char *lastdot = strrchr(argv[1], '.');
    size_t len = lastdot ? (lastdot - argv[1]) : strlen(argv[1]);

    char outpath[len + sizeof(".png")];
    // ^ this is the important part

    memcpy(outpath, argv[1], len);
    memcpy(outpath + len, ".png", sizeof(".png"));

    printf("%s\n", outpath);
}

我主要担心的是 argv[1] 可能小到不会导致 OS 本身出错,但是 ".png" 连接添加的额外 4 个字节分配VLA会不会导致栈溢出?

问题是我不太确定 argv 或 VLA 的成员的大小是如何受到限制的。

显然,对于静态数组大小,PATH_MAX 等常量不可信,因此它要么是这个,要么是 malloc

您的担心是真实的:C 标准中没有任何内容保证您的程序可用的堆栈 space 足以为一个非常长的命令行参数加上 4 个字节分配一个大的 VLA。实际上,在现代操作系统上,堆栈 space 通常至少为 1 兆字节,命令行参数限制在大约 100 KB 以内......所以你应该是安全的。

另请注意,有些编译器根本不支持 VLA,因此从堆中分配是一种更可移植的方法。

顺便说一句,您可以修改程序以完全避免此问题:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
    if (argc != 2)
        return 1;

    char *lastdot = strrchr(argv[1], '.');
    int len = lastdot ? lastdot - argv[1] : strlen(argv[1]);

    printf("%.*s.png\n", len, outpath);
    return 0;
}