首页 > 其他分享 >关注潜在的整数越界问题

关注潜在的整数越界问题

时间:2023-11-20 17:36:04浏览次数:46  
标签:compare code int 整数 越界 VALUE value Integer 潜在

在平时的开发过程中,整数越界是一个容易被忽视的问题,关注潜在的整数越界问题可使我们编写的代码更加健壮,规避因整数越界导致的 bug。

比较器

以下是在 Code Review 中发现的比较器实现:

乍一看该比较器实现不存在问题,但是如果 tag1 = Integer.MIN_VALUE = -2147483648, tag2 为大于 0 的数字如 1,则此时 tag1 - tag2 = 2147483647,但是按照 java.util.Comparator#compare 的定义,tag1 小于 tag2 时,应该返回一个负数,以上写法在遇到这样的示例数据时将导致排序结果错乱,引发相关 bug。

下面看看 Spring 中比较器的实现,在 Spring 中,提供了 @Order 注解用于指定 bean 的顺序,默认值为 Ordered.LOWEST_PRECEDENCE = Integer.MAX_VALUE,即在排序时排在最后,相关源码如下:

对应的比较器实现如下:

可知其采用的 Integer.compare 方法对两个整数进行比较操作,查看 Integer#compare 方法的源码:

/**
 * Compares two {@code int} values numerically.
 * The value returned is identical to what would be returned by:
 * <pre>
 *    Integer.valueOf(x).compareTo(Integer.valueOf(y))
 * </pre>
 *
 * @param  x the first {@code int} to compare
 * @param  y the second {@code int} to compare
 * @return the value {@code 0} if {@code x == y};
 *         a value less than {@code 0} if {@code x < y}; and
 *         a value greater than {@code 0} if {@code x > y}
 * @since 1.7
 */
public static int compare(int x, int y) {
    return (x < y) ? -1 : ((x == y) ? 0 : 1);
}

可知 java.lang.Integer#compare 并未采取 x - y 的方式进行比较,而是使用小于等于运算符直接进行比较,规避了潜在的整数越界问题。 那么文首代码正确的实现方式应为 return Integer.compare(tag1, tag2)。如果查看 JDK 中常见数值类的源码,可知均提供了静态的 compare 方法,如:java.lang.Long#compare,java.lang.Double#compare,此处不再赘述。

切量比例

以上代码是某段业务逻辑中初始切量比例实现,取余 100 的模式常用于按比例切量、按比例降级等业务场景。以上代码使用 userPin 的哈希值取余 100 判断是否小于切量比例以决定是否执行新业务逻辑,如果我们查看 java.lang.String#hashCode 的源码实现:

/**
 * Returns a hash code for this string. The hash code for a
 * {@code String} object is computed as
 * <blockquote><pre>
 * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
 * </pre></blockquote>
 * using {@code int} arithmetic, where {@code s[i]} is the
 * <i>i</i>th character of the string, {@code n} is the length of
 * the string, and {@code ^} indicates exponentiation.
 * (The hash value of the empty string is zero.)
 *
 * @return  a hash code value for this object.
 */
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

可知 java.lang.String#hashCode 本质上是对字符串进行 s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] 多项式求值,此处潜在的风险在于计算出的 hash 值可能越界,导致 userPin.hashCode() 返回值为负数,如:"jd_xxxxxxxxxxxx".hashCode() = -1406647067,且在 Java 语言中,使用负数对正数取余,是可能得到负数的。以上代码的风险在于潜在的放大了期望的切量比例,如使用以上的代码进行上线,那么当我们设定 1% 的切量比例时,会导致远超 1%的用户执行新的业务逻辑(通过采样日志发现用户 pin 集合 hashCode 值负数占比并不低),导致非预期的切量结果。

基于以上的背景,容易想到的一种修复方案为在 userPin.hashCode 外层使用 Math.abs 保证取余前的数字为正数:

以上修复方案看似不再存在问题,但是并不能保证完全正确,我们查看 Math.abs 的源码实现:

/**
 * Returns the absolute value of an {@code int} value.
 * If the argument is not negative, the argument is returned.
 * If the argument is negative, the negation of the argument is returned.
 *
 * <p>Note that if the argument is equal to the value of
 * {@link Integer#MIN_VALUE}, the most negative representable
 * {@code int} value, the result is that same value, which is
 * negative.
 *
 * @param   a   the argument whose absolute value is to be determined
 * @return  the absolute value of the argument.
 */
public static int abs(int a) {
    return (a < 0) ? -a : a;
}

可知在注释中特意提到,如果入参是 Integer.MIN_VALUE,即 int 域中最小的值时,返回值依然为 Integer.MIN_VALUE,因为 int 域的范围为 [-2147483648, 2147483647]。如果按照 JLS 中的解释,-x equals (~x)+1。那么可知:

x = Integer.MIN_VALUE:
10000000_00000000_00000000_00000000

~x:
01111111_11111111_11111111_11111111

(~x) + 1:
10000000_00000000_00000000_00000000

