Repository: eclipse-cyclonedds/cyclonedds
Fuzzing engine: libfuzzer
Fuzz target: fuzz_type_object
Sanitizer: address
Crash type: Heap-buffer-overflow READ 4

Sanitizer report:
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 4244788798
INFO: Loaded 1 modules   (25128 inline 8-bit counters): 25128 [0x561c8c5cf148, 0x561c8c5d5370), 
INFO: Loaded 1 PC tables (25128 PCs): 25128 [0x561c8c5d5370,0x561c8c6375f0), 
/out/fuzz_type_object: Running 1 inputs 1 time(s) each.
Running: /testcase/OSV-2023-556.poc
=================================================================
==7==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000000950 at pc 0x561c8c36f8ce bp 0x7ffe5433ad80 sp 0x7ffe5433ad78
READ of size 4 at 0x502000000950 thread T0
SCARINESS: 17 (4-byte-read-heap-buffer-overflow)
    #0 0x561c8c36f8cd in xt_valid_enum_values /src/cyclonedds/src/core/ddsi/src/ddsi_typewrap.c:664:9
    #1 0x561c8c35260e in xt_validate_impl /src/cyclonedds/src/core/ddsi/src/ddsi_typewrap.c:899:21
    #2 0x561c8c353781 in ddsi_xt_validate /src/cyclonedds/src/core/ddsi/src/ddsi_typewrap.c:966:10
    #3 0x561c8c353781 in ddsi_xt_type_add_typeobj /src/cyclonedds/src/core/ddsi/src/ddsi_typewrap.c:1321:48
    #4 0x561c8c2b1d72 in ddsi_type_add_typeobj /src/cyclonedds/src/core/ddsi/src/ddsi_typelib.c:473:19
    #5 0x561c8c2adadf in LLVMFuzzerTestOneInput /src/cyclonedds/fuzz/fuzz_type_object/fuzz_type_object.c:92:11
    #6 0x561c8c1644e0 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:614:13
    #7 0x561c8c14f755 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:327:6
    #8 0x561c8c1551ef in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:862:9
    #9 0x561c8c180492 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #10 0x7fbcc079b082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 0702430aef5fa3dda43986563e9ffcc47efbd75e)
    #11 0x561c8c14793d in _start (/out/fuzz_type_object+0x12293d)

DEDUP_TOKEN: xt_valid_enum_values--xt_validate_impl--ddsi_xt_validate
0x502000000951 is located 0 bytes after 1-byte region [0x502000000950,0x502000000951)
allocated by thread T0 here:
    #0 0x561c8c2702af in malloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:68:3
    #1 0x561c8c396289 in ddsrt_malloc_s /src/cyclonedds/src/ddsrt/src/heap/posix/heap.c:19:10
    #2 0x561c8c396289 in ddsrt_malloc /src/cyclonedds/src/ddsrt/src/heap/posix/heap.c:25:15
    #3 0x561c8c36ef98 in xt_valid_enum_values /src/cyclonedds/src/core/ddsi/src/ddsi_typewrap.c:658:21
    #4 0x561c8c35260e in xt_validate_impl /src/cyclonedds/src/core/ddsi/src/ddsi_typewrap.c:899:21
    #5 0x561c8c353781 in ddsi_xt_validate /src/cyclonedds/src/core/ddsi/src/ddsi_typewrap.c:966:10
    #6 0x561c8c353781 in ddsi_xt_type_add_typeobj /src/cyclonedds/src/core/ddsi/src/ddsi_typewrap.c:1321:48
    #7 0x561c8c2b1d72 in ddsi_type_add_typeobj /src/cyclonedds/src/core/ddsi/src/ddsi_typelib.c:473:19
    #8 0x561c8c2adadf in LLVMFuzzerTestOneInput /src/cyclonedds/fuzz/fuzz_type_object/fuzz_type_object.c:92:11
    #9 0x561c8c1644e0 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:614:13
    #10 0x561c8c14f755 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:327:6
    #11 0x561c8c1551ef in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:862:9
    #12 0x561c8c180492 in main /src/llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    #13 0x7fbcc079b082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 0702430aef5fa3dda43986563e9ffcc47efbd75e)

