Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8365588

defineClass that accepts a ByteBuffer does not work as expected

XMLWordPrintable

      A DESCRIPTION OF THE PROBLEM :
      It is possible to craft a class that will get loaded by the JVM that should normally not be possible.
      I've attached an example that can craft an invalid modified UTF-8 string.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run the program.

      $ wget https://repo1.maven.org/maven2/org/ow2/asm/asm/9.8/asm-9.8.jar
      $ mkdir -p toctou
      $ javac -d toctou -cp asm-9.8.jar TocTouTest.java
      $ java -cp toctou:asm-9.8.jar toctou.TocTouTest



      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Program keeps running.
      ACTUAL -
      Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.invoke.MethodHandle.asFixedArity()" because "dmh" is null
      at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.makeSpecializedTarget(MethodHandleAccessorFactory.java:271)
      at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.getDirectMethod(MethodHandleAccessorFactory.java:222)
      at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.newMethodAccessor(MethodHandleAccessorFactory.java:81)
      at java.base/jdk.internal.reflect.ReflectionFactory.newMethodAccessor(ReflectionFactory.java:119)
      at java.base/java.lang.reflect.Method.acquireMethodAccessor(Method.java:711)
      at java.base/java.lang.reflect.Method.invoke(Method.java:562)
      at toctou.TocTouTest.main(TocTouTest.java:109)

      ---------- BEGIN SOURCE ----------
      package toctou;

      import org.objectweb.asm.ClassReader;
      import org.objectweb.asm.ClassWriter;

      import java.io.ByteArrayInputStream;
      import java.io.DataInputStream;
      import java.lang.foreign.Arena;
      import java.lang.foreign.MemorySegment;
      import java.lang.foreign.ValueLayout;
      import java.lang.reflect.Method;
      import java.util.concurrent.ConcurrentLinkedDeque;
      import java.util.concurrent.atomic.AtomicInteger;

      import static org.objectweb.asm.Opcodes.*;

      public class TocTouTest {

      public static void main(String[] args) throws Exception {
      var cw = new ClassWriter(0);
      cw.visit(V17, ACC_PUBLIC, "toctou/Test", null, "java/lang/Object", null);
      var string = "corrupted_strin!".replace('!', 'g');
      var corruptedString = '\0' + string.substring(1);
      var mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, string, "()Ljava/lang/String;", null, null);
      mv.visitCode();
      mv.visitLdcInsn(string);
      mv.visitInsn(ARETURN);
      mv.visitMaxs(1, 0);
      mv.visitEnd();
      cw.visitEnd();
      byte[] bytes = cw.toByteArray();
      var cr = new ClassReader(bytes);
      int byteOffset;
      lookup:
      {
      for (int i = 1; i < cr.getItemCount(); i++) {
      int offset = cr.getItem(i);
      int tag = bytes[offset - 1] & 0xFF;
      if (tag != 1) {
      continue;
      }
      if (new DataInputStream(new ByteArrayInputStream(bytes, offset, bytes.length - offset)).readUTF().equals(string)) {
      byteOffset = offset + 2;
      break lookup;
      }
      }
      throw new IllegalStateException();
      }
      record Entry(MemorySegment segment, Arena arena, AtomicInteger uses) {

      void free() {
      if (uses.decrementAndGet() == 0) {
      arena.close();
      }
      }
      }
      var queue = new ConcurrentLinkedDeque<Entry>();
      var thread = Thread.ofPlatform().daemon().start(() -> {
      do {
      Entry entry;
      if ((entry = queue.poll()) != null) {
      try {
      entry.segment.set(ValueLayout.JAVA_BYTE, byteOffset, (byte) 0);
      } finally {
      entry.free();
      }
      }
      } while (!Thread.interrupted());
      });
      class Loader extends ClassLoader {

      Class<?> define() {
      var arena = Arena.ofShared();
      var counter = new AtomicInteger(2);
      var segment = arena.allocate(bytes.length);
      MemorySegment.copy(
      MemorySegment.ofArray(bytes),
      0,
      segment,
      0,
      bytes.length
      );
      queue.offer(new Entry(segment, arena, counter));
      try {
      return defineClass(null, segment.asByteBuffer(), null);
      } finally {
      if (counter.decrementAndGet() == 0) {
      arena.close();
      }
      }
      }
      }
      var loader = new Loader();
      for (; ; ) {
      Class<?> c;
      try {
      c = loader.define();
      loader = new Loader();
      } catch (ClassFormatError ignored) {
      continue;
      }
      Method method;
      try {
      method = c.getDeclaredMethod(string);
      } catch (NoSuchMethodException ignored) {
      method = c.getDeclaredMethod(corruptedString);
      }
      String myString;
      myString = (String) method.invoke(null);
      if (!string.equals(myString)) {
      System.out.println(myString);
      }
      }
      }
      }

      ---------- END SOURCE ----------

        1. TocTouTest.java
          4 kB
          Ioi Lam
        2. TocTouTest2.java
          5 kB
          Coleen Phillimore

            sherman Xueming Shen
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            10 Start watching this issue

              Created:
              Updated: