C语言中固定大小的数组参数
Checked-size array parameters in C

原始链接: https://lwn.net/SubscriberLink/1046840/3eb9029084cc9e1e/

最近,Linux内核开发社区讨论了改进`xchacha20poly1305_encrypt`函数安全性的问题,该函数是加密层的一个关键组件。该函数接受数组参数(如密钥和nonce),但未强制进行大小检查,这可能导致参数重新排序等危险错误。 最初,有人提出了一种涉及指针间接调用的解决方案,需要调用者修改代码。然而,开发者发现了一个鲜为人知的C语言特性——在数组参数声明中使用`static`关键字——可以在不修改现有函数调用情况下实现相同的大小检查。 虽然`static`无法捕获*所有*与大小相关的错误,但它可以防止常见的漏洞,例如参数顺序错误和数组大小不足。尽管有人担心该关键字的语法很笨拙(“一个可怕的hack”),Linus Torvalds还是批准了它的使用,并指出它已经存在于内核中。目前正在考虑使用类似于`at_least`的宏来提高可读性。这表明即使像内核这样成熟的代码库,也可以通过利用鲜为人知的C语言特性来增强安全性。

Hacker News新 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交登录 C语言中带检查大小的数组参数 (lwn.net) 10点 由 chmaynard 1小时前 | 隐藏 | 过去 | 收藏 | 1评论 aaaashley 9分钟前 [–] 关于那个n[static M]数组检查语法,它在1999年就被认为不好,当时它被包含在内:“一致投票认为这个特性很丑陋,并且普遍认为在最后一刻将其纳入标准是一个不幸的决定。” - Raymond Mak (加拿大C工作组),https://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_205.htm回复 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请YC | 联系 搜索:
相关文章

原文

By Jonathan Corbet
December 1, 2025

There are many possible programmer mistakes that are not caught by the minimal checks specified by the C language; among those is passing an array of the wrong size to a function. A recent attempt to add some safety around array parameters within the crypto layer involved the use of some clever tricks, but it turns out that clever tricks are unnecessary in this case. There is an obscure C feature that can cause this checking to happen, and it is already in use in a few places within the kernel.

The discussion started when Ard Biesheuvel sought to improve the safety of the poetically named function xchacha20poly1305_encrypt():

    void xchacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len,
			           const u8 *ad, const size_t ad_len,
			       	   const u8 nonce[XCHACHA20POLY1305_NONCE_SIZE],
			       	   const u8 key[CHACHA20POLY1305_KEY_SIZE]);

A potential problem with this function is that it takes as parameters several pointers to arrays of type u8. As Biesheuvel pointed out, the size of the nonce and key arrays is not checked by the compiler, even though it is clearly specified in the function prototype. That makes it easy to, for example, give the parameters in the wrong order. The resulting vulnerabilities are generally not the outcome developers have in mind when they write cryptographic code.

Biesheuvel suggested that it was possible to write the prototype this way instead (differences shown in bold):

    void xchacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len,
			           const u8 *ad, const size_t ad_len,
			       	   const u8 (*nonce)[XCHACHA20POLY1305_NONCE_SIZE],
			       	   const u8 (*key)[CHACHA20POLY1305_KEY_SIZE]);

The types of the last two arguments have changed; there is a new level of pointer indirection, with the argument being a pointer to an array of a given size. Callers must change their calls by adding an additional & operator to obtain the desired pointer type, but the address that is passed is the same. In this case, though, the compiler will check the sizes of the array passed, and will now catch a reordering of the arguments to the function.

The LWN kernel-source database is the definitive source of information about kernel releases. Try a one-month free trial subscription for immediate access to LWN's kernel content and KSDB as well.

Jason Donenfeld was interested by the idea, but he knew of an arguably more straightforward way to address this problem. It seems that, buried deep within the C standard, is a strange usage of the static keyword, making it possible to write the prototype as:

    void xchacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len,
			           const u8 *ad, const size_t ad_len,
			       	   const u8 nonce[static XCHACHA20POLY1305_NONCE_SIZE],
			       	   const u8 key[static CHACHA20POLY1305_KEY_SIZE]);

This, too, will cause the compiler to check the sizes of the arrays, and it does not require changes on the caller side. Unlike the pointer trick, which requires an exact match on the array size, use of static will only generate a warning if the passed-in array is too small. So it will not catch all mistakes, though it is sufficient to prevent memory-safety and swapped-argument problems.

Eric Biggers pointed out that GCC can often generate "array too small" warnings even without static, but that the kernel currently disables those warnings; that would suppress them when static is used as well. (The warning was disabled in 6.8 due to false positives.) But he thought that adding static was worthwhile to get the warnings in Clang — if Linus Torvalds would be willing to accept use of "this relatively obscure feature of C".

Torvalds, as it turns out, has no objection to this usage; he likes the feature, if not the way it was designed:

The main issue with the whole 'static' thing is just that the syntax is such a horrible hack, where people obviously picked an existing keyword that made absolutely no sense, but was also guaranteed to have no backwards compatibility issues.

He pointed out that there are a number of places in the kernel that are already using it; for a simple example, see getconsxy() in the virtual-terminal driver. He suggested perhaps hiding it with a macro like min_array_size() just to make the usage more obvious, but didn't seem convinced that it was necessary. Donenfeld followed up with a patch to that effect a few days later, but then pivoted to an at_least marker instead.

A lot of work has gone into the kernel to make its use of C as safe as possible, but that does not mean that the low-hanging fruit has all been picked. The language has features, such as static when used to define formal array parameters, that can improve safety, but which are not generally known and not often used. In this particular case, though, it would not be surprising to see this "horrible hack" come into wider use in the near future.




联系我们 contact @ memedata.com