DEDUP_TOKEN: __interceptor_malloc--ddsrt_malloc_s--ddsrt_malloc
SUMMARY: AddressSanitizer: heap-buffer-overflow /src/cyclonedds/src/core/ddsi/src/ddsi_typewrap.c:664:9 in xt_valid_enum_values
Shadow bytes around the buggy address:
  0x502000000680: fa fa 04 fa fa fa 02 fa fa fa 04 fa fa fa 04 fa
  0x502000000700: fa fa 01 fa fa fa 00 fa fa fa 02 fa fa fa 04 fa
  0x502000000780: fa fa 04 fa fa fa 01 fa fa fa 04 fa fa fa 04 fa
  0x502000000800: fa fa 00 00 fa fa 04 fa fa fa 04 fa fa fa 00 00
  0x502000000880: fa fa 04 fa fa fa 04 fa fa fa 02 fa fa fa 00 04
=>0x502000000900: fa fa 00 fa fa fa 01 fa fa fa[01]fa fa fa fa fa
  0x502000000980: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000000a00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000000a80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000000b00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x502000000b80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==7==ABORTING


Fix patch:
diff --git a/src/core/ddsi/src/ddsi_typewrap.c b/src/core/ddsi/src/ddsi_typewrap.c
index 2e20e92f..9301282d 100644
--- a/src/core/ddsi/src/ddsi_typewrap.c
+++ b/src/core/ddsi/src/ddsi_typewrap.c
@@ -584,15 +584,25 @@ static dds_return_t xt_valid_struct_member_ids (struct ddsi_domaingv *gv, const
 {
   assert (ddsi_xt_is_resolved (t) && t->_d == DDS_XTypes_TK_STRUCTURE);
   dds_return_t ret = DDS_RETCODE_OK;
+
   uint32_t cnt = 0;
   for (const struct xt_type *t1 = t; t1 && ddsi_xt_is_resolved (t1); t1 = t1->_u.structure.base_type ? &t1->_u.structure.base_type->xt : NULL)
     cnt += t1->_u.structure.members.length;
   if (cnt == 0 && !t->_u.structure.base_type)
   {
     GVTRACE ("struct has no members\n");
-    return DDS_RETCODE_BAD_PARAMETER;
+    ret = DDS_RETCODE_BAD_PARAMETER;
+    goto failed;
   }
+
   DDS_XTypes_MemberId *ids = ddsrt_malloc (cnt * sizeof (*ids));
+  if (ids == NULL)
+  {
+    GVTRACE ("out-of-memory while checking struct member ids\n");
+    ret = DDS_RETCODE_BAD_PARAMETER;
+    goto failed;
+  }
+
   uint32_t cnt1 = cnt;
   for (const struct xt_type *t1 = t; t1 && ddsi_xt_is_resolved (t1); t1 = t1->_u.structure.base_type ? &t1->_u.structure.base_type->xt : NULL)
   {
@@ -606,12 +616,13 @@ static dds_return_t xt_valid_struct_member_ids (struct ddsi_domaingv *gv, const
     {
       GVTRACE ("duplicate member id %"PRIu32" in struct\n", ids[n]);
       ret = DDS_RETCODE_BAD_PARAMETER;
-      goto err;
+      goto failed_duplicate;
     }
   }
 
-err:
+failed_duplicate:
   ddsrt_free (ids);
+failed:
   return ret;
 }
 
@@ -619,13 +630,23 @@ static dds_return_t xt_valid_union_member_ids (struct ddsi_domaingv *gv, const s
 {
   assert (ddsi_xt_is_resolved (t) && t->_d == DDS_XTypes_TK_UNION);
   dds_return_t ret = DDS_RETCODE_OK;
+
   uint32_t cnt = t->_u.union_type.members.length;
   if (cnt == 0)
   {
     GVTRACE ("union has no members\n");
-    return DDS_RETCODE_BAD_PARAMETER;
+    ret = DDS_RETCODE_BAD_PARAMETER;
+    goto failed;
   }
+
   DDS_XTypes_MemberId *ids = ddsrt_malloc (cnt * sizeof (*ids));
+  if (ids == NULL)
+  {
+    GVTRACE ("out-of-memory while checking union member ids\n");
+    ret = DDS_RETCODE_BAD_PARAMETER;
+    goto failed;
+  }
+
   for (uint32_t n = 0; n < cnt; n++)
     ids[n] = t->_u.union_type.members.seq[n].id;
   qsort (ids, cnt, sizeof (*ids), xt_member_id_cmp);
@@ -635,12 +656,13 @@ static dds_return_t xt_valid_union_member_ids (struct ddsi_domaingv *gv, const s
     {
       GVTRACE ("duplicate member id %"PRIu32" in union\n", ids[n]);
       ret = DDS_RETCODE_BAD_PARAMETER;
-      goto err;
+      goto failed_duplicate;
     }
   }
 
-err:
+failed_duplicate:
   ddsrt_free (ids);
+failed:
   return ret;
 }
 
@@ -654,8 +676,23 @@ static dds_return_t xt_valid_enum_values (struct ddsi_domaingv *gv, const struct
 {
   assert (ddsi_xt_is_resolved (t) && t->_d == DDS_XTypes_TK_ENUM);
   dds_return_t ret = DDS_RETCODE_OK;
+
   uint32_t cnt = t->_u.enum_type.literals.length;
+  if (cnt == 0)
+  {
+    GVTRACE ("enum has no members\n");
+    ret = DDS_RETCODE_BAD_PARAMETER;
+    goto failed;
+  }
+
   int32_t *values = ddsrt_malloc (cnt * sizeof (*values));
+  if (values == NULL)
+  {
+    GVTRACE ("out-of-memory while checking enum values\n");
+    ret = DDS_RETCODE_OUT_OF_RESOURCES;
+    goto failed;
+  }
+
   for (uint32_t n = 0; n < cnt; n++)
     values[n] = t->_u.enum_type.literals.seq[n].value;
   qsort (values, cnt, sizeof (*values), xt_enum_value_cmp);
@@ -665,12 +702,13 @@ static dds_return_t xt_valid_enum_values (struct ddsi_domaingv *gv, const struct
     {
       GVTRACE ("duplicate enum value %"PRIi32"\n", values[n]);
       ret = DDS_RETCODE_BAD_PARAMETER;
-      goto err;
+      goto failed_duplicate;
     }
   }
 
-err:
+failed_duplicate:
   ddsrt_free (values);
+failed:
   return ret;
 }
 
@@ -685,7 +723,21 @@ static dds_return_t xt_valid_bitmask_positions (struct ddsi_domaingv *gv, const
   assert (ddsi_xt_is_resolved (t) && t->_d == DDS_XTypes_TK_BITMASK);
   dds_return_t ret = DDS_RETCODE_OK;
   uint32_t cnt = t->_u.bitmask.bitflags.length;
+  if (cnt == 0)
+  {
+    GVTRACE ("bitmask has no bitflags\n");
+    ret = DDS_RETCODE_BAD_PARAMETER;
+    goto failed;
+  }
+
   uint16_t *positions = ddsrt_malloc (cnt * sizeof (*positions));
+  if (positions == NULL)
+  {
+    GVTRACE ("out-of-memory while checking bitmask positions\n");
+    ret = DDS_RETCODE_OUT_OF_RESOURCES;
+    goto failed;
+  }
+
   for (uint32_t n = 0; n < cnt; n++)
     positions[n] = t->_u.bitmask.bitflags.seq[n].position;
   qsort (positions, cnt, sizeof (*positions), xt_bitmask_position_cmp);
@@ -695,12 +747,13 @@ static dds_return_t xt_valid_bitmask_positions (struct ddsi_domaingv *gv, const
     {
       GVTRACE ("duplicate bitmask position %"PRIu16"\n", positions[n]);
       ret = DDS_RETCODE_BAD_PARAMETER;
-      goto err;
+      goto failed_duplicate;
     }
   }
 
-err:
+failed_duplicate:
   ddsrt_free (positions);
+failed:
   return ret;
 }