Skip to content

Commit 4ec0f4d

Browse files
authored
Merge pull request #74 from dmitry-timofeev/invalidate-not-owning
Invalidate not-owning handle when it's closed.
2 parents ce95566 + ec64665 commit 4ec0f4d

File tree

8 files changed

+88
-36
lines changed

8 files changed

+88
-36
lines changed
Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.exonum.binding.proxy;
22

3+
import static com.exonum.binding.proxy.StoragePreconditions.checkValid;
4+
35
/**
46
* A proxy of a native object.
57
*
@@ -15,22 +17,12 @@ abstract class AbstractNativeProxy implements AutoCloseable {
1517
*/
1618
static final long INVALID_NATIVE_HANDLE = 0L;
1719

18-
/**
19-
* A native implementation-specific handle.
20-
*
21-
* <p>This value shall only be passed as an argument to native methods.
22-
*/
23-
final long nativeHandle;
24-
2520
/**
2621
* Whether this proxy owns the corresponding native object and is responsible to clean it up.
2722
*/
2823
private final boolean owningHandle;
2924

30-
/**
31-
* Whether the proxy is valid and may be used to access the native object.
32-
*/
33-
private boolean valid;
25+
private long nativeHandle;
3426

3527
/**
3628
* @param nativeHandle a native handle: an implementation-specific reference to a native object.
@@ -40,7 +32,30 @@ abstract class AbstractNativeProxy implements AutoCloseable {
4032
AbstractNativeProxy(long nativeHandle, boolean owningHandle) {
4133
this.nativeHandle = nativeHandle;
4234
this.owningHandle = owningHandle;
43-
valid = (nativeHandle != INVALID_NATIVE_HANDLE);
35+
}
36+
37+
/**
38+
* Returns a native implementation-specific handle.
39+
*
40+
* <p>The returned value shall only be passed as an argument to native methods.
41+
*
42+
* <p>Warning: do not cache the return value, as you won't be able to catch use-after-free.
43+
*
44+
* @throws IllegalStateException if this native proxy is invalid (closed or nullptr).
45+
*/
46+
final long getNativeHandle() {
47+
checkValid(this);
48+
return getNativeHandleUnsafe();
49+
}
50+
51+
/**
52+
* Returns a native handle.
53+
*
54+
* <p>If clients ever need to just get a value (e.g., in their {@link #toString()}
55+
* implementation), make this package-local.
56+
*/
57+
private long getNativeHandleUnsafe() {
58+
return nativeHandle;
4459
}
4560

