CipherTest: do not test known ciphertext for randomized algorithms
am: dfb337d6f7
Change-Id: Ia6cd7a7ef43094700d7dd2024a26f7ecd69a0116
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index 5fd4a7d..52df40e 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -53,3 +53,20 @@
return JNI_VERSION_1_6;
}
+
+// DalvikVM calls this on shutdown, do any global cleanup here.
+// -- Very important if we restart multiple DalvikVMs in the same process to reset the state.
+void JNI_OnUnload(JavaVM* vm, void*) {
+ JNIEnv* env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ ALOGE("JavaVM::GetEnv() failed");
+ abort();
+ }
+ ALOGV("libjavacore JNI_OnUnload");
+
+ ScopedLocalFrame localFrame(env);
+
+#define UNREGISTER(FN) extern void FN(JNIEnv*); FN(env)
+ UNREGISTER(unregister_libcore_icu_ICU);
+#undef UNREGISTER
+}
diff --git a/luni/src/main/native/libcore_icu_ICU.cpp b/luni/src/main/native/libcore_icu_ICU.cpp
index 3eda923..e6f378e 100644
--- a/luni/src/main/native/libcore_icu_ICU.cpp
+++ b/luni/src/main/native/libcore_icu_ICU.cpp
@@ -838,56 +838,110 @@
NATIVE_METHOD(ICU, toUpperCase, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
};
+//
+// Global initialization & Teardown for ICU Setup
+// - Contains handlers for JNI_OnLoad and JNI_OnUnload
+//
+
#define FAIL_WITH_STRERROR(s) \
- ALOGE("Couldn't " s " '%s': %s", path.c_str(), strerror(errno)); \
+ ALOGE("Couldn't " s " '%s': %s", path_.c_str(), strerror(errno)); \
return FALSE;
#define MAYBE_FAIL_WITH_ICU_ERROR(s) \
if (status != U_ZERO_ERROR) {\
- ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path.c_str()); \
+ ALOGE("Couldn't initialize ICU (" s "): %s (%s)", u_errorName(status), path_.c_str()); \
return FALSE; \
}
-static bool mapIcuData(const std::string& path) {
+// Contain the memory map for ICU data files.
+// Automatically adds the data file to ICU's list of data files upon constructing.
+//
+// - Automatically unmaps in the destructor.
+struct IcuDataMap {
+ // Map in ICU data at the path, returning null if it failed (prints error to ALOGE).
+ static std::unique_ptr<IcuDataMap> Create(const std::string& path) {
+ std::unique_ptr<IcuDataMap> map(new IcuDataMap(path));
+
+ if (!map->TryMap()) {
+ // madvise or ICU could fail but mmap still succeeds.
+ // Destructor will take care of cleaning up a partial init.
+ return nullptr;
+ }
+
+ return map;
+ }
+
+ // Unmap the ICU data.
+ ~IcuDataMap() {
+ TryUnmap();
+ }
+
+ private:
+ IcuDataMap(const std::string& path)
+ : path_(path),
+ data_(MAP_FAILED),
+ data_length_(0)
+ {}
+
+ bool TryMap() {
// Open the file and get its length.
- android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path_.c_str(), O_RDONLY)));
+
if (fd.get() == -1) {
FAIL_WITH_STRERROR("open");
}
+
struct stat sb;
if (fstat(fd.get(), &sb) == -1) {
FAIL_WITH_STRERROR("stat");
}
+ data_length_ = sb.st_size;
+
// Map it.
- void* data = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd.get(), 0);
- if (data == MAP_FAILED) {
+ data_ = mmap(NULL, data_length_, PROT_READ, MAP_SHARED, fd.get(), 0 /* offset */);
+ if (data_ == MAP_FAILED) {
FAIL_WITH_STRERROR("mmap");
}
// Tell the kernel that accesses are likely to be random rather than sequential.
- if (madvise(data, sb.st_size, MADV_RANDOM) == -1) {
+ if (madvise(data_, data_length_, MADV_RANDOM) == -1) {
FAIL_WITH_STRERROR("madvise(MADV_RANDOM)");
}
UErrorCode status = U_ZERO_ERROR;
// Tell ICU to use our memory-mapped data.
- udata_setCommonData(data, &status);
+ udata_setCommonData(data_, &status);
MAYBE_FAIL_WITH_ICU_ERROR("udata_setCommonData");
- return TRUE;
-}
+ return true;
+ }
-void register_libcore_icu_ICU(JNIEnv* env) {
- // Check the timezone override file exists. If it does, map it first so we use it in preference
- // to the one that shipped with the device.
- const char* dataPathPrefix = getenv("ANDROID_DATA");
- if (dataPathPrefix == NULL) {
- ALOGE("ANDROID_DATA environment variable not set"); \
- abort();
+ bool TryUnmap() {
+ // Don't need to do opposite of udata_setCommonData,
+ // u_cleanup (performed in unregister_libcore_icu_ICU) takes care of it.
+
+ // Don't need to opposite of madvise, munmap will take care of it.
+
+ if (data_ != MAP_FAILED) {
+ if (munmap(data_, data_length_) == -1) {
+ FAIL_WITH_STRERROR("munmap");
+ }
}
+ // Don't need to close the file, it was closed automatically during TryMap.
+ return true;
+ }
+
+ std::string path_; // Save for error messages.
+ void* data_; // Save for munmap.
+ size_t data_length_; // Save for munmap.
+};
+
+struct ICURegistration {
+ // Init ICU, configuring it and loading the data files.
+ ICURegistration(JNIEnv* env) {
UErrorCode status = U_ZERO_ERROR;
// Tell ICU it can *only* use our memory-mapped data.
udata_setFileAccess(UDATA_NO_FILES, &status);
@@ -896,15 +950,13 @@
abort();
}
- // Map in optional TZ data files.
- std::string dataPath;
- dataPath = dataPathPrefix;
- dataPath += "/misc/zoneinfo/current/icu/icu_tzdata.dat";
+ std::string dataPath = getTzDataOverridePath();
+ // Map in optional TZ data files.
struct stat sb;
if (stat(dataPath.c_str(), &sb) == 0) {
ALOGD("Timezone override file found: %s", dataPath.c_str());
- if (!mapIcuData(dataPath)) {
+ if ((icu_datamap_from_data_ = IcuDataMap::Create(dataPath)) == nullptr) {
ALOGW("TZ override file %s exists but could not be loaded. Skipping.", dataPath.c_str());
}
} else {
@@ -912,18 +964,7 @@
}
// Use the ICU data files that shipped with the device for everything else.
- const char* systemPathPrefix = getenv("ANDROID_ROOT");
- if (systemPathPrefix == NULL) {
- ALOGE("ANDROID_ROOT environment variable not set"); \
- abort();
- }
- std::string systemPath;
- systemPath = systemPathPrefix;
- systemPath += "/usr/icu/";
- systemPath += U_ICUDATA_NAME;
- systemPath += ".dat";
-
- if (!mapIcuData(systemPath)) {
+ if ((icu_datamap_from_system_ = IcuDataMap::Create(getSystemPath())) == nullptr) {
abort();
}
@@ -937,4 +978,69 @@
}
jniRegisterNativeMethods(env, "libcore/icu/ICU", gMethods, NELEM(gMethods));
+ }
+
+ // De-init ICU, unloading the data files. Do the opposite of the above function.
+ ~ICURegistration() {
+ // Skip unregistering JNI methods explicitly, class unloading takes care of it.
+
+ // Reset libicu state to before it was loaded.
+ u_cleanup();
+
+ // Unmap ICU data files that shipped with the device for everything else.
+ icu_datamap_from_system_.reset();
+
+ // Unmap optional TZ data files.
+ icu_datamap_from_data_.reset();
+
+ // We don't need to call udata_setFileAccess because u_cleanup takes care of it.
+ }
+
+ // Check the timezone override file exists. If it does, map it first so we use it in preference
+ // to the one that shipped with the device.
+ static std::string getTzDataOverridePath() {
+ const char* dataPathPrefix = getenv("ANDROID_DATA");
+ if (dataPathPrefix == NULL) {
+ ALOGE("ANDROID_DATA environment variable not set"); \
+ abort();
+ }
+ std::string dataPath;
+ dataPath = dataPathPrefix;
+ dataPath += "/misc/zoneinfo/current/icu/icu_tzdata.dat";
+
+ return dataPath;
+ }
+
+ static std::string getSystemPath() {
+ const char* systemPathPrefix = getenv("ANDROID_ROOT");
+ if (systemPathPrefix == NULL) {
+ ALOGE("ANDROID_ROOT environment variable not set"); \
+ abort();
+ }
+
+ std::string systemPath;
+ systemPath = systemPathPrefix;
+ systemPath += "/usr/icu/";
+ systemPath += U_ICUDATA_NAME;
+ systemPath += ".dat";
+ return systemPath;
+ }
+
+ std::unique_ptr<IcuDataMap> icu_datamap_from_data_;
+ std::unique_ptr<IcuDataMap> icu_datamap_from_system_;
+};
+
+// Use RAII-style initialization/teardown so that we can get unregistered
+// when dlclose is called (even if JNI_OnUnload is not).
+static std::unique_ptr<ICURegistration> sIcuRegistration;
+
+// Init ICU, configuring it and loading the data files.
+void register_libcore_icu_ICU(JNIEnv* env) {
+ sIcuRegistration.reset(new ICURegistration(env));
+}
+
+// De-init ICU, unloading the data files. Do the opposite of the above function.
+void unregister_libcore_icu_ICU(JNIEnv*) {
+ // Explicitly calling this is optional. Dlclose will take care of it as well.
+ sIcuRegistration.reset();
}
diff --git a/luni/src/test/java/libcore/java/net/SocketTest.java b/luni/src/test/java/libcore/java/net/SocketTest.java
index f273f8e..1952286 100644
--- a/luni/src/test/java/libcore/java/net/SocketTest.java
+++ b/luni/src/test/java/libcore/java/net/SocketTest.java
@@ -16,6 +16,7 @@
package libcore.java.net;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -43,7 +44,6 @@
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
-import java.io.FileDescriptor;
public class SocketTest extends junit.framework.TestCase {
@@ -379,33 +379,43 @@
public void testCloseDuringConnect() throws Exception {
final CountDownLatch signal = new CountDownLatch(1);
-
final Socket s = new Socket();
- new Thread() {
- @Override
- public void run() {
- try {
- // This address is reserved for documentation: should never be reachable.
- InetSocketAddress unreachableIp = new InetSocketAddress("192.0.2.0", 80);
- // This should never return.
- s.connect(unreachableIp, 0 /* infinite */);
- fail("Connect returned unexpectedly for: " + unreachableIp);
- } catch (SocketException expected) {
- assertTrue(expected.getMessage().contains("Socket closed"));
- signal.countDown();
- } catch (IOException e) {
- fail("Unexpected exception: " + e);
- }
- }
- }.start();
- // Wait for the connect() thread to run and start connect()
+ // Executes a connect() that should block.
+ Callable<String> connectWorker = () -> {
+ try {
+ // This address is reserved for documentation: should never be reachable.
+ InetSocketAddress unreachableIp = new InetSocketAddress("192.0.2.0", 80);
+ // This should never return.
+ s.connect(unreachableIp, 0 /* infinite */);
+ return "Connect returned unexpectedly for: " + unreachableIp;
+ } catch (SocketException expected) {
+ signal.countDown();
+ return expected.getMessage().contains("Socket closed")
+ ? null
+ : "Unexpected SocketException message: " + expected.getMessage();
+ } catch (IOException e) {
+ return "Unexpected exception: " + e;
+ }
+ };
+ Future<String> connectResult =
+ Executors.newSingleThreadScheduledExecutor().submit(connectWorker);
+
+ // Wait sufficient time for the connectWorker thread to run and start connect().
Thread.sleep(2000);
+ // Close the socket that connectWorker should currently be blocked in connect().
s.close();
+ // connectWorker should have been unblocked so await() should return true.
boolean connectUnblocked = signal.await(2000, TimeUnit.MILLISECONDS);
- assertTrue(connectUnblocked);
+
+ // connectWorker should have returned null if everything went as expected.
+ String workerFailure = connectResult.get(2000, TimeUnit.MILLISECONDS);
+
+ assertTrue("connectUnblocked=[" + connectUnblocked
+ + "], workerFailure=[" + workerFailure + "]",
+ connectUnblocked && workerFailure == null);
}
// http://b/29092095