Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/sun/nio/cs/FindDecoderBugs.java
38838 views
/*1* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 638072326* @summary Decode many byte sequences in many ways27* @run main/timeout=1800 FindDecoderBugs28* @author Martin Buchholz29* @key randomness30*/3132import java.util.*;33import java.util.regex.*;34import java.nio.*;35import java.nio.charset.*;3637public class FindDecoderBugs {3839static boolean isBroken(String csn) {40if (csn.equals("x-COMPOUND_TEXT")) return true;41return false;42}4344static <T extends Comparable<? super T>> List<T> sort(Collection<T> c) {45List<T> list = new ArrayList<T>(c);46Collections.sort(list);47return list;48}4950static class TooManyFailures extends RuntimeException {51private static final long serialVersionUID = 0L;52}5354static String string(byte[] a) {55final StringBuilder sb = new StringBuilder();56for (byte b : a) {57if (sb.length() != 0) sb.append(' ');58sb.append(String.format("%02x", b & 0xff));59}60return sb.toString();61}6263static String string(char[] a) {64final StringBuilder sb = new StringBuilder();65for (char c : a) {66if (sb.length() != 0) sb.append(' ');67sb.append(String.format("\\u%04x", (int) c));68}69return sb.toString();70}7172static class Reporter {73// Some machinery to make sure only a small number of errors74// that are "too similar" are reported.75static class Counts extends HashMap<String, Long> {76private static final long serialVersionUID = -1;77long inc(String signature) {78Long count = get(signature);79if (count == null) count = 0L;80put(signature, count+1);81return count+1;82}83}8485final Counts failureCounts = new Counts();86final static long maxFailures = 2;8788final static Pattern hideBytes = Pattern.compile("\"[0-9a-f ]+\"");89final static Pattern hideChars = Pattern.compile("\\\\u[0-9a-f]{4}");9091boolean bug(String format, Object... args) {92String signature = String.format(format, args);93signature = hideBytes.matcher(signature).replaceAll("\"??\"");94signature = hideChars.matcher(signature).replaceAll("\\u????");95failed++;96if (failureCounts.inc(signature) <= maxFailures) {97System.out.printf(format, args);98System.out.println();99return true;100}101return false;102}103104void summarize() {105for (String key : sort(failureCounts.keySet()))106System.out.printf("-----%n%s%nfailures=%d%n",107key, failureCounts.get(key));108}109}110111static final Reporter reporter = new Reporter();112113static class Result {114final int limit;115final int ipos;116final boolean direct;117final byte[] ia;118final char[] oa;119final CoderResult cr;120121Result(ByteBuffer ib, CharBuffer ob, CoderResult cr) {122ipos = ib.position();123ia = toArray(ib);124oa = toArray(ob);125direct = ib.isDirect();126limit = ob.limit();127this.cr = cr;128}129130static byte[] toArray(ByteBuffer b) {131int pos = b.position();132byte[] a = new byte[b.limit()];133b.position(0);134b.get(a);135b.position(pos);136return a;137}138139static char[] toArray(CharBuffer b) {140char[] a = new char[b.position()];141b.position(0);142b.get(a);143return a;144}145146static boolean eq(Result x, Result y) {147return x == y ||148(x != null && y != null &&149(Arrays.equals(x.oa, y.oa) &&150x.ipos == y.ipos &&151x.cr == y.cr));152}153154public String toString() {155return String.format("\"%s\"[%d/%d] => %s \"%s\"[%d/%d]%s",156string(ia), ipos, ia.length,157cr, string(oa), oa.length, limit,158(direct ? " (direct)" : ""));159}160}161162// legend: r=regular d=direct In=Input Ou=Output163static final int maxBufSize = 20;164static final ByteBuffer[] ribs = new ByteBuffer[maxBufSize];165static final ByteBuffer[] dibs = new ByteBuffer[maxBufSize];166167static final CharBuffer[] robs = new CharBuffer[maxBufSize];168static final CharBuffer[] dobs = new CharBuffer[maxBufSize];169static {170for (int i = 0; i < maxBufSize; i++) {171ribs[i] = ByteBuffer.allocate(i);172dibs[i] = ByteBuffer.allocateDirect(i);173robs[i] = CharBuffer.allocate(i);174dobs[i] = ByteBuffer.allocateDirect(i*2).asCharBuffer();175}176}177178static class CharsetTester {179private final Charset cs;180private static final long maxFailures = 5;181private long failures = 0;182// private static final long maxCharsetFailures = Long.MAX_VALUE;183private static final long maxCharsetFailures = 10000L;184private final long failed0 = failed;185186CharsetTester(Charset cs) {187this.cs = cs;188}189190static boolean bug(String format, Object... args) {191return reporter.bug(format, args);192}193194Result recode(ByteBuffer ib, CharBuffer ob) {195try {196char canary = '\u4242';197ib.clear(); // Prepare to read198ob.clear(); // Prepare to write199for (int i = 0; i < ob.limit(); i++)200ob.put(i, canary);201CharsetDecoder coder = cs.newDecoder();202CoderResult cr = coder.decode(ib, ob, false);203equal(ib.limit(), ib.capacity());204equal(ob.limit(), ob.capacity());205Result r = new Result(ib, ob, cr);206if (cr.isError())207check(cr.length() > 0);208if (cr.isOverflow() && ob.remaining() > 10)209bug("OVERFLOW, but there's lots of room: %s %s",210cs, r);211// if (cr.isOverflow() && ib.remaining() == 0)212// bug("OVERFLOW, yet remaining() == 0: %s %s",213// cs, r);214if (cr.isError() && ib.remaining() < cr.length())215bug("remaining() < CoderResult.length(): %s %s",216cs, r);217// if (ib.position() == 0 && ob.position() > 0)218// reporter. bug("output only if input consumed: %s %s",219// cs, r);220// Should we warn if cr.isUnmappable() ??221CoderResult cr2 = coder.decode(ib, ob, false);222if (ib.position() != r.ipos ||223ob.position() != r.oa.length ||224cr != cr2)225bug("Coding operation not idempotent: %s%n %s%n %s",226cs, r, new Result(ib, ob, cr2));227if (ob.position() < ob.limit() &&228ob.get(ob.position()) != canary)229bug("Buffer overrun: %s %s %s",230cs, r, ob.get(ob.position()));231return r;232} catch (Throwable t) {233if (bug("Unexpected exception: %s %s %s",234cs, t.getClass().getSimpleName(),235new Result(ib, ob, null)))236t.printStackTrace();237return null;238}239}240241Result recode2(byte[] ia, int n) {242int len = ia.length;243ByteBuffer rib = ByteBuffer.wrap(ia);244ByteBuffer dib = dibs[len];245dib.clear(); dib.put(ia); dib.clear();246CharBuffer rob = robs[n];247CharBuffer dob = dobs[n];248equal(rob.limit(), n);249equal(dob.limit(), n);250check(dib.isDirect());251check(dob.isDirect());252Result r1 = recode(rib, rob);253Result r2 = recode(dib, dob);254if (r1 != null && r2 != null && ! Result.eq(r1, r2))255bug("Results differ for direct buffers: %s%n %s%n %s",256cs, r1, r2);257return r1;258}259260Result test(byte[] ia) {261if (failed - failed0 >= maxCharsetFailures)262throw new TooManyFailures();263264Result roomy = recode2(ia, maxBufSize - 1);265if (roomy == null) return roomy;266int olen = roomy.oa.length;267if (olen > 0) {268if (roomy.ipos == roomy.ia.length) {269Result perfectFit = recode2(ia, olen);270if (! Result.eq(roomy, perfectFit))271bug("Results differ: %s%n %s%n %s",272cs, roomy, perfectFit);273}274for (int i = 0; i < olen; i++) {275Result claustrophobic = recode2(ia, i);276if (claustrophobic == null) return roomy;277if (roomy.cr.isUnderflow() &&278! claustrophobic.cr.isOverflow())279bug("Expected OVERFLOW: %s%n %s%n %s",280cs, roomy, claustrophobic);281}282}283return roomy;284}285286void testExhaustively(byte[] prefix, int n) {287int len = prefix.length;288byte[] ia = Arrays.copyOf(prefix, len + 1);289for (int i = 0; i < 0x100; i++) {290ia[len] = (byte) i;291if (n == 1)292test(ia);293else294testExhaustively(ia, n - 1);295}296}297298void testRandomly(byte[] prefix, int n) {299int len = prefix.length;300byte[] ia = Arrays.copyOf(prefix, len + n);301for (int i = 0; i < 5000; i++) {302for (int j = 0; j < n; j++)303ia[len + j] = randomByte();304test(ia);305}306}307308void testPrefix(byte[] prefix) {309if (prefix.length > 0)310System.out.printf("Testing prefix %s%n", string(prefix));311312test(prefix);313314testExhaustively(prefix, 1);315testExhaustively(prefix, 2);316// Can you spare a week of CPU time?317// testExhaustively(cs, tester, prefix, 3);318319testRandomly(prefix, 3);320testRandomly(prefix, 4);321}322}323324private final static Random rnd = new Random();325private static byte randomByte() {326return (byte) rnd.nextInt(0x100);327}328private static byte[] randomBytes(int len) {329byte[] a = new byte[len];330for (int i = 0; i < len; i++)331a[i] = randomByte();332return a;333}334335private static final byte SS2 = (byte) 0x8e;336private static final byte SS3 = (byte) 0x8f;337private static final byte ESC = (byte) 0x1b;338private static final byte SO = (byte) 0x0e;339private static final byte SI = (byte) 0x0f;340341private final static byte[][] stateChangers = {342{SS2}, {SS3}, {SO}, {SI}343};344345private final static byte[][]escapeSequences = {346{ESC, '(', 'B'},347{ESC, '(', 'I'},348{ESC, '(', 'J'},349{ESC, '$', '@'},350{ESC, '$', 'A'},351{ESC, '$', ')', 'A'},352{ESC, '$', ')', 'C'},353{ESC, '$', ')', 'G'},354{ESC, '$', '*', 'H'},355{ESC, '$', '+', 'I'},356{ESC, '$', 'B'},357{ESC, 'N'},358{ESC, 'O'},359{ESC, '$', '(', 'D'},360};361362private static boolean isStateChanger(Charset cs, byte[] ia) {363Result r = new CharsetTester(cs).recode2(ia, 9);364return r == null ? false :365(r.cr.isUnderflow() &&366r.ipos == ia.length &&367r.oa.length == 0);368}369370private final static byte[][] incompletePrefixes = {371{ESC},372{ESC, '('},373{ESC, '$'},374{ESC, '$', '(',},375};376377private static boolean isIncompletePrefix(Charset cs, byte[] ia) {378Result r = new CharsetTester(cs).recode2(ia, 9);379return r == null ? false :380(r.cr.isUnderflow() &&381r.ipos == 0 &&382r.oa.length == 0);383}384385private static void testCharset(Charset cs) throws Throwable {386final String csn = cs.name();387388if (isBroken(csn)) {389System.out.printf("Skipping possibly broken charset %s%n", csn);390return;391}392System.out.println(csn);393CharsetTester tester = new CharsetTester(cs);394395tester.testPrefix(new byte[0]);396397if (! csn.matches("(?:x-)?(?:UTF|JIS(?:_X)?0).*")) {398for (byte[] prefix : stateChangers)399if (isStateChanger(cs, prefix))400tester.testPrefix(prefix);401402for (byte[] prefix : incompletePrefixes)403if (isIncompletePrefix(cs, prefix))404tester.testPrefix(prefix);405406if (isIncompletePrefix(cs, new byte[] {ESC}))407for (byte[] prefix : escapeSequences)408if (isStateChanger(cs, prefix))409tester.testPrefix(prefix);410}411}412413private static void realMain(String[] args) {414for (Charset cs : sort(Charset.availableCharsets().values())) {415try {416testCharset(cs);417} catch (TooManyFailures e) {418System.out.printf("Too many failures for %s%n", cs);419} catch (Throwable t) {420unexpected(t);421}422}423reporter.summarize();424}425426//--------------------- Infrastructure ---------------------------427static volatile long passed = 0, failed = 0;428static void pass() {passed++;}429static void fail() {failed++; Thread.dumpStack();}430static void fail(String format, Object... args) {431System.out.println(String.format(format, args)); failed++;}432static void fail(String msg) {System.out.println(msg); fail();}433static void unexpected(Throwable t) {failed++; t.printStackTrace();}434static void check(boolean cond) {if (cond) pass(); else fail();}435static void equal(Object x, Object y) {436if (x == null ? y == null : x.equals(y)) pass();437else fail(x + " not equal to " + y);}438static void equal(int x, int y) {439if (x == y) pass();440else fail(x + " not equal to " + y);}441public static void main(String[] args) throws Throwable {442try {realMain(args);} catch (Throwable t) {unexpected(t);}443System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);444if (failed > 0) throw new AssertionError("Some tests failed");}445}446447448