This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-bcel.git


The following commit(s) were added to refs/heads/master by this push:
     new 512a70bb Silent u2/u1 count truncation across dump paths corrupts 
emitted bytecode (f013).
512a70bb is described below

commit 512a70bb882c27d201e07f1645bd3679f51ab3b3
Author: Gary Gregory <[email protected]>
AuthorDate: Fri Sep 4 17:12:42 2026 -0400

    Silent u2/u1 count truncation across dump paths corrupts emitted
    bytecode (f013).
---
 .../org/apache/bcel/classfile/AnnotationEntry.java |  3 +-
 .../org/apache/bcel/classfile/Annotations.java     |  3 +-
 .../apache/bcel/classfile/ArrayElementValue.java   |  4 +-
 .../org/apache/bcel/classfile/BootstrapMethod.java |  3 +-
 .../apache/bcel/classfile/BootstrapMethods.java    |  3 +-
 src/main/java/org/apache/bcel/classfile/Code.java  |  5 ++-
 .../org/apache/bcel/classfile/ConstantPool.java    | 14 +++---
 .../org/apache/bcel/classfile/ExceptionTable.java  |  2 +-
 .../org/apache/bcel/classfile/FieldOrMethod.java   |  4 +-
 .../org/apache/bcel/classfile/InnerClasses.java    |  2 +-
 .../java/org/apache/bcel/classfile/JavaClass.java  |  9 ++--
 .../org/apache/bcel/classfile/LineNumberTable.java |  2 +-
 .../apache/bcel/classfile/LocalVariableTable.java  |  2 +-
 .../bcel/classfile/LocalVariableTypeTable.java     |  2 +-
 .../apache/bcel/classfile/MethodParameters.java    |  3 +-
 .../java/org/apache/bcel/classfile/Module.java     | 11 ++---
 .../org/apache/bcel/classfile/ModulePackages.java  |  2 +-
 .../org/apache/bcel/classfile/NestMembers.java     |  2 +-
 .../bcel/classfile/ParameterAnnotationEntry.java   |  4 +-
 .../bcel/classfile/ParameterAnnotations.java       |  4 +-
 .../apache/bcel/classfile/PermittedSubclasses.java |  2 +-
 .../java/org/apache/bcel/classfile/Record.java     |  2 +-
 .../apache/bcel/classfile/RecordComponentInfo.java |  3 +-
 .../java/org/apache/bcel/classfile/StackMap.java   |  2 +-
 .../org/apache/bcel/classfile/StackMapEntry.java   |  5 ++-
 .../apache/bcel/generic/AnnotationEntryGen.java    |  7 +--
 .../apache/bcel/generic/ArrayElementValueGen.java  |  3 +-
 .../apache/bcel/classfile/DumpU2OverflowTest.java  | 52 ++++++++++++++++++++++
 28 files changed, 117 insertions(+), 43 deletions(-)

diff --git a/src/main/java/org/apache/bcel/classfile/AnnotationEntry.java 
b/src/main/java/org/apache/bcel/classfile/AnnotationEntry.java
index 0c3ae5e1..9d2ba196 100644
--- a/src/main/java/org/apache/bcel/classfile/AnnotationEntry.java
+++ b/src/main/java/org/apache/bcel/classfile/AnnotationEntry.java
@@ -25,6 +25,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Stream;
 
