From b62fe47ba2bee1a87d8174e9f49f5bf683a65924 Mon Sep 17 00:00:00 2001 From: Justin Jiang Date: Tue, 8 Jul 2025 10:59:36 +0800 Subject: [PATCH] ANDROID: fix ashmem_rust return EINVAL bug in ashmem_rust.rs The application read the length of file and triggered a crash in android16-6.12. We found through analysis that FIONREAD should return ENOTTY, but EINVAL was returned in ashmem_rust.rs, which caused this issue. The default return is ENOTTY in ashmem.c. The failed stacks is as follows: 07-03 10:47:51.830 13963 13963 D AndroidRuntime: Shutting down VM 07-03 10:47:51.834 13963 13963 E AndroidRuntime: FATAL EXCEPTION: main 07-03 10:47:51.834 13963 13963 E AndroidRuntime: Process: com.example.parceldemo:service, PID: 13963 07-03 10:47:51.834 13963 13963 E AndroidRuntime: java.lang.RuntimeException: Unable to start service com.example.parceldemo.services.ProcessService@b11ac4f with Intent { xflg=0x4 cmp=com.example.parceldemo/.services.ProcessService (has extras) }: java.io.IOException: Invalid argument 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:6099) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at android.app.ActivityThread.-$$Nest$mhandleServiceArgs(Unknown Source:0) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:3067) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:110) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:266) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at android.os.Looper.loop(Looper.java:361) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:10155) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:638) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1002) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: Caused by: java.io.IOException: Invalid argument 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at java.io.FileInputStream.available0(Native Method) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at java.io.FileInputStream.available(FileInputStream.java:523) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at kotlin.io.ByteStreamsKt.readBytes(IOStreams.kt:135) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at com.example.parceldemo.model.Book$Companion.from(Book.kt:25) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at com.example.parceldemo.services.ProcessService.onStartCommand(ProcessService.kt:37) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:6081) 07-03 10:47:51.834 13963 13963 E AndroidRuntime: ... 9 more The analysis process is as follows: InputStream.readBytes(): ByteArray --> available() --> ioctl(fd, FIONREAD, &n) --> ashmem_rust.rs fn ioctl(me: Pin<&Ashmem>, _file: &File, cmd: u32, arg: usize) -> Result --> return Err(EINVAL) /** * Reads this stream completely into a byte array. * * **Note**: It is the caller's responsibility to close this stream. */ @SinceKotlin("1.3") public fun InputStream.readBytes(): ByteArray { val buffer = ByteArrayOutputStream(maxOf(DEFAULT_BUFFER_SIZE, this.available())) copyTo(buffer) return buffer.toByteArray() } libcore/ojluni/src/main/native/FileInputStream.c 128 static int available(int fd, jlong *bytes) { 129 // BEGIN Android-added: Fuchsia does not support FIONREAD. http://b/120566512 130 #if defined(__Fuchsia__) 131 *bytes = 0; 132 return 1; 133 #else 134 // END Android-added: Fuchsia does not support FIONREAD. http://b/120566512 135 int n; 136 // Unlike the original OpenJdk implementation, we use FIONREAD for all file 137 // types. For regular files, this is specified to return the difference 138 // between the current position and the file size. Note that this can be 139 // negative if we're positioned past the end of the file. We must return 0 140 // in that case. 141 if (ioctl(fd, FIONREAD, &n) != -1) { 142 if (n < 0) { 143 n = 0; 144 } 145 *bytes = n; 146 return 1; 147 } 148 149 // FIONREAD is specified to return ENOTTY when fd refers to a file 150 // type for which this ioctl isn't implemented. 151 if (errno == ENOTTY) { 152 *bytes = 0; 153 return 1; 154 } 155 156 // Raise an exception for all other error types. 157 return 0; 158 // Android-added: Fuchsia does not support the FIONREAD code. http://b/120566512 159 #endif 160 } 161 162 JNIEXPORT jint JNICALL 163 Java_java_io_FileInputStream_available0(JNIEnv *env, jobject this) { 164 jlong ret; 165 FD fd = getFD(env, this, fis_fd); 166 if (fd == -1) { 167 JNU_ThrowIOException (env, "Stream Closed"); 168 return 0; 169 } 170 if (available(fd, &ret)) { 171 if (ret > INT_MAX) { 172 ret = (jlong) INT_MAX; 173 } else if (ret < 0) { 174 ret = 0; 175 } 176 return jlong_to_jint(ret); 177 } 178 JNU_ThrowIOExceptionWithLastError(env, NULL); 179 return 0; 180 } common/drivers/staging/android/ashmem_rust.rs 245 fn ioctl(me: Pin<&Ashmem>, _file: &File, cmd: u32, arg: usize) -> Result { 246 let size = _IOC_SIZE(cmd); 247 match cmd { 248 bindings::ASHMEM_SET_NAME => me.set_name(UserSlice::new(arg, size).reader()), 249 bindings::ASHMEM_GET_NAME => me.get_name(UserSlice::new(arg, size).writer()), 250 bindings::ASHMEM_SET_SIZE => me.set_size(arg), 251 bindings::ASHMEM_GET_SIZE => me.get_size(), 252 bindings::ASHMEM_SET_PROT_MASK => me.set_prot_mask(arg), 253 bindings::ASHMEM_GET_PROT_MASK => me.get_prot_mask(), 254 bindings::ASHMEM_GET_FILE_ID => me.get_file_id(UserSlice::new(arg, size).writer()), 255 ASHMEM_PIN | ASHMEM_UNPIN | ASHMEM_GET_PIN_STATUS => { 256 me.pin_unpin(cmd, UserSlice::new(arg, size).reader()) 257 } 258 bindings::ASHMEM_PURGE_ALL_CACHES => me.purge_all_caches(), 259 _ => Err(EINVAL), 260 } 261 } common/drivers/staging/android/ashmem.c 828 static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 829 { 830 struct ashmem_area *asma = file->private_data; 831 unsigned long ino; 832 long ret = -ENOTTY; -> For the c-version, it's ENOTTY Bug: 430143362 Change-Id: Ie316e7f3670ecdf59b75ff127283a6744763361d Signed-off-by: Justin Jiang --- drivers/staging/android/ashmem_rust.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/android/ashmem_rust.rs b/drivers/staging/android/ashmem_rust.rs index 3b31d5d8fcfc..8021380c8ed8 100644 --- a/drivers/staging/android/ashmem_rust.rs +++ b/drivers/staging/android/ashmem_rust.rs @@ -258,7 +258,7 @@ impl MiscDevice for Ashmem { me.pin_unpin(cmd, UserSlice::new(arg, size).reader()) } bindings::ASHMEM_PURGE_ALL_CACHES => me.purge_all_caches(), - _ => Err(EINVAL), + _ => Err(ENOTTY), } }