Uploaded image for project: 'Apache Cassandra'
  1. Apache Cassandra
  2. CASSANDRA-2684

IntergerType uses Thrift method that attempts to unsafely access backing array of ByteBuffer and fails

Agile BoardAttach filesAttach ScreenshotBulk Copy AttachmentsBulk Move AttachmentsVotersWatch issueWatchersCreate sub-taskConvert to sub-taskMoveLinkCloneLabelsUpdate Comment AuthorReplace String in CommentUpdate Comment VisibilityDelete Comments
    XMLWordPrintableJSON

Details

    • Bug
    • Status: Resolved
    • Low
    • Resolution: Fixed
    • 0.7.7, 0.8.0
    • None
    • None
    • Low

    Description

      I get the following exception:

      ERROR 13:27:38,153 Fatal exception in thread Thread[ReadStage:36,5,main]
      java.lang.RuntimeException: java.lang.UnsupportedOperationException
      	at org.apache.cassandra.utils.WrappedRunnable.run(WrappedRunnable.java:34)
      	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
      	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
      	at java.lang.Thread.run(Thread.java:680)
      Caused by: java.lang.UnsupportedOperationException
      	at java.nio.ByteBuffer.array(ByteBuffer.java:940)
      	at org.apache.thrift.TBaseHelper.byteBufferToByteArray(TBaseHelper.java:264)
      	at org.apache.thrift.TBaseHelper.byteBufferToByteArray(TBaseHelper.java:251)
      	at org.apache.cassandra.db.marshal.IntegerType.getString(IntegerType.java:136)
      	at org.apache.cassandra.db.marshal.AbstractCompositeType.getString(AbstractCompositeType.java:131)
      	at org.apache.cassandra.db.Column.getString(Column.java:228)
      	at org.apache.cassandra.db.filter.SliceQueryFilter.collectReducedColumns(SliceQueryFilter.java:123)
      	at org.apache.cassandra.db.filter.QueryFilter.collectCollatedColumns(QueryFilter.java:130)
      	at org.apache.cassandra.db.ColumnFamilyStore.getTopLevelColumns(ColumnFamilyStore.java:1303)
      	at org.apache.cassandra.db.ColumnFamilyStore.getColumnFamily(ColumnFamilyStore.java:1188)
      	at org.apache.cassandra.db.ColumnFamilyStore.getColumnFamily(ColumnFamilyStore.java:1145)
      	at org.apache.cassandra.db.Table.getRow(Table.java:385)
      	at org.apache.cassandra.db.SliceFromReadCommand.getRow(SliceFromReadCommand.java:61)
      	at org.apache.cassandra.service.StorageProxy$LocalReadRunnable.runMayThrow(StorageProxy.java:641)
      	at org.apache.cassandra.utils.WrappedRunnable.run(WrappedRunnable.java:30)
      	... 3 more
      

      Tracing it down, I find that IntegerType's getString method() looks like this:

      IntegerType.java
          public String getString(ByteBuffer bytes)
          {
              if (bytes == null)
                  return "null";
              if (bytes.remaining() == 0)
                  return "empty";
      
              return new java.math.BigInteger(TBaseHelper.byteBufferToByteArray(bytes)).toString(10);
          }
      

      TBaseHelper.byteBufferToByteArray() looks like this:

      TBaseHelper.java
        public static byte[] byteBufferToByteArray(ByteBuffer byteBuffer) {
          if (wrapsFullArray(byteBuffer)) {
            return byteBuffer.array();
          }
          byte[] target = new byte[byteBuffer.remaining()];
          byteBufferToByteArray(byteBuffer, target, 0);
          return target;
        }
      
        public static boolean wrapsFullArray(ByteBuffer byteBuffer) {
          return byteBuffer.hasArray()
            && byteBuffer.position() == 0
            && byteBuffer.arrayOffset() == 0
            && byteBuffer.remaining() == byteBuffer.capacity();
        }
      
        public static int byteBufferToByteArray(ByteBuffer byteBuffer, byte[] target, int offset) {
          int remaining = byteBuffer.remaining();
          System.arraycopy(byteBuffer.array(),
              byteBuffer.arrayOffset() + byteBuffer.position(),
              target,
              offset,
              remaining);
          return remaining;
        }
      

      The second overloaded implementation of byteBufferToByteArray is calling the bytebuffer's array() method.

      Suggested fixes:

      1) Don't use TBaseHelper in IntegerType.getString(), use ByteBufferUtil.getArray()

      2) Report problem upstream to Thrift.

      3) Find a better way to deserialize BigIntegers that doesn't require an array copy.

      Attachments

        Activity

          This comment will be Viewable by All Users Viewable by All Users
          Cancel

          People

            edanuff Ed Anuff Assign to me
            edanuff Ed Anuff
            Ed Anuff
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Slack

                Issue deployment