Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2470a475 authored by Priyanka Advani (xWF)'s avatar Priyanka Advani (xWF) Committed by Gerrit Code Review
Browse files

Revert "ArrayUtils: add secure zeroization support"

This reverts commit c948c80b.

Reason for revert: Droidmonitor created revert due to b/381880605. Will be verifying through ABTD before submission.

Change-Id: Iaf6bd3ebd187aa758c524083bc559fac85926ef0
parent c948c80b
Loading
Loading
Loading
Loading
+0 −45
Original line number Diff line number Diff line
@@ -84,51 +84,6 @@ public class ArrayUtils {
        return (T[])VMRuntime.getRuntime().newUnpaddedArray(clazz, minLen);
    }

    /**
     * This is like <code>new byte[length]</code>, but it allocates the array as non-movable. This
     * prevents copies of the data from being left on the Java heap as a result of heap compaction.
     * Use this when the array will contain sensitive data such as a password or cryptographic key
     * that needs to be wiped from memory when no longer needed. The owner of the array is still
     * responsible for the zeroization; {@link #zeroize(byte[])} should be used to do so.
     *
     * @param length the length of the array to allocate
     * @return the new array
     */
    public static byte[] newNonMovableByteArray(int length) {
        return (byte[]) VMRuntime.getRuntime().newNonMovableArray(byte.class, length);
    }

    /**
     * Like {@link #newNonMovableByteArray(int)}, but allocates a char array.
     *
     * @param length the length of the array to allocate
     * @return the new array
     */
    public static char[] newNonMovableCharArray(int length) {
        return (char[]) VMRuntime.getRuntime().newNonMovableArray(char.class, length);
    }

    /**
     * Zeroizes a byte array as securely as possible. Use this when the array contains sensitive
     * data such as a password or cryptographic key.
     * <p>
     * This zeroizes the array in a way that is guaranteed to not be optimized out by the compiler.
     * If supported by the architecture, it zeroizes the data not just in the L1 data cache but also
     * in other levels of the memory hierarchy up to and including main memory (but not above that).
     * <p>
     * This works on any <code>byte[]</code>, but to ensure that copies of the array aren't left on
     * the Java heap the array should have been allocated with {@link #newNonMovableByteArray(int)}.
     * Use on other arrays might also introduce performance anomalies.
     *
     * @param array the array to zeroize
     */
    public static native void zeroize(byte[] array);

    /**
     * Like {@link #zeroize(byte[])}, but for char arrays.
     */
    public static native void zeroize(char[] array);

    /**
     * Checks if the beginnings of two byte arrays are equal.
     *
+0 −1
Original line number Diff line number Diff line
@@ -90,7 +90,6 @@ cc_library_shared_for_libandroid_runtime {
        "android_view_VelocityTracker.cpp",
        "android_view_VerifiedKeyEvent.cpp",
        "android_view_VerifiedMotionEvent.cpp",
        "com_android_internal_util_ArrayUtils.cpp",
        "com_android_internal_util_VirtualRefBasePtr.cpp",
        "core_jni_helpers.cpp",
        ":deviceproductinfoconstants_aidl",
+0 −2
Original line number Diff line number Diff line
@@ -215,7 +215,6 @@ extern int register_com_android_internal_os_Zygote(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
extern int register_com_android_internal_security_VerityUtils(JNIEnv* env);
extern int register_com_android_internal_util_ArrayUtils(JNIEnv* env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
extern int register_android_window_WindowInfosListener(JNIEnv* env);
extern int register_android_window_ScreenCapture(JNIEnv* env);
@@ -1614,7 +1613,6 @@ static const RegJNIRec gRegJNI[] = {
        REG_JNI(register_com_android_internal_os_ZygoteCommandBuffer),
        REG_JNI(register_com_android_internal_os_ZygoteInit),
        REG_JNI(register_com_android_internal_security_VerityUtils),
        REG_JNI(register_com_android_internal_util_ArrayUtils),
        REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
        REG_JNI(register_android_hardware_Camera),
        REG_JNI(register_android_hardware_camera2_CameraMetadata),
+0 −122
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "ArrayUtils"

#include <android-base/logging.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include <string.h>
#include <unistd.h>
#include <utils/Log.h>

namespace android {

static size_t GetCacheLineSize() {
    long size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
    if (size <= 0) {
        ALOGE("Unable to determine L1 data cache line size. Assuming 32 bytes");
        return 32;
    }
    return size;
}

#ifdef __aarch64__
static void CleanDataCache(const uint8_t* p, size_t size, size_t cache_line_size) {
    // Execute 'dc cvac' at least once on each cache line in the memory region.
    //
    // 'dc cvac' stands for "Data Cache line Clean by Virtual Address to point-of-Coherency".
    // It writes the cache line back to the "point-of-coherency", i.e. main memory.
    //
    // Since the memory region is not guaranteed to be cache-line-aligned, we use an "extra"
    // instruction after the loop to make sure the last cache line gets covered.
    for (size_t i = 0; i < size; i += cache_line_size) {
        asm volatile("dc cvac, %0" ::"r"(p + i));
    }
    asm volatile("dc cvac, %0" ::"r"(p + size - 1));
}
#elif defined(__i386__) || defined(__x86_64__)
static void CleanDataCache(const uint8_t* p, size_t size, size_t cache_line_size) {
    for (size_t i = 0; i < size; i += cache_line_size) {
        asm volatile("clflush (%0)" ::"r"(p + i));
    }
    asm volatile("clflush (%0)" ::"r"(p + size - 1));
}
#elif defined(__riscv)
static void CleanDataCache(const uint8_t* p, size_t size, size_t cache_line_size) {
    // This should eventually work, but it is not ready to be enabled yet:
    //  1.) The Android emulator needs to add support for zicbom.
    //  2.) Kernel needs to enable zicbom in usermode.
    //  3.) Android clang needs to add zicbom to the target.
#if 0
    for (size_t i = 0; i < size; i += cache_line_size) {
        asm volatile("cbo.clean (%0)" ::"r"(p + i));
    }
    asm volatile("cbo.clean (%0)" ::"r"(p + size - 1));
#endif
}
#elif defined(__arm__)
// arm32 has a cacheflush() syscall, but it is undocumented and only flushes the icache.
// It is not the same as cacheflush(2) as documented in the Linux man-pages project.
static void CleanDataCache(const uint8_t* p, size_t size, size_t cache_line_size) {}
#else
#error "Unknown architecture"
#endif

static void ZeroizePrimitiveArray(JNIEnv* env, jclass clazz, jarray array, size_t component_len) {
    static const size_t cache_line_size = GetCacheLineSize();

    size_t size = env->GetArrayLength(array) * component_len;
    if (size == 0) {
        return;
    }

    // ART guarantees that GetPrimitiveArrayCritical never copies.
    jboolean isCopy;
    void* elems = env->GetPrimitiveArrayCritical(array, &isCopy);
    CHECK(!isCopy);

#ifdef __BIONIC__
    memset_explicit(elems, 0, size);
#else
    memset(elems, 0, size);
#endif
    // Clean the data cache so that the data gets zeroized in main memory right away.  Without this,
    // it might not be written to main memory until the cache line happens to be evicted.
    CleanDataCache(static_cast<const uint8_t*>(elems), size, cache_line_size);

    env->ReleasePrimitiveArrayCritical(array, elems, /* mode= */ 0);
}

