强制宏参数为字符串字面量:漏洞排查与优化方案
字符串字面量宏的边缘情况与改进方案
原宏#define SV(str) (sizeof(str) - 1)在传入非字符串字面量(如普通char指针)时,编译正常但结果完全错误。改成#define SV(str) (sizeof("" str "") - 1)后,虽能对非字面量参数触发编译错误,但仍存在以下漏洞:
1. 多参数/带逗号参数的陷阱
如果传入带逗号的参数(比如SV("a", "b")),宏展开后会变成sizeof("" "a", "b" "") - 1。由于逗号运算符优先级最低,sizeof实际计算的是最后一个表达式"b" ""的长度,结果为2-1=1,完全不符合预期,且编译不会报错。
2. 复合字面量的绕过
若传入复合字面量(如SV((char[]){"hello"})),虽然它是数组类型能通过字符串拼接检查,但复合字面量是可变的,违反了原约束中“不可变”的要求,且宏无法识别这种情况。
3. 宽字符串不兼容
如果传入宽字符串字面量(如SV(L"hello")),窄字符串与宽字符串的拼接会直接触发编译错误,若宏需要支持宽字符串,当前写法无法适配。
改进后的宏实现
结合预处理参数检查、静态断言和类型匹配,可写出更严格的宏,同时兼容标准C:
// 计算宏参数个数,用于检查是否传入单一参数 #define _SV_NUM_ARGS(...) _SV_NUM_ARGS_IMPL(__VA_ARGS__, 5,4,3,2,1,0) #define _SV_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,N,...) N // 判断参数是否为const字符串字面量(匹配不可变、数组类型的约束) #define _SV_IS_STRING_LITERAL(str) _Generic((str), \ const char[sizeof(str)]: 1, \ default: 0) #define SV(str) \ _Static_assert(_SV_NUM_ARGS(str) == 1, "SV requires exactly one string literal argument"), \ _Static_assert(_SV_IS_STRING_LITERAL(str), "Argument must be a const string literal"), \ (sizeof(str) - 1)
改进点说明
- 参数个数检查:通过
_SV_NUM_ARGS确保只能传入单一参数,避免逗号运算符导致的错误。 - 类型断言:利用
_Generic匹配const char[N]类型(字符串字面量的真实类型),排除普通指针、非const复合字面量等不符合约束的输入。 - 直接使用原sizeof逻辑:在通过所有检查后,回到
sizeof(str)-1的计算,避免字符串拼接带来的额外问题。
如果需要支持宽字符串,可以新增针对宽字符的宏:
#define _SV_IS_WIDE_STRING_LITERAL(str) _Generic((str), \ const wchar_t[sizeof(str)]: 1, \ default: 0) #define SV_W(str) \ _Static_assert(_SV_NUM_ARGS(str) == 1, "SV_W requires exactly one wide string literal argument"), \ _Static_assert(_SV_IS_WIDE_STRING_LITERAL(str), "Argument must be a const wide string literal"), \ (sizeof(str)/sizeof(wchar_t) - 1)
内容的提问来源于stack exchange,提问作者Madagascar
相关产品推荐
相关产品推荐

