安全地添加整数,并certificate安全性
编程课程作业要求
- 写一个(安全)函数,添加两个整数,和
- 表明该function是安全的。
以下代码代表我的解决方案。 我不是C标准(或正式validation方法)的专家。 所以我想问:有更好(或不同)的解决方案吗?
谢谢
#include /* Try to add integers op1 and op2. Return 0 (success) or 1 (overflow prevented). In case of success, write the sum to res. */ int safe_int_add(int * res, int op1, int op2) { if (op2 op2 */ /* 0 < - op2 */ /* INT_MIN < - op2 + INT_MIN */ /* INT_MIN < INT_MIN - op2 */ /* INT_MIN = INT_MIN */ /* - op2 <= - INT_MIN */ /* INT_MIN - op2 <= - INT_MIN + INT_MIN */ /* INT_MIN - op2 <= 0 */ /* INT_MIN - op2 <= INT_MAX */ /* */ /** Hence, we have: ***************************************/ /* */ /* INT_MIN <= INT_MIN - op2 <= INT_MAX */ /* */ /* ie the following subtraction does not overflow. */ /* */ /***********************************************************/ if (op1 < INT_MIN - op2) { return 1; } /** We have: *********************************/ /* */ /* INT_MIN - op2 <= op1 */ /* INT_MIN <= op1 + op2 */ /* */ /** Also, we have: ***************************/ /* */ /* op2 < 0 */ /* op1 + op2 < op1 */ /* op1 + op2 < INT_MAX */ /* op1 + op2 <= INT_MAX */ /* */ /** Hence, we have: **************************/ /* */ /* INT_MIN <= op1 + op2 = 0 */ /* - op2 <= 0 */ /* INT_MAX - op2 = op2 */ /* - INT_MAX <= - op2 */ /* INT_MAX - INT_MAX <= - op2 + INT_MAX */ /* 0 <= - op2 + INT_MAX */ /* 0 <= INT_MAX - op2 */ /* INT_MIN <= INT_MAX - op2 */ /* */ /** Hence, we have: ***************************************/ /* */ /* INT_MIN <= INT_MAX - op2 INT_MAX - op2) { return 1; } /** We have: *********************************/ /* */ /* op1 <= INT_MAX - op2 */ /* op1 + op2 <= INT_MAX */ /* */ /** Also, we have: ***************************/ /* */ /* 0 <= op2 */ /* op1 <= op2 + op1 */ /* INT_MIN <= op2 + op1 */ /* INT_MIN <= op1 + op2 */ /* */ /** Hence, we have: **************************/ /* */ /* INT_MIN <= op1 + op2 <= INT_MAX */ /* */ /* ie the addition does not overflow. */ /* */ /**********************************************/ } *res = op1 + op2; return 0; }
OP的方法可以最佳地移植到int
类型中以及安全 – 没有任何int
组合的未定义行为(UB)。 它独立于特定的int
格式(2的补码,2的补码,符号幅度)。
在C中, int
overflow /(下溢)是未定义的行为。 所以代码,如果保持int
,必须事先确定溢出潜力。 当op1
正时, INT_MAX - op1
不能溢出。 此外,如果op1
负,则INT_MIN - op1
不会溢出。 因此,在正确计算和测试边缘的情况下, op1 + op2
不会溢出。
// Minor re-write: int safe_int_add(int * res, int op1, int op2) { assert(res != NULL); if (op1 >= 0) { if (op2 > INT_MAX - op1) return 1; } else { if (op2 < INT_MIN - op1) return 1; } *res = op1 + op2; return 0; }
也可以看看
如果知道更广泛的类型 ,代码可以使用
int safe_int_add_wide(int * res, int op1, int op2) { int2x sum = (int2x) op1 + op2; if (sum < INT_MIN || sum > INT_MAX) return 1; *res = (int) sum; return 0; }
使用unsigned
等的方法首先需要限定UINT_MAX
> = INT_MAX - INT_MIN
。 这通常是正确的,但不能通过C标准保证。
我就是这样做的:
如果输入参数具有不同的符号,那么结果总是可计算的而没有任何溢出的风险。
如果两个输入参数都是负数,则计算-safe_int_add(res, -op1, -op2);
。 (你需要检查op1
或op2
不是2s赞美中最大的负数)。
需要思考的function是添加两个正数的function:将两个输入转换为无符号类型。 添加那些。 这保证不会溢出无符号类型,因为你可以在unsigned int中存储(至少)两倍大的值而不是在signed int中(对于1s补码来说恰好是两倍,比2s补码多一次)。
然后,如果无符号值足够小,则只尝试转换回签名。
您可以查看JDK 8的实现,它可以很好地参考来自Henry S. Warren,Jr。的Hacker’s Delight一书:
http://hg.openjdk.java.net/jdk8/jdk8/jdk/rev/b971b51bec01
public static int addExact(int x, int y) { int r = x + y; // HD 2-12 Overflow iff both arguments have the opposite sign of the result if (((x ^ r) & (y ^ r)) < 0) { throw new ArithmeticException("integer overflow"); } return r; }
在我的本书中,虽然是第2-13章。 在那里你可以找到关于整个问题的冗长解释。