alibaba / fastjson

FASTJSON 2.0.x has been released, faster and more secure, recommend you upgrade.
https://github.com/alibaba/fastjson2/wiki/fastjson_1_upgrade_cn
Apache License 2.0
25.75k stars 6.5k forks source link

FASTJSON2 crushes JVM when polymorphism is used #4533

Open undqurek opened 1 month ago

undqurek commented 1 month ago

Problem description

FASTJSON2 has problem to parse JSON to specific object by JSON.parseObject() mehod when polymorphism is used. Object type class inherits from some abstract class. To describe classes @JSONType annotation was used. The final result is crushed JVM.

Practical example

Used OS: Ubuntu 24.04.1 LTS x64

Used JVM:

openjdk version "21.0.4" 2024-07-16
OpenJDK Runtime Environment (build 21.0.4+7-Ubuntu-1ubuntu224.04)
OpenJDK 64-Bit Server VM (build 21.0.4+7-Ubuntu-1ubuntu224.04, mixed mode, sharing)

Note: the same problem occurs on Java 17 so it looks like the problem is common.

Used FASTJSON2:

<!-- https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 -->
<dependency>
    <groupId>com.alibaba.fastjson2</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.53</version>
    <scope>compile</scope>
</dependency>

Source code that crushes JVM (Program.java):

package com.example;

import java.util.List;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.annotation.JSONType;

public class Program {

    public static void main(String[] args) {

        MyFile myFile = new MyFile();
        myFile.type = "file";
        myFile.name = "my_file.txt";
        myFile.data = "Some content ...";

        MyDirectory myDirectory = new MyDirectory();
        myDirectory.type = "directory";
        myDirectory.name = "My directory";
        myDirectory.items = List.of(myFile);

        String json = JSON.toJSONString(myDirectory);
        MyDirectory object = JSON.parseObject(json, MyDirectory.class);  // <----- it crushes JVM
    }
}

@JSONType(
    typeKey = "type",
    seeAlso = {MyDirectory.class, MyFile.class}
)
abstract class MyNode {
    public String type;
    public String name;
}

@JSONType(typeName = "file")
class MyFile extends MyNode {
    public String data;
}

@JSONType(typeName = "directory")
class MyDirectory extends MyNode {
    public List<MyNode> items;
}

Console output:

/usr/bin/env /usr/lib/jvm/java-21-openjdk-amd64/bin/java @/tmp/cp_9jg5za4k5xz0ewd7mvown6x99.argfile com.example.Program 
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007b72ea7b7fb1, pid=64235, tid=64236
#
# JRE version: OpenJDK Runtime Environment (21.0.4+7) (build 21.0.4+7-Ubuntu-1ubuntu224.04)
# Java VM: OpenJDK 64-Bit Server VM (21.0.4+7-Ubuntu-1ubuntu224.04, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, linux-amd64)
# Problematic frame:
# V  [libjvm.so+0x9b7fb1]  AccessInternal::PostRuntimeDispatch<G1BarrierSet::AccessBarrier<401510ul, G1BarrierSet>, (AccessInternal::BarrierType)1, 401510ul>::oop_access_barrier(oopDesc*, long, oopDesc*)+0x51
#
# Core dump will be written. Default location: Core dumps may be processed with "/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E" (or dumping to /home/john/Desktop/tmp/java/alibaba_fastjson2_polymorphism/core.64235)
#
# An error report file with more information is saved as:
# /home/john/Desktop/tmp/java/alibaba_fastjson2_polymorphism/hs_err_pid64235.log
#
# If you would like to submit a bug report, please visit:
#   https://bugs.launchpad.net/ubuntu/+source/openjdk-21
#
Aborted (core dumped)

JVM logs (hs_err_pid64235.log):

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007b72ea7b7fb1, pid=64235, tid=64236
#
# JRE version: OpenJDK Runtime Environment (21.0.4+7) (build 21.0.4+7-Ubuntu-1ubuntu224.04)
# Java VM: OpenJDK 64-Bit Server VM (21.0.4+7-Ubuntu-1ubuntu224.04, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, linux-amd64)
# Problematic frame:
# V  [libjvm.so+0x9b7fb1]  AccessInternal::PostRuntimeDispatch<G1BarrierSet::AccessBarrier<401510ul, G1BarrierSet>, (AccessInternal::BarrierType)1, 401510ul>::oop_access_barrier(oopDesc*, long, oopDesc*)+0x51
#
# Core dump will be written. Default location: Core dumps may be processed with "/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E" (or dumping to /home/john/Desktop/tmp/java/alibaba_fastjson2_polymorphism/core.64235)
#
# If you would like to submit a bug report, please visit:
#   https://bugs.launchpad.net/ubuntu/+source/openjdk-21
#

---------------  S U M M A R Y ------------

