CVE-2018-12418 (Java, published 2018-06-14T16:29Z)
Description: value: Archive.java in Junrar before 1.0.1, as used in Apache Tika and other products, is affected by a denial of service vulnerability due to an infinite loop when handling corrupt RAR files.
Severity: MEDIUM (CVSS v3 base score: 5.5)
Weakness: CWE-835 \u2014 Loop with Unreachable Exit Condition ('Infinite Loop')
The product contains an iteration or loop with an exit condition that cannot be reached, i.e., an infinite loop.
Repository: https://github.com/junrar/junrar
Files changed:
src/main/java/com/github/junrar/Archive.java
src/test/java/com/github/junrar/testUtil/SimpleTest.java
Commit message: make Archive slightly more robust against corrupt files.  Add (#8)

unit test file from Apache Tika.

Vulnerable code:

			byte[] baseBlockBuffer = new byte[BaseBlock.BaseBlockSize];
				byte[] mainbuff = new byte[toRead];
				byte[] signBuff = new byte[toRead];
				byte[] avBuff = new byte[toRead];
				byte[] commBuff = new byte[toRead];
					byte[] endArchBuff = new byte[toRead];
				byte[] blockHeaderBuffer = new byte[BlockHeader.blockHeaderSize];
					byte[] fileHeaderBuffer = new byte[toRead];
					byte[] protectHeaderBuffer = new byte[toRead];

					byte[] subHeadbuffer = new byte[SubBlockHeader.SubBlockHeaderSize];
						byte[] macHeaderbuffer = new byte[MacInfoHeader.MacInfoHeaderSize];
						byte[] eaHeaderBuffer = new byte[EAHeader.EAHeaderSize];
						byte[] uoHeaderBuffer = new byte[toRead];

Fixed code:
import java.util.HashSet;
import java.util.Set;
	private static int MAX_HEADER_SIZE = 20971520;//20MB

		//keep track of positions already processed for
		//more robustness against corrupt files
		Set<Long> processedPositions = new HashSet<Long>();
			byte[] baseBlockBuffer = safelyAllocate(BaseBlock.BaseBlockSize, MAX_HEADER_SIZE);
				byte[] mainbuff = safelyAllocate(toRead, MAX_HEADER_SIZE);
				byte[] signBuff = safelyAllocate(toRead, MAX_HEADER_SIZE);
				byte[] avBuff = safelyAllocate(toRead, MAX_HEADER_SIZE);
				byte[] commBuff = safelyAllocate(toRead, MAX_HEADER_SIZE);
				if (processedPositions.contains(newpos)) {
					throw new RarException(RarExceptionType.badRarArchive);
				}
				processedPositions.add(newpos);
					byte[] endArchBuff = safelyAllocate(toRead, MAX_HEADER_SIZE);
				byte[] blockHeaderBuffer = safelyAllocate(BlockHeader.blockHeaderSize, MAX_HEADER_SIZE);
					byte[] fileHeaderBuffer = safelyAllocate(toRead, MAX_HEADER_SIZE);
					if (processedPositions.contains(newpos)) {
						throw new RarException(RarExceptionType.badRarArchive);
					}
					processedPositions.add(newpos);
					byte[] protectHeaderBuffer = safelyAllocate(toRead, MAX_HEADER_SIZE);
					if (processedPositions.contains(newpos)) {
						throw new RarException(RarExceptionType.badRarArchive);
					}
					processedPositions.add(newpos);
					byte[] subHeadbuffer = safelyAllocate(SubBlockHeader.SubBlockHeaderSize, MAX_HEADER_SIZE);
						byte[] macHeaderbuffer = safelyAllocate(MacInfoHeader.MacInfoHeaderSize, MAX_HEADER_SIZE);
						byte[] eaHeaderBuffer = safelyAllocate(EAHeader.EAHeaderSize, MAX_HEADER_SIZE);
						byte[] uoHeaderBuffer = safelyAllocate(toRead, MAX_HEADER_SIZE);
	private static byte[] safelyAllocate(long len, int maxSize) throws RarException {
		if (maxSize < 0) {
			throw new IllegalArgumentException("maxsize must be >= 0");
		}
		if (len < 0 || len > (long)maxSize) {
			throw new RarException(RarExceptionType.badRarArchive);
		}
		return new byte[(int)len];
	}

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.junrar.testUtil;

import com.github.junrar.Archive;
import com.github.junrar.rarfile.FileHeader;
import org.junit.Test;

import java.io.File;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class SimpleTest {

    @Test
    public void testTikaDocs() throws Exception {
        String[] expected = {"testEXCEL.xls", "13824",
                "testHTML.html", "167",
                "testOpenOffice2.odt", "26448",
                "testPDF.pdf", "34824",
                "testPPT.ppt", "16384",
                "testRTF.rtf", "3410",
                "testTXT.txt", "49",
                "testWORD.doc", "19456",
                "testXML.xml", "766"};


        File f = new File(getClass().getResource("test-documents.rar").toURI());
        Archive archive = null;
        try {
            archive = new Archive(f);

            FileHeader fileHeader = archive.nextFileHeader();
            int i = 0;
            while (fileHeader != null) {
                assertTrue(fileHeader.getFileNameString().contains(expected[i++]));
                assertEquals(Long.parseLong(expected[i++]), fileHeader.getUnpSize());
                fileHeader = archive.nextFileHeader();
            }
        } finally {
            archive.close();
        }

    }
}