Author: jukka
Date: Fri Mar 21 21:34:48 2014
New Revision: 1580060

URL: http://svn.apache.org/r1580060
Log:
OAK-1566: ArrayIndexOutOfBoundsException in Segment.getRefId()

Prevent blobrefs from overwriting normal segment content

Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/RecordId.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/RecordId.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/RecordId.java?rev=1580060&r1=1580059&r2=1580060&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/RecordId.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/RecordId.java
 Fri Mar 21 21:34:48 2014
@@ -45,7 +45,7 @@ public final class RecordId implements C
 
     public RecordId(SegmentId segmentId, int offset) {
         checkArgument(offset < Segment.MAX_SEGMENT_SIZE);
-        checkArgument(offset == Segment.align(offset));
+        checkArgument((offset & 3) == 0);
         this.segmentId = checkNotNull(segmentId);
         this.offset = offset;
     }

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java?rev=1580060&r1=1580059&r2=1580060&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/Segment.java
 Fri Mar 21 21:34:48 2014
@@ -62,11 +62,6 @@ public class Segment {
      */
     static final int RECORD_ALIGN_BITS = 2; // align at the four-byte boundary
 
-    static int align(int value) {
-        int mask = -1 << RECORD_ALIGN_BITS;
-        return (value + ~mask) & mask;
-    }
-
     /**
      * Maximum segment size. Record identifiers are stored as three-byte
      * sequences with the first byte indicating the segment and the next

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java?rev=1580060&r1=1580059&r2=1580060&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
 Fri Mar 21 21:34:48 2014
@@ -37,7 +37,6 @@ import static org.apache.jackrabbit.oak.
 import static 
org.apache.jackrabbit.oak.plugins.segment.MapRecord.BUCKETS_PER_LEVEL;
 import static 
org.apache.jackrabbit.oak.plugins.segment.Segment.MAX_SEGMENT_SIZE;
 import static 
org.apache.jackrabbit.oak.plugins.segment.Segment.RECORD_ID_BYTES;
-import static org.apache.jackrabbit.oak.plugins.segment.Segment.align;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -85,6 +84,15 @@ public class SegmentWriter {
         return buffer;
     }
 
+    private static int align(int value) {
+        return align(value, 1 << Segment.RECORD_ALIGN_BITS);
+    }
+
+    private static int align(int value, int boundary) {
+        return (value + boundary - 1) & ~(boundary - 1);
+    }
+
+
     private final SegmentTracker tracker;
 
     private final SegmentStore store;
@@ -151,7 +159,6 @@ public class SegmentWriter {
     public synchronized void flush() {
         if (length > 0) {
             int refcount = segment.getRefCount();
-            length += align(refcount * 16 + roots.size() * 3);
 
             int rootcount = roots.size();
             buffer[Segment.ROOT_COUNT_OFFSET] = (byte) (rootcount >> 8);
@@ -161,12 +168,24 @@ public class SegmentWriter {
             buffer[Segment.BLOBREF_COUNT_OFFSET] = (byte) (blobrefcount >> 8);
             buffer[Segment.BLOBREF_COUNT_OFFSET + 1] = (byte) blobrefcount;
 
+            length = align(
+                    refcount * 16 + rootcount * 3 + blobrefcount * 2 + length,
+                    16);
+
             int pos = refcount * 16;
-            if (length + pos > buffer.length) {
-                length = buffer.length;
-            } else {
+            if (pos + length <= buffer.length) {
+                // the whole segment fits to the space *after* the referenced
+                // segment identifiers we've already written, so we can safely
+                // copy those bits ahead even if concurrent code is still
+                // reading from that part of the buffer
                 System.arraycopy(buffer, 0, buffer, buffer.length-length, pos);
                 pos += buffer.length - length;
+            } else {
+                // this might leave some empty space between the header and
+                // the record data, but this case only occurs when the
+                // segment is >252kB in size and the maximum overhead is <<4kB,
+                // which is acceptable
+                length = buffer.length;
             }
 
             for (Map.Entry<RecordId, RecordType> entry : roots.entrySet()) {
@@ -225,10 +244,9 @@ public class SegmentWriter {
             refcount += segmentIds.size();
         }
 
-        int recordSize = Segment.align(size + ids.size() * RECORD_ID_BYTES);
-        int headerSize = Segment.align(
-                refcount * 16 + rootcount * 3 + blobrefcount * 2);
-        int segmentSize = headerSize + recordSize + length;
+        int recordSize = align(size + ids.size() * RECORD_ID_BYTES);
+        int headerSize = refcount * 16 + rootcount * 3 + blobrefcount * 2;
+        int segmentSize = align(headerSize + recordSize + length, 16);
         if (segmentSize > buffer.length - 1
                 || blobrefcount > 0xffff
                 || rootcount > 0xffff
@@ -266,7 +284,7 @@ public class SegmentWriter {
 
         int offset = recordId.getOffset();
         checkState(0 <= offset && offset < MAX_SEGMENT_SIZE);
-        checkState(offset == Segment.align(offset));
+        checkState(offset == align(offset));
 
         buffer[position++] = (byte) getSegmentRef(recordId.getSegmentId());
         buffer[position++] = (byte) (offset >> (8 + 
Segment.RECORD_ALIGN_BITS));


Reply via email to