如果在神灯上搜索 Math.abs,可以发现有三篇文章与该函数有关,均与 Math.abs(Integer.MIN_VALUE) 依然为 Integer.MIN_VALUE 有关。而我们在 Code Review 阶段发现该问题即从根本上规避了该问题,不会使存在 bug 的代码上线。最后切量比例修改后的实现如下:

总结

  • java.lang.String#hashCode 在计算过程中可能因为整数越界导致返回值为负数
  • Java 语言中的 % 是取余而不是取模,如:(-21) % 4 = (-21) - (-21) / 4 *4 = -1
  • Math.abs(int a) 当入参是 Integer.MIN_VALUE 时返回值依然是负数 Integer.MIN_VALUE

参考

15.15.4. Unary Minus Operator -

What's the difference between “mod” and “remainder”? - Stack Overflow

Best way to make Java's modulus behave like it should with negative numbers? - Stack Overflow

OrderComparator.java · spring-projects/spring-framework

作者:京东物流 刘建设 张九龙 田爽

来源:京东云开发者社区 自猿其说Tech 转载请注明来源

标签:compare,code,int,整数,越界,VALUE,value,Integer,潜在
From: https://www.cnblogs.com/Jcloud/p/17843595.html

相关文章

  • Pwn2own 2022 Tesla 利用链 (ConnMan 堆越界写 RCE)
    Pwn2own2022Tesla利用链(ConnMan堆越界写RCE)Openingthedoorsandwindows0-clickRCEontheTeslaModel3HEXACON2022-0-clickRCEontheTeslaModel3byDavidBerard&VincentDehors漏洞分析ConnMan处理WIFIPortal时的堆越界写,这部分协议特性称作:WISP......
  • C语言基础实例:两个整数相加
    使用 scanf() 来接收输入, printf() 与 %d 格式化输出整数。运行实例实例#include<stdio.h>intmain(){ intfirstNumber,secondNumber,sumOfTwoNumbers;printf("输入两个数:"); scanf("%d%d",&firstNumber,&secondNumber);sumOfTwoNumbers=fir......
  • 【pwn】[FSCTF 2023]2str --整数溢出绕过
    检查一下保护状态接着ida看代码逻辑看func函数第一次看真没发现有什么漏洞,题目给了backdoor,虽然strlen可以\x00绕过,但是strcpy函数也限制漏洞的实现。仔细看的话,会发现v3的类型是 unsigned__int8v3;说明v3是一个字节来表示的,可表示的范围只有0~255,那这样绕过思路就很清......
  • 易基因:综合全基因组DNA甲基化和转录组学分析鉴定调控骨骼肌发育潜在基因 | 研究进展
    大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因。DNA甲基化是骨骼肌发育中关键的表观遗传调控机制。但胚胎鸭骨骼肌发育中负责DNA甲基化的调控因子仍然未知。2023年10月23日,南京农业大学动物科技学院于敏莉副教授团队在《IntJMolSci》杂志发表题为“TheIntegrat......
  • 易基因:MeRIP-seq+ChIP-seq等揭示m6A甲基化在休眠期转录休眠调控中的潜在功能
    大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因。发育通常被认为是遗传程序的顺序展开,复杂程度不断提高,并按固定轨迹随时间进行。然而,调整发育时间可以提高在不利条件下的存活率。在哺乳动物中,这表现为胚胎停育。在小鼠胚泡和胚胎干细胞中,通过抑制mTOR表达以及对Myc......
  • 【pwn】[HGAME 2023 week1]choose_the_seat --数组越界,劫持got表
    查一下程序保护情况发现是partialrelro,说明got表是可以修改的,下一步看代码逻辑看到这一段puts(&seats[16*v0]);存在数组越界的漏洞,因为上面的代码没有对v0进行负数的限制,v0可以是负数,我们来看一下seat的数据可以发现seat上面的数据就是got表,seat到exit的距离只需要传入......
  • 实践2:统计最大整数
    题目描述输入多个整数(一行),输出其中最大的整数。输入格式一行多个整数,用空格隔开。输出格式一个整数。输入输出样例输入123456输出6说明/提示最大的数为6,即输出6。答案:a=map(int,input().split())b=max(a)print(b)......
  • 整数的类型(4)整数的格式化及整数类型选择
    <1>整数的输入和输出(1)只有两种形式int或longlong%d:int;%u:unsigned;%ld:longlong;%lu:unsignedlonglong;举例:通过一段代码输出整数为unsigned的结果#include<stdio.h>intmain(){inti=-1;charc=-1;printf("i=%u,c=%u",i,c);return0;}输出结果为i=4294......
  • 反转一个整数
    #include<stdio.h>intreverse_number(intnum){intreversed=0;while(num!=0){intremainder=num%10;reversed=reversed*10+remainder;num/=10;}returnreversed;}intmain(){intnum=12345;intreversed_num......
  • 整数类型(3)
    <1>数的范围(1)整体范围:对于一个字节(8bit)可以表达的数的范围为00000000——11111111;(2)在数的范围中00000000——>0;00000001~01111111——>1~127,高位为0,在纯二进制中表示1~127;10000000~11111111——>-1~-128,高位为1,在补码表示-1~-128;计算机中所存在的一切数据都用纯二......