@@ -74,6 +74,11 @@ impl BoxedBytes {
7474 index -= 1 ;
7575 result_len += slices[ index] . len ( ) ;
7676 }
77+ // Calling `alloc` with a zero-sized layout is undefined behavior.
78+ // Return a valid empty instance directly instead of entering the unsafe path.
79+ if result_len == 0 {
80+ return BoxedBytes :: empty ( ) ;
81+ }
7782 unsafe {
7883 let layout = Layout :: from_size_align ( result_len, core:: mem:: align_of :: < u8 > ( ) ) . unwrap ( ) ;
7984 let result_ptr = alloc ( layout) ;
@@ -256,6 +261,40 @@ mod tests {
256261 assert_eq ! ( bb, BoxedBytes :: from( & b"" [ ..] ) ) ;
257262 }
258263
264+ /// Regression test: previously triggered UB by calling `alloc(size=0)`.
265+ #[ test]
266+ fn test_concat_single_empty_slice ( ) {
267+ let bb = BoxedBytes :: from_concat ( & [ & b"" [ ..] ] ) ;
268+ assert_eq ! ( bb, BoxedBytes :: empty( ) ) ;
269+ assert ! ( bb. is_empty( ) ) ;
270+ }
271+
272+ #[ test]
273+ fn test_concat_single_nonempty_slice ( ) {
274+ let bb = BoxedBytes :: from_concat ( & [ & b"hello" [ ..] ] ) ;
275+ assert_eq ! ( bb, BoxedBytes :: from( & b"hello" [ ..] ) ) ;
276+ }
277+
278+ #[ test]
279+ fn test_concat_leading_empty_slices ( ) {
280+ let bb = BoxedBytes :: from_concat ( & [ & b"" [ ..] , & b"" [ ..] , & b"abc" [ ..] ] ) ;
281+ assert_eq ! ( bb, BoxedBytes :: from( & b"abc" [ ..] ) ) ;
282+ }
283+
284+ #[ test]
285+ fn test_concat_trailing_empty_slices ( ) {
286+ let bb = BoxedBytes :: from_concat ( & [ & b"abc" [ ..] , & b"" [ ..] , & b"" [ ..] ] ) ;
287+ assert_eq ! ( bb, BoxedBytes :: from( & b"abc" [ ..] ) ) ;
288+ }
289+
290+ #[ test]
291+ fn test_concat_result_is_empty_instance ( ) {
292+ // All-empty concat must return a proper empty BoxedBytes, not a dangling pointer.
293+ let bb = BoxedBytes :: from_concat ( & [ & b"" [ ..] , & b"" [ ..] ] ) ;
294+ assert_eq ! ( bb. len( ) , 0 ) ;
295+ assert_eq ! ( bb. as_slice( ) , & b"" [ ..] ) ;
296+ }
297+
259298 #[ test]
260299 fn test_is_empty ( ) {
261300 assert ! ( BoxedBytes :: empty( ) . is_empty( ) ) ;
0 commit comments