+import org.apache.bcel.util.Args;
 import org.apache.commons.lang3.stream.Streams;
 
 /**
@@ -136,7 +137,7 @@ public class AnnotationEntry implements Node {
      */
     public void dump(final DataOutputStream dos) throws IOException {
         dos.writeShort(typeIndex); // u2 index of type name in cpool
-        dos.writeShort(elementValuePairs.size()); // u2 element_value pair
+        dos.writeShort(Args.requireU2(elementValuePairs.size(), 
"elementValuePairs.size()")); // u2 element_value pair
         // count
         for (final ElementValuePair envp : elementValuePairs) {
             envp.dump(dos);
diff --git a/src/main/java/org/apache/bcel/classfile/Annotations.java 
b/src/main/java/org/apache/bcel/classfile/Annotations.java
index 8d77d3ed..9a4065f8 100644
--- a/src/main/java/org/apache/bcel/classfile/Annotations.java
+++ b/src/main/java/org/apache/bcel/classfile/Annotations.java
@@ -25,6 +25,7 @@ import java.util.Iterator;
 import java.util.stream.Stream;
 
 import org.apache.bcel.Const;
+import org.apache.bcel.util.Args;
 
 /**
  * base class for annotations
@@ -158,7 +159,7 @@ public abstract class Annotations extends Attribute 
implements Iterable<Annotati
      * @throws IOException Thrown if an I/O error occurs.
      */
     protected void writeAnnotations(final DataOutputStream dos) throws 
IOException {
-        dos.writeShort(annotationTable.length);
+        dos.writeShort(Args.requireU2(annotationTable.length, 
"annotationTable.length"));
         for (final AnnotationEntry element : annotationTable) {
             element.dump(dos);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/ArrayElementValue.java 
b/src/main/java/org/apache/bcel/classfile/ArrayElementValue.java
index 43404152..5a8d3195 100644
--- a/src/main/java/org/apache/bcel/classfile/ArrayElementValue.java
+++ b/src/main/java/org/apache/bcel/classfile/ArrayElementValue.java
@@ -21,6 +21,8 @@ package org.apache.bcel.classfile;
 import java.io.DataOutputStream;
 import java.io.IOException;
 
+import org.apache.bcel.util.Args;
+
 /**
  * Represents an array element value in an annotation.
  *
@@ -48,7 +50,7 @@ public class ArrayElementValue extends ElementValue {
     @Override
     public void dump(final DataOutputStream dos) throws IOException {
         dos.writeByte(super.getType()); // u1 type of value (ARRAY == '[')
-        dos.writeShort(elementValues.length);
+        dos.writeShort(Args.requireU2(elementValues.length, 
"elementValues.length"));
         for (final ElementValue evalue : elementValues) {
             evalue.dump(dos);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/BootstrapMethod.java 
b/src/main/java/org/apache/bcel/classfile/BootstrapMethod.java
index 8111e61f..c44af889 100644
--- a/src/main/java/org/apache/bcel/classfile/BootstrapMethod.java
+++ b/src/main/java/org/apache/bcel/classfile/BootstrapMethod.java
@@ -25,6 +25,7 @@ import java.io.IOException;
 import java.util.Arrays;
 
 import org.apache.bcel.Const;
+import org.apache.bcel.util.Args;
 import org.apache.commons.lang3.ArrayUtils;
 
 /**
@@ -108,7 +109,7 @@ public class BootstrapMethod implements Cloneable {
      */
     public final void dump(final DataOutputStream file) throws IOException {
         file.writeShort(bootstrapMethodRef);
-        file.writeShort(bootstrapArguments.length);
+        file.writeShort(Args.requireU2(bootstrapArguments.length, 
"bootstrapArguments.length"));
         for (final int bootstrapArgument : bootstrapArguments) {
             file.writeShort(bootstrapArgument);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/BootstrapMethods.java 
b/src/main/java/org/apache/bcel/classfile/BootstrapMethods.java
index a3a8dcaf..86bd897d 100644
--- a/src/main/java/org/apache/bcel/classfile/BootstrapMethods.java
+++ b/src/main/java/org/apache/bcel/classfile/BootstrapMethods.java
@@ -26,6 +26,7 @@ import java.util.Iterator;
 import java.util.stream.Stream;
 
 import org.apache.bcel.Const;
+import org.apache.bcel.util.Args;
 
 /**
  * This class represents a BootstrapMethods attribute.
@@ -118,7 +119,7 @@ public class BootstrapMethods extends Attribute implements 
Iterable<BootstrapMet
     public final void dump(final DataOutputStream file) throws IOException {
         super.dump(file);
 
-        file.writeShort(bootstrapMethods.length);
+        file.writeShort(Args.requireU2(bootstrapMethods.length, 
"bootstrapMethods.length"));
         for (final BootstrapMethod bootstrapMethod : bootstrapMethods) {
             bootstrapMethod.dump(file);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/Code.java 
b/src/main/java/org/apache/bcel/classfile/Code.java
index 0f5de194..d6a71557 100644
--- a/src/main/java/org/apache/bcel/classfile/Code.java
+++ b/src/main/java/org/apache/bcel/classfile/Code.java
@@ -202,11 +202,11 @@ public final class Code extends Attribute {
         file.writeShort(maxLocals);
         file.writeInt(code.length);
         file.write(code, 0, code.length);
-        file.writeShort(exceptionTable.length);
+        file.writeShort(Args.requireU2(exceptionTable.length, 
"exceptionTable.length"));
         for (final CodeException exception : exceptionTable) {
             exception.dump(file);
         }
-        file.writeShort(attributes.length);
+        file.writeShort(Args.requireU2(attributes.length, 
"attributes.length"));
         for (final Attribute attribute : attributes) {
             attribute.dump(file);
         }
@@ -359,6 +359,7 @@ public final class Code extends Attribute {
      */
     public void setExceptionTable(final CodeException[] exceptionTable) {
         this.exceptionTable = exceptionTable != null ? exceptionTable : 
CodeException.EMPTY_ARRAY;
+        Args.requireU2(this.exceptionTable.length, "exceptionTable.length");
         super.setLength(calculateLength()); // Adjust length
     }
 
diff --git a/src/main/java/org/apache/bcel/classfile/ConstantPool.java 
b/src/main/java/org/apache/bcel/classfile/ConstantPool.java
index 975030e6..6a62dc2f 100644
--- a/src/main/java/org/apache/bcel/classfile/ConstantPool.java
+++ b/src/main/java/org/apache/bcel/classfile/ConstantPool.java
@@ -245,13 +245,15 @@ public class ConstantPool implements Cloneable, Node, 
Iterable<Constant> {
      */
     public void dump(final DataOutputStream file) throws IOException {
         /*
-         * Constants over the size of the constant pool shall not be written 
out. This is a redundant measure as the ConstantPoolGen should have already
-         * reported an error back in the situation.
+         * A constant pool larger than the u2 count field can represent must 
fail loudly instead of being silently
+         * truncated: class structures may still reference the dropped 
entries, and a wrapped count with extra bodies
+         * desynchronizes any consumer that reparses the emitted bytes (the 
CVE-2022-42920 writer-overflow shape).
          */
-        final int size = Math.min(constantPool.length, Const.MAX_CP_ENTRIES);
-
-        file.writeShort(size);
-        for (int i = 1; i < size; i++) {
+        if (constantPool.length > Const.MAX_CP_ENTRIES) {
+            throw new ClassFormatException("Constant pool size " + 
constantPool.length + " exceeds the u2 maximum of " + Const.MAX_CP_ENTRIES);
+        }
+        file.writeShort(constantPool.length);
+        for (int i = 1; i < constantPool.length; i++) {
             if (constantPool[i] != null) {
                 constantPool[i].dump(file);
             }
diff --git a/src/main/java/org/apache/bcel/classfile/ExceptionTable.java 
b/src/main/java/org/apache/bcel/classfile/ExceptionTable.java
index 48aca824..ae490f44 100644
--- a/src/main/java/org/apache/bcel/classfile/ExceptionTable.java
+++ b/src/main/java/org/apache/bcel/classfile/ExceptionTable.java
@@ -122,7 +122,7 @@ public final class ExceptionTable extends Attribute {
     @Override
     public void dump(final DataOutputStream file) throws IOException {
         super.dump(file);
-        file.writeShort(exceptionIndexTable.length);
+        file.writeShort(Args.requireU2(exceptionIndexTable.length, 
"exceptionIndexTable.length"));
         for (final int index : exceptionIndexTable) {
             file.writeShort(index);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/FieldOrMethod.java 
b/src/main/java/org/apache/bcel/classfile/FieldOrMethod.java
index e779686e..6b53db55 100644
--- a/src/main/java/org/apache/bcel/classfile/FieldOrMethod.java
+++ b/src/main/java/org/apache/bcel/classfile/FieldOrMethod.java
@@ -24,6 +24,8 @@ import java.io.DataOutputStream;
 import java.io.IOException;
 import java.util.Arrays;
 
+import org.apache.bcel.util.Args;
+
 /**
  * Abstract super class for fields and methods.
  */
@@ -155,7 +157,7 @@ public abstract class FieldOrMethod extends AccessFlags 
implements Cloneable, No
         file.writeShort(super.getAccessFlags());
         file.writeShort(name_index);
         file.writeShort(signature_index);
-        file.writeShort(attributes_count);
+        file.writeShort(Args.requireU2(attributes_count, "attributes_count"));
         for (final Attribute attribute : attributes) {
             attribute.dump(file);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/InnerClasses.java 
b/src/main/java/org/apache/bcel/classfile/InnerClasses.java
index e130c77a..8a6aab0b 100644
--- a/src/main/java/org/apache/bcel/classfile/InnerClasses.java
+++ b/src/main/java/org/apache/bcel/classfile/InnerClasses.java
@@ -120,7 +120,7 @@ public final class InnerClasses extends Attribute 
implements Iterable<InnerClass
     @Override
     public void dump(final DataOutputStream file) throws IOException {
         super.dump(file);
-        file.writeShort(innerClasses.length);
+        file.writeShort(Args.requireU2(innerClasses.length, 
"innerClasses.length"));
         for (final InnerClass innerClass : innerClasses) {
             innerClass.dump(file);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/JavaClass.java 
b/src/main/java/org/apache/bcel/classfile/JavaClass.java
index 287c9cf4..7a45489d 100644
--- a/src/main/java/org/apache/bcel/classfile/JavaClass.java
+++ b/src/main/java/org/apache/bcel/classfile/JavaClass.java
@@ -35,6 +35,7 @@ import java.util.TreeSet;
 
 import org.apache.bcel.Const;
 import org.apache.bcel.generic.Type;
+import org.apache.bcel.util.Args;
 import org.apache.bcel.util.BCELComparator;
 import org.apache.bcel.util.ClassQueue;
 import org.apache.bcel.util.SyntheticRepository;
@@ -348,20 +349,20 @@ public class JavaClass extends AccessFlags implements 
Cloneable, Node, Comparabl
         file.writeShort(super.getAccessFlags());
         file.writeShort(classNameIndex);
         file.writeShort(superclassNameIndex);
-        file.writeShort(interfaces.length);
+        file.writeShort(Args.requireU2(interfaces.length, 
"interfaces.length"));
         for (final int interface1 : interfaces) {
             file.writeShort(interface1);
         }
-        file.writeShort(fields.length);
+        file.writeShort(Args.requireU2(fields.length, "fields.length"));
         for (final Field field : fields) {
             field.dump(file);
         }
-        file.writeShort(methods.length);
+        file.writeShort(Args.requireU2(methods.length, "methods.length"));
         for (final Method method : methods) {
             method.dump(file);
         }
         if (attributes != null) {
-            file.writeShort(attributes.length);
+            file.writeShort(Args.requireU2(attributes.length, 
"attributes.length"));
             for (final Attribute attribute : attributes) {
                 attribute.dump(file);
             }
diff --git a/src/main/java/org/apache/bcel/classfile/LineNumberTable.java 
b/src/main/java/org/apache/bcel/classfile/LineNumberTable.java
index b206abcc..57d5e538 100644
--- a/src/main/java/org/apache/bcel/classfile/LineNumberTable.java
+++ b/src/main/java/org/apache/bcel/classfile/LineNumberTable.java
@@ -119,7 +119,7 @@ public final class LineNumberTable extends Attribute 
implements Iterable<LineNum
     @Override
     public void dump(final DataOutputStream file) throws IOException {
         super.dump(file);
-        file.writeShort(lineNumberTable.length);
+        file.writeShort(Args.requireU2(lineNumberTable.length, 
"lineNumberTable.length"));
         for (final LineNumber lineNumber : lineNumberTable) {
             lineNumber.dump(file);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/LocalVariableTable.java 
b/src/main/java/org/apache/bcel/classfile/LocalVariableTable.java
index 59218c4b..16a3085e 100644
--- a/src/main/java/org/apache/bcel/classfile/LocalVariableTable.java
+++ b/src/main/java/org/apache/bcel/classfile/LocalVariableTable.java
@@ -115,7 +115,7 @@ public class LocalVariableTable extends Attribute 
implements Iterable<LocalVaria
     @Override
     public final void dump(final DataOutputStream file) throws IOException {
         super.dump(file);
-        file.writeShort(localVariableTable.length);
+        file.writeShort(Args.requireU2(localVariableTable.length, 
"localVariableTable.length"));
         for (final LocalVariable variable : localVariableTable) {
             variable.dump(file);
         }
diff --git 
a/src/main/java/org/apache/bcel/classfile/LocalVariableTypeTable.java 
b/src/main/java/org/apache/bcel/classfile/LocalVariableTypeTable.java
index 61e61a65..16c94593 100644
--- a/src/main/java/org/apache/bcel/classfile/LocalVariableTypeTable.java
+++ b/src/main/java/org/apache/bcel/classfile/LocalVariableTypeTable.java
@@ -120,7 +120,7 @@ public class LocalVariableTypeTable extends Attribute 
implements Iterable<LocalV
     @Override
     public final void dump(final DataOutputStream file) throws IOException {
         super.dump(file);
-        file.writeShort(localVariableTypeTable.length);
+        file.writeShort(Args.requireU2(localVariableTypeTable.length, 
"localVariableTypeTable.length"));
         for (final LocalVariable variable : localVariableTypeTable) {
             variable.dump(file);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/MethodParameters.java 
b/src/main/java/org/apache/bcel/classfile/MethodParameters.java
index ada146e1..624b00c8 100644
--- a/src/main/java/org/apache/bcel/classfile/MethodParameters.java
+++ b/src/main/java/org/apache/bcel/classfile/MethodParameters.java
@@ -27,6 +27,7 @@ import java.util.Iterator;
 import java.util.stream.Stream;
 
 import org.apache.bcel.Const;
+import org.apache.bcel.util.Args;
 
 /**
  * This class represents a MethodParameters attribute.
@@ -76,7 +77,7 @@ public class MethodParameters extends Attribute implements 
Iterable<MethodParame
     @Override
     public void dump(final DataOutputStream file) throws IOException {
         super.dump(file);
-        file.writeByte(parameters.length);
+        file.writeByte(Args.requireU1(parameters.length, "parameters.length"));
         for (final MethodParameter parameter : parameters) {
             parameter.dump(file);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/Module.java 
b/src/main/java/org/apache/bcel/classfile/Module.java
index a40c8d98..4fafe757 100644
--- a/src/main/java/org/apache/bcel/classfile/Module.java
+++ b/src/main/java/org/apache/bcel/classfile/Module.java
@@ -25,6 +25,7 @@ import java.io.IOException;
 import java.util.Arrays;
 
 import org.apache.bcel.Const;
+import org.apache.bcel.util.Args;
 
 /**
  * This class is derived from <em>Attribute</em> and represents the list of 
modules required, exported, opened or
@@ -153,27 +154,27 @@ public final class Module extends Attribute {
         file.writeShort(moduleFlags);
         file.writeShort(moduleVersionIndex);
 
-        file.writeShort(requiresTable.length);
+        file.writeShort(Args.requireU2(requiresTable.length, 
"requiresTable.length"));
         for (final ModuleRequires entry : requiresTable) {
             entry.dump(file);
         }
 
-        file.writeShort(exportsTable.length);
+        file.writeShort(Args.requireU2(exportsTable.length, 
"exportsTable.length"));
         for (final ModuleExports entry : exportsTable) {
             entry.dump(file);
         }
 
-        file.writeShort(opensTable.length);
+        file.writeShort(Args.requireU2(opensTable.length, 
"opensTable.length"));
         for (final ModuleOpens entry : opensTable) {
             entry.dump(file);
         }
 
-        file.writeShort(usesIndex.length);
+        file.writeShort(Args.requireU2(usesIndex.length, "usesIndex.length"));
         for (final int entry : usesIndex) {
             file.writeShort(entry);
         }
 
-        file.writeShort(providesTable.length);
+        file.writeShort(Args.requireU2(providesTable.length, 
"providesTable.length"));
         for (final ModuleProvides entry : providesTable) {
             entry.dump(file);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/ModulePackages.java 
b/src/main/java/org/apache/bcel/classfile/ModulePackages.java
index 45584982..53e55d47 100644
--- a/src/main/java/org/apache/bcel/classfile/ModulePackages.java
+++ b/src/main/java/org/apache/bcel/classfile/ModulePackages.java
@@ -109,7 +109,7 @@ public final class ModulePackages extends Attribute {
     @Override
     public void dump(final DataOutputStream file) throws IOException {
         super.dump(file);
-        file.writeShort(packageIndexTable.length);
+        file.writeShort(Args.requireU2(packageIndexTable.length, 
"packageIndexTable.length"));
         for (final int index : packageIndexTable) {
             file.writeShort(index);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/NestMembers.java 
b/src/main/java/org/apache/bcel/classfile/NestMembers.java
index 2cac6c29..dbfb928e 100644
--- a/src/main/java/org/apache/bcel/classfile/NestMembers.java
+++ b/src/main/java/org/apache/bcel/classfile/NestMembers.java
@@ -110,7 +110,7 @@ public final class NestMembers extends Attribute {
     @Override
     public void dump(final DataOutputStream file) throws IOException {
         super.dump(file);
-        file.writeShort(classes.length);
+        file.writeShort(Args.requireU2(classes.length, "classes.length"));
         for (final int index : classes) {
             file.writeShort(index);
         }
diff --git 
a/src/main/java/org/apache/bcel/classfile/ParameterAnnotationEntry.java 
b/src/main/java/org/apache/bcel/classfile/ParameterAnnotationEntry.java
index b68e630e..df48f743 100644
--- a/src/main/java/org/apache/bcel/classfile/ParameterAnnotationEntry.java
+++ b/src/main/java/org/apache/bcel/classfile/ParameterAnnotationEntry.java
@@ -25,6 +25,8 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import org.apache.bcel.util.Args;
+
 /**
  * represents one parameter annotation in the parameter annotation table
  *
@@ -94,7 +96,7 @@ public class ParameterAnnotationEntry implements Node {
      * @throws IOException Thrown if an I/O error occurs.
      */
     public void dump(final DataOutputStream dos) throws IOException {
-        dos.writeShort(annotationTable.length);
+        dos.writeShort(Args.requireU2(annotationTable.length, 
"annotationTable.length"));
         for (final AnnotationEntry entry : annotationTable) {
             entry.dump(dos);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/ParameterAnnotations.java 
b/src/main/java/org/apache/bcel/classfile/ParameterAnnotations.java
index ac3abf42..8d39af10 100644
--- a/src/main/java/org/apache/bcel/classfile/ParameterAnnotations.java
+++ b/src/main/java/org/apache/bcel/classfile/ParameterAnnotations.java
@@ -24,6 +24,8 @@ import java.io.IOException;
 import java.util.Iterator;
 import java.util.stream.Stream;
 
+import org.apache.bcel.util.Args;
+
 /**
  * base class for parameter annotations
  *
@@ -93,7 +95,7 @@ public abstract class ParameterAnnotations extends Attribute 
implements Iterable
     @Override
     public void dump(final DataOutputStream dos) throws IOException {
         super.dump(dos);
-        dos.writeByte(parameterAnnotationTable.length);
+        dos.writeByte(Args.requireU1(parameterAnnotationTable.length, 
"parameterAnnotationTable.length"));
 
         for (final ParameterAnnotationEntry element : 
parameterAnnotationTable) {
             element.dump(dos);
diff --git a/src/main/java/org/apache/bcel/classfile/PermittedSubclasses.java 
b/src/main/java/org/apache/bcel/classfile/PermittedSubclasses.java
index 3faa5e78..dec87cbd 100644
--- a/src/main/java/org/apache/bcel/classfile/PermittedSubclasses.java
+++ b/src/main/java/org/apache/bcel/classfile/PermittedSubclasses.java
@@ -113,7 +113,7 @@ public final class PermittedSubclasses extends Attribute {
     @Override
     public void dump(final DataOutputStream file) throws IOException {
         super.dump(file);
-        file.writeShort(classes.length);
+        file.writeShort(Args.requireU2(classes.length, "classes.length"));
         for (final int index : classes) {
             file.writeShort(index);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/Record.java 
b/src/main/java/org/apache/bcel/classfile/Record.java
index 4949c5e3..de31aaab 100644
--- a/src/main/java/org/apache/bcel/classfile/Record.java
+++ b/src/main/java/org/apache/bcel/classfile/Record.java
@@ -116,7 +116,7 @@ public final class Record extends Attribute {
     @Override
     public void dump(final DataOutputStream file) throws IOException {
         super.dump(file);
-        file.writeShort(components.length);
+        file.writeShort(Args.requireU2(components.length, 
"components.length"));
         for (final RecordComponentInfo component : components) {
             component.dump(file);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/RecordComponentInfo.java 
b/src/main/java/org/apache/bcel/classfile/RecordComponentInfo.java
index 6d6e5af0..e32e1ac7 100644
--- a/src/main/java/org/apache/bcel/classfile/RecordComponentInfo.java
+++ b/src/main/java/org/apache/bcel/classfile/RecordComponentInfo.java
@@ -23,6 +23,7 @@ import java.io.DataOutputStream;
 import java.io.IOException;
 
 import org.apache.bcel.Const;
+import org.apache.bcel.util.Args;
 
 /**
  * Record component info from a record. Instances from this class maps
@@ -71,7 +72,7 @@ public class RecordComponentInfo implements Node {
     public void dump(final DataOutputStream file) throws IOException {
         file.writeShort(index);
         file.writeShort(descriptorIndex);
-        file.writeShort(attributes.length);
+        file.writeShort(Args.requireU2(attributes.length, 
"attributes.length"));
         for (final Attribute attribute : attributes) {
             attribute.dump(file);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/StackMap.java 
b/src/main/java/org/apache/bcel/classfile/StackMap.java
index 60e9ce2c..72a435ce 100644
--- a/src/main/java/org/apache/bcel/classfile/StackMap.java
+++ b/src/main/java/org/apache/bcel/classfile/StackMap.java
@@ -113,7 +113,7 @@ public final class StackMap extends Attribute {
     @Override
     public void dump(final DataOutputStream file) throws IOException {
         super.dump(file);
-        file.writeShort(table.length);
+        file.writeShort(Args.requireU2(table.length, "table.length"));
         for (final StackMapEntry entry : table) {
             entry.dump(file);
         }
diff --git a/src/main/java/org/apache/bcel/classfile/StackMapEntry.java 
b/src/main/java/org/apache/bcel/classfile/StackMapEntry.java
index e6c77233..93b2fed8 100644
--- a/src/main/java/org/apache/bcel/classfile/StackMapEntry.java
+++ b/src/main/java/org/apache/bcel/classfile/StackMapEntry.java
@@ -25,6 +25,7 @@ import java.io.IOException;
 import java.util.Arrays;
 
 import org.apache.bcel.Const;
+import org.apache.bcel.util.Args;
 
 /**
  * This class represents a stack map entry recording the types of local 
variables and the of stack items at a given
@@ -198,11 +199,11 @@ public final class StackMapEntry implements Node, 
Cloneable {
             }
         } else if (frameType == Const.FULL_FRAME) {
             file.writeShort(byteCodeOffset);
-            file.writeShort(typesOfLocals.length);
+            file.writeShort(Args.requireU2(typesOfLocals.length, 
"typesOfLocals.length"));
             for (final StackMapType type : typesOfLocals) {
                 type.dump(file);
             }
-            file.writeShort(typesOfStackItems.length);
+            file.writeShort(Args.requireU2(typesOfStackItems.length, 
"typesOfStackItems.length"));
             for (final StackMapType type : typesOfStackItems) {
                 type.dump(file);
             }
diff --git a/src/main/java/org/apache/bcel/generic/AnnotationEntryGen.java 
b/src/main/java/org/apache/bcel/generic/AnnotationEntryGen.java
index e91b29e1..feeadc34 100644
--- a/src/main/java/org/apache/bcel/generic/AnnotationEntryGen.java
+++ b/src/main/java/org/apache/bcel/generic/AnnotationEntryGen.java
@@ -36,6 +36,7 @@ import org.apache.bcel.classfile.RuntimeInvisibleAnnotations;
 import org.apache.bcel.classfile.RuntimeInvisibleParameterAnnotations;
 import org.apache.bcel.classfile.RuntimeVisibleAnnotations;
 import org.apache.bcel.classfile.RuntimeVisibleParameterAnnotations;
+import org.apache.bcel.util.Args;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.stream.Streams;
 
@@ -147,7 +148,7 @@ public class AnnotationEntryGen {
             // Lets do the visible ones
             final ByteArrayOutputStream rvaBytes = new ByteArrayOutputStream();
             try (DataOutputStream rvaDos = new DataOutputStream(rvaBytes)) {
-                rvaDos.writeByte(vec.length); // First goes number of 
parameters
+                rvaDos.writeByte(Args.requireU1(vec.length, "vec.length")); // 
First goes number of parameters
                 for (int i = 0; i < vec.length; i++) {
                     rvaDos.writeShort(visCount[i]);
                     if (visCount[i] > 0) {
@@ -162,7 +163,7 @@ public class AnnotationEntryGen {
             // Lets do the invisible ones
             final ByteArrayOutputStream riaBytes = new ByteArrayOutputStream();
             try (DataOutputStream riaDos = new DataOutputStream(riaBytes)) {
-                riaDos.writeByte(vec.length); // First goes number of 
parameters
+                riaDos.writeByte(Args.requireU1(vec.length, "vec.length")); // 
First goes number of parameters
                 for (int i = 0; i < vec.length; i++) {
                     riaDos.writeShort(invisCount[i]);
                     if (invisCount[i] > 0) {
@@ -294,7 +295,7 @@ public class AnnotationEntryGen {
      */
     public void dump(final DataOutputStream dos) throws IOException {
         dos.writeShort(typeIndex); // u2 index of type name in cpool
-        dos.writeShort(evs.size()); // u2 element_value pair count
+        dos.writeShort(Args.requireU2(evs.size(), "evs.size()")); // u2 
element_value pair count
         for (final ElementValuePairGen envp : evs) {
             envp.dump(dos);
         }
diff --git a/src/main/java/org/apache/bcel/generic/ArrayElementValueGen.java 
b/src/main/java/org/apache/bcel/generic/ArrayElementValueGen.java
index c78b7c34..773c41bb 100644
--- a/src/main/java/org/apache/bcel/generic/ArrayElementValueGen.java
+++ b/src/main/java/org/apache/bcel/generic/ArrayElementValueGen.java
@@ -26,6 +26,7 @@ import java.util.stream.Collectors;
 
 import org.apache.bcel.classfile.ArrayElementValue;
 import org.apache.bcel.classfile.ElementValue;
+import org.apache.bcel.util.Args;
 import org.apache.commons.lang3.stream.Streams;
 
 /**
@@ -91,7 +92,7 @@ public class ArrayElementValueGen extends ElementValueGen {
     @Override
     public void dump(final DataOutputStream dos) throws IOException {
         dos.writeByte(super.getElementValueType()); // u1 type of value (ARRAY 
== '[')
-        dos.writeShort(evalues.size());
+        dos.writeShort(Args.requireU2(evalues.size(), "evalues.size()"));
         for (final ElementValueGen element : evalues) {
             element.dump(dos);
         }
diff --git a/src/test/java/org/apache/bcel/classfile/DumpU2OverflowTest.java 
b/src/test/java/org/apache/bcel/classfile/DumpU2OverflowTest.java
new file mode 100644
index 00000000..f9059ae8
--- /dev/null
+++ b/src/test/java/org/apache/bcel/classfile/DumpU2OverflowTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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
+ *
+ *   https://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 org.apache.bcel.classfile;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+
+import org.apache.bcel.Const;
+import org.apache.bcel.generic.ClassGen;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Writer paths must refuse to emit structure counts that overflow their u2/u1 
class-file fields instead of silently truncating them
+ * (DataOutputStream.writeShort keeps only the low 16 bits) while still 
writing every body; the CVE-2022-42920 corruption shape.
+ */
+class DumpU2OverflowTest {
+
+    @Test
+    void testDumpRefusesOverflowingInterfaceCount() throws Exception {
+        final JavaClass javaClass = new ClassGen("A", "java.lang.Object", 
"A.java", Const.ACC_PUBLIC, null).getJavaClass();
+        javaClass.setInterfaces(new int[Const.MAX_SHORT + 1]);
+        try (DataOutputStream dos = new DataOutputStream(new 
ByteArrayOutputStream())) {
+            assertThrows(ClassFormatException.class, () -> 
javaClass.dump(dos));
+        }
+    }
+
+    @Test
+    void testSetExceptionTableRefusesOverflowingCount() {
+        final JavaClass javaClass = new ClassGen("A", "java.lang.Object", 
"A.java", Const.ACC_PUBLIC, null).getJavaClass();
+        final Code code = new Code(0, 0, 0, 0, new byte[0], null, null, 
javaClass.getConstantPool());
+        assertThrows(ClassFormatException.class, () -> 
code.setExceptionTable(new CodeException[Const.MAX_SHORT + 1]));
+    }
+}

Reply via email to