Skip to content

WriterBasedJsonGenerator throws ArrayIndexOutOfBoundsException when a zero-length custom escape ends exactly at the output buffer boundary #1668

Description

@hdimitrieski

Description

When a CharacterEscapes implementation returns a zero-length escape sequence, WriterBasedJsonGenerator.writeString(String) throws:

java.lang.ArrayIndexOutOfBoundsException: Index 4000 out of bounds for length 4000
        at tools.jackson.core.json.WriterBasedJsonGenerator.writeString(WriterBasedJsonGenerator.java:461)

It happens only when the custom-escaped character is the last character of the string being written.

Reproduction

  import tools.jackson.core.*;                                                                                                                                                                                   
  import tools.jackson.core.io.CharacterEscapes;                                                                                                                                                                 
  import tools.jackson.core.io.SerializedString;                                                                                                                                                                 
  import tools.jackson.core.json.JsonFactory;                                                                                                                                                                    
  import java.io.StringWriter;                                                                                                                                                                                   
                                                                                                                                                                                                                 
  public class Repro {                                                                                                                                                                                           
      static class ZeroLengthNulEscape extends CharacterEscapes {                                                                                                                                                
          private static final SerializableString EMPTY = new SerializedString("");                                                                                                                              
          private final int[] escapes;                                                                                                                                                                           
          ZeroLengthNulEscape() {                                                                                                                                                                                
              escapes = standardAsciiEscapesForJSON();                                                                                                                                                           
              escapes[0] = ESCAPE_CUSTOM;                                                                                                                                                                        
          }                                                                                                                                                                                                      
          @Override public int[] getEscapeCodesForAscii() { return escapes; }                                                                                                                                    
          @Override public SerializableString getEscapeSequence(int ch) { return ch == 0 ? EMPTY : null; }                                                                                                       
      }                                                                                                                                                                                                          
                                                                                                                                                                                                                 
      public static void main(String[] args) throws Exception {                                                                                                                                                  
          JsonFactory factory = JsonFactory.builder()                                                                                                                                                            
                  .characterEscapes(new ZeroLengthNulEscape())                                                                                                                                                   
                  .build();                                                                                                                                                                                      
          String value = "Istio LFS144" + '\0';   // last char is the custom-escaped one                                                                                                                         
          for (int pad = 3900; pad <= 4100; pad++) {                                                                                                                                                             
              StringWriter w = new StringWriter();                                                                                                                                                               
              try (JsonGenerator gen = factory.createGenerator(ObjectWriteContext.empty(), w)) {                                                                                                                 
                  gen.writeStartArray();                                                                                                                                                                         
                  gen.writeString("x".repeat(pad));                                                                                                                                                              
                  gen.writeString(value);                                                                                                                                                                        
                  gen.writeEndArray();                                                                                                                                                                           
              } catch (Exception e) {                                                                                                                                                                            
                  System.out.println("FAILED at pad=" + pad + ": " + e);                                                                                                                                         
              }                                                                                                                                                                                                  
          }                                                                                                                                                                                                      
      }                                                                                                                                                                                                          
  }

Expected behavior

The string serializes successfully with the escaped character omitted, as it does at every other alignment.

Metadata

Metadata

Assignees

No one assigned

    Labels

    2.21Issues planned (at earliest) for 2.21

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions