5041655: (ch) FileLock: negative param and overflow issues

Reviewed-by: alanb
This commit is contained in:
Brian Burkhalter 2022-02-22 17:24:15 +00:00
parent 7feabee426
commit 6445ee46b5
9 changed files with 247 additions and 45 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -425,10 +425,13 @@ public abstract class AsynchronousFileChannel
* required then a region starting at zero, and no smaller than the * required then a region starting at zero, and no smaller than the
* expected maximum size of the file, should be locked. The two-argument * expected maximum size of the file, should be locked. The two-argument
* {@link #lock(Object,CompletionHandler)} method simply locks a region * {@link #lock(Object,CompletionHandler)} method simply locks a region
* of size {@link Long#MAX_VALUE}. If a lock that overlaps the requested * of size {@link Long#MAX_VALUE}. If the {@code position} is non-negative
* region is already held by this Java virtual machine, or this method has * and the {@code size} is zero, then a lock of size
* been invoked to lock an overlapping region and that operation has not * {@code Long.MAX_VALUE - position} is returned. If a lock that
* completed, then this method throws {@link OverlappingFileLockException}. * overlaps the requested region is already held by this Java virtual
* machine, or this method has been invoked to lock an overlapping region
* and that operation has not completed, then this method throws
* {@link OverlappingFileLockException}.
* *
* <p> Some operating systems do not support a mechanism to acquire a file * <p> Some operating systems do not support a mechanism to acquire a file
* lock in an asynchronous manner. Consequently an implementation may * lock in an asynchronous manner. Consequently an implementation may
@ -454,7 +457,10 @@ public abstract class AsynchronousFileChannel
* non-negative * non-negative
* @param size * @param size
* The size of the locked region; must be non-negative, and the sum * The size of the locked region; must be non-negative, and the sum
* {@code position}&nbsp;+&nbsp;{@code size} must be non-negative * {@code position}&nbsp;+&nbsp;{@code size} must be non-negative.
* A value of zero means to lock all bytes from the specified
* starting position to the end of the file, regardless of whether
* the file is subsequently extended or truncated
* @param shared * @param shared
* {@code true} to request a shared lock, in which case this * {@code true} to request a shared lock, in which case this
* channel must be open for reading (and possibly writing); * channel must be open for reading (and possibly writing);
@ -532,7 +538,10 @@ public abstract class AsynchronousFileChannel
* non-negative * non-negative
* @param size * @param size
* The size of the locked region; must be non-negative, and the sum * The size of the locked region; must be non-negative, and the sum
* {@code position}&nbsp;+&nbsp;{@code size} must be non-negative * {@code position}&nbsp;+&nbsp;{@code size} must be non-negative.
* A value of zero means to lock all bytes from the specified
* starting position to the end of the file, regardless of whether
* the file is subsequently extended or truncated
* @param shared * @param shared
* {@code true} to request a shared lock, in which case this * {@code true} to request a shared lock, in which case this
* channel must be open for reading (and possibly writing); * channel must be open for reading (and possibly writing);
@ -586,7 +595,9 @@ public abstract class AsynchronousFileChannel
* either having acquired a lock on the requested region or having failed to * either having acquired a lock on the requested region or having failed to
* do so. If it fails to acquire a lock because an overlapping lock is held * do so. If it fails to acquire a lock because an overlapping lock is held
* by another program then it returns {@code null}. If it fails to acquire * by another program then it returns {@code null}. If it fails to acquire
* a lock for any other reason then an appropriate exception is thrown. * a lock for any other reason then an appropriate exception is thrown. If
* the {@code position} is non-negative and the {@code size} is zero, then a
* lock of size {@code Long.MAX_VALUE - position} is returned.
* *
* @param position * @param position
* The position at which the locked region is to start; must be * The position at which the locked region is to start; must be
@ -594,7 +605,10 @@ public abstract class AsynchronousFileChannel
* *
* @param size * @param size
* The size of the locked region; must be non-negative, and the sum * The size of the locked region; must be non-negative, and the sum
* {@code position}&nbsp;+&nbsp;{@code size} must be non-negative * {@code position}&nbsp;+&nbsp;{@code size} must be non-negative.
* A value of zero means to lock all bytes from the specified
* starting position to the end of the file, regardless of whether
* the file is subsequently extended or truncated
* *
* @param shared * @param shared
* {@code true} to request a shared lock, * {@code true} to request a shared lock,

View File

@ -996,7 +996,9 @@ public abstract class FileChannel
* required then a region starting at zero, and no smaller than the * required then a region starting at zero, and no smaller than the
* expected maximum size of the file, should be locked. The zero-argument * expected maximum size of the file, should be locked. The zero-argument
* {@link #lock()} method simply locks a region of size {@link * {@link #lock()} method simply locks a region of size {@link
* Long#MAX_VALUE}. * Long#MAX_VALUE}. If the {@code position} is non-negative and the
* {@code size} is zero, then a lock of size
* {@code Long.MAX_VALUE - position} is returned.
* *
* <p> Some operating systems do not support shared locks, in which case a * <p> Some operating systems do not support shared locks, in which case a
* request for a shared lock is automatically converted into a request for * request for a shared lock is automatically converted into a request for
@ -1014,7 +1016,10 @@ public abstract class FileChannel
* *
* @param size * @param size
* The size of the locked region; must be non-negative, and the sum * The size of the locked region; must be non-negative, and the sum
* {@code position}&nbsp;+&nbsp;{@code size} must be non-negative * {@code position}&nbsp;+&nbsp;{@code size} must be non-negative.
* A value of zero means to lock all bytes from the specified
* starting position to the end of the file, regardless of whether
* the file is subsequently extended or truncated
* *
* @param shared * @param shared
* {@code true} to request a shared lock, in which case this * {@code true} to request a shared lock, in which case this
@ -1123,7 +1128,9 @@ public abstract class FileChannel
* required then a region starting at zero, and no smaller than the * required then a region starting at zero, and no smaller than the
* expected maximum size of the file, should be locked. The zero-argument * expected maximum size of the file, should be locked. The zero-argument
* {@link #tryLock()} method simply locks a region of size {@link * {@link #tryLock()} method simply locks a region of size {@link
* Long#MAX_VALUE}. * Long#MAX_VALUE}. If the {@code position} is non-negative and the
* {@code size} is zero, then a lock of size
* {@code Long.MAX_VALUE - position} is returned.
* *
* <p> Some operating systems do not support shared locks, in which case a * <p> Some operating systems do not support shared locks, in which case a
* request for a shared lock is automatically converted into a request for * request for a shared lock is automatically converted into a request for
@ -1141,7 +1148,10 @@ public abstract class FileChannel
* *
* @param size * @param size
* The size of the locked region; must be non-negative, and the sum * The size of the locked region; must be non-negative, and the sum
* {@code position}&nbsp;+&nbsp;{@code size} must be non-negative * {@code position}&nbsp;+&nbsp;{@code size} must be non-negative.
* A value of zero means to lock all bytes from the specified
* starting position to the end of the file, regardless of whether
* the file is subsequently extended or truncated
* *
* @param shared * @param shared
* {@code true} to request a shared lock, * {@code true} to request a shared lock,

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -269,14 +269,36 @@ public abstract class FileLock implements AutoCloseable {
* @param size * @param size
* The size of the lock range * The size of the lock range
* *
* @return {@code true} if, and only if, this lock and the given lock * @return {@code true} if this lock and the given lock range overlap
* range overlap by at least one byte * by at least one byte; {@code false} if {@code size} is
* negative or the lock range does not overlap this lock
*/ */
public final boolean overlaps(long position, long size) { public final boolean overlaps(long position, long size) {
if (position + size <= this.position) if (size < 0)
return false; // That is below this return false;
if (this.position + this.size <= position)
return false; // This is below that // Test whether this is below that
try {
if (Math.addExact(this.position, this.size) <= position)
return false;
} catch (ArithmeticException ignored) {
// the sum of this.position and this.size overflows the range of
// long hence their mathematical sum is greater than position
}
// if size == 0 then the specified lock range is unbounded and
// cannot be below the range of this lock
if (size > 0) {
// Test whether that is below this
try {
if (Math.addExact(position, size) <= this.position)
return false;
} catch (ArithmeticException ignored) {
// the sum of position and size overflows the range of long
// hence their mathematical sum is greater than this.position
}
}
return true; return true;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -1271,6 +1271,8 @@ public class FileChannelImpl
throw new NonReadableChannelException(); throw new NonReadableChannelException();
if (!shared && !writable) if (!shared && !writable)
throw new NonWritableChannelException(); throw new NonWritableChannelException();
if (size == 0)
size = Long.MAX_VALUE - Math.max(0, position);
FileLockImpl fli = new FileLockImpl(this, position, size, shared); FileLockImpl fli = new FileLockImpl(this, position, size, shared);
FileLockTable flt = fileLockTable(); FileLockTable flt = fileLockTable();
flt.add(fli); flt.add(fli);
@ -1316,6 +1318,8 @@ public class FileChannelImpl
throw new NonReadableChannelException(); throw new NonReadableChannelException();
if (!shared && !writable) if (!shared && !writable)
throw new NonWritableChannelException(); throw new NonWritableChannelException();
if (size == 0)
size = Long.MAX_VALUE - Math.max(0, position);
FileLockImpl fli = new FileLockImpl(this, position, size, shared); FileLockImpl fli = new FileLockImpl(this, position, size, shared);
FileLockTable flt = fileLockTable(); FileLockTable flt = fileLockTable();
flt.add(fli); flt.add(fli);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -181,8 +181,10 @@ public class SimpleAsynchronousFileChannelImpl
if (!shared && !writing) if (!shared && !writing)
throw new NonWritableChannelException(); throw new NonWritableChannelException();
long len = (size != 0) ? size : Long.MAX_VALUE - Math.max(0, position);
// add to lock table // add to lock table
final FileLockImpl fli = addToFileLockTable(position, size, shared); final FileLockImpl fli = addToFileLockTable(position, len, shared);
if (fli == null) { if (fli == null) {
Throwable exc = new ClosedChannelException(); Throwable exc = new ClosedChannelException();
if (handler == null) if (handler == null)
@ -203,7 +205,7 @@ public class SimpleAsynchronousFileChannelImpl
try { try {
begin(); begin();
do { do {
n = nd.lock(fdObj, true, position, size, shared); n = nd.lock(fdObj, true, position, len, shared);
} while ((n == FileDispatcher.INTERRUPTED) && isOpen()); } while ((n == FileDispatcher.INTERRUPTED) && isOpen());
if (n != FileDispatcher.LOCKED || !isOpen()) { if (n != FileDispatcher.LOCKED || !isOpen()) {
throw new AsynchronousCloseException(); throw new AsynchronousCloseException();
@ -248,6 +250,9 @@ public class SimpleAsynchronousFileChannelImpl
if (!shared && !writing) if (!shared && !writing)
throw new NonWritableChannelException(); throw new NonWritableChannelException();
if (size == 0)
size = Long.MAX_VALUE - Math.max(0, position);
// add to lock table // add to lock table
FileLockImpl fli = addToFileLockTable(position, size, shared); FileLockImpl fli = addToFileLockTable(position, size, shared);
if (fli == null) if (fli == null)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -299,8 +299,10 @@ public class WindowsAsynchronousFileChannelImpl
if (!shared && !writing) if (!shared && !writing)
throw new NonWritableChannelException(); throw new NonWritableChannelException();
long len = (size != 0) ? size : Long.MAX_VALUE - Math.max(0, position);
// add to lock table // add to lock table
FileLockImpl fli = addToFileLockTable(position, size, shared); FileLockImpl fli = addToFileLockTable(position, len, shared);
if (fli == null) { if (fli == null) {
Throwable exc = new ClosedChannelException(); Throwable exc = new ClosedChannelException();
if (handler == null) if (handler == null)
@ -332,6 +334,9 @@ public class WindowsAsynchronousFileChannelImpl
if (!shared && !writing) if (!shared && !writing)
throw new NonWritableChannelException(); throw new NonWritableChannelException();
if (size == 0)
size = Long.MAX_VALUE - Math.max(0, position);
// add to lock table // add to lock table
final FileLockImpl fli = addToFileLockTable(position, size, shared); final FileLockImpl fli = addToFileLockTable(position, size, shared);
if (fli == null) if (fli == null)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -22,18 +22,37 @@
*/ */
/* @test /* @test
* @bug 4607272 6822643 6830721 6842687 * @bug 4607272 5041655 6822643 6830721 6842687
* @summary Unit test for AsynchronousFileChannel * @summary Unit test for AsynchronousFileChannel
* @key randomness * @key randomness
*/ */
import java.nio.file.*;
import java.nio.channels.*;
import java.nio.ByteBuffer;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.nio.ByteBuffer;
import java.util.concurrent.*; import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.CompletionHandler;
import java.nio.channels.FileLock;
import java.nio.channels.NonWritableChannelException;
import java.nio.channels.OverlappingFileLockException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeoutException;;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import static java.nio.file.StandardOpenOption.*; import static java.nio.file.StandardOpenOption.*;
@ -176,7 +195,12 @@ public class Basic {
// test 1 - acquire lock and check that tryLock throws // test 1 - acquire lock and check that tryLock throws
// OverlappingFileLockException // OverlappingFileLockException
try { try {
fl = ch.lock().get(); long pos = rand.nextInt(Integer.MAX_VALUE);
fl = ch.lock(pos, 0, false).get();
long expectedSize = Long.MAX_VALUE - pos;
if(fl.size() != expectedSize)
throw new RuntimeException("Lock size " + fl.size() +
" != " + expectedSize + " for position " + pos);
} catch (ExecutionException x) { } catch (ExecutionException x) {
throw new RuntimeException(x); throw new RuntimeException(x);
} catch (InterruptedException x) { } catch (InterruptedException x) {
@ -192,7 +216,12 @@ public class Basic {
fl.release(); fl.release();
// test 2 - acquire try and check that lock throws OverlappingFileLockException // test 2 - acquire try and check that lock throws OverlappingFileLockException
fl = ch.tryLock(); long pos = rand.nextInt(Integer.MAX_VALUE);
fl = ch.tryLock(pos, 0, false);
long expectedSize = Long.MAX_VALUE - pos;
if(fl.size() != expectedSize)
throw new RuntimeException("Lock size " + fl.size() + " != " +
expectedSize + " for position " + pos);
if (fl == null) if (fl == null)
throw new RuntimeException("Unable to acquire lock"); throw new RuntimeException("Unable to acquire lock");
try { try {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -22,12 +22,19 @@
*/ */
/* @test /* @test
* @bug 4429043 4493595 6332756 6709457 7146506 * @bug 4429043 4493595 5041655 6332756 6709457 7146506
* @summary Test FileChannel file locking * @summary Test FileChannel file locking
*/ */
import java.io.*; import java.io.BufferedReader;
import java.nio.channels.*; import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.util.Random;
import static java.nio.file.StandardOpenOption.*; import static java.nio.file.StandardOpenOption.*;
/** /**
@ -126,12 +133,23 @@ public class Lock {
static void test2(File blah, boolean b) throws Exception { static void test2(File blah, boolean b) throws Exception {
try (RandomAccessFile raf = new RandomAccessFile(blah, "rw")) { try (RandomAccessFile raf = new RandomAccessFile(blah, "rw")) {
FileChannel channel = raf.getChannel(); FileChannel channel = raf.getChannel();
FileLock lock; try (FileLock lock = b ? channel.lock() : channel.tryLock()) {
if (b) }
lock = channel.lock();
else Random rnd = new Random(System.currentTimeMillis());
lock = channel.tryLock(); long position = rnd.nextInt(Integer.MAX_VALUE);
lock.release(); long expectedSize = Long.MAX_VALUE - position;
for (boolean shared : new boolean[] {false, true}) {
try (FileLock lock = b ? channel.lock(position, 0, false) :
channel.tryLock(position, 0, false)) {
if(lock.size() != expectedSize)
throw new RuntimeException("Lock size " + lock.size() +
" != " + expectedSize +
" for position " + position + " of " +
(shared ? "exclusive" : "shared") + " lock");
}
}
} }
} }

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 5041655
* @summary Verify FileLock.overlaps
* @run testng Overlaps
*/
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.Files;
import java.nio.file.Path;
import static java.lang.Boolean.*;
import static java.nio.file.StandardOpenOption.*;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class Overlaps {
private static final long POS = 27;
private static final long SIZE = 42;
private static FileChannel fc;
@BeforeClass
public void before() throws IOException {
Path path = Files.createTempFile(Path.of("."), "foo", ".bar");
fc = FileChannel.open(path, CREATE, WRITE, DELETE_ON_CLOSE);
fc.position(POS);
fc.write(ByteBuffer.wrap(new byte[(int)SIZE]));
}
@AfterClass
public void after() throws IOException {
fc.close();
}
@DataProvider
public Object[][] ranges() {
return new Object[][] {
{POS, SIZE, -1,-1, FALSE},
{POS, SIZE, 0, -1, FALSE},
{POS, SIZE, POS - 1, -1, FALSE},
{POS, SIZE, POS + SIZE/2, -1, FALSE},
{POS, SIZE, POS + SIZE, -1, FALSE},
{POS, SIZE, -1, POS, FALSE},
{POS, SIZE, -1, POS + SIZE/2, TRUE},
{POS, SIZE, POS - 2, 1, FALSE},
{POS, SIZE, POS + 1, 1, TRUE},
{POS, SIZE, POS + SIZE/2, 0, TRUE},
{POS, SIZE, Long.MAX_VALUE, 2, FALSE},
{POS, SIZE, POS + SIZE / 2, Long.MAX_VALUE, TRUE},
{POS, SIZE, 0, 0, TRUE},
{Long.MAX_VALUE - SIZE/2, 0, 0, SIZE, FALSE},
{Long.MAX_VALUE - SIZE/2, 0, Long.MAX_VALUE - SIZE/4, SIZE, TRUE},
{Long.MAX_VALUE - SIZE/2, 0, Long.MAX_VALUE - SIZE, 0, TRUE},
{Long.MAX_VALUE - SIZE, 0, Long.MAX_VALUE - SIZE/2, 0, TRUE}
};
}
@Test(dataProvider = "ranges")
public void overlaps(long lockPos, long lockSize, long pos, long size,
boolean overlaps) throws IOException {
try (FileLock lock = fc.lock(lockPos, lockSize, false)) {
assertEquals(lock.overlaps(pos, size), overlaps);
}
}
}