POSIX wordexp(3) is dangerous
All TILs Homepage Blog 2026-10-25 c posix security wordexp(3) is a POSIX API for performing shell-style expansions from C. It’s often cited/recommended as a way to do tilde expansion (e.g. ~/foo to /home/me/foo):
All TILs 主页博客 2026-10-25 c posix 安全性 wordexp(3) 是一个用于从 C 语言执行 shell 风格扩展的 POSIX API。它经常被引用/推荐作为执行波浪号扩展(例如将 ~/foo 转换为 /home/me/foo)的方法:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wordexp.h>
int expand_tilde(const char *input, char **output) {
wordexp_t p;
int ret = wordexp(input, &p, 0);
if (ret == 0) {
*output = strdup(p.we_wordv[0]);
wordfree(&p);
}
return ret;
}
int main(void) {
char *expanded = NULL;
expand_tilde("~/foo", &expanded);
printf("expanded: %s\n", expanded);
return 0;
}
However, wordexp(3) is also dangerous! That last 0 argument is for the flags parameter. By default, all expansion features are enabled, including command expansion: expand_tilde(”$(whoami)/foo”, &expanded); or: expand_tilde(”$(cat /etc/passwd)/foo”, &expanded);
然而,wordexp(3) 也很危险!最后一个参数 0 是用于 flags 参数的。默认情况下,所有扩展功能都是启用的,包括命令扩展:expand_tilde(”$(whoami)/foo”, &expanded); 或者:expand_tilde(”$(cat /etc/passwd)/foo”, &expanded);
Disabling this requires passing WRDE_NOCMD as a flag. Unfortunately, this palliative at best in some libc implementations: musl implements wordexp(3) by delegating to the system shell. It adds some perfunctory scanning for command substitutions before delegating, but fails to match some patterns that the underlying shell (like GNU Bash) can treat as valid command substitutions.
禁用此功能需要传递 WRDE_NOCMD 作为标志。不幸的是,在某些 libc 实现中,这充其量只是权宜之计:musl 通过委托给系统 shell 来实现 wordexp(3)。它在委托之前会进行一些敷衍的命令替换扫描,但无法匹配底层 shell(如 GNU Bash)可以视为有效命令替换的某些模式。
macOS’s libc implements wordexp(3) in a similar manner, delegating to /usr/lib/system/wordexp-helper1. According to Julian Squires this had the same bypass as musl as recently as 2023, although I didn’t bother testing whether it’s still effective now that the default macOS shell is zsh instead of bash.
macOS 的 libc 以类似的方式实现 wordexp(3),委托给 /usr/lib/system/wordexp-helper1。据 Julian Squires 称,直到 2023 年,它还存在与 musl 相同的绕过漏洞,尽管我没有费心去测试在 macOS 的默认 shell 从 bash 变为 zsh 后,它是否仍然有效。
Bottom line: don’t use wordexp(3) on untrusted inputs, even with WRDE_NOCMD. Previously, macOS’s wordexp(3) would shell out to Perl. wordexp-helper appears to be the “modern” solution. ↩
底线:不要在不受信任的输入上使用 wordexp(3),即使使用了 WRDE_NOCMD。以前,macOS 的 wordexp(3) 会调用 Perl。wordexp-helper 似乎是“现代”的解决方案。↩