mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-dfa] Check new array length to be non-negative in DFA
Fixes IDEA-254545 Inspection for allocation of array with negative size Tests are contributed by Ivo Smid (PR#1468) Co-authored-by: Ivo Smid <ivo.smid@gmail.com> GitOrigin-RevId: b58f281faa6caeeb5a7fbb06b110182994369426
This commit is contained in:
committed by
intellij-monorepo-bot
co-authored by
Ivo Smid
parent
4d31494509
commit
5e6932f3b5
@@ -30,6 +30,7 @@ change.type.arguments.to.0=Change type arguments to <{0}>
|
||||
convert.0.to.float=Convert ''{0}'' to float
|
||||
|
||||
dataflow.message.array.index.out.of.bounds=Array index is out of bounds
|
||||
dataflow.message.negative.array.size=Negative array size
|
||||
dataflow.message.arraystore=Storing element of type <code>{0}</code> to array of <code>{1}</code> elements will produce <code>ArrayStoreException</code>
|
||||
dataflow.message.assigning.null.notannotated=Assigning <code>null</code> value to non-annotated field
|
||||
dataflow.message.assigning.null=<code>null</code> is assigned to a variable that is annotated with @NotNull
|
||||
|
||||
+10
-4
@@ -1734,14 +1734,20 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
addInstruction(new PushInstruction(length, null, true));
|
||||
// stack: ... var.length
|
||||
final PsiExpression[] dimensions = expression.getArrayDimensions();
|
||||
if (dimensions.length > 0) {
|
||||
boolean sizeOnStack = false;
|
||||
int dims = dimensions.length;
|
||||
if (dims > 0) {
|
||||
for (final PsiExpression dimension : dimensions) {
|
||||
dimension.accept(this);
|
||||
if (sizeOnStack) {
|
||||
generateBoxingUnboxingInstructionFor(dimension, PsiType.INT);
|
||||
}
|
||||
DfaControlTransferValue transfer =
|
||||
shouldHandleException() ?
|
||||
myFactory.controlTransfer(myExceptionCache.get("java.lang.NegativeArraySizeException"), myTrapStack) : null;
|
||||
for (int i = dims - 1; i >= 0; i--) {
|
||||
addInstruction(new ArraySizeCheckInstruction(dimensions[i], transfer));
|
||||
if (i != 0) {
|
||||
addInstruction(new PopInstruction());
|
||||
}
|
||||
sizeOnStack = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
+3
@@ -641,6 +641,9 @@ public abstract class DataFlowInspectionBase extends AbstractBaseJavaLocalInspec
|
||||
holder.registerProblem(indexExpression, JavaAnalysisBundle.message("dataflow.message.array.index.out.of.bounds"));
|
||||
}
|
||||
});
|
||||
visitor.negativeArraySizes().forEach(dimExpression -> {
|
||||
holder.registerProblem(dimExpression, JavaAnalysisBundle.message("dataflow.message.negative.array.size"));
|
||||
});
|
||||
}
|
||||
|
||||
private static void reportArrayStoreProblems(ProblemsHolder holder, DataFlowInstructionVisitor visitor) {
|
||||
|
||||
+10
@@ -43,6 +43,7 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
||||
private final Map<PsiAssignmentExpression, Pair<PsiType, PsiType>> myArrayStoreProblems = new HashMap<>();
|
||||
private final Map<PsiMethodReferenceExpression, ConstantResult> myMethodReferenceResults = new HashMap<>();
|
||||
private final Map<PsiArrayAccessExpression, ThreeState> myOutOfBoundsArrayAccesses = new HashMap<>();
|
||||
private final Map<PsiExpression, ThreeState> myNegativeArraySizes = new HashMap<>();
|
||||
private final Set<PsiElement> myReceiverMutabilityViolation = new HashSet<>();
|
||||
private final Set<PsiElement> myArgumentMutabilityViolation = new HashSet<>();
|
||||
private final Map<PsiExpression, Boolean> mySameValueAssigned = new HashMap<>();
|
||||
@@ -193,6 +194,10 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
||||
return StreamEx.ofKeys(myOutOfBoundsArrayAccesses, ThreeState.YES::equals);
|
||||
}
|
||||
|
||||
Stream<PsiExpression> negativeArraySizes() {
|
||||
return StreamEx.ofKeys(myNegativeArraySizes, ThreeState.YES::equals);
|
||||
}
|
||||
|
||||
StreamEx<PsiCallExpression> alwaysFailingCalls() {
|
||||
return StreamEx.ofKeys(myFailingCalls, v -> v);
|
||||
}
|
||||
@@ -280,6 +285,11 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
|
||||
myOutOfBoundsArrayAccesses.merge(expression, ThreeState.fromBoolean(alwaysOutOfBounds), ThreeState::merge);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processArrayCreation(PsiExpression expression, boolean alwaysNegative) {
|
||||
myNegativeArraySizes.merge(expression, ThreeState.fromBoolean(alwaysNegative), ThreeState::merge);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processArrayStoreTypeMismatch(PsiAssignmentExpression assignmentExpression, PsiType fromType, PsiType toType) {
|
||||
if (assignmentExpression != null) {
|
||||
|
||||
+4
@@ -304,4 +304,8 @@ public abstract class InstructionVisitor {
|
||||
pushExpressionResult(runner.getFactory().getUnknown(), instruction, memState);
|
||||
return nextInstruction(instruction, runner, memState);
|
||||
}
|
||||
|
||||
public DfaInstructionState[] visitArraySizeCheck(ArraySizeCheckInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||
return nextInstruction(instruction, runner, memState);
|
||||
}
|
||||
}
|
||||
|
||||
+40
@@ -214,6 +214,10 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
|
||||
}
|
||||
|
||||
protected void processArrayCreation(PsiExpression expression, boolean alwaysNegative) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public DfaInstructionState[] visitMethodReference(MethodReferenceInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||
PsiMethodReferenceExpression expression = instruction.getExpression();
|
||||
@@ -285,6 +289,42 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
return new DfaCallArguments(qualifier, arguments, MutationSignature.fromMethod(method));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DfaInstructionState[] visitArraySizeCheck(ArraySizeCheckInstruction instruction,
|
||||
DataFlowRunner runner, DfaMemoryState memState) {
|
||||
DfaValue arraySize = memState.peek();
|
||||
DfaControlTransferValue transfer = instruction.getNegativeSizeExceptionTransfer();
|
||||
DfaCondition cond = arraySize.cond(RelationType.GE, runner.getFactory().getInt(0));
|
||||
Instruction nextInstruction = runner.getInstruction(instruction.getIndex() + 1);
|
||||
DfaInstructionState nextState = new DfaInstructionState(nextInstruction, memState);
|
||||
if (cond.equals(DfaCondition.getTrue())) {
|
||||
return new DfaInstructionState[]{nextState};
|
||||
}
|
||||
if (transfer == null) {
|
||||
boolean hasNonNegative = memState.applyCondition(cond);
|
||||
processArrayCreation(instruction.getExpression(), !hasNonNegative);
|
||||
if (!hasNonNegative) {
|
||||
return DfaInstructionState.EMPTY_ARRAY;
|
||||
}
|
||||
return new DfaInstructionState[]{nextState};
|
||||
}
|
||||
DfaMemoryState negativeSize = memState.createCopy();
|
||||
boolean hasNonNegative = memState.applyCondition(cond);
|
||||
boolean hasNegative = negativeSize.applyCondition(cond.negate());
|
||||
List<DfaInstructionState> result = new ArrayList<>();
|
||||
if (hasNonNegative) {
|
||||
result.add(nextState);
|
||||
}
|
||||
if (hasNegative) {
|
||||
List<DfaInstructionState> states = transfer.dispatch(negativeSize, runner);
|
||||
for (DfaInstructionState negState : states) {
|
||||
negState.getMemoryState().markEphemeral();
|
||||
}
|
||||
result.addAll(states);
|
||||
}
|
||||
return result.toArray(DfaInstructionState.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DfaInstructionState[] visitTypeCast(TypeCastInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||
PsiType type = instruction.getCastTo();
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInspection.dataFlow.instructions;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.*;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ArraySizeCheckInstruction extends Instruction {
|
||||
final @NotNull PsiExpression myExpression;
|
||||
final @Nullable DfaControlTransferValue myTransferValue;
|
||||
|
||||
public ArraySizeCheckInstruction(@NotNull PsiExpression expression,
|
||||
@Nullable DfaControlTransferValue value) {
|
||||
myExpression = expression;
|
||||
myTransferValue = value;
|
||||
}
|
||||
|
||||
public @NotNull PsiExpression getExpression() {
|
||||
return myExpression;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DfaControlTransferValue getNegativeSizeExceptionTransfer() {
|
||||
return myTransferValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DfaInstructionState[] accept(DataFlowRunner runner,
|
||||
DfaMemoryState stateBefore,
|
||||
InstructionVisitor visitor) {
|
||||
return visitor.visitArraySizeCheck(this, runner, stateBefore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CHECK_ARRAY_SIZE";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
class ArrayNegativeSize {
|
||||
private static final int VAL1 = -10;
|
||||
private static int VAL2 = -10;
|
||||
private static final int VAL3 = -10 * 2;
|
||||
private static final short VAL4 = -10 * 2;
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array1 = new int[<warning descr="Negative array size">-10</warning>];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array2 = createArray();
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array3 = new int[]{};
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array4 = new int[<warning descr="Negative array size">10 - 100</warning>];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array5 = new int[((int) (Integer.valueOf(Integer.MAX_VALUE).longValue() - 100)) + 100 - 10];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array5 = new int[<warning descr="Negative array size">((int) (Integer.valueOf(Integer.MAX_VALUE).longValue() - 100L)) + 100 + 10</warning>];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array6 = new int[((int) (Integer.valueOf(Integer.MAX_VALUE).longValue() - 100)) + 99];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array7 = new int[<warning descr="Negative array size">(int) (((long) ((Integer.MAX_VALUE - 100)) + 100) * 2)</warning>];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array8 = new int[num()];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array9 = new int[<warning descr="Negative array size">VAL1</warning>];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array10 = new int[VAL2];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array11 = new int[<warning descr="Negative array size">VAL3</warning>];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array12 = new int[<warning descr="Negative array size">VAL4</warning>];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[][] array13 = new int[0][<warning descr="Negative array size">-1</warning>];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[][][] array14 = new int[0][0][<warning descr="Negative array size">-1</warning>];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[][][] array15 = new int[-1][-2][<warning descr="Negative array size">-3</warning>];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[][][] array15 = new int[-1][<warning descr="Negative array size">-2</warning>][3];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[][][] array15 = new int[<warning descr="Negative array size">-1</warning>][2][3];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array16 = new int[<warning descr="Negative array size">-07</warning>];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array17 = new int[<warning descr="Negative array size">100 * -077</warning>];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array18 = new int[0x7fffffff];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array19 = new int[<warning descr="Negative array size">0x7fffffff + 1</warning>];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array20 = new int[0b1111111111111111111111111111111];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
int[] array21 = new int[<warning descr="Negative array size">0b1111111111111111111111111111111 + 1</warning>];
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
action(new int[<warning descr="Negative array size">-1000000000</warning>]);
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
action(new int[<warning descr="Negative array size">-0xcafe</warning>]);
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
action(new int[<warning descr="Negative array size">(int) -10000000000000L</warning>]);
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
action(new int[<warning descr="Negative array size">2147483647 + 1</warning>]);
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
action(new int["".length() + 456]);
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
action(new int[<warning descr="Negative array size">"".length() - 456</warning>]);
|
||||
}
|
||||
VAL2++;
|
||||
}
|
||||
|
||||
private static int[] createArray() {
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
private static void action(Object obj) {
|
||||
}
|
||||
|
||||
private static int num() {
|
||||
return -123;
|
||||
}
|
||||
|
||||
final int[] array1 = new int[<warning descr="Negative array size">-10</warning>];
|
||||
|
||||
void foo(int size) {
|
||||
int[] data = new int[size];
|
||||
if (<warning descr="Condition 'size < 0' is always 'false'">size < 0</warning>) {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
|
||||
void tryCatch(int size) {
|
||||
try {
|
||||
int[] arr = new int[size];
|
||||
} catch (NegativeArraySizeException e) {
|
||||
if (<warning descr="Condition 'size >= 0' is always 'false'">size >= 0</warning>) {
|
||||
System.out.println("impossible");
|
||||
}
|
||||
}
|
||||
}
|
||||
void testUnboxing(Integer len) {
|
||||
int[] arr = new int[len];
|
||||
long l = len.longValue();
|
||||
if (<warning descr="Condition 'l < 0' is always 'false'">l < 0</warning>) {}
|
||||
}
|
||||
}
|
||||
@@ -685,4 +685,5 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
|
||||
public void testDoubleArrayDiff() { doTest(); }
|
||||
public void testInferenceInPrivateOrLocalClass() { doTest(); }
|
||||
public void testArraysCopyOf() { doTest(); }
|
||||
public void testArrayNegativeSize() { doTest(); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user