static void ZeroizeByteArray(JNIEnv* env, jclass clazz, jbyteArray array) {
    ZeroizePrimitiveArray(env, clazz, array, sizeof(jbyte));
}

static void ZeroizeCharArray(JNIEnv* env, jclass clazz, jcharArray array) {
    ZeroizePrimitiveArray(env, clazz, array, sizeof(jchar));
}

static const JNINativeMethod sMethods[] = {
        {"zeroize", "([B)V", (void*)ZeroizeByteArray},
        {"zeroize", "([C)V", (void*)ZeroizeCharArray},
};

int register_com_android_internal_util_ArrayUtils(JNIEnv* env) {
    return jniRegisterNativeMethods(env, "com/android/internal/util/ArrayUtils", sMethods,
                                    NELEM(sMethods));
}

} // namespace android
+0 −47
Original line number Diff line number Diff line
@@ -496,51 +496,4 @@ public class ArrayUtilsTest {
            // expected
        }
    }

    // Note: the zeroize() tests only test the behavior that can be tested from a Java test.
    // They do not verify that no copy of the data is left anywhere.

    @Test
    @SmallTest
    public void testZeroizeNonMovableByteArray() {
        final int length = 10;
        byte[] array = ArrayUtils.newNonMovableByteArray(length);
        assertArrayEquals(array, new byte[length]);
        Arrays.fill(array, (byte) 0xff);
        ArrayUtils.zeroize(array);
        assertArrayEquals(array, new byte[length]);
    }

    @Test
    @SmallTest
    public void testZeroizeRegularByteArray() {
        final int length = 10;
        byte[] array = new byte[length];
        assertArrayEquals(array, new byte[length]);
        Arrays.fill(array, (byte) 0xff);
        ArrayUtils.zeroize(array);
        assertArrayEquals(array, new byte[length]);
    }

    @Test
    @SmallTest
    public void testZeroizeNonMovableCharArray() {
        final int length = 10;
        char[] array = ArrayUtils.newNonMovableCharArray(length);
        assertArrayEquals(array, new char[length]);
        Arrays.fill(array, (char) 0xff);
        ArrayUtils.zeroize(array);
        assertArrayEquals(array, new char[length]);
    }

    @Test
    @SmallTest
    public void testZeroizeRegularCharArray() {
        final int length = 10;
        char[] array = new char[length];
        assertArrayEquals(array, new char[length]);
        Arrays.fill(array, (char) 0xff);
        ArrayUtils.zeroize(array);
        assertArrayEquals(array, new char[length]);
    }
}