i33*_*36_ 7 c glibc scanf undefined-behavior
我目前正在将一些 HTTP 处理构建到 C 程序(在 Linux 上使用 glibc 编译)中,该程序将位于 nginx 实例后面,并认为sscanf在这种情况下我应该安全地将参数标记化推迟到。
我很高兴地发现从 URI 中提取查询非常简单:
char *path = "/events?a=1&b=2&c=3";
char query[64] = {0};
sscanf(path, "%*[^?]?%64s HTTP", query); // query = "a=1&b=2&c=3"
Run Code Online (Sandbox Code Playgroud)
但我很惊讶的速度有多快的事情成为我??????ň???? T'ê????? R'E'ST ??? ???我???ň?g ^ :(
int pos = -1;
char arg[32] = {0}, value[32] = {0};
int c = sscanf(query, "%32[^=]=%32[^&]&%n", &arg, &value, &pos);
Run Code Online (Sandbox Code Playgroud)
对于 的输入a=1&b=2,我得到arg="a", value="1", c=2, pos=4。完美:我现在可以重新运行 sscanfpath + pos以获得下一个参数。为什么我在这里?
好了,而a=1&的行为相同以上,a=1生产arg="a",value="1",c=2,和pos=-1。我该怎么办?
争夺文档,我读到了
n Nothing is expected; instead, the number of characters consumed
thus far from the input is stored through the next pointer,
which must be a pointer to int. This is not a conversion and
does not increase the count returned by the function. The as?
signment can be suppressed with the * assignment-suppression
character, but the effect on the return value is undefined.
Therefore %*n conversions should not be used.
Run Code Online (Sandbox Code Playgroud)
其中超过 50% 的段落是指簿记细节。没有讨论我所看到的行为。
在谷歌搜索结果中徘徊,我很快找到了 Wikipedia 的Scanf_format_string条目(这是最热门的),但是,呃...
Oookay...我觉得我在这里使用了一个没有人真正关注的功能的风滚草。这并没有激发我剩余的信心。
考虑看看什么似乎是在那里%n被vfscanf-internal.c实现,我发现代码(线),60%涉及到有关讨论的标准不一致,39.6%是实施细节,和0.4%的实际代码(其中包括完整的“ done++;”)。
*似乎* glibc 的行为是保持内部值done(我使用 访问%n)不变 - 或者更确切地说,未定义 - 除非某些操作改变它。似乎%n以这种方式使用是不可预见的,而且我完全处于“这里有龙”的领域?:(
我不认为我会使用scanf...
为了完整起见,这里总结了我所看到的内容。
#include <stdio.h>
void test(const char *str) {
int pos = -1;
char arg[32] = {0}, value[32] = {0};
int c = sscanf(str, "%32[^=]=%32[^&]&%n", (char *)&arg, (char *)&value, &pos);
printf("\"%s\": c=%d arg=\"%s\" value=\"%s\" pos=%d\n", str, c, arg, value, pos);
}
int main() {
test("a=1&b=2"); // "a=1&b=2": c=2 arg="a" value="1" pos=4
test("a=1&"); // "a=1&": c=2 arg="a" value="1" pos=4
test("a=1"); // "a=1": c=2 arg="a" value="1" pos=-1
}
Run Code Online (Sandbox Code Playgroud)
我认为 C 标准保证pos您示例中的值保持不变。
C17 7.21.6.2 说,描述fscanf:
(4) fscanf 函数依次执行格式的每个指令。当所有指令都被执行,或者如果指令失败(如下详述),函数返回。失败被描述为输入失败(由于出现编码错误或输入字符不可用),或匹配失败(由于不适当的输入)。
[...]
(6) 通过读取流的下一个字符来执行作为普通多字节字符的指令。如果这些字符中的任何一个与组成指令的字符不同,则指令失败并且不同的字符和后续字符保持未读状态。类似地,如果文件结束、编码错误或读取错误阻止读取字符,则指令失败。
(此处的“多字节字符”包括普通的单字节字符,例如您的&.)
因此,在您的"a=1"示例中,指令%32[^=]、=和%32[^&]全部成功,现在已到达字符串的末尾。在 7.21.6.7 中解释了 for sscanf,“到达字符串的末尾相当于遇到 fscanf 函数的文件尾。” 因此无法读取任何字符,因此&指令失败,并sscanf返回而无需进一步执行任何操作。该%n指令从未执行过,因此没有任何有权修改pos. 因此它必须具有与之前相同的值,即 -1。
我不认为这个案子是不可预见的;只是它已经被现有规则所涵盖,所以没有人费心去明确地指出它。
| 归档时间: |
|
| 查看次数: |
55 次 |
| 最近记录: |