Repository: avahi/avahi (C)

Issue:
`avahi_record_lexicographical_compare` crashes with RRs with zero-length rdata
It can be reproduced by applying the following patch:
```diff
diff --git a/fuzz/fuzz-consume-record.c b/fuzz/fuzz-consume-record.c
index fe9f969..d59b48a 100644
--- a/fuzz/fuzz-consume-record.c
+++ b/fuzz/fuzz-consume-record.c
@@ -56,6 +56,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
     ret = avahi_record_equal_no_ttl(r, c);
     assert(ret);
 
+    ret = avahi_record_lexicographical_compare(r, c);
+    assert(ret == 0);
+
     avahi_dns_packet_free(p);
     if (!(p = avahi_dns_packet_new(size + AVAHI_DNS_PACKET_EXTRA_SIZE)))
         goto finish;
```
and running the fuzz target. It crashes with
```sh
fuzz-consume-record: rr.c:539: int lexicographical_memcmp(const void *, size_t, const void *, size_t): Assertion `a' failed.                                  
==140723== ERROR: libFuzzer: deadly signal                                                                                                                    
    #0 0x5031b5 in __sanitizer_print_stack_trace (avahi/out/fuzz-consume-record+0x5031b5) (BuildId: 9078d0f1becdb3fc0c1dcf44fcd619d217b22e04)   
    #1 0x45cd6c in fuzzer::PrintStackTrace() (avahi/out/fuzz-consume-record+0x45cd6c) (BuildId: 9078d0f1becdb3fc0c1dcf44fcd619d217b22e04)       
    #2 0x441c47 in fuzzer::Fuzzer::CrashCallback() (avahi/out/fuzz-consume-record+0x441c47) (BuildId: 9078d0f1becdb3fc0c1dcf44fcd619d217b22e04) 
    #3 0x7fdbcd629baf  (/lib64/libc.so.6+0x3dbaf) (BuildId: 3ebe8d97a0ed3e1f13476a02665c5a9442adcd78)                                                         
    #4 0x7fdbcd67a883 in __pthread_kill_implementation (/lib64/libc.so.6+0x8e883) (BuildId: 3ebe8d97a0ed3e1f13476a02665c5a9442adcd78)                         
    #5 0x7fdbcd629afd in gsignal (/lib64/libc.so.6+0x3dafd) (BuildId: 3ebe8d97a0ed3e1f13476a02665c5a9442adcd78)                                               
    #6 0x7fdbcd61287e in abort (/lib64/libc.so.6+0x2687e) (BuildId: 3ebe8d97a0ed3e1f13476a02665c5a9442adcd78)                                                 
    #7 0x7fdbcd61279a in __assert_fail_base.cold (/lib64/libc.so.6+0x2679a) (BuildId: 3ebe8d97a0ed3e1f13476a02665c5a9442adcd78)                               
    #8 0x7fdbcd622186 in __assert_fail (/lib64/libc.so.6+0x36186) (BuildId: 3ebe8d97a0ed3e1f13476a02665c5a9442adcd78)                                         
    #9 0x54d07e in lexicographical_memcmp avahi/avahi-core/rr.c:539:5                                                                           
    #10 0x536561 in LLVMFuzzerTestOneInput avahi/fuzz/fuzz-consume-record.c:59:11                                                               
```
It affects RRs where types are unknown to avahi. TXT RRs with zero-length rdata and so on are fine.

What it means in practice is that when certain conditions are met it's possible to crash avahi remotely by sending a probe with zero-length data. It goes down with:
```sh
#0  0x00007ffff72b0884 in __pthread_kill_implementation ()                                                                                                    
   from /lib64/libc.so.6                                                                                                                                      
#1  0x00007ffff725fafe in raise () from /lib64/libc.so.6                                                                                                      
#2  0x00007ffff724887f in abort () from /lib64/libc.so.6                                                                                                      
#3  0x00007ffff724879b in __assert_fail_base.cold () from /lib64/libc.so.6                                                                                    
#4  0x00007ffff7258187 in __assert_fail () from /lib64/libc.so.6                                                                                              
#5  0x00007ffff75e05d0 in lexicographical_memcmp (a=0x6020000073d0, al=1, 
    b=0x0, bl=0) at rr.c:541