Command Line: -XX:+ShowCodeDetailsInExceptionMessages com.example.Program

Host: AMD Ryzen 9 5900X 12-Core Processor, 32 cores, 62G, Ubuntu 24.04.1 LTS
Time: Thu Sep 19 14:23:18 2024 CEST elapsed time: 0.171075 seconds (0d 0h 0m 0s)

---------------  T H R E A D  ---------------

Current thread (0x00007b72e4019a30):  JavaThread "main"             [_thread_in_vm, id=64236, stack(0x00007b72e9900000,0x00007b72e9a00000) (1024K)]

Stack: [0x00007b72e9900000,0x00007b72e9a00000],  sp=0x00007b72e99fe4b0,  free space=1017k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0x9b7fb1]  AccessInternal::PostRuntimeDispatch<G1BarrierSet::AccessBarrier<401510ul, G1BarrierSet>, (AccessInternal::BarrierType)1, 401510ul>::oop_access_barrier(oopDesc*, long, oopDesc*)+0x51
V  [libjvm.so+0xfa9237]  Unsafe_PutReference+0xb7
j  jdk.internal.misc.Unsafe.putReference(Ljava/lang/Object;JLjava/lang/Object;)V+0 java.base@21.0.4
j  sun.misc.Unsafe.putObject(Ljava/lang/Object;JLjava/lang/Object;)V+7 jdk.unsupported@21.0.4
j  com.alibaba.fastjson2.reader.FieldReaderStringField.readFieldValue(Lcom/alibaba/fastjson2/JSONReader;Ljava/lang/Object;)V+73
j  com.alibaba.fastjson2.reader.ObjectReaderSeeAlso.readObject(Lcom/alibaba/fastjson2/JSONReader;Ljava/lang/reflect/Type;Ljava/lang/Object;J)Ljava/lang/Object;+860
j  com.alibaba.fastjson2.reader.ORG_1_3_MyDirectory.readObject(Lcom/alibaba/fastjson2/JSONReader;Ljava/lang/reflect/Type;Ljava/lang/Object;J)Ljava/lang/Object;+631
j  com.alibaba.fastjson2.JSON.parseObject(Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;+67
j  com.example.Program.main([Ljava/lang/String;)V+62
v  ~StubRoutines::call_stub 0x00007b72d3c5fcc6
V  [libjvm.so+0x90841a]  JavaCalls::call_helper(JavaValue*, methodHandle const&, JavaCallArguments*, JavaThread*)+0x2da
V  [libjvm.so+0x9ae72d]  jni_invoke_static(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, JavaThread*) [clone .constprop.1]+0x35d
V  [libjvm.so+0x9b0c7f]  jni_CallStaticVoidMethod+0x12f
C  [libjli.so+0x55be]  JavaMain+0xfde
C  [libjli.so+0x844d]  ThreadJavaMain+0xd
C  [libc.so.6+0x9ca94]
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  jdk.internal.misc.Unsafe.putReference(Ljava/lang/Object;JLjava/lang/Object;)V+0 java.base@21.0.4
j  sun.misc.Unsafe.putObject(Ljava/lang/Object;JLjava/lang/Object;)V+7 jdk.unsupported@21.0.4
j  com.alibaba.fastjson2.reader.FieldReaderStringField.readFieldValue(Lcom/alibaba/fastjson2/JSONReader;Ljava/lang/Object;)V+73
j  com.alibaba.fastjson2.reader.ObjectReaderSeeAlso.readObject(Lcom/alibaba/fastjson2/JSONReader;Ljava/lang/reflect/Type;Ljava/lang/Object;J)Ljava/lang/Object;+860
j  com.alibaba.fastjson2.reader.ORG_1_3_MyDirectory.readObject(Lcom/alibaba/fastjson2/JSONReader;Ljava/lang/reflect/Type;Ljava/lang/Object;J)Ljava/lang/Object;+631
j  com.alibaba.fastjson2.JSON.parseObject(Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;+67
j  com.example.Program.main([Ljava/lang/String;)V+62
v  ~StubRoutines::call_stub 0x00007b72d3c5fcc6

siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x0000000000000010

...
...
...

vm_info: OpenJDK 64-Bit Server VM (21.0.4+7-Ubuntu-1ubuntu224.04) for linux-amd64 JRE (21.0.4+7-Ubuntu-1ubuntu224.04), built on 2024-07-23T01:34:52Z by "buildd" with gcc 13.2.0

END.
GGGGGHT commented 1 month ago

Just remove the abstract modifier from the MyNode class.

undqurek commented 1 month ago

Thanks. It works, but I think it could be good to add some exception to know what is going on.

GGGGGHT commented 1 month ago

Adding exception information seems to be feasible, Maybe you need to report it in another repository instead of here.