diff --git a/dubbo-common/pom.xml b/dubbo-common/pom.xml index 98ecc9b87b2..43ebf6209c6 100644 --- a/dubbo-common/pom.xml +++ b/dubbo-common/pom.xml @@ -77,5 +77,39 @@ de.ruedigermoeller fst + + org.checkerframework + checker + 2.8.1 + + + org.checkerframework + jdk8 + 2.8.1 + - \ No newline at end of file + + + + maven-compiler-plugin + 3.3 + + 1.8 + 1.8 + + 10000 + 10000 + + + org.checkerframework.checker.index.IndexChecker + + + -Xbootclasspath/p:$CHECKERFRAMEWORK/checker/dist/jdk8.jar + -AprintErrorStack + -AonlyDefs=^org\.apache\.dubbo\.common\.io\. + + + + + + diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/io/Bytes.java b/dubbo-common/src/main/java/org/apache/dubbo/common/io/Bytes.java index 263e7b9f67c..9e928140ab1 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/io/Bytes.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/io/Bytes.java @@ -30,6 +30,11 @@ import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; +import org.checkerframework.checker.index.qual.IndexOrHigh; +import org.checkerframework.checker.index.qual.LTLengthOf; +import org.checkerframework.checker.index.qual.NonNegative; +import org.checkerframework.common.value.qual.MinLen; + /** * CodecUtils. */ @@ -55,7 +60,7 @@ private Bytes() { * @param length new length. * @return new byte array. */ - public static byte[] copyOf(byte[] src, int length) { + public static byte[] copyOf(byte[] src, @NonNegative int length) { byte[] dest = new byte[length]; System.arraycopy(src, 0, dest, 0, Math.min(src.length, length)); return dest; @@ -79,7 +84,7 @@ public static byte[] short2bytes(short v) { * @param v value. * @param b byte array. */ - public static void short2bytes(short v, byte[] b) { + public static void short2bytes(short v, byte @MinLen(2) [] b) { short2bytes(v, b, 0); } @@ -89,7 +94,7 @@ public static void short2bytes(short v, byte[] b) { * @param v value. * @param b byte array. */ - public static void short2bytes(short v, byte[] b, int off) { + public static void short2bytes(short v, byte @MinLen(2) [] b, @NonNegative @LTLengthOf(value = "#2", offset = "1") int off) { b[off + 1] = (byte) v; b[off + 0] = (byte) (v >>> 8); } @@ -112,7 +117,7 @@ public static byte[] int2bytes(int v) { * @param v value. * @param b byte array. */ - public static void int2bytes(int v, byte[] b) { + public static void int2bytes(int v, byte @MinLen(4) [] b) { int2bytes(v, b, 0); } @@ -123,7 +128,7 @@ public static void int2bytes(int v, byte[] b) { * @param b byte array. * @param off array offset. */ - public static void int2bytes(int v, byte[] b, int off) { + public static void int2bytes(int v, byte @MinLen(4) [] b, @NonNegative @LTLengthOf(value = "#2", offset = "3") int off) { b[off + 3] = (byte) v; b[off + 2] = (byte) (v >>> 8); b[off + 1] = (byte) (v >>> 16); @@ -148,7 +153,7 @@ public static byte[] float2bytes(float v) { * @param v value. * @param b byte array. */ - public static void float2bytes(float v, byte[] b) { + public static void float2bytes(float v, byte @MinLen(4) [] b) { float2bytes(v, b, 0); } @@ -159,7 +164,7 @@ public static void float2bytes(float v, byte[] b) { * @param b byte array. * @param off array offset. */ - public static void float2bytes(float v, byte[] b, int off) { + public static void float2bytes(float v, byte @MinLen(4) [] b, @NonNegative @LTLengthOf(value = "#2", offset = "3") int off) { int i = Float.floatToIntBits(v); b[off + 3] = (byte) i; b[off + 2] = (byte) (i >>> 8); @@ -185,7 +190,7 @@ public static byte[] long2bytes(long v) { * @param v value. * @param b byte array. */ - public static void long2bytes(long v, byte[] b) { + public static void long2bytes(long v, byte @MinLen(8) [] b) { long2bytes(v, b, 0); } @@ -196,7 +201,7 @@ public static void long2bytes(long v, byte[] b) { * @param b byte array. * @param off array offset. */ - public static void long2bytes(long v, byte[] b, int off) { + public static void long2bytes(long v, byte @MinLen(8) [] b, @NonNegative @LTLengthOf(value = "#2", offset = "7") int off) { b[off + 7] = (byte) v; b[off + 6] = (byte) (v >>> 8); b[off + 5] = (byte) (v >>> 16); @@ -225,7 +230,7 @@ public static byte[] double2bytes(double v) { * @param v value. * @param b byte array. */ - public static void double2bytes(double v, byte[] b) { + public static void double2bytes(double v, byte @MinLen(8) [] b) { double2bytes(v, b, 0); } @@ -236,7 +241,7 @@ public static void double2bytes(double v, byte[] b) { * @param b byte array. * @param off array offset. */ - public static void double2bytes(double v, byte[] b, int off) { + public static void double2bytes(double v, byte @MinLen(8) [] b, @NonNegative @LTLengthOf(value = "#2", offset = "7") int off) { long j = Double.doubleToLongBits(v); b[off + 7] = (byte) j; b[off + 6] = (byte) (j >>> 8); @@ -254,7 +259,7 @@ public static void double2bytes(double v, byte[] b, int off) { * @param b byte array. * @return short. */ - public static short bytes2short(byte[] b) { + public static short bytes2short(byte @MinLen(2) [] b) { return bytes2short(b, 0); } @@ -265,7 +270,7 @@ public static short bytes2short(byte[] b) { * @param off offset. * @return short. */ - public static short bytes2short(byte[] b, int off) { + public static short bytes2short(byte @MinLen(2) [] b, @NonNegative @LTLengthOf(value = "#1", offset = "1") int off) { return (short) (((b[off + 1] & 0xFF) << 0) + ((b[off + 0]) << 8)); } @@ -276,7 +281,7 @@ public static short bytes2short(byte[] b, int off) { * @param b byte array. * @return int. */ - public static int bytes2int(byte[] b) { + public static int bytes2int(byte @MinLen(4) [] b) { return bytes2int(b, 0); } @@ -287,7 +292,7 @@ public static int bytes2int(byte[] b) { * @param off offset. * @return int. */ - public static int bytes2int(byte[] b, int off) { + public static int bytes2int(byte @MinLen(4) [] b, @NonNegative @LTLengthOf(value = "#1", offset = "3") int off) { return ((b[off + 3] & 0xFF) << 0) + ((b[off + 2] & 0xFF) << 8) + ((b[off + 1] & 0xFF) << 16) + @@ -300,7 +305,7 @@ public static int bytes2int(byte[] b, int off) { * @param b byte array. * @return int. */ - public static float bytes2float(byte[] b) { + public static float bytes2float(byte @MinLen(4) [] b) { return bytes2float(b, 0); } @@ -311,7 +316,7 @@ public static float bytes2float(byte[] b) { * @param off offset. * @return int. */ - public static float bytes2float(byte[] b, int off) { + public static float bytes2float(byte @MinLen(4) [] b, @NonNegative @LTLengthOf(value = "#1", offset = "3") int off) { int i = ((b[off + 3] & 0xFF) << 0) + ((b[off + 2] & 0xFF) << 8) + ((b[off + 1] & 0xFF) << 16) + @@ -325,7 +330,7 @@ public static float bytes2float(byte[] b, int off) { * @param b byte array. * @return long. */ - public static long bytes2long(byte[] b) { + public static long bytes2long(byte @MinLen(8) [] b) { return bytes2long(b, 0); } @@ -336,7 +341,7 @@ public static long bytes2long(byte[] b) { * @param off offset. * @return long. */ - public static long bytes2long(byte[] b, int off) { + public static long bytes2long(byte @MinLen(8) [] b, @NonNegative @LTLengthOf(value = "#1", offset = "7") int off) { return ((b[off + 7] & 0xFFL) << 0) + ((b[off + 6] & 0xFFL) << 8) + ((b[off + 5] & 0xFFL) << 16) + @@ -353,7 +358,7 @@ public static long bytes2long(byte[] b, int off) { * @param b byte array. * @return double. */ - public static double bytes2double(byte[] b) { + public static double bytes2double(byte @MinLen(8) [] b) { return bytes2double(b, 0); } @@ -364,7 +369,7 @@ public static double bytes2double(byte[] b) { * @param off offset. * @return double. */ - public static double bytes2double(byte[] b, int off) { + public static double bytes2double(byte @MinLen(8) [] b, @NonNegative @LTLengthOf(value = "#1", offset = "7") int off) { long j = ((b[off + 7] & 0xFFL) << 0) + ((b[off + 6] & 0xFFL) << 8) + ((b[off + 5] & 0xFFL) << 16) + @@ -394,7 +399,8 @@ public static String bytes2hex(byte[] bs) { * @param len length. * @return hex string. */ - public static String bytes2hex(byte[] bs, int off, int len) { + @SuppressWarnings("index:array.access.unsafe") // The for loop is executed len times. off + len has been verified and cs has length of len * 2 + public static String bytes2hex(byte[] bs, @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) { if (off < 0) { throw new IndexOutOfBoundsException("bytes2hex: offset < 0, offset is " + off); } @@ -434,7 +440,10 @@ public static byte[] hex2bytes(String str) { * @param len length. * @return byte array. */ - public static byte[] hex2bytes(final String str, final int off, int len) { + @SuppressWarnings({"index:argument.type.incompatible", "index:array.access.unsafe.high"}) /* + #1. The loop stops at num steps, which is the length of b. off + len has been previously verified, so accessing str with r is safe. + */ + public static byte[] hex2bytes(final String str, final @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) { if ((len & 1) == 1) { throw new IllegalArgumentException("hex2bytes: ( len & 1 ) == 1."); } @@ -452,7 +461,7 @@ public static byte[] hex2bytes(final String str, final int off, int len) { int num = len / 2, r = off, w = 0; byte[] b = new byte[num]; for (int i = 0; i < num; i++) { - b[w++] = (byte) (hex(str.charAt(r++)) << 4 | hex(str.charAt(r++))); + b[w++] = (byte) (hex(str.charAt(r++)) << 4 | hex(str.charAt(r++))); // #1 } return b; } @@ -473,7 +482,7 @@ public static String bytes2base64(byte[] b) { * @param b byte array. * @return base64 string. */ - public static String bytes2base64(byte[] b, int offset, int length) { + public static String bytes2base64(byte[] b, @IndexOrHigh("#1") int offset, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int length) { return bytes2base64(b, offset, length, BASE64); } @@ -484,7 +493,7 @@ public static String bytes2base64(byte[] b, int offset, int length) { * @param code base64 code string(0-63 is base64 char,64 is pad char). * @return base64 string. */ - public static String bytes2base64(byte[] b, String code) { + public static String bytes2base64(byte[] b, @MinLen(64) String code) { return bytes2base64(b, 0, b.length, code); } @@ -495,7 +504,7 @@ public static String bytes2base64(byte[] b, String code) { * @param code base64 code string(0-63 is base64 char,64 is pad char). * @return base64 string. */ - public static String bytes2base64(byte[] b, int offset, int length, String code) { + public static String bytes2base64(byte[] b, @IndexOrHigh("#1") int offset, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int length, @MinLen(64) String code) { if (code.length() < 64) { throw new IllegalArgumentException("Base64 code length < 64."); } @@ -510,7 +519,7 @@ public static String bytes2base64(byte[] b, int offset, int length, String code) * @param code base64 code(0-63 is base64 char,64 is pad char). * @return base64 string. */ - public static String bytes2base64(byte[] b, char[] code) { + public static String bytes2base64(byte[] b, char @MinLen(64) [] code) { return bytes2base64(b, 0, b.length, code); } @@ -523,7 +532,12 @@ public static String bytes2base64(byte[] b, char[] code) { * @param code base64 code(0-63 is base64 char,64 is pad char). * @return base64 string. */ - public static String bytes2base64(final byte[] bs, final int off, final int len, final char[] code) { + @SuppressWarnings("index:array.access.unsafe") /* + #1 - #5. The loop stops at num steps, which is len / 3 and bigger than cs.length / 4. Every index used to access code is smaller than 64. + #6 - #15. If rem is 1 or 2, then cs and bs are larger, respectively. Every index used to access code is smaller than 64. If pad is true, then + it is safe to access it with an index of 64 and cs is larger as well. + */ + public static String bytes2base64(final byte[] bs, final @IndexOrHigh("#1") int off, final @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len, final char @MinLen(64) [] code) { if (off < 0) { throw new IndexOutOfBoundsException("bytes2base64: offset < 0, offset is " + off); } @@ -543,29 +557,29 @@ public static String bytes2base64(final byte[] bs, final int off, final int len, char[] cs = new char[num * 4 + (rem == 0 ? 0 : pad ? 4 : rem + 1)]; for (int i = 0; i < num; i++) { - int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8, b3 = bs[r++] & MASK8; + int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8, b3 = bs[r++] & MASK8; // #1 - cs[w++] = code[b1 >> 2]; - cs[w++] = code[(b1 << 4) & MASK6 | (b2 >> 4)]; - cs[w++] = code[(b2 << 2) & MASK6 | (b3 >> 6)]; - cs[w++] = code[b3 & MASK6]; + cs[w++] = code[b1 >> 2]; // #2 + cs[w++] = code[(b1 << 4) & MASK6 | (b2 >> 4)]; // #3 + cs[w++] = code[(b2 << 2) & MASK6 | (b3 >> 6)]; // #4 + cs[w++] = code[b3 & MASK6]; // #5 } if (rem == 1) { - int b1 = bs[r++] & MASK8; - cs[w++] = code[b1 >> 2]; - cs[w++] = code[(b1 << 4) & MASK6]; + int b1 = bs[r++] & MASK8; // #6 + cs[w++] = code[b1 >> 2]; // #7 + cs[w++] = code[(b1 << 4) & MASK6]; // #8 if (pad) { - cs[w++] = code[64]; - cs[w++] = code[64]; + cs[w++] = code[64]; // #9 + cs[w++] = code[64]; // #10 } } else if (rem == 2) { - int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8; - cs[w++] = code[b1 >> 2]; - cs[w++] = code[(b1 << 4) & MASK6 | (b2 >> 4)]; - cs[w++] = code[(b2 << 2) & MASK6]; + int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8; // #11 + cs[w++] = code[b1 >> 2]; // #12 + cs[w++] = code[(b1 << 4) & MASK6 | (b2 >> 4)]; // #13 + cs[w++] = code[(b2 << 2) & MASK6]; // #14 if (pad) { - cs[w++] = code[64]; + cs[w++] = code[64]; // #15 } } return new String(cs); @@ -589,7 +603,7 @@ public static byte[] base642bytes(String str) { * @param length length. * @return byte array. */ - public static byte[] base642bytes(String str, int offset, int length) { + public static byte[] base642bytes(String str, @IndexOrHigh("#1") int offset, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int length) { return base642bytes(str, offset, length, C64); } @@ -600,7 +614,7 @@ public static byte[] base642bytes(String str, int offset, int length) { * @param code base64 code(0-63 is base64 char,64 is pad char). * @return byte array. */ - public static byte[] base642bytes(String str, String code) { + public static byte[] base642bytes(String str, @MinLen(64) String code) { return base642bytes(str, 0, str.length(), code); } @@ -613,7 +627,14 @@ public static byte[] base642bytes(String str, String code) { * @param code base64 code(0-63 is base64 char,64 is pad char). * @return byte array. */ - public static byte[] base642bytes(final String str, final int off, final int len, final String code) { + @SuppressWarnings({"index:array.access.unsafe", "index:argument.type.incompatible", "index:array.length.negative"}) /* + #1. + #2. + #3. size cannot be negative because num is 0 only if len < 4. size gets negative if len is 0, and the code would crash at #1 if so + #4 - #8. The loop stops at num steps, which is len / 4 and size / 3. + #9 - #13. If rem is 2 or 3, then str has 2 or 3 more spaces, respectively. + */ + public static byte[] base642bytes(final String str, final @IndexOrHigh("#1") int off, final @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len, final @MinLen(64) String code) { if (off < 0) { throw new IndexOutOfBoundsException("base642bytes: offset < 0, offset is " + off); } @@ -640,11 +661,11 @@ public static byte[] base642bytes(final String str, final int off, final int len } char pc = code.charAt(64); - if (str.charAt(off + len - 2) == pc) { + if (str.charAt(off + len - 2) == pc) { // #1 size -= 2; --num; rem = 2; - } else if (str.charAt(off + len - 1) == pc) { + } else if (str.charAt(off + len - 1) == pc) { // #2 size--; --num; rem = 3; @@ -658,25 +679,25 @@ public static byte[] base642bytes(final String str, final int off, final int len } int r = off, w = 0; - byte[] b = new byte[size], t = decodeTable(code); + byte[] b = new byte[size], t = decodeTable(code); // #3 for (int i = 0; i < num; i++) { - int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)]; - int c3 = t[str.charAt(r++)], c4 = t[str.charAt(r++)]; + int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)]; // #4 + int c3 = t[str.charAt(r++)], c4 = t[str.charAt(r++)]; // #5 - b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); - b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); - b[w++] = (byte) ((c3 << 6) | c4); + b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); // #6 + b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); // #7 + b[w++] = (byte) ((c3 << 6) | c4); // #8 } if (rem == 2) { - int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)]; + int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)]; // #9 - b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); + b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); // #10 } else if (rem == 3) { - int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)], c3 = t[str.charAt(r++)]; + int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)], c3 = t[str.charAt(r++)]; // #11 - b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); - b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); + b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); // #12 + b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); // #13 } return b; } @@ -688,7 +709,7 @@ public static byte[] base642bytes(final String str, final int off, final int len * @param code base64 code(0-63 is base64 char,64 is pad char). * @return byte array. */ - public static byte[] base642bytes(String str, char[] code) { + public static byte[] base642bytes(String str, char @MinLen(64) [] code) { return base642bytes(str, 0, str.length(), code); } @@ -701,7 +722,14 @@ public static byte[] base642bytes(String str, char[] code) { * @param code base64 code(0-63 is base64 char,64 is pad char). * @return byte array. */ - public static byte[] base642bytes(final String str, final int off, final int len, final char[] code) { + @SuppressWarnings({"index:array.access.unsafe", "index:argument.type.incompatible", "index:array.length.negative"}) /* + #1. + #2. + #3. size cannot be negative because num is 0 only if len < 4. size gets negative if len is 0, and the code would crash at #1 if so + #4 - #8. The loop stops at num steps, which is len / 4 and size / 3. + #9 - #13. If rem is 2 or 3, then str has 2 or 3 more spaces, respectively. + */ + public static byte[] base642bytes(final String str, final @IndexOrHigh("#1") int off, final @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len, final char @MinLen(64) [] code) { if (off < 0) { throw new IndexOutOfBoundsException("base642bytes: offset < 0, offset is " + off); } @@ -728,9 +756,9 @@ public static byte[] base642bytes(final String str, final int off, final int len } char pc = code[64]; - if (str.charAt(off + len - 2) == pc) { + if (str.charAt(off + len - 2) == pc) { // #1 size -= 2; - } else if (str.charAt(off + len - 1) == pc) { + } else if (str.charAt(off + len - 1) == pc) { // #2 size--; } } else { @@ -742,25 +770,25 @@ public static byte[] base642bytes(final String str, final int off, final int len } int r = off, w = 0; - byte[] b = new byte[size]; + byte[] b = new byte[size]; // #3 for (int i = 0; i < num; i++) { - int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)); - int c3 = indexOf(code, str.charAt(r++)), c4 = indexOf(code, str.charAt(r++)); + int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)); // #4 + int c3 = indexOf(code, str.charAt(r++)), c4 = indexOf(code, str.charAt(r++)); // #5 - b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); - b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); - b[w++] = (byte) ((c3 << 6) | c4); + b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); // #6 + b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); // #7 + b[w++] = (byte) ((c3 << 6) | c4); // #8 } if (rem == 2) { - int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)); + int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)); // #9 - b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); + b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); // #10 } else if (rem == 3) { - int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)), c3 = indexOf(code, str.charAt(r++)); + int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)), c3 = indexOf(code, str.charAt(r++)); // #11 - b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); - b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); + b[w++] = (byte) ((c1 << 2) | (c2 >> 4)); // #12 + b[w++] = (byte) ((c2 << 4) | (c3 >> 2)); // #13 } return b; } @@ -873,6 +901,10 @@ private static int indexOf(char[] cs, char c) { return -1; } + @SuppressWarnings({"index:array.access.unsafe", "index:argument.type.incompatible"}) /* + #1. ret has length of 128, the loop stops at position 127 + #2. It was verified that code has at least 64 characters. The ascii value of a char is at most 127, which is a valid index for ret + */ private static byte[] decodeTable(String code) { int hash = code.hashCode(); byte[] ret = DECODE_TABLE_MAP.get(hash); @@ -884,17 +916,18 @@ private static byte[] decodeTable(String code) { ret = new byte[128]; for (int i = 0; i < 128; i++) // init table. { - ret[i] = -1; + ret[i] = -1; // #1 } for (int i = 0; i < 64; i++) { - ret[code.charAt(i)] = (byte) i; + ret[code.charAt(i)] = (byte) i; // #2 } DECODE_TABLE_MAP.put(hash, ret); } return ret; } - private static byte[] getMD5(InputStream is, int bs) throws IOException { + @SuppressWarnings("index:argument.type.incompatible") // The call to read is safe because the loop stops before total exceeds bs + private static byte[] getMD5(InputStream is, @NonNegative int bs) throws IOException { MessageDigest md = getMessageDigest(); byte[] buf = new byte[bs]; while (is.available() > 0) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/io/StreamUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/io/StreamUtils.java index ab2836ef913..737a339c246 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/io/StreamUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/io/StreamUtils.java @@ -16,6 +16,8 @@ */ package org.apache.dubbo.common.io; +import org.checkerframework.checker.index.qual.*; + import java.io.IOException; import java.io.InputStream; @@ -31,7 +33,7 @@ public static InputStream limitedInputStream(final InputStream is, final int lim private int mPosition = 0, mMark = 0, mLimit = Math.min(limit, is.available()); @Override - public int read() throws IOException { + public @GTENegativeOne int read() throws IOException { if (mPosition < mLimit) { mPosition++; return is.read(); @@ -40,7 +42,7 @@ public int read() throws IOException { } @Override - public int read(byte[] b, int off, int len) throws IOException { + public @GTENegativeOne @LTEqLengthOf("#1") int read(byte[] b, @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) throws IOException { if (b == null) { throw new NullPointerException(); } @@ -67,7 +69,7 @@ public int read(byte[] b, int off, int len) throws IOException { } @Override - public long skip(long len) throws IOException { + public @NonNegative long skip(long len) throws IOException { if (mPosition + len > mLimit) { len = mLimit - mPosition; } @@ -82,7 +84,7 @@ public long skip(long len) throws IOException { } @Override - public int available() { + public @NonNegative int available() { return mLimit - mPosition; } @@ -92,7 +94,7 @@ public boolean markSupported() { } @Override - public void mark(int readlimit) { + public void mark(@NonNegative int readlimit) { is.mark(readlimit); mMark = mPosition; } @@ -110,7 +112,7 @@ public void close() throws IOException { }; } - public static InputStream markSupportedInputStream(final InputStream is, final int markBufferSize) { + public static InputStream markSupportedInputStream(final InputStream is, final @NonNegative int markBufferSize) { if (is.markSupported()) { return is; } @@ -125,7 +127,7 @@ public static InputStream markSupportedInputStream(final InputStream is, final i private int mCount = 0; @Override - public int read() throws IOException { + public @GTENegativeOne int read() throws IOException { if (!mInMarked) { return is.read(); } else { @@ -202,7 +204,7 @@ public boolean markSupported() { } @Override - public int available() throws IOException { + public @NonNegative int available() throws IOException { int available = is.available(); if (mInMarked && mInReset) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayInputStream.java b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayInputStream.java index df96a1c1d01..f312ae0d6fd 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayInputStream.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayInputStream.java @@ -19,35 +19,48 @@ import java.io.IOException; import java.io.InputStream; +import org.checkerframework.checker.index.qual.GTENegativeOne; +import org.checkerframework.checker.index.qual.IndexFor; +import org.checkerframework.checker.index.qual.IndexOrHigh; +import org.checkerframework.checker.index.qual.LTEqLengthOf; +import org.checkerframework.checker.index.qual.LTLengthOf; +import org.checkerframework.checker.index.qual.NonNegative; + /** * UnsafeByteArrayInputStream. */ public class UnsafeByteArrayInputStream extends InputStream { protected byte[] mData; - protected int mPosition, mLimit, mMark = 0; + protected @IndexOrHigh("this.mData") int mPosition, mMark, mLimit = 0; public UnsafeByteArrayInputStream(byte[] buf) { this(buf, 0, buf.length); } - public UnsafeByteArrayInputStream(byte[] buf, int offset) { + @SuppressWarnings("index:argument.type.incompatible") // The length of the array is always greater than the index used to access it. + public UnsafeByteArrayInputStream(byte[] buf, @IndexOrHigh("#1") int offset) { this(buf, offset, buf.length - offset); } - public UnsafeByteArrayInputStream(byte[] buf, int offset, int length) { + public UnsafeByteArrayInputStream(byte[] buf, @IndexOrHigh("#1") int offset, @NonNegative int length) { mData = buf; mPosition = mMark = offset; mLimit = Math.min(offset + length, buf.length); } @Override - public int read() { + public @GTENegativeOne int read() { return (mPosition < mLimit) ? (mData[mPosition++] & 0xff) : -1; } @Override - public int read(byte[] b, int off, int len) { + @SuppressWarnings({"index:assignment.type.incompatible", "index:argument.type.incompatible", "index:return.type.incompatible"}) /* + #1. mLimit is greater than mPosition and after the assignment the value can only decrease, so it stays valid. + #2. The call is safe because len has been verified before. + #3. The return type is safe because len has been verified. + */ + public @GTENegativeOne @LTEqLengthOf("#1") int read(byte[] b, @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) { if (b == null) { throw new NullPointerException(); } @@ -58,18 +71,18 @@ public int read(byte[] b, int off, int len) { return -1; } if (mPosition + len > mLimit) { - len = mLimit - mPosition; + len = mLimit - mPosition; // #1 } if (len <= 0) { return 0; } - System.arraycopy(mData, mPosition, b, off, len); + System.arraycopy(mData, mPosition, b, off, len); // #2 mPosition += len; - return len; + return len; // #3 } @Override - public long skip(long len) { + public @NonNegative long skip(long len) { if (mPosition + len > mLimit) { len = mLimit - mPosition; } @@ -81,7 +94,8 @@ public long skip(long len) { } @Override - public int available() { + @SuppressWarnings("index:return.type.incompatible") // mPosition is always lower than mLimit + public @NonNegative int available() { return mLimit - mPosition; } @@ -108,7 +122,7 @@ public int position() { return mPosition; } - public void position(int newPosition) { + public void position(@IndexOrHigh("this.mData") int newPosition) { mPosition = newPosition; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayOutputStream.java b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayOutputStream.java index 6e75eb83109..f068e501d27 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayOutputStream.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeByteArrayOutputStream.java @@ -21,19 +21,24 @@ import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; +import org.checkerframework.checker.index.qual.IndexFor; +import org.checkerframework.checker.index.qual.IndexOrHigh; +import org.checkerframework.checker.index.qual.LTLengthOf; +import org.checkerframework.checker.index.qual.NonNegative; + /** * UnsafeByteArrayOutputStream. */ public class UnsafeByteArrayOutputStream extends OutputStream { protected byte[] mBuffer; - protected int mCount; + protected @IndexOrHigh("this.mBuffer") int mCount; public UnsafeByteArrayOutputStream() { this(32); } - public UnsafeByteArrayOutputStream(int size) { + public UnsafeByteArrayOutputStream(@NonNegative int size) { if (size < 0) { throw new IllegalArgumentException("Negative initial size: " + size); } @@ -41,6 +46,7 @@ public UnsafeByteArrayOutputStream(int size) { } @Override + @SuppressWarnings({"index:array.access.unsafe", "index:assignment.type.incompatible"}) // If mCount becomes mBuffer.length, then the array is extended before accessing it public void write(int b) { int newcount = mCount + 1; if (newcount > mBuffer.length) { @@ -51,7 +57,9 @@ public void write(int b) { } @Override - public void write(byte[] b, int off, int len) { + @SuppressWarnings({"index:argument.type.incompatible", "index:assignment.type.incompatible"}) // len is valid because if mCount + len exceeds mBuffer.length, then + // the array is extended + public void write(byte[] b, @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) { if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringReader.java b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringReader.java index ad37476bd72..4a322617010 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringReader.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringReader.java @@ -19,13 +19,19 @@ import java.io.IOException; import java.io.Reader; +import org.checkerframework.checker.index.qual.GTENegativeOne; +import org.checkerframework.checker.index.qual.IndexOrHigh; +import org.checkerframework.checker.index.qual.LTEqLengthOf; +import org.checkerframework.checker.index.qual.LTLengthOf; +import org.checkerframework.checker.index.qual.NonNegative; + /** * Thread-unsafe StringReader. */ public class UnsafeStringReader extends Reader { private String mString; - private int mPosition, mLimit, mMark; + private @IndexOrHigh("this.mString") int mPosition, mLimit, mMark; public UnsafeStringReader(String str) { mString = str; @@ -34,7 +40,8 @@ public UnsafeStringReader(String str) { } @Override - public int read() throws IOException { + @SuppressWarnings("index:return.type.incompatible") // A char is always greater than 0 + public @GTENegativeOne int read() throws IOException { ensureOpen(); if (mPosition >= mLimit) { return -1; @@ -44,7 +51,11 @@ public int read() throws IOException { } @Override - public int read(char[] cs, int off, int len) throws IOException { + @SuppressWarnings({"index:argument.type.incompatible", "index:compound.assignment.type.incompatible", "index:return.type.incompatible"}) /* + #1 and #2. mPosition + n is at most mLimit, which is a valid index + #3. Both mLimit - mPosition and len have been verified, so the returned variable is correct + */ + public @GTENegativeOne @LTEqLengthOf("#1") int read(char[] cs, @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) throws IOException { ensureOpen(); if ((off < 0) || (off > cs.length) || (len < 0) || ((off + len) > cs.length) || ((off + len) < 0)) { @@ -60,13 +71,14 @@ public int read(char[] cs, int off, int len) throws IOException { } int n = Math.min(mLimit - mPosition, len); - mString.getChars(mPosition, mPosition + n, cs, off); - mPosition += n; - return n; + mString.getChars(mPosition, mPosition + n, cs, off); // #1 + mPosition += n; // #2 + return n; // #3 } @Override - public long skip(long ns) throws IOException { + @SuppressWarnings("index:compound.assignment.type.incompatible") // n is valid because it was previously verified + public @NonNegative long skip(@NonNegative long ns) throws IOException { ensureOpen(); if (mPosition >= mLimit) { return 0; @@ -90,7 +102,7 @@ public boolean markSupported() { } @Override - public void mark(int readAheadLimit) throws IOException { + public void mark(@NonNegative int readAheadLimit) throws IOException { if (readAheadLimit < 0) { throw new IllegalArgumentException("Read-ahead limit < 0"); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringWriter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringWriter.java index 47e9a3a1aa5..7661943e15c 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringWriter.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/io/UnsafeStringWriter.java @@ -19,6 +19,10 @@ import java.io.IOException; import java.io.Writer; +import org.checkerframework.checker.index.qual.IndexOrHigh; +import org.checkerframework.checker.index.qual.LTLengthOf; +import org.checkerframework.checker.index.qual.NonNegative; + /** * Thread-unsafe StringWriter. */ @@ -29,7 +33,7 @@ public UnsafeStringWriter() { lock = mBuffer = new StringBuilder(); } - public UnsafeStringWriter(int size) { + public UnsafeStringWriter(@NonNegative int size) { if (size < 0) { throw new IllegalArgumentException("Negative buffer size"); } @@ -48,7 +52,7 @@ public void write(char[] cs) throws IOException { } @Override - public void write(char[] cs, int off, int len) throws IOException { + public void write(char[] cs, @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) throws IOException { if ((off < 0) || (off > cs.length) || (len < 0) || ((off + len) > cs.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); @@ -65,7 +69,7 @@ public void write(String str) { } @Override - public void write(String str, int off, int len) { + public void write(String str, @IndexOrHigh("#1") int off, @NonNegative @LTLengthOf(value = "#1", offset = "#2 - 1") int len) { mBuffer.append(str.substring(off, off + len)); } @@ -80,7 +84,10 @@ public Writer append(CharSequence csq) { } @Override - public Writer append(CharSequence csq, int start, int end) { + @SuppressWarnings("index:argument.type.incompatible") + // The method can throw an IndexOutOfBoundsException when subsequence(int, int) is called, but this is documented in the overridden method: + // https://docs.oracle.com/javase/8/docs/api/java/lang/CharSequence.html#subSequence-int-int- + public Writer append(CharSequence csq, @IndexOrHigh("#1") int start, @IndexOrHigh("#1") int end) { CharSequence cs = (csq == null ? "null" : csq); write(cs.subSequence(start, end).toString()); return this; diff --git a/pom.xml b/pom.xml index 16afc2714ff..572703ac7d7 100644 --- a/pom.xml +++ b/pom.xml @@ -194,6 +194,16 @@ ${cglib_version} test + + org.checkerframework + checker + 2.8.1 + + + org.checkerframework + jdk8 + 2.8.1 + @@ -443,7 +453,6 @@ maven-compiler-plugin ${maven_compiler_version} - -proc:none true ${java_source_version} ${java_target_version} @@ -621,6 +630,26 @@ jetty-maven-plugin ${maven_jetty_version} + + maven-compiler-plugin + 3.3 + + 1.8 + 1.8 + + 10000 + 10000 + + + org.checkerframework.checker.index.IndexChecker + + + -Xbootclasspath/p:$CHECKERFRAMEWORK/checker/dist/jdk8.jar + -AprintErrorStack + -AonlyDefs=^org\.apache\.dubbo\.common\.io\. + + +