#6  0x00007ffff75e1b87 in avahi_record_lexicographical_compare (
    a=0x60400000a750, b=0x604000010490) at rr.c:644
#7  0x00007ffff755362e in incoming_probe (s=0x616000001580, 
    record=0x604000010490, i=0x60d000000520) at server.c:213
#8  0x00007ffff7556e86 in handle_query_packet (s=0x616000001580, 
    p=0x60b000000720, i=0x60d000000520, a=0x7ffff48eb1a0, port=5353, 
    legacy_unicast=0, from_local_iface=0) at server.c:642
#9  0x00007ffff755c42f in dispatch_packet (s=0x616000001580, 
    p=0x60b000000720, src_address=0x7ffff48eb1a0, port=5353, 
    dst_address=0x7ffff48eb160, iface=12, ttl=255) at server.c:1002
#10 0x00007ffff755d8cd in mcast_socket_event (w=0x6060000009e0, fd=11, 
    events=AVAHI_WATCH_IN, userdata=0x616000001580) at server.c:1093
#11 0x00007ffff7f7722c in avahi_simple_poll_dispatch (s=0x60e000000040)
    at simple-watch.c:585
#12 0x00007ffff7f773b5 in avahi_simple_poll_iterate (s=0x60e000000040, 
    timeout=-1) at simple-watch.c:605
#13 0x0000000000412f72 in run_server (c=0x517000 <config>) at main.c:1279
#14 0x0000000000414e30 in main (argc=5, argv=0x7fffffffe318) at main.c:1708
```



Fix (patch):
diff --git a/avahi-core/rr.c b/avahi-core/rr.c
index 9c04ebbdb..782416611 100644
--- a/avahi-core/rr.c
+++ b/avahi-core/rr.c
@@ -547,7 +547,7 @@ static int lexicographical_memcmp(const void* a, size_t al, const void* b, size_
     if (al == bl)
         return 0;
     else
-        return al == c ? 1 : -1;
+        return al == c ? -1 : 1;
 }
 
 static int uint16_cmp(uint16_t a, uint16_t b) {
@@ -640,9 +640,23 @@ int avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) {
         case AVAHI_DNS_TYPE_AAAA:
             return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address));
 
-        default:
-            return lexicographical_memcmp(a->data.generic.data, a->data.generic.size,
-                                          b->data.generic.data, b->data.generic.size);
+        default: {
+            size_t asize, bsize;
+
+            asize = a->data.generic.size;
+            bsize = b->data.generic.size;
+
+            if (asize && bsize)
+                r = lexicographical_memcmp(a->data.generic.data, asize, b->data.generic.data, bsize);
+            else if (asize && !bsize)
+                r = 1;
+            else if (!asize && bsize)
+                r = -1;
+            else
+                r = 0;
+
+            return r;
+        }
     }
 
 
diff --git a/fuzz/fuzz-consume-record.c b/fuzz/fuzz-consume-record.c
index 25c1feccc..40a43b0c0 100644
--- a/fuzz/fuzz-consume-record.c
+++ b/fuzz/fuzz-consume-record.c
@@ -67,12 +67,18 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
     ret = avahi_record_equal_no_ttl(r, rs);
     assert(ret);
 
+    ret = avahi_record_lexicographical_compare(r, rs);
+    assert(ret == 0);
+
     if (!(c = avahi_record_copy(r)))
         goto finish;
 
     ret = avahi_record_equal_no_ttl(r, c);
     assert(ret);
 
+    ret = avahi_record_lexicographical_compare(r, c);
+    assert(ret == 0);
+
     avahi_dns_packet_free(p);
     if (!(p = avahi_dns_packet_new(size + AVAHI_DNS_PACKET_EXTRA_SIZE)))
         goto finish;