@@ -214,7 +214,8 @@ async fn test_data_stream_modified_utf_round_trip_and_malformed_input() -> Resul
214214 let data_output = jvm
215215 . new_class ( "java/io/DataOutputStream" , "(Ljava/io/OutputStream;)V" , ( output. clone ( ) , ) )
216216 . await ?;
217- let expected = JavaLangString :: from_rust_string ( & jvm, "\0 A😀한" ) . await ?;
217+ let units = [ 0 , 0x41 , 0x7f , 0x80 , 0x7ff , 0x800 , 0xd83d , 0xde00 , 0xd800 , 0x41 , 0xdc00 , 0xffff ] ;
218+ let expected = JavaLangString :: from_utf16 ( & jvm, units. to_vec ( ) ) . await ?;
218219 let _: ( ) = jvm
219220 . invoke_virtual (
220221 & data_output,
@@ -229,26 +230,81 @@ async fn test_data_stream_modified_utf_round_trip_and_malformed_input() -> Resul
229230 . invoke_virtual ( & output, & output. class_definition ( ) . name ( ) , "toByteArray" , "()[B" , ( ) )
230231 . await ?;
231232 let encoded: Vec < i8 > = jvm. load_array ( & bytes, 0 , jvm. array_length ( & bytes) . await ?) . await ?;
232- assert_eq ! ( & encoded[ 2 ..4 ] , & [ -64 , -128 ] ) ;
233+ assert_eq ! (
234+ encoded. iter( ) . map( |& byte| byte as u8 ) . collect:: <Vec <_>>( ) ,
235+ [
236+ 0 , 27 , 0xc0 , 0x80 , 0x41 , 0x7f , 0xc2 , 0x80 , 0xdf , 0xbf , 0xe0 , 0xa0 , 0x80 , 0xed , 0xa0 , 0xbd , 0xed , 0xb8 , 0x80 , 0xed , 0xa0 , 0x80 , 0x41 ,
237+ 0xed , 0xb0 , 0x80 , 0xef , 0xbf , 0xbf ,
238+ ]
239+ ) ;
233240
234241 let input = jvm. new_class ( "java/io/ByteArrayInputStream" , "([B)V" , ( bytes, ) ) . await ?;
235242 let data_input = jvm. new_class ( "java/io/DataInputStream" , "(Ljava/io/InputStream;)V" , ( input, ) ) . await ?;
236243 let decoded: ClassInstanceRef < rustjava_runtime:: classes:: java:: lang:: String > = jvm
237244 . invoke_virtual ( & data_input, & data_input. class_definition ( ) . name ( ) , "readUTF" , "()Ljava/lang/String;" , ( ) )
238245 . await ?;
239- assert_eq ! ( JavaLangString :: to_rust_string ( & jvm, & decoded) . await ?, " \0 A😀한" ) ;
246+ assert_eq ! ( JavaLangString :: to_utf16 ( & jvm, & decoded) . await ?, units ) ;
240247
241- let mut malformed = jvm. instantiate_array ( "B" , 4 ) . await ?;
242- jvm. store_array ( & mut malformed, 0 , [ 0i8 , 2 , -62 , 32 ] ) . await ?;
243- let input = jvm. new_class ( "java/io/ByteArrayInputStream" , "([B)V" , ( malformed, ) ) . await ?;
244- let data_input = jvm. new_class ( "java/io/DataInputStream" , "(Ljava/io/InputStream;)V" , ( input, ) ) . await ?;
245- let result: Result < ClassInstanceRef < rustjava_runtime:: classes:: java:: lang:: String > > = jvm
246- . invoke_virtual ( & data_input, & data_input. class_definition ( ) . name ( ) , "readUTF" , "()Ljava/lang/String;" , ( ) )
247- . await ;
248- let Err ( JavaError :: JavaException ( exception) ) = result else {
249- panic ! ( "malformed modified UTF-8 must throw UTFDataFormatException" ) ;
250- } ;
251- assert ! ( jvm. is_instance( & * exception, "java/io/UTFDataFormatException" ) ) ;
248+ for payload in [
249+ & [ 0xc2 , 0x20 ] [ ..] ,
250+ & [ 0xc0 ] ,
251+ & [ 0xe0 , 0xa0 ] ,
252+ & [ 0xe0 , 0x20 , 0x80 ] ,
253+ & [ 0xe0 , 0xa0 , 0x20 ] ,
254+ & [ 0xf0 , 0x9f , 0x98 , 0x80 ] ,
255+ & [ 0x80 ] ,
256+ & [ 0xff ] ,
257+ ] {
258+ let mut data = ( payload. len ( ) as u16 ) . to_be_bytes ( ) . to_vec ( ) ;
259+ data. extend_from_slice ( payload) ;
260+ let mut malformed = jvm. instantiate_array ( "B" , data. len ( ) ) . await ?;
261+ jvm. store_array ( & mut malformed, 0 , data. into_iter ( ) . map ( |byte| byte as i8 ) . collect :: < Vec < _ > > ( ) )
262+ . await ?;
263+ let input = jvm. new_class ( "java/io/ByteArrayInputStream" , "([B)V" , ( malformed, ) ) . await ?;
264+ let data_input = jvm. new_class ( "java/io/DataInputStream" , "(Ljava/io/InputStream;)V" , ( input, ) ) . await ?;
265+ let result: Result < ClassInstanceRef < rustjava_runtime:: classes:: java:: lang:: String > > = jvm
266+ . invoke_virtual ( & data_input, & data_input. class_definition ( ) . name ( ) , "readUTF" , "()Ljava/lang/String;" , ( ) )
267+ . await ;
268+ let Err ( JavaError :: JavaException ( exception) ) = result else {
269+ panic ! ( "malformed modified UTF-8 must throw UTFDataFormatException: {payload:?}" ) ;
270+ } ;
271+ assert ! ( jvm. is_instance( & * exception, "java/io/UTFDataFormatException" ) ) ;
272+ }
273+
274+ Ok ( ( ) )
275+ }
276+
277+ #[ tokio:: test]
278+ async fn test_write_utf_encoded_length_limit ( ) -> Result < ( ) > {
279+ let jvm = test_jvm ( ) . await ?;
280+ let output = jvm. new_class ( "java/io/ByteArrayOutputStream" , "()V" , ( ) ) . await ?;
281+ let data_output = jvm
282+ . new_class ( "java/io/DataOutputStream" , "(Ljava/io/OutputStream;)V" , ( output. clone ( ) , ) )
283+ . await ?;
284+
285+ for ( length, succeeds) in [ ( 0 , true ) , ( 21845 , true ) , ( 21846 , false ) ] {
286+ let _: ( ) = jvm. invoke_virtual ( & output, "java/io/ByteArrayOutputStream" , "reset" , "()V" , ( ) ) . await ?;
287+ let value = JavaLangString :: from_utf16 ( & jvm, vec ! [ 0x800 ; length] ) . await ?;
288+ let result: Result < ( ) > = jvm
289+ . invoke_virtual ( & data_output, "java/io/DataOutputStream" , "writeUTF" , "(Ljava/lang/String;)V" , ( value, ) )
290+ . await ;
291+ if succeeds {
292+ result?;
293+ let bytes: ClassInstanceRef < Array < i8 > > = jvm
294+ . invoke_virtual ( & output, "java/io/ByteArrayOutputStream" , "toByteArray" , "()[B" , ( ) )
295+ . await ?;
296+ assert_eq ! ( jvm. array_length( & bytes) . await ?, length * 3 + 2 ) ;
297+ let header: Vec < i8 > = jvm. load_array ( & bytes, 0 , 2 ) . await ?;
298+ assert_eq ! ( header, if length == 0 { [ 0 , 0 ] } else { [ -1 , -1 ] } ) ;
299+ } else {
300+ let Err ( JavaError :: JavaException ( exception) ) = result else {
301+ panic ! ( "oversized modified UTF-8 must throw UTFDataFormatException" ) ;
302+ } ;
303+ assert ! ( jvm. is_instance( & * exception, "java/io/UTFDataFormatException" ) ) ;
304+ let size: i32 = jvm. invoke_virtual ( & output, "java/io/ByteArrayOutputStream" , "size" , "()I" , ( ) ) . await ?;
305+ assert_eq ! ( size, 0 ) ;
306+ }
307+ }
252308
253309 Ok ( ( ) )
254310}
0 commit comments