https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236
--- Comment #3 from Mukund Sivaraman <muks at banu dot com> --- The following is _incorrect_ generated x86_64 code for dns_rdataslab_fromrdataset() as compiled with: gcc version 4.9.1 20140507 (prerelease) (GCC). (the current version of GCC on Arch Linux). NOTE that there are two copies of generated code for this function. Either the first or second copy of code is called based on whether (nalloc == 0) implying (nitems == 0) too. In the (nitems == 0) case, the entire free_rdatas block is deleted by GCC as x is inferred to be NULL. In the (nitems != 0) case, the "if (x != NULL)" check is deleted as x is inferred to NOT be NULL, and isc_mem_put() is always called. The failed assertion obviously happens when (x == NULL), i.e., (nitems == 0). And the fact that the "if (x != NULL)" check was deleted is an obvious sign that code flow is through that path to cause the assertion failure. Because the code is like noodles, I've added STEP# comments in the code to take you through the (nitems == 0) case. Go from STEP #0 to STEP #13. The bug happens because the (nitems == 0) case branches into code for (nitems != 0) where checks have been removed. ; STEP #0 000000000051fc10 <dns_rdataslab_fromrdataset>: 51fc10: 41 57 push %r15 51fc12: 41 56 push %r14 51fc14: 49 89 f7 mov %rsi,%r15 51fc17: 41 55 push %r13 51fc19: 41 54 push %r12 51fc1b: 49 89 fc mov %rdi,%r12 51fc1e: 55 push %rbp 51fc1f: 53 push %rbx 51fc20: 48 83 ec 38 sub $0x38,%rsp 51fc24: 48 89 54 24 08 mov %rdx,0x8(%rsp) 51fc29: 89 4c 24 20 mov %ecx,0x20(%rsp) 51fc2d: e8 9e e7 ff ff callq 51e3d0 <dns_rdataset_count> 51fc32: 41 89 c5 mov %eax,%r13d ;; STEP #1 ;; Is (nitems == 0)? If not, go to 51fc60. It is 0 in our case. 51fc35: 45 85 ed test %r13d,%r13d 51fc38: 75 26 jne 51fc60 <dns_rdataslab_fromrdataset+0x50> ;; STEP #2 ;; if (nitems == 0 && rdataset->type != 0) ;; return (ISC_R_FAILURE); ;; ;; Here, let's take it that (rdataset->type != 0) so that we can ;; continue execution. Jump to 51fd20 (STEP #3). 51fc3a: 66 41 83 7c 24 22 00 cmpw $0x0,0x22(%r12) 51fc41: ba 19 00 00 00 mov $0x19,%edx 51fc46: 0f 84 d4 00 00 00 je 51fd20 <dns_rdataslab_fromrdataset+0x110> ; out: ; ; control comes back here as part of a "return;" statement's code from ; below. 51fc4c: 48 83 c4 38 add $0x38,%rsp 51fc50: 89 d0 mov %edx,%eax 51fc52: 5b pop %rbx 51fc53: 5d pop %rbp 51fc54: 41 5c pop %r12 51fc56: 41 5d pop %r13 51fc58: 41 5e pop %r14 51fc5a: 41 5f pop %r15 51fc5c: c3 retq 51fc5d: 0f 1f 00 nopl (%rax) ; (nitems != 0) case. 51fc60: 41 81 fd ff ff 00 00 cmp $0xffff,%r13d 51fc67: ba 13 00 00 00 mov $0x13,%edx 51fc6c: 77 de ja 51fc4c <dns_rdataslab_fromrdataset+0x3c> 51fc6e: 44 89 e8 mov %r13d,%eax 51fc71: b9 a1 00 00 00 mov $0xa1,%ecx 51fc76: ba 6b e1 63 00 mov $0x63e16b,%edx 51fc7b: 48 8d 1c 40 lea (%rax,%rax,2),%rbx 51fc7f: 4c 89 ff mov %r15,%rdi 51fc82: 48 c1 e3 04 shl $0x4,%rbx 51fc86: 48 89 de mov %rbx,%rsi 51fc89: e8 d2 a9 0a 00 callq 5ca660 <isc__mem_get> 51fc8e: 48 85 c0 test %rax,%rax 51fc91: 49 89 c6 mov %rax,%r14 51fc94: 0f 84 7d 01 00 00 je 51fe17 <dns_rdataslab_fromrdataset+0x207> 51fc9a: 4c 89 e7 mov %r12,%rdi ; /* FIRST COPY OF GENERATED CODE (nalloc > 0) */ ; ; result = dns_rdataset_first(rdataset); 51fc9d: e8 0e e8 ff ff callq 51e4b0 <dns_rdataset_first> ; if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) ; goto free_rdatas; ; 0x1d (ISC_R_NOMORE) 51fca2: 83 f8 1d cmp $0x1d,%eax 51fca5: 74 08 je 51fcaf <dns_rdataslab_fromrdataset+0x9f> ; 0x0 (ISC_R_SUCCESS). if (result != ISC_R_SUCCESS && result != ; ISC_R_NOMORE), jmp to 51fd53 (free_rdatas) below. 51fca7: 85 c0 test %eax,%eax 51fca9: 0f 85 a4 00 00 00 jne 51fd53 <dns_rdataslab_fromrdataset+0x143> ; go below to free_rdatas. ; ignore: for loop 51fcaf: 31 ed xor %ebp,%ebp 51fcb1: 85 c0 test %eax,%eax 51fcb3: 74 1f je 51fcd4 <dns_rdataslab_fromrdataset+0xc4> 51fcb5: e9 82 00 00 00 jmpq 51fd3c <dns_rdataslab_fromrdataset+0x12c> 51fcba: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) ; ignore: for loop iterate (continue;) 51fcc0: 4c 89 e7 mov %r12,%rdi 51fcc3: 83 c5 01 add $0x1,%ebp 51fcc6: e8 35 e8 ff ff callq 51e500 <dns_rdataset_next> 51fccb: 85 c0 test %eax,%eax 51fccd: 75 6d jne 51fd3c <dns_rdataslab_fromrdataset+0x12c> 51fccf: 41 39 ed cmp %ebp,%r13d 51fcd2: 76 68 jbe 51fd3c <dns_rdataslab_fromrdataset+0x12c> 51fcd4: 89 e8 mov %ebp,%eax 51fcd6: 48 8d 1c 40 lea (%rax,%rax,2),%rbx 51fcda: 48 c1 e3 04 shl $0x4,%rbx 51fcde: 4c 01 f3 add %r14,%rbx 51fce1: 48 89 df mov %rbx,%rdi 51fce4: e8 37 ad fd ff callq 4faa20 <dns_rdata_init> 51fce9: 48 89 de mov %rbx,%rsi 51fcec: 4c 89 e7 mov %r12,%rdi 51fcef: e8 5c e8 ff ff callq 51e550 <dns_rdataset_current> ; Second INSIST() inside for loop. First INSIST() is redundant. In this ; compiler's output, the assertion call is inlined here. 51fcf4: 48 81 3b 28 f2 8b 00 cmpq $0x8bf228,(%rbx) 51fcfb: 75 c3 jne 51fcc0 <dns_rdataslab_fromrdataset+0xb0> 51fcfd: b9 77 e1 63 00 mov $0x63e177,%ecx 51fd02: ba 02 00 00 00 mov $0x2,%edx 51fd07: be b1 00 00 00 mov $0xb1,%esi 51fd0c: bf 6b e1 63 00 mov $0x63e16b,%edi 51fd11: e8 1a 81 09 00 callq 5b7e30 <isc_assertion_failed> 51fd16: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 51fd1d: 00 00 00 ; STEP #3 51fd20: 4c 89 e7 mov %r12,%rdi ; /* SECOND COPY OF GENERATED CODE (nalloc == 0) */ ; ; result = dns_rdataset_first(rdataset); 51fd23: e8 88 e7 ff ff callq 51e4b0 <dns_rdataset_first> ; if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) ; goto free_rdatas; ; 0x1d (ISC_R_NOMORE) ;; Let's assume result is ISC_R_NOMORE here. So jump to 51fd37 (STEP #4). 51fd28: 83 f8 1d cmp $0x1d,%eax 51fd2b: 74 0a je 51fd37 <dns_rdataslab_fromrdataset+0x127> ; 0x0 (ISC_R_SUCCESS). if (result != ISC_R_SUCCESS && result != ; ISC_R_NOMORE), jmp to 51fc4c (out) at the start of the function. ; If this jump is taken, isc__mem_put() is not called before return, but ; x is NULL here in this copy of call to dns_rdataset_first() ; (nalloc = dns_rdataset_count(rdataset) returned 0). ;; The mov between test and jne below won't affect ZF. 51fd2d: 85 c0 test %eax,%eax 51fd2f: 89 c2 mov %eax,%edx 51fd31: 0f 85 15 ff ff ff jne 51fc4c <dns_rdataslab_fromrdataset+0x3c> ; STEP #4 ; ; This just zeros some more variables and because %eax is still ; ISC_R_NOMORE, jumps to 51fd7b (STEP #5). 51fd37: 31 ed xor %ebp,%ebp 51fd39: 45 31 f6 xor %r14d,%r14d 51fd3c: 83 f8 1d cmp $0x1d,%eax 51fd3f: 74 3a je 51fd7b <dns_rdataslab_fromrdataset+0x16b> ; before_free_rdatas: ; if (x == NULL), skip calling isc_mem_put() 51fd41: 4d 85 f6 test %r14,%r14 51fd44: 0f 84 12 02 00 00 je 51ff5c <dns_rdataslab_fromrdataset+0x34c> 51fd4a: 4b 8d 5c 6d 00 lea 0x0(%r13,%r13,2),%rbx 51fd4f: 48 c1 e3 04 shl $0x4,%rbx ; STEP #13: ; ; free_rdatas: (nitems != 0) variant ; ; /* When a jmp is made to this location directly, this check is missing, ; causing the assertion! */ ; ; /* if (x != NULL) */ ; isc_mem_put(mctx, x, nalloc * sizeof(struct xrdata)); ; return (result); ; ; 51fd53: 48 89 da mov %rbx,%rdx 51fd56: 41 b8 4e 01 00 00 mov $0x14e,%r8d 51fd5c: b9 6b e1 63 00 mov $0x63e16b,%ecx 51fd61: 4c 89 f6 mov %r14,%rsi 51fd64: 4c 89 ff mov %r15,%rdi 51fd67: 89 44 24 20 mov %eax,0x20(%rsp) 51fd6b: e8 d0 ba 0a 00 callq 5cb840 <isc__mem_put> 51fd70: 8b 44 24 20 mov 0x20(%rsp),%eax 51fd74: 89 c2 mov %eax,%edx 51fd76: e9 d1 fe ff ff jmpq 51fc4c <dns_rdataslab_fromrdataset+0x3c> ; the jmpq instruction above jumps above to "out:" label at the start of ; this function. ; STEP #5 ; ; nalloc is 0 here, so the for (i = 0; i < nalloc... is skipped. ; ; if (i != nalloc) { ; /* ; * Somehow we iterated over fewer rdatas than ; * dns_rdataset_count() said there were! ; */ ; result = ISC_R_FAILURE; ; goto free_rdatas; ; } ; In the following, the jne jumps to before_free_rdatas (above) where x ; is checked for NULL before calling isc_mem_put(). But (i == nalloc) ; here as the for loop was not entered. So keep going below to STEP #6. 51fd7b: 44 39 ed cmp %r13d,%ebp 51fd7e: b0 19 mov $0x19,%al 51fd80: 75 bf jne 51fd41 <dns_rdataslab_fromrdataset+0x131> ; STEP #6 ; ; qsort(x, nalloc, sizeof(struct xrdata), compare_rdata); 51fd82: 8b 44 24 20 mov 0x20(%rsp),%eax 51fd86: b9 80 fb 51 00 mov $0x51fb80,%ecx 51fd8b: ba 30 00 00 00 mov $0x30,%edx 51fd90: 4c 89 f7 mov %r14,%rdi 51fd93: 8d 58 02 lea 0x2(%rax),%ebx 51fd96: 89 e8 mov %ebp,%eax 51fd98: 48 89 c6 mov %rax,%rsi 51fd9b: 48 89 44 24 28 mov %rax,0x28(%rsp) 51fda0: e8 8b 6f ee ff callq 406d30 <qsort@plt> ; for (i = 1; i < nalloc; i++) { ; Here, nalloc is 0, so we jump to 51ff3c (STEP #7). 51fda5: 83 fd 01 cmp $0x1,%ebp 51fda8: 0f 86 8e 01 00 00 jbe 51ff3c <dns_rdataslab_fromrdataset+0x32c> 51fdae: 8d 45 fe lea -0x2(%rbp),%eax 51fdb1: 4d 89 f5 mov %r14,%r13 51fdb4: 89 6c 24 24 mov %ebp,0x24(%rsp) 51fdb8: 48 8d 44 40 03 lea 0x3(%rax,%rax,2),%rax 51fdbd: 48 c1 e0 04 shl $0x4,%rax 51fdc1: 4c 01 f0 add %r14,%rax 51fdc4: 48 89 44 24 18 mov %rax,0x18(%rsp) 51fdc9: eb 17 jmp 51fde2 <dns_rdataslab_fromrdataset+0x1d2> 51fdcb: 83 6c 24 24 01 subl $0x1,0x24(%rsp) 51fdd0: 49 c7 45 00 28 f2 8b movq $0x8bf228,0x0(%r13) 51fdd7: 00 51fdd8: 48 3b 4c 24 18 cmp 0x18(%rsp),%rcx 51fddd: 49 89 cd mov %rcx,%r13 51fde0: 74 3f je 51fe21 <dns_rdataslab_fromrdataset+0x211> 51fde2: 49 8d 4d 30 lea 0x30(%r13),%rcx 51fde6: 4c 89 ef mov %r13,%rdi 51fde9: 48 89 ce mov %rcx,%rsi 51fdec: 48 89 4c 24 10 mov %rcx,0x10(%rsp) 51fdf1: e8 2a e2 fd ff callq 4fe020 <dns_rdata_compare> 51fdf6: 85 c0 test %eax,%eax 51fdf8: 48 8b 4c 24 10 mov 0x10(%rsp),%rcx 51fdfd: 74 cc je 51fdcb <dns_rdataslab_fromrdataset+0x1bb> 51fdff: 41 8b 45 08 mov 0x8(%r13),%eax 51fe03: 01 d8 add %ebx,%eax 51fe05: 8d 58 02 lea 0x2(%rax),%ebx 51fe08: 83 c0 03 add $0x3,%eax 51fe0b: 66 41 83 7c 24 22 2e cmpw $0x2e,0x22(%r12) 51fe12: 0f 44 d8 cmove %eax,%ebx 51fe15: eb c1 jmp 51fdd8 <dns_rdataslab_fromrdataset+0x1c8> 51fe17: ba 01 00 00 00 mov $0x1,%edx 51fe1c: e9 2b fe ff ff jmpq 51fc4c <dns_rdataslab_fromrdataset+0x3c> 51fe21: 89 e8 mov %ebp,%eax 51fe23: 83 e8 01 sub $0x1,%eax 51fe26: 48 8d 04 40 lea (%rax,%rax,2),%rax 51fe2a: 48 c1 e0 04 shl $0x4,%rax 51fe2e: 41 8b 44 06 08 mov 0x8(%r14,%rax,1),%eax 51fe33: 8d 5c 03 02 lea 0x2(%rbx,%rax,1),%ebx ; STEP #8 51fe37: 41 0f b7 44 24 22 movzwl 0x22(%r12),%eax 51fe3d: 31 d2 xor %edx,%edx 51fe3f: 66 83 f8 2e cmp $0x2e,%ax 51fe43: 0f 94 c2 sete %dl 51fe46: 01 d3 add %edx,%ebx ; if (nitems > 1 && dns_rdatatype_issingleton(rdataset->type)) { ; ; here, nitems = 0. so jmp to 51fe72 (STEP #9) 51fe48: 83 7c 24 24 01 cmpl $0x1,0x24(%rsp) 51fe4d: 76 23 jbe 51fe72 <dns_rdataslab_fromrdataset+0x262> 51fe4f: 0f b7 f8 movzwl %ax,%edi 51fe52: e8 c9 d6 ff ff callq 51d520 <dns_rdatatype_issingleton> 51fe57: 85 c0 test %eax,%eax 51fe59: 74 17 je 51fe72 <dns_rdataslab_fromrdataset+0x262> 51fe5b: b8 48 00 01 00 mov $0x10048,%eax ; STEP #12 ; ; Jump to 51fd53 (STEP #13) 51fe60: 48 8b 4c 24 28 mov 0x28(%rsp),%rcx 51fe65: 48 8d 1c 49 lea (%rcx,%rcx,2),%rbx 51fe69: 48 c1 e3 04 shl $0x4,%rbx 51fe6d: e9 e1 fe ff ff jmpq 51fd53 <dns_rdataslab_fromrdataset+0x143> ; STEP #9 ; ; rawbuf = isc_mem_get(mctx, buflen); 51fe72: 89 de mov %ebx,%esi 51fe74: b9 0a 01 00 00 mov $0x10a,%ecx 51fe79: ba 6b e1 63 00 mov $0x63e16b,%edx 51fe7e: 4c 89 ff mov %r15,%rdi 51fe81: e8 da a7 0a 00 callq 5ca660 <isc__mem_get> ; if (rawbuf == NULL) { ; result = ISC_R_NOMEMORY; ; goto free_rdatas; ; } ; ; ; Here, things are already falling apart. if rawbuf is NULL, jump to 51ff52 which is the ; free_rdatas variant for (nalloc != 0) where the (x != NULL) check is ; deleted. But let's assume that rawbuf is not NULL here (likely case). 51fe86: 48 85 c0 test %rax,%rax 51fe89: 0f 84 c3 00 00 00 je 51ff52 <dns_rdataslab_fromrdataset+0x342> ; STEP #10 ; ; for (i = 0; i < nalloc; i++) { ; if (x[i].rdata.data == &removed) ; continue; ; ; ; Here we test first that nalloc (%ebp) is 0. If it is (true in our ; case), jmp to 51ff35 (STEP #11). This also takes us to the free_rdatas ; variant for (nalloc != 0) where the (x != NULL) is deleted. 51fe8f: 48 8b 4c 24 08 mov 0x8(%rsp),%rcx 51fe94: 8b 54 24 20 mov 0x20(%rsp),%edx 51fe98: 48 89 01 mov %rax,(%rcx) 51fe9b: 89 59 08 mov %ebx,0x8(%rcx) 51fe9e: 8b 4c 24 24 mov 0x24(%rsp),%ecx 51fea2: 48 01 c2 add %rax,%rdx 51fea5: 85 ed test %ebp,%ebp 51fea7: 48 89 c8 mov %rcx,%rax 51feaa: 88 4a 01 mov %cl,0x1(%rdx) 51fead: 0f b6 c4 movzbl %ah,%eax 51feb0: 88 02 mov %al,(%rdx) 51feb2: 48 8d 42 02 lea 0x2(%rdx),%rax 51feb6: 74 7d je 51ff35 <dns_rdataslab_fromrdataset+0x325> 51feb8: 83 ed 01 sub $0x1,%ebp 51febb: 49 8d 5e 08 lea 0x8(%r14),%rbx 51febf: 48 8d 54 6d 00 lea 0x0(%rbp,%rbp,2),%rdx 51fec4: 48 c1 e2 04 shl $0x4,%rdx 51fec8: 49 8d 6c 16 38 lea 0x38(%r14,%rdx,1),%rbp 51fecd: eb 20 jmp 51feef <dns_rdataslab_fromrdataset+0x2df> 51fecf: 90 nop 51fed0: 8b 13 mov (%rbx),%edx 51fed2: 48 8b 73 f8 mov -0x8(%rbx),%rsi 51fed6: 48 89 cf mov %rcx,%rdi 51fed9: e8 02 6c ee ff callq 406ae0 <memmove@plt> 51fede: 48 89 c1 mov %rax,%rcx 51fee1: 8b 03 mov (%rbx),%eax 51fee3: 48 01 c8 add %rcx,%rax 51fee6: 48 83 c3 30 add $0x30,%rbx 51feea: 48 39 eb cmp %rbp,%rbx 51feed: 74 46 je 51ff35 <dns_rdataslab_fromrdataset+0x325> 51feef: 48 81 7b f8 28 f2 8b cmpq $0x8bf228,-0x8(%rbx) 51fef6: 00 51fef7: 74 ed je 51fee6 <dns_rdataslab_fromrdataset+0x2d6> 51fef9: 31 d2 xor %edx,%edx 51fefb: 66 41 83 7c 24 22 2e cmpw $0x2e,0x22(%r12) 51ff02: 0f 94 c2 sete %dl 51ff05: 03 13 add (%rbx),%edx 51ff07: 81 fa ff ff 00 00 cmp $0xffff,%edx 51ff0d: 77 54 ja 51ff63 <dns_rdataslab_fromrdataset+0x353> 51ff0f: 0f b6 ce movzbl %dh,%ecx 51ff12: 88 50 01 mov %dl,0x1(%rax) 51ff15: 88 08 mov %cl,(%rax) 51ff17: 66 41 83 7c 24 22 2e cmpw $0x2e,0x22(%r12) 51ff1e: 48 8d 48 02 lea 0x2(%rax),%rcx 51ff22: 75 ac jne 51fed0 <dns_rdataslab_fromrdataset+0x2c0> 51ff24: 8b 53 08 mov 0x8(%rbx),%edx 51ff27: 48 8d 48 03 lea 0x3(%rax),%rcx 51ff2b: d1 ea shr %edx 51ff2d: 83 e2 01 and $0x1,%edx 51ff30: 08 50 02 or %dl,0x2(%rax) 51ff33: eb 9b jmp 51fed0 <dns_rdataslab_fromrdataset+0x2c0> ; STEP #11 ; ; Jump to 51fe60 (STEP #12) 51ff35: 31 c0 xor %eax,%eax 51ff37: e9 24 ff ff ff jmpq 51fe60 <dns_rdataslab_fromrdataset+0x250> ; STEP #7 ; ; check if nalloc is 0 (it is). jmp to 51fe37 (STEP #8). 51ff3c: 85 ed test %ebp,%ebp 51ff3e: 89 6c 24 24 mov %ebp,0x24(%rsp) 51ff42: b8 01 00 00 00 mov $0x1,%eax 51ff47: 0f 84 ea fe ff ff je 51fe37 <dns_rdataslab_fromrdataset+0x227> 51ff4d: e9 d1 fe ff ff jmpq 51fe23 <dns_rdataslab_fromrdataset+0x213> 51ff52: b8 01 00 00 00 mov $0x1,%eax 51ff57: e9 04 ff ff ff jmpq 51fe60 <dns_rdataslab_fromrdataset+0x250> 51ff5c: 89 c2 mov %eax,%edx 51ff5e: e9 e9 fc ff ff jmpq 51fc4c <dns_rdataslab_fromrdataset+0x3c> 51ff63: b9 33 2c 5f 00 mov $0x5f2c33,%ecx 51ff68: ba 02 00 00 00 mov $0x2,%edx 51ff6d: be 34 01 00 00 mov $0x134,%esi 51ff72: bf 6b e1 63 00 mov $0x63e16b,%edi 51ff77: e8 b4 7e 09 00 callq 5b7e30 <isc_assertion_failed> 51ff7c: 0f 1f 40 00 nopl 0x0(%rax)