4661
/**
@@ -53,24 +68,30 @@ abstract class AbstractNativeProxy implements AutoCloseable {
5368
*/
5469
@Override
5570
public final void close() {
56-
if (owningHandle && valid) {
71+
if (isValid()) {
5772
try {
58-
disposeInternal();
73+
if (owningHandle) {
74+
disposeInternal();
75+
}
5976
} finally {
60-
valid = false;
77+
invalidate();
6178
}
6279
}
6380
}
6481

82+
/** Returns true if the proxy is valid and may be used to access the native object. */
83+
final boolean isValid() {
84+
return nativeHandle != INVALID_NATIVE_HANDLE;
85+
}
86+
6587
/**
6688
* Destroys the corresponding native object.
6789
*
68-
* <p>This method is only called from {@link #close()} and shall not be called directly.
90+
* <p>This method is only called once from {@link #close()} and shall not be called directly.
6991
*/
7092
abstract void disposeInternal();
7193

72-
/** Returns true if the proxy is valid and may be used to access the native object. */
73-
final boolean isValid() {
74-
return valid;
94+
private void invalidate() {
95+
nativeHandle = INVALID_NATIVE_HANDLE;
7596
}
7697
}

src/main/java/com/exonum/binding/proxy/Fork.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ public Fork(long nativeHandle) {
1717

1818
@Override
1919
void disposeInternal() {
20-
Views.nativeFree(nativeHandle);
20+
Views.nativeFree(getNativeHandle());
2121
}
2222
}

src/main/java/com/exonum/binding/proxy/LevelDb.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ public LevelDb(String pathToDb) {
88

99
@Override
1010
public Snapshot createSnapshot() {
11-
return new Snapshot(nativeCreateSnapshot(this.nativeHandle));
11+
return new Snapshot(nativeCreateSnapshot(getNativeHandle()));
1212
}
1313

1414
@Override
1515
public Fork createFork() {
16-
return new Fork(nativeCreateFork(this.nativeHandle));
16+
return new Fork(nativeCreateFork(getNativeHandle()));
1717
}
1818

1919
@Override
2020
void disposeInternal() {
21-
nativeFree(nativeHandle);
21+
nativeFree(getNativeHandle());
2222
}
2323

2424
private static native long nativeCreate(String path);

src/main/java/com/exonum/binding/proxy/MapIndexProxy.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,25 @@ public MapIndexProxy(View view, byte[] prefix) {
2424
}
2525

2626
public void put(byte[] key, byte[] value) {
27-
nativePut(checkStorageKey(key), checkStorageValue(value), nativeHandle);
27+
nativePut(checkStorageKey(key), checkStorageValue(value), getNativeHandle());
2828
}
2929

3030
public byte[] get(byte[] key) {
31-
return nativeGet(checkStorageKey(key), nativeHandle);
31+
return nativeGet(checkStorageKey(key), getNativeHandle());
3232
}
3333

3434
public void remove(byte[] key) {
35-
nativeRemove(checkStorageKey(key), nativeHandle);
35+
nativeRemove(checkStorageKey(key), getNativeHandle());
3636
}
3737

3838
public void clear() {
39-
nativeClear(nativeHandle);
39+
nativeClear(getNativeHandle());
4040
}
4141

4242
@Override
4343
void disposeInternal() {
4444
checkValid(dbView);
45-
nativeFree(nativeHandle);
45+
nativeFree(getNativeHandle());
4646
}
4747

4848
private static native long nativeCreate(long viewNativeHandle, byte[] prefix);

src/main/java/com/exonum/binding/proxy/MemoryDb.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ public MemoryDb() {
88

99
@Override
1010
public Snapshot createSnapshot() {
11-
return new Snapshot(nativeCreateSnapshot(nativeHandle));
11+
return new Snapshot(nativeCreateSnapshot(getNativeHandle()));
1212
}
1313

1414
@Override
1515
public Fork createFork() {
16-
return new Fork(nativeCreateFork(nativeHandle));
16+
return new Fork(nativeCreateFork(getNativeHandle()));
1717
}
1818

1919
@Override
2020
void disposeInternal() {
21-
nativeFree(nativeHandle);
21+
nativeFree(getNativeHandle());
2222
}
2323

2424
private static native long nativeCreate();

src/main/java/com/exonum/binding/proxy/Snapshot.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ public Snapshot(long nativeHandle) {
2929

3030
@Override
3131
void disposeInternal() {
32-
Views.nativeFree(nativeHandle);
32+
Views.nativeFree(getNativeHandle());
3333
}
3434
}

src/main/java/com/exonum/binding/proxy/View.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,4 @@ public abstract class View extends AbstractNativeProxy {
2929
View(long nativeHandle, boolean owningHandle) {
3030
super(nativeHandle, owningHandle);
3131
}
32-
33-
long getNativeHandle() {
34-
return nativeHandle;
35-
}
3632
}

src/test/java/com/exonum/binding/proxy/AbstractNativeProxyTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,47 @@ public void shallNotBeValidOnceClosed() throws Exception {
5555
assertFalse(proxy.isValid());
5656
}
5757

58+
@Test
59+
public void notOwningShallNotBeValidOnceClosed() throws Exception {
60+
proxy = new NativeProxyFake(1L, false);
61+
proxy.close();
62+
assertFalse(proxy.isValid());
63+
}
64+
5865
@Test
5966
public void shallNotBeValidIfInvalidHandle() throws Exception {
6067
proxy = new NativeProxyFake(INVALID_NATIVE_HANDLE, true);
6168
assertFalse(proxy.isValid());
6269
}
6370

71+
@Test
72+
public void getNativeHandle() throws Exception {
73+
long expectedNativeHandle = 0x1FL;
74+
75+
proxy = new NativeProxyFake(expectedNativeHandle, true);
76+
77+
assertThat(proxy.getNativeHandle(), equalTo(expectedNativeHandle));
78+
}
79+
80+
@Test(expected = IllegalStateException.class)
81+
public void getNativeHandleShallFailIfProxyIsClosed() throws Exception {
82+
long nativeHandle = 0x1FL;
83+
84+
proxy = new NativeProxyFake(nativeHandle, true);
85+
proxy.close();
86+
87+
proxy.getNativeHandle(); // boom
88+
}
89+
90+
@Test(expected = IllegalStateException.class)
91+
public void getNativeHandleShallFailIfInvalid() throws Exception {
92+
long invalidHandle = 0x0L;
93+
94+
proxy = new NativeProxyFake(invalidHandle, true);
95+
96+
proxy.getNativeHandle(); // boom
97+
}
98+
6499
private static class NativeProxyFake extends AbstractNativeProxy {
65100

66101
int timesDisposed;

0 commit comments

Comments
 (0)