代码资格 - libc 的 qsort 中有符号和无符号值之间的比较
Qualification of code - comparisons between signed and unsigned values in libc's qsort
在对安全关键代码进行验证传递时 - 并且应该在嵌入式设备中执行 - 我看到 GCC 和 Clang 都发出了一些警告。警告很容易重现,因为有问题的代码来自开源 BSD libc:
$ wget https://raw.githubusercontent.com/freebsd/\
freebsd/af3e10e5a78d3af8cef6088748978c6c612757f0/lib/libc/stdlib/qsort.c
$ gcc -c -Wall -Wsign-compare -DI_AM_QSORT_R -Wall qsort.c
qsort.c: In function 'qsort_r':
qsort.c:45:24: warning: comparison between signed and unsigned
integer expressions [-Wsign-compare]
#define MIN(a, b) ((a) < (b) ? a : b)
^
qsort.c:186:6: note: in expansion of macro 'MIN'
r = MIN(pd - pc, pn - pd - es);
^
qsort.c:45:34: warning: signed and unsigned type in conditional
expression [-Wsign-compare]
#define MIN(a, b) ((a) < (b) ? a : b)
^
qsort.c:186:6: note: in expansion of macro 'MIN'
r = MIN(pd - pc, pn - pd - es);
了解这些警告(从 GCC 和 CLang 发出)...
pd
、pc
和pn
是指针
es
是size_t
(即unsigned
)
可以说,C 处理签名和未签名实体之间比较的规则可以充分表达为 DON'T, FOR THE LOVE OF GOD [1]。
但在这种情况下,qsort
的 BSD 实现比较...
- 两个指针相减的结果(有符号类型,
ptrdiff_t
)
- 减去两个指针的结果,减去
es
- 这是无符号的。
为什么从 ptrdiff_t
中减去 unsigned
值(即带符号的值)会得到 unsigned
值?您可以在上面引用的 post 中阅读相关内容。可以这么说,对于单词大小的实体,signed OPERATOR unsigned
的任何表达式都会导致 unsigned
类型。
所以,长话短说,要让 GCC 和 CLang 停止抱怨,这些行必须从...
r = MIN(pd - pc, pn - pd - es);
至:
r = MIN((unsigned)(pd - pc), pn - pd - es)
或:
r = MIN(pd - pc, (signed)(pn - pd - es));
问题是……什么是正确的补丁?
[1] "Why Not Mix Signed and Unsigned Values in C/C++?", http://blog.regehr.org/archives/268
很明显我一个人在这里:-)
我对这个问题的回答来自于一种非常简单的看待事物的方式...
如果您在左侧应用无符号转换并重新编译,目标文件不会更改(通过 MD5 和验证)。如果您改用右侧(签名),则目标文件确实会发生变化 - 这意味着此变化(至少在汇编级别 - 对于 amd64 和 sparcs)偏离了原始代码所做的。
简短回答:我继续转换为无符号 (size_t
)。这也是 Apple seems to have done(感谢指点,@alk)。
在对安全关键代码进行验证传递时 - 并且应该在嵌入式设备中执行 - 我看到 GCC 和 Clang 都发出了一些警告。警告很容易重现,因为有问题的代码来自开源 BSD libc:
$ wget https://raw.githubusercontent.com/freebsd/\
freebsd/af3e10e5a78d3af8cef6088748978c6c612757f0/lib/libc/stdlib/qsort.c
$ gcc -c -Wall -Wsign-compare -DI_AM_QSORT_R -Wall qsort.c
qsort.c: In function 'qsort_r':
qsort.c:45:24: warning: comparison between signed and unsigned
integer expressions [-Wsign-compare]
#define MIN(a, b) ((a) < (b) ? a : b)
^
qsort.c:186:6: note: in expansion of macro 'MIN'
r = MIN(pd - pc, pn - pd - es);
^
qsort.c:45:34: warning: signed and unsigned type in conditional
expression [-Wsign-compare]
#define MIN(a, b) ((a) < (b) ? a : b)
^
qsort.c:186:6: note: in expansion of macro 'MIN'
r = MIN(pd - pc, pn - pd - es);
了解这些警告(从 GCC 和 CLang 发出)...
pd
、pc
和pn
是指针es
是size_t
(即unsigned
)
可以说,C 处理签名和未签名实体之间比较的规则可以充分表达为 DON'T, FOR THE LOVE OF GOD [1]。
但在这种情况下,qsort
的 BSD 实现比较...
- 两个指针相减的结果(有符号类型,
ptrdiff_t
) - 减去两个指针的结果,减去
es
- 这是无符号的。
为什么从 ptrdiff_t
中减去 unsigned
值(即带符号的值)会得到 unsigned
值?您可以在上面引用的 post 中阅读相关内容。可以这么说,对于单词大小的实体,signed OPERATOR unsigned
的任何表达式都会导致 unsigned
类型。
所以,长话短说,要让 GCC 和 CLang 停止抱怨,这些行必须从...
r = MIN(pd - pc, pn - pd - es);
至:
r = MIN((unsigned)(pd - pc), pn - pd - es)
或:
r = MIN(pd - pc, (signed)(pn - pd - es));
问题是……什么是正确的补丁?
[1] "Why Not Mix Signed and Unsigned Values in C/C++?", http://blog.regehr.org/archives/268
很明显我一个人在这里:-)
我对这个问题的回答来自于一种非常简单的看待事物的方式...
如果您在左侧应用无符号转换并重新编译,目标文件不会更改(通过 MD5 和验证)。如果您改用右侧(签名),则目标文件确实会发生变化 - 这意味着此变化(至少在汇编级别 - 对于 amd64 和 sparcs)偏离了原始代码所做的。
简短回答:我继续转换为无符号 (size_t
)。这也是 Apple seems to have done(感谢指点,@alk)。