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));